From 048863e03991a3b49429a77827da2f96b32889f9 Mon Sep 17 00:00:00 2001 From: Piotr Rogowski Date: Sun, 26 Jan 2025 08:31:07 +0100 Subject: [PATCH 001/585] Do not exclude whole project dir when listing in case where project is places inside excluded dir (like /tmp or ~/tmp) --- src/services/glob/list-files.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/services/glob/list-files.ts b/src/services/glob/list-files.ts index 8578b914d72..c7e3d41cf03 100644 --- a/src/services/glob/list-files.ts +++ b/src/services/glob/list-files.ts @@ -34,7 +34,7 @@ export async function listFiles(dirPath: string, recursive: boolean, limit: numb "pkg", "Pods", ".*", // '!**/.*' excludes hidden directories, while '!**/.*/**' excludes only their contents. This way we are at least aware of the existence of hidden directories. - ].map((dir) => `**/${dir}/**`) + ].map((dir) => `${dirPath}/**/${dir}/**`) const options = { cwd: dirPath, From 1cd90a655b0db197b6dd248e854eef366d148849 Mon Sep 17 00:00:00 2001 From: kohii Date: Sun, 2 Feb 2025 10:17:57 +0900 Subject: [PATCH 002/585] feat: Add Kotlin support in list_code_definition_names --- esbuild.js | 1 + .../tree-sitter/__tests__/index.test.ts | 6 ++++ .../__tests__/languageParser.test.ts | 11 ++++++++ src/services/tree-sitter/index.ts | 3 ++ src/services/tree-sitter/languageParser.ts | 6 ++++ src/services/tree-sitter/queries/index.ts | 1 + src/services/tree-sitter/queries/kotlin.ts | 28 +++++++++++++++++++ 7 files changed, 56 insertions(+) create mode 100644 src/services/tree-sitter/queries/kotlin.ts diff --git a/esbuild.js b/esbuild.js index 8b203076e45..7907dd1c390 100644 --- a/esbuild.js +++ b/esbuild.js @@ -52,6 +52,7 @@ const copyWasmFiles = { "java", "php", "swift", + "kotlin", ] languages.forEach((lang) => { diff --git a/src/services/tree-sitter/__tests__/index.test.ts b/src/services/tree-sitter/__tests__/index.test.ts index 4a5782dcb1e..8372e7e5808 100644 --- a/src/services/tree-sitter/__tests__/index.test.ts +++ b/src/services/tree-sitter/__tests__/index.test.ts @@ -169,6 +169,8 @@ describe("Tree-sitter Service", () => { "/test/path/main.rs", "/test/path/program.cpp", "/test/path/code.go", + "/test/path/app.kt", + "/test/path/script.kts", ] ;(listFiles as jest.Mock).mockResolvedValue([mockFiles, new Set()]) @@ -197,6 +199,8 @@ describe("Tree-sitter Service", () => { rs: { parser: mockParser, query: mockQuery }, cpp: { parser: mockParser, query: mockQuery }, go: { parser: mockParser, query: mockQuery }, + kt: { parser: mockParser, query: mockQuery }, + kts: { parser: mockParser, query: mockQuery }, }) ;(fs.readFile as jest.Mock).mockResolvedValue("function test() {}") @@ -207,6 +211,8 @@ describe("Tree-sitter Service", () => { expect(result).toContain("main.rs") expect(result).toContain("program.cpp") expect(result).toContain("code.go") + expect(result).toContain("app.kt") + expect(result).toContain("script.kts") }) it("should normalize paths in output", async () => { diff --git a/src/services/tree-sitter/__tests__/languageParser.test.ts b/src/services/tree-sitter/__tests__/languageParser.test.ts index 1b92d81b6be..54271e30e87 100644 --- a/src/services/tree-sitter/__tests__/languageParser.test.ts +++ b/src/services/tree-sitter/__tests__/languageParser.test.ts @@ -92,6 +92,17 @@ describe("Language Parser", () => { expect(parsers.hpp).toBeDefined() }) + it("should handle Kotlin files correctly", async () => { + const files = ["test.kt", "test.kts"] + const parsers = await loadRequiredLanguageParsers(files) + + expect(ParserMock.Language.load).toHaveBeenCalledWith(expect.stringContaining("tree-sitter-kotlin.wasm")) + expect(parsers.kt).toBeDefined() + expect(parsers.kts).toBeDefined() + expect(parsers.kt.query).toBeDefined() + expect(parsers.kts.query).toBeDefined() + }) + it("should throw error for unsupported file extensions", async () => { const files = ["test.unsupported"] diff --git a/src/services/tree-sitter/index.ts b/src/services/tree-sitter/index.ts index 83e02ac6158..5b48da885dc 100644 --- a/src/services/tree-sitter/index.ts +++ b/src/services/tree-sitter/index.ts @@ -73,6 +73,9 @@ function separateFiles(allFiles: string[]): { filesToParse: string[]; remainingF "java", "php", "swift", + // Kotlin + "kt", + "kts", ].map((e) => `.${e}`) const filesToParse = allFiles.filter((file) => extensions.includes(path.extname(file))).slice(0, 50) // 50 files max const remainingFiles = allFiles.filter((file) => !filesToParse.includes(file)) diff --git a/src/services/tree-sitter/languageParser.ts b/src/services/tree-sitter/languageParser.ts index 2d791b39a8d..f256b0b62ad 100644 --- a/src/services/tree-sitter/languageParser.ts +++ b/src/services/tree-sitter/languageParser.ts @@ -13,6 +13,7 @@ import { javaQuery, phpQuery, swiftQuery, + kotlinQuery, } from "./queries" export interface LanguageParser { @@ -120,6 +121,11 @@ export async function loadRequiredLanguageParsers(filesToParse: string[]): Promi language = await loadLanguage("swift") query = language.query(swiftQuery) break + case "kt": + case "kts": + language = await loadLanguage("kotlin") + query = language.query(kotlinQuery) + break default: throw new Error(`Unsupported language: ${ext}`) } diff --git a/src/services/tree-sitter/queries/index.ts b/src/services/tree-sitter/queries/index.ts index 889210a8e58..818eacca01e 100644 --- a/src/services/tree-sitter/queries/index.ts +++ b/src/services/tree-sitter/queries/index.ts @@ -10,3 +10,4 @@ export { default as cQuery } from "./c" export { default as csharpQuery } from "./c-sharp" export { default as goQuery } from "./go" export { default as swiftQuery } from "./swift" +export { default as kotlinQuery } from "./kotlin" diff --git a/src/services/tree-sitter/queries/kotlin.ts b/src/services/tree-sitter/queries/kotlin.ts new file mode 100644 index 00000000000..61eb112448b --- /dev/null +++ b/src/services/tree-sitter/queries/kotlin.ts @@ -0,0 +1,28 @@ +/* +- class declarations (including interfaces) +- function declarations +- object declarations +- property declarations +- type alias declarations +*/ +export default ` +(class_declaration + (type_identifier) @name.definition.class +) @definition.class + +(function_declaration + (simple_identifier) @name.definition.function +) @definition.function + +(object_declaration + (type_identifier) @name.definition.object +) @definition.object + +(property_declaration + (simple_identifier) @name.definition.property +) @definition.property + +(type_alias + (type_identifier) @name.definition.type +) @definition.type +` From fbf65bfc6c1eeef107e9aa27245ea7bd64a5adf7 Mon Sep 17 00:00:00 2001 From: axb Date: Wed, 12 Feb 2025 17:32:55 +0800 Subject: [PATCH 003/585] Reduce the probability of errors when the model tries to fix the problem due to mismatched line numbers after applying diff --- src/core/mentions/index.ts | 4 ++-- src/integrations/diagnostics/index.ts | 10 +++++++--- src/integrations/editor/DiffViewProvider.ts | 2 +- 3 files changed, 10 insertions(+), 6 deletions(-) diff --git a/src/core/mentions/index.ts b/src/core/mentions/index.ts index cf5bdeaae01..cf87241f23a 100644 --- a/src/core/mentions/index.ts +++ b/src/core/mentions/index.ts @@ -186,9 +186,9 @@ async function getFileOrFolderContent(mentionPath: string, cwd: string): Promise } } -function getWorkspaceProblems(cwd: string): string { +async function getWorkspaceProblems(cwd: string): Promise { const diagnostics = vscode.languages.getDiagnostics() - const result = diagnosticsToProblemsString( + const result = await diagnosticsToProblemsString( diagnostics, [vscode.DiagnosticSeverity.Error, vscode.DiagnosticSeverity.Warning], cwd, diff --git a/src/integrations/diagnostics/index.ts b/src/integrations/diagnostics/index.ts index ad4ee7755cd..2d829f26e76 100644 --- a/src/integrations/diagnostics/index.ts +++ b/src/integrations/diagnostics/index.ts @@ -70,11 +70,12 @@ export function getNewDiagnostics( // // - New error in file3 (1:1) // will return empty string if no problems with the given severity are found -export function diagnosticsToProblemsString( +export async function diagnosticsToProblemsString( diagnostics: [vscode.Uri, vscode.Diagnostic[]][], severities: vscode.DiagnosticSeverity[], cwd: string, -): string { +): Promise { + const documents = new Map() let result = "" for (const [uri, fileDiagnostics] of diagnostics) { const problems = fileDiagnostics.filter((d) => severities.includes(d.severity)) @@ -100,7 +101,10 @@ export function diagnosticsToProblemsString( } const line = diagnostic.range.start.line + 1 // VSCode lines are 0-indexed const source = diagnostic.source ? `${diagnostic.source} ` : "" - result += `\n- [${source}${label}] Line ${line}: ${diagnostic.message}` + const document = documents.get(uri) || (await vscode.workspace.openTextDocument(uri)) + documents.set(uri, document) + const lineContent = document.lineAt(diagnostic.range.start.line).text + result += `\n- [${source}${label}] ${line} | ${lineContent} : ${diagnostic.message}` } } } diff --git a/src/integrations/editor/DiffViewProvider.ts b/src/integrations/editor/DiffViewProvider.ts index ee24d7db4ee..8f7e387c7a2 100644 --- a/src/integrations/editor/DiffViewProvider.ts +++ b/src/integrations/editor/DiffViewProvider.ts @@ -172,7 +172,7 @@ export class DiffViewProvider { initial fix is usually correct and it may just take time for linters to catch up. */ const postDiagnostics = vscode.languages.getDiagnostics() - const newProblems = diagnosticsToProblemsString( + const newProblems = await diagnosticsToProblemsString( getNewDiagnostics(this.preDiagnostics, postDiagnostics), [ vscode.DiagnosticSeverity.Error, // only including errors since warnings can be distracting (if user wants to fix warnings they can use the @problems mention) From a7c842fd9d9871f562844a9c05375090981a2365 Mon Sep 17 00:00:00 2001 From: Marvin Thobejane Date: Fri, 21 Feb 2025 00:11:18 +0200 Subject: [PATCH 004/585] gpt 4o price update --- src/shared/api.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/shared/api.ts b/src/shared/api.ts index 5ad9df8dfa7..9ecb12c1403 100644 --- a/src/shared/api.ts +++ b/src/shared/api.ts @@ -622,8 +622,8 @@ export const openAiNativeModels = { contextWindow: 128_000, supportsImages: true, supportsPromptCache: false, - inputPrice: 5, - outputPrice: 15, + inputPrice: 2.5, + outputPrice: 10, }, "gpt-4o-mini": { maxTokens: 16_384, From 6a29a8bd9985cfec3e8871159539a306a1851306 Mon Sep 17 00:00:00 2001 From: EMSHVAC Date: Thu, 20 Feb 2025 18:56:05 -0600 Subject: [PATCH 005/585] Prevents accidental task deletions by requiring explicit user confirmation through a modal dialog. This improves the user experience by adding a safety check before performing irreversible actions. --- src/core/webview/ClineProvider.ts | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/core/webview/ClineProvider.ts b/src/core/webview/ClineProvider.ts index 05faa138342..6e6ba17db54 100644 --- a/src/core/webview/ClineProvider.ts +++ b/src/core/webview/ClineProvider.ts @@ -2309,6 +2309,17 @@ export class ClineProvider implements vscode.WebviewViewProvider { } async deleteTaskWithId(id: string) { + const answer = await vscode.window.showInformationMessage( + "Are you sure you want to delete this task? This action cannot be undone.", + { modal: true }, + "Delete", + "Cancel", + ) + + if (answer !== "Delete") { + return + } + if (id === this.cline?.taskId) { await this.clearTask() } From 2fd34316210fd1ecdc635e915ac07a6aef2c6f5e Mon Sep 17 00:00:00 2001 From: EMSHVAC Date: Thu, 20 Feb 2025 19:04:15 -0600 Subject: [PATCH 006/585] no duplicated cancel button --- src/core/webview/ClineProvider.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/core/webview/ClineProvider.ts b/src/core/webview/ClineProvider.ts index 6e6ba17db54..824d7a85ea8 100644 --- a/src/core/webview/ClineProvider.ts +++ b/src/core/webview/ClineProvider.ts @@ -2313,7 +2313,6 @@ export class ClineProvider implements vscode.WebviewViewProvider { "Are you sure you want to delete this task? This action cannot be undone.", { modal: true }, "Delete", - "Cancel", ) if (answer !== "Delete") { From 97b8ee3b5681562e4d4f8bdaba4e9c66aa1fa0f5 Mon Sep 17 00:00:00 2001 From: oprstchn Date: Fri, 21 Feb 2025 20:01:03 +0900 Subject: [PATCH 007/585] fix: Update AWS Bedrock region options --- webview-ui/src/components/settings/ApiOptions.tsx | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/webview-ui/src/components/settings/ApiOptions.tsx b/webview-ui/src/components/settings/ApiOptions.tsx index 19813a1f6d5..1303e79c7ab 100644 --- a/webview-ui/src/components/settings/ApiOptions.tsx +++ b/webview-ui/src/components/settings/ApiOptions.tsx @@ -468,9 +468,7 @@ const ApiOptions = ({ id="aws-region-dropdown" value={apiConfiguration?.awsRegion || ""} style={{ width: "100%" }} - onChange={(value: unknown) => { - handleInputChange("awsRegion", dropdownEventTransform) - }} + onChange={handleInputChange("awsRegion", dropdownEventTransform)} options={[ { value: "", label: "Select a region..." }, { value: "us-east-1", label: "us-east-1" }, From 5a43f867fbf53c14f68743937fde0e6a4f07b2fb Mon Sep 17 00:00:00 2001 From: EMSHVAC Date: Fri, 21 Feb 2025 08:46:56 -0600 Subject: [PATCH 008/585] This commit replaces the VSCode native dialog with a Radix AlertDialog component for task deletion confirmation. The change provides a more consistent user experience by using the same design system as the rest of the application. Changes: Add new DeleteTaskDialog component using Radix UIs AlertDialog Update HistoryView to manage dialog state and task deletion flow Remove VSCode native dialog from ClineProvider.ts --- src/core/webview/ClineProvider.ts | 10 ---- .../components/history/DeleteTaskDialog.tsx | 49 +++++++++++++++++++ .../src/components/history/HistoryView.tsx | 19 ++++++- 3 files changed, 67 insertions(+), 11 deletions(-) create mode 100644 webview-ui/src/components/history/DeleteTaskDialog.tsx diff --git a/src/core/webview/ClineProvider.ts b/src/core/webview/ClineProvider.ts index 824d7a85ea8..05faa138342 100644 --- a/src/core/webview/ClineProvider.ts +++ b/src/core/webview/ClineProvider.ts @@ -2309,16 +2309,6 @@ export class ClineProvider implements vscode.WebviewViewProvider { } async deleteTaskWithId(id: string) { - const answer = await vscode.window.showInformationMessage( - "Are you sure you want to delete this task? This action cannot be undone.", - { modal: true }, - "Delete", - ) - - if (answer !== "Delete") { - return - } - if (id === this.cline?.taskId) { await this.clearTask() } diff --git a/webview-ui/src/components/history/DeleteTaskDialog.tsx b/webview-ui/src/components/history/DeleteTaskDialog.tsx new file mode 100644 index 00000000000..b40adeae3de --- /dev/null +++ b/webview-ui/src/components/history/DeleteTaskDialog.tsx @@ -0,0 +1,49 @@ +import React from "react" +import { + AlertDialog, + AlertDialogAction, + AlertDialogCancel, + AlertDialogContent, + AlertDialogDescription, + AlertDialogFooter, + AlertDialogHeader, + AlertDialogTitle, +} from "@/components/ui/alert-dialog" +import { Button } from "@/components/ui" +import { vscode } from "@/utils/vscode" + +interface DeleteTaskDialogProps { + taskId: string + open: boolean + onOpenChange: (open: boolean) => void +} + +export const DeleteTaskDialog = ({ taskId, open, onOpenChange }: DeleteTaskDialogProps) => { + const handleDelete = () => { + vscode.postMessage({ type: "deleteTaskWithId", text: taskId }) + onOpenChange(false) + } + + return ( + + + + Delete Task + + Are you sure you want to delete this task? This action cannot be undone. + + + + + + + + + + + + + ) +} diff --git a/webview-ui/src/components/history/HistoryView.tsx b/webview-ui/src/components/history/HistoryView.tsx index 38ca14df46c..ca60e1fcb89 100644 --- a/webview-ui/src/components/history/HistoryView.tsx +++ b/webview-ui/src/components/history/HistoryView.tsx @@ -1,4 +1,5 @@ import React, { memo, useMemo, useState, useEffect } from "react" +import { DeleteTaskDialog } from "./DeleteTaskDialog" import { Fzf } from "fzf" import prettyBytes from "pretty-bytes" import { Virtuoso } from "react-virtuoso" @@ -37,8 +38,12 @@ const HistoryView = ({ onDone }: HistoryViewProps) => { vscode.postMessage({ type: "showTaskWithId", text: id }) } + const [deleteDialogOpen, setDeleteDialogOpen] = useState(false) + const [taskToDelete, setTaskToDelete] = useState(null) + const handleDeleteHistoryItem = (id: string) => { - vscode.postMessage({ type: "deleteTaskWithId", text: id }) + setTaskToDelete(id) + setDeleteDialogOpen(true) } const formatDate = (timestamp: number) => { @@ -398,6 +403,18 @@ const HistoryView = ({ onDone }: HistoryViewProps) => { )} /> + {taskToDelete && ( + { + setDeleteDialogOpen(open) + if (!open) { + setTaskToDelete(null) + } + }} + /> + )} ) } From 2f608f736c3d25f37a3667ff33f654ffddd797b0 Mon Sep 17 00:00:00 2001 From: Matt Rubens Date: Fri, 21 Feb 2025 17:29:10 -0500 Subject: [PATCH 009/585] v3.3.24 --- .changeset/large-geese-sparkle.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/large-geese-sparkle.md diff --git a/.changeset/large-geese-sparkle.md b/.changeset/large-geese-sparkle.md new file mode 100644 index 00000000000..a46f340da88 --- /dev/null +++ b/.changeset/large-geese-sparkle.md @@ -0,0 +1,5 @@ +--- +"roo-cline": patch +--- + +v3.3.24 From 04009e5e2654700a33993fb32a50cb0d103cdcdd Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Fri, 21 Feb 2025 22:31:48 +0000 Subject: [PATCH 010/585] changeset version bump --- .changeset/large-geese-sparkle.md | 5 ----- CHANGELOG.md | 6 ++++++ package-lock.json | 4 ++-- package.json | 2 +- 4 files changed, 9 insertions(+), 8 deletions(-) delete mode 100644 .changeset/large-geese-sparkle.md diff --git a/.changeset/large-geese-sparkle.md b/.changeset/large-geese-sparkle.md deleted file mode 100644 index a46f340da88..00000000000 --- a/.changeset/large-geese-sparkle.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"roo-cline": patch ---- - -v3.3.24 diff --git a/CHANGELOG.md b/CHANGELOG.md index 3365f290019..e27d9f1697b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ # Roo Code Changelog +## 3.3.24 + +### Patch Changes + +- v3.3.24 + ## [3.3.23] - Handle errors more gracefully when reading custom instructions from files (thanks @joemanley201!) diff --git a/package-lock.json b/package-lock.json index e3ee5517724..08b537e0562 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "roo-cline", - "version": "3.3.23", + "version": "3.3.24", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "roo-cline", - "version": "3.3.23", + "version": "3.3.24", "dependencies": { "@anthropic-ai/bedrock-sdk": "^0.10.2", "@anthropic-ai/sdk": "^0.26.0", diff --git a/package.json b/package.json index ebf733ed26f..0931199b6f5 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.23", + "version": "3.3.24", "icon": "assets/icons/rocket.png", "galleryBanner": { "color": "#617A91", From 6e7085aad306e06de03da42ed56d34c695f20995 Mon Sep 17 00:00:00 2001 From: Matt Rubens Date: Fri, 21 Feb 2025 17:32:54 -0500 Subject: [PATCH 011/585] Update CHANGELOG.md --- CHANGELOG.md | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e27d9f1697b..07a306fcc3f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,10 +1,9 @@ # Roo Code Changelog -## 3.3.24 +## [3.3.24] -### Patch Changes - -- v3.3.24 +- Fixed a bug with region selection preventing AWS Bedrock profiles from being saved (thanks @oprstchn!) +- Updated the price of gpt-4o (thanks @marvijo-code!) ## [3.3.23] From 68aee001fc5308af25c5f899e84cff0b72c739c4 Mon Sep 17 00:00:00 2001 From: Matt Rubens Date: Fri, 21 Feb 2025 17:51:48 -0500 Subject: [PATCH 012/585] Skip flaky tests and suppress logging to get back to clean output --- jest.config.js | 2 +- src/__mocks__/jest.setup.ts | 17 +++++++++++++++++ src/core/Cline.ts | 2 -- src/core/__tests__/Cline.test.ts | 32 ++++++++++++++++++++++++++++---- 4 files changed, 46 insertions(+), 7 deletions(-) create mode 100644 src/__mocks__/jest.setup.ts diff --git a/jest.config.js b/jest.config.js index d0efc8eaaf7..dbe5ee54ebe 100644 --- a/jest.config.js +++ b/jest.config.js @@ -37,5 +37,5 @@ module.exports = { roots: ["/src", "/webview-ui/src"], modulePathIgnorePatterns: [".vscode-test"], reporters: [["jest-simple-dot-reporter", {}]], - setupFiles: [], + setupFiles: ["/src/__mocks__/jest.setup.ts"], } diff --git a/src/__mocks__/jest.setup.ts b/src/__mocks__/jest.setup.ts new file mode 100644 index 00000000000..6bd00e95673 --- /dev/null +++ b/src/__mocks__/jest.setup.ts @@ -0,0 +1,17 @@ +// Mock the logger globally for all tests +jest.mock("../utils/logging", () => ({ + logger: { + debug: jest.fn(), + info: jest.fn(), + warn: jest.fn(), + error: jest.fn(), + fatal: jest.fn(), + child: jest.fn().mockReturnValue({ + debug: jest.fn(), + info: jest.fn(), + warn: jest.fn(), + error: jest.fn(), + fatal: jest.fn(), + }), + }, +})) diff --git a/src/core/Cline.ts b/src/core/Cline.ts index 9c2977a2669..558fe0af30c 100644 --- a/src/core/Cline.ts +++ b/src/core/Cline.ts @@ -2836,8 +2836,6 @@ export class Cline { } const abortStream = async (cancelReason: ClineApiReqCancelReason, streamingFailedMessage?: string) => { - console.log(`[Cline#abortStream] cancelReason = ${cancelReason}`) - if (this.diffViewProvider.isEditing) { await this.diffViewProvider.revertChanges() // closes diff view } diff --git a/src/core/__tests__/Cline.test.ts b/src/core/__tests__/Cline.test.ts index 3da0c8cdd3d..2eb9ab83f30 100644 --- a/src/core/__tests__/Cline.test.ts +++ b/src/core/__tests__/Cline.test.ts @@ -82,7 +82,20 @@ jest.mock("fs/promises", () => ({ return Promise.resolve(JSON.stringify(mockMessages)) } if (filePath.includes("api_conversation_history.json")) { - return Promise.resolve("[]") + return Promise.resolve( + JSON.stringify([ + { + role: "user", + content: [{ type: "text", text: "historical task" }], + ts: Date.now(), + }, + { + role: "assistant", + content: [{ type: "text", text: "I'll help you with that task." }], + ts: Date.now(), + }, + ]), + ) } return Promise.resolve("[]") }), @@ -295,7 +308,18 @@ describe("Cline", () => { taskDirPath: "/mock/storage/path/tasks/123", apiConversationHistoryFilePath: "/mock/storage/path/tasks/123/api_conversation_history.json", uiMessagesFilePath: "/mock/storage/path/tasks/123/ui_messages.json", - apiConversationHistory: [], + apiConversationHistory: [ + { + role: "user", + content: [{ type: "text", text: "historical task" }], + ts: Date.now(), + }, + { + role: "assistant", + content: [{ type: "text", text: "I'll help you with that task." }], + ts: Date.now(), + }, + ], })) }) @@ -670,7 +694,7 @@ describe("Cline", () => { }) }) - it("should handle API retry with countdown", async () => { + it.skip("should handle API retry with countdown", async () => { const cline = new Cline(mockProvider, mockApiConfig, undefined, false, false, undefined, "test task") // Mock delay to track countdown timing @@ -787,7 +811,7 @@ describe("Cline", () => { ) }) - it("should not apply retry delay twice", async () => { + it.skip("should not apply retry delay twice", async () => { const cline = new Cline(mockProvider, mockApiConfig, undefined, false, false, undefined, "test task") // Mock delay to track countdown timing From c4bc1c0861f195640390f3c7ebe1882136f8d5e5 Mon Sep 17 00:00:00 2001 From: Matt Rubens Date: Wed, 19 Feb 2025 10:58:38 -0500 Subject: [PATCH 013/585] Add debugger mode --- src/shared/modes.ts | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/shared/modes.ts b/src/shared/modes.ts index 72f7785f134..073426a3a0b 100644 --- a/src/shared/modes.ts +++ b/src/shared/modes.ts @@ -98,6 +98,15 @@ export const modes: readonly ModeConfig[] = [ customInstructions: "You can analyze code, explain concepts, and access external resources. Make sure to answer the user's questions and don't rush to switch to implementing code.", }, + { + slug: "debug", + name: "Debug", + roleDefinition: + "You are Roo, an expert software debugger specializing in systematic problem diagnosis and resolution.", + groups: ["read", "edit", "browser", "command", "mcp"], + customInstructions: + "Reflect on 5-7 different possible sources of the problem, prioritizing them based on likelihood, impact on functionality, and frequency in similar issues. Only consider sources that align with the error logs, recent code changes, and system constraints. Ignore external dependencies unless logs indicate otherwise.\n\nOnce you've narrowed it down to the 1-2 most likely sources, cross-check them against previous error logs, relevant system state, and expected behaviors. If inconsistencies arise, refine your hypothesis.\n\nWhen adding logs, ensure they are strategically placed to confirm or eliminate multiple causes. If the logs do not support your assumptions, suggest an alternative debugging strategy before proceeding.\n\nBefore implementing a fix, summarize the issue, validated assumptions, and expected log outputs that would confirm the problem is solved.", + }, ] as const // Export the default mode slug From df46f6011763e33e62c7ae0d1dfd64fa8a36ef5e Mon Sep 17 00:00:00 2001 From: Matt Rubens Date: Thu, 20 Feb 2025 00:59:39 -0500 Subject: [PATCH 014/585] Power steering --- src/core/Cline.ts | 28 ++++++++++++++++++++++---- src/shared/experiments.ts | 7 +++++++ src/shared/modes.ts | 41 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 72 insertions(+), 4 deletions(-) diff --git a/src/core/Cline.ts b/src/core/Cline.ts index 558fe0af30c..560609a109a 100644 --- a/src/core/Cline.ts +++ b/src/core/Cline.ts @@ -54,7 +54,7 @@ import { parseMentions } from "./mentions" import { AssistantMessageContent, parseAssistantMessage, ToolParamName, ToolUseName } from "./assistant-message" import { formatResponse } from "./prompts/responses" import { SYSTEM_PROMPT } from "./prompts/system" -import { modes, defaultModeSlug, getModeBySlug } from "../shared/modes" +import { modes, defaultModeSlug, getModeBySlug, getFullModeDetails } from "../shared/modes" import { truncateConversationIfNeeded } from "./sliding-window" import { ClineProvider, GlobalFileNames } from "./webview/ClineProvider" import { detectCodeOmission } from "../integrations/editor/detect-omission" @@ -63,7 +63,7 @@ import { OpenRouterHandler } from "../api/providers/openrouter" import { McpHub } from "../services/mcp/McpHub" import crypto from "crypto" import { insertGroups } from "./diff/insert-groups" -import { EXPERIMENT_IDS, experiments as Experiments } from "../shared/experiments" +import { EXPERIMENT_IDS, experiments as Experiments, ExperimentId } from "../shared/experiments" const cwd = vscode.workspace.workspaceFolders?.map((folder) => folder.uri.fsPath).at(0) ?? path.join(os.homedir(), "Desktop") // may or may not exist but fs checking existence would immediately ask for permission which would be bad UX, need to come up with a better solution @@ -3235,9 +3235,29 @@ export class Cline { details += `\n\n# Current Context Size (Tokens)\n${contextTokens ? `${contextTokens.toLocaleString()} (${contextPercentage}%)` : "(Not available)"}` // Add current mode and any mode-specific warnings - const { mode, customModes } = (await this.providerRef.deref()?.getState()) ?? {} + const { + mode, + customModes, + customModePrompts, + experiments = {} as Record, + customInstructions: globalCustomInstructions, + preferredLanguage, + } = (await this.providerRef.deref()?.getState()) ?? {} const currentMode = mode ?? defaultModeSlug - details += `\n\n# Current Mode\n${currentMode}` + const modeDetails = await getFullModeDetails(currentMode, customModes, customModePrompts, { + cwd, + globalCustomInstructions, + preferredLanguage, + }) + details += `\n\n# Current Mode\n` + details += `${currentMode}\n` + details += `${modeDetails.name}\n` + if (Experiments.isEnabled(experiments ?? {}, EXPERIMENT_IDS.POWER_STEERING)) { + details += `${modeDetails.roleDefinition}\n` + if (modeDetails.customInstructions) { + details += `${modeDetails.customInstructions}\n` + } + } // Add warning if not in code mode if ( diff --git a/src/shared/experiments.ts b/src/shared/experiments.ts index 9849e225263..27a5e7530d2 100644 --- a/src/shared/experiments.ts +++ b/src/shared/experiments.ts @@ -2,6 +2,7 @@ export const EXPERIMENT_IDS = { DIFF_STRATEGY: "experimentalDiffStrategy", SEARCH_AND_REPLACE: "search_and_replace", INSERT_BLOCK: "insert_content", + POWER_STEERING: "powerSteering", } as const export type ExperimentKey = keyof typeof EXPERIMENT_IDS @@ -35,6 +36,12 @@ export const experimentConfigsMap: Record = { "Enable the experimental insert content tool, allowing Roo to insert content at specific line numbers without needing to create a diff.", enabled: false, }, + POWER_STEERING: { + name: 'Use experimental "power steering" mode', + description: + "When enabled, Roo will remind the model about the details of its current mode definition more frequently. This will lead to stronger adherence to role definitions and custom instructions, but will use additional tokens.", + enabled: false, + }, } export const experimentDefault = Object.fromEntries( diff --git a/src/shared/modes.ts b/src/shared/modes.ts index 073426a3a0b..7ab0dd86d66 100644 --- a/src/shared/modes.ts +++ b/src/shared/modes.ts @@ -1,5 +1,6 @@ import * as vscode from "vscode" import { TOOL_GROUPS, ToolGroup, ALWAYS_AVAILABLE_TOOLS } from "./tool-groups" +import { addCustomInstructions } from "../core/prompts/sections/custom-instructions" // Mode types export type Mode = string @@ -262,6 +263,46 @@ export async function getAllModesWithPrompts(context: vscode.ExtensionContext): })) } +// Helper function to get complete mode details with all overrides +export async function getFullModeDetails( + modeSlug: string, + customModes?: ModeConfig[], + customModePrompts?: CustomModePrompts, + options?: { + cwd?: string + globalCustomInstructions?: string + preferredLanguage?: string + }, +): Promise { + // First get the base mode config from custom modes or built-in modes + const baseMode = getModeBySlug(modeSlug, customModes) || modes.find((m) => m.slug === modeSlug) || modes[0] + + // Check for any prompt component overrides + const promptComponent = customModePrompts?.[modeSlug] + + // Get the base custom instructions + const baseCustomInstructions = promptComponent?.customInstructions || baseMode.customInstructions || "" + + // If we have cwd, load and combine all custom instructions + let fullCustomInstructions = baseCustomInstructions + if (options?.cwd) { + fullCustomInstructions = await addCustomInstructions( + baseCustomInstructions, + options.globalCustomInstructions || "", + options.cwd, + modeSlug, + { preferredLanguage: options.preferredLanguage }, + ) + } + + // Return mode with any overrides applied + return { + ...baseMode, + roleDefinition: promptComponent?.roleDefinition || baseMode.roleDefinition, + customInstructions: fullCustomInstructions, + } +} + // Helper function to safely get role definition export function getRoleDefinition(modeSlug: string, customModes?: ModeConfig[]): string { const mode = getModeBySlug(modeSlug, customModes) From 97b67928f0919242d074bc6722f64d3c2746baf2 Mon Sep 17 00:00:00 2001 From: Matt Rubens Date: Fri, 21 Feb 2025 11:14:52 -0500 Subject: [PATCH 015/585] Add tests --- .../webview/__tests__/ClineProvider.test.ts | 39 ++++++- src/shared/__tests__/experiments.test.ts | 47 ++++++++ src/shared/__tests__/modes.test.ts | 101 +++++++++++++++++- 3 files changed, 181 insertions(+), 6 deletions(-) create mode 100644 src/shared/__tests__/experiments.test.ts diff --git a/src/core/webview/__tests__/ClineProvider.test.ts b/src/core/webview/__tests__/ClineProvider.test.ts index 0a8f73308f5..13a2e6d84f7 100644 --- a/src/core/webview/__tests__/ClineProvider.test.ts +++ b/src/core/webview/__tests__/ClineProvider.test.ts @@ -9,12 +9,41 @@ import { setSoundEnabled } from "../../../utils/sound" import { defaultModeSlug } from "../../../shared/modes" import { experimentDefault } from "../../../shared/experiments" -// Mock custom-instructions module -const mockAddCustomInstructions = jest.fn() +// Mock setup must come before imports +jest.mock("../../prompts/sections/custom-instructions") -jest.mock("../../prompts/sections/custom-instructions", () => ({ - addCustomInstructions: mockAddCustomInstructions, -})) +// Mock dependencies +jest.mock("vscode") +jest.mock("delay") +jest.mock( + "@modelcontextprotocol/sdk/types.js", + () => ({ + CallToolResultSchema: {}, + ListResourcesResultSchema: {}, + ListResourceTemplatesResultSchema: {}, + ListToolsResultSchema: {}, + ReadResourceResultSchema: {}, + ErrorCode: { + InvalidRequest: "InvalidRequest", + MethodNotFound: "MethodNotFound", + InternalError: "InternalError", + }, + McpError: class McpError extends Error { + code: string + constructor(code: string, message: string) { + super(message) + this.code = code + this.name = "McpError" + } + }, + }), + { virtual: true }, +) + +// Initialize mocks +const mockAddCustomInstructions = jest.fn().mockResolvedValue("Combined instructions") +;(jest.requireMock("../../prompts/sections/custom-instructions") as any).addCustomInstructions = + mockAddCustomInstructions // Mock delay module jest.mock("delay", () => { diff --git a/src/shared/__tests__/experiments.test.ts b/src/shared/__tests__/experiments.test.ts new file mode 100644 index 00000000000..cd3828193fe --- /dev/null +++ b/src/shared/__tests__/experiments.test.ts @@ -0,0 +1,47 @@ +import { EXPERIMENT_IDS, experimentConfigsMap, experiments as Experiments, ExperimentId } from "../experiments" + +describe("experiments", () => { + describe("POWER_STEERING", () => { + it("is configured correctly", () => { + expect(EXPERIMENT_IDS.POWER_STEERING).toBe("powerSteering") + expect(experimentConfigsMap.POWER_STEERING).toMatchObject({ + name: 'Use experimental "power steering" mode', + description: + "When enabled, Roo will remind the model about the details of its current mode definition more frequently. This will lead to stronger adherence to role definitions and custom instructions, but will use additional tokens.", + enabled: false, + }) + }) + }) + + describe("isEnabled", () => { + it("returns false when experiment is not enabled", () => { + const experiments: Record = { + powerSteering: false, + experimentalDiffStrategy: false, + search_and_replace: false, + insert_content: false, + } + expect(Experiments.isEnabled(experiments, EXPERIMENT_IDS.POWER_STEERING)).toBe(false) + }) + + it("returns true when experiment is enabled", () => { + const experiments: Record = { + powerSteering: true, + experimentalDiffStrategy: false, + search_and_replace: false, + insert_content: false, + } + expect(Experiments.isEnabled(experiments, EXPERIMENT_IDS.POWER_STEERING)).toBe(true) + }) + + it("returns false when experiment is not present", () => { + const experiments: Record = { + experimentalDiffStrategy: false, + search_and_replace: false, + insert_content: false, + powerSteering: false, + } + expect(Experiments.isEnabled(experiments, EXPERIMENT_IDS.POWER_STEERING)).toBe(false) + }) + }) +}) diff --git a/src/shared/__tests__/modes.test.ts b/src/shared/__tests__/modes.test.ts index aaf7338b5e7..62c84808059 100644 --- a/src/shared/__tests__/modes.test.ts +++ b/src/shared/__tests__/modes.test.ts @@ -1,4 +1,13 @@ -import { isToolAllowedForMode, FileRestrictionError, ModeConfig } from "../modes" +// Mock setup must come before imports +jest.mock("vscode") +const mockAddCustomInstructions = jest.fn().mockResolvedValue("Combined instructions") +jest.mock("../../core/prompts/sections/custom-instructions", () => ({ + addCustomInstructions: mockAddCustomInstructions, +})) + +import { isToolAllowedForMode, FileRestrictionError, ModeConfig, getFullModeDetails, modes } from "../modes" +import * as vscode from "vscode" +import { addCustomInstructions } from "../../core/prompts/sections/custom-instructions" describe("isToolAllowedForMode", () => { const customModes: ModeConfig[] = [ @@ -324,6 +333,96 @@ describe("FileRestrictionError", () => { expect(error.name).toBe("FileRestrictionError") }) + describe("debug mode", () => { + it("is configured correctly", () => { + const debugMode = modes.find((mode) => mode.slug === "debug") + expect(debugMode).toBeDefined() + expect(debugMode).toMatchObject({ + slug: "debug", + name: "Debug", + roleDefinition: + "You are Roo, an expert software debugger specializing in systematic problem diagnosis and resolution.", + groups: ["read", "edit", "browser", "command", "mcp"], + }) + expect(debugMode?.customInstructions).toContain("Reflect on 5-7 different possible sources of the problem") + }) + }) + + describe("getFullModeDetails", () => { + beforeEach(() => { + jest.clearAllMocks() + ;(addCustomInstructions as jest.Mock).mockResolvedValue("Combined instructions") + }) + + it("returns base mode when no overrides exist", async () => { + const result = await getFullModeDetails("debug") + expect(result).toMatchObject({ + slug: "debug", + name: "Debug", + roleDefinition: + "You are Roo, an expert software debugger specializing in systematic problem diagnosis and resolution.", + }) + }) + + it("applies custom mode overrides", async () => { + const customModes = [ + { + slug: "debug", + name: "Custom Debug", + roleDefinition: "Custom debug role", + groups: ["read"], + }, + ] + + const result = await getFullModeDetails("debug", customModes) + expect(result).toMatchObject({ + slug: "debug", + name: "Custom Debug", + roleDefinition: "Custom debug role", + groups: ["read"], + }) + }) + + it("applies prompt component overrides", async () => { + const customModePrompts = { + debug: { + roleDefinition: "Overridden role", + customInstructions: "Overridden instructions", + }, + } + + const result = await getFullModeDetails("debug", undefined, customModePrompts) + expect(result.roleDefinition).toBe("Overridden role") + expect(result.customInstructions).toBe("Overridden instructions") + }) + + it("combines custom instructions when cwd provided", async () => { + const options = { + cwd: "/test/path", + globalCustomInstructions: "Global instructions", + preferredLanguage: "en", + } + + await getFullModeDetails("debug", undefined, undefined, options) + + expect(addCustomInstructions).toHaveBeenCalledWith( + expect.any(String), + "Global instructions", + "/test/path", + "debug", + { preferredLanguage: "en" }, + ) + }) + + it("falls back to first mode for non-existent mode", async () => { + const result = await getFullModeDetails("non-existent") + expect(result).toMatchObject({ + ...modes[0], + customInstructions: "", + }) + }) + }) + it("formats error message with description when provided", () => { const error = new FileRestrictionError("Markdown Editor", "\\.md$", "Markdown files only", "test.js") expect(error.message).toBe( From 7f1579a74d17ab069719e905acc5656d22919920 Mon Sep 17 00:00:00 2001 From: Matt Rubens Date: Fri, 21 Feb 2025 19:46:01 -0500 Subject: [PATCH 016/585] Changeset --- .changeset/flat-snails-unite.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/flat-snails-unite.md diff --git a/.changeset/flat-snails-unite.md b/.changeset/flat-snails-unite.md new file mode 100644 index 00000000000..66e920d1414 --- /dev/null +++ b/.changeset/flat-snails-unite.md @@ -0,0 +1,5 @@ +--- +"roo-cline": patch +--- + +Debugger mode From 6d7f4d92fc69be7eaf30e590c2f91e888015d931 Mon Sep 17 00:00:00 2001 From: Matt Rubens Date: Fri, 21 Feb 2025 19:48:37 -0500 Subject: [PATCH 017/585] Update experiments.ts --- src/shared/experiments.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/shared/experiments.ts b/src/shared/experiments.ts index 27a5e7530d2..2f946283c0d 100644 --- a/src/shared/experiments.ts +++ b/src/shared/experiments.ts @@ -39,7 +39,7 @@ export const experimentConfigsMap: Record = { POWER_STEERING: { name: 'Use experimental "power steering" mode', description: - "When enabled, Roo will remind the model about the details of its current mode definition more frequently. This will lead to stronger adherence to role definitions and custom instructions, but will use additional tokens.", + "When enabled, Roo will remind the model about the details of its current mode definition more frequently. This will lead to stronger adherence to role definitions and custom instructions, but will use more tokens per message.", enabled: false, }, } From c0090952d685a6f75f3aee24765dcc538825c1e5 Mon Sep 17 00:00:00 2001 From: Matt Rubens Date: Fri, 21 Feb 2025 19:53:05 -0500 Subject: [PATCH 018/585] Update experiments.test.ts --- src/shared/__tests__/experiments.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/shared/__tests__/experiments.test.ts b/src/shared/__tests__/experiments.test.ts index cd3828193fe..c5b999a1a33 100644 --- a/src/shared/__tests__/experiments.test.ts +++ b/src/shared/__tests__/experiments.test.ts @@ -7,7 +7,7 @@ describe("experiments", () => { expect(experimentConfigsMap.POWER_STEERING).toMatchObject({ name: 'Use experimental "power steering" mode', description: - "When enabled, Roo will remind the model about the details of its current mode definition more frequently. This will lead to stronger adherence to role definitions and custom instructions, but will use additional tokens.", + "When enabled, Roo will remind the model about the details of its current mode definition more frequently. This will lead to stronger adherence to role definitions and custom instructions, but will use more tokens per message.", enabled: false, }) }) From ec7f11a59228e2be3012591807d8c7409216fb26 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Sat, 22 Feb 2025 00:57:22 +0000 Subject: [PATCH 019/585] changeset version bump --- .changeset/flat-snails-unite.md | 5 ----- CHANGELOG.md | 6 ++++++ package-lock.json | 4 ++-- package.json | 2 +- 4 files changed, 9 insertions(+), 8 deletions(-) delete mode 100644 .changeset/flat-snails-unite.md diff --git a/.changeset/flat-snails-unite.md b/.changeset/flat-snails-unite.md deleted file mode 100644 index 66e920d1414..00000000000 --- a/.changeset/flat-snails-unite.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"roo-cline": patch ---- - -Debugger mode diff --git a/CHANGELOG.md b/CHANGELOG.md index 07a306fcc3f..96bbc4695c8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ # Roo Code Changelog +## 3.3.25 + +### Patch Changes + +- Debugger mode + ## [3.3.24] - Fixed a bug with region selection preventing AWS Bedrock profiles from being saved (thanks @oprstchn!) diff --git a/package-lock.json b/package-lock.json index 08b537e0562..75e033dffbe 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "roo-cline", - "version": "3.3.24", + "version": "3.3.25", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "roo-cline", - "version": "3.3.24", + "version": "3.3.25", "dependencies": { "@anthropic-ai/bedrock-sdk": "^0.10.2", "@anthropic-ai/sdk": "^0.26.0", diff --git a/package.json b/package.json index 0931199b6f5..34329c72da7 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.24", + "version": "3.3.25", "icon": "assets/icons/rocket.png", "galleryBanner": { "color": "#617A91", From e86e2a9047c0629901cc29399c8a8999b14af9fe Mon Sep 17 00:00:00 2001 From: R00-B0T Date: Sat, 22 Feb 2025 00:57:47 +0000 Subject: [PATCH 020/585] Updating CHANGELOG.md format --- CHANGELOG.md | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 96bbc4695c8..9b8a6edb872 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,8 +1,6 @@ # Roo Code Changelog -## 3.3.25 - -### Patch Changes +## [3.3.25] - Debugger mode From b3229a90185fe61101d230b883464801019f9b83 Mon Sep 17 00:00:00 2001 From: Matt Rubens Date: Fri, 21 Feb 2025 20:19:17 -0500 Subject: [PATCH 021/585] Update CHANGELOG.md --- CHANGELOG.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9b8a6edb872..44fe17de115 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,7 +2,8 @@ ## [3.3.25] -- Debugger mode +- Add a "Debug" mode that specializes in debugging tricky problems (thanks [Ted Werbel](https://x.com/tedx_ai/status/1891514191179309457) and [Carlos E. Perez](https://x.com/IntuitMachine/status/1891516362486337739)!) +- Add an experimental "Power Steering" option to significantly improve adherence to role definitions and custom instructions ## [3.3.24] From 6e554701556eac3e25d363877447beb2cce3d2ed Mon Sep 17 00:00:00 2001 From: Matt Rubens Date: Fri, 21 Feb 2025 23:50:24 -0500 Subject: [PATCH 022/585] Tweak the debug prompt --- .changeset/cuddly-crabs-shake.md | 5 +++++ src/shared/__tests__/modes.test.ts | 4 +++- src/shared/modes.ts | 2 +- 3 files changed, 9 insertions(+), 2 deletions(-) create mode 100644 .changeset/cuddly-crabs-shake.md diff --git a/.changeset/cuddly-crabs-shake.md b/.changeset/cuddly-crabs-shake.md new file mode 100644 index 00000000000..2f23536e172 --- /dev/null +++ b/.changeset/cuddly-crabs-shake.md @@ -0,0 +1,5 @@ +--- +"roo-cline": patch +--- + +Tweak the debug prompt diff --git a/src/shared/__tests__/modes.test.ts b/src/shared/__tests__/modes.test.ts index 62c84808059..3bd89c4ecb5 100644 --- a/src/shared/__tests__/modes.test.ts +++ b/src/shared/__tests__/modes.test.ts @@ -344,7 +344,9 @@ describe("FileRestrictionError", () => { "You are Roo, an expert software debugger specializing in systematic problem diagnosis and resolution.", groups: ["read", "edit", "browser", "command", "mcp"], }) - expect(debugMode?.customInstructions).toContain("Reflect on 5-7 different possible sources of the problem") + expect(debugMode?.customInstructions).toContain( + "Reflect on 5-7 different possible sources of the problem, distill those down to 1-2 most likely sources, and then add logs to validate your assumptions. Explicitly ask the user to confirm the diagnosis before fixing the problem.", + ) }) }) diff --git a/src/shared/modes.ts b/src/shared/modes.ts index 7ab0dd86d66..ceb1e3e11fe 100644 --- a/src/shared/modes.ts +++ b/src/shared/modes.ts @@ -106,7 +106,7 @@ export const modes: readonly ModeConfig[] = [ "You are Roo, an expert software debugger specializing in systematic problem diagnosis and resolution.", groups: ["read", "edit", "browser", "command", "mcp"], customInstructions: - "Reflect on 5-7 different possible sources of the problem, prioritizing them based on likelihood, impact on functionality, and frequency in similar issues. Only consider sources that align with the error logs, recent code changes, and system constraints. Ignore external dependencies unless logs indicate otherwise.\n\nOnce you've narrowed it down to the 1-2 most likely sources, cross-check them against previous error logs, relevant system state, and expected behaviors. If inconsistencies arise, refine your hypothesis.\n\nWhen adding logs, ensure they are strategically placed to confirm or eliminate multiple causes. If the logs do not support your assumptions, suggest an alternative debugging strategy before proceeding.\n\nBefore implementing a fix, summarize the issue, validated assumptions, and expected log outputs that would confirm the problem is solved.", + "Reflect on 5-7 different possible sources of the problem, distill those down to 1-2 most likely sources, and then add logs to validate your assumptions. Explicitly ask the user to confirm the diagnosis before fixing the problem.", }, ] as const From b06d4b2a8a0d3557baaf217f32b17dd8f702af47 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Sat, 22 Feb 2025 05:09:52 +0000 Subject: [PATCH 023/585] changeset version bump --- .changeset/cuddly-crabs-shake.md | 5 ----- CHANGELOG.md | 6 ++++++ package-lock.json | 4 ++-- package.json | 2 +- 4 files changed, 9 insertions(+), 8 deletions(-) delete mode 100644 .changeset/cuddly-crabs-shake.md diff --git a/.changeset/cuddly-crabs-shake.md b/.changeset/cuddly-crabs-shake.md deleted file mode 100644 index 2f23536e172..00000000000 --- a/.changeset/cuddly-crabs-shake.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"roo-cline": patch ---- - -Tweak the debug prompt diff --git a/CHANGELOG.md b/CHANGELOG.md index 44fe17de115..082100f9a67 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ # Roo Code Changelog +## 3.3.26 + +### Patch Changes + +- Tweak the debug prompt + ## [3.3.25] - Add a "Debug" mode that specializes in debugging tricky problems (thanks [Ted Werbel](https://x.com/tedx_ai/status/1891514191179309457) and [Carlos E. Perez](https://x.com/IntuitMachine/status/1891516362486337739)!) diff --git a/package-lock.json b/package-lock.json index 75e033dffbe..4f7cf4246ce 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "roo-cline", - "version": "3.3.25", + "version": "3.3.26", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "roo-cline", - "version": "3.3.25", + "version": "3.3.26", "dependencies": { "@anthropic-ai/bedrock-sdk": "^0.10.2", "@anthropic-ai/sdk": "^0.26.0", diff --git a/package.json b/package.json index 34329c72da7..4a5ba593254 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.25", + "version": "3.3.26", "icon": "assets/icons/rocket.png", "galleryBanner": { "color": "#617A91", From 336b822d8e63ce6586d07dcb47917468cfffe925 Mon Sep 17 00:00:00 2001 From: R00-B0T Date: Sat, 22 Feb 2025 05:10:19 +0000 Subject: [PATCH 024/585] Updating CHANGELOG.md format --- CHANGELOG.md | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 082100f9a67..a4e67a963bc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,8 +1,6 @@ # Roo Code Changelog -## 3.3.26 - -### Patch Changes +## [3.3.26] - Tweak the debug prompt From 0f457632dfaed3a0dfddb6e36b2fcc5853ef0e23 Mon Sep 17 00:00:00 2001 From: Matt Rubens Date: Sat, 22 Feb 2025 00:12:28 -0500 Subject: [PATCH 025/585] Update CHANGELOG.md --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a4e67a963bc..748f8c78f88 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,7 +2,7 @@ ## [3.3.26] -- Tweak the debug prompt +- Adjust the default prompt for Debug mode to focus more on diagnosis and to require user confirmation before moving on to implementation ## [3.3.25] From c3c3874593bbd00e6411c566d0875c700994a061 Mon Sep 17 00:00:00 2001 From: ShayBC Date: Sat, 22 Feb 2025 23:52:13 +0200 Subject: [PATCH 026/585] subtasks alpha version (still in development) --- src/activate/registerCommands.ts | 2 +- src/core/Cline.ts | 79 ++++++- src/core/webview/ClineProvider.ts | 211 ++++++++++++------ .../webview/__tests__/ClineProvider.test.ts | 2 +- src/exports/index.ts | 2 +- 5 files changed, 218 insertions(+), 78 deletions(-) diff --git a/src/activate/registerCommands.ts b/src/activate/registerCommands.ts index 69e257e7a51..79f57b25095 100644 --- a/src/activate/registerCommands.ts +++ b/src/activate/registerCommands.ts @@ -20,7 +20,7 @@ export const registerCommands = (options: RegisterCommandOptions) => { const getCommandsMap = ({ context, outputChannel, provider }: RegisterCommandOptions) => { return { "roo-cline.plusButtonClicked": async () => { - await provider.clearTask() + await provider.removeClineFromStack() await provider.postStateToWebview() await provider.postMessageToWebview({ type: "action", action: "chatButtonClicked" }) }, diff --git a/src/core/Cline.ts b/src/core/Cline.ts index 9c2977a2669..134961129cc 100644 --- a/src/core/Cline.ts +++ b/src/core/Cline.ts @@ -75,6 +75,10 @@ type UserContent = Array< export class Cline { readonly taskId: string + // a flag that indicated if this Cline instance is a subtask (on finish return control to parent task) + private isSubTask: boolean = false + // a flag that indicated if this Cline instance is paused (waiting for provider to resume it after subtask completion) + private isPaused: boolean = false api: ApiHandler private terminalManager: TerminalManager private urlContentFetcher: UrlContentFetcher @@ -160,6 +164,12 @@ export class Cline { } } + // a helper function to set the private member isSubTask to true + // and by that set this Cline instance to be a subtask (on finish return control to parent task) + setSubTask() { + this.isSubTask = true + } + // Add method to update diffStrategy async updateDiffStrategy(experimentalDiffStrategy?: boolean) { // If not provided, get from current state @@ -480,6 +490,43 @@ export class Cline { ]) } + async resumePausedTask() { + // release this Cline instance from paused state + this.isPaused = false + + // Clear any existing ask state and simulate a completed ask response + // this.askResponse = "messageResponse"; + // this.askResponseText = "Sub Task finished Successfully!\nthere is no need to perform this task again, please continue to the next task."; + // this.askResponseImages = undefined; + // this.lastMessageTs = Date.now(); + + // This adds the completion message to conversation history + await this.say( + "text", + "Sub Task finished Successfully!\nthere is no need to perform this task again, please continue to the next task.", + ) + + // this.userMessageContent.push({ + // type: "text", + // text: `${"Result:\\n\\nSub Task finished Successfully!\nthere is no need to perform this task again, please continue to the next task."}`, + // }) + + try { + // Resume parent task + await this.ask("resume_task") + } catch (error) { + if (error.message === "Current ask promise was ignored") { + // ignore the ignored promise, since it was performed by launching a subtask and it probably took more then 1 sec, + // also set the didAlreadyUseTool flag to indicate that the tool was already used, and there is no need to relaunch it + this.didAlreadyUseTool = true + } else { + // Handle error appropriately + console.error("Failed to resume task:", error) + throw error + } + } + } + private async resumeTaskFromHistory() { const modifiedClineMessages = await this.getSavedClineMessages() @@ -2553,10 +2600,12 @@ export class Cline { const provider = this.providerRef.deref() if (provider) { await provider.handleModeSwitch(mode) - await provider.initClineWithTask(message) + await provider.initClineWithSubTask(message) pushToolResult( `Successfully created new task in ${targetMode.name} mode with message: ${message}`, ) + // pasue the current task and start the new task + this.isPaused = true } else { pushToolResult( formatResponse.toolError("Failed to create new task: provider not available"), @@ -2648,6 +2697,10 @@ export class Cline { if (lastMessage && lastMessage.ask !== "command") { // havent sent a command message yet so first send completion_result then command await this.say("completion_result", result, undefined, false) + if (this.isSubTask) { + // tell the provider to remove the current subtask and resume the previous task in the stack + this.providerRef.deref()?.finishSubTask() + } } // complete command message @@ -2665,6 +2718,10 @@ export class Cline { commandResult = execCommandResult } else { await this.say("completion_result", result, undefined, false) + if (this.isSubTask) { + // tell the provider to remove the current subtask and resume the previous task in the stack + this.providerRef.deref()?.finishSubTask() + } } // we already sent completion_result says, an empty string asks relinquishes control over button and field @@ -2740,6 +2797,20 @@ export class Cline { } } + // this function checks if this Cline instance is set to pause state and wait for being resumed, + // this is used when a sub-task is launched and the parent task is waiting for it to finish + async waitForResume() { + // wait until isPaused is false + await new Promise((resolve) => { + const interval = setInterval(() => { + if (!this.isPaused) { + clearInterval(interval) + resolve() + } + }, 1000) // TBD: the 1 sec should be added to the settings, also should add a timeout to prevent infinit wait + }) + } + async recursivelyMakeClineRequests( userContent: UserContent, includeFileDetails: boolean = false, @@ -2779,6 +2850,12 @@ export class Cline { await this.checkpointSave({ isFirst: true }) } + // in this Cline request loop, we need to check if this cline (Task) instance has been asked to wait + // for a sub-task (it has launched) to finish before continuing + if (this.isPaused) { + await this.waitForResume() + } + // getting verbose details is an expensive operation, it uses globby to top-down build file structure of project which for large projects can take a few seconds // for the best UX we show a placeholder api_req_started message with a loading spinner as this happens await this.say( diff --git a/src/core/webview/ClineProvider.ts b/src/core/webview/ClineProvider.ts index 05faa138342..d690efa9dd5 100644 --- a/src/core/webview/ClineProvider.ts +++ b/src/core/webview/ClineProvider.ts @@ -147,7 +147,7 @@ export class ClineProvider implements vscode.WebviewViewProvider { private disposables: vscode.Disposable[] = [] private view?: vscode.WebviewView | vscode.WebviewPanel private isViewLaunched = false - private cline?: Cline + private clineStack: Cline[] = [] private workspaceTracker?: WorkspaceTracker protected mcpHub?: McpHub // Change from private to protected private latestAnnouncementId = "jan-21-2025-custom-modes" // update to some unique identifier when we add a new announcement @@ -176,6 +176,55 @@ export class ClineProvider implements vscode.WebviewViewProvider { }) } + // Adds a new Cline instance to clineStack, marking the start of a new task. + // The instance is pushed to the top of the stack (LIFO order). + // When the task is completed, the top instance is removed, reactivating the previous task. + addClineToStack(cline: Cline): void { + this.clineStack.push(cline) + } + + // Removes and destroys the top Cline instance (the current finished task), activating the previous one (resuming the parent task). + async removeClineFromStack() { + // pop the top Cline instance from the stack + var clineToBeRemoved = this.clineStack.pop() + if (clineToBeRemoved) { + await clineToBeRemoved.abortTask() + // make sure no reference kept, once promises end it will be garbage collected + clineToBeRemoved = undefined + } + } + + // remove the cline object with the received clineId, and all the cline objects bove it in the stack + // for each cline object removed, pop it from the stack, abort the task and set it to undefined + async removeClineWithIdFromStack(clineId: string) { + const index = this.clineStack.findIndex((c) => c.taskId === clineId) + if (index === -1) { + return + } + for (let i = this.clineStack.length - 1; i >= index; i--) { + this.removeClineFromStack() + } + } + + // returns the current cline object in the stack (the top one) + // if the stack is empty, returns undefined + getCurrentCline(): Cline | undefined { + if (this.clineStack.length === 0) { + return undefined + } + return this.clineStack[this.clineStack.length - 1] + } + + // remove the current task/cline instance (at the top of the stack), ao this task is finished + // and resume the previous task/cline instance (if it exists) + // this is used when a sub task is finished and the parent task needs to be resumed + async finishSubTask() { + // remove the last cline instance from the stack (this is the finished sub task) + await this.removeClineFromStack() + // resume the last cline instance in the stack (if it exists - this is the 'parnt' calling task) + this.getCurrentCline()?.resumePausedTask() + } + /* VSCode extensions use the disposable pattern to clean up resources when the sidebar/editor tab is closed by the user or system. This applies to event listening, commands, interacting with the UI, etc. - https://vscode-docs.readthedocs.io/en/stable/extensions/patterns-and-principles/ @@ -183,7 +232,7 @@ export class ClineProvider implements vscode.WebviewViewProvider { */ async dispose() { this.outputChannel.appendLine("Disposing ClineProvider...") - await this.clearTask() + await this.removeClineFromStack() this.outputChannel.appendLine("Cleared task") if (this.view && "dispose" in this.view) { this.view.dispose() @@ -236,7 +285,8 @@ export class ClineProvider implements vscode.WebviewViewProvider { return false } - if (visibleProvider.cline) { + // check if there is a cline instance in the stack (if this provider has an active task) + if (visibleProvider.getCurrentCline()) { return true } @@ -267,7 +317,7 @@ export class ClineProvider implements vscode.WebviewViewProvider { return } - if (visibleProvider.cline && command.endsWith("InCurrentTask")) { + if (visibleProvider.getCurrentCline() && command.endsWith("InCurrentTask")) { await visibleProvider.postMessageToWebview({ type: "invoke", invoke: "sendMessage", @@ -303,7 +353,7 @@ export class ClineProvider implements vscode.WebviewViewProvider { return } - if (visibleProvider.cline && command.endsWith("InCurrentTask")) { + if (visibleProvider.getCurrentCline() && command.endsWith("InCurrentTask")) { await visibleProvider.postMessageToWebview({ type: "invoke", invoke: "sendMessage", @@ -392,13 +442,21 @@ export class ClineProvider implements vscode.WebviewViewProvider { ) // if the extension is starting a new session, clear previous task state - this.clearTask() + await this.removeClineFromStack() this.outputChannel.appendLine("Webview view resolved") } + // a wrapper that inits a new Cline instance (Task) ans setting it as a sub task of the current task + public async initClineWithSubTask(task?: string, images?: string[]) { + await this.initClineWithTask(task, images) + this.getCurrentCline()?.setSubTask() + } + + // when initializing a new task, (not from history but from a tool command new_task) there is no need to remove the previouse task + // since the new task is a sub task of the previous one, and when it finishes it is removed from the stack and the caller is resumed + // in this way we can have a chain of tasks, each one being a sub task of the previous one until the main task is finished public async initClineWithTask(task?: string, images?: string[]) { - await this.clearTask() const { apiConfiguration, customModePrompts, @@ -413,7 +471,7 @@ export class ClineProvider implements vscode.WebviewViewProvider { const modePrompt = customModePrompts?.[mode] as PromptComponent const effectiveInstructions = [globalInstructions, modePrompt?.customInstructions].filter(Boolean).join("\n\n") - this.cline = new Cline( + const newCline = new Cline( this, apiConfiguration, effectiveInstructions, @@ -425,10 +483,11 @@ export class ClineProvider implements vscode.WebviewViewProvider { undefined, experiments, ) + this.addClineToStack(newCline) } public async initClineWithHistoryItem(historyItem: HistoryItem) { - await this.clearTask() + await this.removeClineFromStack() const { apiConfiguration, @@ -444,7 +503,7 @@ export class ClineProvider implements vscode.WebviewViewProvider { const modePrompt = customModePrompts?.[mode] as PromptComponent const effectiveInstructions = [globalInstructions, modePrompt?.customInstructions].filter(Boolean).join("\n\n") - this.cline = new Cline( + const newCline = new Cline( this, apiConfiguration, effectiveInstructions, @@ -456,6 +515,7 @@ export class ClineProvider implements vscode.WebviewViewProvider { historyItem, experiments, ) + this.addClineToStack(newCline) } public async postMessageToWebview(message: ExtensionMessage) { @@ -810,11 +870,15 @@ export class ClineProvider implements vscode.WebviewViewProvider { await this.postStateToWebview() break case "askResponse": - this.cline?.handleWebviewAskResponse(message.askResponse!, message.text, message.images) + this.getCurrentCline()?.handleWebviewAskResponse( + message.askResponse!, + message.text, + message.images, + ) break case "clearTask": // newTask will start a new task with a given task text, while clear task resets the current session and allows for a new task to be started - await this.clearTask() + await this.removeClineFromStack() await this.postStateToWebview() break case "didShowAnnouncement": @@ -826,7 +890,7 @@ export class ClineProvider implements vscode.WebviewViewProvider { await this.postMessageToWebview({ type: "selectedImages", images }) break case "exportCurrentTask": - const currentTaskId = this.cline?.taskId + const currentTaskId = this.getCurrentCline()?.taskId if (currentTaskId) { this.exportTaskWithId(currentTaskId) } @@ -892,7 +956,7 @@ export class ClineProvider implements vscode.WebviewViewProvider { const result = checkoutDiffPayloadSchema.safeParse(message.payload) if (result.success) { - await this.cline?.checkpointDiff(result.data) + await this.getCurrentCline()?.checkpointDiff(result.data) } break @@ -903,13 +967,13 @@ export class ClineProvider implements vscode.WebviewViewProvider { await this.cancelTask() try { - await pWaitFor(() => this.cline?.isInitialized === true, { timeout: 3_000 }) + await pWaitFor(() => this.getCurrentCline()?.isInitialized === true, { timeout: 3_000 }) } catch (error) { vscode.window.showErrorMessage("Timed out when attempting to restore checkpoint.") } try { - await this.cline?.checkpointRestore(result.data) + await this.getCurrentCline()?.checkpointRestore(result.data) } catch (error) { vscode.window.showErrorMessage("Failed to restore checkpoint.") } @@ -1145,42 +1209,43 @@ export class ClineProvider implements vscode.WebviewViewProvider { ) if ( (answer === "Just this message" || answer === "This and all subsequent messages") && - this.cline && + this.getCurrentCline() && typeof message.value === "number" && message.value ) { const timeCutoff = message.value - 1000 // 1 second buffer before the message to delete - const messageIndex = this.cline.clineMessages.findIndex( - (msg) => msg.ts && msg.ts >= timeCutoff, - ) - const apiConversationHistoryIndex = this.cline.apiConversationHistory.findIndex( + const messageIndex = this.getCurrentCline()!.clineMessages.findIndex( (msg) => msg.ts && msg.ts >= timeCutoff, ) + const apiConversationHistoryIndex = + this.getCurrentCline()?.apiConversationHistory.findIndex( + (msg) => msg.ts && msg.ts >= timeCutoff, + ) if (messageIndex !== -1) { - const { historyItem } = await this.getTaskWithId(this.cline.taskId) + const { historyItem } = await this.getTaskWithId(this.getCurrentCline()!.taskId) if (answer === "Just this message") { // Find the next user message first - const nextUserMessage = this.cline.clineMessages - .slice(messageIndex + 1) + const nextUserMessage = this.getCurrentCline()! + .clineMessages.slice(messageIndex + 1) .find((msg) => msg.type === "say" && msg.say === "user_feedback") // Handle UI messages if (nextUserMessage) { // Find absolute index of next user message - const nextUserMessageIndex = this.cline.clineMessages.findIndex( + const nextUserMessageIndex = this.getCurrentCline()!.clineMessages.findIndex( (msg) => msg === nextUserMessage, ) // Keep messages before current message and after next user message - await this.cline.overwriteClineMessages([ - ...this.cline.clineMessages.slice(0, messageIndex), - ...this.cline.clineMessages.slice(nextUserMessageIndex), + await this.getCurrentCline()!.overwriteClineMessages([ + ...this.getCurrentCline()!.clineMessages.slice(0, messageIndex), + ...this.getCurrentCline()!.clineMessages.slice(nextUserMessageIndex), ]) } else { // If no next user message, keep only messages before current message - await this.cline.overwriteClineMessages( - this.cline.clineMessages.slice(0, messageIndex), + await this.getCurrentCline()!.overwriteClineMessages( + this.getCurrentCline()!.clineMessages.slice(0, messageIndex), ) } @@ -1188,30 +1253,36 @@ export class ClineProvider implements vscode.WebviewViewProvider { if (apiConversationHistoryIndex !== -1) { if (nextUserMessage && nextUserMessage.ts) { // Keep messages before current API message and after next user message - await this.cline.overwriteApiConversationHistory([ - ...this.cline.apiConversationHistory.slice( + await this.getCurrentCline()!.overwriteApiConversationHistory([ + ...this.getCurrentCline()!.apiConversationHistory.slice( 0, apiConversationHistoryIndex, ), - ...this.cline.apiConversationHistory.filter( + ...this.getCurrentCline()!.apiConversationHistory.filter( (msg) => msg.ts && msg.ts >= nextUserMessage.ts, ), ]) } else { // If no next user message, keep only messages before current API message - await this.cline.overwriteApiConversationHistory( - this.cline.apiConversationHistory.slice(0, apiConversationHistoryIndex), + await this.getCurrentCline()!.overwriteApiConversationHistory( + this.getCurrentCline()!.apiConversationHistory.slice( + 0, + apiConversationHistoryIndex, + ), ) } } } else if (answer === "This and all subsequent messages") { // Delete this message and all that follow - await this.cline.overwriteClineMessages( - this.cline.clineMessages.slice(0, messageIndex), + await this.getCurrentCline()!.overwriteClineMessages( + this.getCurrentCline()!.clineMessages.slice(0, messageIndex), ) if (apiConversationHistoryIndex !== -1) { - await this.cline.overwriteApiConversationHistory( - this.cline.apiConversationHistory.slice(0, apiConversationHistoryIndex), + await this.getCurrentCline()!.overwriteApiConversationHistory( + this.getCurrentCline()!.apiConversationHistory.slice( + 0, + apiConversationHistoryIndex, + ), ) } } @@ -1481,8 +1552,8 @@ export class ClineProvider implements vscode.WebviewViewProvider { await this.updateGlobalState("experiments", updatedExperiments) // Update diffStrategy in current Cline instance if it exists - if (message.values[EXPERIMENT_IDS.DIFF_STRATEGY] !== undefined && this.cline) { - await this.cline.updateDiffStrategy( + if (message.values[EXPERIMENT_IDS.DIFF_STRATEGY] !== undefined && this.getCurrentCline()) { + await this.getCurrentCline()!.updateDiffStrategy( Experiments.isEnabled(updatedExperiments, EXPERIMENT_IDS.DIFF_STRATEGY), ) } @@ -1724,25 +1795,25 @@ export class ClineProvider implements vscode.WebviewViewProvider { this.updateGlobalState("requestyModelInfo", requestyModelInfo), this.updateGlobalState("modelTemperature", modelTemperature), ]) - if (this.cline) { - this.cline.api = buildApiHandler(apiConfiguration) + if (this.getCurrentCline()) { + this.getCurrentCline()!.api = buildApiHandler(apiConfiguration) } } async cancelTask() { - if (this.cline) { - const { historyItem } = await this.getTaskWithId(this.cline.taskId) - this.cline.abortTask() + if (this.getCurrentCline()) { + const { historyItem } = await this.getTaskWithId(this.getCurrentCline()!.taskId) + this.getCurrentCline()!.abortTask() await pWaitFor( () => - this.cline === undefined || - this.cline.isStreaming === false || - this.cline.didFinishAbortingStream || + this.getCurrentCline()! === undefined || + this.getCurrentCline()!.isStreaming === false || + this.getCurrentCline()!.didFinishAbortingStream || // If only the first chunk is processed, then there's no // need to wait for graceful abort (closes edits, browser, // etc). - this.cline.isWaitingForFirstChunk, + this.getCurrentCline()!.isWaitingForFirstChunk, { timeout: 3_000, }, @@ -1750,11 +1821,11 @@ export class ClineProvider implements vscode.WebviewViewProvider { console.error("Failed to abort task") }) - if (this.cline) { + if (this.getCurrentCline()) { // 'abandoned' will prevent this Cline instance from affecting // future Cline instances. This may happen if its hanging on a // streaming request. - this.cline.abandoned = true + this.getCurrentCline()!.abandoned = true } // Clears task again, so we need to abortTask manually above. @@ -1765,8 +1836,8 @@ export class ClineProvider implements vscode.WebviewViewProvider { async updateCustomInstructions(instructions?: string) { // User may be clearing the field await this.updateGlobalState("customInstructions", instructions || undefined) - if (this.cline) { - this.cline.customInstructions = instructions || undefined + if (this.getCurrentCline()) { + this.getCurrentCline()!.customInstructions = instructions || undefined } await this.postStateToWebview() } @@ -1980,8 +2051,8 @@ export class ClineProvider implements vscode.WebviewViewProvider { await this.updateGlobalState("apiProvider", openrouter) await this.storeSecret("openRouterApiKey", apiKey) await this.postStateToWebview() - if (this.cline) { - this.cline.api = buildApiHandler({ apiProvider: openrouter, openRouterApiKey: apiKey }) + if (this.getCurrentCline()) { + this.getCurrentCline()!.api = buildApiHandler({ apiProvider: openrouter, openRouterApiKey: apiKey }) } // await this.postMessageToWebview({ type: "action", action: "settingsButtonClicked" }) // bad ux if user is on welcome } @@ -2012,8 +2083,8 @@ export class ClineProvider implements vscode.WebviewViewProvider { await this.updateGlobalState("apiProvider", glama) await this.storeSecret("glamaApiKey", apiKey) await this.postStateToWebview() - if (this.cline) { - this.cline.api = buildApiHandler({ + if (this.getCurrentCline()) { + this.getCurrentCline()!.api = buildApiHandler({ apiProvider: glama, glamaApiKey: apiKey, }) @@ -2295,7 +2366,7 @@ export class ClineProvider implements vscode.WebviewViewProvider { } async showTaskWithId(id: string) { - if (id !== this.cline?.taskId) { + if (id !== this.getCurrentCline()?.taskId) { // non-current task const { historyItem } = await this.getTaskWithId(id) await this.initClineWithHistoryItem(historyItem) // clears existing task @@ -2309,8 +2380,8 @@ export class ClineProvider implements vscode.WebviewViewProvider { } async deleteTaskWithId(id: string) { - if (id === this.cline?.taskId) { - await this.clearTask() + if (id === this.getCurrentCline()?.taskId) { + await this.removeClineWithIdFromStack(id) } const { taskDirPath, apiConversationHistoryFilePath, uiMessagesFilePath } = await this.getTaskWithId(id) @@ -2434,10 +2505,10 @@ export class ClineProvider implements vscode.WebviewViewProvider { alwaysAllowMcp: alwaysAllowMcp ?? false, alwaysAllowModeSwitch: alwaysAllowModeSwitch ?? false, uriScheme: vscode.env.uriScheme, - currentTaskItem: this.cline?.taskId - ? (taskHistory || []).find((item) => item.id === this.cline?.taskId) + currentTaskItem: this.getCurrentCline()?.taskId + ? (taskHistory || []).find((item) => item.id === this.getCurrentCline()?.taskId) : undefined, - clineMessages: this.cline?.clineMessages || [], + clineMessages: this.getCurrentCline()?.clineMessages || [], taskHistory: (taskHistory || []) .filter((item: HistoryItem) => item.ts && item.task) .sort((a: HistoryItem, b: HistoryItem) => b.ts - a.ts), @@ -2472,11 +2543,6 @@ export class ClineProvider implements vscode.WebviewViewProvider { } } - async clearTask() { - this.cline?.abortTask() - this.cline = undefined // removes reference to it, so once promises end it will be garbage collected - } - // Caching mechanism to keep track of webview messages + API conversation history per provider instance /* @@ -2914,10 +2980,7 @@ export class ClineProvider implements vscode.WebviewViewProvider { } await this.configManager.resetAllConfigs() await this.customModesManager.resetCustomModes() - if (this.cline) { - this.cline.abortTask() - this.cline = undefined - } + await this.removeClineFromStack() await this.postStateToWebview() await this.postMessageToWebview({ type: "action", action: "chatButtonClicked" }) } @@ -2935,7 +2998,7 @@ export class ClineProvider implements vscode.WebviewViewProvider { } get messages() { - return this.cline?.clineMessages || [] + return this.getCurrentCline()?.clineMessages || [] } // Add public getter diff --git a/src/core/webview/__tests__/ClineProvider.test.ts b/src/core/webview/__tests__/ClineProvider.test.ts index 0a8f73308f5..15d0eff7086 100644 --- a/src/core/webview/__tests__/ClineProvider.test.ts +++ b/src/core/webview/__tests__/ClineProvider.test.ts @@ -381,7 +381,7 @@ describe("ClineProvider", () => { // @ts-ignore - accessing private property for testing provider.cline = { abortTask: mockAbortTask } - await provider.clearTask() + await provider.removeClineFromStack() expect(mockAbortTask).toHaveBeenCalled() // @ts-ignore - accessing private property for testing diff --git a/src/exports/index.ts b/src/exports/index.ts index a0680b04829..e4b17da4844 100644 --- a/src/exports/index.ts +++ b/src/exports/index.ts @@ -15,7 +15,7 @@ export function createClineAPI(outputChannel: vscode.OutputChannel, sidebarProvi startNewTask: async (task?: string, images?: string[]) => { outputChannel.appendLine("Starting new task") - await sidebarProvider.clearTask() + await sidebarProvider.removeClineFromStack() await sidebarProvider.postStateToWebview() await sidebarProvider.postMessageToWebview({ type: "action", action: "chatButtonClicked" }) await sidebarProvider.postMessageToWebview({ From 24d52809d73ed5662d54eb987f9c10b99b706082 Mon Sep 17 00:00:00 2001 From: Roo Code Date: Sat, 22 Feb 2025 16:38:46 -0700 Subject: [PATCH 027/585] Reorder tool groups and update tool usage instructions to always mention apply_diff before write_to_file --- src/core/prompts/sections/capabilities.ts | 2 +- src/core/prompts/sections/modes.ts | 2 +- src/core/prompts/sections/rules.ts | 19 ++++++++++++------- src/shared/tool-groups.ts | 2 +- 4 files changed, 15 insertions(+), 10 deletions(-) diff --git a/src/core/prompts/sections/capabilities.ts b/src/core/prompts/sections/capabilities.ts index c292eeffbc3..983d07bf761 100644 --- a/src/core/prompts/sections/capabilities.ts +++ b/src/core/prompts/sections/capabilities.ts @@ -17,7 +17,7 @@ CAPABILITIES - When the user initially gives you a task, a recursive list of all filepaths in the current working directory ('${cwd}') will be included in environment_details. This provides an overview of the project's file structure, offering key insights into the project from directory/file names (how developers conceptualize and organize their code) and file extensions (the language used). This can also guide decision-making on which files to explore further. If you need to further explore directories such as outside the current working directory, you can use the list_files tool. If you pass 'true' for the recursive parameter, it will list files recursively. Otherwise, it will list files at the top level, which is better suited for generic directories where you don't necessarily need the nested structure, like the Desktop. - You can use search_files to perform regex searches across files in a specified directory, outputting context-rich results that include surrounding lines. This is particularly useful for understanding code patterns, finding specific implementations, or identifying areas that need refactoring. - You can use the list_code_definition_names tool to get an overview of source code definitions for all files at the top level of a specified directory. This can be particularly useful when you need to understand the broader context and relationships between certain parts of the code. You may need to call this tool multiple times to understand various parts of the codebase related to the task. - - For example, when asked to make edits or improvements you might analyze the file structure in the initial environment_details to get an overview of the project, then use list_code_definition_names to get further insight using source code definitions for files located in relevant directories, then read_file to examine the contents of relevant files, analyze the code and suggest improvements or make necessary edits, then use the write_to_file${diffStrategy ? " or apply_diff" : ""} tool to apply the changes. If you refactored code that could affect other parts of the codebase, you could use search_files to ensure you update other files as needed. + - For example, when asked to make edits or improvements you might analyze the file structure in the initial environment_details to get an overview of the project, then use list_code_definition_names to get further insight using source code definitions for files located in relevant directories, then read_file to examine the contents of relevant files, analyze the code and suggest improvements or make necessary edits, then use ${diffStrategy ? "the apply_diff or write_to_file" : "the write_to_file"} tool to apply the changes. If you refactored code that could affect other parts of the codebase, you could use search_files to ensure you update other files as needed. - You can use the execute_command tool to run commands on the user's computer whenever you feel it can help accomplish the user's task. When you need to execute a CLI command, you must provide a clear explanation of what the command does. Prefer to execute complex CLI commands over creating executable scripts, since they are more flexible and easier to run. Interactive and long-running commands are allowed, since the commands are run in the user's VSCode terminal. The user may keep commands running in the background and you will be kept updated on their status along the way. Each command you execute is run in a new terminal instance.${ supportsComputerUse ? "\n- You can use the browser_action tool to interact with websites (including html files and locally running development servers) through a Puppeteer-controlled browser when you feel it is necessary in accomplishing the user's task. This tool is particularly useful for web development tasks as it allows you to launch a browser, navigate to pages, interact with elements through clicks and keyboard input, and capture the results through screenshots and console logs. This tool may be useful at key stages of web development tasks-such as after implementing new features, making substantial changes, when troubleshooting issues, or to verify the result of your work. You can analyze the provided screenshots to ensure correct rendering or identify errors, and review console logs for runtime issues.\n - For example, if asked to add a component to a react website, you might create the necessary files, use execute_command to run the site locally, then use browser_action to launch the browser, navigate to the local server, and verify the component renders & functions correctly before closing the browser." diff --git a/src/core/prompts/sections/modes.ts b/src/core/prompts/sections/modes.ts index eff950c2c2f..f3863870dbc 100644 --- a/src/core/prompts/sections/modes.ts +++ b/src/core/prompts/sections/modes.ts @@ -45,7 +45,7 @@ Both files should follow this structure: "roleDefinition": "You are Roo, a UI/UX expert specializing in design systems and frontend development. Your expertise includes:\\n- Creating and maintaining design systems\\n- Implementing responsive and accessible web interfaces\\n- Working with CSS, HTML, and modern frontend frameworks\\n- Ensuring consistent user experiences across platforms", // Required: non-empty "groups": [ // Required: array of tool groups (can be empty) "read", // Read files group (read_file, search_files, list_files, list_code_definition_names) - "edit", // Edit files group (write_to_file, apply_diff) - allows editing any file + "edit", // Edit files group (apply_diff, write_to_file) - allows editing any file // Or with file restrictions: // ["edit", { fileRegex: "\\.md$", description: "Markdown files only" }], // Edit group that only allows editing markdown files "browser", // Browser group (browser_action) diff --git a/src/core/prompts/sections/rules.ts b/src/core/prompts/sections/rules.ts index b6e19eb08c9..86e554a157e 100644 --- a/src/core/prompts/sections/rules.ts +++ b/src/core/prompts/sections/rules.ts @@ -5,11 +5,16 @@ import * as path from "path" function getEditingInstructions(diffStrategy?: DiffStrategy, experiments?: Record): string { const instructions: string[] = [] - const availableTools: string[] = ["write_to_file (for creating new files or complete file rewrites)"] + const availableTools: string[] = [] // Collect available editing tools if (diffStrategy) { - availableTools.push("apply_diff (for replacing lines in existing files)") + availableTools.push( + "apply_diff (for replacing lines in existing files)", + "write_to_file (for creating new files or complete file rewrites)", + ) + } else { + availableTools.push("write_to_file (for creating new files or complete file rewrites)") } if (experiments?.["insert_content"]) { availableTools.push("insert_content (for adding lines to existing files)") @@ -36,16 +41,16 @@ function getEditingInstructions(diffStrategy?: DiffStrategy, experiments?: Recor ) } - instructions.push( - "- When using the write_to_file tool to modify a file, use the tool directly with the desired content. You do not need to display the content before using the tool. ALWAYS provide the COMPLETE file content in your response. This is NON-NEGOTIABLE. Partial updates or placeholders like '// rest of code unchanged' are STRICTLY FORBIDDEN. You MUST include ALL parts of the file, even if they haven't been modified. Failure to do so will result in incomplete or broken code, severely impacting the user's project.", - ) - if (availableTools.length > 1) { instructions.push( "- You should always prefer using other editing tools over write_to_file when making changes to existing files since write_to_file is much slower and cannot handle large files.", ) } + instructions.push( + "- When using the write_to_file tool to modify a file, use the tool directly with the desired content. You do not need to display the content before using the tool. ALWAYS provide the COMPLETE file content in your response. This is NON-NEGOTIABLE. Partial updates or placeholders like '// rest of code unchanged' are STRICTLY FORBIDDEN. You MUST include ALL parts of the file, even if they haven't been modified. Failure to do so will result in incomplete or broken code, severely impacting the user's project.", + ) + return instructions.join("\n") } @@ -63,7 +68,7 @@ RULES - You cannot \`cd\` into a different directory to complete a task. You are stuck operating from '${cwd.toPosix()}', so be sure to pass in the correct 'path' parameter when using tools that require a path. - Do not use the ~ character or $HOME to refer to the home directory. - Before using the execute_command tool, you must first think about the SYSTEM INFORMATION context provided to understand the user's environment and tailor your commands to ensure they are compatible with their system. You must also consider if the command you need to run should be executed in a specific directory outside of the current working directory '${cwd.toPosix()}', and if so prepend with \`cd\`'ing into that directory && then executing the command (as one command since you are stuck operating from '${cwd.toPosix()}'). For example, if you needed to run \`npm install\` in a project outside of '${cwd.toPosix()}', you would need to prepend with a \`cd\` i.e. pseudocode for this would be \`cd (path to project) && (command, in this case npm install)\`. -- When using the search_files tool, craft your regex patterns carefully to balance specificity and flexibility. Based on the user's task you may use it to find code patterns, TODO comments, function definitions, or any text-based information across the project. The results include context, so analyze the surrounding code to better understand the matches. Leverage the search_files tool in combination with other tools for more comprehensive analysis. For example, use it to find specific code patterns, then use read_file to examine the full context of interesting matches before using write_to_file to make informed changes. +- When using the search_files tool, craft your regex patterns carefully to balance specificity and flexibility. Based on the user's task you may use it to find code patterns, TODO comments, function definitions, or any text-based information across the project. The results include context, so analyze the surrounding code to better understand the matches. Leverage the search_files tool in combination with other tools for more comprehensive analysis. For example, use it to find specific code patterns, then use read_file to examine the full context of interesting matches before using ${diffStrategy ? "apply_diff or write_to_file" : "write_to_file"} to make informed changes. - When creating a new project (such as an app, website, or any software project), organize all new files within a dedicated project directory unless the user specifies otherwise. Use appropriate file paths when writing files, as the write_to_file tool will automatically create any necessary directories. Structure the project logically, adhering to best practices for the specific type of project being created. Unless otherwise specified, new projects should be easily run without additional setup, for example most projects can be built in HTML, CSS, and JavaScript - which you can open in a browser. ${getEditingInstructions(diffStrategy, experiments)} - Some modes have restrictions on which files they can edit. If you attempt to edit a restricted file, the operation will be rejected with a FileRestrictionError that will specify which file patterns are allowed for the current mode. diff --git a/src/shared/tool-groups.ts b/src/shared/tool-groups.ts index 2728d42319d..50c7b80ca9e 100644 --- a/src/shared/tool-groups.ts +++ b/src/shared/tool-groups.ts @@ -28,7 +28,7 @@ export const TOOL_GROUPS: Record = { tools: ["read_file", "search_files", "list_files", "list_code_definition_names"], }, edit: { - tools: ["write_to_file", "apply_diff", "insert_content", "search_and_replace"], + tools: ["apply_diff", "write_to_file", "insert_content", "search_and_replace"], }, browser: { tools: ["browser_action"], From e134e544a69a6b1d545fc79ad381334a8594a3cb Mon Sep 17 00:00:00 2001 From: Roo Code Date: Sat, 22 Feb 2025 18:30:10 -0700 Subject: [PATCH 028/585] Rename 'apply_diff' tool to 'edit_file' across codebase --- src/core/Cline.ts | 16 ++++----- src/core/__tests__/mode-validator.test.ts | 34 +++++++++---------- src/core/assistant-message/index.ts | 2 +- .../strategies/__tests__/new-unified.test.ts | 2 +- .../__tests__/search-replace.test.ts | 4 +-- .../diff/strategies/__tests__/unified.test.ts | 2 +- src/core/diff/strategies/new-unified/index.ts | 6 ++-- src/core/diff/strategies/search-replace.ts | 6 ++-- src/core/diff/strategies/unified.ts | 6 ++-- .../__snapshots__/system.test.ts.snap | 10 +++--- src/core/prompts/__tests__/sections.test.ts | 14 ++++---- src/core/prompts/__tests__/system.test.ts | 8 ++--- src/core/prompts/sections/capabilities.ts | 2 +- src/core/prompts/sections/mcp-servers.ts | 2 +- src/core/prompts/sections/modes.ts | 2 +- src/core/prompts/sections/rules.ts | 4 +-- src/core/prompts/tools/index.ts | 2 +- .../webview/__tests__/ClineProvider.test.ts | 2 +- src/shared/ExtensionMessage.ts | 2 +- src/shared/__tests__/modes.test.ts | 18 +++++----- src/shared/tool-groups.ts | 4 +-- 21 files changed, 74 insertions(+), 74 deletions(-) diff --git a/src/core/Cline.ts b/src/core/Cline.ts index 560609a109a..8c2924b3f49 100644 --- a/src/core/Cline.ts +++ b/src/core/Cline.ts @@ -1104,7 +1104,7 @@ export class Cline { return `[${block.name} for '${block.params.path}']` case "write_to_file": return `[${block.name} for '${block.params.path}']` - case "apply_diff": + case "edit_file": return `[${block.name} for '${block.params.path}']` case "search_files": return `[${block.name} for '${block.params.regex}'${ @@ -1256,7 +1256,7 @@ export class Cline { mode ?? defaultModeSlug, customModes ?? [], { - apply_diff: this.diffEnabled, + edit_file: this.diffEnabled, }, block.params, ) @@ -1382,7 +1382,7 @@ export class Cline { formatResponse.toolError( `Content appears to be truncated (file has ${ newContent.split("\n").length - } lines but was predicted to have ${predictedLineCount} lines), and found comments indicating omitted code (e.g., '// rest of code unchanged', '/* previous code */'). Please provide the complete file content without any omissions if possible, or otherwise use the 'apply_diff' tool to apply the diff to the original file.`, + } lines but was predicted to have ${predictedLineCount} lines), and found comments indicating omitted code (e.g., '// rest of code unchanged', '/* previous code */'). Please provide the complete file content without any omissions if possible, or otherwise use the 'edit_file' tool to apply the diff to the original file.`, ), ) break @@ -1458,7 +1458,7 @@ export class Cline { break } } - case "apply_diff": { + case "edit_file": { const relPath: string | undefined = block.params.path const diffContent: string | undefined = block.params.diff @@ -1476,12 +1476,12 @@ export class Cline { } else { if (!relPath) { this.consecutiveMistakeCount++ - pushToolResult(await this.sayAndCreateMissingParamError("apply_diff", "path")) + pushToolResult(await this.sayAndCreateMissingParamError("edit_file", "path")) break } if (!diffContent) { this.consecutiveMistakeCount++ - pushToolResult(await this.sayAndCreateMissingParamError("apply_diff", "diff")) + pushToolResult(await this.sayAndCreateMissingParamError("edit_file", "diff")) break } @@ -3262,9 +3262,9 @@ export class Cline { // Add warning if not in code mode if ( !isToolAllowedForMode("write_to_file", currentMode, customModes ?? [], { - apply_diff: this.diffEnabled, + edit_file: this.diffEnabled, }) && - !isToolAllowedForMode("apply_diff", currentMode, customModes ?? [], { apply_diff: this.diffEnabled }) + !isToolAllowedForMode("edit_file", currentMode, customModes ?? [], { edit_file: this.diffEnabled }) ) { const currentModeName = getModeBySlug(currentMode, customModes)?.name ?? currentMode const defaultModeName = getModeBySlug(defaultModeSlug, customModes)?.name ?? defaultModeSlug diff --git a/src/core/__tests__/mode-validator.test.ts b/src/core/__tests__/mode-validator.test.ts index 632ca8a8ab0..a10da470bd5 100644 --- a/src/core/__tests__/mode-validator.test.ts +++ b/src/core/__tests__/mode-validator.test.ts @@ -88,10 +88,10 @@ describe("mode-validator", () => { groups: ["edit"] as const, }, ] - const requirements = { apply_diff: false } + const requirements = { edit_file: false } // Should respect disabled requirement even if tool group is allowed - expect(isToolAllowedForMode("apply_diff", "custom-mode", customModes, requirements)).toBe(false) + expect(isToolAllowedForMode("edit_file", "custom-mode", customModes, requirements)).toBe(false) // Should allow other edit tools expect(isToolAllowedForMode("write_to_file", "custom-mode", customModes, requirements)).toBe(true) @@ -100,27 +100,27 @@ describe("mode-validator", () => { describe("tool requirements", () => { it("respects tool requirements when provided", () => { - const requirements = { apply_diff: false } - expect(isToolAllowedForMode("apply_diff", codeMode, [], requirements)).toBe(false) + const requirements = { edit_file: false } + expect(isToolAllowedForMode("edit_file", codeMode, [], requirements)).toBe(false) - const enabledRequirements = { apply_diff: true } - expect(isToolAllowedForMode("apply_diff", codeMode, [], enabledRequirements)).toBe(true) + const enabledRequirements = { edit_file: true } + expect(isToolAllowedForMode("edit_file", codeMode, [], enabledRequirements)).toBe(true) }) it("allows tools when their requirements are not specified", () => { const requirements = { some_other_tool: true } - expect(isToolAllowedForMode("apply_diff", codeMode, [], requirements)).toBe(true) + expect(isToolAllowedForMode("edit_file", codeMode, [], requirements)).toBe(true) }) it("handles undefined and empty requirements", () => { - expect(isToolAllowedForMode("apply_diff", codeMode, [], undefined)).toBe(true) - expect(isToolAllowedForMode("apply_diff", codeMode, [], {})).toBe(true) + expect(isToolAllowedForMode("edit_file", codeMode, [], undefined)).toBe(true) + expect(isToolAllowedForMode("edit_file", codeMode, [], {})).toBe(true) }) it("prioritizes requirements over mode configuration", () => { - const requirements = { apply_diff: false } + const requirements = { edit_file: false } // Even in code mode which allows all tools, disabled requirement should take precedence - expect(isToolAllowedForMode("apply_diff", codeMode, [], requirements)).toBe(false) + expect(isToolAllowedForMode("edit_file", codeMode, [], requirements)).toBe(false) }) }) }) @@ -137,19 +137,19 @@ describe("mode-validator", () => { }) it("throws error when tool requirement is not met", () => { - const requirements = { apply_diff: false } - expect(() => validateToolUse("apply_diff", codeMode, [], requirements)).toThrow( - 'Tool "apply_diff" is not allowed in code mode.', + const requirements = { edit_file: false } + expect(() => validateToolUse("edit_file", codeMode, [], requirements)).toThrow( + 'Tool "edit_file" is not allowed in code mode.', ) }) it("does not throw when tool requirement is met", () => { - const requirements = { apply_diff: true } - expect(() => validateToolUse("apply_diff", codeMode, [], requirements)).not.toThrow() + const requirements = { edit_file: true } + expect(() => validateToolUse("edit_file", codeMode, [], requirements)).not.toThrow() }) it("handles undefined requirements gracefully", () => { - expect(() => validateToolUse("apply_diff", codeMode, [], undefined)).not.toThrow() + expect(() => validateToolUse("edit_file", codeMode, [], undefined)).not.toThrow() }) }) }) diff --git a/src/core/assistant-message/index.ts b/src/core/assistant-message/index.ts index f1c49f85ab7..c67c85933cb 100644 --- a/src/core/assistant-message/index.ts +++ b/src/core/assistant-message/index.ts @@ -12,7 +12,7 @@ export const toolUseNames = [ "execute_command", "read_file", "write_to_file", - "apply_diff", + "edit_file", "insert_content", "search_and_replace", "search_files", diff --git a/src/core/diff/strategies/__tests__/new-unified.test.ts b/src/core/diff/strategies/__tests__/new-unified.test.ts index 8832f9e7c08..9d30cece7e4 100644 --- a/src/core/diff/strategies/__tests__/new-unified.test.ts +++ b/src/core/diff/strategies/__tests__/new-unified.test.ts @@ -29,7 +29,7 @@ describe("main", () => { const cwd = "/test/path" const description = strategy.getToolDescription({ cwd }) - expect(description).toContain("apply_diff Tool - Generate Precise Code Changes") + expect(description).toContain("edit_file Tool - Generate Precise Code Changes") expect(description).toContain(cwd) expect(description).toContain("Step-by-Step Instructions") expect(description).toContain("Requirements") diff --git a/src/core/diff/strategies/__tests__/search-replace.test.ts b/src/core/diff/strategies/__tests__/search-replace.test.ts index cd71edac475..723beee23a7 100644 --- a/src/core/diff/strategies/__tests__/search-replace.test.ts +++ b/src/core/diff/strategies/__tests__/search-replace.test.ts @@ -1544,8 +1544,8 @@ function two() { expect(description).toContain("<<<<<<< SEARCH") expect(description).toContain("=======") expect(description).toContain(">>>>>>> REPLACE") - expect(description).toContain("") - expect(description).toContain("") + expect(description).toContain("") + expect(description).toContain("") }) it("should document start_line and end_line parameters", async () => { diff --git a/src/core/diff/strategies/__tests__/unified.test.ts b/src/core/diff/strategies/__tests__/unified.test.ts index 1d9847b3c51..ae7860869bb 100644 --- a/src/core/diff/strategies/__tests__/unified.test.ts +++ b/src/core/diff/strategies/__tests__/unified.test.ts @@ -12,7 +12,7 @@ describe("UnifiedDiffStrategy", () => { const cwd = "/test/path" const description = strategy.getToolDescription({ cwd }) - expect(description).toContain("apply_diff") + expect(description).toContain("edit_file") expect(description).toContain(cwd) expect(description).toContain("Parameters:") expect(description).toContain("Format Requirements:") diff --git a/src/core/diff/strategies/new-unified/index.ts b/src/core/diff/strategies/new-unified/index.ts index d82a05a1045..df130ffaca6 100644 --- a/src/core/diff/strategies/new-unified/index.ts +++ b/src/core/diff/strategies/new-unified/index.ts @@ -108,7 +108,7 @@ export class NewUnifiedDiffStrategy implements DiffStrategy { } getToolDescription(args: { cwd: string; toolOptions?: { [key: string]: string } }): string { - return `# apply_diff Tool - Generate Precise Code Changes + return `# edit_file Tool - Generate Precise Code Changes Generate a unified diff that can be cleanly applied to modify code files. @@ -168,12 +168,12 @@ Parameters: - diff: (required) Unified diff content in unified format to apply to the file. Usage: - + path/to/file.ext Your diff here -` +` } // Helper function to split a hunk into smaller hunks based on contiguous changes diff --git a/src/core/diff/strategies/search-replace.ts b/src/core/diff/strategies/search-replace.ts index a9bf46758de..c8d4f22c8d1 100644 --- a/src/core/diff/strategies/search-replace.ts +++ b/src/core/diff/strategies/search-replace.ts @@ -40,7 +40,7 @@ export class SearchReplaceDiffStrategy implements DiffStrategy { } getToolDescription(args: { cwd: string; toolOptions?: { [key: string]: string } }): string { - return `## apply_diff + return `## edit_file Description: Request to replace existing code using a search and replace block. This tool allows for precise, surgical replaces to files by specifying exactly what content to search for and what to replace it with. The tool will maintain proper indentation and formatting while making changes. @@ -91,14 +91,14 @@ def calculate_total(items): \`\`\` Usage: - + File path here Your search/replace content here 1 5 -` +` } async applyDiff( diff --git a/src/core/diff/strategies/unified.ts b/src/core/diff/strategies/unified.ts index f1cdb3b5849..5947391df69 100644 --- a/src/core/diff/strategies/unified.ts +++ b/src/core/diff/strategies/unified.ts @@ -3,7 +3,7 @@ import { DiffStrategy, DiffResult } from "../types" export class UnifiedDiffStrategy implements DiffStrategy { getToolDescription(args: { cwd: string; toolOptions?: { [key: string]: string } }): string { - return `## apply_diff + return `## edit_file Description: Apply a unified diff to a file at the specified path. This tool is useful when you need to make specific modifications to a file based on a set of changes provided in unified diff format (diff -U3). Parameters: @@ -100,12 +100,12 @@ Best Practices: 4. Verify line numbers match the line numbers you have in the file Usage: - + File path here Your diff here -` +` } async applyDiff(originalContent: string, diffContent: string): Promise { diff --git a/src/core/prompts/__tests__/__snapshots__/system.test.ts.snap b/src/core/prompts/__tests__/__snapshots__/system.test.ts.snap index 2abc6138619..b88ff5b1c69 100644 --- a/src/core/prompts/__tests__/__snapshots__/system.test.ts.snap +++ b/src/core/prompts/__tests__/__snapshots__/system.test.ts.snap @@ -2580,7 +2580,7 @@ Example: Requesting to write to frontend-config.json 14 -## apply_diff +## edit_file Description: Request to replace existing code using a search and replace block. This tool allows for precise, surgical replaces to files by specifying exactly what content to search for and what to replace it with. The tool will maintain proper indentation and formatting while making changes. @@ -2631,14 +2631,14 @@ def calculate_total(items): \`\`\` Usage: - + File path here Your search/replace content here 1 5 - + ## execute_command Description: Request to execute a CLI command on the system. Use this when you need to perform system operations or run specific commands to accomplish any step in the user's task. You must tailor your command to the user's system and provide a clear explanation of what the command does. For command chaining, use the appropriate chaining syntax for the user's shell. Prefer to execute complex CLI commands over creating executable scripts, as they are more flexible and easier to run. Commands will be executed in the current working directory: /test/path @@ -2758,7 +2758,7 @@ CAPABILITIES - When the user initially gives you a task, a recursive list of all filepaths in the current working directory ('/test/path') will be included in environment_details. This provides an overview of the project's file structure, offering key insights into the project from directory/file names (how developers conceptualize and organize their code) and file extensions (the language used). This can also guide decision-making on which files to explore further. If you need to further explore directories such as outside the current working directory, you can use the list_files tool. If you pass 'true' for the recursive parameter, it will list files recursively. Otherwise, it will list files at the top level, which is better suited for generic directories where you don't necessarily need the nested structure, like the Desktop. - You can use search_files to perform regex searches across files in a specified directory, outputting context-rich results that include surrounding lines. This is particularly useful for understanding code patterns, finding specific implementations, or identifying areas that need refactoring. - You can use the list_code_definition_names tool to get an overview of source code definitions for all files at the top level of a specified directory. This can be particularly useful when you need to understand the broader context and relationships between certain parts of the code. You may need to call this tool multiple times to understand various parts of the codebase related to the task. - - For example, when asked to make edits or improvements you might analyze the file structure in the initial environment_details to get an overview of the project, then use list_code_definition_names to get further insight using source code definitions for files located in relevant directories, then read_file to examine the contents of relevant files, analyze the code and suggest improvements or make necessary edits, then use the write_to_file or apply_diff tool to apply the changes. If you refactored code that could affect other parts of the codebase, you could use search_files to ensure you update other files as needed. + - For example, when asked to make edits or improvements you might analyze the file structure in the initial environment_details to get an overview of the project, then use list_code_definition_names to get further insight using source code definitions for files located in relevant directories, then read_file to examine the contents of relevant files, analyze the code and suggest improvements or make necessary edits, then use the write_to_file or edit_file tool to apply the changes. If you refactored code that could affect other parts of the codebase, you could use search_files to ensure you update other files as needed. - You can use the execute_command tool to run commands on the user's computer whenever you feel it can help accomplish the user's task. When you need to execute a CLI command, you must provide a clear explanation of what the command does. Prefer to execute complex CLI commands over creating executable scripts, since they are more flexible and easier to run. Interactive and long-running commands are allowed, since the commands are run in the user's VSCode terminal. The user may keep commands running in the background and you will be kept updated on their status along the way. Each command you execute is run in a new terminal instance. ==== @@ -2777,7 +2777,7 @@ RULES - Before using the execute_command tool, you must first think about the SYSTEM INFORMATION context provided to understand the user's environment and tailor your commands to ensure they are compatible with their system. You must also consider if the command you need to run should be executed in a specific directory outside of the current working directory '/test/path', and if so prepend with \`cd\`'ing into that directory && then executing the command (as one command since you are stuck operating from '/test/path'). For example, if you needed to run \`npm install\` in a project outside of '/test/path', you would need to prepend with a \`cd\` i.e. pseudocode for this would be \`cd (path to project) && (command, in this case npm install)\`. - When using the search_files tool, craft your regex patterns carefully to balance specificity and flexibility. Based on the user's task you may use it to find code patterns, TODO comments, function definitions, or any text-based information across the project. The results include context, so analyze the surrounding code to better understand the matches. Leverage the search_files tool in combination with other tools for more comprehensive analysis. For example, use it to find specific code patterns, then use read_file to examine the full context of interesting matches before using write_to_file to make informed changes. - When creating a new project (such as an app, website, or any software project), organize all new files within a dedicated project directory unless the user specifies otherwise. Use appropriate file paths when writing files, as the write_to_file tool will automatically create any necessary directories. Structure the project logically, adhering to best practices for the specific type of project being created. Unless otherwise specified, new projects should be easily run without additional setup, for example most projects can be built in HTML, CSS, and JavaScript - which you can open in a browser. -- For editing files, you have access to these tools: write_to_file (for creating new files or complete file rewrites), apply_diff (for replacing lines in existing files). +- For editing files, you have access to these tools: write_to_file (for creating new files or complete file rewrites), edit_file (for replacing lines in existing files). - When using the write_to_file tool to modify a file, use the tool directly with the desired content. You do not need to display the content before using the tool. ALWAYS provide the COMPLETE file content in your response. This is NON-NEGOTIABLE. Partial updates or placeholders like '// rest of code unchanged' are STRICTLY FORBIDDEN. You MUST include ALL parts of the file, even if they haven't been modified. Failure to do so will result in incomplete or broken code, severely impacting the user's project. - You should always prefer using other editing tools over write_to_file when making changes to existing files since write_to_file is much slower and cannot handle large files. - Some modes have restrictions on which files they can edit. If you attempt to edit a restricted file, the operation will be rejected with a FileRestrictionError that will specify which file patterns are allowed for the current mode. diff --git a/src/core/prompts/__tests__/sections.test.ts b/src/core/prompts/__tests__/sections.test.ts index 2100016e467..4a18f83de46 100644 --- a/src/core/prompts/__tests__/sections.test.ts +++ b/src/core/prompts/__tests__/sections.test.ts @@ -33,24 +33,24 @@ describe("getCapabilitiesSection", () => { const cwd = "/test/path" const mcpHub = undefined const mockDiffStrategy: DiffStrategy = { - getToolDescription: () => "apply_diff tool description", + getToolDescription: () => "edit_file tool description", applyDiff: async (originalContent: string, diffContent: string): Promise => { return { success: true, content: "mock result" } }, } - test("includes apply_diff in capabilities when diffStrategy is provided", () => { + test("includes edit_file in capabilities when diffStrategy is provided", () => { const result = getCapabilitiesSection(cwd, false, mcpHub, mockDiffStrategy) - expect(result).toContain("or apply_diff") - expect(result).toContain("then use the write_to_file or apply_diff tool") + expect(result).toContain("or edit_file") + expect(result).toContain("then use the write_to_file or edit_file tool") }) - test("excludes apply_diff from capabilities when diffStrategy is undefined", () => { + test("excludes edit_file from capabilities when diffStrategy is undefined", () => { const result = getCapabilitiesSection(cwd, false, mcpHub, undefined) - expect(result).not.toContain("or apply_diff") + expect(result).not.toContain("or edit_file") expect(result).toContain("then use the write_to_file tool") - expect(result).not.toContain("write_to_file or apply_diff") + expect(result).not.toContain("write_to_file or edit_file") }) }) diff --git a/src/core/prompts/__tests__/system.test.ts b/src/core/prompts/__tests__/system.test.ts index 2adfa927eb6..ad63318efd1 100644 --- a/src/core/prompts/__tests__/system.test.ts +++ b/src/core/prompts/__tests__/system.test.ts @@ -288,7 +288,7 @@ describe("SYSTEM_PROMPT", () => { true, // enableMcpServerCreation ) - expect(prompt).toContain("apply_diff") + expect(prompt).toContain("edit_file") expect(prompt).toMatchSnapshot() }) @@ -310,7 +310,7 @@ describe("SYSTEM_PROMPT", () => { true, // enableMcpServerCreation ) - expect(prompt).not.toContain("apply_diff") + expect(prompt).not.toContain("edit_file") expect(prompt).toMatchSnapshot() }) @@ -332,7 +332,7 @@ describe("SYSTEM_PROMPT", () => { true, // enableMcpServerCreation ) - expect(prompt).not.toContain("apply_diff") + expect(prompt).not.toContain("edit_file") expect(prompt).toMatchSnapshot() }) @@ -562,7 +562,7 @@ describe("SYSTEM_PROMPT", () => { ) // Verify base instruction lists all available tools - expect(prompt).toContain("apply_diff (for replacing lines in existing files)") + expect(prompt).toContain("edit_file (for replacing lines in existing files)") expect(prompt).toContain("write_to_file (for creating new files or complete file rewrites)") expect(prompt).toContain("insert_content (for adding lines to existing files)") expect(prompt).toContain("search_and_replace (for finding and replacing individual pieces of text)") diff --git a/src/core/prompts/sections/capabilities.ts b/src/core/prompts/sections/capabilities.ts index 983d07bf761..16163dd2b31 100644 --- a/src/core/prompts/sections/capabilities.ts +++ b/src/core/prompts/sections/capabilities.ts @@ -17,7 +17,7 @@ CAPABILITIES - When the user initially gives you a task, a recursive list of all filepaths in the current working directory ('${cwd}') will be included in environment_details. This provides an overview of the project's file structure, offering key insights into the project from directory/file names (how developers conceptualize and organize their code) and file extensions (the language used). This can also guide decision-making on which files to explore further. If you need to further explore directories such as outside the current working directory, you can use the list_files tool. If you pass 'true' for the recursive parameter, it will list files recursively. Otherwise, it will list files at the top level, which is better suited for generic directories where you don't necessarily need the nested structure, like the Desktop. - You can use search_files to perform regex searches across files in a specified directory, outputting context-rich results that include surrounding lines. This is particularly useful for understanding code patterns, finding specific implementations, or identifying areas that need refactoring. - You can use the list_code_definition_names tool to get an overview of source code definitions for all files at the top level of a specified directory. This can be particularly useful when you need to understand the broader context and relationships between certain parts of the code. You may need to call this tool multiple times to understand various parts of the codebase related to the task. - - For example, when asked to make edits or improvements you might analyze the file structure in the initial environment_details to get an overview of the project, then use list_code_definition_names to get further insight using source code definitions for files located in relevant directories, then read_file to examine the contents of relevant files, analyze the code and suggest improvements or make necessary edits, then use ${diffStrategy ? "the apply_diff or write_to_file" : "the write_to_file"} tool to apply the changes. If you refactored code that could affect other parts of the codebase, you could use search_files to ensure you update other files as needed. + - For example, when asked to make edits or improvements you might analyze the file structure in the initial environment_details to get an overview of the project, then use list_code_definition_names to get further insight using source code definitions for files located in relevant directories, then read_file to examine the contents of relevant files, analyze the code and suggest improvements or make necessary edits, then use ${diffStrategy ? "the edit_file or write_to_file" : "the write_to_file"} tool to apply the changes. If you refactored code that could affect other parts of the codebase, you could use search_files to ensure you update other files as needed. - You can use the execute_command tool to run commands on the user's computer whenever you feel it can help accomplish the user's task. When you need to execute a CLI command, you must provide a clear explanation of what the command does. Prefer to execute complex CLI commands over creating executable scripts, since they are more flexible and easier to run. Interactive and long-running commands are allowed, since the commands are run in the user's VSCode terminal. The user may keep commands running in the background and you will be kept updated on their status along the way. Each command you execute is run in a new terminal instance.${ supportsComputerUse ? "\n- You can use the browser_action tool to interact with websites (including html files and locally running development servers) through a Puppeteer-controlled browser when you feel it is necessary in accomplishing the user's task. This tool is particularly useful for web development tasks as it allows you to launch a browser, navigate to pages, interact with elements through clicks and keyboard input, and capture the results through screenshots and console logs. This tool may be useful at key stages of web development tasks-such as after implementing new features, making substantial changes, when troubleshooting issues, or to verify the result of your work. You can analyze the provided screenshots to ensure correct rendering or identify errors, and review console logs for runtime issues.\n - For example, if asked to add a component to a react website, you might create the necessary files, use execute_command to run the site locally, then use browser_action to launch the browser, navigate to the local server, and verify the component renders & functions correctly before closing the browser." diff --git a/src/core/prompts/sections/mcp-servers.ts b/src/core/prompts/sections/mcp-servers.ts index 3f7ec88297c..e44020091d0 100644 --- a/src/core/prompts/sections/mcp-servers.ts +++ b/src/core/prompts/sections/mcp-servers.ts @@ -414,7 +414,7 @@ The user may ask to add tools or resources that may make sense to add to an exis .getServers() .map((server) => server.name) .join(", ") || "(None running currently)" - }, e.g. if it would use the same API. This would be possible if you can locate the MCP server repository on the user's system by looking at the server arguments for a filepath. You might then use list_files and read_file to explore the files in the repository, and use write_to_file${diffStrategy ? " or apply_diff" : ""} to make changes to the files. + }, e.g. if it would use the same API. This would be possible if you can locate the MCP server repository on the user's system by looking at the server arguments for a filepath. You might then use list_files and read_file to explore the files in the repository, and use write_to_file${diffStrategy ? " or edit_file" : ""} to make changes to the files. However some MCP servers may be running from installed packages rather than a local repository, in which case it may make more sense to create a new MCP server. diff --git a/src/core/prompts/sections/modes.ts b/src/core/prompts/sections/modes.ts index f3863870dbc..499d4b3882f 100644 --- a/src/core/prompts/sections/modes.ts +++ b/src/core/prompts/sections/modes.ts @@ -45,7 +45,7 @@ Both files should follow this structure: "roleDefinition": "You are Roo, a UI/UX expert specializing in design systems and frontend development. Your expertise includes:\\n- Creating and maintaining design systems\\n- Implementing responsive and accessible web interfaces\\n- Working with CSS, HTML, and modern frontend frameworks\\n- Ensuring consistent user experiences across platforms", // Required: non-empty "groups": [ // Required: array of tool groups (can be empty) "read", // Read files group (read_file, search_files, list_files, list_code_definition_names) - "edit", // Edit files group (apply_diff, write_to_file) - allows editing any file + "edit", // Edit files group (edit_file, write_to_file) - allows editing any file // Or with file restrictions: // ["edit", { fileRegex: "\\.md$", description: "Markdown files only" }], // Edit group that only allows editing markdown files "browser", // Browser group (browser_action) diff --git a/src/core/prompts/sections/rules.ts b/src/core/prompts/sections/rules.ts index 86e554a157e..82c29cff495 100644 --- a/src/core/prompts/sections/rules.ts +++ b/src/core/prompts/sections/rules.ts @@ -10,7 +10,7 @@ function getEditingInstructions(diffStrategy?: DiffStrategy, experiments?: Recor // Collect available editing tools if (diffStrategy) { availableTools.push( - "apply_diff (for replacing lines in existing files)", + "edit_file (for replacing lines in existing files)", "write_to_file (for creating new files or complete file rewrites)", ) } else { @@ -68,7 +68,7 @@ RULES - You cannot \`cd\` into a different directory to complete a task. You are stuck operating from '${cwd.toPosix()}', so be sure to pass in the correct 'path' parameter when using tools that require a path. - Do not use the ~ character or $HOME to refer to the home directory. - Before using the execute_command tool, you must first think about the SYSTEM INFORMATION context provided to understand the user's environment and tailor your commands to ensure they are compatible with their system. You must also consider if the command you need to run should be executed in a specific directory outside of the current working directory '${cwd.toPosix()}', and if so prepend with \`cd\`'ing into that directory && then executing the command (as one command since you are stuck operating from '${cwd.toPosix()}'). For example, if you needed to run \`npm install\` in a project outside of '${cwd.toPosix()}', you would need to prepend with a \`cd\` i.e. pseudocode for this would be \`cd (path to project) && (command, in this case npm install)\`. -- When using the search_files tool, craft your regex patterns carefully to balance specificity and flexibility. Based on the user's task you may use it to find code patterns, TODO comments, function definitions, or any text-based information across the project. The results include context, so analyze the surrounding code to better understand the matches. Leverage the search_files tool in combination with other tools for more comprehensive analysis. For example, use it to find specific code patterns, then use read_file to examine the full context of interesting matches before using ${diffStrategy ? "apply_diff or write_to_file" : "write_to_file"} to make informed changes. +- When using the search_files tool, craft your regex patterns carefully to balance specificity and flexibility. Based on the user's task you may use it to find code patterns, TODO comments, function definitions, or any text-based information across the project. The results include context, so analyze the surrounding code to better understand the matches. Leverage the search_files tool in combination with other tools for more comprehensive analysis. For example, use it to find specific code patterns, then use read_file to examine the full context of interesting matches before using ${diffStrategy ? "edit_file or write_to_file" : "write_to_file"} to make informed changes. - When creating a new project (such as an app, website, or any software project), organize all new files within a dedicated project directory unless the user specifies otherwise. Use appropriate file paths when writing files, as the write_to_file tool will automatically create any necessary directories. Structure the project logically, adhering to best practices for the specific type of project being created. Unless otherwise specified, new projects should be easily run without additional setup, for example most projects can be built in HTML, CSS, and JavaScript - which you can open in a browser. ${getEditingInstructions(diffStrategy, experiments)} - Some modes have restrictions on which files they can edit. If you attempt to edit a restricted file, the operation will be rejected with a FileRestrictionError that will specify which file patterns are allowed for the current mode. diff --git a/src/core/prompts/tools/index.ts b/src/core/prompts/tools/index.ts index 1b9b9a43d9d..5a8477bc4f7 100644 --- a/src/core/prompts/tools/index.ts +++ b/src/core/prompts/tools/index.ts @@ -36,7 +36,7 @@ const toolDescriptionMap: Record string | undefined> new_task: (args) => getNewTaskDescription(args), insert_content: (args) => getInsertContentDescription(args), search_and_replace: (args) => getSearchAndReplaceDescription(args), - apply_diff: (args) => + edit_file: (args) => args.diffStrategy ? args.diffStrategy.getToolDescription({ cwd: args.cwd, toolOptions: args.toolOptions }) : "", } diff --git a/src/core/webview/__tests__/ClineProvider.test.ts b/src/core/webview/__tests__/ClineProvider.test.ts index 13a2e6d84f7..902e536d9c8 100644 --- a/src/core/webview/__tests__/ClineProvider.test.ts +++ b/src/core/webview/__tests__/ClineProvider.test.ts @@ -107,7 +107,7 @@ jest.mock( // Mock DiffStrategy jest.mock("../../diff/DiffStrategy", () => ({ getDiffStrategy: jest.fn().mockImplementation(() => ({ - getToolDescription: jest.fn().mockReturnValue("apply_diff tool description"), + getToolDescription: jest.fn().mockReturnValue("edit_file tool description"), })), })) diff --git a/src/shared/ExtensionMessage.ts b/src/shared/ExtensionMessage.ts index fe9fa394270..5d0e16e39cd 100644 --- a/src/shared/ExtensionMessage.ts +++ b/src/shared/ExtensionMessage.ts @@ -127,7 +127,7 @@ export interface ExtensionState { experiments: Record // Map of experiment IDs to their enabled state autoApprovalEnabled?: boolean customModes: ModeConfig[] - toolRequirements?: Record // Map of tool names to their requirements (e.g. {"apply_diff": true} if diffEnabled) + toolRequirements?: Record // Map of tool names to their requirements (e.g. {"edit_file": true} if diffEnabled) maxOpenTabsContext: number // Maximum number of VSCode open tabs to include in context (0-500) } diff --git a/src/shared/__tests__/modes.test.ts b/src/shared/__tests__/modes.test.ts index 3bd89c4ecb5..1618ae6bc10 100644 --- a/src/shared/__tests__/modes.test.ts +++ b/src/shared/__tests__/modes.test.ts @@ -97,7 +97,7 @@ describe("isToolAllowedForMode", () => { ).toBe(true) expect( - isToolAllowedForMode("apply_diff", "markdown-editor", customModes, undefined, { + isToolAllowedForMode("edit_file", "markdown-editor", customModes, undefined, { path: "test.js", }), ).toBe(true) @@ -110,7 +110,7 @@ describe("isToolAllowedForMode", () => { ).toBe(true) }) - it("applies restrictions to both write_to_file and apply_diff", () => { + it("applies restrictions to both write_to_file and edit_file", () => { // Test write_to_file const writeResult = isToolAllowedForMode("write_to_file", "markdown-editor", customModes, undefined, { path: "test.md", @@ -118,8 +118,8 @@ describe("isToolAllowedForMode", () => { }) expect(writeResult).toBe(true) - // Test apply_diff - const diffResult = isToolAllowedForMode("apply_diff", "markdown-editor", customModes, undefined, { + // Test edit_file + const diffResult = isToolAllowedForMode("edit_file", "markdown-editor", customModes, undefined, { path: "test.md", diff: "- old\n+ new", }) @@ -134,7 +134,7 @@ describe("isToolAllowedForMode", () => { ).toThrow(FileRestrictionError) expect(() => - isToolAllowedForMode("apply_diff", "markdown-editor", customModes, undefined, { + isToolAllowedForMode("edit_file", "markdown-editor", customModes, undefined, { path: "test.js", diff: "- old\n+ new", }), @@ -169,15 +169,15 @@ describe("isToolAllowedForMode", () => { }), ).toThrow(/Documentation files only/) - // Test apply_diff with non-matching file + // Test edit_file with non-matching file expect(() => - isToolAllowedForMode("apply_diff", "docs-editor", customModesWithDescription, undefined, { + isToolAllowedForMode("edit_file", "docs-editor", customModesWithDescription, undefined, { path: "test.js", diff: "- old\n+ new", }), ).toThrow(FileRestrictionError) expect(() => - isToolAllowedForMode("apply_diff", "docs-editor", customModesWithDescription, undefined, { + isToolAllowedForMode("edit_file", "docs-editor", customModesWithDescription, undefined, { path: "test.js", diff: "- old\n+ new", }), @@ -217,7 +217,7 @@ describe("isToolAllowedForMode", () => { // Should allow applying diffs to markdown files expect( - isToolAllowedForMode("apply_diff", "architect", [], undefined, { + isToolAllowedForMode("edit_file", "architect", [], undefined, { path: "readme.md", diff: "- old\n+ new", }), diff --git a/src/shared/tool-groups.ts b/src/shared/tool-groups.ts index 50c7b80ca9e..8e982b20b0f 100644 --- a/src/shared/tool-groups.ts +++ b/src/shared/tool-groups.ts @@ -9,7 +9,7 @@ export const TOOL_DISPLAY_NAMES = { execute_command: "run commands", read_file: "read files", write_to_file: "write files", - apply_diff: "apply changes", + edit_file: "apply changes", search_files: "search files", list_files: "list files", list_code_definition_names: "list definitions", @@ -28,7 +28,7 @@ export const TOOL_GROUPS: Record = { tools: ["read_file", "search_files", "list_files", "list_code_definition_names"], }, edit: { - tools: ["apply_diff", "write_to_file", "insert_content", "search_and_replace"], + tools: ["edit_file", "write_to_file", "insert_content", "search_and_replace"], }, browser: { tools: ["browser_action"], From 6225994a7cce4b72445e34072d6aca8788b57b92 Mon Sep 17 00:00:00 2001 From: Roo Code Date: Sat, 22 Feb 2025 20:02:17 -0700 Subject: [PATCH 029/585] Rename 'write_to_file' tool to 'create_file' across codebase and fixed tests --- CHANGELOG.md | 2 +- src/api/transform/o1-format.ts | 22 +- src/core/Cline.ts | 18 +- src/core/__tests__/mode-validator.test.ts | 6 +- src/core/assistant-message/index.ts | 4 +- .../parse-assistant-message.ts | 4 +- .../__snapshots__/system.test.ts.snap | 278 +++++++++--------- src/core/prompts/__tests__/sections.test.ts | 8 +- src/core/prompts/__tests__/system.test.ts | 4 +- src/core/prompts/sections/capabilities.ts | 2 +- src/core/prompts/sections/mcp-servers.ts | 2 +- src/core/prompts/sections/modes.ts | 2 +- src/core/prompts/sections/rules.ts | 12 +- src/core/prompts/tools/index.ts | 2 +- src/core/prompts/tools/write-to-file.ts | 10 +- src/shared/__tests__/modes.test.ts | 50 ++-- src/shared/tool-groups.ts | 4 +- 17 files changed, 215 insertions(+), 215 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 748f8c78f88..ff8cfdc3e85 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -479,7 +479,7 @@ Join us at https://www.reddit.com/r/RooCode to share your custom modes and be pa ## [2.1.14] - Fix bug where diffs were not being applied correctly and try Aider's [unified diff prompt](https://github.com/Aider-AI/aider/blob/3995accd0ca71cea90ef76d516837f8c2731b9fe/aider/coders/udiff_prompts.py#L75-L105) -- If diffs are enabled, automatically reject write_to_file commands that lead to truncated output +- If diffs are enabled, automatically reject create_file commands that lead to truncated output ## [2.1.13] diff --git a/src/api/transform/o1-format.ts b/src/api/transform/o1-format.ts index 1346fdbd54d..040bca7a303 100644 --- a/src/api/transform/o1-format.ts +++ b/src/api/transform/o1-format.ts @@ -67,13 +67,13 @@ Description: Perform a regex search across files in a specified directory, provi Description: Read the contents of a file at the specified path. Use this when you need to examine the contents of an existing file, for example to analyze code, review text files, or extract information from configuration files. Automatically extracts raw text from PDF and DOCX files. May not be suitable for other types of binary files, as it returns the raw content as a string. -6. write_to_file: - +6. create_file: + File path here Your file content here - + Description: Write content to a file at the specified path. If the file exists, it will be overwritten with the provided content. If the file doesn't exist, it will be created. Always provide the full intended content of the file, without any truncation. This tool will automatically create any directories needed to write the file. 7. ask_followup_question: @@ -107,7 +107,7 @@ Example 2: Using multiple tools Let's create two new configuration files for the web application: one for the frontend and one for the backend. - + ./frontend-config.json { @@ -125,9 +125,9 @@ Let's create two new configuration files for the web application: one for the fr "version": "1.0.0" } - + - + ./backend-config.yaml database: @@ -154,7 +154,7 @@ externalServices: emailProvider: sendgrid storageProvider: aws-s3 - + Example 3: Asking a follow-up question @@ -239,7 +239,7 @@ const toolNames = [ "list_code_definition_names", "search_files", "read_file", - "write_to_file", + "create_file", "ask_followup_question", "attempt_completion", ] @@ -331,7 +331,7 @@ function validateToolInput(toolName: string, tool_input: Record) return "path" in tool_input case "search_files": return "path" in tool_input && "regex" in tool_input - case "write_to_file": + case "create_file": return "path" in tool_input && "content" in tool_input case "ask_followup_question": return "question" in tool_input @@ -349,10 +349,10 @@ function validateToolInput(toolName: string, tool_input: Record) // ls -la // -// +// // ./example.txt // Hello, World! -// `; +// `; // // const { normalText, toolCalls } = parseAIResponse(aiResponse); // console.log(normalText); diff --git a/src/core/Cline.ts b/src/core/Cline.ts index 8c2924b3f49..94bc6126924 100644 --- a/src/core/Cline.ts +++ b/src/core/Cline.ts @@ -698,7 +698,7 @@ export class Cline { text: `[TASK RESUMPTION] This task was interrupted ${agoText}. It may or may not be complete, so please reassess the task context. Be aware that the project state may have changed since then. The current working directory is now '${cwd.toPosix()}'. If the task has not been completed, retry the last step before interruption and proceed with completing the task.\n\nNote: If you previously attempted a tool use that the user did not provide a result for, you should assume the tool use was not successful and assess whether you should retry. If the last tool was a browser_action, the browser has been closed and you must launch a new browser if needed.${ wasRecent - ? "\n\nIMPORTANT: If the last tool use was a write_to_file that was interrupted, the file was reverted back to its original state before the interrupted edit, and you do NOT need to re-read the file as you already have its up-to-date contents." + ? "\n\nIMPORTANT: If the last tool use was a create_file that was interrupted, the file was reverted back to its original state before the interrupted edit, and you do NOT need to re-read the file as you already have its up-to-date contents." : "" }` + (responseText @@ -1102,7 +1102,7 @@ export class Cline { return `[${block.name} for '${block.params.command}']` case "read_file": return `[${block.name} for '${block.params.path}']` - case "write_to_file": + case "create_file": return `[${block.name} for '${block.params.path}']` case "edit_file": return `[${block.name} for '${block.params.path}']` @@ -1267,7 +1267,7 @@ export class Cline { } switch (block.name) { - case "write_to_file": { + case "create_file": { const relPath: string | undefined = block.params.path let newContent: string | undefined = block.params.content let predictedLineCount: number | undefined = parseInt(block.params.line_count ?? "0") @@ -1332,20 +1332,20 @@ export class Cline { } else { if (!relPath) { this.consecutiveMistakeCount++ - pushToolResult(await this.sayAndCreateMissingParamError("write_to_file", "path")) + pushToolResult(await this.sayAndCreateMissingParamError("create_file", "path")) await this.diffViewProvider.reset() break } if (!newContent) { this.consecutiveMistakeCount++ - pushToolResult(await this.sayAndCreateMissingParamError("write_to_file", "content")) + pushToolResult(await this.sayAndCreateMissingParamError("create_file", "content")) await this.diffViewProvider.reset() break } if (!predictedLineCount) { this.consecutiveMistakeCount++ pushToolResult( - await this.sayAndCreateMissingParamError("write_to_file", "line_count"), + await this.sayAndCreateMissingParamError("create_file", "line_count"), ) await this.diffViewProvider.reset() break @@ -2194,7 +2194,7 @@ export class Cline { formatResponse.toolResult( `The browser action has been executed. The console logs and screenshot have been captured for your analysis.\n\nConsole logs:\n${ browserActionResult.logs || "(No new logs)" - }\n\n(REMEMBER: if you need to proceed to using non-\`browser_action\` tools or launch a new browser, you MUST first close this browser. For example, if after analyzing the logs and screenshot you need to edit a file, you must first close the browser before you can use the write_to_file tool.)`, + }\n\n(REMEMBER: if you need to proceed to using non-\`browser_action\` tools or launch a new browser, you MUST first close this browser. For example, if after analyzing the logs and screenshot you need to edit a file, you must first close the browser before you can use the create_file tool.)`, browserActionResult.screenshot ? [browserActionResult.screenshot] : [], ), ) @@ -2711,7 +2711,7 @@ export class Cline { /* Seeing out of bounds is fine, it means that the next too call is being built up and ready to add to assistantMessageContent to present. - When you see the UI inactive during this, it means that a tool is breaking without presenting any UI. For example the write_to_file tool was breaking when relpath was undefined, and for invalid relpath it never presented UI. + When you see the UI inactive during this, it means that a tool is breaking without presenting any UI. For example the create_file tool was breaking when relpath was undefined, and for invalid relpath it never presented UI. */ this.presentAssistantMessageLocked = false // this needs to be placed here, if not then calling this.presentAssistantMessage below would fail (sometimes) since it's locked // NOTE: when tool is rejected, iterator stream is interrupted and it waits for userMessageContentReady to be true. Future calls to present will skip execution since didRejectTool and iterate until contentIndex is set to message length and it sets userMessageContentReady to true itself (instead of preemptively doing it in iterator) @@ -3261,7 +3261,7 @@ export class Cline { // Add warning if not in code mode if ( - !isToolAllowedForMode("write_to_file", currentMode, customModes ?? [], { + !isToolAllowedForMode("create_file", currentMode, customModes ?? [], { edit_file: this.diffEnabled, }) && !isToolAllowedForMode("edit_file", currentMode, customModes ?? [], { edit_file: this.diffEnabled }) diff --git a/src/core/__tests__/mode-validator.test.ts b/src/core/__tests__/mode-validator.test.ts index a10da470bd5..4efcd06e3e7 100644 --- a/src/core/__tests__/mode-validator.test.ts +++ b/src/core/__tests__/mode-validator.test.ts @@ -59,7 +59,7 @@ describe("mode-validator", () => { ] // Should allow tools from read and edit groups expect(isToolAllowedForMode("read_file", "custom-mode", customModes)).toBe(true) - expect(isToolAllowedForMode("write_to_file", "custom-mode", customModes)).toBe(true) + expect(isToolAllowedForMode("create_file", "custom-mode", customModes)).toBe(true) // Should not allow tools from other groups expect(isToolAllowedForMode("execute_command", "custom-mode", customModes)).toBe(false) }) @@ -76,7 +76,7 @@ describe("mode-validator", () => { // Should allow tools from read group expect(isToolAllowedForMode("read_file", codeMode, customModes)).toBe(true) // Should not allow tools from other groups - expect(isToolAllowedForMode("write_to_file", codeMode, customModes)).toBe(false) + expect(isToolAllowedForMode("create_file", codeMode, customModes)).toBe(false) }) it("respects tool requirements in custom modes", () => { @@ -94,7 +94,7 @@ describe("mode-validator", () => { expect(isToolAllowedForMode("edit_file", "custom-mode", customModes, requirements)).toBe(false) // Should allow other edit tools - expect(isToolAllowedForMode("write_to_file", "custom-mode", customModes, requirements)).toBe(true) + expect(isToolAllowedForMode("create_file", "custom-mode", customModes, requirements)).toBe(true) }) }) diff --git a/src/core/assistant-message/index.ts b/src/core/assistant-message/index.ts index c67c85933cb..46b29a703db 100644 --- a/src/core/assistant-message/index.ts +++ b/src/core/assistant-message/index.ts @@ -11,7 +11,7 @@ export interface TextContent { export const toolUseNames = [ "execute_command", "read_file", - "write_to_file", + "create_file", "edit_file", "insert_content", "search_and_replace", @@ -80,7 +80,7 @@ export interface ReadFileToolUse extends ToolUse { } export interface WriteToFileToolUse extends ToolUse { - name: "write_to_file" + name: "create_file" params: Partial, "path" | "content" | "line_count">> } diff --git a/src/core/assistant-message/parse-assistant-message.ts b/src/core/assistant-message/parse-assistant-message.ts index e38e8f6458e..9b1cea70a9b 100644 --- a/src/core/assistant-message/parse-assistant-message.ts +++ b/src/core/assistant-message/parse-assistant-message.ts @@ -61,9 +61,9 @@ export function parseAssistantMessage(assistantMessage: string) { // there's no current param, and not starting a new param - // special case for write_to_file where file contents could contain the closing tag, in which case the param would have closed and we end up with the rest of the file contents here. To work around this, we get the string between the starting content tag and the LAST content tag. + // special case for create_file where file contents could contain the closing tag, in which case the param would have closed and we end up with the rest of the file contents here. To work around this, we get the string between the starting content tag and the LAST content tag. const contentParamName: ToolParamName = "content" - if (currentToolUse.name === "write_to_file" && accumulator.endsWith(``)) { + if (currentToolUse.name === "create_file" && accumulator.endsWith(``)) { const toolContent = accumulator.slice(currentToolUseStartIndex) const contentStartTag = `<${contentParamName}>` const contentEndTag = `` diff --git a/src/core/prompts/__tests__/__snapshots__/system.test.ts.snap b/src/core/prompts/__tests__/__snapshots__/system.test.ts.snap index b88ff5b1c69..e4447d31eef 100644 --- a/src/core/prompts/__tests__/__snapshots__/system.test.ts.snap +++ b/src/core/prompts/__tests__/__snapshots__/system.test.ts.snap @@ -94,23 +94,23 @@ Example: Requesting to list all top level source code definitions in the current . -## write_to_file +## create_file Description: Request to write full content to a file at the specified path. If the file exists, it will be overwritten with the provided content. If the file doesn't exist, it will be created. This tool will automatically create any directories needed to write the file. Parameters: - path: (required) The path of the file to write to (relative to the current working directory /test/path) - content: (required) The content to write to the file. ALWAYS provide the COMPLETE intended content of the file, without any truncation or omissions. You MUST include ALL parts of the file, even if they haven't been modified. Do NOT include the line numbers in the content though, just the actual content of the file. - line_count: (required) The number of lines in the file. Make sure to compute this based on the actual content of the file, not the number of lines in the content you're providing. Usage: - + File path here Your file content here total number of lines in the file, including empty lines - + Example: Requesting to write to frontend-config.json - + frontend-config.json { @@ -129,7 +129,7 @@ Example: Requesting to write to frontend-config.json } 14 - + ## execute_command Description: Request to execute a CLI command on the system. Use this when you need to perform system operations or run specific commands to accomplish any step in the user's task. You must tailor your command to the user's system and provide a clear explanation of what the command does. For command chaining, use the appropriate chaining syntax for the user's shell. Prefer to execute complex CLI commands over creating executable scripts, as they are more flexible and easier to run. Commands will be executed in the current working directory: /test/path @@ -249,7 +249,7 @@ CAPABILITIES - When the user initially gives you a task, a recursive list of all filepaths in the current working directory ('/test/path') will be included in environment_details. This provides an overview of the project's file structure, offering key insights into the project from directory/file names (how developers conceptualize and organize their code) and file extensions (the language used). This can also guide decision-making on which files to explore further. If you need to further explore directories such as outside the current working directory, you can use the list_files tool. If you pass 'true' for the recursive parameter, it will list files recursively. Otherwise, it will list files at the top level, which is better suited for generic directories where you don't necessarily need the nested structure, like the Desktop. - You can use search_files to perform regex searches across files in a specified directory, outputting context-rich results that include surrounding lines. This is particularly useful for understanding code patterns, finding specific implementations, or identifying areas that need refactoring. - You can use the list_code_definition_names tool to get an overview of source code definitions for all files at the top level of a specified directory. This can be particularly useful when you need to understand the broader context and relationships between certain parts of the code. You may need to call this tool multiple times to understand various parts of the codebase related to the task. - - For example, when asked to make edits or improvements you might analyze the file structure in the initial environment_details to get an overview of the project, then use list_code_definition_names to get further insight using source code definitions for files located in relevant directories, then read_file to examine the contents of relevant files, analyze the code and suggest improvements or make necessary edits, then use the write_to_file tool to apply the changes. If you refactored code that could affect other parts of the codebase, you could use search_files to ensure you update other files as needed. + - For example, when asked to make edits or improvements you might analyze the file structure in the initial environment_details to get an overview of the project, then use list_code_definition_names to get further insight using source code definitions for files located in relevant directories, then read_file to examine the contents of relevant files, analyze the code and suggest improvements or make necessary edits, then use the create_file tool to apply the changes. If you refactored code that could affect other parts of the codebase, you could use search_files to ensure you update other files as needed. - You can use the execute_command tool to run commands on the user's computer whenever you feel it can help accomplish the user's task. When you need to execute a CLI command, you must provide a clear explanation of what the command does. Prefer to execute complex CLI commands over creating executable scripts, since they are more flexible and easier to run. Interactive and long-running commands are allowed, since the commands are run in the user's VSCode terminal. The user may keep commands running in the background and you will be kept updated on their status along the way. Each command you execute is run in a new terminal instance. ==== @@ -266,9 +266,9 @@ RULES - You cannot \`cd\` into a different directory to complete a task. You are stuck operating from '/test/path', so be sure to pass in the correct 'path' parameter when using tools that require a path. - Do not use the ~ character or $HOME to refer to the home directory. - Before using the execute_command tool, you must first think about the SYSTEM INFORMATION context provided to understand the user's environment and tailor your commands to ensure they are compatible with their system. You must also consider if the command you need to run should be executed in a specific directory outside of the current working directory '/test/path', and if so prepend with \`cd\`'ing into that directory && then executing the command (as one command since you are stuck operating from '/test/path'). For example, if you needed to run \`npm install\` in a project outside of '/test/path', you would need to prepend with a \`cd\` i.e. pseudocode for this would be \`cd (path to project) && (command, in this case npm install)\`. -- When using the search_files tool, craft your regex patterns carefully to balance specificity and flexibility. Based on the user's task you may use it to find code patterns, TODO comments, function definitions, or any text-based information across the project. The results include context, so analyze the surrounding code to better understand the matches. Leverage the search_files tool in combination with other tools for more comprehensive analysis. For example, use it to find specific code patterns, then use read_file to examine the full context of interesting matches before using write_to_file to make informed changes. -- When creating a new project (such as an app, website, or any software project), organize all new files within a dedicated project directory unless the user specifies otherwise. Use appropriate file paths when writing files, as the write_to_file tool will automatically create any necessary directories. Structure the project logically, adhering to best practices for the specific type of project being created. Unless otherwise specified, new projects should be easily run without additional setup, for example most projects can be built in HTML, CSS, and JavaScript - which you can open in a browser. -- When using the write_to_file tool to modify a file, use the tool directly with the desired content. You do not need to display the content before using the tool. ALWAYS provide the COMPLETE file content in your response. This is NON-NEGOTIABLE. Partial updates or placeholders like '// rest of code unchanged' are STRICTLY FORBIDDEN. You MUST include ALL parts of the file, even if they haven't been modified. Failure to do so will result in incomplete or broken code, severely impacting the user's project. +- When using the search_files tool, craft your regex patterns carefully to balance specificity and flexibility. Based on the user's task you may use it to find code patterns, TODO comments, function definitions, or any text-based information across the project. The results include context, so analyze the surrounding code to better understand the matches. Leverage the search_files tool in combination with other tools for more comprehensive analysis. For example, use it to find specific code patterns, then use read_file to examine the full context of interesting matches before using create_file to make informed changes. +- When creating a new project (such as an app, website, or any software project), organize all new files within a dedicated project directory unless the user specifies otherwise. Use appropriate file paths when writing files, as the create_file tool will automatically create any necessary directories. Structure the project logically, adhering to best practices for the specific type of project being created. Unless otherwise specified, new projects should be easily run without additional setup, for example most projects can be built in HTML, CSS, and JavaScript - which you can open in a browser. +- When using the create_file tool to modify a file, use the tool directly with the desired content. You do not need to display the content before using the tool. ALWAYS provide the COMPLETE file content in your response. This is NON-NEGOTIABLE. Partial updates or placeholders like '// rest of code unchanged' are STRICTLY FORBIDDEN. You MUST include ALL parts of the file, even if they haven't been modified. Failure to do so will result in incomplete or broken code, severely impacting the user's project. - Some modes have restrictions on which files they can edit. If you attempt to edit a restricted file, the operation will be rejected with a FileRestrictionError that will specify which file patterns are allowed for the current mode. - Be sure to consider the type of project (e.g. Python, JavaScript, web application) when determining the appropriate structure and files to include. Also consider what files may be most relevant to accomplishing the task, for example looking at a project's manifest file would help you understand the project's dependencies, which you could incorporate into any code you write. * For example, in architect mode trying to edit app.js would be rejected because architect mode can only edit files matching "\\.md$" @@ -417,23 +417,23 @@ Example: Requesting to list all top level source code definitions in the current . -## write_to_file +## create_file Description: Request to write full content to a file at the specified path. If the file exists, it will be overwritten with the provided content. If the file doesn't exist, it will be created. This tool will automatically create any directories needed to write the file. Parameters: - path: (required) The path of the file to write to (relative to the current working directory /test/path) - content: (required) The content to write to the file. ALWAYS provide the COMPLETE intended content of the file, without any truncation or omissions. You MUST include ALL parts of the file, even if they haven't been modified. Do NOT include the line numbers in the content though, just the actual content of the file. - line_count: (required) The number of lines in the file. Make sure to compute this based on the actual content of the file, not the number of lines in the content you're providing. Usage: - + File path here Your file content here total number of lines in the file, including empty lines - + Example: Requesting to write to frontend-config.json - + frontend-config.json { @@ -452,7 +452,7 @@ Example: Requesting to write to frontend-config.json } 14 - + ## execute_command Description: Request to execute a CLI command on the system. Use this when you need to perform system operations or run specific commands to accomplish any step in the user's task. You must tailor your command to the user's system and provide a clear explanation of what the command does. For command chaining, use the appropriate chaining syntax for the user's shell. Prefer to execute complex CLI commands over creating executable scripts, as they are more flexible and easier to run. Commands will be executed in the current working directory: /test/path @@ -572,7 +572,7 @@ CAPABILITIES - When the user initially gives you a task, a recursive list of all filepaths in the current working directory ('/test/path') will be included in environment_details. This provides an overview of the project's file structure, offering key insights into the project from directory/file names (how developers conceptualize and organize their code) and file extensions (the language used). This can also guide decision-making on which files to explore further. If you need to further explore directories such as outside the current working directory, you can use the list_files tool. If you pass 'true' for the recursive parameter, it will list files recursively. Otherwise, it will list files at the top level, which is better suited for generic directories where you don't necessarily need the nested structure, like the Desktop. - You can use search_files to perform regex searches across files in a specified directory, outputting context-rich results that include surrounding lines. This is particularly useful for understanding code patterns, finding specific implementations, or identifying areas that need refactoring. - You can use the list_code_definition_names tool to get an overview of source code definitions for all files at the top level of a specified directory. This can be particularly useful when you need to understand the broader context and relationships between certain parts of the code. You may need to call this tool multiple times to understand various parts of the codebase related to the task. - - For example, when asked to make edits or improvements you might analyze the file structure in the initial environment_details to get an overview of the project, then use list_code_definition_names to get further insight using source code definitions for files located in relevant directories, then read_file to examine the contents of relevant files, analyze the code and suggest improvements or make necessary edits, then use the write_to_file tool to apply the changes. If you refactored code that could affect other parts of the codebase, you could use search_files to ensure you update other files as needed. + - For example, when asked to make edits or improvements you might analyze the file structure in the initial environment_details to get an overview of the project, then use list_code_definition_names to get further insight using source code definitions for files located in relevant directories, then read_file to examine the contents of relevant files, analyze the code and suggest improvements or make necessary edits, then use the create_file tool to apply the changes. If you refactored code that could affect other parts of the codebase, you could use search_files to ensure you update other files as needed. - You can use the execute_command tool to run commands on the user's computer whenever you feel it can help accomplish the user's task. When you need to execute a CLI command, you must provide a clear explanation of what the command does. Prefer to execute complex CLI commands over creating executable scripts, since they are more flexible and easier to run. Interactive and long-running commands are allowed, since the commands are run in the user's VSCode terminal. The user may keep commands running in the background and you will be kept updated on their status along the way. Each command you execute is run in a new terminal instance. ==== @@ -589,9 +589,9 @@ RULES - You cannot \`cd\` into a different directory to complete a task. You are stuck operating from '/test/path', so be sure to pass in the correct 'path' parameter when using tools that require a path. - Do not use the ~ character or $HOME to refer to the home directory. - Before using the execute_command tool, you must first think about the SYSTEM INFORMATION context provided to understand the user's environment and tailor your commands to ensure they are compatible with their system. You must also consider if the command you need to run should be executed in a specific directory outside of the current working directory '/test/path', and if so prepend with \`cd\`'ing into that directory && then executing the command (as one command since you are stuck operating from '/test/path'). For example, if you needed to run \`npm install\` in a project outside of '/test/path', you would need to prepend with a \`cd\` i.e. pseudocode for this would be \`cd (path to project) && (command, in this case npm install)\`. -- When using the search_files tool, craft your regex patterns carefully to balance specificity and flexibility. Based on the user's task you may use it to find code patterns, TODO comments, function definitions, or any text-based information across the project. The results include context, so analyze the surrounding code to better understand the matches. Leverage the search_files tool in combination with other tools for more comprehensive analysis. For example, use it to find specific code patterns, then use read_file to examine the full context of interesting matches before using write_to_file to make informed changes. -- When creating a new project (such as an app, website, or any software project), organize all new files within a dedicated project directory unless the user specifies otherwise. Use appropriate file paths when writing files, as the write_to_file tool will automatically create any necessary directories. Structure the project logically, adhering to best practices for the specific type of project being created. Unless otherwise specified, new projects should be easily run without additional setup, for example most projects can be built in HTML, CSS, and JavaScript - which you can open in a browser. -- When using the write_to_file tool to modify a file, use the tool directly with the desired content. You do not need to display the content before using the tool. ALWAYS provide the COMPLETE file content in your response. This is NON-NEGOTIABLE. Partial updates or placeholders like '// rest of code unchanged' are STRICTLY FORBIDDEN. You MUST include ALL parts of the file, even if they haven't been modified. Failure to do so will result in incomplete or broken code, severely impacting the user's project. +- When using the search_files tool, craft your regex patterns carefully to balance specificity and flexibility. Based on the user's task you may use it to find code patterns, TODO comments, function definitions, or any text-based information across the project. The results include context, so analyze the surrounding code to better understand the matches. Leverage the search_files tool in combination with other tools for more comprehensive analysis. For example, use it to find specific code patterns, then use read_file to examine the full context of interesting matches before using create_file to make informed changes. +- When creating a new project (such as an app, website, or any software project), organize all new files within a dedicated project directory unless the user specifies otherwise. Use appropriate file paths when writing files, as the create_file tool will automatically create any necessary directories. Structure the project logically, adhering to best practices for the specific type of project being created. Unless otherwise specified, new projects should be easily run without additional setup, for example most projects can be built in HTML, CSS, and JavaScript - which you can open in a browser. +- When using the create_file tool to modify a file, use the tool directly with the desired content. You do not need to display the content before using the tool. ALWAYS provide the COMPLETE file content in your response. This is NON-NEGOTIABLE. Partial updates or placeholders like '// rest of code unchanged' are STRICTLY FORBIDDEN. You MUST include ALL parts of the file, even if they haven't been modified. Failure to do so will result in incomplete or broken code, severely impacting the user's project. - Some modes have restrictions on which files they can edit. If you attempt to edit a restricted file, the operation will be rejected with a FileRestrictionError that will specify which file patterns are allowed for the current mode. - Be sure to consider the type of project (e.g. Python, JavaScript, web application) when determining the appropriate structure and files to include. Also consider what files may be most relevant to accomplishing the task, for example looking at a project's manifest file would help you understand the project's dependencies, which you could incorporate into any code you write. * For example, in architect mode trying to edit app.js would be rejected because architect mode can only edit files matching "\\.md$" @@ -740,23 +740,23 @@ Example: Requesting to list all top level source code definitions in the current . -## write_to_file +## create_file Description: Request to write full content to a file at the specified path. If the file exists, it will be overwritten with the provided content. If the file doesn't exist, it will be created. This tool will automatically create any directories needed to write the file. Parameters: - path: (required) The path of the file to write to (relative to the current working directory /test/path) - content: (required) The content to write to the file. ALWAYS provide the COMPLETE intended content of the file, without any truncation or omissions. You MUST include ALL parts of the file, even if they haven't been modified. Do NOT include the line numbers in the content though, just the actual content of the file. - line_count: (required) The number of lines in the file. Make sure to compute this based on the actual content of the file, not the number of lines in the content you're providing. Usage: - + File path here Your file content here total number of lines in the file, including empty lines - + Example: Requesting to write to frontend-config.json - + frontend-config.json { @@ -775,7 +775,7 @@ Example: Requesting to write to frontend-config.json } 14 - + ## execute_command Description: Request to execute a CLI command on the system. Use this when you need to perform system operations or run specific commands to accomplish any step in the user's task. You must tailor your command to the user's system and provide a clear explanation of what the command does. For command chaining, use the appropriate chaining syntax for the user's shell. Prefer to execute complex CLI commands over creating executable scripts, as they are more flexible and easier to run. Commands will be executed in the current working directory: /test/path @@ -895,7 +895,7 @@ CAPABILITIES - When the user initially gives you a task, a recursive list of all filepaths in the current working directory ('/test/path') will be included in environment_details. This provides an overview of the project's file structure, offering key insights into the project from directory/file names (how developers conceptualize and organize their code) and file extensions (the language used). This can also guide decision-making on which files to explore further. If you need to further explore directories such as outside the current working directory, you can use the list_files tool. If you pass 'true' for the recursive parameter, it will list files recursively. Otherwise, it will list files at the top level, which is better suited for generic directories where you don't necessarily need the nested structure, like the Desktop. - You can use search_files to perform regex searches across files in a specified directory, outputting context-rich results that include surrounding lines. This is particularly useful for understanding code patterns, finding specific implementations, or identifying areas that need refactoring. - You can use the list_code_definition_names tool to get an overview of source code definitions for all files at the top level of a specified directory. This can be particularly useful when you need to understand the broader context and relationships between certain parts of the code. You may need to call this tool multiple times to understand various parts of the codebase related to the task. - - For example, when asked to make edits or improvements you might analyze the file structure in the initial environment_details to get an overview of the project, then use list_code_definition_names to get further insight using source code definitions for files located in relevant directories, then read_file to examine the contents of relevant files, analyze the code and suggest improvements or make necessary edits, then use the write_to_file tool to apply the changes. If you refactored code that could affect other parts of the codebase, you could use search_files to ensure you update other files as needed. + - For example, when asked to make edits or improvements you might analyze the file structure in the initial environment_details to get an overview of the project, then use list_code_definition_names to get further insight using source code definitions for files located in relevant directories, then read_file to examine the contents of relevant files, analyze the code and suggest improvements or make necessary edits, then use the create_file tool to apply the changes. If you refactored code that could affect other parts of the codebase, you could use search_files to ensure you update other files as needed. - You can use the execute_command tool to run commands on the user's computer whenever you feel it can help accomplish the user's task. When you need to execute a CLI command, you must provide a clear explanation of what the command does. Prefer to execute complex CLI commands over creating executable scripts, since they are more flexible and easier to run. Interactive and long-running commands are allowed, since the commands are run in the user's VSCode terminal. The user may keep commands running in the background and you will be kept updated on their status along the way. Each command you execute is run in a new terminal instance. ==== @@ -912,9 +912,9 @@ RULES - You cannot \`cd\` into a different directory to complete a task. You are stuck operating from '/test/path', so be sure to pass in the correct 'path' parameter when using tools that require a path. - Do not use the ~ character or $HOME to refer to the home directory. - Before using the execute_command tool, you must first think about the SYSTEM INFORMATION context provided to understand the user's environment and tailor your commands to ensure they are compatible with their system. You must also consider if the command you need to run should be executed in a specific directory outside of the current working directory '/test/path', and if so prepend with \`cd\`'ing into that directory && then executing the command (as one command since you are stuck operating from '/test/path'). For example, if you needed to run \`npm install\` in a project outside of '/test/path', you would need to prepend with a \`cd\` i.e. pseudocode for this would be \`cd (path to project) && (command, in this case npm install)\`. -- When using the search_files tool, craft your regex patterns carefully to balance specificity and flexibility. Based on the user's task you may use it to find code patterns, TODO comments, function definitions, or any text-based information across the project. The results include context, so analyze the surrounding code to better understand the matches. Leverage the search_files tool in combination with other tools for more comprehensive analysis. For example, use it to find specific code patterns, then use read_file to examine the full context of interesting matches before using write_to_file to make informed changes. -- When creating a new project (such as an app, website, or any software project), organize all new files within a dedicated project directory unless the user specifies otherwise. Use appropriate file paths when writing files, as the write_to_file tool will automatically create any necessary directories. Structure the project logically, adhering to best practices for the specific type of project being created. Unless otherwise specified, new projects should be easily run without additional setup, for example most projects can be built in HTML, CSS, and JavaScript - which you can open in a browser. -- When using the write_to_file tool to modify a file, use the tool directly with the desired content. You do not need to display the content before using the tool. ALWAYS provide the COMPLETE file content in your response. This is NON-NEGOTIABLE. Partial updates or placeholders like '// rest of code unchanged' are STRICTLY FORBIDDEN. You MUST include ALL parts of the file, even if they haven't been modified. Failure to do so will result in incomplete or broken code, severely impacting the user's project. +- When using the search_files tool, craft your regex patterns carefully to balance specificity and flexibility. Based on the user's task you may use it to find code patterns, TODO comments, function definitions, or any text-based information across the project. The results include context, so analyze the surrounding code to better understand the matches. Leverage the search_files tool in combination with other tools for more comprehensive analysis. For example, use it to find specific code patterns, then use read_file to examine the full context of interesting matches before using create_file to make informed changes. +- When creating a new project (such as an app, website, or any software project), organize all new files within a dedicated project directory unless the user specifies otherwise. Use appropriate file paths when writing files, as the create_file tool will automatically create any necessary directories. Structure the project logically, adhering to best practices for the specific type of project being created. Unless otherwise specified, new projects should be easily run without additional setup, for example most projects can be built in HTML, CSS, and JavaScript - which you can open in a browser. +- When using the create_file tool to modify a file, use the tool directly with the desired content. You do not need to display the content before using the tool. ALWAYS provide the COMPLETE file content in your response. This is NON-NEGOTIABLE. Partial updates or placeholders like '// rest of code unchanged' are STRICTLY FORBIDDEN. You MUST include ALL parts of the file, even if they haven't been modified. Failure to do so will result in incomplete or broken code, severely impacting the user's project. - Some modes have restrictions on which files they can edit. If you attempt to edit a restricted file, the operation will be rejected with a FileRestrictionError that will specify which file patterns are allowed for the current mode. - Be sure to consider the type of project (e.g. Python, JavaScript, web application) when determining the appropriate structure and files to include. Also consider what files may be most relevant to accomplishing the task, for example looking at a project's manifest file would help you understand the project's dependencies, which you could incorporate into any code you write. * For example, in architect mode trying to edit app.js would be rejected because architect mode can only edit files matching "\\.md$" @@ -1063,23 +1063,23 @@ Example: Requesting to list all top level source code definitions in the current . -## write_to_file +## create_file Description: Request to write full content to a file at the specified path. If the file exists, it will be overwritten with the provided content. If the file doesn't exist, it will be created. This tool will automatically create any directories needed to write the file. Parameters: - path: (required) The path of the file to write to (relative to the current working directory /test/path) - content: (required) The content to write to the file. ALWAYS provide the COMPLETE intended content of the file, without any truncation or omissions. You MUST include ALL parts of the file, even if they haven't been modified. Do NOT include the line numbers in the content though, just the actual content of the file. - line_count: (required) The number of lines in the file. Make sure to compute this based on the actual content of the file, not the number of lines in the content you're providing. Usage: - + File path here Your file content here total number of lines in the file, including empty lines - + Example: Requesting to write to frontend-config.json - + frontend-config.json { @@ -1098,7 +1098,7 @@ Example: Requesting to write to frontend-config.json } 14 - + ## browser_action Description: Request to interact with a Puppeteer-controlled browser. Every action, except \`close\`, will be responded to with a screenshot of the browser's current state, along with any new console logs. You may only perform one browser action per message, and wait for the user's response including a screenshot and logs to determine the next action. @@ -1264,7 +1264,7 @@ CAPABILITIES - When the user initially gives you a task, a recursive list of all filepaths in the current working directory ('/test/path') will be included in environment_details. This provides an overview of the project's file structure, offering key insights into the project from directory/file names (how developers conceptualize and organize their code) and file extensions (the language used). This can also guide decision-making on which files to explore further. If you need to further explore directories such as outside the current working directory, you can use the list_files tool. If you pass 'true' for the recursive parameter, it will list files recursively. Otherwise, it will list files at the top level, which is better suited for generic directories where you don't necessarily need the nested structure, like the Desktop. - You can use search_files to perform regex searches across files in a specified directory, outputting context-rich results that include surrounding lines. This is particularly useful for understanding code patterns, finding specific implementations, or identifying areas that need refactoring. - You can use the list_code_definition_names tool to get an overview of source code definitions for all files at the top level of a specified directory. This can be particularly useful when you need to understand the broader context and relationships between certain parts of the code. You may need to call this tool multiple times to understand various parts of the codebase related to the task. - - For example, when asked to make edits or improvements you might analyze the file structure in the initial environment_details to get an overview of the project, then use list_code_definition_names to get further insight using source code definitions for files located in relevant directories, then read_file to examine the contents of relevant files, analyze the code and suggest improvements or make necessary edits, then use the write_to_file tool to apply the changes. If you refactored code that could affect other parts of the codebase, you could use search_files to ensure you update other files as needed. + - For example, when asked to make edits or improvements you might analyze the file structure in the initial environment_details to get an overview of the project, then use list_code_definition_names to get further insight using source code definitions for files located in relevant directories, then read_file to examine the contents of relevant files, analyze the code and suggest improvements or make necessary edits, then use the create_file tool to apply the changes. If you refactored code that could affect other parts of the codebase, you could use search_files to ensure you update other files as needed. - You can use the execute_command tool to run commands on the user's computer whenever you feel it can help accomplish the user's task. When you need to execute a CLI command, you must provide a clear explanation of what the command does. Prefer to execute complex CLI commands over creating executable scripts, since they are more flexible and easier to run. Interactive and long-running commands are allowed, since the commands are run in the user's VSCode terminal. The user may keep commands running in the background and you will be kept updated on their status along the way. Each command you execute is run in a new terminal instance. - You can use the browser_action tool to interact with websites (including html files and locally running development servers) through a Puppeteer-controlled browser when you feel it is necessary in accomplishing the user's task. This tool is particularly useful for web development tasks as it allows you to launch a browser, navigate to pages, interact with elements through clicks and keyboard input, and capture the results through screenshots and console logs. This tool may be useful at key stages of web development tasks-such as after implementing new features, making substantial changes, when troubleshooting issues, or to verify the result of your work. You can analyze the provided screenshots to ensure correct rendering or identify errors, and review console logs for runtime issues. - For example, if asked to add a component to a react website, you might create the necessary files, use execute_command to run the site locally, then use browser_action to launch the browser, navigate to the local server, and verify the component renders & functions correctly before closing the browser. @@ -1283,9 +1283,9 @@ RULES - You cannot \`cd\` into a different directory to complete a task. You are stuck operating from '/test/path', so be sure to pass in the correct 'path' parameter when using tools that require a path. - Do not use the ~ character or $HOME to refer to the home directory. - Before using the execute_command tool, you must first think about the SYSTEM INFORMATION context provided to understand the user's environment and tailor your commands to ensure they are compatible with their system. You must also consider if the command you need to run should be executed in a specific directory outside of the current working directory '/test/path', and if so prepend with \`cd\`'ing into that directory && then executing the command (as one command since you are stuck operating from '/test/path'). For example, if you needed to run \`npm install\` in a project outside of '/test/path', you would need to prepend with a \`cd\` i.e. pseudocode for this would be \`cd (path to project) && (command, in this case npm install)\`. -- When using the search_files tool, craft your regex patterns carefully to balance specificity and flexibility. Based on the user's task you may use it to find code patterns, TODO comments, function definitions, or any text-based information across the project. The results include context, so analyze the surrounding code to better understand the matches. Leverage the search_files tool in combination with other tools for more comprehensive analysis. For example, use it to find specific code patterns, then use read_file to examine the full context of interesting matches before using write_to_file to make informed changes. -- When creating a new project (such as an app, website, or any software project), organize all new files within a dedicated project directory unless the user specifies otherwise. Use appropriate file paths when writing files, as the write_to_file tool will automatically create any necessary directories. Structure the project logically, adhering to best practices for the specific type of project being created. Unless otherwise specified, new projects should be easily run without additional setup, for example most projects can be built in HTML, CSS, and JavaScript - which you can open in a browser. -- When using the write_to_file tool to modify a file, use the tool directly with the desired content. You do not need to display the content before using the tool. ALWAYS provide the COMPLETE file content in your response. This is NON-NEGOTIABLE. Partial updates or placeholders like '// rest of code unchanged' are STRICTLY FORBIDDEN. You MUST include ALL parts of the file, even if they haven't been modified. Failure to do so will result in incomplete or broken code, severely impacting the user's project. +- When using the search_files tool, craft your regex patterns carefully to balance specificity and flexibility. Based on the user's task you may use it to find code patterns, TODO comments, function definitions, or any text-based information across the project. The results include context, so analyze the surrounding code to better understand the matches. Leverage the search_files tool in combination with other tools for more comprehensive analysis. For example, use it to find specific code patterns, then use read_file to examine the full context of interesting matches before using create_file to make informed changes. +- When creating a new project (such as an app, website, or any software project), organize all new files within a dedicated project directory unless the user specifies otherwise. Use appropriate file paths when writing files, as the create_file tool will automatically create any necessary directories. Structure the project logically, adhering to best practices for the specific type of project being created. Unless otherwise specified, new projects should be easily run without additional setup, for example most projects can be built in HTML, CSS, and JavaScript - which you can open in a browser. +- When using the create_file tool to modify a file, use the tool directly with the desired content. You do not need to display the content before using the tool. ALWAYS provide the COMPLETE file content in your response. This is NON-NEGOTIABLE. Partial updates or placeholders like '// rest of code unchanged' are STRICTLY FORBIDDEN. You MUST include ALL parts of the file, even if they haven't been modified. Failure to do so will result in incomplete or broken code, severely impacting the user's project. - Some modes have restrictions on which files they can edit. If you attempt to edit a restricted file, the operation will be rejected with a FileRestrictionError that will specify which file patterns are allowed for the current mode. - Be sure to consider the type of project (e.g. Python, JavaScript, web application) when determining the appropriate structure and files to include. Also consider what files may be most relevant to accomplishing the task, for example looking at a project's manifest file would help you understand the project's dependencies, which you could incorporate into any code you write. * For example, in architect mode trying to edit app.js would be rejected because architect mode can only edit files matching "\\.md$" @@ -1435,23 +1435,23 @@ Example: Requesting to list all top level source code definitions in the current . -## write_to_file +## create_file Description: Request to write full content to a file at the specified path. If the file exists, it will be overwritten with the provided content. If the file doesn't exist, it will be created. This tool will automatically create any directories needed to write the file. Parameters: - path: (required) The path of the file to write to (relative to the current working directory /test/path) - content: (required) The content to write to the file. ALWAYS provide the COMPLETE intended content of the file, without any truncation or omissions. You MUST include ALL parts of the file, even if they haven't been modified. Do NOT include the line numbers in the content though, just the actual content of the file. - line_count: (required) The number of lines in the file. Make sure to compute this based on the actual content of the file, not the number of lines in the content you're providing. Usage: - + File path here Your file content here total number of lines in the file, including empty lines - + Example: Requesting to write to frontend-config.json - + frontend-config.json { @@ -1470,7 +1470,7 @@ Example: Requesting to write to frontend-config.json } 14 - + ## execute_command Description: Request to execute a CLI command on the system. Use this when you need to perform system operations or run specific commands to accomplish any step in the user's task. You must tailor your command to the user's system and provide a clear explanation of what the command does. For command chaining, use the appropriate chaining syntax for the user's shell. Prefer to execute complex CLI commands over creating executable scripts, as they are more flexible and easier to run. Commands will be executed in the current working directory: /test/path @@ -1983,7 +1983,7 @@ IMPORTANT: Regardless of what else you see in the MCP settings file, you must de ## Editing MCP Servers -The user may ask to add tools or resources that may make sense to add to an existing MCP server (listed under 'Connected MCP Servers' above: (None running currently), e.g. if it would use the same API. This would be possible if you can locate the MCP server repository on the user's system by looking at the server arguments for a filepath. You might then use list_files and read_file to explore the files in the repository, and use write_to_file to make changes to the files. +The user may ask to add tools or resources that may make sense to add to an existing MCP server (listed under 'Connected MCP Servers' above: (None running currently), e.g. if it would use the same API. This would be possible if you can locate the MCP server repository on the user's system by looking at the server arguments for a filepath. You might then use list_files and read_file to explore the files in the repository, and use create_file to make changes to the files. However some MCP servers may be running from installed packages rather than a local repository, in which case it may make more sense to create a new MCP server. @@ -2001,7 +2001,7 @@ CAPABILITIES - When the user initially gives you a task, a recursive list of all filepaths in the current working directory ('/test/path') will be included in environment_details. This provides an overview of the project's file structure, offering key insights into the project from directory/file names (how developers conceptualize and organize their code) and file extensions (the language used). This can also guide decision-making on which files to explore further. If you need to further explore directories such as outside the current working directory, you can use the list_files tool. If you pass 'true' for the recursive parameter, it will list files recursively. Otherwise, it will list files at the top level, which is better suited for generic directories where you don't necessarily need the nested structure, like the Desktop. - You can use search_files to perform regex searches across files in a specified directory, outputting context-rich results that include surrounding lines. This is particularly useful for understanding code patterns, finding specific implementations, or identifying areas that need refactoring. - You can use the list_code_definition_names tool to get an overview of source code definitions for all files at the top level of a specified directory. This can be particularly useful when you need to understand the broader context and relationships between certain parts of the code. You may need to call this tool multiple times to understand various parts of the codebase related to the task. - - For example, when asked to make edits or improvements you might analyze the file structure in the initial environment_details to get an overview of the project, then use list_code_definition_names to get further insight using source code definitions for files located in relevant directories, then read_file to examine the contents of relevant files, analyze the code and suggest improvements or make necessary edits, then use the write_to_file tool to apply the changes. If you refactored code that could affect other parts of the codebase, you could use search_files to ensure you update other files as needed. + - For example, when asked to make edits or improvements you might analyze the file structure in the initial environment_details to get an overview of the project, then use list_code_definition_names to get further insight using source code definitions for files located in relevant directories, then read_file to examine the contents of relevant files, analyze the code and suggest improvements or make necessary edits, then use the create_file tool to apply the changes. If you refactored code that could affect other parts of the codebase, you could use search_files to ensure you update other files as needed. - You can use the execute_command tool to run commands on the user's computer whenever you feel it can help accomplish the user's task. When you need to execute a CLI command, you must provide a clear explanation of what the command does. Prefer to execute complex CLI commands over creating executable scripts, since they are more flexible and easier to run. Interactive and long-running commands are allowed, since the commands are run in the user's VSCode terminal. The user may keep commands running in the background and you will be kept updated on their status along the way. Each command you execute is run in a new terminal instance. - You have access to MCP servers that may provide additional tools and resources. Each server may provide different capabilities that you can use to accomplish tasks more effectively. @@ -2020,9 +2020,9 @@ RULES - You cannot \`cd\` into a different directory to complete a task. You are stuck operating from '/test/path', so be sure to pass in the correct 'path' parameter when using tools that require a path. - Do not use the ~ character or $HOME to refer to the home directory. - Before using the execute_command tool, you must first think about the SYSTEM INFORMATION context provided to understand the user's environment and tailor your commands to ensure they are compatible with their system. You must also consider if the command you need to run should be executed in a specific directory outside of the current working directory '/test/path', and if so prepend with \`cd\`'ing into that directory && then executing the command (as one command since you are stuck operating from '/test/path'). For example, if you needed to run \`npm install\` in a project outside of '/test/path', you would need to prepend with a \`cd\` i.e. pseudocode for this would be \`cd (path to project) && (command, in this case npm install)\`. -- When using the search_files tool, craft your regex patterns carefully to balance specificity and flexibility. Based on the user's task you may use it to find code patterns, TODO comments, function definitions, or any text-based information across the project. The results include context, so analyze the surrounding code to better understand the matches. Leverage the search_files tool in combination with other tools for more comprehensive analysis. For example, use it to find specific code patterns, then use read_file to examine the full context of interesting matches before using write_to_file to make informed changes. -- When creating a new project (such as an app, website, or any software project), organize all new files within a dedicated project directory unless the user specifies otherwise. Use appropriate file paths when writing files, as the write_to_file tool will automatically create any necessary directories. Structure the project logically, adhering to best practices for the specific type of project being created. Unless otherwise specified, new projects should be easily run without additional setup, for example most projects can be built in HTML, CSS, and JavaScript - which you can open in a browser. -- When using the write_to_file tool to modify a file, use the tool directly with the desired content. You do not need to display the content before using the tool. ALWAYS provide the COMPLETE file content in your response. This is NON-NEGOTIABLE. Partial updates or placeholders like '// rest of code unchanged' are STRICTLY FORBIDDEN. You MUST include ALL parts of the file, even if they haven't been modified. Failure to do so will result in incomplete or broken code, severely impacting the user's project. +- When using the search_files tool, craft your regex patterns carefully to balance specificity and flexibility. Based on the user's task you may use it to find code patterns, TODO comments, function definitions, or any text-based information across the project. The results include context, so analyze the surrounding code to better understand the matches. Leverage the search_files tool in combination with other tools for more comprehensive analysis. For example, use it to find specific code patterns, then use read_file to examine the full context of interesting matches before using create_file to make informed changes. +- When creating a new project (such as an app, website, or any software project), organize all new files within a dedicated project directory unless the user specifies otherwise. Use appropriate file paths when writing files, as the create_file tool will automatically create any necessary directories. Structure the project logically, adhering to best practices for the specific type of project being created. Unless otherwise specified, new projects should be easily run without additional setup, for example most projects can be built in HTML, CSS, and JavaScript - which you can open in a browser. +- When using the create_file tool to modify a file, use the tool directly with the desired content. You do not need to display the content before using the tool. ALWAYS provide the COMPLETE file content in your response. This is NON-NEGOTIABLE. Partial updates or placeholders like '// rest of code unchanged' are STRICTLY FORBIDDEN. You MUST include ALL parts of the file, even if they haven't been modified. Failure to do so will result in incomplete or broken code, severely impacting the user's project. - Some modes have restrictions on which files they can edit. If you attempt to edit a restricted file, the operation will be rejected with a FileRestrictionError that will specify which file patterns are allowed for the current mode. - Be sure to consider the type of project (e.g. Python, JavaScript, web application) when determining the appropriate structure and files to include. Also consider what files may be most relevant to accomplishing the task, for example looking at a project's manifest file would help you understand the project's dependencies, which you could incorporate into any code you write. * For example, in architect mode trying to edit app.js would be rejected because architect mode can only edit files matching "\\.md$" @@ -2171,23 +2171,23 @@ Example: Requesting to list all top level source code definitions in the current . -## write_to_file +## create_file Description: Request to write full content to a file at the specified path. If the file exists, it will be overwritten with the provided content. If the file doesn't exist, it will be created. This tool will automatically create any directories needed to write the file. Parameters: - path: (required) The path of the file to write to (relative to the current working directory /test/path) - content: (required) The content to write to the file. ALWAYS provide the COMPLETE intended content of the file, without any truncation or omissions. You MUST include ALL parts of the file, even if they haven't been modified. Do NOT include the line numbers in the content though, just the actual content of the file. - line_count: (required) The number of lines in the file. Make sure to compute this based on the actual content of the file, not the number of lines in the content you're providing. Usage: - + File path here Your file content here total number of lines in the file, including empty lines - + Example: Requesting to write to frontend-config.json - + frontend-config.json { @@ -2206,7 +2206,7 @@ Example: Requesting to write to frontend-config.json } 14 - + ## browser_action Description: Request to interact with a Puppeteer-controlled browser. Every action, except \`close\`, will be responded to with a screenshot of the browser's current state, along with any new console logs. You may only perform one browser action per message, and wait for the user's response including a screenshot and logs to determine the next action. @@ -2372,7 +2372,7 @@ CAPABILITIES - When the user initially gives you a task, a recursive list of all filepaths in the current working directory ('/test/path') will be included in environment_details. This provides an overview of the project's file structure, offering key insights into the project from directory/file names (how developers conceptualize and organize their code) and file extensions (the language used). This can also guide decision-making on which files to explore further. If you need to further explore directories such as outside the current working directory, you can use the list_files tool. If you pass 'true' for the recursive parameter, it will list files recursively. Otherwise, it will list files at the top level, which is better suited for generic directories where you don't necessarily need the nested structure, like the Desktop. - You can use search_files to perform regex searches across files in a specified directory, outputting context-rich results that include surrounding lines. This is particularly useful for understanding code patterns, finding specific implementations, or identifying areas that need refactoring. - You can use the list_code_definition_names tool to get an overview of source code definitions for all files at the top level of a specified directory. This can be particularly useful when you need to understand the broader context and relationships between certain parts of the code. You may need to call this tool multiple times to understand various parts of the codebase related to the task. - - For example, when asked to make edits or improvements you might analyze the file structure in the initial environment_details to get an overview of the project, then use list_code_definition_names to get further insight using source code definitions for files located in relevant directories, then read_file to examine the contents of relevant files, analyze the code and suggest improvements or make necessary edits, then use the write_to_file tool to apply the changes. If you refactored code that could affect other parts of the codebase, you could use search_files to ensure you update other files as needed. + - For example, when asked to make edits or improvements you might analyze the file structure in the initial environment_details to get an overview of the project, then use list_code_definition_names to get further insight using source code definitions for files located in relevant directories, then read_file to examine the contents of relevant files, analyze the code and suggest improvements or make necessary edits, then use the create_file tool to apply the changes. If you refactored code that could affect other parts of the codebase, you could use search_files to ensure you update other files as needed. - You can use the execute_command tool to run commands on the user's computer whenever you feel it can help accomplish the user's task. When you need to execute a CLI command, you must provide a clear explanation of what the command does. Prefer to execute complex CLI commands over creating executable scripts, since they are more flexible and easier to run. Interactive and long-running commands are allowed, since the commands are run in the user's VSCode terminal. The user may keep commands running in the background and you will be kept updated on their status along the way. Each command you execute is run in a new terminal instance. - You can use the browser_action tool to interact with websites (including html files and locally running development servers) through a Puppeteer-controlled browser when you feel it is necessary in accomplishing the user's task. This tool is particularly useful for web development tasks as it allows you to launch a browser, navigate to pages, interact with elements through clicks and keyboard input, and capture the results through screenshots and console logs. This tool may be useful at key stages of web development tasks-such as after implementing new features, making substantial changes, when troubleshooting issues, or to verify the result of your work. You can analyze the provided screenshots to ensure correct rendering or identify errors, and review console logs for runtime issues. - For example, if asked to add a component to a react website, you might create the necessary files, use execute_command to run the site locally, then use browser_action to launch the browser, navigate to the local server, and verify the component renders & functions correctly before closing the browser. @@ -2391,9 +2391,9 @@ RULES - You cannot \`cd\` into a different directory to complete a task. You are stuck operating from '/test/path', so be sure to pass in the correct 'path' parameter when using tools that require a path. - Do not use the ~ character or $HOME to refer to the home directory. - Before using the execute_command tool, you must first think about the SYSTEM INFORMATION context provided to understand the user's environment and tailor your commands to ensure they are compatible with their system. You must also consider if the command you need to run should be executed in a specific directory outside of the current working directory '/test/path', and if so prepend with \`cd\`'ing into that directory && then executing the command (as one command since you are stuck operating from '/test/path'). For example, if you needed to run \`npm install\` in a project outside of '/test/path', you would need to prepend with a \`cd\` i.e. pseudocode for this would be \`cd (path to project) && (command, in this case npm install)\`. -- When using the search_files tool, craft your regex patterns carefully to balance specificity and flexibility. Based on the user's task you may use it to find code patterns, TODO comments, function definitions, or any text-based information across the project. The results include context, so analyze the surrounding code to better understand the matches. Leverage the search_files tool in combination with other tools for more comprehensive analysis. For example, use it to find specific code patterns, then use read_file to examine the full context of interesting matches before using write_to_file to make informed changes. -- When creating a new project (such as an app, website, or any software project), organize all new files within a dedicated project directory unless the user specifies otherwise. Use appropriate file paths when writing files, as the write_to_file tool will automatically create any necessary directories. Structure the project logically, adhering to best practices for the specific type of project being created. Unless otherwise specified, new projects should be easily run without additional setup, for example most projects can be built in HTML, CSS, and JavaScript - which you can open in a browser. -- When using the write_to_file tool to modify a file, use the tool directly with the desired content. You do not need to display the content before using the tool. ALWAYS provide the COMPLETE file content in your response. This is NON-NEGOTIABLE. Partial updates or placeholders like '// rest of code unchanged' are STRICTLY FORBIDDEN. You MUST include ALL parts of the file, even if they haven't been modified. Failure to do so will result in incomplete or broken code, severely impacting the user's project. +- When using the search_files tool, craft your regex patterns carefully to balance specificity and flexibility. Based on the user's task you may use it to find code patterns, TODO comments, function definitions, or any text-based information across the project. The results include context, so analyze the surrounding code to better understand the matches. Leverage the search_files tool in combination with other tools for more comprehensive analysis. For example, use it to find specific code patterns, then use read_file to examine the full context of interesting matches before using create_file to make informed changes. +- When creating a new project (such as an app, website, or any software project), organize all new files within a dedicated project directory unless the user specifies otherwise. Use appropriate file paths when writing files, as the create_file tool will automatically create any necessary directories. Structure the project logically, adhering to best practices for the specific type of project being created. Unless otherwise specified, new projects should be easily run without additional setup, for example most projects can be built in HTML, CSS, and JavaScript - which you can open in a browser. +- When using the create_file tool to modify a file, use the tool directly with the desired content. You do not need to display the content before using the tool. ALWAYS provide the COMPLETE file content in your response. This is NON-NEGOTIABLE. Partial updates or placeholders like '// rest of code unchanged' are STRICTLY FORBIDDEN. You MUST include ALL parts of the file, even if they haven't been modified. Failure to do so will result in incomplete or broken code, severely impacting the user's project. - Some modes have restrictions on which files they can edit. If you attempt to edit a restricted file, the operation will be rejected with a FileRestrictionError that will specify which file patterns are allowed for the current mode. - Be sure to consider the type of project (e.g. Python, JavaScript, web application) when determining the appropriate structure and files to include. Also consider what files may be most relevant to accomplishing the task, for example looking at a project's manifest file would help you understand the project's dependencies, which you could incorporate into any code you write. * For example, in architect mode trying to edit app.js would be rejected because architect mode can only edit files matching "\\.md$" @@ -2543,43 +2543,6 @@ Example: Requesting to list all top level source code definitions in the current . -## write_to_file -Description: Request to write full content to a file at the specified path. If the file exists, it will be overwritten with the provided content. If the file doesn't exist, it will be created. This tool will automatically create any directories needed to write the file. -Parameters: -- path: (required) The path of the file to write to (relative to the current working directory /test/path) -- content: (required) The content to write to the file. ALWAYS provide the COMPLETE intended content of the file, without any truncation or omissions. You MUST include ALL parts of the file, even if they haven't been modified. Do NOT include the line numbers in the content though, just the actual content of the file. -- line_count: (required) The number of lines in the file. Make sure to compute this based on the actual content of the file, not the number of lines in the content you're providing. -Usage: - -File path here - -Your file content here - -total number of lines in the file, including empty lines - - -Example: Requesting to write to frontend-config.json - -frontend-config.json - -{ - "apiEndpoint": "https://api.example.com", - "theme": { - "primaryColor": "#007bff", - "secondaryColor": "#6c757d", - "fontFamily": "Arial, sans-serif" - }, - "features": { - "darkMode": true, - "notifications": true, - "analytics": false - }, - "version": "1.0.0" -} - -14 - - ## edit_file Description: Request to replace existing code using a search and replace block. This tool allows for precise, surgical replaces to files by specifying exactly what content to search for and what to replace it with. @@ -2640,6 +2603,43 @@ Your search/replace content here 5 +## create_file +Description: Request to write full content to a file at the specified path. If the file exists, it will be overwritten with the provided content. If the file doesn't exist, it will be created. This tool will automatically create any directories needed to write the file. +Parameters: +- path: (required) The path of the file to write to (relative to the current working directory /test/path) +- content: (required) The content to write to the file. ALWAYS provide the COMPLETE intended content of the file, without any truncation or omissions. You MUST include ALL parts of the file, even if they haven't been modified. Do NOT include the line numbers in the content though, just the actual content of the file. +- line_count: (required) The number of lines in the file. Make sure to compute this based on the actual content of the file, not the number of lines in the content you're providing. +Usage: + +File path here + +Your file content here + +total number of lines in the file, including empty lines + + +Example: Requesting to write to frontend-config.json + +frontend-config.json + +{ + "apiEndpoint": "https://api.example.com", + "theme": { + "primaryColor": "#007bff", + "secondaryColor": "#6c757d", + "fontFamily": "Arial, sans-serif" + }, + "features": { + "darkMode": true, + "notifications": true, + "analytics": false + }, + "version": "1.0.0" +} + +14 + + ## execute_command Description: Request to execute a CLI command on the system. Use this when you need to perform system operations or run specific commands to accomplish any step in the user's task. You must tailor your command to the user's system and provide a clear explanation of what the command does. For command chaining, use the appropriate chaining syntax for the user's shell. Prefer to execute complex CLI commands over creating executable scripts, as they are more flexible and easier to run. Commands will be executed in the current working directory: /test/path Parameters: @@ -2758,7 +2758,7 @@ CAPABILITIES - When the user initially gives you a task, a recursive list of all filepaths in the current working directory ('/test/path') will be included in environment_details. This provides an overview of the project's file structure, offering key insights into the project from directory/file names (how developers conceptualize and organize their code) and file extensions (the language used). This can also guide decision-making on which files to explore further. If you need to further explore directories such as outside the current working directory, you can use the list_files tool. If you pass 'true' for the recursive parameter, it will list files recursively. Otherwise, it will list files at the top level, which is better suited for generic directories where you don't necessarily need the nested structure, like the Desktop. - You can use search_files to perform regex searches across files in a specified directory, outputting context-rich results that include surrounding lines. This is particularly useful for understanding code patterns, finding specific implementations, or identifying areas that need refactoring. - You can use the list_code_definition_names tool to get an overview of source code definitions for all files at the top level of a specified directory. This can be particularly useful when you need to understand the broader context and relationships between certain parts of the code. You may need to call this tool multiple times to understand various parts of the codebase related to the task. - - For example, when asked to make edits or improvements you might analyze the file structure in the initial environment_details to get an overview of the project, then use list_code_definition_names to get further insight using source code definitions for files located in relevant directories, then read_file to examine the contents of relevant files, analyze the code and suggest improvements or make necessary edits, then use the write_to_file or edit_file tool to apply the changes. If you refactored code that could affect other parts of the codebase, you could use search_files to ensure you update other files as needed. + - For example, when asked to make edits or improvements you might analyze the file structure in the initial environment_details to get an overview of the project, then use list_code_definition_names to get further insight using source code definitions for files located in relevant directories, then read_file to examine the contents of relevant files, analyze the code and suggest improvements or make necessary edits, then use the edit_file or create_file tool to apply the changes. If you refactored code that could affect other parts of the codebase, you could use search_files to ensure you update other files as needed. - You can use the execute_command tool to run commands on the user's computer whenever you feel it can help accomplish the user's task. When you need to execute a CLI command, you must provide a clear explanation of what the command does. Prefer to execute complex CLI commands over creating executable scripts, since they are more flexible and easier to run. Interactive and long-running commands are allowed, since the commands are run in the user's VSCode terminal. The user may keep commands running in the background and you will be kept updated on their status along the way. Each command you execute is run in a new terminal instance. ==== @@ -2775,11 +2775,11 @@ RULES - You cannot \`cd\` into a different directory to complete a task. You are stuck operating from '/test/path', so be sure to pass in the correct 'path' parameter when using tools that require a path. - Do not use the ~ character or $HOME to refer to the home directory. - Before using the execute_command tool, you must first think about the SYSTEM INFORMATION context provided to understand the user's environment and tailor your commands to ensure they are compatible with their system. You must also consider if the command you need to run should be executed in a specific directory outside of the current working directory '/test/path', and if so prepend with \`cd\`'ing into that directory && then executing the command (as one command since you are stuck operating from '/test/path'). For example, if you needed to run \`npm install\` in a project outside of '/test/path', you would need to prepend with a \`cd\` i.e. pseudocode for this would be \`cd (path to project) && (command, in this case npm install)\`. -- When using the search_files tool, craft your regex patterns carefully to balance specificity and flexibility. Based on the user's task you may use it to find code patterns, TODO comments, function definitions, or any text-based information across the project. The results include context, so analyze the surrounding code to better understand the matches. Leverage the search_files tool in combination with other tools for more comprehensive analysis. For example, use it to find specific code patterns, then use read_file to examine the full context of interesting matches before using write_to_file to make informed changes. -- When creating a new project (such as an app, website, or any software project), organize all new files within a dedicated project directory unless the user specifies otherwise. Use appropriate file paths when writing files, as the write_to_file tool will automatically create any necessary directories. Structure the project logically, adhering to best practices for the specific type of project being created. Unless otherwise specified, new projects should be easily run without additional setup, for example most projects can be built in HTML, CSS, and JavaScript - which you can open in a browser. -- For editing files, you have access to these tools: write_to_file (for creating new files or complete file rewrites), edit_file (for replacing lines in existing files). -- When using the write_to_file tool to modify a file, use the tool directly with the desired content. You do not need to display the content before using the tool. ALWAYS provide the COMPLETE file content in your response. This is NON-NEGOTIABLE. Partial updates or placeholders like '// rest of code unchanged' are STRICTLY FORBIDDEN. You MUST include ALL parts of the file, even if they haven't been modified. Failure to do so will result in incomplete or broken code, severely impacting the user's project. -- You should always prefer using other editing tools over write_to_file when making changes to existing files since write_to_file is much slower and cannot handle large files. +- When using the search_files tool, craft your regex patterns carefully to balance specificity and flexibility. Based on the user's task you may use it to find code patterns, TODO comments, function definitions, or any text-based information across the project. The results include context, so analyze the surrounding code to better understand the matches. Leverage the search_files tool in combination with other tools for more comprehensive analysis. For example, use it to find specific code patterns, then use read_file to examine the full context of interesting matches before using edit_file or create_file to make informed changes. +- When creating a new project (such as an app, website, or any software project), organize all new files within a dedicated project directory unless the user specifies otherwise. Use appropriate file paths when writing files, as the create_file tool will automatically create any necessary directories. Structure the project logically, adhering to best practices for the specific type of project being created. Unless otherwise specified, new projects should be easily run without additional setup, for example most projects can be built in HTML, CSS, and JavaScript - which you can open in a browser. +- For editing files, you have access to these tools: edit_file (for replacing lines in existing files), create_file (for creating new files or complete file rewrites). +- You should always prefer using other editing tools over create_file when making changes to existing files since create_file is much slower and cannot handle large files. +- When using the create_file tool to modify a file, use the tool directly with the desired content. You do not need to display the content before using the tool. ALWAYS provide the COMPLETE file content in your response. This is NON-NEGOTIABLE. Partial updates or placeholders like '// rest of code unchanged' are STRICTLY FORBIDDEN. You MUST include ALL parts of the file, even if they haven't been modified. Failure to do so will result in incomplete or broken code, severely impacting the user's project. - Some modes have restrictions on which files they can edit. If you attempt to edit a restricted file, the operation will be rejected with a FileRestrictionError that will specify which file patterns are allowed for the current mode. - Be sure to consider the type of project (e.g. Python, JavaScript, web application) when determining the appropriate structure and files to include. Also consider what files may be most relevant to accomplishing the task, for example looking at a project's manifest file would help you understand the project's dependencies, which you could incorporate into any code you write. * For example, in architect mode trying to edit app.js would be rejected because architect mode can only edit files matching "\\.md$" @@ -2928,23 +2928,23 @@ Example: Requesting to list all top level source code definitions in the current . -## write_to_file +## create_file Description: Request to write full content to a file at the specified path. If the file exists, it will be overwritten with the provided content. If the file doesn't exist, it will be created. This tool will automatically create any directories needed to write the file. Parameters: - path: (required) The path of the file to write to (relative to the current working directory /test/path) - content: (required) The content to write to the file. ALWAYS provide the COMPLETE intended content of the file, without any truncation or omissions. You MUST include ALL parts of the file, even if they haven't been modified. Do NOT include the line numbers in the content though, just the actual content of the file. - line_count: (required) The number of lines in the file. Make sure to compute this based on the actual content of the file, not the number of lines in the content you're providing. Usage: - + File path here Your file content here total number of lines in the file, including empty lines - + Example: Requesting to write to frontend-config.json - + frontend-config.json { @@ -2963,7 +2963,7 @@ Example: Requesting to write to frontend-config.json } 14 - + ## execute_command Description: Request to execute a CLI command on the system. Use this when you need to perform system operations or run specific commands to accomplish any step in the user's task. You must tailor your command to the user's system and provide a clear explanation of what the command does. For command chaining, use the appropriate chaining syntax for the user's shell. Prefer to execute complex CLI commands over creating executable scripts, as they are more flexible and easier to run. Commands will be executed in the current working directory: /test/path @@ -3083,7 +3083,7 @@ CAPABILITIES - When the user initially gives you a task, a recursive list of all filepaths in the current working directory ('/test/path') will be included in environment_details. This provides an overview of the project's file structure, offering key insights into the project from directory/file names (how developers conceptualize and organize their code) and file extensions (the language used). This can also guide decision-making on which files to explore further. If you need to further explore directories such as outside the current working directory, you can use the list_files tool. If you pass 'true' for the recursive parameter, it will list files recursively. Otherwise, it will list files at the top level, which is better suited for generic directories where you don't necessarily need the nested structure, like the Desktop. - You can use search_files to perform regex searches across files in a specified directory, outputting context-rich results that include surrounding lines. This is particularly useful for understanding code patterns, finding specific implementations, or identifying areas that need refactoring. - You can use the list_code_definition_names tool to get an overview of source code definitions for all files at the top level of a specified directory. This can be particularly useful when you need to understand the broader context and relationships between certain parts of the code. You may need to call this tool multiple times to understand various parts of the codebase related to the task. - - For example, when asked to make edits or improvements you might analyze the file structure in the initial environment_details to get an overview of the project, then use list_code_definition_names to get further insight using source code definitions for files located in relevant directories, then read_file to examine the contents of relevant files, analyze the code and suggest improvements or make necessary edits, then use the write_to_file tool to apply the changes. If you refactored code that could affect other parts of the codebase, you could use search_files to ensure you update other files as needed. + - For example, when asked to make edits or improvements you might analyze the file structure in the initial environment_details to get an overview of the project, then use list_code_definition_names to get further insight using source code definitions for files located in relevant directories, then read_file to examine the contents of relevant files, analyze the code and suggest improvements or make necessary edits, then use the create_file tool to apply the changes. If you refactored code that could affect other parts of the codebase, you could use search_files to ensure you update other files as needed. - You can use the execute_command tool to run commands on the user's computer whenever you feel it can help accomplish the user's task. When you need to execute a CLI command, you must provide a clear explanation of what the command does. Prefer to execute complex CLI commands over creating executable scripts, since they are more flexible and easier to run. Interactive and long-running commands are allowed, since the commands are run in the user's VSCode terminal. The user may keep commands running in the background and you will be kept updated on their status along the way. Each command you execute is run in a new terminal instance. ==== @@ -3100,9 +3100,9 @@ RULES - You cannot \`cd\` into a different directory to complete a task. You are stuck operating from '/test/path', so be sure to pass in the correct 'path' parameter when using tools that require a path. - Do not use the ~ character or $HOME to refer to the home directory. - Before using the execute_command tool, you must first think about the SYSTEM INFORMATION context provided to understand the user's environment and tailor your commands to ensure they are compatible with their system. You must also consider if the command you need to run should be executed in a specific directory outside of the current working directory '/test/path', and if so prepend with \`cd\`'ing into that directory && then executing the command (as one command since you are stuck operating from '/test/path'). For example, if you needed to run \`npm install\` in a project outside of '/test/path', you would need to prepend with a \`cd\` i.e. pseudocode for this would be \`cd (path to project) && (command, in this case npm install)\`. -- When using the search_files tool, craft your regex patterns carefully to balance specificity and flexibility. Based on the user's task you may use it to find code patterns, TODO comments, function definitions, or any text-based information across the project. The results include context, so analyze the surrounding code to better understand the matches. Leverage the search_files tool in combination with other tools for more comprehensive analysis. For example, use it to find specific code patterns, then use read_file to examine the full context of interesting matches before using write_to_file to make informed changes. -- When creating a new project (such as an app, website, or any software project), organize all new files within a dedicated project directory unless the user specifies otherwise. Use appropriate file paths when writing files, as the write_to_file tool will automatically create any necessary directories. Structure the project logically, adhering to best practices for the specific type of project being created. Unless otherwise specified, new projects should be easily run without additional setup, for example most projects can be built in HTML, CSS, and JavaScript - which you can open in a browser. -- When using the write_to_file tool to modify a file, use the tool directly with the desired content. You do not need to display the content before using the tool. ALWAYS provide the COMPLETE file content in your response. This is NON-NEGOTIABLE. Partial updates or placeholders like '// rest of code unchanged' are STRICTLY FORBIDDEN. You MUST include ALL parts of the file, even if they haven't been modified. Failure to do so will result in incomplete or broken code, severely impacting the user's project. +- When using the search_files tool, craft your regex patterns carefully to balance specificity and flexibility. Based on the user's task you may use it to find code patterns, TODO comments, function definitions, or any text-based information across the project. The results include context, so analyze the surrounding code to better understand the matches. Leverage the search_files tool in combination with other tools for more comprehensive analysis. For example, use it to find specific code patterns, then use read_file to examine the full context of interesting matches before using create_file to make informed changes. +- When creating a new project (such as an app, website, or any software project), organize all new files within a dedicated project directory unless the user specifies otherwise. Use appropriate file paths when writing files, as the create_file tool will automatically create any necessary directories. Structure the project logically, adhering to best practices for the specific type of project being created. Unless otherwise specified, new projects should be easily run without additional setup, for example most projects can be built in HTML, CSS, and JavaScript - which you can open in a browser. +- When using the create_file tool to modify a file, use the tool directly with the desired content. You do not need to display the content before using the tool. ALWAYS provide the COMPLETE file content in your response. This is NON-NEGOTIABLE. Partial updates or placeholders like '// rest of code unchanged' are STRICTLY FORBIDDEN. You MUST include ALL parts of the file, even if they haven't been modified. Failure to do so will result in incomplete or broken code, severely impacting the user's project. - Some modes have restrictions on which files they can edit. If you attempt to edit a restricted file, the operation will be rejected with a FileRestrictionError that will specify which file patterns are allowed for the current mode. - Be sure to consider the type of project (e.g. Python, JavaScript, web application) when determining the appropriate structure and files to include. Also consider what files may be most relevant to accomplishing the task, for example looking at a project's manifest file would help you understand the project's dependencies, which you could incorporate into any code you write. * For example, in architect mode trying to edit app.js would be rejected because architect mode can only edit files matching "\\.md$" @@ -3293,23 +3293,23 @@ Example: Requesting to list all top level source code definitions in the current . -## write_to_file +## create_file Description: Request to write full content to a file at the specified path. If the file exists, it will be overwritten with the provided content. If the file doesn't exist, it will be created. This tool will automatically create any directories needed to write the file. Parameters: - path: (required) The path of the file to write to (relative to the current working directory /test/path) - content: (required) The content to write to the file. ALWAYS provide the COMPLETE intended content of the file, without any truncation or omissions. You MUST include ALL parts of the file, even if they haven't been modified. Do NOT include the line numbers in the content though, just the actual content of the file. - line_count: (required) The number of lines in the file. Make sure to compute this based on the actual content of the file, not the number of lines in the content you're providing. Usage: - + File path here Your file content here total number of lines in the file, including empty lines - + Example: Requesting to write to frontend-config.json - + frontend-config.json { @@ -3328,7 +3328,7 @@ Example: Requesting to write to frontend-config.json } 14 - + ## execute_command Description: Request to execute a CLI command on the system. Use this when you need to perform system operations or run specific commands to accomplish any step in the user's task. You must tailor your command to the user's system and provide a clear explanation of what the command does. For command chaining, use the appropriate chaining syntax for the user's shell. Prefer to execute complex CLI commands over creating executable scripts, as they are more flexible and easier to run. Commands will be executed in the current working directory: /test/path @@ -3505,7 +3505,7 @@ CAPABILITIES - When the user initially gives you a task, a recursive list of all filepaths in the current working directory ('/test/path') will be included in environment_details. This provides an overview of the project's file structure, offering key insights into the project from directory/file names (how developers conceptualize and organize their code) and file extensions (the language used). This can also guide decision-making on which files to explore further. If you need to further explore directories such as outside the current working directory, you can use the list_files tool. If you pass 'true' for the recursive parameter, it will list files recursively. Otherwise, it will list files at the top level, which is better suited for generic directories where you don't necessarily need the nested structure, like the Desktop. - You can use search_files to perform regex searches across files in a specified directory, outputting context-rich results that include surrounding lines. This is particularly useful for understanding code patterns, finding specific implementations, or identifying areas that need refactoring. - You can use the list_code_definition_names tool to get an overview of source code definitions for all files at the top level of a specified directory. This can be particularly useful when you need to understand the broader context and relationships between certain parts of the code. You may need to call this tool multiple times to understand various parts of the codebase related to the task. - - For example, when asked to make edits or improvements you might analyze the file structure in the initial environment_details to get an overview of the project, then use list_code_definition_names to get further insight using source code definitions for files located in relevant directories, then read_file to examine the contents of relevant files, analyze the code and suggest improvements or make necessary edits, then use the write_to_file tool to apply the changes. If you refactored code that could affect other parts of the codebase, you could use search_files to ensure you update other files as needed. + - For example, when asked to make edits or improvements you might analyze the file structure in the initial environment_details to get an overview of the project, then use list_code_definition_names to get further insight using source code definitions for files located in relevant directories, then read_file to examine the contents of relevant files, analyze the code and suggest improvements or make necessary edits, then use the create_file tool to apply the changes. If you refactored code that could affect other parts of the codebase, you could use search_files to ensure you update other files as needed. - You can use the execute_command tool to run commands on the user's computer whenever you feel it can help accomplish the user's task. When you need to execute a CLI command, you must provide a clear explanation of what the command does. Prefer to execute complex CLI commands over creating executable scripts, since they are more flexible and easier to run. Interactive and long-running commands are allowed, since the commands are run in the user's VSCode terminal. The user may keep commands running in the background and you will be kept updated on their status along the way. Each command you execute is run in a new terminal instance. - You have access to MCP servers that may provide additional tools and resources. Each server may provide different capabilities that you can use to accomplish tasks more effectively. @@ -3524,9 +3524,9 @@ RULES - You cannot \`cd\` into a different directory to complete a task. You are stuck operating from '/test/path', so be sure to pass in the correct 'path' parameter when using tools that require a path. - Do not use the ~ character or $HOME to refer to the home directory. - Before using the execute_command tool, you must first think about the SYSTEM INFORMATION context provided to understand the user's environment and tailor your commands to ensure they are compatible with their system. You must also consider if the command you need to run should be executed in a specific directory outside of the current working directory '/test/path', and if so prepend with \`cd\`'ing into that directory && then executing the command (as one command since you are stuck operating from '/test/path'). For example, if you needed to run \`npm install\` in a project outside of '/test/path', you would need to prepend with a \`cd\` i.e. pseudocode for this would be \`cd (path to project) && (command, in this case npm install)\`. -- When using the search_files tool, craft your regex patterns carefully to balance specificity and flexibility. Based on the user's task you may use it to find code patterns, TODO comments, function definitions, or any text-based information across the project. The results include context, so analyze the surrounding code to better understand the matches. Leverage the search_files tool in combination with other tools for more comprehensive analysis. For example, use it to find specific code patterns, then use read_file to examine the full context of interesting matches before using write_to_file to make informed changes. -- When creating a new project (such as an app, website, or any software project), organize all new files within a dedicated project directory unless the user specifies otherwise. Use appropriate file paths when writing files, as the write_to_file tool will automatically create any necessary directories. Structure the project logically, adhering to best practices for the specific type of project being created. Unless otherwise specified, new projects should be easily run without additional setup, for example most projects can be built in HTML, CSS, and JavaScript - which you can open in a browser. -- When using the write_to_file tool to modify a file, use the tool directly with the desired content. You do not need to display the content before using the tool. ALWAYS provide the COMPLETE file content in your response. This is NON-NEGOTIABLE. Partial updates or placeholders like '// rest of code unchanged' are STRICTLY FORBIDDEN. You MUST include ALL parts of the file, even if they haven't been modified. Failure to do so will result in incomplete or broken code, severely impacting the user's project. +- When using the search_files tool, craft your regex patterns carefully to balance specificity and flexibility. Based on the user's task you may use it to find code patterns, TODO comments, function definitions, or any text-based information across the project. The results include context, so analyze the surrounding code to better understand the matches. Leverage the search_files tool in combination with other tools for more comprehensive analysis. For example, use it to find specific code patterns, then use read_file to examine the full context of interesting matches before using create_file to make informed changes. +- When creating a new project (such as an app, website, or any software project), organize all new files within a dedicated project directory unless the user specifies otherwise. Use appropriate file paths when writing files, as the create_file tool will automatically create any necessary directories. Structure the project logically, adhering to best practices for the specific type of project being created. Unless otherwise specified, new projects should be easily run without additional setup, for example most projects can be built in HTML, CSS, and JavaScript - which you can open in a browser. +- When using the create_file tool to modify a file, use the tool directly with the desired content. You do not need to display the content before using the tool. ALWAYS provide the COMPLETE file content in your response. This is NON-NEGOTIABLE. Partial updates or placeholders like '// rest of code unchanged' are STRICTLY FORBIDDEN. You MUST include ALL parts of the file, even if they haven't been modified. Failure to do so will result in incomplete or broken code, severely impacting the user's project. - Some modes have restrictions on which files they can edit. If you attempt to edit a restricted file, the operation will be rejected with a FileRestrictionError that will specify which file patterns are allowed for the current mode. - Be sure to consider the type of project (e.g. Python, JavaScript, web application) when determining the appropriate structure and files to include. Also consider what files may be most relevant to accomplishing the task, for example looking at a project's manifest file would help you understand the project's dependencies, which you could incorporate into any code you write. * For example, in architect mode trying to edit app.js would be rejected because architect mode can only edit files matching "\\.md$" @@ -3690,23 +3690,23 @@ Example: Requesting to list all top level source code definitions in the current . -## write_to_file +## create_file Description: Request to write full content to a file at the specified path. If the file exists, it will be overwritten with the provided content. If the file doesn't exist, it will be created. This tool will automatically create any directories needed to write the file. Parameters: - path: (required) The path of the file to write to (relative to the current working directory /test/path) - content: (required) The content to write to the file. ALWAYS provide the COMPLETE intended content of the file, without any truncation or omissions. You MUST include ALL parts of the file, even if they haven't been modified. Do NOT include the line numbers in the content though, just the actual content of the file. - line_count: (required) The number of lines in the file. Make sure to compute this based on the actual content of the file, not the number of lines in the content you're providing. Usage: - + File path here Your file content here total number of lines in the file, including empty lines - + Example: Requesting to write to frontend-config.json - + frontend-config.json { @@ -3725,7 +3725,7 @@ Example: Requesting to write to frontend-config.json } 14 - + ## ask_followup_question Description: Ask the user a question to gather additional information needed to complete the task. This tool should be used when you encounter ambiguities, need clarification, or require more details to proceed effectively. It allows for interactive problem-solving by enabling direct communication with the user. Use this tool judiciously to maintain a balance between gathering necessary information and avoiding excessive back-and-forth. @@ -3831,7 +3831,7 @@ CAPABILITIES - When the user initially gives you a task, a recursive list of all filepaths in the current working directory ('/test/path') will be included in environment_details. This provides an overview of the project's file structure, offering key insights into the project from directory/file names (how developers conceptualize and organize their code) and file extensions (the language used). This can also guide decision-making on which files to explore further. If you need to further explore directories such as outside the current working directory, you can use the list_files tool. If you pass 'true' for the recursive parameter, it will list files recursively. Otherwise, it will list files at the top level, which is better suited for generic directories where you don't necessarily need the nested structure, like the Desktop. - You can use search_files to perform regex searches across files in a specified directory, outputting context-rich results that include surrounding lines. This is particularly useful for understanding code patterns, finding specific implementations, or identifying areas that need refactoring. - You can use the list_code_definition_names tool to get an overview of source code definitions for all files at the top level of a specified directory. This can be particularly useful when you need to understand the broader context and relationships between certain parts of the code. You may need to call this tool multiple times to understand various parts of the codebase related to the task. - - For example, when asked to make edits or improvements you might analyze the file structure in the initial environment_details to get an overview of the project, then use list_code_definition_names to get further insight using source code definitions for files located in relevant directories, then read_file to examine the contents of relevant files, analyze the code and suggest improvements or make necessary edits, then use the write_to_file tool to apply the changes. If you refactored code that could affect other parts of the codebase, you could use search_files to ensure you update other files as needed. + - For example, when asked to make edits or improvements you might analyze the file structure in the initial environment_details to get an overview of the project, then use list_code_definition_names to get further insight using source code definitions for files located in relevant directories, then read_file to examine the contents of relevant files, analyze the code and suggest improvements or make necessary edits, then use the create_file tool to apply the changes. If you refactored code that could affect other parts of the codebase, you could use search_files to ensure you update other files as needed. - You can use the execute_command tool to run commands on the user's computer whenever you feel it can help accomplish the user's task. When you need to execute a CLI command, you must provide a clear explanation of what the command does. Prefer to execute complex CLI commands over creating executable scripts, since they are more flexible and easier to run. Interactive and long-running commands are allowed, since the commands are run in the user's VSCode terminal. The user may keep commands running in the background and you will be kept updated on their status along the way. Each command you execute is run in a new terminal instance. ==== @@ -3848,9 +3848,9 @@ RULES - You cannot \`cd\` into a different directory to complete a task. You are stuck operating from '/test/path', so be sure to pass in the correct 'path' parameter when using tools that require a path. - Do not use the ~ character or $HOME to refer to the home directory. - Before using the execute_command tool, you must first think about the SYSTEM INFORMATION context provided to understand the user's environment and tailor your commands to ensure they are compatible with their system. You must also consider if the command you need to run should be executed in a specific directory outside of the current working directory '/test/path', and if so prepend with \`cd\`'ing into that directory && then executing the command (as one command since you are stuck operating from '/test/path'). For example, if you needed to run \`npm install\` in a project outside of '/test/path', you would need to prepend with a \`cd\` i.e. pseudocode for this would be \`cd (path to project) && (command, in this case npm install)\`. -- When using the search_files tool, craft your regex patterns carefully to balance specificity and flexibility. Based on the user's task you may use it to find code patterns, TODO comments, function definitions, or any text-based information across the project. The results include context, so analyze the surrounding code to better understand the matches. Leverage the search_files tool in combination with other tools for more comprehensive analysis. For example, use it to find specific code patterns, then use read_file to examine the full context of interesting matches before using write_to_file to make informed changes. -- When creating a new project (such as an app, website, or any software project), organize all new files within a dedicated project directory unless the user specifies otherwise. Use appropriate file paths when writing files, as the write_to_file tool will automatically create any necessary directories. Structure the project logically, adhering to best practices for the specific type of project being created. Unless otherwise specified, new projects should be easily run without additional setup, for example most projects can be built in HTML, CSS, and JavaScript - which you can open in a browser. -- When using the write_to_file tool to modify a file, use the tool directly with the desired content. You do not need to display the content before using the tool. ALWAYS provide the COMPLETE file content in your response. This is NON-NEGOTIABLE. Partial updates or placeholders like '// rest of code unchanged' are STRICTLY FORBIDDEN. You MUST include ALL parts of the file, even if they haven't been modified. Failure to do so will result in incomplete or broken code, severely impacting the user's project. +- When using the search_files tool, craft your regex patterns carefully to balance specificity and flexibility. Based on the user's task you may use it to find code patterns, TODO comments, function definitions, or any text-based information across the project. The results include context, so analyze the surrounding code to better understand the matches. Leverage the search_files tool in combination with other tools for more comprehensive analysis. For example, use it to find specific code patterns, then use read_file to examine the full context of interesting matches before using create_file to make informed changes. +- When creating a new project (such as an app, website, or any software project), organize all new files within a dedicated project directory unless the user specifies otherwise. Use appropriate file paths when writing files, as the create_file tool will automatically create any necessary directories. Structure the project logically, adhering to best practices for the specific type of project being created. Unless otherwise specified, new projects should be easily run without additional setup, for example most projects can be built in HTML, CSS, and JavaScript - which you can open in a browser. +- When using the create_file tool to modify a file, use the tool directly with the desired content. You do not need to display the content before using the tool. ALWAYS provide the COMPLETE file content in your response. This is NON-NEGOTIABLE. Partial updates or placeholders like '// rest of code unchanged' are STRICTLY FORBIDDEN. You MUST include ALL parts of the file, even if they haven't been modified. Failure to do so will result in incomplete or broken code, severely impacting the user's project. - Some modes have restrictions on which files they can edit. If you attempt to edit a restricted file, the operation will be rejected with a FileRestrictionError that will specify which file patterns are allowed for the current mode. - Be sure to consider the type of project (e.g. Python, JavaScript, web application) when determining the appropriate structure and files to include. Also consider what files may be most relevant to accomplishing the task, for example looking at a project's manifest file would help you understand the project's dependencies, which you could incorporate into any code you write. * For example, in architect mode trying to edit app.js would be rejected because architect mode can only edit files matching "\\.md$" @@ -4108,7 +4108,7 @@ CAPABILITIES - When the user initially gives you a task, a recursive list of all filepaths in the current working directory ('/test/path') will be included in environment_details. This provides an overview of the project's file structure, offering key insights into the project from directory/file names (how developers conceptualize and organize their code) and file extensions (the language used). This can also guide decision-making on which files to explore further. If you need to further explore directories such as outside the current working directory, you can use the list_files tool. If you pass 'true' for the recursive parameter, it will list files recursively. Otherwise, it will list files at the top level, which is better suited for generic directories where you don't necessarily need the nested structure, like the Desktop. - You can use search_files to perform regex searches across files in a specified directory, outputting context-rich results that include surrounding lines. This is particularly useful for understanding code patterns, finding specific implementations, or identifying areas that need refactoring. - You can use the list_code_definition_names tool to get an overview of source code definitions for all files at the top level of a specified directory. This can be particularly useful when you need to understand the broader context and relationships between certain parts of the code. You may need to call this tool multiple times to understand various parts of the codebase related to the task. - - For example, when asked to make edits or improvements you might analyze the file structure in the initial environment_details to get an overview of the project, then use list_code_definition_names to get further insight using source code definitions for files located in relevant directories, then read_file to examine the contents of relevant files, analyze the code and suggest improvements or make necessary edits, then use the write_to_file tool to apply the changes. If you refactored code that could affect other parts of the codebase, you could use search_files to ensure you update other files as needed. + - For example, when asked to make edits or improvements you might analyze the file structure in the initial environment_details to get an overview of the project, then use list_code_definition_names to get further insight using source code definitions for files located in relevant directories, then read_file to examine the contents of relevant files, analyze the code and suggest improvements or make necessary edits, then use the create_file tool to apply the changes. If you refactored code that could affect other parts of the codebase, you could use search_files to ensure you update other files as needed. - You can use the execute_command tool to run commands on the user's computer whenever you feel it can help accomplish the user's task. When you need to execute a CLI command, you must provide a clear explanation of what the command does. Prefer to execute complex CLI commands over creating executable scripts, since they are more flexible and easier to run. Interactive and long-running commands are allowed, since the commands are run in the user's VSCode terminal. The user may keep commands running in the background and you will be kept updated on their status along the way. Each command you execute is run in a new terminal instance. ==== @@ -4125,9 +4125,9 @@ RULES - You cannot \`cd\` into a different directory to complete a task. You are stuck operating from '/test/path', so be sure to pass in the correct 'path' parameter when using tools that require a path. - Do not use the ~ character or $HOME to refer to the home directory. - Before using the execute_command tool, you must first think about the SYSTEM INFORMATION context provided to understand the user's environment and tailor your commands to ensure they are compatible with their system. You must also consider if the command you need to run should be executed in a specific directory outside of the current working directory '/test/path', and if so prepend with \`cd\`'ing into that directory && then executing the command (as one command since you are stuck operating from '/test/path'). For example, if you needed to run \`npm install\` in a project outside of '/test/path', you would need to prepend with a \`cd\` i.e. pseudocode for this would be \`cd (path to project) && (command, in this case npm install)\`. -- When using the search_files tool, craft your regex patterns carefully to balance specificity and flexibility. Based on the user's task you may use it to find code patterns, TODO comments, function definitions, or any text-based information across the project. The results include context, so analyze the surrounding code to better understand the matches. Leverage the search_files tool in combination with other tools for more comprehensive analysis. For example, use it to find specific code patterns, then use read_file to examine the full context of interesting matches before using write_to_file to make informed changes. -- When creating a new project (such as an app, website, or any software project), organize all new files within a dedicated project directory unless the user specifies otherwise. Use appropriate file paths when writing files, as the write_to_file tool will automatically create any necessary directories. Structure the project logically, adhering to best practices for the specific type of project being created. Unless otherwise specified, new projects should be easily run without additional setup, for example most projects can be built in HTML, CSS, and JavaScript - which you can open in a browser. -- When using the write_to_file tool to modify a file, use the tool directly with the desired content. You do not need to display the content before using the tool. ALWAYS provide the COMPLETE file content in your response. This is NON-NEGOTIABLE. Partial updates or placeholders like '// rest of code unchanged' are STRICTLY FORBIDDEN. You MUST include ALL parts of the file, even if they haven't been modified. Failure to do so will result in incomplete or broken code, severely impacting the user's project. +- When using the search_files tool, craft your regex patterns carefully to balance specificity and flexibility. Based on the user's task you may use it to find code patterns, TODO comments, function definitions, or any text-based information across the project. The results include context, so analyze the surrounding code to better understand the matches. Leverage the search_files tool in combination with other tools for more comprehensive analysis. For example, use it to find specific code patterns, then use read_file to examine the full context of interesting matches before using create_file to make informed changes. +- When creating a new project (such as an app, website, or any software project), organize all new files within a dedicated project directory unless the user specifies otherwise. Use appropriate file paths when writing files, as the create_file tool will automatically create any necessary directories. Structure the project logically, adhering to best practices for the specific type of project being created. Unless otherwise specified, new projects should be easily run without additional setup, for example most projects can be built in HTML, CSS, and JavaScript - which you can open in a browser. +- When using the create_file tool to modify a file, use the tool directly with the desired content. You do not need to display the content before using the tool. ALWAYS provide the COMPLETE file content in your response. This is NON-NEGOTIABLE. Partial updates or placeholders like '// rest of code unchanged' are STRICTLY FORBIDDEN. You MUST include ALL parts of the file, even if they haven't been modified. Failure to do so will result in incomplete or broken code, severely impacting the user's project. - Some modes have restrictions on which files they can edit. If you attempt to edit a restricted file, the operation will be rejected with a FileRestrictionError that will specify which file patterns are allowed for the current mode. - Be sure to consider the type of project (e.g. Python, JavaScript, web application) when determining the appropriate structure and files to include. Also consider what files may be most relevant to accomplishing the task, for example looking at a project's manifest file would help you understand the project's dependencies, which you could incorporate into any code you write. * For example, in architect mode trying to edit app.js would be rejected because architect mode can only edit files matching "\\.md$" @@ -4309,23 +4309,23 @@ Example: Requesting to list all top level source code definitions in the current . -## write_to_file +## create_file Description: Request to write full content to a file at the specified path. If the file exists, it will be overwritten with the provided content. If the file doesn't exist, it will be created. This tool will automatically create any directories needed to write the file. Parameters: - path: (required) The path of the file to write to (relative to the current working directory /test/path) - content: (required) The content to write to the file. ALWAYS provide the COMPLETE intended content of the file, without any truncation or omissions. You MUST include ALL parts of the file, even if they haven't been modified. Do NOT include the line numbers in the content though, just the actual content of the file. - line_count: (required) The number of lines in the file. Make sure to compute this based on the actual content of the file, not the number of lines in the content you're providing. Usage: - + File path here Your file content here total number of lines in the file, including empty lines - + Example: Requesting to write to frontend-config.json - + frontend-config.json { @@ -4344,7 +4344,7 @@ Example: Requesting to write to frontend-config.json } 14 - + ## execute_command Description: Request to execute a CLI command on the system. Use this when you need to perform system operations or run specific commands to accomplish any step in the user's task. You must tailor your command to the user's system and provide a clear explanation of what the command does. For command chaining, use the appropriate chaining syntax for the user's shell. Prefer to execute complex CLI commands over creating executable scripts, as they are more flexible and easier to run. Commands will be executed in the current working directory: /test/path @@ -4857,7 +4857,7 @@ IMPORTANT: Regardless of what else you see in the MCP settings file, you must de ## Editing MCP Servers -The user may ask to add tools or resources that may make sense to add to an existing MCP server (listed under 'Connected MCP Servers' above: (None running currently), e.g. if it would use the same API. This would be possible if you can locate the MCP server repository on the user's system by looking at the server arguments for a filepath. You might then use list_files and read_file to explore the files in the repository, and use write_to_file to make changes to the files. +The user may ask to add tools or resources that may make sense to add to an existing MCP server (listed under 'Connected MCP Servers' above: (None running currently), e.g. if it would use the same API. This would be possible if you can locate the MCP server repository on the user's system by looking at the server arguments for a filepath. You might then use list_files and read_file to explore the files in the repository, and use create_file to make changes to the files. However some MCP servers may be running from installed packages rather than a local repository, in which case it may make more sense to create a new MCP server. @@ -4875,7 +4875,7 @@ CAPABILITIES - When the user initially gives you a task, a recursive list of all filepaths in the current working directory ('/test/path') will be included in environment_details. This provides an overview of the project's file structure, offering key insights into the project from directory/file names (how developers conceptualize and organize their code) and file extensions (the language used). This can also guide decision-making on which files to explore further. If you need to further explore directories such as outside the current working directory, you can use the list_files tool. If you pass 'true' for the recursive parameter, it will list files recursively. Otherwise, it will list files at the top level, which is better suited for generic directories where you don't necessarily need the nested structure, like the Desktop. - You can use search_files to perform regex searches across files in a specified directory, outputting context-rich results that include surrounding lines. This is particularly useful for understanding code patterns, finding specific implementations, or identifying areas that need refactoring. - You can use the list_code_definition_names tool to get an overview of source code definitions for all files at the top level of a specified directory. This can be particularly useful when you need to understand the broader context and relationships between certain parts of the code. You may need to call this tool multiple times to understand various parts of the codebase related to the task. - - For example, when asked to make edits or improvements you might analyze the file structure in the initial environment_details to get an overview of the project, then use list_code_definition_names to get further insight using source code definitions for files located in relevant directories, then read_file to examine the contents of relevant files, analyze the code and suggest improvements or make necessary edits, then use the write_to_file tool to apply the changes. If you refactored code that could affect other parts of the codebase, you could use search_files to ensure you update other files as needed. + - For example, when asked to make edits or improvements you might analyze the file structure in the initial environment_details to get an overview of the project, then use list_code_definition_names to get further insight using source code definitions for files located in relevant directories, then read_file to examine the contents of relevant files, analyze the code and suggest improvements or make necessary edits, then use the create_file tool to apply the changes. If you refactored code that could affect other parts of the codebase, you could use search_files to ensure you update other files as needed. - You can use the execute_command tool to run commands on the user's computer whenever you feel it can help accomplish the user's task. When you need to execute a CLI command, you must provide a clear explanation of what the command does. Prefer to execute complex CLI commands over creating executable scripts, since they are more flexible and easier to run. Interactive and long-running commands are allowed, since the commands are run in the user's VSCode terminal. The user may keep commands running in the background and you will be kept updated on their status along the way. Each command you execute is run in a new terminal instance. - You have access to MCP servers that may provide additional tools and resources. Each server may provide different capabilities that you can use to accomplish tasks more effectively. @@ -4894,9 +4894,9 @@ RULES - You cannot \`cd\` into a different directory to complete a task. You are stuck operating from '/test/path', so be sure to pass in the correct 'path' parameter when using tools that require a path. - Do not use the ~ character or $HOME to refer to the home directory. - Before using the execute_command tool, you must first think about the SYSTEM INFORMATION context provided to understand the user's environment and tailor your commands to ensure they are compatible with their system. You must also consider if the command you need to run should be executed in a specific directory outside of the current working directory '/test/path', and if so prepend with \`cd\`'ing into that directory && then executing the command (as one command since you are stuck operating from '/test/path'). For example, if you needed to run \`npm install\` in a project outside of '/test/path', you would need to prepend with a \`cd\` i.e. pseudocode for this would be \`cd (path to project) && (command, in this case npm install)\`. -- When using the search_files tool, craft your regex patterns carefully to balance specificity and flexibility. Based on the user's task you may use it to find code patterns, TODO comments, function definitions, or any text-based information across the project. The results include context, so analyze the surrounding code to better understand the matches. Leverage the search_files tool in combination with other tools for more comprehensive analysis. For example, use it to find specific code patterns, then use read_file to examine the full context of interesting matches before using write_to_file to make informed changes. -- When creating a new project (such as an app, website, or any software project), organize all new files within a dedicated project directory unless the user specifies otherwise. Use appropriate file paths when writing files, as the write_to_file tool will automatically create any necessary directories. Structure the project logically, adhering to best practices for the specific type of project being created. Unless otherwise specified, new projects should be easily run without additional setup, for example most projects can be built in HTML, CSS, and JavaScript - which you can open in a browser. -- When using the write_to_file tool to modify a file, use the tool directly with the desired content. You do not need to display the content before using the tool. ALWAYS provide the COMPLETE file content in your response. This is NON-NEGOTIABLE. Partial updates or placeholders like '// rest of code unchanged' are STRICTLY FORBIDDEN. You MUST include ALL parts of the file, even if they haven't been modified. Failure to do so will result in incomplete or broken code, severely impacting the user's project. +- When using the search_files tool, craft your regex patterns carefully to balance specificity and flexibility. Based on the user's task you may use it to find code patterns, TODO comments, function definitions, or any text-based information across the project. The results include context, so analyze the surrounding code to better understand the matches. Leverage the search_files tool in combination with other tools for more comprehensive analysis. For example, use it to find specific code patterns, then use read_file to examine the full context of interesting matches before using create_file to make informed changes. +- When creating a new project (such as an app, website, or any software project), organize all new files within a dedicated project directory unless the user specifies otherwise. Use appropriate file paths when writing files, as the create_file tool will automatically create any necessary directories. Structure the project logically, adhering to best practices for the specific type of project being created. Unless otherwise specified, new projects should be easily run without additional setup, for example most projects can be built in HTML, CSS, and JavaScript - which you can open in a browser. +- When using the create_file tool to modify a file, use the tool directly with the desired content. You do not need to display the content before using the tool. ALWAYS provide the COMPLETE file content in your response. This is NON-NEGOTIABLE. Partial updates or placeholders like '// rest of code unchanged' are STRICTLY FORBIDDEN. You MUST include ALL parts of the file, even if they haven't been modified. Failure to do so will result in incomplete or broken code, severely impacting the user's project. - Some modes have restrictions on which files they can edit. If you attempt to edit a restricted file, the operation will be rejected with a FileRestrictionError that will specify which file patterns are allowed for the current mode. - Be sure to consider the type of project (e.g. Python, JavaScript, web application) when determining the appropriate structure and files to include. Also consider what files may be most relevant to accomplishing the task, for example looking at a project's manifest file would help you understand the project's dependencies, which you could incorporate into any code you write. * For example, in architect mode trying to edit app.js would be rejected because architect mode can only edit files matching "\\.md$" diff --git a/src/core/prompts/__tests__/sections.test.ts b/src/core/prompts/__tests__/sections.test.ts index 4a18f83de46..75af4ce6a6b 100644 --- a/src/core/prompts/__tests__/sections.test.ts +++ b/src/core/prompts/__tests__/sections.test.ts @@ -42,15 +42,15 @@ describe("getCapabilitiesSection", () => { test("includes edit_file in capabilities when diffStrategy is provided", () => { const result = getCapabilitiesSection(cwd, false, mcpHub, mockDiffStrategy) - expect(result).toContain("or edit_file") - expect(result).toContain("then use the write_to_file or edit_file tool") + expect(result).toContain("or create_file") + expect(result).toContain("then use the edit_file or create_file tool") }) test("excludes edit_file from capabilities when diffStrategy is undefined", () => { const result = getCapabilitiesSection(cwd, false, mcpHub, undefined) expect(result).not.toContain("or edit_file") - expect(result).toContain("then use the write_to_file tool") - expect(result).not.toContain("write_to_file or edit_file") + expect(result).toContain("then use the create_file tool") + expect(result).not.toContain("create_file or edit_file") }) }) diff --git a/src/core/prompts/__tests__/system.test.ts b/src/core/prompts/__tests__/system.test.ts index ad63318efd1..5f936fd4058 100644 --- a/src/core/prompts/__tests__/system.test.ts +++ b/src/core/prompts/__tests__/system.test.ts @@ -563,7 +563,7 @@ describe("SYSTEM_PROMPT", () => { // Verify base instruction lists all available tools expect(prompt).toContain("edit_file (for replacing lines in existing files)") - expect(prompt).toContain("write_to_file (for creating new files or complete file rewrites)") + expect(prompt).toContain("create_file (for creating new files or complete file rewrites)") expect(prompt).toContain("insert_content (for adding lines to existing files)") expect(prompt).toContain("search_and_replace (for finding and replacing individual pieces of text)") }) @@ -593,7 +593,7 @@ describe("SYSTEM_PROMPT", () => { // Verify detailed instructions for each tool expect(prompt).toContain( - "You should always prefer using other editing tools over write_to_file when making changes to existing files since write_to_file is much slower and cannot handle large files.", + "You should always prefer using other editing tools over create_file when making changes to existing files since create_file is much slower and cannot handle large files.", ) expect(prompt).toContain("The insert_content tool adds lines of text to files") expect(prompt).toContain("The search_and_replace tool finds and replaces text or regex in files") diff --git a/src/core/prompts/sections/capabilities.ts b/src/core/prompts/sections/capabilities.ts index 16163dd2b31..9cd39bde580 100644 --- a/src/core/prompts/sections/capabilities.ts +++ b/src/core/prompts/sections/capabilities.ts @@ -17,7 +17,7 @@ CAPABILITIES - When the user initially gives you a task, a recursive list of all filepaths in the current working directory ('${cwd}') will be included in environment_details. This provides an overview of the project's file structure, offering key insights into the project from directory/file names (how developers conceptualize and organize their code) and file extensions (the language used). This can also guide decision-making on which files to explore further. If you need to further explore directories such as outside the current working directory, you can use the list_files tool. If you pass 'true' for the recursive parameter, it will list files recursively. Otherwise, it will list files at the top level, which is better suited for generic directories where you don't necessarily need the nested structure, like the Desktop. - You can use search_files to perform regex searches across files in a specified directory, outputting context-rich results that include surrounding lines. This is particularly useful for understanding code patterns, finding specific implementations, or identifying areas that need refactoring. - You can use the list_code_definition_names tool to get an overview of source code definitions for all files at the top level of a specified directory. This can be particularly useful when you need to understand the broader context and relationships between certain parts of the code. You may need to call this tool multiple times to understand various parts of the codebase related to the task. - - For example, when asked to make edits or improvements you might analyze the file structure in the initial environment_details to get an overview of the project, then use list_code_definition_names to get further insight using source code definitions for files located in relevant directories, then read_file to examine the contents of relevant files, analyze the code and suggest improvements or make necessary edits, then use ${diffStrategy ? "the edit_file or write_to_file" : "the write_to_file"} tool to apply the changes. If you refactored code that could affect other parts of the codebase, you could use search_files to ensure you update other files as needed. + - For example, when asked to make edits or improvements you might analyze the file structure in the initial environment_details to get an overview of the project, then use list_code_definition_names to get further insight using source code definitions for files located in relevant directories, then read_file to examine the contents of relevant files, analyze the code and suggest improvements or make necessary edits, then use ${diffStrategy ? "the edit_file or create_file" : "the create_file"} tool to apply the changes. If you refactored code that could affect other parts of the codebase, you could use search_files to ensure you update other files as needed. - You can use the execute_command tool to run commands on the user's computer whenever you feel it can help accomplish the user's task. When you need to execute a CLI command, you must provide a clear explanation of what the command does. Prefer to execute complex CLI commands over creating executable scripts, since they are more flexible and easier to run. Interactive and long-running commands are allowed, since the commands are run in the user's VSCode terminal. The user may keep commands running in the background and you will be kept updated on their status along the way. Each command you execute is run in a new terminal instance.${ supportsComputerUse ? "\n- You can use the browser_action tool to interact with websites (including html files and locally running development servers) through a Puppeteer-controlled browser when you feel it is necessary in accomplishing the user's task. This tool is particularly useful for web development tasks as it allows you to launch a browser, navigate to pages, interact with elements through clicks and keyboard input, and capture the results through screenshots and console logs. This tool may be useful at key stages of web development tasks-such as after implementing new features, making substantial changes, when troubleshooting issues, or to verify the result of your work. You can analyze the provided screenshots to ensure correct rendering or identify errors, and review console logs for runtime issues.\n - For example, if asked to add a component to a react website, you might create the necessary files, use execute_command to run the site locally, then use browser_action to launch the browser, navigate to the local server, and verify the component renders & functions correctly before closing the browser." diff --git a/src/core/prompts/sections/mcp-servers.ts b/src/core/prompts/sections/mcp-servers.ts index e44020091d0..fd7f520ddd9 100644 --- a/src/core/prompts/sections/mcp-servers.ts +++ b/src/core/prompts/sections/mcp-servers.ts @@ -414,7 +414,7 @@ The user may ask to add tools or resources that may make sense to add to an exis .getServers() .map((server) => server.name) .join(", ") || "(None running currently)" - }, e.g. if it would use the same API. This would be possible if you can locate the MCP server repository on the user's system by looking at the server arguments for a filepath. You might then use list_files and read_file to explore the files in the repository, and use write_to_file${diffStrategy ? " or edit_file" : ""} to make changes to the files. + }, e.g. if it would use the same API. This would be possible if you can locate the MCP server repository on the user's system by looking at the server arguments for a filepath. You might then use list_files and read_file to explore the files in the repository, and use create_file${diffStrategy ? " or edit_file" : ""} to make changes to the files. However some MCP servers may be running from installed packages rather than a local repository, in which case it may make more sense to create a new MCP server. diff --git a/src/core/prompts/sections/modes.ts b/src/core/prompts/sections/modes.ts index 499d4b3882f..de3cac9c947 100644 --- a/src/core/prompts/sections/modes.ts +++ b/src/core/prompts/sections/modes.ts @@ -45,7 +45,7 @@ Both files should follow this structure: "roleDefinition": "You are Roo, a UI/UX expert specializing in design systems and frontend development. Your expertise includes:\\n- Creating and maintaining design systems\\n- Implementing responsive and accessible web interfaces\\n- Working with CSS, HTML, and modern frontend frameworks\\n- Ensuring consistent user experiences across platforms", // Required: non-empty "groups": [ // Required: array of tool groups (can be empty) "read", // Read files group (read_file, search_files, list_files, list_code_definition_names) - "edit", // Edit files group (edit_file, write_to_file) - allows editing any file + "edit", // Edit files group (edit_file, create_file) - allows editing any file // Or with file restrictions: // ["edit", { fileRegex: "\\.md$", description: "Markdown files only" }], // Edit group that only allows editing markdown files "browser", // Browser group (browser_action) diff --git a/src/core/prompts/sections/rules.ts b/src/core/prompts/sections/rules.ts index 82c29cff495..e0d65976182 100644 --- a/src/core/prompts/sections/rules.ts +++ b/src/core/prompts/sections/rules.ts @@ -11,10 +11,10 @@ function getEditingInstructions(diffStrategy?: DiffStrategy, experiments?: Recor if (diffStrategy) { availableTools.push( "edit_file (for replacing lines in existing files)", - "write_to_file (for creating new files or complete file rewrites)", + "create_file (for creating new files or complete file rewrites)", ) } else { - availableTools.push("write_to_file (for creating new files or complete file rewrites)") + availableTools.push("create_file (for creating new files or complete file rewrites)") } if (experiments?.["insert_content"]) { availableTools.push("insert_content (for adding lines to existing files)") @@ -43,12 +43,12 @@ function getEditingInstructions(diffStrategy?: DiffStrategy, experiments?: Recor if (availableTools.length > 1) { instructions.push( - "- You should always prefer using other editing tools over write_to_file when making changes to existing files since write_to_file is much slower and cannot handle large files.", + "- You should always prefer using other editing tools over create_file when making changes to existing files since create_file is much slower and cannot handle large files.", ) } instructions.push( - "- When using the write_to_file tool to modify a file, use the tool directly with the desired content. You do not need to display the content before using the tool. ALWAYS provide the COMPLETE file content in your response. This is NON-NEGOTIABLE. Partial updates or placeholders like '// rest of code unchanged' are STRICTLY FORBIDDEN. You MUST include ALL parts of the file, even if they haven't been modified. Failure to do so will result in incomplete or broken code, severely impacting the user's project.", + "- When using the create_file tool to modify a file, use the tool directly with the desired content. You do not need to display the content before using the tool. ALWAYS provide the COMPLETE file content in your response. This is NON-NEGOTIABLE. Partial updates or placeholders like '// rest of code unchanged' are STRICTLY FORBIDDEN. You MUST include ALL parts of the file, even if they haven't been modified. Failure to do so will result in incomplete or broken code, severely impacting the user's project.", ) return instructions.join("\n") @@ -68,8 +68,8 @@ RULES - You cannot \`cd\` into a different directory to complete a task. You are stuck operating from '${cwd.toPosix()}', so be sure to pass in the correct 'path' parameter when using tools that require a path. - Do not use the ~ character or $HOME to refer to the home directory. - Before using the execute_command tool, you must first think about the SYSTEM INFORMATION context provided to understand the user's environment and tailor your commands to ensure they are compatible with their system. You must also consider if the command you need to run should be executed in a specific directory outside of the current working directory '${cwd.toPosix()}', and if so prepend with \`cd\`'ing into that directory && then executing the command (as one command since you are stuck operating from '${cwd.toPosix()}'). For example, if you needed to run \`npm install\` in a project outside of '${cwd.toPosix()}', you would need to prepend with a \`cd\` i.e. pseudocode for this would be \`cd (path to project) && (command, in this case npm install)\`. -- When using the search_files tool, craft your regex patterns carefully to balance specificity and flexibility. Based on the user's task you may use it to find code patterns, TODO comments, function definitions, or any text-based information across the project. The results include context, so analyze the surrounding code to better understand the matches. Leverage the search_files tool in combination with other tools for more comprehensive analysis. For example, use it to find specific code patterns, then use read_file to examine the full context of interesting matches before using ${diffStrategy ? "edit_file or write_to_file" : "write_to_file"} to make informed changes. -- When creating a new project (such as an app, website, or any software project), organize all new files within a dedicated project directory unless the user specifies otherwise. Use appropriate file paths when writing files, as the write_to_file tool will automatically create any necessary directories. Structure the project logically, adhering to best practices for the specific type of project being created. Unless otherwise specified, new projects should be easily run without additional setup, for example most projects can be built in HTML, CSS, and JavaScript - which you can open in a browser. +- When using the search_files tool, craft your regex patterns carefully to balance specificity and flexibility. Based on the user's task you may use it to find code patterns, TODO comments, function definitions, or any text-based information across the project. The results include context, so analyze the surrounding code to better understand the matches. Leverage the search_files tool in combination with other tools for more comprehensive analysis. For example, use it to find specific code patterns, then use read_file to examine the full context of interesting matches before using ${diffStrategy ? "edit_file or create_file" : "create_file"} to make informed changes. +- When creating a new project (such as an app, website, or any software project), organize all new files within a dedicated project directory unless the user specifies otherwise. Use appropriate file paths when writing files, as the create_file tool will automatically create any necessary directories. Structure the project logically, adhering to best practices for the specific type of project being created. Unless otherwise specified, new projects should be easily run without additional setup, for example most projects can be built in HTML, CSS, and JavaScript - which you can open in a browser. ${getEditingInstructions(diffStrategy, experiments)} - Some modes have restrictions on which files they can edit. If you attempt to edit a restricted file, the operation will be rejected with a FileRestrictionError that will specify which file patterns are allowed for the current mode. - Be sure to consider the type of project (e.g. Python, JavaScript, web application) when determining the appropriate structure and files to include. Also consider what files may be most relevant to accomplishing the task, for example looking at a project's manifest file would help you understand the project's dependencies, which you could incorporate into any code you write. diff --git a/src/core/prompts/tools/index.ts b/src/core/prompts/tools/index.ts index 5a8477bc4f7..6310620aac9 100644 --- a/src/core/prompts/tools/index.ts +++ b/src/core/prompts/tools/index.ts @@ -23,7 +23,7 @@ import { ToolArgs } from "./types" const toolDescriptionMap: Record string | undefined> = { execute_command: (args) => getExecuteCommandDescription(args), read_file: (args) => getReadFileDescription(args), - write_to_file: (args) => getWriteToFileDescription(args), + create_file: (args) => getWriteToFileDescription(args), search_files: (args) => getSearchFilesDescription(args), list_files: (args) => getListFilesDescription(args), list_code_definition_names: (args) => getListCodeDefinitionNamesDescription(args), diff --git a/src/core/prompts/tools/write-to-file.ts b/src/core/prompts/tools/write-to-file.ts index c2a311cf361..7a20e9b3f4f 100644 --- a/src/core/prompts/tools/write-to-file.ts +++ b/src/core/prompts/tools/write-to-file.ts @@ -1,23 +1,23 @@ import { ToolArgs } from "./types" export function getWriteToFileDescription(args: ToolArgs): string { - return `## write_to_file + return `## create_file Description: Request to write full content to a file at the specified path. If the file exists, it will be overwritten with the provided content. If the file doesn't exist, it will be created. This tool will automatically create any directories needed to write the file. Parameters: - path: (required) The path of the file to write to (relative to the current working directory ${args.cwd}) - content: (required) The content to write to the file. ALWAYS provide the COMPLETE intended content of the file, without any truncation or omissions. You MUST include ALL parts of the file, even if they haven't been modified. Do NOT include the line numbers in the content though, just the actual content of the file. - line_count: (required) The number of lines in the file. Make sure to compute this based on the actual content of the file, not the number of lines in the content you're providing. Usage: - + File path here Your file content here total number of lines in the file, including empty lines - + Example: Requesting to write to frontend-config.json - + frontend-config.json { @@ -36,5 +36,5 @@ Example: Requesting to write to frontend-config.json } 14 -` +` } diff --git a/src/shared/__tests__/modes.test.ts b/src/shared/__tests__/modes.test.ts index 1618ae6bc10..52d26735a9e 100644 --- a/src/shared/__tests__/modes.test.ts +++ b/src/shared/__tests__/modes.test.ts @@ -44,14 +44,14 @@ describe("isToolAllowedForMode", () => { describe("file restrictions", () => { it("allows editing matching files", () => { // Test markdown editor mode - const mdResult = isToolAllowedForMode("write_to_file", "markdown-editor", customModes, undefined, { + const mdResult = isToolAllowedForMode("create_file", "markdown-editor", customModes, undefined, { path: "test.md", content: "# Test", }) expect(mdResult).toBe(true) // Test CSS editor mode - const cssResult = isToolAllowedForMode("write_to_file", "css-editor", customModes, undefined, { + const cssResult = isToolAllowedForMode("create_file", "css-editor", customModes, undefined, { path: "styles.css", content: ".test { color: red; }", }) @@ -61,13 +61,13 @@ describe("isToolAllowedForMode", () => { it("rejects editing non-matching files", () => { // Test markdown editor mode with non-markdown file expect(() => - isToolAllowedForMode("write_to_file", "markdown-editor", customModes, undefined, { + isToolAllowedForMode("create_file", "markdown-editor", customModes, undefined, { path: "test.js", content: "console.log('test')", }), ).toThrow(FileRestrictionError) expect(() => - isToolAllowedForMode("write_to_file", "markdown-editor", customModes, undefined, { + isToolAllowedForMode("create_file", "markdown-editor", customModes, undefined, { path: "test.js", content: "console.log('test')", }), @@ -75,13 +75,13 @@ describe("isToolAllowedForMode", () => { // Test CSS editor mode with non-CSS file expect(() => - isToolAllowedForMode("write_to_file", "css-editor", customModes, undefined, { + isToolAllowedForMode("create_file", "css-editor", customModes, undefined, { path: "test.js", content: "console.log('test')", }), ).toThrow(FileRestrictionError) expect(() => - isToolAllowedForMode("write_to_file", "css-editor", customModes, undefined, { + isToolAllowedForMode("create_file", "css-editor", customModes, undefined, { path: "test.js", content: "console.log('test')", }), @@ -91,7 +91,7 @@ describe("isToolAllowedForMode", () => { it("handles partial streaming cases (path only, no content/diff)", () => { // Should allow path-only for matching files (no validation yet since content/diff not provided) expect( - isToolAllowedForMode("write_to_file", "markdown-editor", customModes, undefined, { + isToolAllowedForMode("create_file", "markdown-editor", customModes, undefined, { path: "test.js", }), ).toBe(true) @@ -104,15 +104,15 @@ describe("isToolAllowedForMode", () => { // Should allow path-only for architect mode too expect( - isToolAllowedForMode("write_to_file", "architect", [], undefined, { + isToolAllowedForMode("create_file", "architect", [], undefined, { path: "test.js", }), ).toBe(true) }) - it("applies restrictions to both write_to_file and edit_file", () => { - // Test write_to_file - const writeResult = isToolAllowedForMode("write_to_file", "markdown-editor", customModes, undefined, { + it("applies restrictions to both create_file and edit_file", () => { + // Test create_file + const writeResult = isToolAllowedForMode("create_file", "markdown-editor", customModes, undefined, { path: "test.md", content: "# Test", }) @@ -127,7 +127,7 @@ describe("isToolAllowedForMode", () => { // Test both with non-matching file expect(() => - isToolAllowedForMode("write_to_file", "markdown-editor", customModes, undefined, { + isToolAllowedForMode("create_file", "markdown-editor", customModes, undefined, { path: "test.js", content: "console.log('test')", }), @@ -155,15 +155,15 @@ describe("isToolAllowedForMode", () => { }, ] - // Test write_to_file with non-matching file + // Test create_file with non-matching file expect(() => - isToolAllowedForMode("write_to_file", "docs-editor", customModesWithDescription, undefined, { + isToolAllowedForMode("create_file", "docs-editor", customModesWithDescription, undefined, { path: "test.js", content: "console.log('test')", }), ).toThrow(FileRestrictionError) expect(() => - isToolAllowedForMode("write_to_file", "docs-editor", customModesWithDescription, undefined, { + isToolAllowedForMode("create_file", "docs-editor", customModesWithDescription, undefined, { path: "test.js", content: "console.log('test')", }), @@ -185,14 +185,14 @@ describe("isToolAllowedForMode", () => { // Test that matching files are allowed expect( - isToolAllowedForMode("write_to_file", "docs-editor", customModesWithDescription, undefined, { + isToolAllowedForMode("create_file", "docs-editor", customModesWithDescription, undefined, { path: "test.md", content: "# Test", }), ).toBe(true) expect( - isToolAllowedForMode("write_to_file", "docs-editor", customModesWithDescription, undefined, { + isToolAllowedForMode("create_file", "docs-editor", customModesWithDescription, undefined, { path: "test.txt", content: "Test content", }), @@ -200,7 +200,7 @@ describe("isToolAllowedForMode", () => { // Test partial streaming cases expect( - isToolAllowedForMode("write_to_file", "docs-editor", customModesWithDescription, undefined, { + isToolAllowedForMode("create_file", "docs-editor", customModesWithDescription, undefined, { path: "test.js", }), ).toBe(true) @@ -209,7 +209,7 @@ describe("isToolAllowedForMode", () => { it("allows architect mode to edit markdown files only", () => { // Should allow editing markdown files expect( - isToolAllowedForMode("write_to_file", "architect", [], undefined, { + isToolAllowedForMode("create_file", "architect", [], undefined, { path: "test.md", content: "# Test", }), @@ -225,13 +225,13 @@ describe("isToolAllowedForMode", () => { // Should reject non-markdown files expect(() => - isToolAllowedForMode("write_to_file", "architect", [], undefined, { + isToolAllowedForMode("create_file", "architect", [], undefined, { path: "test.js", content: "console.log('test')", }), ).toThrow(FileRestrictionError) expect(() => - isToolAllowedForMode("write_to_file", "architect", [], undefined, { + isToolAllowedForMode("create_file", "architect", [], undefined, { path: "test.js", content: "console.log('test')", }), @@ -245,15 +245,15 @@ describe("isToolAllowedForMode", () => { }) it("handles non-existent modes", () => { - expect(isToolAllowedForMode("write_to_file", "non-existent", customModes)).toBe(false) + expect(isToolAllowedForMode("create_file", "non-existent", customModes)).toBe(false) }) it("respects tool requirements", () => { const toolRequirements = { - write_to_file: false, + create_file: false, } - expect(isToolAllowedForMode("write_to_file", "markdown-editor", customModes, toolRequirements)).toBe(false) + expect(isToolAllowedForMode("create_file", "markdown-editor", customModes, toolRequirements)).toBe(false) }) describe("experimental tools", () => { @@ -312,7 +312,7 @@ describe("isToolAllowedForMode", () => { ).toBe(true) expect( isToolAllowedForMode( - "write_to_file", + "create_file", "markdown-editor", customModes, undefined, diff --git a/src/shared/tool-groups.ts b/src/shared/tool-groups.ts index 8e982b20b0f..8a25e1400ee 100644 --- a/src/shared/tool-groups.ts +++ b/src/shared/tool-groups.ts @@ -8,7 +8,7 @@ export type ToolGroupConfig = { export const TOOL_DISPLAY_NAMES = { execute_command: "run commands", read_file: "read files", - write_to_file: "write files", + create_file: "write files", edit_file: "apply changes", search_files: "search files", list_files: "list files", @@ -28,7 +28,7 @@ export const TOOL_GROUPS: Record = { tools: ["read_file", "search_files", "list_files", "list_code_definition_names"], }, edit: { - tools: ["edit_file", "write_to_file", "insert_content", "search_and_replace"], + tools: ["edit_file", "create_file", "insert_content", "search_and_replace"], }, browser: { tools: ["browser_action"], From 87f6ac4d06083a895aadc5c15ffb1532eb70ba9e Mon Sep 17 00:00:00 2001 From: ShayBC Date: Sun, 23 Feb 2025 05:15:23 +0200 Subject: [PATCH 030/585] pass last message of a subtask to parent task --- src/core/Cline.ts | 32 ++++++++++++++----------------- src/core/webview/ClineProvider.ts | 4 ++-- 2 files changed, 16 insertions(+), 20 deletions(-) diff --git a/src/core/Cline.ts b/src/core/Cline.ts index 134961129cc..54de357d469 100644 --- a/src/core/Cline.ts +++ b/src/core/Cline.ts @@ -490,26 +490,22 @@ export class Cline { ]) } - async resumePausedTask() { + async resumePausedTask(lastMessage?: string) { // release this Cline instance from paused state this.isPaused = false - // Clear any existing ask state and simulate a completed ask response - // this.askResponse = "messageResponse"; - // this.askResponseText = "Sub Task finished Successfully!\nthere is no need to perform this task again, please continue to the next task."; - // this.askResponseImages = undefined; - // this.lastMessageTs = Date.now(); - // This adds the completion message to conversation history - await this.say( - "text", - "Sub Task finished Successfully!\nthere is no need to perform this task again, please continue to the next task.", - ) - - // this.userMessageContent.push({ - // type: "text", - // text: `${"Result:\\n\\nSub Task finished Successfully!\nthere is no need to perform this task again, please continue to the next task."}`, - // }) + await this.say("text", `new_task finished successfully! ${lastMessage ?? "Please continue to the next task."}`) + + await this.addToApiConversationHistory({ + role: "assistant", + content: [ + { + type: "text", + text: `new_task finished successfully! ${lastMessage ?? "Please continue to the next task."}`, + }, + ], + }) try { // Resume parent task @@ -2699,7 +2695,7 @@ export class Cline { await this.say("completion_result", result, undefined, false) if (this.isSubTask) { // tell the provider to remove the current subtask and resume the previous task in the stack - this.providerRef.deref()?.finishSubTask() + this.providerRef.deref()?.finishSubTask(lastMessage?.text) } } @@ -2720,7 +2716,7 @@ export class Cline { await this.say("completion_result", result, undefined, false) if (this.isSubTask) { // tell the provider to remove the current subtask and resume the previous task in the stack - this.providerRef.deref()?.finishSubTask() + this.providerRef.deref()?.finishSubTask(lastMessage?.text) } } diff --git a/src/core/webview/ClineProvider.ts b/src/core/webview/ClineProvider.ts index d690efa9dd5..b00c744ff66 100644 --- a/src/core/webview/ClineProvider.ts +++ b/src/core/webview/ClineProvider.ts @@ -218,11 +218,11 @@ export class ClineProvider implements vscode.WebviewViewProvider { // remove the current task/cline instance (at the top of the stack), ao this task is finished // and resume the previous task/cline instance (if it exists) // this is used when a sub task is finished and the parent task needs to be resumed - async finishSubTask() { + async finishSubTask(lastMessage?: string) { // remove the last cline instance from the stack (this is the finished sub task) await this.removeClineFromStack() // resume the last cline instance in the stack (if it exists - this is the 'parnt' calling task) - this.getCurrentCline()?.resumePausedTask() + this.getCurrentCline()?.resumePausedTask(lastMessage) } /* From 816847c5c0d40d3b7d69be014966a19729d7e151 Mon Sep 17 00:00:00 2001 From: Matt Rubens Date: Sun, 23 Feb 2025 00:30:03 -0500 Subject: [PATCH 031/585] Add Knip check for unused files --- .github/workflows/code-qa.yml | 15 + knip.json | 27 ++ package-lock.json | 324 ++++++++++++- package.json | 2 + src/api/transform/o1-format.ts | 429 ------------------ .../diagnostics/DiagnosticsMonitor.ts | 123 ----- src/integrations/terminal/TerminalActions.ts | 0 src/integrations/workspace/get-python-env.ts | 42 -- webview-ui/src/components/common/Demo.tsx | 130 ------ .../src/components/settings/TabNavbar.tsx | 167 ------- webview-ui/src/services/GitService.ts | 46 -- 11 files changed, 361 insertions(+), 944 deletions(-) create mode 100644 knip.json delete mode 100644 src/api/transform/o1-format.ts delete mode 100644 src/integrations/diagnostics/DiagnosticsMonitor.ts delete mode 100644 src/integrations/terminal/TerminalActions.ts delete mode 100644 src/integrations/workspace/get-python-env.ts delete mode 100644 webview-ui/src/components/common/Demo.tsx delete mode 100644 webview-ui/src/components/settings/TabNavbar.tsx delete mode 100644 webview-ui/src/services/GitService.ts diff --git a/.github/workflows/code-qa.yml b/.github/workflows/code-qa.yml index 06367080fc3..e28302f46d8 100644 --- a/.github/workflows/code-qa.yml +++ b/.github/workflows/code-qa.yml @@ -28,6 +28,21 @@ jobs: - name: Lint run: npm run lint + knip: + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v4 + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: '18' + cache: 'npm' + - name: Install dependencies + run: npm run install:all + - name: Run knip checks + run: npm run knip + unit-test: runs-on: ubuntu-latest steps: diff --git a/knip.json b/knip.json new file mode 100644 index 00000000000..b0e0839da77 --- /dev/null +++ b/knip.json @@ -0,0 +1,27 @@ +{ + "$schema": "https://unpkg.com/knip@latest/schema.json", + "entry": ["src/extension.ts", "src/activate/index.ts", "webview-ui/src/index.tsx"], + "project": ["src/**/*.ts", "webview-ui/src/**/*.{ts,tsx}"], + "ignore": [ + "**/__mocks__/**", + "**/__tests__/**", + "**/test/**", + "**/*.test.ts", + "**/*.test.tsx", + "**/stories/**", + "coverage/**", + "dist/**", + "out/**", + "bin/**", + "src/activate/**", + "src/exports/**", + "src/extension.ts", + ".vscode-test.mjs" + ], + "workspaces": { + "webview-ui": { + "entry": ["src/index.tsx"], + "project": ["src/**/*.{ts,tsx}"] + } + } +} diff --git a/package-lock.json b/package-lock.json index 4f7cf4246ce..0e139c0ff82 100644 --- a/package-lock.json +++ b/package-lock.json @@ -76,6 +76,7 @@ "husky": "^9.1.7", "jest": "^29.7.0", "jest-simple-dot-reporter": "^1.0.5", + "knip": "^5.44.4", "lint-staged": "^15.2.11", "mkdirp": "^3.0.1", "mocha": "^11.1.0", @@ -5854,6 +5855,47 @@ "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==" }, + "node_modules/@snyk/github-codeowners": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@snyk/github-codeowners/-/github-codeowners-1.1.0.tgz", + "integrity": "sha512-lGFf08pbkEac0NYgVf4hdANpAgApRjNByLXB+WBip3qj1iendOIyAwP2GKkKbQMNVy2r1xxDf0ssfWscoiC+Vw==", + "dev": true, + "dependencies": { + "commander": "^4.1.1", + "ignore": "^5.1.8", + "p-map": "^4.0.0" + }, + "bin": { + "github-codeowners": "dist/cli.js" + }, + "engines": { + "node": ">=8.10" + } + }, + "node_modules/@snyk/github-codeowners/node_modules/commander": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", + "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==", + "dev": true, + "engines": { + "node": ">= 6" + } + }, + "node_modules/@snyk/github-codeowners/node_modules/p-map": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz", + "integrity": "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==", + "dev": true, + "dependencies": { + "aggregate-error": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/@tootallnate/quickjs-emscripten": { "version": "0.23.0", "resolved": "https://registry.npmjs.org/@tootallnate/quickjs-emscripten/-/quickjs-emscripten-0.23.0.tgz", @@ -6696,6 +6738,19 @@ "node": ">= 8.0.0" } }, + "node_modules/aggregate-error": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", + "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==", + "dev": true, + "dependencies": { + "clean-stack": "^2.0.0", + "indent-string": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/ajv": { "version": "6.12.6", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", @@ -7530,6 +7585,15 @@ "integrity": "sha512-cuSVIHi9/9E/+821Qjdvngor+xpnlwnuwIyZOaLmHBVdXL+gP+I6QQB9VkO7RI77YIcTV+S1W9AreJ5eN63JBA==", "dev": true }, + "node_modules/clean-stack": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", + "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==", + "dev": true, + "engines": { + "node": ">=6" + } + }, "node_modules/cli-cursor": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-4.0.0.tgz", @@ -7662,6 +7726,16 @@ "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, + "node_modules/clone": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", + "integrity": "sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==", + "dev": true, + "optional": true, + "engines": { + "node": ">=0.8" + } + }, "node_modules/clone-deep": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/clone-deep/-/clone-deep-4.0.1.tgz", @@ -7957,6 +8031,19 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/defaults": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.4.tgz", + "integrity": "sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==", + "dev": true, + "optional": true, + "dependencies": { + "clone": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/define-data-property": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", @@ -8207,6 +8294,27 @@ "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", "dev": true }, + "node_modules/easy-table": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/easy-table/-/easy-table-1.2.0.tgz", + "integrity": "sha512-OFzVOv03YpvtcWGe5AayU5G2hgybsg3iqA6drU8UaoZyB9jLGMTrz9+asnLp/E+6qPh88yEI1gvyZFZ41dmgww==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "optionalDependencies": { + "wcwidth": "^1.0.1" + } + }, + "node_modules/easy-table/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, "node_modules/ecdsa-sig-formatter": { "version": "1.0.11", "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", @@ -8299,9 +8407,9 @@ } }, "node_modules/enhanced-resolve": { - "version": "5.17.1", - "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.17.1.tgz", - "integrity": "sha512-LMHl3dXhTcfv8gM4kEzIUeTQ+7fpdA0l2tUf34BddXPkz2A5xJ5L/Pchd5BL6rdccM9QGvu0sWZzK1Z1t4wwyg==", + "version": "5.18.1", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.18.1.tgz", + "integrity": "sha512-ZSW3ma5GkcQBIpwZTSRAI8N71Uuwgs93IezB7mf7R60tC8ZbJideoDNKjHn2O9KIlx6rkGTTEk1xUCK2E1Y2Yg==", "dev": true, "dependencies": { "graceful-fs": "^4.2.4", @@ -8947,15 +9055,15 @@ "integrity": "sha512-/d9sfos4yxzpwkDkuN7k2SqFKtYNmCTzgfEpz82x34IM9/zc8KGxQoXg1liNC/izpRM/MBdt44Nmx41ZWqk+FQ==" }, "node_modules/fast-glob": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", - "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==", + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", + "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", "dependencies": { "@nodelib/fs.stat": "^2.0.2", "@nodelib/fs.walk": "^1.2.3", "glob-parent": "^5.1.2", "merge2": "^1.3.0", - "micromatch": "^4.0.4" + "micromatch": "^4.0.8" }, "engines": { "node": ">=8.6.0" @@ -9957,6 +10065,15 @@ "node": ">=0.8.19" } }, + "node_modules/indent-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", + "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, "node_modules/inflight": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", @@ -11323,6 +11440,15 @@ "url": "https://github.com/chalk/supports-color?sponsor=1" } }, + "node_modules/jiti": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.4.2.tgz", + "integrity": "sha512-rg9zJN+G4n2nfJl5MW3BMygZX56zKPNVEYYqq7adpmMh4Jn2QNEwhvQlFy6jPVdcod7txZtKHWnyZiA3a0zP7A==", + "dev": true, + "bin": { + "jiti": "lib/jiti-cli.mjs" + } + }, "node_modules/js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", @@ -11472,6 +11598,114 @@ "node": ">=6" } }, + "node_modules/knip": { + "version": "5.44.4", + "resolved": "https://registry.npmjs.org/knip/-/knip-5.44.4.tgz", + "integrity": "sha512-Ryn8LwWHLId8jSK1DgtT0hmg5DbzkqAtH+Gg3vZJpmSMgGHMspej9Ag+qKTm8wsPLDjVetuEz/lIsobo0XCMvQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/webpro" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/knip" + }, + { + "type": "polar", + "url": "https://polar.sh/webpro-nl" + } + ], + "dependencies": { + "@nodelib/fs.walk": "3.0.1", + "@snyk/github-codeowners": "1.1.0", + "easy-table": "1.2.0", + "enhanced-resolve": "^5.18.0", + "fast-glob": "^3.3.3", + "jiti": "^2.4.2", + "js-yaml": "^4.1.0", + "minimist": "^1.2.8", + "picocolors": "^1.1.0", + "picomatch": "^4.0.1", + "pretty-ms": "^9.0.0", + "smol-toml": "^1.3.1", + "strip-json-comments": "5.0.1", + "summary": "2.1.0", + "zod": "^3.22.4", + "zod-validation-error": "^3.0.3" + }, + "bin": { + "knip": "bin/knip.js", + "knip-bun": "bin/knip-bun.js" + }, + "engines": { + "node": ">=18.18.0" + }, + "peerDependencies": { + "@types/node": ">=18", + "typescript": ">=5.0.4" + } + }, + "node_modules/knip/node_modules/@nodelib/fs.scandir": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-4.0.1.tgz", + "integrity": "sha512-vAkI715yhnmiPupY+dq+xenu5Tdf2TBQ66jLvBIcCddtz+5Q8LbMKaf9CIJJreez8fQ8fgaY+RaywQx8RJIWpw==", + "dev": true, + "dependencies": { + "@nodelib/fs.stat": "4.0.0", + "run-parallel": "^1.2.0" + }, + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/knip/node_modules/@nodelib/fs.stat": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-4.0.0.tgz", + "integrity": "sha512-ctr6bByzksKRCV0bavi8WoQevU6plSp2IkllIsEqaiKe2mwNNnaluhnRhcsgGZHrrHk57B3lf95MkLMO3STYcg==", + "dev": true, + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/knip/node_modules/@nodelib/fs.walk": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-3.0.1.tgz", + "integrity": "sha512-nIh/M6Kh3ZtOmlY00DaUYB4xeeV6F3/ts1l29iwl3/cfyY/OuCfUx+v08zgx8TKPTifXRcjjqVQ4KB2zOYSbyw==", + "dev": true, + "dependencies": { + "@nodelib/fs.scandir": "4.0.1", + "fastq": "^1.15.0" + }, + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/knip/node_modules/picomatch": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz", + "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/knip/node_modules/strip-json-comments": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-5.0.1.tgz", + "integrity": "sha512-0fk9zBqO67Nq5M/m45qHCJxylV/DhBlIOVExqgOMiCCrzrhU6tCibRXNqE3jwJLftzE9SNuZtYbpzcO+i9FiKw==", + "dev": true, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/leven": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", @@ -12205,6 +12439,15 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/minipass": { "version": "7.1.2", "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", @@ -13170,6 +13413,18 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/parse-ms": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/parse-ms/-/parse-ms-4.0.0.tgz", + "integrity": "sha512-TXfryirbmq34y8QBwgqCVLi+8oA3oWx2eAnSn62ITyEhEYaWRlVZ2DvMM9eZbMs/RfxPu/PK/aBLyGj4IrqMHw==", + "dev": true, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/parse5": { "version": "7.2.1", "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.2.1.tgz", @@ -13480,6 +13735,21 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, + "node_modules/pretty-ms": { + "version": "9.2.0", + "resolved": "https://registry.npmjs.org/pretty-ms/-/pretty-ms-9.2.0.tgz", + "integrity": "sha512-4yf0QO/sllf/1zbZWYnvWw3NxCQwLXKzIj0G849LSufP15BXKM0rbD2Z3wVnkMfjdn/CB0Dpp444gYAACdsplg==", + "dev": true, + "dependencies": { + "parse-ms": "^4.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/process-nextick-args": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", @@ -14340,6 +14610,18 @@ "npm": ">= 3.0.0" } }, + "node_modules/smol-toml": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/smol-toml/-/smol-toml-1.3.1.tgz", + "integrity": "sha512-tEYNll18pPKHroYSmLLrksq233j021G0giwW7P3D24jC54pQ5W5BXMsQ/Mvw1OJCmEYDgY+lrzT+3nNUtoNfXQ==", + "dev": true, + "engines": { + "node": ">= 18" + }, + "funding": { + "url": "https://github.com/sponsors/cyyynthia" + } + }, "node_modules/socks": { "version": "2.8.3", "resolved": "https://registry.npmjs.org/socks/-/socks-2.8.3.tgz", @@ -14761,6 +15043,12 @@ "resolved": "https://registry.npmjs.org/strnum/-/strnum-1.0.5.tgz", "integrity": "sha512-J8bbNyKKXl5qYcR36TIO8W3mVGVHrmmxsd5PAItGkmyzwJvybiw2IVq5nqd0i4LSNSkB/sx9VHllbfFdr9k1JA==" }, + "node_modules/summary": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/summary/-/summary-2.1.0.tgz", + "integrity": "sha512-nMIjMrd5Z2nuB2RZCKJfFMjgS3fygbeyGk9PxPPaJR1RIcyN9yn4A63Isovzm3ZtQuEkLBVgMdPup8UeLH7aQw==", + "dev": true + }, "node_modules/supports-color": { "version": "9.4.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-9.4.0.tgz", @@ -15336,6 +15624,16 @@ "makeerror": "1.0.12" } }, + "node_modules/wcwidth": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz", + "integrity": "sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==", + "dev": true, + "optional": true, + "dependencies": { + "defaults": "^1.0.3" + } + }, "node_modules/web-streams-polyfill": { "version": "4.0.0-beta.3", "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-4.0.0-beta.3.tgz", @@ -15962,6 +16260,18 @@ "funding": { "url": "https://github.com/sponsors/colinhacks" } + }, + "node_modules/zod-validation-error": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/zod-validation-error/-/zod-validation-error-3.4.0.tgz", + "integrity": "sha512-ZOPR9SVY6Pb2qqO5XHt+MkkTRxGXb4EVtnjc9JpXUOtUB1T9Ru7mZOT361AN3MsetVe7R0a1KZshJDZdgp9miQ==", + "dev": true, + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "zod": "^3.18.0" + } } } } diff --git a/package.json b/package.json index 4a5ba593254..133580698df 100644 --- a/package.json +++ b/package.json @@ -281,6 +281,7 @@ "compile": "tsc -p . --outDir out && node esbuild.js", "compile:integration": "tsc -p tsconfig.integration.json", "install:all": "npm install && cd webview-ui && npm install", + "knip": "knip --include files", "lint": "eslint src --ext ts && npm run lint --prefix webview-ui", "lint-local": "eslint -c .eslintrc.local.json src --ext ts && npm run lint --prefix webview-ui", "lint-fix": "eslint src --ext ts --fix && npm run lint-fix --prefix webview-ui", @@ -371,6 +372,7 @@ "husky": "^9.1.7", "jest": "^29.7.0", "jest-simple-dot-reporter": "^1.0.5", + "knip": "^5.44.4", "lint-staged": "^15.2.11", "mkdirp": "^3.0.1", "mocha": "^11.1.0", diff --git a/src/api/transform/o1-format.ts b/src/api/transform/o1-format.ts deleted file mode 100644 index 040bca7a303..00000000000 --- a/src/api/transform/o1-format.ts +++ /dev/null @@ -1,429 +0,0 @@ -import { Anthropic } from "@anthropic-ai/sdk" -import OpenAI from "openai" - -const o1SystemPrompt = (systemPrompt: string) => ` -# System Prompt - -${systemPrompt} - -# Instructions for Formulating Your Response - -You must respond to the user's request by using at least one tool call. When formulating your response, follow these guidelines: - -1. Begin your response with normal text, explaining your thoughts, analysis, or plan of action. -2. If you need to use any tools, place ALL tool calls at the END of your message, after your normal text explanation. -3. You can use multiple tool calls if needed, but they should all be grouped together at the end of your message. -4. After placing the tool calls, do not add any additional normal text. The tool calls should be the final content in your message. - -Here's the general structure your responses should follow: - -\`\`\` -[Your normal text response explaining your thoughts and actions] - -[Tool Call 1] -[Tool Call 2 if needed] -[Tool Call 3 if needed] -... -\`\`\` - -Remember: -- Choose the most appropriate tool(s) based on the task and the tool descriptions provided. -- Formulate your tool calls using the XML format specified for each tool. -- Provide clear explanations in your normal text about what actions you're taking and why you're using particular tools. -- Act as if the tool calls will be executed immediately after your message, and your next response will have access to their results. - -# Tool Descriptions and XML Formats - -1. execute_command: - -Your command here - -Description: Execute a CLI command on the system. Use this when you need to perform system operations or run specific commands to accomplish any step in the user's task. You must tailor your command to the user's system and provide a clear explanation of what the command does. Prefer to execute complex CLI commands over creating executable scripts, as they are more flexible and easier to run. Commands will be executed in the current working directory. - -2. list_files: - -Directory path here -true or false (optional) - -Description: List files and directories within the specified directory. If recursive is true, it will list all files and directories recursively. If recursive is false or not provided, it will only list the top-level contents. - -3. list_code_definition_names: - -Directory path here - -Description: Lists definition names (classes, functions, methods, etc.) used in source code files at the top level of the specified directory. This tool provides insights into the codebase structure and important constructs, encapsulating high-level concepts and relationships that are crucial for understanding the overall architecture. - -4. search_files: - -Directory path here -Your regex pattern here -Optional file pattern here - -Description: Perform a regex search across files in a specified directory, providing context-rich results. This tool searches for patterns or specific content across multiple files, displaying each match with encapsulating context. - -5. read_file: - -File path here - -Description: Read the contents of a file at the specified path. Use this when you need to examine the contents of an existing file, for example to analyze code, review text files, or extract information from configuration files. Automatically extracts raw text from PDF and DOCX files. May not be suitable for other types of binary files, as it returns the raw content as a string. - -6. create_file: - -File path here - -Your file content here - - -Description: Write content to a file at the specified path. If the file exists, it will be overwritten with the provided content. If the file doesn't exist, it will be created. Always provide the full intended content of the file, without any truncation. This tool will automatically create any directories needed to write the file. - -7. ask_followup_question: - -Your question here - -Description: Ask the user a question to gather additional information needed to complete the task. This tool should be used when you encounter ambiguities, need clarification, or require more details to proceed effectively. It allows for interactive problem-solving by enabling direct communication with the user. Use this tool judiciously to maintain a balance between gathering necessary information and avoiding excessive back-and-forth. - -8. attempt_completion: - -Optional command to demonstrate result - -Your final result description here - - -Description: Once you've completed the task, use this tool to present the result to the user. They may respond with feedback if they are not satisfied with the result, which you can use to make improvements and try again. - -# Examples - -Here are some examples of how to structure your responses with tool calls: - -Example 1: Using a single tool - -Let's run the test suite for our project. This will help us ensure that all our components are functioning correctly. - - -npm test - - -Example 2: Using multiple tools - -Let's create two new configuration files for the web application: one for the frontend and one for the backend. - - -./frontend-config.json - -{ - "apiEndpoint": "https://api.example.com", - "theme": { - "primaryColor": "#007bff", - "secondaryColor": "#6c757d", - "fontFamily": "Arial, sans-serif" - }, - "features": { - "darkMode": true, - "notifications": true, - "analytics": false - }, - "version": "1.0.0" -} - - - - -./backend-config.yaml - -database: - host: localhost - port: 5432 - name: myapp_db - user: admin - -server: - port: 3000 - environment: development - logLevel: debug - -security: - jwtSecret: your-secret-key-here - passwordSaltRounds: 10 - -caching: - enabled: true - provider: redis - ttl: 3600 - -externalServices: - emailProvider: sendgrid - storageProvider: aws-s3 - - - -Example 3: Asking a follow-up question - -I've analyzed the project structure, but I need more information to proceed. Let me ask the user for clarification. - - -Which specific feature would you like me to implement in the example.py file? - -` - -export function convertToO1Messages( - openAiMessages: OpenAI.Chat.ChatCompletionMessageParam[], - systemPrompt: string, -): OpenAI.Chat.ChatCompletionMessageParam[] { - const toolsReplaced = openAiMessages.reduce((acc, message) => { - if (message.role === "tool") { - // Convert tool messages to user messages - acc.push({ - role: "user", - content: message.content || "", - }) - } else if (message.role === "assistant" && message.tool_calls) { - // Convert tool calls to content and remove tool_calls - let content = message.content || "" - message.tool_calls.forEach((toolCall) => { - if (toolCall.type === "function") { - content += `\nTool Call: ${toolCall.function.name}\nArguments: ${toolCall.function.arguments}` - } - }) - acc.push({ - role: "assistant", - content: content, - tool_calls: undefined, - }) - } else { - // Keep other messages as they are - acc.push(message) - } - return acc - }, [] as OpenAI.Chat.ChatCompletionMessageParam[]) - - // Find the index of the last assistant message - // const lastAssistantIndex = findLastIndex(toolsReplaced, (message) => message.role === "assistant") - - // Create a new array to hold the modified messages - const messagesWithSystemPrompt = [ - { - role: "user", - content: o1SystemPrompt(systemPrompt), - } as OpenAI.Chat.ChatCompletionUserMessageParam, - ...toolsReplaced, - ] - - // If there's an assistant message, insert the system prompt after it - // if (lastAssistantIndex !== -1) { - // const insertIndex = lastAssistantIndex + 1 - // if (insertIndex < messagesWithSystemPrompt.length && messagesWithSystemPrompt[insertIndex].role === "user") { - // messagesWithSystemPrompt.splice(insertIndex, 0, { - // role: "user", - // content: o1SystemPrompt(systemPrompt), - // }) - // } - // } else { - // // If there were no assistant messages, prepend the system prompt - // messagesWithSystemPrompt.unshift({ - // role: "user", - // content: o1SystemPrompt(systemPrompt), - // }) - // } - - return messagesWithSystemPrompt -} - -interface ToolCall { - tool: string - tool_input: Record -} - -const toolNames = [ - "execute_command", - "list_files", - "list_code_definition_names", - "search_files", - "read_file", - "create_file", - "ask_followup_question", - "attempt_completion", -] - -function parseAIResponse(response: string): { normalText: string; toolCalls: ToolCall[] } { - // Create a regex pattern to match any tool call opening tag - const toolCallPattern = new RegExp(`<(${toolNames.join("|")})`, "i") - const match = response.match(toolCallPattern) - - if (!match) { - // No tool calls found - return { normalText: response.trim(), toolCalls: [] } - } - - const toolCallStart = match.index! - const normalText = response.slice(0, toolCallStart).trim() - const toolCallsText = response.slice(toolCallStart) - - const toolCalls = parseToolCalls(toolCallsText) - - return { normalText, toolCalls } -} - -function parseToolCalls(toolCallsText: string): ToolCall[] { - const toolCalls: ToolCall[] = [] - - let remainingText = toolCallsText - - while (remainingText.length > 0) { - const toolMatch = toolNames.find((tool) => new RegExp(`<${tool}`, "i").test(remainingText)) - - if (!toolMatch) { - break // No more tool calls found - } - - const startTag = `<${toolMatch}` - const endTag = `` - const startIndex = remainingText.indexOf(startTag) - const endIndex = remainingText.indexOf(endTag, startIndex) - - if (endIndex === -1) { - break // Malformed XML, no closing tag found - } - - const toolCallContent = remainingText.slice(startIndex, endIndex + endTag.length) - remainingText = remainingText.slice(endIndex + endTag.length).trim() - - const toolCall = parseToolCall(toolMatch, toolCallContent) - if (toolCall) { - toolCalls.push(toolCall) - } - } - - return toolCalls -} - -function parseToolCall(toolName: string, content: string): ToolCall | null { - const tool_input: Record = {} - - // Remove the outer tool tags - const innerContent = content.replace(new RegExp(`^<${toolName}>|$`, "g"), "").trim() - - // Parse nested XML elements - const paramRegex = /<(\w+)>([\s\S]*?)<\/\1>/gs - let match - - while ((match = paramRegex.exec(innerContent)) !== null) { - const [, paramName, paramValue] = match - // Preserve newlines and trim only leading/trailing whitespace - tool_input[paramName] = paramValue.replace(/^\s+|\s+$/g, "") - } - - // Validate required parameters - if (!validateToolInput(toolName, tool_input)) { - console.error(`Invalid tool call for ${toolName}:`, content) - return null - } - - return { tool: toolName, tool_input } -} - -function validateToolInput(toolName: string, tool_input: Record): boolean { - switch (toolName) { - case "execute_command": - return "command" in tool_input - case "read_file": - case "list_code_definition_names": - case "list_files": - return "path" in tool_input - case "search_files": - return "path" in tool_input && "regex" in tool_input - case "create_file": - return "path" in tool_input && "content" in tool_input - case "ask_followup_question": - return "question" in tool_input - case "attempt_completion": - return "result" in tool_input - default: - return false - } -} - -// Example usage: -// const aiResponse = `Here's my analysis of the situation... - -// -// ls -la -// - -// -// ./example.txt -// Hello, World! -// `; -// -// const { normalText, toolCalls } = parseAIResponse(aiResponse); -// console.log(normalText); -// console.log(toolCalls); - -// Convert OpenAI response to Anthropic format -export function convertO1ResponseToAnthropicMessage( - completion: OpenAI.Chat.Completions.ChatCompletion, -): Anthropic.Messages.Message { - const openAiMessage = completion.choices[0].message - const { normalText, toolCalls } = parseAIResponse(openAiMessage.content || "") - - const anthropicMessage: Anthropic.Messages.Message = { - id: completion.id, - type: "message", - role: openAiMessage.role, // always "assistant" - content: [ - { - type: "text", - text: normalText, - }, - ], - model: completion.model, - stop_reason: (() => { - switch (completion.choices[0].finish_reason) { - case "stop": - return "end_turn" - case "length": - return "max_tokens" - case "tool_calls": - return "tool_use" - case "content_filter": // Anthropic doesn't have an exact equivalent - default: - return null - } - })(), - stop_sequence: null, // which custom stop_sequence was generated, if any (not applicable if you don't use stop_sequence) - usage: { - input_tokens: completion.usage?.prompt_tokens || 0, - output_tokens: completion.usage?.completion_tokens || 0, - }, - } - - if (toolCalls.length > 0) { - anthropicMessage.content.push( - ...toolCalls.map((toolCall: ToolCall, index: number): Anthropic.ToolUseBlock => { - return { - type: "tool_use", - id: `call_${index}_${Date.now()}`, // Generate a unique ID for each tool call - name: toolCall.tool, - input: toolCall.tool_input, - } - }), - ) - } - - return anthropicMessage -} - -// Example usage: -// const openAICompletion = { -// id: "cmpl-123", -// choices: [{ -// message: { -// role: "assistant", -// content: "Here's my analysis...\n\n\n ls -la\n" -// }, -// finish_reason: "stop" -// }], -// model: "gpt-3.5-turbo", -// usage: { prompt_tokens: 50, completion_tokens: 100 } -// }; -// const anthropicMessage = convertO1ResponseToAnthropicMessage(openAICompletion); -// console.log(anthropicMessage); diff --git a/src/integrations/diagnostics/DiagnosticsMonitor.ts b/src/integrations/diagnostics/DiagnosticsMonitor.ts deleted file mode 100644 index 67735674844..00000000000 --- a/src/integrations/diagnostics/DiagnosticsMonitor.ts +++ /dev/null @@ -1,123 +0,0 @@ -/* -import * as vscode from "vscode" -import deepEqual from "fast-deep-equal" - -type FileDiagnostics = [vscode.Uri, vscode.Diagnostic[]][] - - -About Diagnostics: -The Problems tab shows diagnostics that have been reported for your project. These diagnostics are categorized into: -Errors: Critical issues that usually prevent your code from compiling or running correctly. -Warnings: Potential problems in the code that may not prevent it from running but could cause issues (e.g., bad practices, unused variables). -Information: Non-critical suggestions or tips (e.g., formatting issues or notes from linters). -The Problems tab displays diagnostics from various sources: -1. Language Servers: - - TypeScript: Type errors, missing imports, syntax issues - - Python: Syntax errors, invalid type hints, undefined variables - - JavaScript/Node.js: Parsing and execution errors -2. Linters: - - ESLint: Code style, best practices, potential bugs - - Pylint: Unused imports, naming conventions - - TSLint: Style and correctness issues in TypeScript -3. Build Tools: - - Webpack: Module resolution failures, build errors - - Gulp: Build errors during task execution -4. Custom Validators: - - Extensions can generate custom diagnostics for specific languages or tools -Each problem typically indicates its source (e.g., language server, linter, build tool). -Diagnostics update in real-time as you edit code, helping identify issues quickly. For example, if you introduce a syntax error in a TypeScript file, the Problems tab will immediately display the new error. - -Notes on diagnostics: -- linter diagnostics are only captured for open editors -- this works great for us since when cline edits/creates files its through vscode's textedit api's and we get those diagnostics for free -- some tools might require you to save the file or manually refresh to clear the problem from the list. - -System Prompt -- You will automatically receive workspace error diagnostics in environment_details. Be mindful that this may include issues beyond the scope of your task or the user's request. Only address errors relevant to your work, and avoid fixing pre-existing or unrelated issues unless the user specifically instructs you to do so. -- If you are unable to resolve errors provided in environment_details after two attempts, consider using ask_followup_question to ask the user for additional information, such as the latest documentation related to a problematic framework, to help you make progress on the task. If the error remains unresolved after this step, proceed with your task while disregarding the error. - -class DiagnosticsMonitor { - private diagnosticsChangeEmitter: vscode.EventEmitter = new vscode.EventEmitter() - private disposables: vscode.Disposable[] = [] - private lastDiagnostics: FileDiagnostics = [] - - constructor() { - this.disposables.push( - vscode.languages.onDidChangeDiagnostics(() => { - this.diagnosticsChangeEmitter.fire() - }) - ) - } - - public async getCurrentDiagnostics(shouldWaitForChanges: boolean): Promise { - const currentDiagnostics = this.getDiagnostics() - if (!shouldWaitForChanges) { - this.lastDiagnostics = currentDiagnostics - return currentDiagnostics - } - - if (!deepEqual(this.lastDiagnostics, currentDiagnostics)) { - this.lastDiagnostics = currentDiagnostics - return currentDiagnostics - } - - let timeout = 300 // only way this happens is if theres no errors - - // if diagnostics contain existing errors (since the check above didn't trigger) then it's likely cline just did something that should have fixed the error, so we'll give a longer grace period for diagnostics to catch up - const hasErrors = currentDiagnostics.some(([_, diagnostics]) => - diagnostics.some((d) => d.severity === vscode.DiagnosticSeverity.Error) - ) - if (hasErrors) { - console.log("Existing errors detected, extending timeout", currentDiagnostics) - timeout = 10_000 - } - - return this.waitForUpdatedDiagnostics(timeout) - } - - private async waitForUpdatedDiagnostics(timeout: number): Promise { - return new Promise((resolve, reject) => { - const timer = setTimeout(() => { - cleanup() - const finalDiagnostics = this.getDiagnostics() - this.lastDiagnostics = finalDiagnostics - resolve(finalDiagnostics) - }, timeout) - - const disposable = this.diagnosticsChangeEmitter.event(() => { - const updatedDiagnostics = this.getDiagnostics() // I thought this would only trigger when diagnostics changed, but that's not the case. - if (deepEqual(this.lastDiagnostics, updatedDiagnostics)) { - // diagnostics have not changed, ignoring... - return - } - cleanup() - this.lastDiagnostics = updatedDiagnostics - resolve(updatedDiagnostics) - }) - - const cleanup = () => { - clearTimeout(timer) - disposable.dispose() - } - }) - } - - private getDiagnostics(): FileDiagnostics { - const allDiagnostics = vscode.languages.getDiagnostics() - return allDiagnostics - .filter(([_, diagnostics]) => diagnostics.some((d) => d.severity === vscode.DiagnosticSeverity.Error)) - .map(([uri, diagnostics]) => [ - uri, - diagnostics.filter((d) => d.severity === vscode.DiagnosticSeverity.Error), - ]) - } - - public dispose() { - this.disposables.forEach((d) => d.dispose()) - this.disposables = [] - this.diagnosticsChangeEmitter.dispose() - } -} - -export default DiagnosticsMonitor -*/ diff --git a/src/integrations/terminal/TerminalActions.ts b/src/integrations/terminal/TerminalActions.ts deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/src/integrations/workspace/get-python-env.ts b/src/integrations/workspace/get-python-env.ts deleted file mode 100644 index 92575b408ab..00000000000 --- a/src/integrations/workspace/get-python-env.ts +++ /dev/null @@ -1,42 +0,0 @@ -import * as vscode from "vscode" - -/* -Used to get user's current python environment (unnecessary now that we use the IDE's terminal) -${await (async () => { - try { - const pythonEnvPath = await getPythonEnvPath() - if (pythonEnvPath) { - return `\nPython Environment: ${pythonEnvPath}` - } - } catch {} - return "" - })()} -*/ -export async function getPythonEnvPath(): Promise { - const pythonExtension = vscode.extensions.getExtension("ms-python.python") - - if (!pythonExtension) { - return undefined - } - - // Ensure the Python extension is activated - if (!pythonExtension.isActive) { - // if the python extension is not active, we can assume the project is not a python project - return undefined - } - - // Access the Python extension API - const pythonApi = pythonExtension.exports - // Get the active environment path for the current workspace - const workspaceFolder = vscode.workspace.workspaceFolders?.[0] - if (!workspaceFolder) { - return undefined - } - // Get the active python environment path for the current workspace - const pythonEnv = await pythonApi?.environments?.getActiveEnvironmentPath(workspaceFolder.uri) - if (pythonEnv && pythonEnv.path) { - return pythonEnv.path - } else { - return undefined - } -} diff --git a/webview-ui/src/components/common/Demo.tsx b/webview-ui/src/components/common/Demo.tsx deleted file mode 100644 index 6efd7e34301..00000000000 --- a/webview-ui/src/components/common/Demo.tsx +++ /dev/null @@ -1,130 +0,0 @@ -import { - VSCodeBadge, - VSCodeButton, - VSCodeCheckbox, - VSCodeDataGrid, - VSCodeDataGridCell, - VSCodeDataGridRow, - VSCodeDivider, - VSCodeDropdown, - VSCodeLink, - VSCodeOption, - VSCodePanels, - VSCodePanelTab, - VSCodePanelView, - VSCodeProgressRing, - VSCodeRadio, - VSCodeRadioGroup, - VSCodeTag, - VSCodeTextArea, - VSCodeTextField, -} from "@vscode/webview-ui-toolkit/react" - -function Demo() { - // function handleHowdyClick() { - // vscode.postMessage({ - // command: "hello", - // text: "Hey there partner! 🤠", - // }) - // } - - const rowData = [ - { - cell1: "Cell Data", - cell2: "Cell Data", - cell3: "Cell Data", - cell4: "Cell Data", - }, - { - cell1: "Cell Data", - cell2: "Cell Data", - cell3: "Cell Data", - cell4: "Cell Data", - }, - { - cell1: "Cell Data", - cell2: "Cell Data", - cell3: "Cell Data", - cell4: "Cell Data", - }, - ] - - return ( -
-

Hello World!

- Howdy! - -
- - - - A Custom Header Title - - - Another Custom Title - - - Title Is Custom - - - Custom Title - - - {rowData.map((row, index) => ( - - {row.cell1} - {row.cell2} - {row.cell3} - {row.cell4} - - ))} - - - -
- - - - - - - - - -
-
- - - - - - Add - Remove - - - Badge - Checkbox - - - Option 1 - Option 2 - - Link - - Tab 1 - Tab 2 - Panel View 1 - Panel View 2 - - - Radio 1 - Radio 2 - - Tag - -
-
- ) -} - -export default Demo diff --git a/webview-ui/src/components/settings/TabNavbar.tsx b/webview-ui/src/components/settings/TabNavbar.tsx deleted file mode 100644 index cdf40c64d21..00000000000 --- a/webview-ui/src/components/settings/TabNavbar.tsx +++ /dev/null @@ -1,167 +0,0 @@ -import { VSCodeButton } from "@vscode/webview-ui-toolkit/react" -import React, { useState } from "react" - -export const TAB_NAVBAR_HEIGHT = 24 -const BUTTON_MARGIN_RIGHT = "3px" -const LAST_BUTTON_MARGIN_RIGHT = "13px" - -type TabNavbarProps = { - onPlusClick: () => void - onHistoryClick: () => void - onSettingsClick: () => void -} - -type TooltipProps = { - text: string - isVisible: boolean - position: { x: number; y: number } - align?: "left" | "center" | "right" -} - -const Tooltip: React.FC = ({ text, isVisible, position, align = "center" }) => { - let leftPosition = position.x - let triangleStyle: React.CSSProperties = { - left: "50%", - marginLeft: "-5px", - } - - if (align === "right") { - leftPosition = position.x - 10 // Adjust this value as needed - triangleStyle = { - right: "10px", // Adjust this value to match the tooltip's right padding - marginLeft: "0", - } - } else if (align === "left") { - leftPosition = position.x + 10 // Adjust this value as needed - triangleStyle = { - left: "10px", // Adjust this value to match the tooltip's left padding - marginLeft: "0", - } - } - - return ( -
-
-
- {text} -
- ) -} - -const TabNavbar = ({ onPlusClick, onHistoryClick, onSettingsClick }: TabNavbarProps) => { - const [tooltip, setTooltip] = useState({ - text: "", - isVisible: false, - position: { x: 0, y: 0 }, - align: "center", - }) - - const showTooltip = (text: string, event: React.MouseEvent, align: "left" | "center" | "right" = "center") => { - const rect = event.currentTarget.getBoundingClientRect() - setTooltip({ - text, - isVisible: true, - position: { x: rect.left + rect.width / 2, y: rect.bottom + 7 }, - align, - }) - } - - const hideTooltip = () => { - setTooltip((prev) => ({ ...prev, isVisible: false })) - } - - const buttonStyle = { - marginRight: BUTTON_MARGIN_RIGHT, - } - - const lastButtonStyle = { - ...buttonStyle, - marginRight: LAST_BUTTON_MARGIN_RIGHT, - } - - return ( - <> -
- showTooltip("New Chat", e, "center")} - onMouseLeave={hideTooltip} - onMouseMove={(e) => showTooltip("New Chat", e, "center")}> - - - showTooltip("History", e, "center")} - onMouseLeave={hideTooltip} - onMouseMove={(e) => showTooltip("History", e, "center")}> - - - showTooltip("Settings", e, "right")} - onMouseLeave={hideTooltip} - onMouseMove={(e) => showTooltip("Settings", e, "right")}> - - -
- - - ) -} - -export default TabNavbar diff --git a/webview-ui/src/services/GitService.ts b/webview-ui/src/services/GitService.ts deleted file mode 100644 index 57a83e5ad93..00000000000 --- a/webview-ui/src/services/GitService.ts +++ /dev/null @@ -1,46 +0,0 @@ -import { vscode } from "../utils/vscode" - -export interface GitCommit { - hash: string - shortHash: string - subject: string - author: string - date: string -} - -class GitService { - private commits: GitCommit[] | null = null - private lastQuery: string = "" - - async searchCommits(query: string = ""): Promise { - if (query === this.lastQuery && this.commits) { - return this.commits - } - - // Request search from extension - vscode.postMessage({ type: "searchCommits", query }) - - // Wait for response - const response = await new Promise((resolve) => { - const handler = (event: MessageEvent) => { - const message = event.data - if (message.type === "commitSearchResults") { - window.removeEventListener("message", handler) - resolve(message.commits) - } - } - window.addEventListener("message", handler) - }) - - this.commits = response - this.lastQuery = query - return response - } - - clearCache() { - this.commits = null - this.lastQuery = "" - } -} - -export const gitService = new GitService() From 20f90732043223163275ba1c69d406d03a3ba22f Mon Sep 17 00:00:00 2001 From: Matt Rubens Date: Sun, 23 Feb 2025 02:20:36 -0500 Subject: [PATCH 032/585] Change response to be a user message --- src/core/Cline.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/core/Cline.ts b/src/core/Cline.ts index 54de357d469..35879a42913 100644 --- a/src/core/Cline.ts +++ b/src/core/Cline.ts @@ -498,11 +498,11 @@ export class Cline { await this.say("text", `new_task finished successfully! ${lastMessage ?? "Please continue to the next task."}`) await this.addToApiConversationHistory({ - role: "assistant", + role: "user", content: [ { type: "text", - text: `new_task finished successfully! ${lastMessage ?? "Please continue to the next task."}`, + text: `[new_task completed] Result: ${lastMessage ?? "Please continue to the next task."}`, }, ], }) From f11c0afa1e68da80b6d31d293ad30e0dedbfc535 Mon Sep 17 00:00:00 2001 From: cte Date: Sun, 23 Feb 2025 02:28:10 -0800 Subject: [PATCH 033/585] Stabilize tests in CI --- .github/workflows/code-qa.yml | 27 +- package-lock.json | 3 +- package.json | 5 +- .../__tests__/edit-strategies.test.ts | 6 +- .../__tests__/LocalCheckpointService.test.ts | 15 +- .../__tests__/ShadowCheckpointService.test.ts | 20 +- webview-ui/.eslintrc.json | 3 +- webview-ui/package-lock.json | 2139 +++++++++++------ webview-ui/package.json | 6 +- 9 files changed, 1427 insertions(+), 797 deletions(-) diff --git a/.github/workflows/code-qa.yml b/.github/workflows/code-qa.yml index e28302f46d8..fde891f8041 100644 --- a/.github/workflows/code-qa.yml +++ b/.github/workflows/code-qa.yml @@ -43,7 +43,22 @@ jobs: - name: Run knip checks run: npm run knip - unit-test: + test-extension: + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v4 + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: '18' + cache: 'npm' + - name: Install dependencies + run: npm run install:all + - name: Run unit tests + run: npx jest --silent + + test-webview: runs-on: ubuntu-latest steps: - name: Checkout code @@ -56,7 +71,15 @@ jobs: - name: Install dependencies run: npm run install:all - name: Run unit tests - run: npm test + working-directory: webview-ui + run: npx jest --silent + + unit-test: + needs: [test-extension, test-webview] + runs-on: ubuntu-latest + steps: + - name: NO-OP + run: echo "All unit tests passed." check-openrouter-api-key: runs-on: ubuntu-latest diff --git a/package-lock.json b/package-lock.json index 0e139c0ff82..9822548678d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11312,7 +11312,8 @@ "version": "1.0.5", "resolved": "https://registry.npmjs.org/jest-simple-dot-reporter/-/jest-simple-dot-reporter-1.0.5.tgz", "integrity": "sha512-cZLFG/C7k0+WYoIGGuGXKm0vmJiXlWG/m3uCZ4RaMPYxt8lxjdXMLHYkxXaQ7gVWaSPe7uAPCEUcRxthC5xskg==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/jest-snapshot": { "version": "29.7.0", diff --git a/package.json b/package.json index 133580698df..c4c38e78571 100644 --- a/package.json +++ b/package.json @@ -277,7 +277,7 @@ "build": "npm run build:webview && npm run vsix", "build:webview": "cd webview-ui && npm run build", "changeset": "changeset", - "check-types": "tsc --noEmit", + "check-types": "tsc --noEmit && cd webview-ui && npm run check-types", "compile": "tsc -p . --outDir out && node esbuild.js", "compile:integration": "tsc -p tsconfig.integration.json", "install:all": "npm install && cd webview-ui && npm install", @@ -289,8 +289,7 @@ "package": "npm run build:webview && npm run check-types && npm run lint && node esbuild.js --production", "pretest": "npm run compile && npm run compile:integration", "dev": "cd webview-ui && npm run dev", - "test": "jest && npm run test:webview", - "test:webview": "cd webview-ui && npm run test", + "test": "jest && cd webview-ui && npm run test", "test:integration": "npm run build && npm run compile:integration && npx dotenvx run -f .env.integration -- node ./out-integration/test/runTest.js", "prepare": "husky", "publish:marketplace": "vsce publish && ovsx publish", diff --git a/src/core/diff/strategies/new-unified/__tests__/edit-strategies.test.ts b/src/core/diff/strategies/new-unified/__tests__/edit-strategies.test.ts index 2bc35540baf..f8251e3d6e6 100644 --- a/src/core/diff/strategies/new-unified/__tests__/edit-strategies.test.ts +++ b/src/core/diff/strategies/new-unified/__tests__/edit-strategies.test.ts @@ -1,3 +1,5 @@ +/// + import { applyContextMatching, applyDMP, applyGitFallback } from "../edit-strategies" import { Hunk } from "../types" @@ -275,7 +277,7 @@ describe("applyGitFallback", () => { expect(result.result.join("\n")).toEqual("line1\nnew line2\nline3") expect(result.confidence).toBe(1) expect(result.strategy).toBe("git-fallback") - }) + }, 10_000) it("should return original content with 0 confidence when changes cannot be applied", async () => { const hunk = { @@ -291,5 +293,5 @@ describe("applyGitFallback", () => { expect(result.result).toEqual(content) expect(result.confidence).toBe(0) expect(result.strategy).toBe("git-fallback") - }) + }, 10_000) }) diff --git a/src/services/checkpoints/__tests__/LocalCheckpointService.test.ts b/src/services/checkpoints/__tests__/LocalCheckpointService.test.ts index 5ba2721b8c2..29ab7133a56 100644 --- a/src/services/checkpoints/__tests__/LocalCheckpointService.test.ts +++ b/src/services/checkpoints/__tests__/LocalCheckpointService.test.ts @@ -9,6 +9,8 @@ import { simpleGit, SimpleGit } from "simple-git" import { CheckpointServiceFactory } from "../CheckpointServiceFactory" import { LocalCheckpointService } from "../LocalCheckpointService" +const tmpDir = path.join(os.tmpdir(), "test-LocalCheckpointService") + describe("LocalCheckpointService", () => { const taskId = "test-task" @@ -29,7 +31,7 @@ describe("LocalCheckpointService", () => { textFileContent?: string }) => { // Create a temporary directory for testing. - await fs.mkdir(workspaceDir) + await fs.mkdir(workspaceDir, { recursive: true }) // Initialize git repo. const git = simpleGit(workspaceDir) @@ -49,7 +51,7 @@ describe("LocalCheckpointService", () => { } beforeEach(async () => { - const workspaceDir = path.join(os.tmpdir(), `checkpoint-service-test-${Date.now()}`) + const workspaceDir = path.join(tmpDir, `checkpoint-service-test-${Date.now()}`) const repo = await initRepo({ workspaceDir }) testFile = repo.testFile @@ -60,10 +62,13 @@ describe("LocalCheckpointService", () => { }) afterEach(async () => { - await fs.rm(service.workspaceDir, { recursive: true, force: true }) jest.restoreAllMocks() }) + afterAll(async () => { + await fs.rm(tmpDir, { recursive: true, force: true }) + }) + describe("getDiff", () => { it("returns the correct diff between commits", async () => { await fs.writeFile(testFile, "Ahoy, world!") @@ -316,7 +321,7 @@ describe("LocalCheckpointService", () => { describe("create", () => { it("initializes a git repository if one does not already exist", async () => { - const workspaceDir = path.join(os.tmpdir(), `checkpoint-service-test2-${Date.now()}`) + const workspaceDir = path.join(tmpDir, `checkpoint-service-test2-${Date.now()}`) await fs.mkdir(workspaceDir) const newTestFile = path.join(workspaceDir, "test.txt") await fs.writeFile(newTestFile, "Hello, world!") @@ -364,7 +369,7 @@ describe("LocalCheckpointService", () => { }) it("respects existing git user configuration", async () => { - const workspaceDir = path.join(os.tmpdir(), `checkpoint-service-test-config2-${Date.now()}`) + const workspaceDir = path.join(tmpDir, `checkpoint-service-test-config2-${Date.now()}`) const userName = "Custom User" const userEmail = "custom@example.com" await initRepo({ workspaceDir, userName, userEmail }) diff --git a/src/services/checkpoints/__tests__/ShadowCheckpointService.test.ts b/src/services/checkpoints/__tests__/ShadowCheckpointService.test.ts index 0e32e82fdfa..5bcc5d444cc 100644 --- a/src/services/checkpoints/__tests__/ShadowCheckpointService.test.ts +++ b/src/services/checkpoints/__tests__/ShadowCheckpointService.test.ts @@ -13,11 +13,12 @@ jest.mock("globby", () => ({ globby: jest.fn().mockResolvedValue([]), })) +const tmpDir = path.join(os.tmpdir(), "test-ShadowCheckpointService") + describe("ShadowCheckpointService", () => { const taskId = "test-task" let workspaceGit: SimpleGit - let shadowGit: SimpleGit let testFile: string let service: ShadowCheckpointService @@ -35,7 +36,7 @@ describe("ShadowCheckpointService", () => { textFileContent?: string }) => { // Create a temporary directory for testing. - await fs.mkdir(workspaceDir) + await fs.mkdir(workspaceDir, { recursive: true }) // Initialize git repo. const git = simpleGit(workspaceDir) @@ -57,8 +58,8 @@ describe("ShadowCheckpointService", () => { beforeEach(async () => { jest.mocked(require("globby").globby).mockClear().mockResolvedValue([]) - const shadowDir = path.join(os.tmpdir(), `shadow-${Date.now()}`) - const workspaceDir = path.join(os.tmpdir(), `workspace-${Date.now()}`) + const shadowDir = path.join(tmpDir, `shadow-${Date.now()}`) + const workspaceDir = path.join(tmpDir, `workspace-${Date.now()}`) const repo = await initRepo({ workspaceDir }) testFile = repo.testFile @@ -69,15 +70,16 @@ describe("ShadowCheckpointService", () => { }) workspaceGit = repo.git - shadowGit = service.git }) afterEach(async () => { - await fs.rm(service.shadowDir, { recursive: true, force: true }) - await fs.rm(service.workspaceDir, { recursive: true, force: true }) jest.restoreAllMocks() }) + afterAll(async () => { + await fs.rm(tmpDir, { recursive: true, force: true }) + }) + describe("getDiff", () => { it("returns the correct diff between commits", async () => { await fs.writeFile(testFile, "Ahoy, world!") @@ -299,8 +301,8 @@ describe("ShadowCheckpointService", () => { describe("create", () => { it("initializes a git repository if one does not already exist", async () => { - const shadowDir = path.join(os.tmpdir(), `shadow2-${Date.now()}`) - const workspaceDir = path.join(os.tmpdir(), `workspace2-${Date.now()}`) + const shadowDir = path.join(tmpDir, `shadow2-${Date.now()}`) + const workspaceDir = path.join(tmpDir, `workspace2-${Date.now()}`) await fs.mkdir(workspaceDir) const newTestFile = path.join(workspaceDir, "test.txt") diff --git a/webview-ui/.eslintrc.json b/webview-ui/.eslintrc.json index e6aa150f118..4309a7c3ed6 100644 --- a/webview-ui/.eslintrc.json +++ b/webview-ui/.eslintrc.json @@ -1,3 +1,4 @@ { - "extends": "react-app" + "extends": "react-app", + "ignorePatterns": ["!.storybook"] } diff --git a/webview-ui/package-lock.json b/webview-ui/package-lock.json index 325b8100ee8..1d64f934dc2 100644 --- a/webview-ui/package-lock.json +++ b/webview-ui/package-lock.json @@ -68,13 +68,13 @@ "eslint-plugin-react-hooks": "^4.6.0", "eslint-plugin-storybook": "^0.11.2", "identity-obj-proxy": "^3.0.0", - "jest": "^27.5.1", - "jest-environment-jsdom": "^27.5.1", + "jest": "^29.7.0", + "jest-environment-jsdom": "^29.7.0", "jest-simple-dot-reporter": "^1.0.5", "shiki": "^2.3.2", "storybook": "^8.5.6", "storybook-dark-mode": "^4.0.2", - "ts-jest": "^27.1.5", + "ts-jest": "^29.2.5", "typescript": "^4.9.5", "vite": "6.0.11" } @@ -3006,61 +3006,61 @@ } }, "node_modules/@jest/console": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/@jest/console/-/console-27.5.1.tgz", - "integrity": "sha512-kZ/tNpS3NXn0mlXXXPNuDZnb4c0oZ20r4K5eemM2k30ZC3G0T02nXUvyhf5YdbXWHPEJLc9qGLxEZ216MdL+Zg==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/console/-/console-29.7.0.tgz", + "integrity": "sha512-5Ni4CU7XHQi32IJ398EEP4RrB8eV09sXP2ROqD4bksHrnTree52PsxvX8tpL8LvTZ3pFzXyPbNQReSN41CAhOg==", "dev": true, "license": "MIT", "dependencies": { - "@jest/types": "^27.5.1", + "@jest/types": "^29.6.3", "@types/node": "*", "chalk": "^4.0.0", - "jest-message-util": "^27.5.1", - "jest-util": "^27.5.1", + "jest-message-util": "^29.7.0", + "jest-util": "^29.7.0", "slash": "^3.0.0" }, "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, "node_modules/@jest/core": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/@jest/core/-/core-27.5.1.tgz", - "integrity": "sha512-AK6/UTrvQD0Cd24NSqmIA6rKsu0tKIxfiCducZvqxYdmMisOYAsdItspT+fQDQYARPf8XgjAFZi0ogW2agH5nQ==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/core/-/core-29.7.0.tgz", + "integrity": "sha512-n7aeXWKMnGtDA48y8TLWJPJmLmmZ642Ceo78cYWEpiD7FzDgmNDV/GCVRorPABdXLJZ/9wzzgZAlHjXjxDHGsg==", "dev": true, "license": "MIT", "dependencies": { - "@jest/console": "^27.5.1", - "@jest/reporters": "^27.5.1", - "@jest/test-result": "^27.5.1", - "@jest/transform": "^27.5.1", - "@jest/types": "^27.5.1", + "@jest/console": "^29.7.0", + "@jest/reporters": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", "@types/node": "*", "ansi-escapes": "^4.2.1", "chalk": "^4.0.0", - "emittery": "^0.8.1", + "ci-info": "^3.2.0", "exit": "^0.1.2", "graceful-fs": "^4.2.9", - "jest-changed-files": "^27.5.1", - "jest-config": "^27.5.1", - "jest-haste-map": "^27.5.1", - "jest-message-util": "^27.5.1", - "jest-regex-util": "^27.5.1", - "jest-resolve": "^27.5.1", - "jest-resolve-dependencies": "^27.5.1", - "jest-runner": "^27.5.1", - "jest-runtime": "^27.5.1", - "jest-snapshot": "^27.5.1", - "jest-util": "^27.5.1", - "jest-validate": "^27.5.1", - "jest-watcher": "^27.5.1", + "jest-changed-files": "^29.7.0", + "jest-config": "^29.7.0", + "jest-haste-map": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-regex-util": "^29.6.3", + "jest-resolve": "^29.7.0", + "jest-resolve-dependencies": "^29.7.0", + "jest-runner": "^29.7.0", + "jest-runtime": "^29.7.0", + "jest-snapshot": "^29.7.0", + "jest-util": "^29.7.0", + "jest-validate": "^29.7.0", + "jest-watcher": "^29.7.0", "micromatch": "^4.0.4", - "rimraf": "^3.0.0", + "pretty-format": "^29.7.0", "slash": "^3.0.0", "strip-ansi": "^6.0.0" }, "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" }, "peerDependencies": { "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" @@ -3071,90 +3071,162 @@ } } }, + "node_modules/@jest/core/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@jest/core/node_modules/pretty-format": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", + "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/schemas": "^29.6.3", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/core/node_modules/react-is": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", + "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", + "dev": true, + "license": "MIT" + }, "node_modules/@jest/environment": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-27.5.1.tgz", - "integrity": "sha512-/WQjhPJe3/ghaol/4Bq480JKXV/Rfw8nQdN7f41fM8VDHLcxKXou6QyXAh3EFr9/bVG3x74z1NWDkP87EiY8gA==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-29.7.0.tgz", + "integrity": "sha512-aQIfHDq33ExsN4jP1NWGXhxgQ/wixs60gDiKO+XVMd8Mn0NWPWgc34ZQDTb2jKaUWQ7MuwoitXAsN2XVXNMpAw==", "dev": true, "license": "MIT", "dependencies": { - "@jest/fake-timers": "^27.5.1", - "@jest/types": "^27.5.1", + "@jest/fake-timers": "^29.7.0", + "@jest/types": "^29.6.3", "@types/node": "*", - "jest-mock": "^27.5.1" + "jest-mock": "^29.7.0" }, "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/expect": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/expect/-/expect-29.7.0.tgz", + "integrity": "sha512-8uMeAMycttpva3P1lBHB8VciS9V0XAr3GymPpipdyQXbBcuhkLQOSe8E/p92RyAdToS6ZD1tFkX+CkhoECE0dQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "expect": "^29.7.0", + "jest-snapshot": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/expect-utils": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-29.7.0.tgz", + "integrity": "sha512-GlsNBWiFQFCVi9QVSx7f5AgMeLxe9YCCs5PuP2O2LdjDAA8Jh9eX7lA1Jq/xdXw3Wb3hyvlFNfZIfcRetSzYcA==", + "dev": true, + "license": "MIT", + "dependencies": { + "jest-get-type": "^29.6.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/expect-utils/node_modules/jest-get-type": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-29.6.3.tgz", + "integrity": "sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, "node_modules/@jest/fake-timers": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-27.5.1.tgz", - "integrity": "sha512-/aPowoolwa07k7/oM3aASneNeBGCmGQsc3ugN4u6s4C/+s5M64MFo/+djTdiwcbQlRfFElGuDXWzaWj6QgKObQ==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-29.7.0.tgz", + "integrity": "sha512-q4DH1Ha4TTFPdxLsqDXK1d3+ioSL7yL5oCMJZgDYm6i+6CygW5E5xVr/D1HdsGxjt1ZWSfUAs9OxSB/BNelWrQ==", "dev": true, "license": "MIT", "dependencies": { - "@jest/types": "^27.5.1", - "@sinonjs/fake-timers": "^8.0.1", + "@jest/types": "^29.6.3", + "@sinonjs/fake-timers": "^10.0.2", "@types/node": "*", - "jest-message-util": "^27.5.1", - "jest-mock": "^27.5.1", - "jest-util": "^27.5.1" + "jest-message-util": "^29.7.0", + "jest-mock": "^29.7.0", + "jest-util": "^29.7.0" }, "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, "node_modules/@jest/globals": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-27.5.1.tgz", - "integrity": "sha512-ZEJNB41OBQQgGzgyInAv0UUfDDj3upmHydjieSxFvTRuZElrx7tXg/uVQ5hYVEwiXs3+aMsAeEc9X7xiSKCm4Q==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-29.7.0.tgz", + "integrity": "sha512-mpiz3dutLbkW2MNFubUGUEVLkTGiqW6yLVTA+JbP6fI6J5iL9Y0Nlg8k95pcF8ctKwCS7WVxteBs29hhfAotzQ==", "dev": true, "license": "MIT", "dependencies": { - "@jest/environment": "^27.5.1", - "@jest/types": "^27.5.1", - "expect": "^27.5.1" + "@jest/environment": "^29.7.0", + "@jest/expect": "^29.7.0", + "@jest/types": "^29.6.3", + "jest-mock": "^29.7.0" }, "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, "node_modules/@jest/reporters": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-27.5.1.tgz", - "integrity": "sha512-cPXh9hWIlVJMQkVk84aIvXuBB4uQQmFqZiacloFuGiP3ah1sbCxCosidXFDfqG8+6fO1oR2dTJTlsOy4VFmUfw==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-29.7.0.tgz", + "integrity": "sha512-DApq0KJbJOEzAFYjHADNNxAE3KbhxQB1y5Kplb5Waqw6zVbuWatSnMjE5gs8FUgEPmNsnZA3NCWl9NG0ia04Pg==", "dev": true, "license": "MIT", "dependencies": { "@bcoe/v8-coverage": "^0.2.3", - "@jest/console": "^27.5.1", - "@jest/test-result": "^27.5.1", - "@jest/transform": "^27.5.1", - "@jest/types": "^27.5.1", + "@jest/console": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", + "@jridgewell/trace-mapping": "^0.3.18", "@types/node": "*", "chalk": "^4.0.0", "collect-v8-coverage": "^1.0.0", "exit": "^0.1.2", - "glob": "^7.1.2", + "glob": "^7.1.3", "graceful-fs": "^4.2.9", "istanbul-lib-coverage": "^3.0.0", - "istanbul-lib-instrument": "^5.1.0", + "istanbul-lib-instrument": "^6.0.0", "istanbul-lib-report": "^3.0.0", "istanbul-lib-source-maps": "^4.0.0", "istanbul-reports": "^3.1.3", - "jest-haste-map": "^27.5.1", - "jest-resolve": "^27.5.1", - "jest-util": "^27.5.1", - "jest-worker": "^27.5.1", + "jest-message-util": "^29.7.0", + "jest-util": "^29.7.0", + "jest-worker": "^29.7.0", "slash": "^3.0.0", - "source-map": "^0.6.0", "string-length": "^4.0.1", - "terminal-link": "^2.0.0", - "v8-to-istanbul": "^8.1.0" + "strip-ansi": "^6.0.0", + "v8-to-istanbul": "^9.0.1" }, "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" }, "peerDependencies": { "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" @@ -3165,102 +3237,109 @@ } } }, + "node_modules/@jest/schemas": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.6.3.tgz", + "integrity": "sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@sinclair/typebox": "^0.27.8" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, "node_modules/@jest/source-map": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-27.5.1.tgz", - "integrity": "sha512-y9NIHUYF3PJRlHk98NdC/N1gl88BL08aQQgu4k4ZopQkCw9t9cV8mtl3TV8b/YCB8XaVTFrmUTAJvjsntDireg==", + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-29.6.3.tgz", + "integrity": "sha512-MHjT95QuipcPrpLM+8JMSzFx6eHp5Bm+4XeFDJlwsvVBjmKNiIAvasGK2fxz2WbGRlnvqehFbh07MMa7n3YJnw==", "dev": true, "license": "MIT", "dependencies": { + "@jridgewell/trace-mapping": "^0.3.18", "callsites": "^3.0.0", - "graceful-fs": "^4.2.9", - "source-map": "^0.6.0" + "graceful-fs": "^4.2.9" }, "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, "node_modules/@jest/test-result": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-27.5.1.tgz", - "integrity": "sha512-EW35l2RYFUcUQxFJz5Cv5MTOxlJIQs4I7gxzi2zVU7PJhOwfYq1MdC5nhSmYjX1gmMmLPvB3sIaC+BkcHRBfag==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-29.7.0.tgz", + "integrity": "sha512-Fdx+tv6x1zlkJPcWXmMDAG2HBnaR9XPSd5aDWQVsfrZmLVT3lU1cwyxLgRmXR9yrq4NBoEm9BMsfgFzTQAbJYA==", "dev": true, "license": "MIT", "dependencies": { - "@jest/console": "^27.5.1", - "@jest/types": "^27.5.1", + "@jest/console": "^29.7.0", + "@jest/types": "^29.6.3", "@types/istanbul-lib-coverage": "^2.0.0", "collect-v8-coverage": "^1.0.0" }, "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, "node_modules/@jest/test-sequencer": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-27.5.1.tgz", - "integrity": "sha512-LCheJF7WB2+9JuCS7VB/EmGIdQuhtqjRNI9A43idHv3E4KltCTsPsLxvdaubFHSYwY/fNjMWjl6vNRhDiN7vpQ==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-29.7.0.tgz", + "integrity": "sha512-GQwJ5WZVrKnOJuiYiAF52UNUJXgTZx1NHjFSEB0qEMmSZKAkdMoIzw/Cj6x6NF4AvV23AUqDpFzQkN/eYCYTxw==", "dev": true, "license": "MIT", "dependencies": { - "@jest/test-result": "^27.5.1", + "@jest/test-result": "^29.7.0", "graceful-fs": "^4.2.9", - "jest-haste-map": "^27.5.1", - "jest-runtime": "^27.5.1" + "jest-haste-map": "^29.7.0", + "slash": "^3.0.0" }, "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, "node_modules/@jest/transform": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-27.5.1.tgz", - "integrity": "sha512-ipON6WtYgl/1329g5AIJVbUuEh0wZVbdpGwC99Jw4LwuoBNS95MVphU6zOeD9pDkon+LLbFL7lOQRapbB8SCHw==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-29.7.0.tgz", + "integrity": "sha512-ok/BTPFzFKVMwO5eOHRrvnBVHdRy9IrsrW1GpMaQ9MCnilNLXQKmAX8s1YXDFaai9xJpac2ySzV0YeRRECr2Vw==", "dev": true, "license": "MIT", "dependencies": { - "@babel/core": "^7.1.0", - "@jest/types": "^27.5.1", + "@babel/core": "^7.11.6", + "@jest/types": "^29.6.3", + "@jridgewell/trace-mapping": "^0.3.18", "babel-plugin-istanbul": "^6.1.1", "chalk": "^4.0.0", - "convert-source-map": "^1.4.0", - "fast-json-stable-stringify": "^2.0.0", + "convert-source-map": "^2.0.0", + "fast-json-stable-stringify": "^2.1.0", "graceful-fs": "^4.2.9", - "jest-haste-map": "^27.5.1", - "jest-regex-util": "^27.5.1", - "jest-util": "^27.5.1", + "jest-haste-map": "^29.7.0", + "jest-regex-util": "^29.6.3", + "jest-util": "^29.7.0", "micromatch": "^4.0.4", "pirates": "^4.0.4", "slash": "^3.0.0", - "source-map": "^0.6.1", - "write-file-atomic": "^3.0.0" + "write-file-atomic": "^4.0.2" }, "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/@jest/transform/node_modules/convert-source-map": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", - "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==", - "dev": true, - "license": "MIT" - }, "node_modules/@jest/types": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-27.5.1.tgz", - "integrity": "sha512-Cx46iJ9QpwQTjIdq5VJu2QTMMs3QlEjI0x1QbBP5W1+nMzyc2XmimiRR/CbX9TO0cPTeUlxWMOu8mslYsJ8DEw==", + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz", + "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==", "dev": true, "license": "MIT", "dependencies": { + "@jest/schemas": "^29.6.3", "@types/istanbul-lib-coverage": "^2.0.0", "@types/istanbul-reports": "^3.0.0", "@types/node": "*", - "@types/yargs": "^16.0.0", + "@types/yargs": "^17.0.8", "chalk": "^4.0.0" }, "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, "node_modules/@joshwooding/vite-plugin-react-docgen-typescript": { @@ -5358,10 +5437,17 @@ "dev": true, "license": "MIT" }, + "node_modules/@sinclair/typebox": { + "version": "0.27.8", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz", + "integrity": "sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==", + "dev": true, + "license": "MIT" + }, "node_modules/@sinonjs/commons": { - "version": "1.8.6", - "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.8.6.tgz", - "integrity": "sha512-Ky+XkAkqPZSm3NLBeUng77EBQl3cmeJhITaGHdYH8kjVB+aun3S4XBRti2zt17mtt0mIUDiNxYeoJm6drVvBJQ==", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.1.tgz", + "integrity": "sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ==", "dev": true, "license": "BSD-3-Clause", "dependencies": { @@ -5369,13 +5455,13 @@ } }, "node_modules/@sinonjs/fake-timers": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-8.1.0.tgz", - "integrity": "sha512-OAPJUAtgeINhh/TAlUID4QTs53Njm7xzddaVlEs/SXwgtiD1tW22zAB/W1wdqfrpmikgaWQ9Fw6Ws+hsiRm5Vg==", + "version": "10.3.0", + "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-10.3.0.tgz", + "integrity": "sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA==", "dev": true, "license": "BSD-3-Clause", "dependencies": { - "@sinonjs/commons": "^1.7.0" + "@sinonjs/commons": "^3.0.0" } }, "node_modules/@storybook/addon-actions": { @@ -5692,28 +5778,6 @@ "node": ">=10" } }, - "node_modules/@storybook/core/node_modules/ws": { - "version": "8.18.0", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.0.tgz", - "integrity": "sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10.0.0" - }, - "peerDependencies": { - "bufferutil": "^4.0.1", - "utf-8-validate": ">=5.0.2" - }, - "peerDependenciesMeta": { - "bufferutil": { - "optional": true - }, - "utf-8-validate": { - "optional": true - } - } - }, "node_modules/@storybook/csf": { "version": "0.1.12", "resolved": "https://registry.npmjs.org/@storybook/csf/-/csf-0.1.12.tgz", @@ -6356,13 +6420,13 @@ } }, "node_modules/@tootallnate/once": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz", - "integrity": "sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-2.0.0.tgz", + "integrity": "sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A==", "dev": true, "license": "MIT", "engines": { - "node": ">= 6" + "node": ">= 10" } }, "node_modules/@types/aria-query": { @@ -6512,6 +6576,18 @@ "integrity": "sha512-aLkWa0C0vO5b4Sr798E26QgOkss68Un0bLjs7u9qxzPT5CG+8DuNTffWES58YzJs3hrVAOs1wonycqEBqNJubA==", "license": "MIT" }, + "node_modules/@types/jsdom": { + "version": "20.0.1", + "resolved": "https://registry.npmjs.org/@types/jsdom/-/jsdom-20.0.1.tgz", + "integrity": "sha512-d0r18sZPmMQr1eG35u12FZfhIXNrnsPU/g5wvRKCUf/tOGilKKwYMYGqh33BNR6ba+2gkHw1EUiHoN3mn7E5IQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*", + "@types/tough-cookie": "*", + "parse5": "^7.0.0" + } + }, "node_modules/@types/json-schema": { "version": "7.0.15", "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", @@ -6571,13 +6647,6 @@ "dev": true, "license": "MIT" }, - "node_modules/@types/prettier": { - "version": "2.7.3", - "resolved": "https://registry.npmjs.org/@types/prettier/-/prettier-2.7.3.tgz", - "integrity": "sha512-+68kP9yzs4LMp7VNh8gdzMSPZFL44MLGqiHWvttYJe+6qnuVr4Ek9wSBQoveqY/r+LwjCcU29kNVkidwim+kYA==", - "dev": true, - "license": "MIT" - }, "node_modules/@types/prop-types": { "version": "15.7.14", "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.14.tgz", @@ -6648,6 +6717,13 @@ "@types/jest": "*" } }, + "node_modules/@types/tough-cookie": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/@types/tough-cookie/-/tough-cookie-4.0.5.tgz", + "integrity": "sha512-/Ad8+nIOV7Rl++6f1BdKxFSMgmoqEoYbHRpPcx3JEfv8VRsQe9Z4mCXeJBzxs7mbHY/XOZZuXlRNfhpVPbs6ZA==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/unist": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz", @@ -6669,9 +6745,9 @@ "license": "MIT" }, "node_modules/@types/yargs": { - "version": "16.0.9", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.9.tgz", - "integrity": "sha512-tHhzvkFXZQeTECenFoRljLBYPZJ7jAVxqqtEI0qTLOmuultnFp4I9yKE17vTuhf7BkhCu7I4XuemPgikDVuYqA==", + "version": "17.0.33", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.33.tgz", + "integrity": "sha512-WpxBCKWPLr4xSsHgz511rFJAM+wS28w2zEO1QDNY5zM/S8ok70NNfztH0xwhqKyaK0OHCbN98LDAZuy1ctxDkA==", "dev": true, "license": "MIT", "dependencies": { @@ -7290,27 +7366,14 @@ } }, "node_modules/acorn-globals": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/acorn-globals/-/acorn-globals-6.0.0.tgz", - "integrity": "sha512-ZQl7LOWaF5ePqqcX4hLuv/bLXYQNfNWw2c0/yX/TsPRKamzHcTGQnlCjHT3TsmkOUVEPS3crCxiPfdzE/Trlhg==", + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/acorn-globals/-/acorn-globals-7.0.1.tgz", + "integrity": "sha512-umOSDSDrfHbTNPuNpC2NSnnA3LUrqpevPb4T9jRx4MagXNS0rs+gwiTcAvqCRmsD6utzsrzNt+ebm00SNWiC3Q==", "dev": true, "license": "MIT", "dependencies": { - "acorn": "^7.1.1", - "acorn-walk": "^7.1.1" - } - }, - "node_modules/acorn-globals/node_modules/acorn": { - "version": "7.4.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", - "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==", - "dev": true, - "license": "MIT", - "bin": { - "acorn": "bin/acorn" - }, - "engines": { - "node": ">=0.4.0" + "acorn": "^8.1.0", + "acorn-walk": "^8.0.2" } }, "node_modules/acorn-jsx": { @@ -7324,11 +7387,14 @@ } }, "node_modules/acorn-walk": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-7.2.0.tgz", - "integrity": "sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA==", + "version": "8.3.4", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.4.tgz", + "integrity": "sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g==", "dev": true, "license": "MIT", + "dependencies": { + "acorn": "^8.11.0" + }, "engines": { "node": ">=0.4.0" } @@ -7647,6 +7713,13 @@ "dev": true, "license": "MIT" }, + "node_modules/async": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz", + "integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==", + "dev": true, + "license": "MIT" + }, "node_modules/async-function": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/async-function/-/async-function-1.0.0.tgz", @@ -7701,23 +7774,22 @@ } }, "node_modules/babel-jest": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-27.5.1.tgz", - "integrity": "sha512-cdQ5dXjGRd0IBRATiQ4mZGlGlRE8kJpjPOixdNRdT+m3UcNqmYWN6rK6nvtXYfY3D76cb8s/O1Ss8ea24PIwcg==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-29.7.0.tgz", + "integrity": "sha512-BrvGY3xZSwEcCzKvKsCi2GgHqDqsYkOP4/by5xCgIwGXQxIEh+8ew3gmrE1y7XRR6LHZIj6yLYnUi/mm2KXKBg==", "dev": true, "license": "MIT", "dependencies": { - "@jest/transform": "^27.5.1", - "@jest/types": "^27.5.1", + "@jest/transform": "^29.7.0", "@types/babel__core": "^7.1.14", "babel-plugin-istanbul": "^6.1.1", - "babel-preset-jest": "^27.5.1", + "babel-preset-jest": "^29.6.3", "chalk": "^4.0.0", "graceful-fs": "^4.2.9", "slash": "^3.0.0" }, "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" }, "peerDependencies": { "@babel/core": "^7.8.0" @@ -7740,20 +7812,37 @@ "node": ">=8" } }, + "node_modules/babel-plugin-istanbul/node_modules/istanbul-lib-instrument": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.1.tgz", + "integrity": "sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@babel/core": "^7.12.3", + "@babel/parser": "^7.14.7", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-coverage": "^3.2.0", + "semver": "^6.3.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/babel-plugin-jest-hoist": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-27.5.1.tgz", - "integrity": "sha512-50wCwD5EMNW4aRpOwtqzyZHIewTYNxLA4nhB+09d8BIssfNfzBRhkBIHiaPv1Si226TQSvp8gxAJm2iY2qs2hQ==", + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-29.6.3.tgz", + "integrity": "sha512-ESAc/RJvGTFEzRwOTT4+lNDk/GNHMkKbNzsvT0qKRfDyyYTskxB5rnU2njIDYVxXCBHHEI1c0YwHob3WaYujOg==", "dev": true, "license": "MIT", "dependencies": { "@babel/template": "^7.3.3", "@babel/types": "^7.3.3", - "@types/babel__core": "^7.0.0", + "@types/babel__core": "^7.1.14", "@types/babel__traverse": "^7.0.6" }, "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, "node_modules/babel-plugin-macros": { @@ -7849,17 +7938,17 @@ } }, "node_modules/babel-preset-jest": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-27.5.1.tgz", - "integrity": "sha512-Nptf2FzlPCWYuJg41HBqXVT8ym6bXOevuCTbhxlUpjwtysGaIWFvDEjp4y+G7fl13FgOdjs7P/DmErqH7da0Ag==", + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-29.6.3.tgz", + "integrity": "sha512-0B3bhxR6snWXJZtR/RliHTDPRgn1sNHOR0yVtq/IiQFyuOVjFS+wuio/R4gSNkyYmKmJB4wGZv2NZanmKmTnNA==", "dev": true, "license": "MIT", "dependencies": { - "babel-plugin-jest-hoist": "^27.5.1", + "babel-plugin-jest-hoist": "^29.6.3", "babel-preset-current-node-syntax": "^1.0.0" }, "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" }, "peerDependencies": { "@babel/core": "^7.0.0" @@ -7950,13 +8039,6 @@ "integrity": "sha512-nfulgvOR6S4gt9UKCeGJOuSGBPGiFT6oQ/2UBnvTY/5aQ1PnksW72fhZkM30DzoRRv2WpwZf1vHHEr3mtuXIWQ==", "dev": true }, - "node_modules/browser-process-hrtime": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/browser-process-hrtime/-/browser-process-hrtime-1.0.0.tgz", - "integrity": "sha512-9o5UecI3GhkpM6DrXr69PblIuWxPKk9Y0jHBRhdocZ2y7YECBFCsHm79Pr3OyR2AvjhDkabFJaDJMYRazHgsow==", - "dev": true, - "license": "BSD-2-Clause" - }, "node_modules/browserslist": { "version": "4.24.4", "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.24.4.tgz", @@ -8245,9 +8327,9 @@ } }, "node_modules/cjs-module-lexer": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.4.1.tgz", - "integrity": "sha512-cuSVIHi9/9E/+821Qjdvngor+xpnlwnuwIyZOaLmHBVdXL+gP+I6QQB9VkO7RI77YIcTV+S1W9AreJ5eN63JBA==", + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.4.3.tgz", + "integrity": "sha512-9z8TZaGM1pfswYeXrUpzPrkx8UnWYdhJclsiYMm6x/w5+nN+8Tf/LnAgfLGQCm59qAOxU8WwHEq2vNwF6i4j+Q==", "dev": true, "license": "MIT" }, @@ -8264,15 +8346,18 @@ } }, "node_modules/cliui": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", - "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", "dev": true, "license": "ISC", "dependencies": { "string-width": "^4.2.0", - "strip-ansi": "^6.0.0", + "strip-ansi": "^6.0.1", "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" } }, "node_modules/clsx": { @@ -8848,6 +8933,28 @@ "node": ">= 6" } }, + "node_modules/create-jest": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/create-jest/-/create-jest-29.7.0.tgz", + "integrity": "sha512-Adz2bdH0Vq3F53KEMJOoftQFutWCukm6J24wbPWRO4k1kMY7gS7ds/uoJkNuV8wDCtWWnuwGcJwpWcih+zEW1Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "^29.6.3", + "chalk": "^4.0.0", + "exit": "^0.1.2", + "graceful-fs": "^4.2.9", + "jest-config": "^29.7.0", + "jest-util": "^29.7.0", + "prompts": "^2.0.1" + }, + "bin": { + "create-jest": "bin/create-jest.js" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, "node_modules/cross-spawn": { "version": "7.0.6", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", @@ -8913,9 +9020,9 @@ "license": "MIT" }, "node_modules/cssom": { - "version": "0.4.4", - "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.4.4.tgz", - "integrity": "sha512-p3pvU7r1MyyqbTk+WbNJIgJjG2VmTIaB10rI93LzVPrmDJKkzKYMtxxyAvQXR/NS6otuzveI7+7BBq3SjBS2mw==", + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.5.0.tgz", + "integrity": "sha512-iKuQcq+NdHqlAcwUY0o/HL69XQrUaQdMjmStJ8JFmUaiiQErlhrmuigkg/CU4E2J0IyUKUrMAgl36TvN67MqTw==", "dev": true, "license": "MIT" }, @@ -8953,18 +9060,18 @@ "license": "BSD-2-Clause" }, "node_modules/data-urls": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-2.0.0.tgz", - "integrity": "sha512-X5eWTSXO/BJmpdIKCRuKUgSCgAN0OwliVK3yPKbwIWU1Tdw5BRajxlzMidvh+gwko9AfQ9zIj52pzF91Q3YAvQ==", + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-3.0.2.tgz", + "integrity": "sha512-Jy/tj3ldjZJo63sVAvg6LHt2mHvl4V6AgRAmNDtLdm7faqtsx+aJG42rsyCo9JCoRVKwPFzKlIPx3DIibwSIaQ==", "dev": true, "license": "MIT", "dependencies": { - "abab": "^2.0.3", - "whatwg-mimetype": "^2.3.0", - "whatwg-url": "^8.0.0" + "abab": "^2.0.6", + "whatwg-mimetype": "^3.0.0", + "whatwg-url": "^11.0.0" }, "engines": { - "node": ">=10" + "node": ">=12" } }, "node_modules/data-view-buffer": { @@ -9081,11 +9188,19 @@ } }, "node_modules/dedent": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/dedent/-/dedent-0.7.0.tgz", - "integrity": "sha512-Q6fKUPqnAHAyhiUgFU7BUzLiv0kd8saH9al7tnu5Q/okj6dnupxyTgFIBjVzJATdfIAm9NAsvXNzjaKa+bxVyA==", + "version": "1.5.3", + "resolved": "https://registry.npmjs.org/dedent/-/dedent-1.5.3.tgz", + "integrity": "sha512-NHQtfOOW68WD8lgypbLA5oT+Bt0xXJhiYvoR6SmmNXZfpzOGXwdKWmcwG8N7PwVVWV3eF/68nmD9BaJSsTBhyQ==", "dev": true, - "license": "MIT" + "license": "MIT", + "peerDependencies": { + "babel-plugin-macros": "^3.1.0" + }, + "peerDependenciesMeta": { + "babel-plugin-macros": { + "optional": true + } + } }, "node_modules/deep-eql": { "version": "5.0.2", @@ -9267,27 +9382,17 @@ "peer": true }, "node_modules/domexception": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/domexception/-/domexception-2.0.1.tgz", - "integrity": "sha512-yxJ2mFy/sibVQlu5qHjOkf9J3K6zgmCxgJ94u2EdvDOV09H+32LtRswEcUsmUWN72pVLOEnTSRaIVVzVQgS0dg==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/domexception/-/domexception-4.0.0.tgz", + "integrity": "sha512-A2is4PLG+eeSfoTMA95/s4pvAoSo2mKtiM5jlHkAVewmiO8ISFTFKZjH7UAM1Atli/OT/7JHOrJRJiMKUZKYBw==", "deprecated": "Use your platform's native DOMException instead", "dev": true, "license": "MIT", "dependencies": { - "webidl-conversions": "^5.0.0" + "webidl-conversions": "^7.0.0" }, "engines": { - "node": ">=8" - } - }, - "node_modules/domexception/node_modules/webidl-conversions": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-5.0.0.tgz", - "integrity": "sha512-VlZwKPCkYKxQgeSbH5EyngOmRp7Ww7I9rQLERETtf5ofd9pGeswWiOtogpEO850jziPRarreGxn5QIiTqpb2wA==", - "dev": true, - "license": "BSD-2-Clause", - "engines": { - "node": ">=8" + "node": ">=12" } }, "node_modules/dunder-proto": { @@ -9312,6 +9417,22 @@ "dev": true, "license": "MIT" }, + "node_modules/ejs": { + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.10.tgz", + "integrity": "sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "jake": "^10.8.5" + }, + "bin": { + "ejs": "bin/cli.js" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/electron-to-chromium": { "version": "1.5.88", "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.88.tgz", @@ -9320,13 +9441,13 @@ "license": "ISC" }, "node_modules/emittery": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.8.1.tgz", - "integrity": "sha512-uDfvUjVrfGJJhymx/kz6prltenw1u7WrCg1oa94zYY8xxVpLLUu045LAT0dhDZdXG58/EpPL/5kA180fQ/qudg==", + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.13.1.tgz", + "integrity": "sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==", "dev": true, "license": "MIT", "engines": { - "node": ">=10" + "node": ">=12" }, "funding": { "url": "https://github.com/sindresorhus/emittery?sponsor=1" @@ -9359,6 +9480,19 @@ "node": ">=10.13.0" } }, + "node_modules/entities": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", + "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, "node_modules/error-ex": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", @@ -10814,43 +10948,131 @@ } }, "node_modules/expect": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/expect/-/expect-27.5.1.tgz", - "integrity": "sha512-E1q5hSUG2AmYQwQJ041nvgpkODHQvB+RKlB4IYdru6uJsyFTRyZAP463M+1lINorwbqAmUggi6+WwkD8lCS/Dw==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/expect/-/expect-29.7.0.tgz", + "integrity": "sha512-2Zks0hf1VLFYI1kbh0I5jP3KHHyCHpkfyHBzsSXRFgl/Bg9mWYfMW8oD+PdMPlEwy5HNsR9JutYy6pMeOh61nw==", "dev": true, "license": "MIT", "dependencies": { - "@jest/types": "^27.5.1", - "jest-get-type": "^27.5.1", - "jest-matcher-utils": "^27.5.1", - "jest-message-util": "^27.5.1" + "@jest/expect-utils": "^29.7.0", + "jest-get-type": "^29.6.3", + "jest-matcher-utils": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-util": "^29.7.0" }, "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/extend": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", - "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", - "license": "MIT" - }, - "node_modules/fast-deep-equal": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", - "license": "MIT" - }, - "node_modules/fast-glob": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", - "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", + "node_modules/expect/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", "dev": true, "license": "MIT", - "dependencies": { - "@nodelib/fs.stat": "^2.0.2", - "@nodelib/fs.walk": "^1.2.3", - "glob-parent": "^5.1.2", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/expect/node_modules/diff-sequences": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.6.3.tgz", + "integrity": "sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/expect/node_modules/jest-diff": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-29.7.0.tgz", + "integrity": "sha512-LMIgiIrhigmPrs03JHpxUh2yISK3vLFPkAodPeo0+BuF7wA2FoQbkEg1u8gBYBThncu7e1oEDUfIXVuTqLRUjw==", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^4.0.0", + "diff-sequences": "^29.6.3", + "jest-get-type": "^29.6.3", + "pretty-format": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/expect/node_modules/jest-get-type": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-29.6.3.tgz", + "integrity": "sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/expect/node_modules/jest-matcher-utils": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-29.7.0.tgz", + "integrity": "sha512-sBkD+Xi9DtcChsI3L3u0+N0opgPYnCRPtGcQYrgXmR+hmt/fYfWAL0xRXYU8eWOdfuLgBe0YCW3AFtnRLagq/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^4.0.0", + "jest-diff": "^29.7.0", + "jest-get-type": "^29.6.3", + "pretty-format": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/expect/node_modules/pretty-format": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", + "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/schemas": "^29.6.3", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/expect/node_modules/react-is": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", + "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", + "dev": true, + "license": "MIT" + }, + "node_modules/extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", + "license": "MIT" + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "license": "MIT" + }, + "node_modules/fast-glob": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", + "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", "merge2": "^1.3.0", "micromatch": "^4.0.8" }, @@ -10929,6 +11151,39 @@ "node": "^10.12.0 || >=12.0.0" } }, + "node_modules/filelist": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/filelist/-/filelist-1.0.4.tgz", + "integrity": "sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "minimatch": "^5.0.1" + } + }, + "node_modules/filelist/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/filelist/node_modules/minimatch": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", + "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/fill-range": { "version": "7.1.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", @@ -11028,14 +11283,15 @@ } }, "node_modules/form-data": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.2.tgz", - "integrity": "sha512-sJe+TQb2vIaIyO783qN6BlMYWMw3WBOHA1Ay2qxsnjuafEOQFJ2JakedOQirT6D5XPRxDvS7AHYyem9fTpb4LQ==", + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.2.tgz", + "integrity": "sha512-hGfm/slu0ZabnNt4oaRZ6uREyfCj6P4fT/n6A1rGV+Z0VdGXjfOhVUpkn6qVQONHGIFwmveGXyDs75+nr6FM8w==", "dev": true, "license": "MIT", "dependencies": { "asynckit": "^0.4.0", "combined-stream": "^1.0.8", + "es-set-tostringtag": "^2.1.0", "mime-types": "^2.1.12" }, "engines": { @@ -11694,16 +11950,16 @@ } }, "node_modules/html-encoding-sniffer": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-2.0.1.tgz", - "integrity": "sha512-D5JbOMBIR/TVZkubHT+OyT2705QvogUW4IBn6nHd756OwieSF9aDYFj4dv6HHEVGYbHaLETa3WggZYWWMyy3ZQ==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-3.0.0.tgz", + "integrity": "sha512-oWv4T4yJ52iKrufjnyZPkrN0CH3QnrUqdB6In1g5Fe1mia8GmF36gnfNySxoZtxD5+NmYw1EElVXiBk93UeskA==", "dev": true, "license": "MIT", "dependencies": { - "whatwg-encoding": "^1.0.5" + "whatwg-encoding": "^2.0.0" }, "engines": { - "node": ">=10" + "node": ">=12" } }, "node_modules/html-escaper": { @@ -11735,13 +11991,13 @@ } }, "node_modules/http-proxy-agent": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz", - "integrity": "sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz", + "integrity": "sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w==", "dev": true, "license": "MIT", "dependencies": { - "@tootallnate/once": "1", + "@tootallnate/once": "2", "agent-base": "6", "debug": "4" }, @@ -11780,13 +12036,13 @@ "license": "BSD-3-Clause" }, "node_modules/iconv-lite": { - "version": "0.4.24", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", - "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", "dev": true, "license": "MIT", "dependencies": { - "safer-buffer": ">= 2.1.2 < 3" + "safer-buffer": ">= 2.1.2 < 3.0.0" }, "engines": { "node": ">=0.10.0" @@ -12419,13 +12675,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/is-typedarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", - "integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==", - "dev": true, - "license": "MIT" - }, "node_modules/is-weakmap": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.2.tgz", @@ -12510,20 +12759,33 @@ } }, "node_modules/istanbul-lib-instrument": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.1.tgz", - "integrity": "sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg==", + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-6.0.3.tgz", + "integrity": "sha512-Vtgk7L/R2JHyyGW07spoFlB8/lpjiOLTjMdms6AFMraYt3BaJauod/NGrfnVG/y4Ix1JEuMRPDPEj2ua+zz1/Q==", "dev": true, "license": "BSD-3-Clause", "dependencies": { - "@babel/core": "^7.12.3", - "@babel/parser": "^7.14.7", - "@istanbuljs/schema": "^0.1.2", + "@babel/core": "^7.23.9", + "@babel/parser": "^7.23.9", + "@istanbuljs/schema": "^0.1.3", "istanbul-lib-coverage": "^3.2.0", - "semver": "^6.3.0" + "semver": "^7.5.4" }, "engines": { - "node": ">=8" + "node": ">=10" + } + }, + "node_modules/istanbul-lib-instrument/node_modules/semver": { + "version": "7.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz", + "integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" } }, "node_modules/istanbul-lib-report": { @@ -12604,22 +12866,42 @@ "@pkgjs/parseargs": "^0.11.0" } }, + "node_modules/jake": { + "version": "10.9.2", + "resolved": "https://registry.npmjs.org/jake/-/jake-10.9.2.tgz", + "integrity": "sha512-2P4SQ0HrLQ+fw6llpLnOaGAvN2Zu6778SJMrCUwns4fOoG9ayrTiZk3VV8sCPkVZF8ab0zksVpS8FDY5pRCNBA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "async": "^3.2.3", + "chalk": "^4.0.2", + "filelist": "^1.0.4", + "minimatch": "^3.1.2" + }, + "bin": { + "jake": "bin/cli.js" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/jest": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest/-/jest-27.5.1.tgz", - "integrity": "sha512-Yn0mADZB89zTtjkPJEXwrac3LHudkQMR+Paqa8uxJHCBr9agxztUifWCyiYrjhMPBoUVBjyny0I7XH6ozDr7QQ==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest/-/jest-29.7.0.tgz", + "integrity": "sha512-NIy3oAFp9shda19hy4HK0HRTWKtPJmGdnvywu01nOqNC2vZg+Z+fvJDxpMQA88eb2I9EcafcdjYgsDthnYTvGw==", "dev": true, "license": "MIT", "dependencies": { - "@jest/core": "^27.5.1", + "@jest/core": "^29.7.0", + "@jest/types": "^29.6.3", "import-local": "^3.0.2", - "jest-cli": "^27.5.1" + "jest-cli": "^29.7.0" }, "bin": { "jest": "bin/jest.js" }, "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" }, "peerDependencies": { "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" @@ -12631,76 +12913,163 @@ } }, "node_modules/jest-changed-files": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-27.5.1.tgz", - "integrity": "sha512-buBLMiByfWGCoMsLLzGUUSpAmIAGnbR2KJoMN10ziLhOLvP4e0SlypHnAel8iqQXTrcbmfEY9sSqae5sgUsTvw==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-29.7.0.tgz", + "integrity": "sha512-fEArFiwf1BpQ+4bXSprcDc3/x4HSzL4al2tozwVpDFpsxALjLYdyiIK4e5Vz66GQJIbXJ82+35PtysofptNX2w==", "dev": true, "license": "MIT", "dependencies": { - "@jest/types": "^27.5.1", "execa": "^5.0.0", - "throat": "^6.0.1" + "jest-util": "^29.7.0", + "p-limit": "^3.1.0" }, "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, "node_modules/jest-circus": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-27.5.1.tgz", - "integrity": "sha512-D95R7x5UtlMA5iBYsOHFFbMD/GVA4R/Kdq15f7xYWUfWHBto9NYRsOvnSauTgdF+ogCpJ4tyKOXhUifxS65gdw==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-29.7.0.tgz", + "integrity": "sha512-3E1nCMgipcTkCocFwM90XXQab9bS+GMsjdpmPrlelaxwD93Ad8iVEjX/vvHPdLPnFf+L40u+5+iutRdA1N9myw==", "dev": true, "license": "MIT", "dependencies": { - "@jest/environment": "^27.5.1", - "@jest/test-result": "^27.5.1", - "@jest/types": "^27.5.1", + "@jest/environment": "^29.7.0", + "@jest/expect": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/types": "^29.6.3", "@types/node": "*", "chalk": "^4.0.0", "co": "^4.6.0", - "dedent": "^0.7.0", - "expect": "^27.5.1", + "dedent": "^1.0.0", "is-generator-fn": "^2.0.0", - "jest-each": "^27.5.1", - "jest-matcher-utils": "^27.5.1", - "jest-message-util": "^27.5.1", - "jest-runtime": "^27.5.1", - "jest-snapshot": "^27.5.1", - "jest-util": "^27.5.1", - "pretty-format": "^27.5.1", + "jest-each": "^29.7.0", + "jest-matcher-utils": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-runtime": "^29.7.0", + "jest-snapshot": "^29.7.0", + "jest-util": "^29.7.0", + "p-limit": "^3.1.0", + "pretty-format": "^29.7.0", + "pure-rand": "^6.0.0", "slash": "^3.0.0", - "stack-utils": "^2.0.3", - "throat": "^6.0.1" + "stack-utils": "^2.0.3" }, "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, + "node_modules/jest-circus/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/jest-circus/node_modules/diff-sequences": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.6.3.tgz", + "integrity": "sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-circus/node_modules/jest-diff": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-29.7.0.tgz", + "integrity": "sha512-LMIgiIrhigmPrs03JHpxUh2yISK3vLFPkAodPeo0+BuF7wA2FoQbkEg1u8gBYBThncu7e1oEDUfIXVuTqLRUjw==", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^4.0.0", + "diff-sequences": "^29.6.3", + "jest-get-type": "^29.6.3", + "pretty-format": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-circus/node_modules/jest-get-type": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-29.6.3.tgz", + "integrity": "sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-circus/node_modules/jest-matcher-utils": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-29.7.0.tgz", + "integrity": "sha512-sBkD+Xi9DtcChsI3L3u0+N0opgPYnCRPtGcQYrgXmR+hmt/fYfWAL0xRXYU8eWOdfuLgBe0YCW3AFtnRLagq/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^4.0.0", + "jest-diff": "^29.7.0", + "jest-get-type": "^29.6.3", + "pretty-format": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-circus/node_modules/pretty-format": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", + "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/schemas": "^29.6.3", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-circus/node_modules/react-is": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", + "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", + "dev": true, + "license": "MIT" + }, "node_modules/jest-cli": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-27.5.1.tgz", - "integrity": "sha512-Hc6HOOwYq4/74/c62dEE3r5elx8wjYqxY0r0G/nFrLDPMFRu6RA/u8qINOIkvhxG7mMQ5EJsOGfRpI8L6eFUVw==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-29.7.0.tgz", + "integrity": "sha512-OVVobw2IubN/GSYsxETi+gOe7Ka59EFMR/twOU3Jb2GnKKeMGJB5SGUUrEz3SFVmJASUdZUzy83sLNNQ2gZslg==", "dev": true, "license": "MIT", "dependencies": { - "@jest/core": "^27.5.1", - "@jest/test-result": "^27.5.1", - "@jest/types": "^27.5.1", + "@jest/core": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/types": "^29.6.3", "chalk": "^4.0.0", + "create-jest": "^29.7.0", "exit": "^0.1.2", - "graceful-fs": "^4.2.9", "import-local": "^3.0.2", - "jest-config": "^27.5.1", - "jest-util": "^27.5.1", - "jest-validate": "^27.5.1", - "prompts": "^2.0.1", - "yargs": "^16.2.0" + "jest-config": "^29.7.0", + "jest-util": "^29.7.0", + "jest-validate": "^29.7.0", + "yargs": "^17.3.1" }, "bin": { "jest": "bin/jest.js" }, "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" }, "peerDependencies": { "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" @@ -12712,49 +13081,96 @@ } }, "node_modules/jest-config": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-27.5.1.tgz", - "integrity": "sha512-5sAsjm6tGdsVbW9ahcChPAFCk4IlkQUknH5AvKjuLTSlcO/wCZKyFdn7Rg0EkC+OGgWODEy2hDpWB1PgzH0JNA==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-29.7.0.tgz", + "integrity": "sha512-uXbpfeQ7R6TZBqI3/TxCU4q4ttk3u0PJeC+E0zbfSoSjq6bJ7buBPxzQPL0ifrkY4DNu4JUdk0ImlBUYi840eQ==", "dev": true, "license": "MIT", "dependencies": { - "@babel/core": "^7.8.0", - "@jest/test-sequencer": "^27.5.1", - "@jest/types": "^27.5.1", - "babel-jest": "^27.5.1", + "@babel/core": "^7.11.6", + "@jest/test-sequencer": "^29.7.0", + "@jest/types": "^29.6.3", + "babel-jest": "^29.7.0", "chalk": "^4.0.0", "ci-info": "^3.2.0", "deepmerge": "^4.2.2", - "glob": "^7.1.1", + "glob": "^7.1.3", "graceful-fs": "^4.2.9", - "jest-circus": "^27.5.1", - "jest-environment-jsdom": "^27.5.1", - "jest-environment-node": "^27.5.1", - "jest-get-type": "^27.5.1", - "jest-jasmine2": "^27.5.1", - "jest-regex-util": "^27.5.1", - "jest-resolve": "^27.5.1", - "jest-runner": "^27.5.1", - "jest-util": "^27.5.1", - "jest-validate": "^27.5.1", + "jest-circus": "^29.7.0", + "jest-environment-node": "^29.7.0", + "jest-get-type": "^29.6.3", + "jest-regex-util": "^29.6.3", + "jest-resolve": "^29.7.0", + "jest-runner": "^29.7.0", + "jest-util": "^29.7.0", + "jest-validate": "^29.7.0", "micromatch": "^4.0.4", "parse-json": "^5.2.0", - "pretty-format": "^27.5.1", + "pretty-format": "^29.7.0", "slash": "^3.0.0", "strip-json-comments": "^3.1.1" }, "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" }, "peerDependencies": { + "@types/node": "*", "ts-node": ">=9.0.0" }, "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, "ts-node": { "optional": true } } }, + "node_modules/jest-config/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/jest-config/node_modules/jest-get-type": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-29.6.3.tgz", + "integrity": "sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-config/node_modules/pretty-format": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", + "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/schemas": "^29.6.3", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-config/node_modules/react-is": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", + "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", + "dev": true, + "license": "MIT" + }, "node_modules/jest-diff": { "version": "27.5.1", "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-27.5.1.tgz", @@ -12772,70 +13188,124 @@ } }, "node_modules/jest-docblock": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-27.5.1.tgz", - "integrity": "sha512-rl7hlABeTsRYxKiUfpHrQrG4e2obOiTQWfMEH3PxPjOtdsfLQO4ReWSZaQ7DETm4xu07rl4q/h4zcKXyU0/OzQ==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-29.7.0.tgz", + "integrity": "sha512-q617Auw3A612guyaFgsbFeYpNP5t2aoUNLwBUbc/0kD1R4t9ixDbyFTHd1nok4epoVFpr7PmeWHrhvuV3XaJ4g==", "dev": true, "license": "MIT", "dependencies": { "detect-newline": "^3.0.0" }, "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, "node_modules/jest-each": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-27.5.1.tgz", - "integrity": "sha512-1Ff6p+FbhT/bXQnEouYy00bkNSY7OUpfIcmdl8vZ31A1UUaurOLPA8a8BbJOF2RDUElwJhmeaV7LnagI+5UwNQ==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-29.7.0.tgz", + "integrity": "sha512-gns+Er14+ZrEoC5fhOfYCY1LOHHr0TI+rQUHZS8Ttw2l7gl+80eHc/gFf2Ktkw0+SIACDTeWvpFcv3B04VembQ==", "dev": true, "license": "MIT", "dependencies": { - "@jest/types": "^27.5.1", + "@jest/types": "^29.6.3", "chalk": "^4.0.0", - "jest-get-type": "^27.5.1", - "jest-util": "^27.5.1", - "pretty-format": "^27.5.1" + "jest-get-type": "^29.6.3", + "jest-util": "^29.7.0", + "pretty-format": "^29.7.0" }, "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-each/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/jest-each/node_modules/jest-get-type": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-29.6.3.tgz", + "integrity": "sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-each/node_modules/pretty-format": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", + "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/schemas": "^29.6.3", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, + "node_modules/jest-each/node_modules/react-is": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", + "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", + "dev": true, + "license": "MIT" + }, "node_modules/jest-environment-jsdom": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-environment-jsdom/-/jest-environment-jsdom-27.5.1.tgz", - "integrity": "sha512-TFBvkTC1Hnnnrka/fUb56atfDtJ9VMZ94JkjTbggl1PEpwrYtUBKMezB3inLmWqQsXYLcMwNoDQwoBTAvFfsfw==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-environment-jsdom/-/jest-environment-jsdom-29.7.0.tgz", + "integrity": "sha512-k9iQbsf9OyOfdzWH8HDmrRT0gSIcX+FLNW7IQq94tFX0gynPwqDTW0Ho6iMVNjGz/nb+l/vW3dWM2bbLLpkbXA==", "dev": true, "license": "MIT", "dependencies": { - "@jest/environment": "^27.5.1", - "@jest/fake-timers": "^27.5.1", - "@jest/types": "^27.5.1", + "@jest/environment": "^29.7.0", + "@jest/fake-timers": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/jsdom": "^20.0.0", "@types/node": "*", - "jest-mock": "^27.5.1", - "jest-util": "^27.5.1", - "jsdom": "^16.6.0" + "jest-mock": "^29.7.0", + "jest-util": "^29.7.0", + "jsdom": "^20.0.0" }, "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "canvas": "^2.5.0" + }, + "peerDependenciesMeta": { + "canvas": { + "optional": true + } } }, "node_modules/jest-environment-node": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-27.5.1.tgz", - "integrity": "sha512-Jt4ZUnxdOsTGwSRAfKEnE6BcwsSPNOijjwifq5sDFSA2kesnXTvNqKHYgM0hDq3549Uf/KzdXNYn4wMZJPlFLw==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-29.7.0.tgz", + "integrity": "sha512-DOSwCRqXirTOyheM+4d5YZOrWcdu0LNZ87ewUoywbcb2XR4wKgqiG8vNeYwhjFMbEkfju7wx2GYH0P2gevGvFw==", "dev": true, "license": "MIT", "dependencies": { - "@jest/environment": "^27.5.1", - "@jest/fake-timers": "^27.5.1", - "@jest/types": "^27.5.1", + "@jest/environment": "^29.7.0", + "@jest/fake-timers": "^29.7.0", + "@jest/types": "^29.6.3", "@types/node": "*", - "jest-mock": "^27.5.1", - "jest-util": "^27.5.1" + "jest-mock": "^29.7.0", + "jest-util": "^29.7.0" }, "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, "node_modules/jest-get-type": { @@ -12849,75 +13319,90 @@ } }, "node_modules/jest-haste-map": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-27.5.1.tgz", - "integrity": "sha512-7GgkZ4Fw4NFbMSDSpZwXeBiIbx+t/46nJ2QitkOjvwPYyZmqttu2TDSimMHP1EkPOi4xUZAN1doE5Vd25H4Jng==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-29.7.0.tgz", + "integrity": "sha512-fP8u2pyfqx0K1rGn1R9pyE0/KTn+G7PxktWidOBTqFPLYX0b9ksaMFkhK5vrS3DVun09pckLdlx90QthlW7AmA==", "dev": true, "license": "MIT", "dependencies": { - "@jest/types": "^27.5.1", - "@types/graceful-fs": "^4.1.2", + "@jest/types": "^29.6.3", + "@types/graceful-fs": "^4.1.3", "@types/node": "*", "anymatch": "^3.0.3", "fb-watchman": "^2.0.0", "graceful-fs": "^4.2.9", - "jest-regex-util": "^27.5.1", - "jest-serializer": "^27.5.1", - "jest-util": "^27.5.1", - "jest-worker": "^27.5.1", + "jest-regex-util": "^29.6.3", + "jest-util": "^29.7.0", + "jest-worker": "^29.7.0", "micromatch": "^4.0.4", - "walker": "^1.0.7" + "walker": "^1.0.8" }, "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" }, "optionalDependencies": { "fsevents": "^2.3.2" } }, - "node_modules/jest-jasmine2": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-jasmine2/-/jest-jasmine2-27.5.1.tgz", - "integrity": "sha512-jtq7VVyG8SqAorDpApwiJJImd0V2wv1xzdheGHRGyuT7gZm6gG47QEskOlzsN1PG/6WNaCo5pmwMHDf3AkG2pQ==", + "node_modules/jest-leak-detector": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-29.7.0.tgz", + "integrity": "sha512-kYA8IJcSYtST2BY9I+SMC32nDpBT3J2NvWJx8+JCuCdl/CR1I4EKUJROiP8XtCcxqgTTBGJNdbB1A8XRKbTetw==", "dev": true, "license": "MIT", "dependencies": { - "@jest/environment": "^27.5.1", - "@jest/source-map": "^27.5.1", - "@jest/test-result": "^27.5.1", - "@jest/types": "^27.5.1", - "@types/node": "*", - "chalk": "^4.0.0", - "co": "^4.6.0", - "expect": "^27.5.1", - "is-generator-fn": "^2.0.0", - "jest-each": "^27.5.1", - "jest-matcher-utils": "^27.5.1", - "jest-message-util": "^27.5.1", - "jest-runtime": "^27.5.1", - "jest-snapshot": "^27.5.1", - "jest-util": "^27.5.1", - "pretty-format": "^27.5.1", - "throat": "^6.0.1" + "jest-get-type": "^29.6.3", + "pretty-format": "^29.7.0" }, "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/jest-leak-detector": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-27.5.1.tgz", - "integrity": "sha512-POXfWAMvfU6WMUXftV4HolnJfnPOGEu10fscNCA76KBpRRhcMN2c8d3iT2pxQS3HLbA+5X4sOUPzYO2NUyIlHQ==", + "node_modules/jest-leak-detector/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/jest-leak-detector/node_modules/jest-get-type": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-29.6.3.tgz", + "integrity": "sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-leak-detector/node_modules/pretty-format": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", + "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", "dev": true, "license": "MIT", "dependencies": { - "jest-get-type": "^27.5.1", - "pretty-format": "^27.5.1" + "@jest/schemas": "^29.6.3", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" }, "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, + "node_modules/jest-leak-detector/node_modules/react-is": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", + "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", + "dev": true, + "license": "MIT" + }, "node_modules/jest-matcher-utils": { "version": "27.5.1", "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-27.5.1.tgz", @@ -12935,38 +13420,74 @@ } }, "node_modules/jest-message-util": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-27.5.1.tgz", - "integrity": "sha512-rMyFe1+jnyAAf+NHwTclDz0eAaLkVDdKVHHBFWsBWHnnh5YeJMNWWsv7AbFYXfK3oTqvL7VTWkhNLu1jX24D+g==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-29.7.0.tgz", + "integrity": "sha512-GBEV4GRADeP+qtB2+6u61stea8mGcOT4mCtrYISZwfu9/ISHFJ/5zOMXYbpBE9RsS5+Gb63DW4FgmnKJ79Kf6w==", "dev": true, "license": "MIT", "dependencies": { "@babel/code-frame": "^7.12.13", - "@jest/types": "^27.5.1", + "@jest/types": "^29.6.3", "@types/stack-utils": "^2.0.0", "chalk": "^4.0.0", "graceful-fs": "^4.2.9", "micromatch": "^4.0.4", - "pretty-format": "^27.5.1", + "pretty-format": "^29.7.0", "slash": "^3.0.0", "stack-utils": "^2.0.3" }, "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, + "node_modules/jest-message-util/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/jest-message-util/node_modules/pretty-format": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", + "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/schemas": "^29.6.3", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-message-util/node_modules/react-is": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", + "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", + "dev": true, + "license": "MIT" + }, "node_modules/jest-mock": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-27.5.1.tgz", - "integrity": "sha512-K4jKbY1d4ENhbrG2zuPWaQBvDly+iZ2yAW+T1fATN78hc0sInwn7wZB8XtlNnvHug5RMwV897Xm4LqmPM4e2Og==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-29.7.0.tgz", + "integrity": "sha512-ITOMZn+UkYS4ZFh83xYAOzWStloNzJFO2s8DWrE4lhtGD+AorgnbkiKERe4wQVBydIGPx059g6riW5Btp6Llnw==", "dev": true, "license": "MIT", "dependencies": { - "@jest/types": "^27.5.1", - "@types/node": "*" + "@jest/types": "^29.6.3", + "@types/node": "*", + "jest-util": "^29.7.0" }, "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, "node_modules/jest-pnp-resolver": { @@ -12988,131 +13509,115 @@ } }, "node_modules/jest-regex-util": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-27.5.1.tgz", - "integrity": "sha512-4bfKq2zie+x16okqDXjXn9ql2B0dScQu+vcwe4TvFVhkVyuWLqpZrZtXxLLWoXYgn0E87I6r6GRYHF7wFZBUvg==", + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-29.6.3.tgz", + "integrity": "sha512-KJJBsRCyyLNWCNBOvZyRDnAIfUiRJ8v+hOBQYGn8gDyF3UegwiP4gwRR3/SDa42g1YbVycTidUF3rKjyLFDWbg==", "dev": true, "license": "MIT", "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, "node_modules/jest-resolve": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-27.5.1.tgz", - "integrity": "sha512-FFDy8/9E6CV83IMbDpcjOhumAQPDyETnU2KZ1O98DwTnz8AOBsW/Xv3GySr1mOZdItLR+zDZ7I/UdTFbgSOVCw==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-29.7.0.tgz", + "integrity": "sha512-IOVhZSrg+UvVAshDSDtHyFCCBUl/Q3AAJv8iZ6ZjnZ74xzvwuzLXid9IIIPgTnY62SJjfuupMKZsZQRsCvxEgA==", "dev": true, "license": "MIT", "dependencies": { - "@jest/types": "^27.5.1", "chalk": "^4.0.0", "graceful-fs": "^4.2.9", - "jest-haste-map": "^27.5.1", + "jest-haste-map": "^29.7.0", "jest-pnp-resolver": "^1.2.2", - "jest-util": "^27.5.1", - "jest-validate": "^27.5.1", + "jest-util": "^29.7.0", + "jest-validate": "^29.7.0", "resolve": "^1.20.0", - "resolve.exports": "^1.1.0", + "resolve.exports": "^2.0.0", "slash": "^3.0.0" }, "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, "node_modules/jest-resolve-dependencies": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-27.5.1.tgz", - "integrity": "sha512-QQOOdY4PE39iawDn5rzbIePNigfe5B9Z91GDD1ae/xNDlu9kaat8QQ5EKnNmVWPV54hUdxCVwwj6YMgR2O7IOg==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-29.7.0.tgz", + "integrity": "sha512-un0zD/6qxJ+S0et7WxeI3H5XSe9lTBBR7bOHCHXkKR6luG5mwDDlIzVQ0V5cZCuoTgEdcdwzTghYkTWfubi+nA==", "dev": true, "license": "MIT", "dependencies": { - "@jest/types": "^27.5.1", - "jest-regex-util": "^27.5.1", - "jest-snapshot": "^27.5.1" + "jest-regex-util": "^29.6.3", + "jest-snapshot": "^29.7.0" }, "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, "node_modules/jest-runner": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-27.5.1.tgz", - "integrity": "sha512-g4NPsM4mFCOwFKXO4p/H/kWGdJp9V8kURY2lX8Me2drgXqG7rrZAx5kv+5H7wtt/cdFIjhqYx1HrlqWHaOvDaQ==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-29.7.0.tgz", + "integrity": "sha512-fsc4N6cPCAahybGBfTRcq5wFR6fpLznMg47sY5aDpsoejOcVYFb07AHuSnR0liMcPTgBsA3ZJL6kFOjPdoNipQ==", "dev": true, "license": "MIT", "dependencies": { - "@jest/console": "^27.5.1", - "@jest/environment": "^27.5.1", - "@jest/test-result": "^27.5.1", - "@jest/transform": "^27.5.1", - "@jest/types": "^27.5.1", + "@jest/console": "^29.7.0", + "@jest/environment": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", "@types/node": "*", "chalk": "^4.0.0", - "emittery": "^0.8.1", + "emittery": "^0.13.1", "graceful-fs": "^4.2.9", - "jest-docblock": "^27.5.1", - "jest-environment-jsdom": "^27.5.1", - "jest-environment-node": "^27.5.1", - "jest-haste-map": "^27.5.1", - "jest-leak-detector": "^27.5.1", - "jest-message-util": "^27.5.1", - "jest-resolve": "^27.5.1", - "jest-runtime": "^27.5.1", - "jest-util": "^27.5.1", - "jest-worker": "^27.5.1", - "source-map-support": "^0.5.6", - "throat": "^6.0.1" + "jest-docblock": "^29.7.0", + "jest-environment-node": "^29.7.0", + "jest-haste-map": "^29.7.0", + "jest-leak-detector": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-resolve": "^29.7.0", + "jest-runtime": "^29.7.0", + "jest-util": "^29.7.0", + "jest-watcher": "^29.7.0", + "jest-worker": "^29.7.0", + "p-limit": "^3.1.0", + "source-map-support": "0.5.13" }, "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, "node_modules/jest-runtime": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-27.5.1.tgz", - "integrity": "sha512-o7gxw3Gf+H2IGt8fv0RiyE1+r83FJBRruoA+FXrlHw6xEyBsU8ugA6IPfTdVyA0w8HClpbK+DGJxH59UrNMx8A==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-29.7.0.tgz", + "integrity": "sha512-gUnLjgwdGqW7B4LvOIkbKs9WGbn+QLqRQQ9juC6HndeDiezIwhDP+mhMwHWCEcfQ5RUXa6OPnFF8BJh5xegwwQ==", "dev": true, "license": "MIT", "dependencies": { - "@jest/environment": "^27.5.1", - "@jest/fake-timers": "^27.5.1", - "@jest/globals": "^27.5.1", - "@jest/source-map": "^27.5.1", - "@jest/test-result": "^27.5.1", - "@jest/transform": "^27.5.1", - "@jest/types": "^27.5.1", + "@jest/environment": "^29.7.0", + "@jest/fake-timers": "^29.7.0", + "@jest/globals": "^29.7.0", + "@jest/source-map": "^29.6.3", + "@jest/test-result": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", "chalk": "^4.0.0", "cjs-module-lexer": "^1.0.0", "collect-v8-coverage": "^1.0.0", - "execa": "^5.0.0", "glob": "^7.1.3", "graceful-fs": "^4.2.9", - "jest-haste-map": "^27.5.1", - "jest-message-util": "^27.5.1", - "jest-mock": "^27.5.1", - "jest-regex-util": "^27.5.1", - "jest-resolve": "^27.5.1", - "jest-snapshot": "^27.5.1", - "jest-util": "^27.5.1", + "jest-haste-map": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-mock": "^29.7.0", + "jest-regex-util": "^29.6.3", + "jest-resolve": "^29.7.0", + "jest-snapshot": "^29.7.0", + "jest-util": "^29.7.0", "slash": "^3.0.0", "strip-bom": "^4.0.0" }, "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-serializer": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-serializer/-/jest-serializer-27.5.1.tgz", - "integrity": "sha512-jZCyo6iIxO1aqUxpuBlwTDMkzOAJS4a3eYz3YzgxxVQFwLeSA7Jfq5cbqCY+JLvTDrWirgusI/0KwxKMgrdf7w==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/node": "*", - "graceful-fs": "^4.2.9" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, "node_modules/jest-simple-dot-reporter": { @@ -13123,43 +13628,128 @@ "license": "MIT" }, "node_modules/jest-snapshot": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-27.5.1.tgz", - "integrity": "sha512-yYykXI5a0I31xX67mgeLw1DZ0bJB+gpq5IpSuCAoyDi0+BhgU/RIrL+RTzDmkNTchvDFWKP8lp+w/42Z3us5sA==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-29.7.0.tgz", + "integrity": "sha512-Rm0BMWtxBcioHr1/OX5YCP8Uov4riHvKPknOGs804Zg9JGZgmIBkbtlxJC/7Z4msKYVbIJtfU+tKb8xlYNfdkw==", "dev": true, "license": "MIT", "dependencies": { - "@babel/core": "^7.7.2", + "@babel/core": "^7.11.6", "@babel/generator": "^7.7.2", + "@babel/plugin-syntax-jsx": "^7.7.2", "@babel/plugin-syntax-typescript": "^7.7.2", - "@babel/traverse": "^7.7.2", - "@babel/types": "^7.0.0", - "@jest/transform": "^27.5.1", - "@jest/types": "^27.5.1", - "@types/babel__traverse": "^7.0.4", - "@types/prettier": "^2.1.5", + "@babel/types": "^7.3.3", + "@jest/expect-utils": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", "babel-preset-current-node-syntax": "^1.0.0", "chalk": "^4.0.0", - "expect": "^27.5.1", + "expect": "^29.7.0", "graceful-fs": "^4.2.9", - "jest-diff": "^27.5.1", - "jest-get-type": "^27.5.1", - "jest-haste-map": "^27.5.1", - "jest-matcher-utils": "^27.5.1", - "jest-message-util": "^27.5.1", - "jest-util": "^27.5.1", + "jest-diff": "^29.7.0", + "jest-get-type": "^29.6.3", + "jest-matcher-utils": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-util": "^29.7.0", "natural-compare": "^1.4.0", - "pretty-format": "^27.5.1", - "semver": "^7.3.2" + "pretty-format": "^29.7.0", + "semver": "^7.5.3" }, "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-snapshot/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/jest-snapshot/node_modules/diff-sequences": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.6.3.tgz", + "integrity": "sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-snapshot/node_modules/jest-diff": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-29.7.0.tgz", + "integrity": "sha512-LMIgiIrhigmPrs03JHpxUh2yISK3vLFPkAodPeo0+BuF7wA2FoQbkEg1u8gBYBThncu7e1oEDUfIXVuTqLRUjw==", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^4.0.0", + "diff-sequences": "^29.6.3", + "jest-get-type": "^29.6.3", + "pretty-format": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-snapshot/node_modules/jest-get-type": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-29.6.3.tgz", + "integrity": "sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-snapshot/node_modules/jest-matcher-utils": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-29.7.0.tgz", + "integrity": "sha512-sBkD+Xi9DtcChsI3L3u0+N0opgPYnCRPtGcQYrgXmR+hmt/fYfWAL0xRXYU8eWOdfuLgBe0YCW3AFtnRLagq/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^4.0.0", + "jest-diff": "^29.7.0", + "jest-get-type": "^29.6.3", + "pretty-format": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-snapshot/node_modules/pretty-format": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", + "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/schemas": "^29.6.3", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, + "node_modules/jest-snapshot/node_modules/react-is": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", + "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", + "dev": true, + "license": "MIT" + }, "node_modules/jest-snapshot/node_modules/semver": { - "version": "7.6.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", - "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", + "version": "7.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz", + "integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==", "dev": true, "license": "ISC", "bin": { @@ -13170,13 +13760,13 @@ } }, "node_modules/jest-util": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-27.5.1.tgz", - "integrity": "sha512-Kv2o/8jNvX1MQ0KGtw480E/w4fBCDOnH6+6DmeKi6LZUIlKA5kwY0YNdlzaWTiVgxqAqik11QyxDOKk543aKXw==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.7.0.tgz", + "integrity": "sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==", "dev": true, "license": "MIT", "dependencies": { - "@jest/types": "^27.5.1", + "@jest/types": "^29.6.3", "@types/node": "*", "chalk": "^4.0.0", "ci-info": "^3.2.0", @@ -13184,25 +13774,38 @@ "picomatch": "^2.2.3" }, "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, "node_modules/jest-validate": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-27.5.1.tgz", - "integrity": "sha512-thkNli0LYTmOI1tDB3FI1S1RTp/Bqyd9pTarJwL87OIBFuqEb5Apv5EaApEudYg4g86e3CT6kM0RowkhtEnCBQ==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-29.7.0.tgz", + "integrity": "sha512-ZB7wHqaRGVw/9hST/OuFUReG7M8vKeq0/J2egIGLdvjHCmYqGARhzXmtgi+gVeZ5uXFF219aOc3Ls2yLg27tkw==", "dev": true, "license": "MIT", "dependencies": { - "@jest/types": "^27.5.1", + "@jest/types": "^29.6.3", "camelcase": "^6.2.0", "chalk": "^4.0.0", - "jest-get-type": "^27.5.1", + "jest-get-type": "^29.6.3", "leven": "^3.1.0", - "pretty-format": "^27.5.1" + "pretty-format": "^29.7.0" }, "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-validate/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, "node_modules/jest-validate/node_modules/camelcase": { @@ -13218,38 +13821,72 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/jest-validate/node_modules/jest-get-type": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-29.6.3.tgz", + "integrity": "sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-validate/node_modules/pretty-format": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", + "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/schemas": "^29.6.3", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-validate/node_modules/react-is": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", + "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", + "dev": true, + "license": "MIT" + }, "node_modules/jest-watcher": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-27.5.1.tgz", - "integrity": "sha512-z676SuD6Z8o8qbmEGhoEUFOM1+jfEiL3DXHK/xgEiG2EyNYfFG60jluWcupY6dATjfEsKQuibReS1djInQnoVw==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-29.7.0.tgz", + "integrity": "sha512-49Fg7WXkU3Vl2h6LbLtMQ/HyB6rXSIX7SqvBLQmssRBGN9I0PNvPmAmCWSOY6SOvrjhI/F7/bGAv9RtnsPA03g==", "dev": true, "license": "MIT", "dependencies": { - "@jest/test-result": "^27.5.1", - "@jest/types": "^27.5.1", + "@jest/test-result": "^29.7.0", + "@jest/types": "^29.6.3", "@types/node": "*", "ansi-escapes": "^4.2.1", "chalk": "^4.0.0", - "jest-util": "^27.5.1", + "emittery": "^0.13.1", + "jest-util": "^29.7.0", "string-length": "^4.0.1" }, "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, "node_modules/jest-worker": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz", - "integrity": "sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.7.0.tgz", + "integrity": "sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw==", "dev": true, "license": "MIT", "dependencies": { "@types/node": "*", + "jest-util": "^29.7.0", "merge-stream": "^2.0.0", "supports-color": "^8.0.0" }, "engines": { - "node": ">= 10.13.0" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, "node_modules/jest-worker/node_modules/supports-color": { @@ -13304,42 +13941,41 @@ } }, "node_modules/jsdom": { - "version": "16.7.0", - "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-16.7.0.tgz", - "integrity": "sha512-u9Smc2G1USStM+s/x1ru5Sxrl6mPYCbByG1U/hUmqaVsm4tbNyS7CicOSRyuGQYZhTu0h84qkZZQ/I+dzizSVw==", + "version": "20.0.3", + "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-20.0.3.tgz", + "integrity": "sha512-SYhBvTh89tTfCD/CRdSOm13mOBa42iTaTyfyEWBdKcGdPxPtLFBXuHR8XHb33YNYaP+lLbmSvBTsnoesCNJEsQ==", "dev": true, "license": "MIT", "dependencies": { - "abab": "^2.0.5", - "acorn": "^8.2.4", - "acorn-globals": "^6.0.0", - "cssom": "^0.4.4", + "abab": "^2.0.6", + "acorn": "^8.8.1", + "acorn-globals": "^7.0.0", + "cssom": "^0.5.0", "cssstyle": "^2.3.0", - "data-urls": "^2.0.0", - "decimal.js": "^10.2.1", - "domexception": "^2.0.1", + "data-urls": "^3.0.2", + "decimal.js": "^10.4.2", + "domexception": "^4.0.0", "escodegen": "^2.0.0", - "form-data": "^3.0.0", - "html-encoding-sniffer": "^2.0.1", - "http-proxy-agent": "^4.0.1", - "https-proxy-agent": "^5.0.0", + "form-data": "^4.0.0", + "html-encoding-sniffer": "^3.0.0", + "http-proxy-agent": "^5.0.0", + "https-proxy-agent": "^5.0.1", "is-potential-custom-element-name": "^1.0.1", - "nwsapi": "^2.2.0", - "parse5": "6.0.1", - "saxes": "^5.0.1", + "nwsapi": "^2.2.2", + "parse5": "^7.1.1", + "saxes": "^6.0.0", "symbol-tree": "^3.2.4", - "tough-cookie": "^4.0.0", - "w3c-hr-time": "^1.0.2", - "w3c-xmlserializer": "^2.0.0", - "webidl-conversions": "^6.1.0", - "whatwg-encoding": "^1.0.5", - "whatwg-mimetype": "^2.3.0", - "whatwg-url": "^8.5.0", - "ws": "^7.4.6", - "xml-name-validator": "^3.0.0" + "tough-cookie": "^4.1.2", + "w3c-xmlserializer": "^4.0.0", + "webidl-conversions": "^7.0.0", + "whatwg-encoding": "^2.0.0", + "whatwg-mimetype": "^3.0.0", + "whatwg-url": "^11.0.0", + "ws": "^8.11.0", + "xml-name-validator": "^4.0.0" }, "engines": { - "node": ">=10" + "node": ">=14" }, "peerDependencies": { "canvas": "^2.5.0" @@ -13866,9 +14502,9 @@ } }, "node_modules/make-dir/node_modules/semver": { - "version": "7.6.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", - "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", + "version": "7.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz", + "integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==", "dev": true, "license": "ISC", "bin": { @@ -16285,11 +16921,17 @@ } }, "node_modules/parse5": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz", - "integrity": "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==", + "version": "7.2.1", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.2.1.tgz", + "integrity": "sha512-BuBYQYlv1ckiPdQi/ohiivi9Sagc9JG+Ozs0r7b/0iK3sKmrb0b9FdWdBbOdx6hBCM/F9Ir82ofnBhtZOjCRPQ==", "dev": true, - "license": "MIT" + "license": "MIT", + "dependencies": { + "entities": "^4.5.0" + }, + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" + } }, "node_modules/path-exists": { "version": "4.0.0", @@ -16646,6 +17288,23 @@ "node": ">=6" } }, + "node_modules/pure-rand": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-6.1.0.tgz", + "integrity": "sha512-bVWawvoZoBYpp6yIoQtQXHZjmz35RSVHnUOTefl8Vcjr8snTPY1wnpSPMWekcFwbxI6gtmT7rSYPFvz71ldiOA==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/dubzzz" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fast-check" + } + ], + "license": "MIT" + }, "node_modules/querystringify": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz", @@ -17736,9 +18395,9 @@ } }, "node_modules/resolve.exports": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/resolve.exports/-/resolve.exports-1.1.1.tgz", - "integrity": "sha512-/NtpHNDN7jWhAaQ9BvBUYZ6YTXsRBgfqWFWP7BZBaoMJO/I3G5OFzvTuWNlZC3aPjins1F+TNrLKsGbH4rfsRQ==", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/resolve.exports/-/resolve.exports-2.0.3.tgz", + "integrity": "sha512-OcXjMsGdhL4XnbShKpAcSqPMzQoYkYyhbEaeSko47MjRP9NfEQMhZkXL1DoFlt9LWQn4YttrdnV6X2OiyzBi+A==", "dev": true, "license": "MIT", "engines": { @@ -17907,16 +18566,16 @@ "license": "MIT" }, "node_modules/saxes": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/saxes/-/saxes-5.0.1.tgz", - "integrity": "sha512-5LBh1Tls8c9xgGjw3QrMwETmTMVk0oFgvrFSvWx62llR2hcEInrKNZ2GZCCuuy2lvWrdl5jhbpeqc5hRYKFOcw==", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/saxes/-/saxes-6.0.0.tgz", + "integrity": "sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==", "dev": true, "license": "ISC", "dependencies": { "xmlchars": "^2.2.0" }, "engines": { - "node": ">=10" + "node": ">=v12.22.7" } }, "node_modules/scheduler": { @@ -18185,9 +18844,9 @@ } }, "node_modules/source-map-support": { - "version": "0.5.21", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", - "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", + "version": "0.5.13", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.13.tgz", + "integrity": "sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==", "dev": true, "license": "MIT", "dependencies": { @@ -18682,20 +19341,6 @@ "node": ">=8" } }, - "node_modules/supports-hyperlinks": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/supports-hyperlinks/-/supports-hyperlinks-2.3.0.tgz", - "integrity": "sha512-RpsAZlpWcDwOPQA22aCH4J0t7L8JmAvsCxfOSEwm7cQs3LshN36QaTkwd70DnBOXDWGssw2eUoc8CaRWT0XunA==", - "dev": true, - "license": "MIT", - "dependencies": { - "has-flag": "^4.0.0", - "supports-color": "^7.0.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/supports-preserve-symlinks-flag": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", @@ -18756,23 +19401,6 @@ "node": ">=6" } }, - "node_modules/terminal-link": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/terminal-link/-/terminal-link-2.1.1.tgz", - "integrity": "sha512-un0FmiRUQNr5PJqy9kP7c40F5BOfpGlYTrxonDChEZB7pzZxRNp/bt+ymiy9/npwXya9KH99nJ/GXFIiUkYGFQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-escapes": "^4.2.1", - "supports-hyperlinks": "^2.0.0" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/test-exclude": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", @@ -18795,13 +19423,6 @@ "dev": true, "license": "MIT" }, - "node_modules/throat": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/throat/-/throat-6.0.2.tgz", - "integrity": "sha512-WKexMoJj3vEuK0yFEapj8y64V0A6xcuPuK9Gt1d0R+dzCSJc0lHqQytAbSB4cDAK0dWh4T0E2ETkoLE2WZ41OQ==", - "dev": true, - "license": "MIT" - }, "node_modules/throttle-debounce": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/throttle-debounce/-/throttle-debounce-3.0.1.tgz", @@ -18885,16 +19506,16 @@ } }, "node_modules/tr46": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-2.1.0.tgz", - "integrity": "sha512-15Ih7phfcdP5YxqiB+iDtLoaTz4Nd35+IiAv0kQ5FNKHzXgdWqPoTIqEDDJmXceQt4JZk6lVPT8lnDlPpGDppw==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-3.0.0.tgz", + "integrity": "sha512-l7FvfAHlcmulp8kr+flpQZmVwtu7nfRV7NZujtN0OqES8EL4O4e0qqzL0DC5gAvx/ZC/9lk6rhcUwYvkBnBnYA==", "dev": true, "license": "MIT", "dependencies": { "punycode": "^2.1.1" }, "engines": { - "node": ">=8" + "node": ">=12" } }, "node_modules/trim-lines": { @@ -18947,39 +19568,44 @@ "license": "Unlicense" }, "node_modules/ts-jest": { - "version": "27.1.5", - "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-27.1.5.tgz", - "integrity": "sha512-Xv6jBQPoBEvBq/5i2TeSG9tt/nqkbpcurrEG1b+2yfBrcJelOZF9Ml6dmyMh7bcW9JyFbRYpR5rxROSlBLTZHA==", + "version": "29.2.6", + "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-29.2.6.tgz", + "integrity": "sha512-yTNZVZqc8lSixm+QGVFcPe6+yj7+TWZwIesuOWvfcn4B9bz5x4NDzVCQQjOs7Hfouu36aEqfEbo9Qpo+gq8dDg==", "dev": true, "license": "MIT", "dependencies": { - "bs-logger": "0.x", - "fast-json-stable-stringify": "2.x", - "jest-util": "^27.0.0", - "json5": "2.x", - "lodash.memoize": "4.x", - "make-error": "1.x", - "semver": "7.x", - "yargs-parser": "20.x" + "bs-logger": "^0.2.6", + "ejs": "^3.1.10", + "fast-json-stable-stringify": "^2.1.0", + "jest-util": "^29.0.0", + "json5": "^2.2.3", + "lodash.memoize": "^4.1.2", + "make-error": "^1.3.6", + "semver": "^7.7.1", + "yargs-parser": "^21.1.1" }, "bin": { "ts-jest": "cli.js" }, "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + "node": "^14.15.0 || ^16.10.0 || ^18.0.0 || >=20.0.0" }, "peerDependencies": { "@babel/core": ">=7.0.0-beta.0 <8", - "@types/jest": "^27.0.0", - "babel-jest": ">=27.0.0 <28", - "jest": "^27.0.0", - "typescript": ">=3.8 <5.0" + "@jest/transform": "^29.0.0", + "@jest/types": "^29.0.0", + "babel-jest": "^29.0.0", + "jest": "^29.0.0", + "typescript": ">=4.3 <6" }, "peerDependenciesMeta": { "@babel/core": { "optional": true }, - "@types/jest": { + "@jest/transform": { + "optional": true + }, + "@jest/types": { "optional": true }, "babel-jest": { @@ -18991,9 +19617,9 @@ } }, "node_modules/ts-jest/node_modules/semver": { - "version": "7.6.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", - "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", + "version": "7.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz", + "integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==", "dev": true, "license": "ISC", "bin": { @@ -19171,16 +19797,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/typedarray-to-buffer": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz", - "integrity": "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "is-typedarray": "^1.0.0" - } - }, "node_modules/typescript": { "version": "4.9.5", "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz", @@ -19653,37 +20269,20 @@ } }, "node_modules/v8-to-istanbul": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-8.1.1.tgz", - "integrity": "sha512-FGtKtv3xIpR6BYhvgH8MI/y78oT7d8Au3ww4QIxymrCtZEh5b8gCw2siywE+puhEmuWKDtmfrvF5UlB298ut3w==", + "version": "9.3.0", + "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.3.0.tgz", + "integrity": "sha512-kiGUalWN+rgBJ/1OHZsBtU4rXZOfj/7rKQxULKlIzwzQSvMJUUNgPwJEEh7gU6xEVxC0ahoOBvN2YI8GH6FNgA==", "dev": true, "license": "ISC", "dependencies": { + "@jridgewell/trace-mapping": "^0.3.12", "@types/istanbul-lib-coverage": "^2.0.1", - "convert-source-map": "^1.6.0", - "source-map": "^0.7.3" + "convert-source-map": "^2.0.0" }, "engines": { "node": ">=10.12.0" } }, - "node_modules/v8-to-istanbul/node_modules/convert-source-map": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", - "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==", - "dev": true, - "license": "MIT" - }, - "node_modules/v8-to-istanbul/node_modules/source-map": { - "version": "0.7.4", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz", - "integrity": "sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==", - "dev": true, - "license": "BSD-3-Clause", - "engines": { - "node": ">= 8" - } - }, "node_modules/vfile": { "version": "6.0.3", "resolved": "https://registry.npmjs.org/vfile/-/vfile-6.0.3.tgz", @@ -19810,28 +20409,17 @@ "react": "^17 || ^18 || ^19" } }, - "node_modules/w3c-hr-time": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/w3c-hr-time/-/w3c-hr-time-1.0.2.tgz", - "integrity": "sha512-z8P5DvDNjKDoFIHK7q8r8lackT6l+jo/Ye3HOle7l9nICP9lf1Ci25fy9vHd0JOWewkIFzXIEig3TdKT7JQ5fQ==", - "deprecated": "Use your platform's native performance.now() and performance.timeOrigin.", - "dev": true, - "license": "MIT", - "dependencies": { - "browser-process-hrtime": "^1.0.0" - } - }, "node_modules/w3c-xmlserializer": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-2.0.0.tgz", - "integrity": "sha512-4tzD0mF8iSiMiNs30BiLO3EpfGLZUT2MSX/G+o7ZywDzliWQ3OPtTZ0PTC3B3ca1UAf4cJMHB+2Bf56EriJuRA==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-4.0.0.tgz", + "integrity": "sha512-d+BFHzbiCx6zGfz0HyQ6Rg69w9k19nviJspaj4yNscGjrHu94sVP+aRm75yEbCh+r2/yR+7q6hux9LVtbuTGBw==", "dev": true, "license": "MIT", "dependencies": { - "xml-name-validator": "^3.0.0" + "xml-name-validator": "^4.0.0" }, "engines": { - "node": ">=10" + "node": ">=14" } }, "node_modules/walker": { @@ -19855,13 +20443,13 @@ } }, "node_modules/webidl-conversions": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-6.1.0.tgz", - "integrity": "sha512-qBIvFLGiBpLjfwmYAaHPXsn+ho5xZnGvyGvsarywGNc8VyQJUMHJ8OBKGGrPER0okBeMDaan4mNBlgBROxuI8w==", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz", + "integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==", "dev": true, "license": "BSD-2-Clause", "engines": { - "node": ">=10.4" + "node": ">=12" } }, "node_modules/webpack-virtual-modules": { @@ -19872,35 +20460,40 @@ "license": "MIT" }, "node_modules/whatwg-encoding": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-1.0.5.tgz", - "integrity": "sha512-b5lim54JOPN9HtzvK9HFXvBma/rnfFeqsic0hSpjtDbVxR3dJKLc+KB4V6GgiGOvl7CY/KNh8rxSo9DKQrnUEw==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-2.0.0.tgz", + "integrity": "sha512-p41ogyeMUrw3jWclHWTQg1k05DSVXPLcVxRTYsXUk+ZooOCZLcoYgPZ/HL/D/N+uQPOtcp1me1WhBEaX02mhWg==", "dev": true, "license": "MIT", "dependencies": { - "iconv-lite": "0.4.24" + "iconv-lite": "0.6.3" + }, + "engines": { + "node": ">=12" } }, "node_modules/whatwg-mimetype": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-2.3.0.tgz", - "integrity": "sha512-M4yMwr6mAnQz76TbJm914+gPpB/nCwvZbJU28cUD6dR004SAxDLOOSUaB1JDRqLtaOV/vi0IC5lEAGFgrjGv/g==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-3.0.0.tgz", + "integrity": "sha512-nt+N2dzIutVRxARx1nghPKGv1xHikU7HKdfafKkLNLindmPU/ch3U31NOCGGA/dmPcmb1VlofO0vnKAcsm0o/Q==", "dev": true, - "license": "MIT" + "license": "MIT", + "engines": { + "node": ">=12" + } }, "node_modules/whatwg-url": { - "version": "8.7.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-8.7.0.tgz", - "integrity": "sha512-gAojqb/m9Q8a5IV96E3fHJM70AzCkgt4uXYX2O7EmuyOnLrViCQlsEBmF9UQIu3/aeAIp2U17rtbpZWNntQqdg==", + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-11.0.0.tgz", + "integrity": "sha512-RKT8HExMpoYx4igMiVMY83lN6UeITKJlBQ+vR/8ZJ8OCdSiN3RwCq+9gH0+Xzj0+5IrM6i4j/6LuvzbZIQgEcQ==", "dev": true, "license": "MIT", "dependencies": { - "lodash": "^4.7.0", - "tr46": "^2.1.0", - "webidl-conversions": "^6.1.0" + "tr46": "^3.0.0", + "webidl-conversions": "^7.0.0" }, "engines": { - "node": ">=10" + "node": ">=12" } }, "node_modules/which": { @@ -20062,30 +20655,31 @@ "license": "ISC" }, "node_modules/write-file-atomic": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-3.0.3.tgz", - "integrity": "sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==", + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-4.0.2.tgz", + "integrity": "sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg==", "dev": true, "license": "ISC", "dependencies": { "imurmurhash": "^0.1.4", - "is-typedarray": "^1.0.0", - "signal-exit": "^3.0.2", - "typedarray-to-buffer": "^3.1.5" + "signal-exit": "^3.0.7" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" } }, "node_modules/ws": { - "version": "7.5.10", - "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.10.tgz", - "integrity": "sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ==", + "version": "8.18.1", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.1.tgz", + "integrity": "sha512-RKW2aJZMXeMxVpnZ6bck+RswznaxmzdULiBr6KY7XkTnW8uvt0iT9H5DkHUChXrc+uurzwa0rVI16n/Xzjdz1w==", "dev": true, "license": "MIT", "engines": { - "node": ">=8.3.0" + "node": ">=10.0.0" }, "peerDependencies": { "bufferutil": "^4.0.1", - "utf-8-validate": "^5.0.2" + "utf-8-validate": ">=5.0.2" }, "peerDependenciesMeta": { "bufferutil": { @@ -20097,11 +20691,14 @@ } }, "node_modules/xml-name-validator": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-3.0.0.tgz", - "integrity": "sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-4.0.0.tgz", + "integrity": "sha512-ICP2e+jsHvAj2E2lIHxa5tjXRlKDJo4IdvPvCXbXQGdzSfmSpNVyIKMvoZHjDY9DP0zV17iI85o90vRFXNccRw==", "dev": true, - "license": "Apache-2.0" + "license": "Apache-2.0", + "engines": { + "node": ">=12" + } }, "node_modules/xmlchars": { "version": "2.2.0", @@ -20137,32 +20734,32 @@ "license": "ISC" }, "node_modules/yargs": { - "version": "16.2.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", - "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", "dev": true, "license": "MIT", "dependencies": { - "cliui": "^7.0.2", + "cliui": "^8.0.1", "escalade": "^3.1.1", "get-caller-file": "^2.0.5", "require-directory": "^2.1.1", - "string-width": "^4.2.0", + "string-width": "^4.2.3", "y18n": "^5.0.5", - "yargs-parser": "^20.2.2" + "yargs-parser": "^21.1.1" }, "engines": { - "node": ">=10" + "node": ">=12" } }, "node_modules/yargs-parser": { - "version": "20.2.9", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", - "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", "dev": true, "license": "ISC", "engines": { - "node": ">=10" + "node": ">=12" } }, "node_modules/yocto-queue": { diff --git a/webview-ui/package.json b/webview-ui/package.json index 49bd0decbc8..2206fb35c94 100644 --- a/webview-ui/package.json +++ b/webview-ui/package.json @@ -75,13 +75,13 @@ "eslint-plugin-react-hooks": "^4.6.0", "eslint-plugin-storybook": "^0.11.2", "identity-obj-proxy": "^3.0.0", - "jest": "^27.5.1", - "jest-environment-jsdom": "^27.5.1", + "jest": "^29.7.0", + "jest-environment-jsdom": "^29.7.0", "jest-simple-dot-reporter": "^1.0.5", "shiki": "^2.3.2", "storybook": "^8.5.6", "storybook-dark-mode": "^4.0.2", - "ts-jest": "^27.1.5", + "ts-jest": "^29.2.5", "typescript": "^4.9.5", "vite": "6.0.11" } From 873720f0a7a7fbea210af31104c2775dc3f6861b Mon Sep 17 00:00:00 2001 From: John Stearns Date: Sun, 23 Feb 2025 08:12:03 -0800 Subject: [PATCH 034/585] Updating CODEOWNERS to better reflect the current state --- .github/CODEOWNERS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index c4c59d49a80..7feb2005ae1 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -1,2 +1,2 @@ # These owners will be the default owners for everything in the repo -* @stea9499 @ColemanRoo @mrubens @cte +* @mrubens @cte From 655930cf5f3925460414e9098d8851549e4a3333 Mon Sep 17 00:00:00 2001 From: ShayBC Date: Mon, 24 Feb 2025 00:27:40 +0200 Subject: [PATCH 035/585] added getClineStackSize() to ClineProvider and fixed its tests --- src/core/webview/ClineProvider.ts | 5 ++ .../webview/__tests__/ClineProvider.test.ts | 57 +++++++++++++------ 2 files changed, 46 insertions(+), 16 deletions(-) diff --git a/src/core/webview/ClineProvider.ts b/src/core/webview/ClineProvider.ts index b00c744ff66..a8f10f6fcd0 100644 --- a/src/core/webview/ClineProvider.ts +++ b/src/core/webview/ClineProvider.ts @@ -215,6 +215,11 @@ export class ClineProvider implements vscode.WebviewViewProvider { return this.clineStack[this.clineStack.length - 1] } + // returns the current clineStack length (how many cline objects are in the stack) + getClineStackSize(): number { + return this.clineStack.length + } + // remove the current task/cline instance (at the top of the stack), ao this task is finished // and resume the previous task/cline instance (if it exists) // this is used when a sub task is finished and the parent task needs to be resumed diff --git a/src/core/webview/__tests__/ClineProvider.test.ts b/src/core/webview/__tests__/ClineProvider.test.ts index 15d0eff7086..65408b8973b 100644 --- a/src/core/webview/__tests__/ClineProvider.test.ts +++ b/src/core/webview/__tests__/ClineProvider.test.ts @@ -8,6 +8,7 @@ import { ExtensionMessage, ExtensionState } from "../../../shared/ExtensionMessa import { setSoundEnabled } from "../../../utils/sound" import { defaultModeSlug } from "../../../shared/modes" import { experimentDefault } from "../../../shared/experiments" +import { Cline } from "../../Cline" // Mock custom-instructions module const mockAddCustomInstructions = jest.fn() @@ -377,15 +378,43 @@ describe("ClineProvider", () => { }) test("clearTask aborts current task", async () => { + // prepare the mock object const mockAbortTask = jest.fn() - // @ts-ignore - accessing private property for testing - provider.cline = { abortTask: mockAbortTask } + const clineMock = { abortTask: mockAbortTask } as unknown as Cline + + // add the mock object to the stack + provider.addClineToStack(clineMock) + // get the stack size before the abort call + const stackSizeBeforeAbort = provider.getClineStackSize() + + // call the removeClineFromStack method so it will call the current cline abort and remove it from the stack await provider.removeClineFromStack() + // get the stack size after the abort call + const stackSizeAfterAbort = provider.getClineStackSize() + + // check if the abort method was called expect(mockAbortTask).toHaveBeenCalled() - // @ts-ignore - accessing private property for testing - expect(provider.cline).toBeUndefined() + + // check if the stack size was decreased + expect(stackSizeBeforeAbort - stackSizeAfterAbort).toBe(1) + }) + + test("addClineToStack adds multiple Cline instances to the stack", () => { + // prepare test data + const mockCline1 = { taskId: "test-task-id-1" } as unknown as Cline + const mockCline2 = { taskId: "test-task-id-2" } as unknown as Cline + + // add Cline instances to the stack + provider.addClineToStack(mockCline1) + provider.addClineToStack(mockCline2) + + // verify cline instances were added to the stack + expect(provider.getClineStackSize()).toBe(2) + + // verify current cline instance is the last one added + expect(provider.getCurrentCline()).toBe(mockCline2) }) test("getState returns correct initial state", async () => { @@ -788,9 +817,8 @@ describe("ClineProvider", () => { taskId: "test-task-id", abortTask: jest.fn(), handleWebviewAskResponse: jest.fn(), - } - // @ts-ignore - accessing private property for testing - provider.cline = mockCline + } as unknown as Cline + provider.addClineToStack(mockCline) // Mock getTaskWithId ;(provider as any).getTaskWithId = jest.fn().mockResolvedValue({ @@ -841,9 +869,8 @@ describe("ClineProvider", () => { taskId: "test-task-id", abortTask: jest.fn(), handleWebviewAskResponse: jest.fn(), - } - // @ts-ignore - accessing private property for testing - provider.cline = mockCline + } as unknown as Cline + provider.addClineToStack(mockCline) // Mock getTaskWithId ;(provider as any).getTaskWithId = jest.fn().mockResolvedValue({ @@ -871,9 +898,8 @@ describe("ClineProvider", () => { overwriteClineMessages: jest.fn(), overwriteApiConversationHistory: jest.fn(), taskId: "test-task-id", - } - // @ts-ignore - accessing private property for testing - provider.cline = mockCline + } as unknown as Cline + provider.addClineToStack(mockCline) // Trigger message deletion const messageHandler = (mockWebviewView.webview.onDidReceiveMessage as jest.Mock).mock.calls[0][0] @@ -1377,9 +1403,8 @@ describe("ClineProvider", () => { const mockCline = { api: undefined, abortTask: jest.fn(), - } - // @ts-ignore - accessing private property for testing - provider.cline = mockCline + } as unknown as Cline + provider.addClineToStack(mockCline) const testApiConfig = { apiProvider: "anthropic" as const, From 86896a8865bf1222a546a60d2857f2efb9191841 Mon Sep 17 00:00:00 2001 From: Matt Rubens Date: Sun, 23 Feb 2025 21:31:37 -0600 Subject: [PATCH 036/585] Include the original task in the power steering content --- .changeset/weak-swans-study.md | 5 ++ src/core/Cline.ts | 24 +++++-- src/core/__tests__/Cline.test.ts | 84 ++++++++++++++++++++++++ src/shared/__tests__/experiments.test.ts | 2 +- src/shared/experiments.ts | 2 +- 5 files changed, 110 insertions(+), 7 deletions(-) create mode 100644 .changeset/weak-swans-study.md diff --git a/.changeset/weak-swans-study.md b/.changeset/weak-swans-study.md new file mode 100644 index 00000000000..82997382145 --- /dev/null +++ b/.changeset/weak-swans-study.md @@ -0,0 +1,5 @@ +--- +"roo-cline": patch +--- + +Include the original task in the power steering content diff --git a/src/core/Cline.ts b/src/core/Cline.ts index 94bc6126924..4bf94e5fabb 100644 --- a/src/core/Cline.ts +++ b/src/core/Cline.ts @@ -3243,6 +3243,9 @@ export class Cline { customInstructions: globalCustomInstructions, preferredLanguage, } = (await this.providerRef.deref()?.getState()) ?? {} + + const powerSteering = Experiments.isEnabled(experiments ?? {}, EXPERIMENT_IDS.POWER_STEERING) + const currentMode = mode ?? defaultModeSlug const modeDetails = await getFullModeDetails(currentMode, customModes, customModePrompts, { cwd, @@ -3252,11 +3255,8 @@ export class Cline { details += `\n\n# Current Mode\n` details += `${currentMode}\n` details += `${modeDetails.name}\n` - if (Experiments.isEnabled(experiments ?? {}, EXPERIMENT_IDS.POWER_STEERING)) { + if (powerSteering) { details += `${modeDetails.roleDefinition}\n` - if (modeDetails.customInstructions) { - details += `${modeDetails.customInstructions}\n` - } } // Add warning if not in code mode @@ -3268,7 +3268,21 @@ export class Cline { ) { const currentModeName = getModeBySlug(currentMode, customModes)?.name ?? currentMode const defaultModeName = getModeBySlug(defaultModeSlug, customModes)?.name ?? defaultModeSlug - details += `\n\nNOTE: You are currently in '${currentModeName}' mode which only allows read-only operations. To write files or execute commands, the user will need to switch to '${defaultModeName}' mode. Note that only the user can switch modes.` + details += `\n\nNOTE: You are currently in '${currentModeName}' mode which only allows read-only operations. To write files or execute commands, the user will need to switch to '${defaultModeName}' mode or another mode with these capabilities. Note that only the user can switch modes.` + } + + if (powerSteering) { + if (modeDetails.customInstructions) { + details += `\n\n# Custom Instructions\n` + details += `${modeDetails.customInstructions}\n` + } + + const taskMessage = this.clineMessages[0]?.text ?? "" + + if (taskMessage) { + details += `\n\n# Current Task\n\n` + details += `${taskMessage}\n` + } } if (includeFileDetails) { diff --git a/src/core/__tests__/Cline.test.ts b/src/core/__tests__/Cline.test.ts index 2eb9ab83f30..e55a9037948 100644 --- a/src/core/__tests__/Cline.test.ts +++ b/src/core/__tests__/Cline.test.ts @@ -466,6 +466,90 @@ describe("Cline", () => { expect(details).toMatch(/1\/1\/2024.*5:00:00 AM.*\(America\/Los_Angeles, UTC-7:00\)/) // Full time string format }) + it("should maintain correct section order with context size before file listing", async () => { + const cline = new Cline(mockProvider, mockApiConfig, undefined, false, false, undefined, "test task") + + const details = await cline["getEnvironmentDetails"](true) + + const contextSizeIndex = details.indexOf("# Current Context Size") + const fileListingIndex = details.indexOf("# Current Working Directory") + + expect(contextSizeIndex).toBeGreaterThan(-1) + expect(fileListingIndex).toBeGreaterThan(-1) + expect(contextSizeIndex).toBeLessThan(fileListingIndex) + }) + + it("should include power steering content when experiment is enabled", async () => { + // Mock provider state + mockProvider.getState = jest.fn().mockResolvedValue({ + customInstructions: "test instructions", + mode: "code", + customModes: [], + customModePrompts: {}, + preferredLanguage: "en", + }) + + const cline = new Cline( + mockProvider, + mockApiConfig, + "test instructions", + false, + false, + undefined, + "test task", + ) + + // Mock experiments module + const { experiments, EXPERIMENT_IDS } = require("../../shared/experiments") + jest.spyOn(experiments, "isEnabled").mockImplementation((config, id) => { + return id === EXPERIMENT_IDS.POWER_STEERING ? true : false + }) + + const details = await cline["getEnvironmentDetails"](false) + + // Verify sections are present + expect(details).toContain("# Custom Instructions") + expect(details).toContain("# Current Task") + + // Verify task content + expect(details).toContain("test task") + }) + + it("should exclude power steering content when experiment is disabled", async () => { + // Mock provider state + mockProvider.getState = jest.fn().mockResolvedValue({ + customInstructions: "test instructions", + mode: "code", + customModes: [], + customModePrompts: {}, + preferredLanguage: "en", + }) + + const cline = new Cline( + mockProvider, + mockApiConfig, + "test instructions", + false, + false, + undefined, + "test task", + ) + + // Mock experiments module + const { experiments, EXPERIMENT_IDS } = require("../../shared/experiments") + jest.spyOn(experiments, "isEnabled").mockImplementation((config, id) => { + return id === EXPERIMENT_IDS.POWER_STEERING ? false : true + }) + + const details = await cline["getEnvironmentDetails"](false) + + // Verify sections are not present + expect(details).not.toContain("# Custom Instructions") + expect(details).not.toContain("") + expect(details).not.toContain("# Current Task") + expect(details).not.toContain("") + }) + describe("API conversation handling", () => { it("should clean conversation history before sending to API", async () => { const cline = new Cline(mockProvider, mockApiConfig, undefined, false, false, undefined, "test task") diff --git a/src/shared/__tests__/experiments.test.ts b/src/shared/__tests__/experiments.test.ts index c5b999a1a33..d759d9b7535 100644 --- a/src/shared/__tests__/experiments.test.ts +++ b/src/shared/__tests__/experiments.test.ts @@ -7,7 +7,7 @@ describe("experiments", () => { expect(experimentConfigsMap.POWER_STEERING).toMatchObject({ name: 'Use experimental "power steering" mode', description: - "When enabled, Roo will remind the model about the details of its current mode definition more frequently. This will lead to stronger adherence to role definitions and custom instructions, but will use more tokens per message.", + "When enabled, Roo will remind the model about the details of the original task and its current mode definition more frequently. This will lead to stronger adherence to its instructions, but will use more tokens per message.", enabled: false, }) }) diff --git a/src/shared/experiments.ts b/src/shared/experiments.ts index 2f946283c0d..cfde5641461 100644 --- a/src/shared/experiments.ts +++ b/src/shared/experiments.ts @@ -39,7 +39,7 @@ export const experimentConfigsMap: Record = { POWER_STEERING: { name: 'Use experimental "power steering" mode', description: - "When enabled, Roo will remind the model about the details of its current mode definition more frequently. This will lead to stronger adherence to role definitions and custom instructions, but will use more tokens per message.", + "When enabled, Roo will remind the model about the details of the original task and its current mode definition more frequently. This will lead to stronger adherence to its instructions, but will use more tokens per message.", enabled: false, }, } From 5a6ca2bef141e3f5bbaf4874b0174b62db4cbbb6 Mon Sep 17 00:00:00 2001 From: Matt Rubens Date: Sun, 23 Feb 2025 23:04:22 -0600 Subject: [PATCH 037/585] Revert "Include the original task in the power steering content" --- .changeset/weak-swans-study.md | 5 -- src/core/Cline.ts | 24 ++----- src/core/__tests__/Cline.test.ts | 84 ------------------------ src/shared/__tests__/experiments.test.ts | 2 +- src/shared/experiments.ts | 2 +- 5 files changed, 7 insertions(+), 110 deletions(-) delete mode 100644 .changeset/weak-swans-study.md diff --git a/.changeset/weak-swans-study.md b/.changeset/weak-swans-study.md deleted file mode 100644 index 82997382145..00000000000 --- a/.changeset/weak-swans-study.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"roo-cline": patch ---- - -Include the original task in the power steering content diff --git a/src/core/Cline.ts b/src/core/Cline.ts index 4bf94e5fabb..94bc6126924 100644 --- a/src/core/Cline.ts +++ b/src/core/Cline.ts @@ -3243,9 +3243,6 @@ export class Cline { customInstructions: globalCustomInstructions, preferredLanguage, } = (await this.providerRef.deref()?.getState()) ?? {} - - const powerSteering = Experiments.isEnabled(experiments ?? {}, EXPERIMENT_IDS.POWER_STEERING) - const currentMode = mode ?? defaultModeSlug const modeDetails = await getFullModeDetails(currentMode, customModes, customModePrompts, { cwd, @@ -3255,8 +3252,11 @@ export class Cline { details += `\n\n# Current Mode\n` details += `${currentMode}\n` details += `${modeDetails.name}\n` - if (powerSteering) { + if (Experiments.isEnabled(experiments ?? {}, EXPERIMENT_IDS.POWER_STEERING)) { details += `${modeDetails.roleDefinition}\n` + if (modeDetails.customInstructions) { + details += `${modeDetails.customInstructions}\n` + } } // Add warning if not in code mode @@ -3268,21 +3268,7 @@ export class Cline { ) { const currentModeName = getModeBySlug(currentMode, customModes)?.name ?? currentMode const defaultModeName = getModeBySlug(defaultModeSlug, customModes)?.name ?? defaultModeSlug - details += `\n\nNOTE: You are currently in '${currentModeName}' mode which only allows read-only operations. To write files or execute commands, the user will need to switch to '${defaultModeName}' mode or another mode with these capabilities. Note that only the user can switch modes.` - } - - if (powerSteering) { - if (modeDetails.customInstructions) { - details += `\n\n# Custom Instructions\n` - details += `${modeDetails.customInstructions}\n` - } - - const taskMessage = this.clineMessages[0]?.text ?? "" - - if (taskMessage) { - details += `\n\n# Current Task\n\n` - details += `${taskMessage}\n` - } + details += `\n\nNOTE: You are currently in '${currentModeName}' mode which only allows read-only operations. To write files or execute commands, the user will need to switch to '${defaultModeName}' mode. Note that only the user can switch modes.` } if (includeFileDetails) { diff --git a/src/core/__tests__/Cline.test.ts b/src/core/__tests__/Cline.test.ts index e55a9037948..2eb9ab83f30 100644 --- a/src/core/__tests__/Cline.test.ts +++ b/src/core/__tests__/Cline.test.ts @@ -466,90 +466,6 @@ describe("Cline", () => { expect(details).toMatch(/1\/1\/2024.*5:00:00 AM.*\(America\/Los_Angeles, UTC-7:00\)/) // Full time string format }) - it("should maintain correct section order with context size before file listing", async () => { - const cline = new Cline(mockProvider, mockApiConfig, undefined, false, false, undefined, "test task") - - const details = await cline["getEnvironmentDetails"](true) - - const contextSizeIndex = details.indexOf("# Current Context Size") - const fileListingIndex = details.indexOf("# Current Working Directory") - - expect(contextSizeIndex).toBeGreaterThan(-1) - expect(fileListingIndex).toBeGreaterThan(-1) - expect(contextSizeIndex).toBeLessThan(fileListingIndex) - }) - - it("should include power steering content when experiment is enabled", async () => { - // Mock provider state - mockProvider.getState = jest.fn().mockResolvedValue({ - customInstructions: "test instructions", - mode: "code", - customModes: [], - customModePrompts: {}, - preferredLanguage: "en", - }) - - const cline = new Cline( - mockProvider, - mockApiConfig, - "test instructions", - false, - false, - undefined, - "test task", - ) - - // Mock experiments module - const { experiments, EXPERIMENT_IDS } = require("../../shared/experiments") - jest.spyOn(experiments, "isEnabled").mockImplementation((config, id) => { - return id === EXPERIMENT_IDS.POWER_STEERING ? true : false - }) - - const details = await cline["getEnvironmentDetails"](false) - - // Verify sections are present - expect(details).toContain("# Custom Instructions") - expect(details).toContain("# Current Task") - - // Verify task content - expect(details).toContain("test task") - }) - - it("should exclude power steering content when experiment is disabled", async () => { - // Mock provider state - mockProvider.getState = jest.fn().mockResolvedValue({ - customInstructions: "test instructions", - mode: "code", - customModes: [], - customModePrompts: {}, - preferredLanguage: "en", - }) - - const cline = new Cline( - mockProvider, - mockApiConfig, - "test instructions", - false, - false, - undefined, - "test task", - ) - - // Mock experiments module - const { experiments, EXPERIMENT_IDS } = require("../../shared/experiments") - jest.spyOn(experiments, "isEnabled").mockImplementation((config, id) => { - return id === EXPERIMENT_IDS.POWER_STEERING ? false : true - }) - - const details = await cline["getEnvironmentDetails"](false) - - // Verify sections are not present - expect(details).not.toContain("# Custom Instructions") - expect(details).not.toContain("") - expect(details).not.toContain("# Current Task") - expect(details).not.toContain("") - }) - describe("API conversation handling", () => { it("should clean conversation history before sending to API", async () => { const cline = new Cline(mockProvider, mockApiConfig, undefined, false, false, undefined, "test task") diff --git a/src/shared/__tests__/experiments.test.ts b/src/shared/__tests__/experiments.test.ts index d759d9b7535..c5b999a1a33 100644 --- a/src/shared/__tests__/experiments.test.ts +++ b/src/shared/__tests__/experiments.test.ts @@ -7,7 +7,7 @@ describe("experiments", () => { expect(experimentConfigsMap.POWER_STEERING).toMatchObject({ name: 'Use experimental "power steering" mode', description: - "When enabled, Roo will remind the model about the details of the original task and its current mode definition more frequently. This will lead to stronger adherence to its instructions, but will use more tokens per message.", + "When enabled, Roo will remind the model about the details of its current mode definition more frequently. This will lead to stronger adherence to role definitions and custom instructions, but will use more tokens per message.", enabled: false, }) }) diff --git a/src/shared/experiments.ts b/src/shared/experiments.ts index cfde5641461..2f946283c0d 100644 --- a/src/shared/experiments.ts +++ b/src/shared/experiments.ts @@ -39,7 +39,7 @@ export const experimentConfigsMap: Record = { POWER_STEERING: { name: 'Use experimental "power steering" mode', description: - "When enabled, Roo will remind the model about the details of the original task and its current mode definition more frequently. This will lead to stronger adherence to its instructions, but will use more tokens per message.", + "When enabled, Roo will remind the model about the details of its current mode definition more frequently. This will lead to stronger adherence to role definitions and custom instructions, but will use more tokens per message.", enabled: false, }, } From 40a86b1f709e8be2988d69bcba27f44b640b441a Mon Sep 17 00:00:00 2001 From: cte Date: Sun, 23 Feb 2025 21:16:43 -0800 Subject: [PATCH 038/585] Debug test flakes --- src/core/Cline.ts | 40 +++++-- src/core/__tests__/Cline.test.ts | 102 +++++++++++++++--- .../__tests__/edit-strategies.test.ts | 6 +- 3 files changed, 122 insertions(+), 26 deletions(-) diff --git a/src/core/Cline.ts b/src/core/Cline.ts index 94bc6126924..720b1b0d625 100644 --- a/src/core/Cline.ts +++ b/src/core/Cline.ts @@ -129,8 +129,9 @@ export class Cline { images?: string[] | undefined, historyItem?: HistoryItem | undefined, experiments?: Record, + startTask = true, ) { - if (!task && !images && !historyItem) { + if (startTask && !task && !images && !historyItem) { throw new Error("Either historyItem or task/images must be provided") } @@ -153,11 +154,32 @@ export class Cline { // Initialize diffStrategy based on current state this.updateDiffStrategy(Experiments.isEnabled(experiments ?? {}, EXPERIMENT_IDS.DIFF_STRATEGY)) - if (task || images) { - this.startTask(task, images) - } else if (historyItem) { - this.resumeTaskFromHistory() + if (startTask) { + if (task || images) { + this.startTask(task, images) + } else if (historyItem) { + this.resumeTaskFromHistory() + } else { + throw new Error("Either historyItem or task/images must be provided") + } + } + } + + static create(...args: ConstructorParameters): [Cline, Promise] { + args[10] = false // startTask + const instance = new Cline(...args) + + let task + + if (args[6] || args[7]) { + task = instance.startTask(args[6], args[7]) + } else if (args[8]) { + task = instance.resumeTaskFromHistory() + } else { + throw new Error("Either historyItem or task/images must be provided") } + + return [instance, task] } // Add method to update diffStrategy @@ -745,8 +767,12 @@ export class Cline { } } - async abortTask() { + async abortTask(isAbandoned = false) { // Will stop any autonomously running promises. + if (isAbandoned) { + this.abandoned = true + } + this.abort = true this.terminalManager.disposeAll() @@ -2967,7 +2993,7 @@ export class Cline { } // need to call here in case the stream was aborted - if (this.abort) { + if (this.abort || this.abandoned) { throw new Error("Roo Code instance aborted") } diff --git a/src/core/__tests__/Cline.test.ts b/src/core/__tests__/Cline.test.ts index 2eb9ab83f30..9f72c4931a7 100644 --- a/src/core/__tests__/Cline.test.ts +++ b/src/core/__tests__/Cline.test.ts @@ -1,3 +1,5 @@ +// npx jest src/core/__tests__/Cline.test.ts + import { Cline } from "../Cline" import { ClineProvider } from "../webview/ClineProvider" import { ApiConfiguration, ModelInfo } from "../../shared/api" @@ -324,8 +326,8 @@ describe("Cline", () => { }) describe("constructor", () => { - it("should respect provided settings", () => { - const cline = new Cline( + it("should respect provided settings", async () => { + const [cline, task] = Cline.create( mockProvider, mockApiConfig, "custom instructions", @@ -337,10 +339,13 @@ describe("Cline", () => { expect(cline.customInstructions).toBe("custom instructions") expect(cline.diffEnabled).toBe(false) + + await cline.abortTask(true) + await task.catch(() => {}) }) - it("should use default fuzzy match threshold when not provided", () => { - const cline = new Cline( + it("should use default fuzzy match threshold when not provided", async () => { + const [cline, task] = await Cline.create( mockProvider, mockApiConfig, "custom instructions", @@ -353,12 +358,15 @@ describe("Cline", () => { expect(cline.diffEnabled).toBe(true) // The diff strategy should be created with default threshold (1.0) expect(cline.diffStrategy).toBeDefined() + + await cline.abortTask(true) + await task.catch(() => {}) }) - it("should use provided fuzzy match threshold", () => { + it("should use provided fuzzy match threshold", async () => { const getDiffStrategySpy = jest.spyOn(require("../diff/DiffStrategy"), "getDiffStrategy") - const cline = new Cline( + const [cline, task] = Cline.create( mockProvider, mockApiConfig, "custom instructions", @@ -373,12 +381,15 @@ describe("Cline", () => { expect(getDiffStrategySpy).toHaveBeenCalledWith("claude-3-5-sonnet-20241022", 0.9, false) getDiffStrategySpy.mockRestore() + + await cline.abortTask(true) + await task.catch(() => {}) }) - it("should pass default threshold to diff strategy when not provided", () => { + it("should pass default threshold to diff strategy when not provided", async () => { const getDiffStrategySpy = jest.spyOn(require("../diff/DiffStrategy"), "getDiffStrategy") - const cline = new Cline( + const [cline, task] = Cline.create( mockProvider, mockApiConfig, "custom instructions", @@ -393,6 +404,9 @@ describe("Cline", () => { expect(getDiffStrategySpy).toHaveBeenCalledWith("claude-3-5-sonnet-20241022", 1.0, false) getDiffStrategySpy.mockRestore() + + await cline.abortTask(true) + await task.catch(() => {}) }) it("should require either task or historyItem", () => { @@ -455,7 +469,15 @@ describe("Cline", () => { }) it("should include timezone information in environment details", async () => { - const cline = new Cline(mockProvider, mockApiConfig, undefined, false, false, undefined, "test task") + const [cline, task] = Cline.create( + mockProvider, + mockApiConfig, + undefined, + false, + false, + undefined, + "test task", + ) const details = await cline["getEnvironmentDetails"](false) @@ -464,11 +486,24 @@ describe("Cline", () => { expect(details).toMatch(/UTC-7:00/) // Fixed offset for America/Los_Angeles expect(details).toContain("# Current Time") expect(details).toMatch(/1\/1\/2024.*5:00:00 AM.*\(America\/Los_Angeles, UTC-7:00\)/) // Full time string format + + await cline.abortTask(true) + await task.catch(() => {}) }) describe("API conversation handling", () => { it("should clean conversation history before sending to API", async () => { - const cline = new Cline(mockProvider, mockApiConfig, undefined, false, false, undefined, "test task") + const [cline, task] = Cline.create( + mockProvider, + mockApiConfig, + undefined, + false, + false, + undefined, + "test task", + ) + cline.abandoned = true + await task // Mock the API's createMessage method to capture the conversation history const createMessageSpy = jest.fn() @@ -576,7 +611,7 @@ describe("Cline", () => { ] // Test with model that supports images - const clineWithImages = new Cline( + const [clineWithImages, taskWithImages] = Cline.create( mockProvider, configWithImages, undefined, @@ -585,6 +620,7 @@ describe("Cline", () => { undefined, "test task", ) + // Mock the model info to indicate image support jest.spyOn(clineWithImages.api, "getModel").mockReturnValue({ id: "claude-3-sonnet", @@ -598,10 +634,11 @@ describe("Cline", () => { outputPrice: 0.75, } as ModelInfo, }) + clineWithImages.apiConversationHistory = conversationHistory // Test with model that doesn't support images - const clineWithoutImages = new Cline( + const [clineWithoutImages, taskWithoutImages] = Cline.create( mockProvider, configWithoutImages, undefined, @@ -610,6 +647,7 @@ describe("Cline", () => { undefined, "test task", ) + // Mock the model info to indicate no image support jest.spyOn(clineWithoutImages.api, "getModel").mockReturnValue({ id: "gpt-3.5-turbo", @@ -623,6 +661,7 @@ describe("Cline", () => { outputPrice: 0.2, } as ModelInfo, }) + clineWithoutImages.apiConversationHistory = conversationHistory // Mock abort state for both instances @@ -631,6 +670,7 @@ describe("Cline", () => { set: () => {}, configurable: true, }) + Object.defineProperty(clineWithoutImages, "abort", { get: () => false, set: () => {}, @@ -645,6 +685,7 @@ describe("Cline", () => { content, "", ]) + // Set up mock streams const mockStreamWithImages = (async function* () { yield { type: "text", text: "test response" } @@ -672,6 +713,12 @@ describe("Cline", () => { }, ] + clineWithImages.abandoned = true + await taskWithImages.catch(() => {}) + + clineWithoutImages.abandoned = true + await taskWithoutImages.catch(() => {}) + // Trigger API requests await clineWithImages.recursivelyMakeClineRequests([{ type: "text", text: "test request" }]) await clineWithoutImages.recursivelyMakeClineRequests([{ type: "text", text: "test request" }]) @@ -695,7 +742,15 @@ describe("Cline", () => { }) it.skip("should handle API retry with countdown", async () => { - const cline = new Cline(mockProvider, mockApiConfig, undefined, false, false, undefined, "test task") + const [cline, task] = Cline.create( + mockProvider, + mockApiConfig, + undefined, + false, + false, + undefined, + "test task", + ) // Mock delay to track countdown timing const mockDelay = jest.fn().mockResolvedValue(undefined) @@ -809,10 +864,21 @@ describe("Cline", () => { expect(errorMessage).toBe( `${mockError.message}\n\nRetry attempt 1\nRetrying in ${baseDelay} seconds...`, ) + + await cline.abortTask(true) + await task.catch(() => {}) }) it.skip("should not apply retry delay twice", async () => { - const cline = new Cline(mockProvider, mockApiConfig, undefined, false, false, undefined, "test task") + const [cline, task] = Cline.create( + mockProvider, + mockApiConfig, + undefined, + false, + false, + undefined, + "test task", + ) // Mock delay to track countdown timing const mockDelay = jest.fn().mockResolvedValue(undefined) @@ -925,11 +991,14 @@ describe("Cline", () => { undefined, false, ) + + await cline.abortTask(true) + await task.catch(() => {}) }) describe("loadContext", () => { it("should process mentions in task and feedback tags", async () => { - const cline = new Cline( + const [cline, task] = Cline.create( mockProvider, mockApiConfig, undefined, @@ -1002,6 +1071,9 @@ describe("Cline", () => { const toolResult2 = processedContent[3] as Anthropic.ToolResultBlockParam const content2 = Array.isArray(toolResult2.content) ? toolResult2.content[0] : toolResult2.content expect((content2 as Anthropic.TextBlockParam).text).toBe("Regular tool result with @/path") + + await cline.abortTask(true) + await task.catch(() => {}) }) }) }) diff --git a/src/core/diff/strategies/new-unified/__tests__/edit-strategies.test.ts b/src/core/diff/strategies/new-unified/__tests__/edit-strategies.test.ts index f8251e3d6e6..2bc35540baf 100644 --- a/src/core/diff/strategies/new-unified/__tests__/edit-strategies.test.ts +++ b/src/core/diff/strategies/new-unified/__tests__/edit-strategies.test.ts @@ -1,5 +1,3 @@ -/// - import { applyContextMatching, applyDMP, applyGitFallback } from "../edit-strategies" import { Hunk } from "../types" @@ -277,7 +275,7 @@ describe("applyGitFallback", () => { expect(result.result.join("\n")).toEqual("line1\nnew line2\nline3") expect(result.confidence).toBe(1) expect(result.strategy).toBe("git-fallback") - }, 10_000) + }) it("should return original content with 0 confidence when changes cannot be applied", async () => { const hunk = { @@ -293,5 +291,5 @@ describe("applyGitFallback", () => { expect(result.result).toEqual(content) expect(result.confidence).toBe(0) expect(result.strategy).toBe("git-fallback") - }, 10_000) + }) }) From b82a81c826886a6c7ec582b38ef4797fe6ec87e8 Mon Sep 17 00:00:00 2001 From: cte Date: Sun, 23 Feb 2025 21:59:21 -0800 Subject: [PATCH 039/585] Add a ClineOptions type --- src/core/Cline.ts | 57 +++--- src/core/__tests__/Cline.test.ts | 175 +++++++----------- src/core/webview/ClineProvider.ts | 27 ++- .../webview/__tests__/ClineProvider.test.ts | 21 +-- 4 files changed, 124 insertions(+), 156 deletions(-) diff --git a/src/core/Cline.ts b/src/core/Cline.ts index 720b1b0d625..522af873274 100644 --- a/src/core/Cline.ts +++ b/src/core/Cline.ts @@ -73,6 +73,20 @@ type UserContent = Array< Anthropic.TextBlockParam | Anthropic.ImageBlockParam | Anthropic.ToolUseBlockParam | Anthropic.ToolResultBlockParam > +export type ClineOptions = { + provider: ClineProvider + apiConfiguration: ApiConfiguration + customInstructions?: string + enableDiff?: boolean + enableCheckpoints?: boolean + fuzzyMatchThreshold?: number + task?: string + images?: string[] + historyItem?: HistoryItem + experiments?: Record + startTask?: boolean +} + export class Cline { readonly taskId: string api: ApiHandler @@ -118,19 +132,19 @@ export class Cline { private didAlreadyUseTool = false private didCompleteReadingStream = false - constructor( - provider: ClineProvider, - apiConfiguration: ApiConfiguration, - customInstructions?: string, - enableDiff?: boolean, - enableCheckpoints?: boolean, - fuzzyMatchThreshold?: number, - task?: string | undefined, - images?: string[] | undefined, - historyItem?: HistoryItem | undefined, - experiments?: Record, + constructor({ + provider, + apiConfiguration, + customInstructions, + enableDiff, + enableCheckpoints, + fuzzyMatchThreshold, + task, + images, + historyItem, + experiments, startTask = true, - ) { + }: ClineOptions) { if (startTask && !task && !images && !historyItem) { throw new Error("Either historyItem or task/images must be provided") } @@ -165,21 +179,20 @@ export class Cline { } } - static create(...args: ConstructorParameters): [Cline, Promise] { - args[10] = false // startTask - const instance = new Cline(...args) - - let task + static create(options: ClineOptions): [Cline, Promise] { + const instance = new Cline({ ...options, startTask: false }) + const { images, task, historyItem } = options + let promise - if (args[6] || args[7]) { - task = instance.startTask(args[6], args[7]) - } else if (args[8]) { - task = instance.resumeTaskFromHistory() + if (images || task) { + promise = instance.startTask(task, images) + } else if (historyItem) { + promise = instance.resumeTaskFromHistory() } else { throw new Error("Either historyItem or task/images must be provided") } - return [instance, task] + return [instance, promise] } // Add method to update diffStrategy diff --git a/src/core/__tests__/Cline.test.ts b/src/core/__tests__/Cline.test.ts index 9f72c4931a7..9910896ebb9 100644 --- a/src/core/__tests__/Cline.test.ts +++ b/src/core/__tests__/Cline.test.ts @@ -327,15 +327,13 @@ describe("Cline", () => { describe("constructor", () => { it("should respect provided settings", async () => { - const [cline, task] = Cline.create( - mockProvider, - mockApiConfig, - "custom instructions", - false, - false, - 0.95, // 95% threshold - "test task", - ) + const [cline, task] = Cline.create({ + provider: mockProvider, + apiConfiguration: mockApiConfig, + customInstructions: "custom instructions", + fuzzyMatchThreshold: 0.95, + task: "test task", + }) expect(cline.customInstructions).toBe("custom instructions") expect(cline.diffEnabled).toBe(false) @@ -345,15 +343,14 @@ describe("Cline", () => { }) it("should use default fuzzy match threshold when not provided", async () => { - const [cline, task] = await Cline.create( - mockProvider, - mockApiConfig, - "custom instructions", - true, - false, - undefined, - "test task", - ) + const [cline, task] = await Cline.create({ + provider: mockProvider, + apiConfiguration: mockApiConfig, + customInstructions: "custom instructions", + enableDiff: true, + fuzzyMatchThreshold: 0.95, + task: "test task", + }) expect(cline.diffEnabled).toBe(true) // The diff strategy should be created with default threshold (1.0) @@ -366,15 +363,14 @@ describe("Cline", () => { it("should use provided fuzzy match threshold", async () => { const getDiffStrategySpy = jest.spyOn(require("../diff/DiffStrategy"), "getDiffStrategy") - const [cline, task] = Cline.create( - mockProvider, - mockApiConfig, - "custom instructions", - true, - false, - 0.9, // 90% threshold - "test task", - ) + const [cline, task] = await Cline.create({ + provider: mockProvider, + apiConfiguration: mockApiConfig, + customInstructions: "custom instructions", + enableDiff: true, + fuzzyMatchThreshold: 0.9, + task: "test task", + }) expect(cline.diffEnabled).toBe(true) expect(cline.diffStrategy).toBeDefined() @@ -389,15 +385,13 @@ describe("Cline", () => { it("should pass default threshold to diff strategy when not provided", async () => { const getDiffStrategySpy = jest.spyOn(require("../diff/DiffStrategy"), "getDiffStrategy") - const [cline, task] = Cline.create( - mockProvider, - mockApiConfig, - "custom instructions", - true, - false, - undefined, - "test task", - ) + const [cline, task] = Cline.create({ + provider: mockProvider, + apiConfiguration: mockApiConfig, + customInstructions: "custom instructions", + enableDiff: true, + task: "test task", + }) expect(cline.diffEnabled).toBe(true) expect(cline.diffStrategy).toBeDefined() @@ -411,15 +405,7 @@ describe("Cline", () => { it("should require either task or historyItem", () => { expect(() => { - new Cline( - mockProvider, - mockApiConfig, - undefined, // customInstructions - false, // diffEnabled - false, // checkpointsEnabled - undefined, // fuzzyMatchThreshold - undefined, // task - ) + new Cline({ provider: mockProvider, apiConfiguration: mockApiConfig }) }).toThrow("Either historyItem or task/images must be provided") }) }) @@ -469,15 +455,11 @@ describe("Cline", () => { }) it("should include timezone information in environment details", async () => { - const [cline, task] = Cline.create( - mockProvider, - mockApiConfig, - undefined, - false, - false, - undefined, - "test task", - ) + const [cline, task] = Cline.create({ + provider: mockProvider, + apiConfiguration: mockApiConfig, + task: "test task", + }) const details = await cline["getEnvironmentDetails"](false) @@ -493,15 +475,12 @@ describe("Cline", () => { describe("API conversation handling", () => { it("should clean conversation history before sending to API", async () => { - const [cline, task] = Cline.create( - mockProvider, - mockApiConfig, - undefined, - false, - false, - undefined, - "test task", - ) + const [cline, task] = Cline.create({ + provider: mockProvider, + apiConfiguration: mockApiConfig, + task: "test task", + }) + cline.abandoned = true await task @@ -611,15 +590,11 @@ describe("Cline", () => { ] // Test with model that supports images - const [clineWithImages, taskWithImages] = Cline.create( - mockProvider, - configWithImages, - undefined, - false, - false, - undefined, - "test task", - ) + const [clineWithImages, taskWithImages] = Cline.create({ + provider: mockProvider, + apiConfiguration: configWithImages, + task: "test task", + }) // Mock the model info to indicate image support jest.spyOn(clineWithImages.api, "getModel").mockReturnValue({ @@ -638,15 +613,11 @@ describe("Cline", () => { clineWithImages.apiConversationHistory = conversationHistory // Test with model that doesn't support images - const [clineWithoutImages, taskWithoutImages] = Cline.create( - mockProvider, - configWithoutImages, - undefined, - false, - false, - undefined, - "test task", - ) + const [clineWithoutImages, taskWithoutImages] = Cline.create({ + provider: mockProvider, + apiConfiguration: configWithoutImages, + task: "test task", + }) // Mock the model info to indicate no image support jest.spyOn(clineWithoutImages.api, "getModel").mockReturnValue({ @@ -742,15 +713,11 @@ describe("Cline", () => { }) it.skip("should handle API retry with countdown", async () => { - const [cline, task] = Cline.create( - mockProvider, - mockApiConfig, - undefined, - false, - false, - undefined, - "test task", - ) + const [cline, task] = Cline.create({ + provider: mockProvider, + apiConfiguration: mockApiConfig, + task: "test task", + }) // Mock delay to track countdown timing const mockDelay = jest.fn().mockResolvedValue(undefined) @@ -870,15 +837,11 @@ describe("Cline", () => { }) it.skip("should not apply retry delay twice", async () => { - const [cline, task] = Cline.create( - mockProvider, - mockApiConfig, - undefined, - false, - false, - undefined, - "test task", - ) + const [cline, task] = Cline.create({ + provider: mockProvider, + apiConfiguration: mockApiConfig, + task: "test task", + }) // Mock delay to track countdown timing const mockDelay = jest.fn().mockResolvedValue(undefined) @@ -998,15 +961,11 @@ describe("Cline", () => { describe("loadContext", () => { it("should process mentions in task and feedback tags", async () => { - const [cline, task] = Cline.create( - mockProvider, - mockApiConfig, - undefined, - false, - false, - undefined, - "test task", - ) + const [cline, task] = Cline.create({ + provider: mockProvider, + apiConfiguration: mockApiConfig, + task: "test task", + }) // Mock parseMentions to track calls const mockParseMentions = jest.fn().mockImplementation((text) => `processed: ${text}`) diff --git a/src/core/webview/ClineProvider.ts b/src/core/webview/ClineProvider.ts index 05faa138342..6790224ecae 100644 --- a/src/core/webview/ClineProvider.ts +++ b/src/core/webview/ClineProvider.ts @@ -413,18 +413,17 @@ export class ClineProvider implements vscode.WebviewViewProvider { const modePrompt = customModePrompts?.[mode] as PromptComponent const effectiveInstructions = [globalInstructions, modePrompt?.customInstructions].filter(Boolean).join("\n\n") - this.cline = new Cline( - this, + this.cline = new Cline({ + provider: this, apiConfiguration, - effectiveInstructions, - diffEnabled, - checkpointsEnabled, + customInstructions: effectiveInstructions, + enableDiff: diffEnabled, + enableCheckpoints: checkpointsEnabled, fuzzyMatchThreshold, task, images, - undefined, experiments, - ) + }) } public async initClineWithHistoryItem(historyItem: HistoryItem) { @@ -444,18 +443,16 @@ export class ClineProvider implements vscode.WebviewViewProvider { const modePrompt = customModePrompts?.[mode] as PromptComponent const effectiveInstructions = [globalInstructions, modePrompt?.customInstructions].filter(Boolean).join("\n\n") - this.cline = new Cline( - this, + this.cline = new Cline({ + provider: this, apiConfiguration, - effectiveInstructions, - diffEnabled, - checkpointsEnabled, + customInstructions: effectiveInstructions, + enableDiff: diffEnabled, + enableCheckpoints: checkpointsEnabled, fuzzyMatchThreshold, - undefined, - undefined, historyItem, experiments, - ) + }) } public async postMessageToWebview(message: ExtensionMessage) { diff --git a/src/core/webview/__tests__/ClineProvider.test.ts b/src/core/webview/__tests__/ClineProvider.test.ts index 902e536d9c8..f8df84721da 100644 --- a/src/core/webview/__tests__/ClineProvider.test.ts +++ b/src/core/webview/__tests__/ClineProvider.test.ts @@ -690,19 +690,18 @@ describe("ClineProvider", () => { await provider.initClineWithTask("Test task") // Verify Cline was initialized with mode-specific instructions - expect(Cline).toHaveBeenCalledWith( + expect(Cline).toHaveBeenCalledWith({ provider, - mockApiConfig, - modeCustomInstructions, - true, - false, - 1.0, - "Test task", - undefined, - undefined, - experimentDefault, - ) + apiConfiguration: mockApiConfig, + customInstructions: modeCustomInstructions, + enableDiff: true, + enableCheckpoints: false, + fuzzyMatchThreshold: 1.0, + task: "Test task", + experiments: experimentDefault, + }) }) + test("handles mode-specific custom instructions updates", async () => { await provider.resolveWebviewView(mockWebviewView) const messageHandler = (mockWebviewView.webview.onDidReceiveMessage as jest.Mock).mock.calls[0][0] From 1922b8aea9eea349144bc820d5f3c170530c3dfe Mon Sep 17 00:00:00 2001 From: EMSHVAC Date: Mon, 24 Feb 2025 11:54:10 -0600 Subject: [PATCH 040/585] test: update HistoryView delete task test to handle confirmation dialog --- .../src/components/history/__tests__/HistoryView.test.tsx | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/webview-ui/src/components/history/__tests__/HistoryView.test.tsx b/webview-ui/src/components/history/__tests__/HistoryView.test.tsx index 7408b268786..12b0181af6b 100644 --- a/webview-ui/src/components/history/__tests__/HistoryView.test.tsx +++ b/webview-ui/src/components/history/__tests__/HistoryView.test.tsx @@ -135,7 +135,7 @@ describe("HistoryView", () => { }) }) - it("handles task deletion", () => { + it("handles task deletion", async () => { const onDone = jest.fn() render() @@ -143,9 +143,14 @@ describe("HistoryView", () => { const taskContainer = screen.getByTestId("virtuoso-item-1") fireEvent.mouseEnter(taskContainer) + // Click delete button to open confirmation dialog const deleteButton = within(taskContainer).getByTitle("Delete Task") fireEvent.click(deleteButton) + // Find and click the confirm delete button in the dialog + const confirmDeleteButton = screen.getByRole("button", { name: /delete/i }) + fireEvent.click(confirmDeleteButton) + // Verify vscode message was sent expect(vscode.postMessage).toHaveBeenCalledWith({ type: "deleteTaskWithId", From 39715f13e1f7078f11701d52a2a1d23fba8a93ea Mon Sep 17 00:00:00 2001 From: Catalin Lupuleti Date: Mon, 24 Feb 2025 19:05:05 +0000 Subject: [PATCH 041/585] Added support for Claude sonnet 3.7 via VertexAI --- src/shared/api.ts | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/shared/api.ts b/src/shared/api.ts index 9ecb12c1403..5bcf60138ef 100644 --- a/src/shared/api.ts +++ b/src/shared/api.ts @@ -396,6 +396,15 @@ export const openRouterDefaultModelInfo: ModelInfo = { export type VertexModelId = keyof typeof vertexModels export const vertexDefaultModelId: VertexModelId = "claude-3-5-sonnet-v2@20241022" export const vertexModels = { + "claude-3-7-sonnet@20250219": { + maxTokens: 8192, + contextWindow: 200_000, + supportsImages: true, + supportsComputerUse: true, + supportsPromptCache: false, + inputPrice: 3.0, + outputPrice: 15.0, + }, "claude-3-5-sonnet-v2@20241022": { maxTokens: 8192, contextWindow: 200_000, From 92ac610366ca4c21e3d237325c8bf3239888b30d Mon Sep 17 00:00:00 2001 From: cte Date: Mon, 24 Feb 2025 11:14:08 -0800 Subject: [PATCH 042/585] Add Claude 3.7 --- src/api/providers/anthropic.ts | 2 ++ src/api/providers/openrouter.ts | 1 + src/shared/api.ts | 11 +++++++++++ 3 files changed, 14 insertions(+) diff --git a/src/api/providers/anthropic.ts b/src/api/providers/anthropic.ts index 9a14756f5d2..4c62238f461 100644 --- a/src/api/providers/anthropic.ts +++ b/src/api/providers/anthropic.ts @@ -27,8 +27,10 @@ export class AnthropicHandler implements ApiHandler, SingleCompletionHandler { async *createMessage(systemPrompt: string, messages: Anthropic.Messages.MessageParam[]): ApiStream { let stream: AnthropicStream const modelId = this.getModel().id + switch (modelId) { // 'latest' alias does not support cache_control + case "claude-3-7-sonnet-20250219": case "claude-3-5-sonnet-20241022": case "claude-3-5-haiku-20241022": case "claude-3-opus-20240229": diff --git a/src/api/providers/openrouter.ts b/src/api/providers/openrouter.ts index 1fcf25260ef..af087226ebd 100644 --- a/src/api/providers/openrouter.ts +++ b/src/api/providers/openrouter.ts @@ -55,6 +55,7 @@ export class OpenRouterHandler implements ApiHandler, SingleCompletionHandler { // prompt caching: https://openrouter.ai/docs/prompt-caching // this is specifically for claude models (some models may 'support prompt caching' automatically without this) switch (this.getModel().id) { + case "anthropic/claude-3.7-sonnet": case "anthropic/claude-3.5-sonnet": case "anthropic/claude-3.5-sonnet:beta": case "anthropic/claude-3.5-sonnet-20240620": diff --git a/src/shared/api.ts b/src/shared/api.ts index 9ecb12c1403..3598eb16ac9 100644 --- a/src/shared/api.ts +++ b/src/shared/api.ts @@ -95,6 +95,17 @@ export interface ModelInfo { export type AnthropicModelId = keyof typeof anthropicModels export const anthropicDefaultModelId: AnthropicModelId = "claude-3-5-sonnet-20241022" export const anthropicModels = { + "claude-3-7-sonnet-20250219": { + maxTokens: 128_000, + contextWindow: 200_000, + supportsImages: true, + supportsComputerUse: true, + supportsPromptCache: true, + inputPrice: 3.0, // $3 per million input tokens + outputPrice: 15.0, // $15 per million output tokens + cacheWritesPrice: 3.75, // $3.75 per million tokens + cacheReadsPrice: 0.3, // $0.30 per million tokens + }, "claude-3-5-sonnet-20241022": { maxTokens: 8192, contextWindow: 200_000, From 08aa911e138a771d54f16a443074ca635f68bf90 Mon Sep 17 00:00:00 2001 From: Matt Rubens Date: Mon, 24 Feb 2025 13:25:33 -0600 Subject: [PATCH 043/585] Revert "Merge pull request #1125 from hannesrudolph/change_order_apply_diff" This reverts commit dddac90721719c5e54100e80205e30f3a4738b96, reversing changes made to 1c9dcaddc417b3ce48c2c39bf7ecc8484e1aa063. --- CHANGELOG.md | 2 +- src/core/Cline.ts | 34 +-- src/core/__tests__/mode-validator.test.ts | 40 +-- src/core/assistant-message/index.ts | 6 +- .../parse-assistant-message.ts | 4 +- .../strategies/__tests__/new-unified.test.ts | 2 +- .../__tests__/search-replace.test.ts | 4 +- .../diff/strategies/__tests__/unified.test.ts | 2 +- src/core/diff/strategies/new-unified/index.ts | 6 +- src/core/diff/strategies/search-replace.ts | 6 +- src/core/diff/strategies/unified.ts | 6 +- .../__snapshots__/system.test.ts.snap | 284 +++++++++--------- src/core/prompts/__tests__/sections.test.ts | 16 +- src/core/prompts/__tests__/system.test.ts | 12 +- src/core/prompts/sections/capabilities.ts | 2 +- src/core/prompts/sections/mcp-servers.ts | 2 +- src/core/prompts/sections/modes.ts | 2 +- src/core/prompts/sections/rules.ts | 23 +- src/core/prompts/tools/index.ts | 4 +- src/core/prompts/tools/write-to-file.ts | 10 +- .../webview/__tests__/ClineProvider.test.ts | 2 +- src/shared/ExtensionMessage.ts | 2 +- src/shared/__tests__/modes.test.ts | 66 ++-- src/shared/tool-groups.ts | 6 +- 24 files changed, 269 insertions(+), 274 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ff8cfdc3e85..748f8c78f88 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -479,7 +479,7 @@ Join us at https://www.reddit.com/r/RooCode to share your custom modes and be pa ## [2.1.14] - Fix bug where diffs were not being applied correctly and try Aider's [unified diff prompt](https://github.com/Aider-AI/aider/blob/3995accd0ca71cea90ef76d516837f8c2731b9fe/aider/coders/udiff_prompts.py#L75-L105) -- If diffs are enabled, automatically reject create_file commands that lead to truncated output +- If diffs are enabled, automatically reject write_to_file commands that lead to truncated output ## [2.1.13] diff --git a/src/core/Cline.ts b/src/core/Cline.ts index 522af873274..12cf062406b 100644 --- a/src/core/Cline.ts +++ b/src/core/Cline.ts @@ -733,7 +733,7 @@ export class Cline { text: `[TASK RESUMPTION] This task was interrupted ${agoText}. It may or may not be complete, so please reassess the task context. Be aware that the project state may have changed since then. The current working directory is now '${cwd.toPosix()}'. If the task has not been completed, retry the last step before interruption and proceed with completing the task.\n\nNote: If you previously attempted a tool use that the user did not provide a result for, you should assume the tool use was not successful and assess whether you should retry. If the last tool was a browser_action, the browser has been closed and you must launch a new browser if needed.${ wasRecent - ? "\n\nIMPORTANT: If the last tool use was a create_file that was interrupted, the file was reverted back to its original state before the interrupted edit, and you do NOT need to re-read the file as you already have its up-to-date contents." + ? "\n\nIMPORTANT: If the last tool use was a write_to_file that was interrupted, the file was reverted back to its original state before the interrupted edit, and you do NOT need to re-read the file as you already have its up-to-date contents." : "" }` + (responseText @@ -1141,9 +1141,9 @@ export class Cline { return `[${block.name} for '${block.params.command}']` case "read_file": return `[${block.name} for '${block.params.path}']` - case "create_file": + case "write_to_file": return `[${block.name} for '${block.params.path}']` - case "edit_file": + case "apply_diff": return `[${block.name} for '${block.params.path}']` case "search_files": return `[${block.name} for '${block.params.regex}'${ @@ -1295,7 +1295,7 @@ export class Cline { mode ?? defaultModeSlug, customModes ?? [], { - edit_file: this.diffEnabled, + apply_diff: this.diffEnabled, }, block.params, ) @@ -1306,7 +1306,7 @@ export class Cline { } switch (block.name) { - case "create_file": { + case "write_to_file": { const relPath: string | undefined = block.params.path let newContent: string | undefined = block.params.content let predictedLineCount: number | undefined = parseInt(block.params.line_count ?? "0") @@ -1371,20 +1371,20 @@ export class Cline { } else { if (!relPath) { this.consecutiveMistakeCount++ - pushToolResult(await this.sayAndCreateMissingParamError("create_file", "path")) + pushToolResult(await this.sayAndCreateMissingParamError("write_to_file", "path")) await this.diffViewProvider.reset() break } if (!newContent) { this.consecutiveMistakeCount++ - pushToolResult(await this.sayAndCreateMissingParamError("create_file", "content")) + pushToolResult(await this.sayAndCreateMissingParamError("write_to_file", "content")) await this.diffViewProvider.reset() break } if (!predictedLineCount) { this.consecutiveMistakeCount++ pushToolResult( - await this.sayAndCreateMissingParamError("create_file", "line_count"), + await this.sayAndCreateMissingParamError("write_to_file", "line_count"), ) await this.diffViewProvider.reset() break @@ -1421,7 +1421,7 @@ export class Cline { formatResponse.toolError( `Content appears to be truncated (file has ${ newContent.split("\n").length - } lines but was predicted to have ${predictedLineCount} lines), and found comments indicating omitted code (e.g., '// rest of code unchanged', '/* previous code */'). Please provide the complete file content without any omissions if possible, or otherwise use the 'edit_file' tool to apply the diff to the original file.`, + } lines but was predicted to have ${predictedLineCount} lines), and found comments indicating omitted code (e.g., '// rest of code unchanged', '/* previous code */'). Please provide the complete file content without any omissions if possible, or otherwise use the 'apply_diff' tool to apply the diff to the original file.`, ), ) break @@ -1497,7 +1497,7 @@ export class Cline { break } } - case "edit_file": { + case "apply_diff": { const relPath: string | undefined = block.params.path const diffContent: string | undefined = block.params.diff @@ -1515,12 +1515,12 @@ export class Cline { } else { if (!relPath) { this.consecutiveMistakeCount++ - pushToolResult(await this.sayAndCreateMissingParamError("edit_file", "path")) + pushToolResult(await this.sayAndCreateMissingParamError("apply_diff", "path")) break } if (!diffContent) { this.consecutiveMistakeCount++ - pushToolResult(await this.sayAndCreateMissingParamError("edit_file", "diff")) + pushToolResult(await this.sayAndCreateMissingParamError("apply_diff", "diff")) break } @@ -2233,7 +2233,7 @@ export class Cline { formatResponse.toolResult( `The browser action has been executed. The console logs and screenshot have been captured for your analysis.\n\nConsole logs:\n${ browserActionResult.logs || "(No new logs)" - }\n\n(REMEMBER: if you need to proceed to using non-\`browser_action\` tools or launch a new browser, you MUST first close this browser. For example, if after analyzing the logs and screenshot you need to edit a file, you must first close the browser before you can use the create_file tool.)`, + }\n\n(REMEMBER: if you need to proceed to using non-\`browser_action\` tools or launch a new browser, you MUST first close this browser. For example, if after analyzing the logs and screenshot you need to edit a file, you must first close the browser before you can use the write_to_file tool.)`, browserActionResult.screenshot ? [browserActionResult.screenshot] : [], ), ) @@ -2750,7 +2750,7 @@ export class Cline { /* Seeing out of bounds is fine, it means that the next too call is being built up and ready to add to assistantMessageContent to present. - When you see the UI inactive during this, it means that a tool is breaking without presenting any UI. For example the create_file tool was breaking when relpath was undefined, and for invalid relpath it never presented UI. + When you see the UI inactive during this, it means that a tool is breaking without presenting any UI. For example the write_to_file tool was breaking when relpath was undefined, and for invalid relpath it never presented UI. */ this.presentAssistantMessageLocked = false // this needs to be placed here, if not then calling this.presentAssistantMessage below would fail (sometimes) since it's locked // NOTE: when tool is rejected, iterator stream is interrupted and it waits for userMessageContentReady to be true. Future calls to present will skip execution since didRejectTool and iterate until contentIndex is set to message length and it sets userMessageContentReady to true itself (instead of preemptively doing it in iterator) @@ -3300,10 +3300,10 @@ export class Cline { // Add warning if not in code mode if ( - !isToolAllowedForMode("create_file", currentMode, customModes ?? [], { - edit_file: this.diffEnabled, + !isToolAllowedForMode("write_to_file", currentMode, customModes ?? [], { + apply_diff: this.diffEnabled, }) && - !isToolAllowedForMode("edit_file", currentMode, customModes ?? [], { edit_file: this.diffEnabled }) + !isToolAllowedForMode("apply_diff", currentMode, customModes ?? [], { apply_diff: this.diffEnabled }) ) { const currentModeName = getModeBySlug(currentMode, customModes)?.name ?? currentMode const defaultModeName = getModeBySlug(defaultModeSlug, customModes)?.name ?? defaultModeSlug diff --git a/src/core/__tests__/mode-validator.test.ts b/src/core/__tests__/mode-validator.test.ts index 4efcd06e3e7..632ca8a8ab0 100644 --- a/src/core/__tests__/mode-validator.test.ts +++ b/src/core/__tests__/mode-validator.test.ts @@ -59,7 +59,7 @@ describe("mode-validator", () => { ] // Should allow tools from read and edit groups expect(isToolAllowedForMode("read_file", "custom-mode", customModes)).toBe(true) - expect(isToolAllowedForMode("create_file", "custom-mode", customModes)).toBe(true) + expect(isToolAllowedForMode("write_to_file", "custom-mode", customModes)).toBe(true) // Should not allow tools from other groups expect(isToolAllowedForMode("execute_command", "custom-mode", customModes)).toBe(false) }) @@ -76,7 +76,7 @@ describe("mode-validator", () => { // Should allow tools from read group expect(isToolAllowedForMode("read_file", codeMode, customModes)).toBe(true) // Should not allow tools from other groups - expect(isToolAllowedForMode("create_file", codeMode, customModes)).toBe(false) + expect(isToolAllowedForMode("write_to_file", codeMode, customModes)).toBe(false) }) it("respects tool requirements in custom modes", () => { @@ -88,39 +88,39 @@ describe("mode-validator", () => { groups: ["edit"] as const, }, ] - const requirements = { edit_file: false } + const requirements = { apply_diff: false } // Should respect disabled requirement even if tool group is allowed - expect(isToolAllowedForMode("edit_file", "custom-mode", customModes, requirements)).toBe(false) + expect(isToolAllowedForMode("apply_diff", "custom-mode", customModes, requirements)).toBe(false) // Should allow other edit tools - expect(isToolAllowedForMode("create_file", "custom-mode", customModes, requirements)).toBe(true) + expect(isToolAllowedForMode("write_to_file", "custom-mode", customModes, requirements)).toBe(true) }) }) describe("tool requirements", () => { it("respects tool requirements when provided", () => { - const requirements = { edit_file: false } - expect(isToolAllowedForMode("edit_file", codeMode, [], requirements)).toBe(false) + const requirements = { apply_diff: false } + expect(isToolAllowedForMode("apply_diff", codeMode, [], requirements)).toBe(false) - const enabledRequirements = { edit_file: true } - expect(isToolAllowedForMode("edit_file", codeMode, [], enabledRequirements)).toBe(true) + const enabledRequirements = { apply_diff: true } + expect(isToolAllowedForMode("apply_diff", codeMode, [], enabledRequirements)).toBe(true) }) it("allows tools when their requirements are not specified", () => { const requirements = { some_other_tool: true } - expect(isToolAllowedForMode("edit_file", codeMode, [], requirements)).toBe(true) + expect(isToolAllowedForMode("apply_diff", codeMode, [], requirements)).toBe(true) }) it("handles undefined and empty requirements", () => { - expect(isToolAllowedForMode("edit_file", codeMode, [], undefined)).toBe(true) - expect(isToolAllowedForMode("edit_file", codeMode, [], {})).toBe(true) + expect(isToolAllowedForMode("apply_diff", codeMode, [], undefined)).toBe(true) + expect(isToolAllowedForMode("apply_diff", codeMode, [], {})).toBe(true) }) it("prioritizes requirements over mode configuration", () => { - const requirements = { edit_file: false } + const requirements = { apply_diff: false } // Even in code mode which allows all tools, disabled requirement should take precedence - expect(isToolAllowedForMode("edit_file", codeMode, [], requirements)).toBe(false) + expect(isToolAllowedForMode("apply_diff", codeMode, [], requirements)).toBe(false) }) }) }) @@ -137,19 +137,19 @@ describe("mode-validator", () => { }) it("throws error when tool requirement is not met", () => { - const requirements = { edit_file: false } - expect(() => validateToolUse("edit_file", codeMode, [], requirements)).toThrow( - 'Tool "edit_file" is not allowed in code mode.', + const requirements = { apply_diff: false } + expect(() => validateToolUse("apply_diff", codeMode, [], requirements)).toThrow( + 'Tool "apply_diff" is not allowed in code mode.', ) }) it("does not throw when tool requirement is met", () => { - const requirements = { edit_file: true } - expect(() => validateToolUse("edit_file", codeMode, [], requirements)).not.toThrow() + const requirements = { apply_diff: true } + expect(() => validateToolUse("apply_diff", codeMode, [], requirements)).not.toThrow() }) it("handles undefined requirements gracefully", () => { - expect(() => validateToolUse("edit_file", codeMode, [], undefined)).not.toThrow() + expect(() => validateToolUse("apply_diff", codeMode, [], undefined)).not.toThrow() }) }) }) diff --git a/src/core/assistant-message/index.ts b/src/core/assistant-message/index.ts index 46b29a703db..f1c49f85ab7 100644 --- a/src/core/assistant-message/index.ts +++ b/src/core/assistant-message/index.ts @@ -11,8 +11,8 @@ export interface TextContent { export const toolUseNames = [ "execute_command", "read_file", - "create_file", - "edit_file", + "write_to_file", + "apply_diff", "insert_content", "search_and_replace", "search_files", @@ -80,7 +80,7 @@ export interface ReadFileToolUse extends ToolUse { } export interface WriteToFileToolUse extends ToolUse { - name: "create_file" + name: "write_to_file" params: Partial, "path" | "content" | "line_count">> } diff --git a/src/core/assistant-message/parse-assistant-message.ts b/src/core/assistant-message/parse-assistant-message.ts index 9b1cea70a9b..e38e8f6458e 100644 --- a/src/core/assistant-message/parse-assistant-message.ts +++ b/src/core/assistant-message/parse-assistant-message.ts @@ -61,9 +61,9 @@ export function parseAssistantMessage(assistantMessage: string) { // there's no current param, and not starting a new param - // special case for create_file where file contents could contain the closing tag, in which case the param would have closed and we end up with the rest of the file contents here. To work around this, we get the string between the starting content tag and the LAST content tag. + // special case for write_to_file where file contents could contain the closing tag, in which case the param would have closed and we end up with the rest of the file contents here. To work around this, we get the string between the starting content tag and the LAST content tag. const contentParamName: ToolParamName = "content" - if (currentToolUse.name === "create_file" && accumulator.endsWith(``)) { + if (currentToolUse.name === "write_to_file" && accumulator.endsWith(``)) { const toolContent = accumulator.slice(currentToolUseStartIndex) const contentStartTag = `<${contentParamName}>` const contentEndTag = `` diff --git a/src/core/diff/strategies/__tests__/new-unified.test.ts b/src/core/diff/strategies/__tests__/new-unified.test.ts index 9d30cece7e4..8832f9e7c08 100644 --- a/src/core/diff/strategies/__tests__/new-unified.test.ts +++ b/src/core/diff/strategies/__tests__/new-unified.test.ts @@ -29,7 +29,7 @@ describe("main", () => { const cwd = "/test/path" const description = strategy.getToolDescription({ cwd }) - expect(description).toContain("edit_file Tool - Generate Precise Code Changes") + expect(description).toContain("apply_diff Tool - Generate Precise Code Changes") expect(description).toContain(cwd) expect(description).toContain("Step-by-Step Instructions") expect(description).toContain("Requirements") diff --git a/src/core/diff/strategies/__tests__/search-replace.test.ts b/src/core/diff/strategies/__tests__/search-replace.test.ts index 723beee23a7..cd71edac475 100644 --- a/src/core/diff/strategies/__tests__/search-replace.test.ts +++ b/src/core/diff/strategies/__tests__/search-replace.test.ts @@ -1544,8 +1544,8 @@ function two() { expect(description).toContain("<<<<<<< SEARCH") expect(description).toContain("=======") expect(description).toContain(">>>>>>> REPLACE") - expect(description).toContain("") - expect(description).toContain("") + expect(description).toContain("") + expect(description).toContain("") }) it("should document start_line and end_line parameters", async () => { diff --git a/src/core/diff/strategies/__tests__/unified.test.ts b/src/core/diff/strategies/__tests__/unified.test.ts index ae7860869bb..1d9847b3c51 100644 --- a/src/core/diff/strategies/__tests__/unified.test.ts +++ b/src/core/diff/strategies/__tests__/unified.test.ts @@ -12,7 +12,7 @@ describe("UnifiedDiffStrategy", () => { const cwd = "/test/path" const description = strategy.getToolDescription({ cwd }) - expect(description).toContain("edit_file") + expect(description).toContain("apply_diff") expect(description).toContain(cwd) expect(description).toContain("Parameters:") expect(description).toContain("Format Requirements:") diff --git a/src/core/diff/strategies/new-unified/index.ts b/src/core/diff/strategies/new-unified/index.ts index df130ffaca6..d82a05a1045 100644 --- a/src/core/diff/strategies/new-unified/index.ts +++ b/src/core/diff/strategies/new-unified/index.ts @@ -108,7 +108,7 @@ export class NewUnifiedDiffStrategy implements DiffStrategy { } getToolDescription(args: { cwd: string; toolOptions?: { [key: string]: string } }): string { - return `# edit_file Tool - Generate Precise Code Changes + return `# apply_diff Tool - Generate Precise Code Changes Generate a unified diff that can be cleanly applied to modify code files. @@ -168,12 +168,12 @@ Parameters: - diff: (required) Unified diff content in unified format to apply to the file. Usage: - + path/to/file.ext Your diff here -` +` } // Helper function to split a hunk into smaller hunks based on contiguous changes diff --git a/src/core/diff/strategies/search-replace.ts b/src/core/diff/strategies/search-replace.ts index c8d4f22c8d1..a9bf46758de 100644 --- a/src/core/diff/strategies/search-replace.ts +++ b/src/core/diff/strategies/search-replace.ts @@ -40,7 +40,7 @@ export class SearchReplaceDiffStrategy implements DiffStrategy { } getToolDescription(args: { cwd: string; toolOptions?: { [key: string]: string } }): string { - return `## edit_file + return `## apply_diff Description: Request to replace existing code using a search and replace block. This tool allows for precise, surgical replaces to files by specifying exactly what content to search for and what to replace it with. The tool will maintain proper indentation and formatting while making changes. @@ -91,14 +91,14 @@ def calculate_total(items): \`\`\` Usage: - + File path here Your search/replace content here 1 5 -` +` } async applyDiff( diff --git a/src/core/diff/strategies/unified.ts b/src/core/diff/strategies/unified.ts index 5947391df69..f1cdb3b5849 100644 --- a/src/core/diff/strategies/unified.ts +++ b/src/core/diff/strategies/unified.ts @@ -3,7 +3,7 @@ import { DiffStrategy, DiffResult } from "../types" export class UnifiedDiffStrategy implements DiffStrategy { getToolDescription(args: { cwd: string; toolOptions?: { [key: string]: string } }): string { - return `## edit_file + return `## apply_diff Description: Apply a unified diff to a file at the specified path. This tool is useful when you need to make specific modifications to a file based on a set of changes provided in unified diff format (diff -U3). Parameters: @@ -100,12 +100,12 @@ Best Practices: 4. Verify line numbers match the line numbers you have in the file Usage: - + File path here Your diff here -` +` } async applyDiff(originalContent: string, diffContent: string): Promise { diff --git a/src/core/prompts/__tests__/__snapshots__/system.test.ts.snap b/src/core/prompts/__tests__/__snapshots__/system.test.ts.snap index e4447d31eef..2abc6138619 100644 --- a/src/core/prompts/__tests__/__snapshots__/system.test.ts.snap +++ b/src/core/prompts/__tests__/__snapshots__/system.test.ts.snap @@ -94,23 +94,23 @@ Example: Requesting to list all top level source code definitions in the current . -## create_file +## write_to_file Description: Request to write full content to a file at the specified path. If the file exists, it will be overwritten with the provided content. If the file doesn't exist, it will be created. This tool will automatically create any directories needed to write the file. Parameters: - path: (required) The path of the file to write to (relative to the current working directory /test/path) - content: (required) The content to write to the file. ALWAYS provide the COMPLETE intended content of the file, without any truncation or omissions. You MUST include ALL parts of the file, even if they haven't been modified. Do NOT include the line numbers in the content though, just the actual content of the file. - line_count: (required) The number of lines in the file. Make sure to compute this based on the actual content of the file, not the number of lines in the content you're providing. Usage: - + File path here Your file content here total number of lines in the file, including empty lines - + Example: Requesting to write to frontend-config.json - + frontend-config.json { @@ -129,7 +129,7 @@ Example: Requesting to write to frontend-config.json } 14 - + ## execute_command Description: Request to execute a CLI command on the system. Use this when you need to perform system operations or run specific commands to accomplish any step in the user's task. You must tailor your command to the user's system and provide a clear explanation of what the command does. For command chaining, use the appropriate chaining syntax for the user's shell. Prefer to execute complex CLI commands over creating executable scripts, as they are more flexible and easier to run. Commands will be executed in the current working directory: /test/path @@ -249,7 +249,7 @@ CAPABILITIES - When the user initially gives you a task, a recursive list of all filepaths in the current working directory ('/test/path') will be included in environment_details. This provides an overview of the project's file structure, offering key insights into the project from directory/file names (how developers conceptualize and organize their code) and file extensions (the language used). This can also guide decision-making on which files to explore further. If you need to further explore directories such as outside the current working directory, you can use the list_files tool. If you pass 'true' for the recursive parameter, it will list files recursively. Otherwise, it will list files at the top level, which is better suited for generic directories where you don't necessarily need the nested structure, like the Desktop. - You can use search_files to perform regex searches across files in a specified directory, outputting context-rich results that include surrounding lines. This is particularly useful for understanding code patterns, finding specific implementations, or identifying areas that need refactoring. - You can use the list_code_definition_names tool to get an overview of source code definitions for all files at the top level of a specified directory. This can be particularly useful when you need to understand the broader context and relationships between certain parts of the code. You may need to call this tool multiple times to understand various parts of the codebase related to the task. - - For example, when asked to make edits or improvements you might analyze the file structure in the initial environment_details to get an overview of the project, then use list_code_definition_names to get further insight using source code definitions for files located in relevant directories, then read_file to examine the contents of relevant files, analyze the code and suggest improvements or make necessary edits, then use the create_file tool to apply the changes. If you refactored code that could affect other parts of the codebase, you could use search_files to ensure you update other files as needed. + - For example, when asked to make edits or improvements you might analyze the file structure in the initial environment_details to get an overview of the project, then use list_code_definition_names to get further insight using source code definitions for files located in relevant directories, then read_file to examine the contents of relevant files, analyze the code and suggest improvements or make necessary edits, then use the write_to_file tool to apply the changes. If you refactored code that could affect other parts of the codebase, you could use search_files to ensure you update other files as needed. - You can use the execute_command tool to run commands on the user's computer whenever you feel it can help accomplish the user's task. When you need to execute a CLI command, you must provide a clear explanation of what the command does. Prefer to execute complex CLI commands over creating executable scripts, since they are more flexible and easier to run. Interactive and long-running commands are allowed, since the commands are run in the user's VSCode terminal. The user may keep commands running in the background and you will be kept updated on their status along the way. Each command you execute is run in a new terminal instance. ==== @@ -266,9 +266,9 @@ RULES - You cannot \`cd\` into a different directory to complete a task. You are stuck operating from '/test/path', so be sure to pass in the correct 'path' parameter when using tools that require a path. - Do not use the ~ character or $HOME to refer to the home directory. - Before using the execute_command tool, you must first think about the SYSTEM INFORMATION context provided to understand the user's environment and tailor your commands to ensure they are compatible with their system. You must also consider if the command you need to run should be executed in a specific directory outside of the current working directory '/test/path', and if so prepend with \`cd\`'ing into that directory && then executing the command (as one command since you are stuck operating from '/test/path'). For example, if you needed to run \`npm install\` in a project outside of '/test/path', you would need to prepend with a \`cd\` i.e. pseudocode for this would be \`cd (path to project) && (command, in this case npm install)\`. -- When using the search_files tool, craft your regex patterns carefully to balance specificity and flexibility. Based on the user's task you may use it to find code patterns, TODO comments, function definitions, or any text-based information across the project. The results include context, so analyze the surrounding code to better understand the matches. Leverage the search_files tool in combination with other tools for more comprehensive analysis. For example, use it to find specific code patterns, then use read_file to examine the full context of interesting matches before using create_file to make informed changes. -- When creating a new project (such as an app, website, or any software project), organize all new files within a dedicated project directory unless the user specifies otherwise. Use appropriate file paths when writing files, as the create_file tool will automatically create any necessary directories. Structure the project logically, adhering to best practices for the specific type of project being created. Unless otherwise specified, new projects should be easily run without additional setup, for example most projects can be built in HTML, CSS, and JavaScript - which you can open in a browser. -- When using the create_file tool to modify a file, use the tool directly with the desired content. You do not need to display the content before using the tool. ALWAYS provide the COMPLETE file content in your response. This is NON-NEGOTIABLE. Partial updates or placeholders like '// rest of code unchanged' are STRICTLY FORBIDDEN. You MUST include ALL parts of the file, even if they haven't been modified. Failure to do so will result in incomplete or broken code, severely impacting the user's project. +- When using the search_files tool, craft your regex patterns carefully to balance specificity and flexibility. Based on the user's task you may use it to find code patterns, TODO comments, function definitions, or any text-based information across the project. The results include context, so analyze the surrounding code to better understand the matches. Leverage the search_files tool in combination with other tools for more comprehensive analysis. For example, use it to find specific code patterns, then use read_file to examine the full context of interesting matches before using write_to_file to make informed changes. +- When creating a new project (such as an app, website, or any software project), organize all new files within a dedicated project directory unless the user specifies otherwise. Use appropriate file paths when writing files, as the write_to_file tool will automatically create any necessary directories. Structure the project logically, adhering to best practices for the specific type of project being created. Unless otherwise specified, new projects should be easily run without additional setup, for example most projects can be built in HTML, CSS, and JavaScript - which you can open in a browser. +- When using the write_to_file tool to modify a file, use the tool directly with the desired content. You do not need to display the content before using the tool. ALWAYS provide the COMPLETE file content in your response. This is NON-NEGOTIABLE. Partial updates or placeholders like '// rest of code unchanged' are STRICTLY FORBIDDEN. You MUST include ALL parts of the file, even if they haven't been modified. Failure to do so will result in incomplete or broken code, severely impacting the user's project. - Some modes have restrictions on which files they can edit. If you attempt to edit a restricted file, the operation will be rejected with a FileRestrictionError that will specify which file patterns are allowed for the current mode. - Be sure to consider the type of project (e.g. Python, JavaScript, web application) when determining the appropriate structure and files to include. Also consider what files may be most relevant to accomplishing the task, for example looking at a project's manifest file would help you understand the project's dependencies, which you could incorporate into any code you write. * For example, in architect mode trying to edit app.js would be rejected because architect mode can only edit files matching "\\.md$" @@ -417,23 +417,23 @@ Example: Requesting to list all top level source code definitions in the current . -## create_file +## write_to_file Description: Request to write full content to a file at the specified path. If the file exists, it will be overwritten with the provided content. If the file doesn't exist, it will be created. This tool will automatically create any directories needed to write the file. Parameters: - path: (required) The path of the file to write to (relative to the current working directory /test/path) - content: (required) The content to write to the file. ALWAYS provide the COMPLETE intended content of the file, without any truncation or omissions. You MUST include ALL parts of the file, even if they haven't been modified. Do NOT include the line numbers in the content though, just the actual content of the file. - line_count: (required) The number of lines in the file. Make sure to compute this based on the actual content of the file, not the number of lines in the content you're providing. Usage: - + File path here Your file content here total number of lines in the file, including empty lines - + Example: Requesting to write to frontend-config.json - + frontend-config.json { @@ -452,7 +452,7 @@ Example: Requesting to write to frontend-config.json } 14 - + ## execute_command Description: Request to execute a CLI command on the system. Use this when you need to perform system operations or run specific commands to accomplish any step in the user's task. You must tailor your command to the user's system and provide a clear explanation of what the command does. For command chaining, use the appropriate chaining syntax for the user's shell. Prefer to execute complex CLI commands over creating executable scripts, as they are more flexible and easier to run. Commands will be executed in the current working directory: /test/path @@ -572,7 +572,7 @@ CAPABILITIES - When the user initially gives you a task, a recursive list of all filepaths in the current working directory ('/test/path') will be included in environment_details. This provides an overview of the project's file structure, offering key insights into the project from directory/file names (how developers conceptualize and organize their code) and file extensions (the language used). This can also guide decision-making on which files to explore further. If you need to further explore directories such as outside the current working directory, you can use the list_files tool. If you pass 'true' for the recursive parameter, it will list files recursively. Otherwise, it will list files at the top level, which is better suited for generic directories where you don't necessarily need the nested structure, like the Desktop. - You can use search_files to perform regex searches across files in a specified directory, outputting context-rich results that include surrounding lines. This is particularly useful for understanding code patterns, finding specific implementations, or identifying areas that need refactoring. - You can use the list_code_definition_names tool to get an overview of source code definitions for all files at the top level of a specified directory. This can be particularly useful when you need to understand the broader context and relationships between certain parts of the code. You may need to call this tool multiple times to understand various parts of the codebase related to the task. - - For example, when asked to make edits or improvements you might analyze the file structure in the initial environment_details to get an overview of the project, then use list_code_definition_names to get further insight using source code definitions for files located in relevant directories, then read_file to examine the contents of relevant files, analyze the code and suggest improvements or make necessary edits, then use the create_file tool to apply the changes. If you refactored code that could affect other parts of the codebase, you could use search_files to ensure you update other files as needed. + - For example, when asked to make edits or improvements you might analyze the file structure in the initial environment_details to get an overview of the project, then use list_code_definition_names to get further insight using source code definitions for files located in relevant directories, then read_file to examine the contents of relevant files, analyze the code and suggest improvements or make necessary edits, then use the write_to_file tool to apply the changes. If you refactored code that could affect other parts of the codebase, you could use search_files to ensure you update other files as needed. - You can use the execute_command tool to run commands on the user's computer whenever you feel it can help accomplish the user's task. When you need to execute a CLI command, you must provide a clear explanation of what the command does. Prefer to execute complex CLI commands over creating executable scripts, since they are more flexible and easier to run. Interactive and long-running commands are allowed, since the commands are run in the user's VSCode terminal. The user may keep commands running in the background and you will be kept updated on their status along the way. Each command you execute is run in a new terminal instance. ==== @@ -589,9 +589,9 @@ RULES - You cannot \`cd\` into a different directory to complete a task. You are stuck operating from '/test/path', so be sure to pass in the correct 'path' parameter when using tools that require a path. - Do not use the ~ character or $HOME to refer to the home directory. - Before using the execute_command tool, you must first think about the SYSTEM INFORMATION context provided to understand the user's environment and tailor your commands to ensure they are compatible with their system. You must also consider if the command you need to run should be executed in a specific directory outside of the current working directory '/test/path', and if so prepend with \`cd\`'ing into that directory && then executing the command (as one command since you are stuck operating from '/test/path'). For example, if you needed to run \`npm install\` in a project outside of '/test/path', you would need to prepend with a \`cd\` i.e. pseudocode for this would be \`cd (path to project) && (command, in this case npm install)\`. -- When using the search_files tool, craft your regex patterns carefully to balance specificity and flexibility. Based on the user's task you may use it to find code patterns, TODO comments, function definitions, or any text-based information across the project. The results include context, so analyze the surrounding code to better understand the matches. Leverage the search_files tool in combination with other tools for more comprehensive analysis. For example, use it to find specific code patterns, then use read_file to examine the full context of interesting matches before using create_file to make informed changes. -- When creating a new project (such as an app, website, or any software project), organize all new files within a dedicated project directory unless the user specifies otherwise. Use appropriate file paths when writing files, as the create_file tool will automatically create any necessary directories. Structure the project logically, adhering to best practices for the specific type of project being created. Unless otherwise specified, new projects should be easily run without additional setup, for example most projects can be built in HTML, CSS, and JavaScript - which you can open in a browser. -- When using the create_file tool to modify a file, use the tool directly with the desired content. You do not need to display the content before using the tool. ALWAYS provide the COMPLETE file content in your response. This is NON-NEGOTIABLE. Partial updates or placeholders like '// rest of code unchanged' are STRICTLY FORBIDDEN. You MUST include ALL parts of the file, even if they haven't been modified. Failure to do so will result in incomplete or broken code, severely impacting the user's project. +- When using the search_files tool, craft your regex patterns carefully to balance specificity and flexibility. Based on the user's task you may use it to find code patterns, TODO comments, function definitions, or any text-based information across the project. The results include context, so analyze the surrounding code to better understand the matches. Leverage the search_files tool in combination with other tools for more comprehensive analysis. For example, use it to find specific code patterns, then use read_file to examine the full context of interesting matches before using write_to_file to make informed changes. +- When creating a new project (such as an app, website, or any software project), organize all new files within a dedicated project directory unless the user specifies otherwise. Use appropriate file paths when writing files, as the write_to_file tool will automatically create any necessary directories. Structure the project logically, adhering to best practices for the specific type of project being created. Unless otherwise specified, new projects should be easily run without additional setup, for example most projects can be built in HTML, CSS, and JavaScript - which you can open in a browser. +- When using the write_to_file tool to modify a file, use the tool directly with the desired content. You do not need to display the content before using the tool. ALWAYS provide the COMPLETE file content in your response. This is NON-NEGOTIABLE. Partial updates or placeholders like '// rest of code unchanged' are STRICTLY FORBIDDEN. You MUST include ALL parts of the file, even if they haven't been modified. Failure to do so will result in incomplete or broken code, severely impacting the user's project. - Some modes have restrictions on which files they can edit. If you attempt to edit a restricted file, the operation will be rejected with a FileRestrictionError that will specify which file patterns are allowed for the current mode. - Be sure to consider the type of project (e.g. Python, JavaScript, web application) when determining the appropriate structure and files to include. Also consider what files may be most relevant to accomplishing the task, for example looking at a project's manifest file would help you understand the project's dependencies, which you could incorporate into any code you write. * For example, in architect mode trying to edit app.js would be rejected because architect mode can only edit files matching "\\.md$" @@ -740,23 +740,23 @@ Example: Requesting to list all top level source code definitions in the current . -## create_file +## write_to_file Description: Request to write full content to a file at the specified path. If the file exists, it will be overwritten with the provided content. If the file doesn't exist, it will be created. This tool will automatically create any directories needed to write the file. Parameters: - path: (required) The path of the file to write to (relative to the current working directory /test/path) - content: (required) The content to write to the file. ALWAYS provide the COMPLETE intended content of the file, without any truncation or omissions. You MUST include ALL parts of the file, even if they haven't been modified. Do NOT include the line numbers in the content though, just the actual content of the file. - line_count: (required) The number of lines in the file. Make sure to compute this based on the actual content of the file, not the number of lines in the content you're providing. Usage: - + File path here Your file content here total number of lines in the file, including empty lines - + Example: Requesting to write to frontend-config.json - + frontend-config.json { @@ -775,7 +775,7 @@ Example: Requesting to write to frontend-config.json } 14 - + ## execute_command Description: Request to execute a CLI command on the system. Use this when you need to perform system operations or run specific commands to accomplish any step in the user's task. You must tailor your command to the user's system and provide a clear explanation of what the command does. For command chaining, use the appropriate chaining syntax for the user's shell. Prefer to execute complex CLI commands over creating executable scripts, as they are more flexible and easier to run. Commands will be executed in the current working directory: /test/path @@ -895,7 +895,7 @@ CAPABILITIES - When the user initially gives you a task, a recursive list of all filepaths in the current working directory ('/test/path') will be included in environment_details. This provides an overview of the project's file structure, offering key insights into the project from directory/file names (how developers conceptualize and organize their code) and file extensions (the language used). This can also guide decision-making on which files to explore further. If you need to further explore directories such as outside the current working directory, you can use the list_files tool. If you pass 'true' for the recursive parameter, it will list files recursively. Otherwise, it will list files at the top level, which is better suited for generic directories where you don't necessarily need the nested structure, like the Desktop. - You can use search_files to perform regex searches across files in a specified directory, outputting context-rich results that include surrounding lines. This is particularly useful for understanding code patterns, finding specific implementations, or identifying areas that need refactoring. - You can use the list_code_definition_names tool to get an overview of source code definitions for all files at the top level of a specified directory. This can be particularly useful when you need to understand the broader context and relationships between certain parts of the code. You may need to call this tool multiple times to understand various parts of the codebase related to the task. - - For example, when asked to make edits or improvements you might analyze the file structure in the initial environment_details to get an overview of the project, then use list_code_definition_names to get further insight using source code definitions for files located in relevant directories, then read_file to examine the contents of relevant files, analyze the code and suggest improvements or make necessary edits, then use the create_file tool to apply the changes. If you refactored code that could affect other parts of the codebase, you could use search_files to ensure you update other files as needed. + - For example, when asked to make edits or improvements you might analyze the file structure in the initial environment_details to get an overview of the project, then use list_code_definition_names to get further insight using source code definitions for files located in relevant directories, then read_file to examine the contents of relevant files, analyze the code and suggest improvements or make necessary edits, then use the write_to_file tool to apply the changes. If you refactored code that could affect other parts of the codebase, you could use search_files to ensure you update other files as needed. - You can use the execute_command tool to run commands on the user's computer whenever you feel it can help accomplish the user's task. When you need to execute a CLI command, you must provide a clear explanation of what the command does. Prefer to execute complex CLI commands over creating executable scripts, since they are more flexible and easier to run. Interactive and long-running commands are allowed, since the commands are run in the user's VSCode terminal. The user may keep commands running in the background and you will be kept updated on their status along the way. Each command you execute is run in a new terminal instance. ==== @@ -912,9 +912,9 @@ RULES - You cannot \`cd\` into a different directory to complete a task. You are stuck operating from '/test/path', so be sure to pass in the correct 'path' parameter when using tools that require a path. - Do not use the ~ character or $HOME to refer to the home directory. - Before using the execute_command tool, you must first think about the SYSTEM INFORMATION context provided to understand the user's environment and tailor your commands to ensure they are compatible with their system. You must also consider if the command you need to run should be executed in a specific directory outside of the current working directory '/test/path', and if so prepend with \`cd\`'ing into that directory && then executing the command (as one command since you are stuck operating from '/test/path'). For example, if you needed to run \`npm install\` in a project outside of '/test/path', you would need to prepend with a \`cd\` i.e. pseudocode for this would be \`cd (path to project) && (command, in this case npm install)\`. -- When using the search_files tool, craft your regex patterns carefully to balance specificity and flexibility. Based on the user's task you may use it to find code patterns, TODO comments, function definitions, or any text-based information across the project. The results include context, so analyze the surrounding code to better understand the matches. Leverage the search_files tool in combination with other tools for more comprehensive analysis. For example, use it to find specific code patterns, then use read_file to examine the full context of interesting matches before using create_file to make informed changes. -- When creating a new project (such as an app, website, or any software project), organize all new files within a dedicated project directory unless the user specifies otherwise. Use appropriate file paths when writing files, as the create_file tool will automatically create any necessary directories. Structure the project logically, adhering to best practices for the specific type of project being created. Unless otherwise specified, new projects should be easily run without additional setup, for example most projects can be built in HTML, CSS, and JavaScript - which you can open in a browser. -- When using the create_file tool to modify a file, use the tool directly with the desired content. You do not need to display the content before using the tool. ALWAYS provide the COMPLETE file content in your response. This is NON-NEGOTIABLE. Partial updates or placeholders like '// rest of code unchanged' are STRICTLY FORBIDDEN. You MUST include ALL parts of the file, even if they haven't been modified. Failure to do so will result in incomplete or broken code, severely impacting the user's project. +- When using the search_files tool, craft your regex patterns carefully to balance specificity and flexibility. Based on the user's task you may use it to find code patterns, TODO comments, function definitions, or any text-based information across the project. The results include context, so analyze the surrounding code to better understand the matches. Leverage the search_files tool in combination with other tools for more comprehensive analysis. For example, use it to find specific code patterns, then use read_file to examine the full context of interesting matches before using write_to_file to make informed changes. +- When creating a new project (such as an app, website, or any software project), organize all new files within a dedicated project directory unless the user specifies otherwise. Use appropriate file paths when writing files, as the write_to_file tool will automatically create any necessary directories. Structure the project logically, adhering to best practices for the specific type of project being created. Unless otherwise specified, new projects should be easily run without additional setup, for example most projects can be built in HTML, CSS, and JavaScript - which you can open in a browser. +- When using the write_to_file tool to modify a file, use the tool directly with the desired content. You do not need to display the content before using the tool. ALWAYS provide the COMPLETE file content in your response. This is NON-NEGOTIABLE. Partial updates or placeholders like '// rest of code unchanged' are STRICTLY FORBIDDEN. You MUST include ALL parts of the file, even if they haven't been modified. Failure to do so will result in incomplete or broken code, severely impacting the user's project. - Some modes have restrictions on which files they can edit. If you attempt to edit a restricted file, the operation will be rejected with a FileRestrictionError that will specify which file patterns are allowed for the current mode. - Be sure to consider the type of project (e.g. Python, JavaScript, web application) when determining the appropriate structure and files to include. Also consider what files may be most relevant to accomplishing the task, for example looking at a project's manifest file would help you understand the project's dependencies, which you could incorporate into any code you write. * For example, in architect mode trying to edit app.js would be rejected because architect mode can only edit files matching "\\.md$" @@ -1063,23 +1063,23 @@ Example: Requesting to list all top level source code definitions in the current . -## create_file +## write_to_file Description: Request to write full content to a file at the specified path. If the file exists, it will be overwritten with the provided content. If the file doesn't exist, it will be created. This tool will automatically create any directories needed to write the file. Parameters: - path: (required) The path of the file to write to (relative to the current working directory /test/path) - content: (required) The content to write to the file. ALWAYS provide the COMPLETE intended content of the file, without any truncation or omissions. You MUST include ALL parts of the file, even if they haven't been modified. Do NOT include the line numbers in the content though, just the actual content of the file. - line_count: (required) The number of lines in the file. Make sure to compute this based on the actual content of the file, not the number of lines in the content you're providing. Usage: - + File path here Your file content here total number of lines in the file, including empty lines - + Example: Requesting to write to frontend-config.json - + frontend-config.json { @@ -1098,7 +1098,7 @@ Example: Requesting to write to frontend-config.json } 14 - + ## browser_action Description: Request to interact with a Puppeteer-controlled browser. Every action, except \`close\`, will be responded to with a screenshot of the browser's current state, along with any new console logs. You may only perform one browser action per message, and wait for the user's response including a screenshot and logs to determine the next action. @@ -1264,7 +1264,7 @@ CAPABILITIES - When the user initially gives you a task, a recursive list of all filepaths in the current working directory ('/test/path') will be included in environment_details. This provides an overview of the project's file structure, offering key insights into the project from directory/file names (how developers conceptualize and organize their code) and file extensions (the language used). This can also guide decision-making on which files to explore further. If you need to further explore directories such as outside the current working directory, you can use the list_files tool. If you pass 'true' for the recursive parameter, it will list files recursively. Otherwise, it will list files at the top level, which is better suited for generic directories where you don't necessarily need the nested structure, like the Desktop. - You can use search_files to perform regex searches across files in a specified directory, outputting context-rich results that include surrounding lines. This is particularly useful for understanding code patterns, finding specific implementations, or identifying areas that need refactoring. - You can use the list_code_definition_names tool to get an overview of source code definitions for all files at the top level of a specified directory. This can be particularly useful when you need to understand the broader context and relationships between certain parts of the code. You may need to call this tool multiple times to understand various parts of the codebase related to the task. - - For example, when asked to make edits or improvements you might analyze the file structure in the initial environment_details to get an overview of the project, then use list_code_definition_names to get further insight using source code definitions for files located in relevant directories, then read_file to examine the contents of relevant files, analyze the code and suggest improvements or make necessary edits, then use the create_file tool to apply the changes. If you refactored code that could affect other parts of the codebase, you could use search_files to ensure you update other files as needed. + - For example, when asked to make edits or improvements you might analyze the file structure in the initial environment_details to get an overview of the project, then use list_code_definition_names to get further insight using source code definitions for files located in relevant directories, then read_file to examine the contents of relevant files, analyze the code and suggest improvements or make necessary edits, then use the write_to_file tool to apply the changes. If you refactored code that could affect other parts of the codebase, you could use search_files to ensure you update other files as needed. - You can use the execute_command tool to run commands on the user's computer whenever you feel it can help accomplish the user's task. When you need to execute a CLI command, you must provide a clear explanation of what the command does. Prefer to execute complex CLI commands over creating executable scripts, since they are more flexible and easier to run. Interactive and long-running commands are allowed, since the commands are run in the user's VSCode terminal. The user may keep commands running in the background and you will be kept updated on their status along the way. Each command you execute is run in a new terminal instance. - You can use the browser_action tool to interact with websites (including html files and locally running development servers) through a Puppeteer-controlled browser when you feel it is necessary in accomplishing the user's task. This tool is particularly useful for web development tasks as it allows you to launch a browser, navigate to pages, interact with elements through clicks and keyboard input, and capture the results through screenshots and console logs. This tool may be useful at key stages of web development tasks-such as after implementing new features, making substantial changes, when troubleshooting issues, or to verify the result of your work. You can analyze the provided screenshots to ensure correct rendering or identify errors, and review console logs for runtime issues. - For example, if asked to add a component to a react website, you might create the necessary files, use execute_command to run the site locally, then use browser_action to launch the browser, navigate to the local server, and verify the component renders & functions correctly before closing the browser. @@ -1283,9 +1283,9 @@ RULES - You cannot \`cd\` into a different directory to complete a task. You are stuck operating from '/test/path', so be sure to pass in the correct 'path' parameter when using tools that require a path. - Do not use the ~ character or $HOME to refer to the home directory. - Before using the execute_command tool, you must first think about the SYSTEM INFORMATION context provided to understand the user's environment and tailor your commands to ensure they are compatible with their system. You must also consider if the command you need to run should be executed in a specific directory outside of the current working directory '/test/path', and if so prepend with \`cd\`'ing into that directory && then executing the command (as one command since you are stuck operating from '/test/path'). For example, if you needed to run \`npm install\` in a project outside of '/test/path', you would need to prepend with a \`cd\` i.e. pseudocode for this would be \`cd (path to project) && (command, in this case npm install)\`. -- When using the search_files tool, craft your regex patterns carefully to balance specificity and flexibility. Based on the user's task you may use it to find code patterns, TODO comments, function definitions, or any text-based information across the project. The results include context, so analyze the surrounding code to better understand the matches. Leverage the search_files tool in combination with other tools for more comprehensive analysis. For example, use it to find specific code patterns, then use read_file to examine the full context of interesting matches before using create_file to make informed changes. -- When creating a new project (such as an app, website, or any software project), organize all new files within a dedicated project directory unless the user specifies otherwise. Use appropriate file paths when writing files, as the create_file tool will automatically create any necessary directories. Structure the project logically, adhering to best practices for the specific type of project being created. Unless otherwise specified, new projects should be easily run without additional setup, for example most projects can be built in HTML, CSS, and JavaScript - which you can open in a browser. -- When using the create_file tool to modify a file, use the tool directly with the desired content. You do not need to display the content before using the tool. ALWAYS provide the COMPLETE file content in your response. This is NON-NEGOTIABLE. Partial updates or placeholders like '// rest of code unchanged' are STRICTLY FORBIDDEN. You MUST include ALL parts of the file, even if they haven't been modified. Failure to do so will result in incomplete or broken code, severely impacting the user's project. +- When using the search_files tool, craft your regex patterns carefully to balance specificity and flexibility. Based on the user's task you may use it to find code patterns, TODO comments, function definitions, or any text-based information across the project. The results include context, so analyze the surrounding code to better understand the matches. Leverage the search_files tool in combination with other tools for more comprehensive analysis. For example, use it to find specific code patterns, then use read_file to examine the full context of interesting matches before using write_to_file to make informed changes. +- When creating a new project (such as an app, website, or any software project), organize all new files within a dedicated project directory unless the user specifies otherwise. Use appropriate file paths when writing files, as the write_to_file tool will automatically create any necessary directories. Structure the project logically, adhering to best practices for the specific type of project being created. Unless otherwise specified, new projects should be easily run without additional setup, for example most projects can be built in HTML, CSS, and JavaScript - which you can open in a browser. +- When using the write_to_file tool to modify a file, use the tool directly with the desired content. You do not need to display the content before using the tool. ALWAYS provide the COMPLETE file content in your response. This is NON-NEGOTIABLE. Partial updates or placeholders like '// rest of code unchanged' are STRICTLY FORBIDDEN. You MUST include ALL parts of the file, even if they haven't been modified. Failure to do so will result in incomplete or broken code, severely impacting the user's project. - Some modes have restrictions on which files they can edit. If you attempt to edit a restricted file, the operation will be rejected with a FileRestrictionError that will specify which file patterns are allowed for the current mode. - Be sure to consider the type of project (e.g. Python, JavaScript, web application) when determining the appropriate structure and files to include. Also consider what files may be most relevant to accomplishing the task, for example looking at a project's manifest file would help you understand the project's dependencies, which you could incorporate into any code you write. * For example, in architect mode trying to edit app.js would be rejected because architect mode can only edit files matching "\\.md$" @@ -1435,23 +1435,23 @@ Example: Requesting to list all top level source code definitions in the current . -## create_file +## write_to_file Description: Request to write full content to a file at the specified path. If the file exists, it will be overwritten with the provided content. If the file doesn't exist, it will be created. This tool will automatically create any directories needed to write the file. Parameters: - path: (required) The path of the file to write to (relative to the current working directory /test/path) - content: (required) The content to write to the file. ALWAYS provide the COMPLETE intended content of the file, without any truncation or omissions. You MUST include ALL parts of the file, even if they haven't been modified. Do NOT include the line numbers in the content though, just the actual content of the file. - line_count: (required) The number of lines in the file. Make sure to compute this based on the actual content of the file, not the number of lines in the content you're providing. Usage: - + File path here Your file content here total number of lines in the file, including empty lines - + Example: Requesting to write to frontend-config.json - + frontend-config.json { @@ -1470,7 +1470,7 @@ Example: Requesting to write to frontend-config.json } 14 - + ## execute_command Description: Request to execute a CLI command on the system. Use this when you need to perform system operations or run specific commands to accomplish any step in the user's task. You must tailor your command to the user's system and provide a clear explanation of what the command does. For command chaining, use the appropriate chaining syntax for the user's shell. Prefer to execute complex CLI commands over creating executable scripts, as they are more flexible and easier to run. Commands will be executed in the current working directory: /test/path @@ -1983,7 +1983,7 @@ IMPORTANT: Regardless of what else you see in the MCP settings file, you must de ## Editing MCP Servers -The user may ask to add tools or resources that may make sense to add to an existing MCP server (listed under 'Connected MCP Servers' above: (None running currently), e.g. if it would use the same API. This would be possible if you can locate the MCP server repository on the user's system by looking at the server arguments for a filepath. You might then use list_files and read_file to explore the files in the repository, and use create_file to make changes to the files. +The user may ask to add tools or resources that may make sense to add to an existing MCP server (listed under 'Connected MCP Servers' above: (None running currently), e.g. if it would use the same API. This would be possible if you can locate the MCP server repository on the user's system by looking at the server arguments for a filepath. You might then use list_files and read_file to explore the files in the repository, and use write_to_file to make changes to the files. However some MCP servers may be running from installed packages rather than a local repository, in which case it may make more sense to create a new MCP server. @@ -2001,7 +2001,7 @@ CAPABILITIES - When the user initially gives you a task, a recursive list of all filepaths in the current working directory ('/test/path') will be included in environment_details. This provides an overview of the project's file structure, offering key insights into the project from directory/file names (how developers conceptualize and organize their code) and file extensions (the language used). This can also guide decision-making on which files to explore further. If you need to further explore directories such as outside the current working directory, you can use the list_files tool. If you pass 'true' for the recursive parameter, it will list files recursively. Otherwise, it will list files at the top level, which is better suited for generic directories where you don't necessarily need the nested structure, like the Desktop. - You can use search_files to perform regex searches across files in a specified directory, outputting context-rich results that include surrounding lines. This is particularly useful for understanding code patterns, finding specific implementations, or identifying areas that need refactoring. - You can use the list_code_definition_names tool to get an overview of source code definitions for all files at the top level of a specified directory. This can be particularly useful when you need to understand the broader context and relationships between certain parts of the code. You may need to call this tool multiple times to understand various parts of the codebase related to the task. - - For example, when asked to make edits or improvements you might analyze the file structure in the initial environment_details to get an overview of the project, then use list_code_definition_names to get further insight using source code definitions for files located in relevant directories, then read_file to examine the contents of relevant files, analyze the code and suggest improvements or make necessary edits, then use the create_file tool to apply the changes. If you refactored code that could affect other parts of the codebase, you could use search_files to ensure you update other files as needed. + - For example, when asked to make edits or improvements you might analyze the file structure in the initial environment_details to get an overview of the project, then use list_code_definition_names to get further insight using source code definitions for files located in relevant directories, then read_file to examine the contents of relevant files, analyze the code and suggest improvements or make necessary edits, then use the write_to_file tool to apply the changes. If you refactored code that could affect other parts of the codebase, you could use search_files to ensure you update other files as needed. - You can use the execute_command tool to run commands on the user's computer whenever you feel it can help accomplish the user's task. When you need to execute a CLI command, you must provide a clear explanation of what the command does. Prefer to execute complex CLI commands over creating executable scripts, since they are more flexible and easier to run. Interactive and long-running commands are allowed, since the commands are run in the user's VSCode terminal. The user may keep commands running in the background and you will be kept updated on their status along the way. Each command you execute is run in a new terminal instance. - You have access to MCP servers that may provide additional tools and resources. Each server may provide different capabilities that you can use to accomplish tasks more effectively. @@ -2020,9 +2020,9 @@ RULES - You cannot \`cd\` into a different directory to complete a task. You are stuck operating from '/test/path', so be sure to pass in the correct 'path' parameter when using tools that require a path. - Do not use the ~ character or $HOME to refer to the home directory. - Before using the execute_command tool, you must first think about the SYSTEM INFORMATION context provided to understand the user's environment and tailor your commands to ensure they are compatible with their system. You must also consider if the command you need to run should be executed in a specific directory outside of the current working directory '/test/path', and if so prepend with \`cd\`'ing into that directory && then executing the command (as one command since you are stuck operating from '/test/path'). For example, if you needed to run \`npm install\` in a project outside of '/test/path', you would need to prepend with a \`cd\` i.e. pseudocode for this would be \`cd (path to project) && (command, in this case npm install)\`. -- When using the search_files tool, craft your regex patterns carefully to balance specificity and flexibility. Based on the user's task you may use it to find code patterns, TODO comments, function definitions, or any text-based information across the project. The results include context, so analyze the surrounding code to better understand the matches. Leverage the search_files tool in combination with other tools for more comprehensive analysis. For example, use it to find specific code patterns, then use read_file to examine the full context of interesting matches before using create_file to make informed changes. -- When creating a new project (such as an app, website, or any software project), organize all new files within a dedicated project directory unless the user specifies otherwise. Use appropriate file paths when writing files, as the create_file tool will automatically create any necessary directories. Structure the project logically, adhering to best practices for the specific type of project being created. Unless otherwise specified, new projects should be easily run without additional setup, for example most projects can be built in HTML, CSS, and JavaScript - which you can open in a browser. -- When using the create_file tool to modify a file, use the tool directly with the desired content. You do not need to display the content before using the tool. ALWAYS provide the COMPLETE file content in your response. This is NON-NEGOTIABLE. Partial updates or placeholders like '// rest of code unchanged' are STRICTLY FORBIDDEN. You MUST include ALL parts of the file, even if they haven't been modified. Failure to do so will result in incomplete or broken code, severely impacting the user's project. +- When using the search_files tool, craft your regex patterns carefully to balance specificity and flexibility. Based on the user's task you may use it to find code patterns, TODO comments, function definitions, or any text-based information across the project. The results include context, so analyze the surrounding code to better understand the matches. Leverage the search_files tool in combination with other tools for more comprehensive analysis. For example, use it to find specific code patterns, then use read_file to examine the full context of interesting matches before using write_to_file to make informed changes. +- When creating a new project (such as an app, website, or any software project), organize all new files within a dedicated project directory unless the user specifies otherwise. Use appropriate file paths when writing files, as the write_to_file tool will automatically create any necessary directories. Structure the project logically, adhering to best practices for the specific type of project being created. Unless otherwise specified, new projects should be easily run without additional setup, for example most projects can be built in HTML, CSS, and JavaScript - which you can open in a browser. +- When using the write_to_file tool to modify a file, use the tool directly with the desired content. You do not need to display the content before using the tool. ALWAYS provide the COMPLETE file content in your response. This is NON-NEGOTIABLE. Partial updates or placeholders like '// rest of code unchanged' are STRICTLY FORBIDDEN. You MUST include ALL parts of the file, even if they haven't been modified. Failure to do so will result in incomplete or broken code, severely impacting the user's project. - Some modes have restrictions on which files they can edit. If you attempt to edit a restricted file, the operation will be rejected with a FileRestrictionError that will specify which file patterns are allowed for the current mode. - Be sure to consider the type of project (e.g. Python, JavaScript, web application) when determining the appropriate structure and files to include. Also consider what files may be most relevant to accomplishing the task, for example looking at a project's manifest file would help you understand the project's dependencies, which you could incorporate into any code you write. * For example, in architect mode trying to edit app.js would be rejected because architect mode can only edit files matching "\\.md$" @@ -2171,23 +2171,23 @@ Example: Requesting to list all top level source code definitions in the current . -## create_file +## write_to_file Description: Request to write full content to a file at the specified path. If the file exists, it will be overwritten with the provided content. If the file doesn't exist, it will be created. This tool will automatically create any directories needed to write the file. Parameters: - path: (required) The path of the file to write to (relative to the current working directory /test/path) - content: (required) The content to write to the file. ALWAYS provide the COMPLETE intended content of the file, without any truncation or omissions. You MUST include ALL parts of the file, even if they haven't been modified. Do NOT include the line numbers in the content though, just the actual content of the file. - line_count: (required) The number of lines in the file. Make sure to compute this based on the actual content of the file, not the number of lines in the content you're providing. Usage: - + File path here Your file content here total number of lines in the file, including empty lines - + Example: Requesting to write to frontend-config.json - + frontend-config.json { @@ -2206,7 +2206,7 @@ Example: Requesting to write to frontend-config.json } 14 - + ## browser_action Description: Request to interact with a Puppeteer-controlled browser. Every action, except \`close\`, will be responded to with a screenshot of the browser's current state, along with any new console logs. You may only perform one browser action per message, and wait for the user's response including a screenshot and logs to determine the next action. @@ -2372,7 +2372,7 @@ CAPABILITIES - When the user initially gives you a task, a recursive list of all filepaths in the current working directory ('/test/path') will be included in environment_details. This provides an overview of the project's file structure, offering key insights into the project from directory/file names (how developers conceptualize and organize their code) and file extensions (the language used). This can also guide decision-making on which files to explore further. If you need to further explore directories such as outside the current working directory, you can use the list_files tool. If you pass 'true' for the recursive parameter, it will list files recursively. Otherwise, it will list files at the top level, which is better suited for generic directories where you don't necessarily need the nested structure, like the Desktop. - You can use search_files to perform regex searches across files in a specified directory, outputting context-rich results that include surrounding lines. This is particularly useful for understanding code patterns, finding specific implementations, or identifying areas that need refactoring. - You can use the list_code_definition_names tool to get an overview of source code definitions for all files at the top level of a specified directory. This can be particularly useful when you need to understand the broader context and relationships between certain parts of the code. You may need to call this tool multiple times to understand various parts of the codebase related to the task. - - For example, when asked to make edits or improvements you might analyze the file structure in the initial environment_details to get an overview of the project, then use list_code_definition_names to get further insight using source code definitions for files located in relevant directories, then read_file to examine the contents of relevant files, analyze the code and suggest improvements or make necessary edits, then use the create_file tool to apply the changes. If you refactored code that could affect other parts of the codebase, you could use search_files to ensure you update other files as needed. + - For example, when asked to make edits or improvements you might analyze the file structure in the initial environment_details to get an overview of the project, then use list_code_definition_names to get further insight using source code definitions for files located in relevant directories, then read_file to examine the contents of relevant files, analyze the code and suggest improvements or make necessary edits, then use the write_to_file tool to apply the changes. If you refactored code that could affect other parts of the codebase, you could use search_files to ensure you update other files as needed. - You can use the execute_command tool to run commands on the user's computer whenever you feel it can help accomplish the user's task. When you need to execute a CLI command, you must provide a clear explanation of what the command does. Prefer to execute complex CLI commands over creating executable scripts, since they are more flexible and easier to run. Interactive and long-running commands are allowed, since the commands are run in the user's VSCode terminal. The user may keep commands running in the background and you will be kept updated on their status along the way. Each command you execute is run in a new terminal instance. - You can use the browser_action tool to interact with websites (including html files and locally running development servers) through a Puppeteer-controlled browser when you feel it is necessary in accomplishing the user's task. This tool is particularly useful for web development tasks as it allows you to launch a browser, navigate to pages, interact with elements through clicks and keyboard input, and capture the results through screenshots and console logs. This tool may be useful at key stages of web development tasks-such as after implementing new features, making substantial changes, when troubleshooting issues, or to verify the result of your work. You can analyze the provided screenshots to ensure correct rendering or identify errors, and review console logs for runtime issues. - For example, if asked to add a component to a react website, you might create the necessary files, use execute_command to run the site locally, then use browser_action to launch the browser, navigate to the local server, and verify the component renders & functions correctly before closing the browser. @@ -2391,9 +2391,9 @@ RULES - You cannot \`cd\` into a different directory to complete a task. You are stuck operating from '/test/path', so be sure to pass in the correct 'path' parameter when using tools that require a path. - Do not use the ~ character or $HOME to refer to the home directory. - Before using the execute_command tool, you must first think about the SYSTEM INFORMATION context provided to understand the user's environment and tailor your commands to ensure they are compatible with their system. You must also consider if the command you need to run should be executed in a specific directory outside of the current working directory '/test/path', and if so prepend with \`cd\`'ing into that directory && then executing the command (as one command since you are stuck operating from '/test/path'). For example, if you needed to run \`npm install\` in a project outside of '/test/path', you would need to prepend with a \`cd\` i.e. pseudocode for this would be \`cd (path to project) && (command, in this case npm install)\`. -- When using the search_files tool, craft your regex patterns carefully to balance specificity and flexibility. Based on the user's task you may use it to find code patterns, TODO comments, function definitions, or any text-based information across the project. The results include context, so analyze the surrounding code to better understand the matches. Leverage the search_files tool in combination with other tools for more comprehensive analysis. For example, use it to find specific code patterns, then use read_file to examine the full context of interesting matches before using create_file to make informed changes. -- When creating a new project (such as an app, website, or any software project), organize all new files within a dedicated project directory unless the user specifies otherwise. Use appropriate file paths when writing files, as the create_file tool will automatically create any necessary directories. Structure the project logically, adhering to best practices for the specific type of project being created. Unless otherwise specified, new projects should be easily run without additional setup, for example most projects can be built in HTML, CSS, and JavaScript - which you can open in a browser. -- When using the create_file tool to modify a file, use the tool directly with the desired content. You do not need to display the content before using the tool. ALWAYS provide the COMPLETE file content in your response. This is NON-NEGOTIABLE. Partial updates or placeholders like '// rest of code unchanged' are STRICTLY FORBIDDEN. You MUST include ALL parts of the file, even if they haven't been modified. Failure to do so will result in incomplete or broken code, severely impacting the user's project. +- When using the search_files tool, craft your regex patterns carefully to balance specificity and flexibility. Based on the user's task you may use it to find code patterns, TODO comments, function definitions, or any text-based information across the project. The results include context, so analyze the surrounding code to better understand the matches. Leverage the search_files tool in combination with other tools for more comprehensive analysis. For example, use it to find specific code patterns, then use read_file to examine the full context of interesting matches before using write_to_file to make informed changes. +- When creating a new project (such as an app, website, or any software project), organize all new files within a dedicated project directory unless the user specifies otherwise. Use appropriate file paths when writing files, as the write_to_file tool will automatically create any necessary directories. Structure the project logically, adhering to best practices for the specific type of project being created. Unless otherwise specified, new projects should be easily run without additional setup, for example most projects can be built in HTML, CSS, and JavaScript - which you can open in a browser. +- When using the write_to_file tool to modify a file, use the tool directly with the desired content. You do not need to display the content before using the tool. ALWAYS provide the COMPLETE file content in your response. This is NON-NEGOTIABLE. Partial updates or placeholders like '// rest of code unchanged' are STRICTLY FORBIDDEN. You MUST include ALL parts of the file, even if they haven't been modified. Failure to do so will result in incomplete or broken code, severely impacting the user's project. - Some modes have restrictions on which files they can edit. If you attempt to edit a restricted file, the operation will be rejected with a FileRestrictionError that will specify which file patterns are allowed for the current mode. - Be sure to consider the type of project (e.g. Python, JavaScript, web application) when determining the appropriate structure and files to include. Also consider what files may be most relevant to accomplishing the task, for example looking at a project's manifest file would help you understand the project's dependencies, which you could incorporate into any code you write. * For example, in architect mode trying to edit app.js would be rejected because architect mode can only edit files matching "\\.md$" @@ -2543,7 +2543,44 @@ Example: Requesting to list all top level source code definitions in the current . -## edit_file +## write_to_file +Description: Request to write full content to a file at the specified path. If the file exists, it will be overwritten with the provided content. If the file doesn't exist, it will be created. This tool will automatically create any directories needed to write the file. +Parameters: +- path: (required) The path of the file to write to (relative to the current working directory /test/path) +- content: (required) The content to write to the file. ALWAYS provide the COMPLETE intended content of the file, without any truncation or omissions. You MUST include ALL parts of the file, even if they haven't been modified. Do NOT include the line numbers in the content though, just the actual content of the file. +- line_count: (required) The number of lines in the file. Make sure to compute this based on the actual content of the file, not the number of lines in the content you're providing. +Usage: + +File path here + +Your file content here + +total number of lines in the file, including empty lines + + +Example: Requesting to write to frontend-config.json + +frontend-config.json + +{ + "apiEndpoint": "https://api.example.com", + "theme": { + "primaryColor": "#007bff", + "secondaryColor": "#6c757d", + "fontFamily": "Arial, sans-serif" + }, + "features": { + "darkMode": true, + "notifications": true, + "analytics": false + }, + "version": "1.0.0" +} + +14 + + +## apply_diff Description: Request to replace existing code using a search and replace block. This tool allows for precise, surgical replaces to files by specifying exactly what content to search for and what to replace it with. The tool will maintain proper indentation and formatting while making changes. @@ -2594,51 +2631,14 @@ def calculate_total(items): \`\`\` Usage: - + File path here Your search/replace content here 1 5 - - -## create_file -Description: Request to write full content to a file at the specified path. If the file exists, it will be overwritten with the provided content. If the file doesn't exist, it will be created. This tool will automatically create any directories needed to write the file. -Parameters: -- path: (required) The path of the file to write to (relative to the current working directory /test/path) -- content: (required) The content to write to the file. ALWAYS provide the COMPLETE intended content of the file, without any truncation or omissions. You MUST include ALL parts of the file, even if they haven't been modified. Do NOT include the line numbers in the content though, just the actual content of the file. -- line_count: (required) The number of lines in the file. Make sure to compute this based on the actual content of the file, not the number of lines in the content you're providing. -Usage: - -File path here - -Your file content here - -total number of lines in the file, including empty lines - - -Example: Requesting to write to frontend-config.json - -frontend-config.json - -{ - "apiEndpoint": "https://api.example.com", - "theme": { - "primaryColor": "#007bff", - "secondaryColor": "#6c757d", - "fontFamily": "Arial, sans-serif" - }, - "features": { - "darkMode": true, - "notifications": true, - "analytics": false - }, - "version": "1.0.0" -} - -14 - + ## execute_command Description: Request to execute a CLI command on the system. Use this when you need to perform system operations or run specific commands to accomplish any step in the user's task. You must tailor your command to the user's system and provide a clear explanation of what the command does. For command chaining, use the appropriate chaining syntax for the user's shell. Prefer to execute complex CLI commands over creating executable scripts, as they are more flexible and easier to run. Commands will be executed in the current working directory: /test/path @@ -2758,7 +2758,7 @@ CAPABILITIES - When the user initially gives you a task, a recursive list of all filepaths in the current working directory ('/test/path') will be included in environment_details. This provides an overview of the project's file structure, offering key insights into the project from directory/file names (how developers conceptualize and organize their code) and file extensions (the language used). This can also guide decision-making on which files to explore further. If you need to further explore directories such as outside the current working directory, you can use the list_files tool. If you pass 'true' for the recursive parameter, it will list files recursively. Otherwise, it will list files at the top level, which is better suited for generic directories where you don't necessarily need the nested structure, like the Desktop. - You can use search_files to perform regex searches across files in a specified directory, outputting context-rich results that include surrounding lines. This is particularly useful for understanding code patterns, finding specific implementations, or identifying areas that need refactoring. - You can use the list_code_definition_names tool to get an overview of source code definitions for all files at the top level of a specified directory. This can be particularly useful when you need to understand the broader context and relationships between certain parts of the code. You may need to call this tool multiple times to understand various parts of the codebase related to the task. - - For example, when asked to make edits or improvements you might analyze the file structure in the initial environment_details to get an overview of the project, then use list_code_definition_names to get further insight using source code definitions for files located in relevant directories, then read_file to examine the contents of relevant files, analyze the code and suggest improvements or make necessary edits, then use the edit_file or create_file tool to apply the changes. If you refactored code that could affect other parts of the codebase, you could use search_files to ensure you update other files as needed. + - For example, when asked to make edits or improvements you might analyze the file structure in the initial environment_details to get an overview of the project, then use list_code_definition_names to get further insight using source code definitions for files located in relevant directories, then read_file to examine the contents of relevant files, analyze the code and suggest improvements or make necessary edits, then use the write_to_file or apply_diff tool to apply the changes. If you refactored code that could affect other parts of the codebase, you could use search_files to ensure you update other files as needed. - You can use the execute_command tool to run commands on the user's computer whenever you feel it can help accomplish the user's task. When you need to execute a CLI command, you must provide a clear explanation of what the command does. Prefer to execute complex CLI commands over creating executable scripts, since they are more flexible and easier to run. Interactive and long-running commands are allowed, since the commands are run in the user's VSCode terminal. The user may keep commands running in the background and you will be kept updated on their status along the way. Each command you execute is run in a new terminal instance. ==== @@ -2775,11 +2775,11 @@ RULES - You cannot \`cd\` into a different directory to complete a task. You are stuck operating from '/test/path', so be sure to pass in the correct 'path' parameter when using tools that require a path. - Do not use the ~ character or $HOME to refer to the home directory. - Before using the execute_command tool, you must first think about the SYSTEM INFORMATION context provided to understand the user's environment and tailor your commands to ensure they are compatible with their system. You must also consider if the command you need to run should be executed in a specific directory outside of the current working directory '/test/path', and if so prepend with \`cd\`'ing into that directory && then executing the command (as one command since you are stuck operating from '/test/path'). For example, if you needed to run \`npm install\` in a project outside of '/test/path', you would need to prepend with a \`cd\` i.e. pseudocode for this would be \`cd (path to project) && (command, in this case npm install)\`. -- When using the search_files tool, craft your regex patterns carefully to balance specificity and flexibility. Based on the user's task you may use it to find code patterns, TODO comments, function definitions, or any text-based information across the project. The results include context, so analyze the surrounding code to better understand the matches. Leverage the search_files tool in combination with other tools for more comprehensive analysis. For example, use it to find specific code patterns, then use read_file to examine the full context of interesting matches before using edit_file or create_file to make informed changes. -- When creating a new project (such as an app, website, or any software project), organize all new files within a dedicated project directory unless the user specifies otherwise. Use appropriate file paths when writing files, as the create_file tool will automatically create any necessary directories. Structure the project logically, adhering to best practices for the specific type of project being created. Unless otherwise specified, new projects should be easily run without additional setup, for example most projects can be built in HTML, CSS, and JavaScript - which you can open in a browser. -- For editing files, you have access to these tools: edit_file (for replacing lines in existing files), create_file (for creating new files or complete file rewrites). -- You should always prefer using other editing tools over create_file when making changes to existing files since create_file is much slower and cannot handle large files. -- When using the create_file tool to modify a file, use the tool directly with the desired content. You do not need to display the content before using the tool. ALWAYS provide the COMPLETE file content in your response. This is NON-NEGOTIABLE. Partial updates or placeholders like '// rest of code unchanged' are STRICTLY FORBIDDEN. You MUST include ALL parts of the file, even if they haven't been modified. Failure to do so will result in incomplete or broken code, severely impacting the user's project. +- When using the search_files tool, craft your regex patterns carefully to balance specificity and flexibility. Based on the user's task you may use it to find code patterns, TODO comments, function definitions, or any text-based information across the project. The results include context, so analyze the surrounding code to better understand the matches. Leverage the search_files tool in combination with other tools for more comprehensive analysis. For example, use it to find specific code patterns, then use read_file to examine the full context of interesting matches before using write_to_file to make informed changes. +- When creating a new project (such as an app, website, or any software project), organize all new files within a dedicated project directory unless the user specifies otherwise. Use appropriate file paths when writing files, as the write_to_file tool will automatically create any necessary directories. Structure the project logically, adhering to best practices for the specific type of project being created. Unless otherwise specified, new projects should be easily run without additional setup, for example most projects can be built in HTML, CSS, and JavaScript - which you can open in a browser. +- For editing files, you have access to these tools: write_to_file (for creating new files or complete file rewrites), apply_diff (for replacing lines in existing files). +- When using the write_to_file tool to modify a file, use the tool directly with the desired content. You do not need to display the content before using the tool. ALWAYS provide the COMPLETE file content in your response. This is NON-NEGOTIABLE. Partial updates or placeholders like '// rest of code unchanged' are STRICTLY FORBIDDEN. You MUST include ALL parts of the file, even if they haven't been modified. Failure to do so will result in incomplete or broken code, severely impacting the user's project. +- You should always prefer using other editing tools over write_to_file when making changes to existing files since write_to_file is much slower and cannot handle large files. - Some modes have restrictions on which files they can edit. If you attempt to edit a restricted file, the operation will be rejected with a FileRestrictionError that will specify which file patterns are allowed for the current mode. - Be sure to consider the type of project (e.g. Python, JavaScript, web application) when determining the appropriate structure and files to include. Also consider what files may be most relevant to accomplishing the task, for example looking at a project's manifest file would help you understand the project's dependencies, which you could incorporate into any code you write. * For example, in architect mode trying to edit app.js would be rejected because architect mode can only edit files matching "\\.md$" @@ -2928,23 +2928,23 @@ Example: Requesting to list all top level source code definitions in the current . -## create_file +## write_to_file Description: Request to write full content to a file at the specified path. If the file exists, it will be overwritten with the provided content. If the file doesn't exist, it will be created. This tool will automatically create any directories needed to write the file. Parameters: - path: (required) The path of the file to write to (relative to the current working directory /test/path) - content: (required) The content to write to the file. ALWAYS provide the COMPLETE intended content of the file, without any truncation or omissions. You MUST include ALL parts of the file, even if they haven't been modified. Do NOT include the line numbers in the content though, just the actual content of the file. - line_count: (required) The number of lines in the file. Make sure to compute this based on the actual content of the file, not the number of lines in the content you're providing. Usage: - + File path here Your file content here total number of lines in the file, including empty lines - + Example: Requesting to write to frontend-config.json - + frontend-config.json { @@ -2963,7 +2963,7 @@ Example: Requesting to write to frontend-config.json } 14 - + ## execute_command Description: Request to execute a CLI command on the system. Use this when you need to perform system operations or run specific commands to accomplish any step in the user's task. You must tailor your command to the user's system and provide a clear explanation of what the command does. For command chaining, use the appropriate chaining syntax for the user's shell. Prefer to execute complex CLI commands over creating executable scripts, as they are more flexible and easier to run. Commands will be executed in the current working directory: /test/path @@ -3083,7 +3083,7 @@ CAPABILITIES - When the user initially gives you a task, a recursive list of all filepaths in the current working directory ('/test/path') will be included in environment_details. This provides an overview of the project's file structure, offering key insights into the project from directory/file names (how developers conceptualize and organize their code) and file extensions (the language used). This can also guide decision-making on which files to explore further. If you need to further explore directories such as outside the current working directory, you can use the list_files tool. If you pass 'true' for the recursive parameter, it will list files recursively. Otherwise, it will list files at the top level, which is better suited for generic directories where you don't necessarily need the nested structure, like the Desktop. - You can use search_files to perform regex searches across files in a specified directory, outputting context-rich results that include surrounding lines. This is particularly useful for understanding code patterns, finding specific implementations, or identifying areas that need refactoring. - You can use the list_code_definition_names tool to get an overview of source code definitions for all files at the top level of a specified directory. This can be particularly useful when you need to understand the broader context and relationships between certain parts of the code. You may need to call this tool multiple times to understand various parts of the codebase related to the task. - - For example, when asked to make edits or improvements you might analyze the file structure in the initial environment_details to get an overview of the project, then use list_code_definition_names to get further insight using source code definitions for files located in relevant directories, then read_file to examine the contents of relevant files, analyze the code and suggest improvements or make necessary edits, then use the create_file tool to apply the changes. If you refactored code that could affect other parts of the codebase, you could use search_files to ensure you update other files as needed. + - For example, when asked to make edits or improvements you might analyze the file structure in the initial environment_details to get an overview of the project, then use list_code_definition_names to get further insight using source code definitions for files located in relevant directories, then read_file to examine the contents of relevant files, analyze the code and suggest improvements or make necessary edits, then use the write_to_file tool to apply the changes. If you refactored code that could affect other parts of the codebase, you could use search_files to ensure you update other files as needed. - You can use the execute_command tool to run commands on the user's computer whenever you feel it can help accomplish the user's task. When you need to execute a CLI command, you must provide a clear explanation of what the command does. Prefer to execute complex CLI commands over creating executable scripts, since they are more flexible and easier to run. Interactive and long-running commands are allowed, since the commands are run in the user's VSCode terminal. The user may keep commands running in the background and you will be kept updated on their status along the way. Each command you execute is run in a new terminal instance. ==== @@ -3100,9 +3100,9 @@ RULES - You cannot \`cd\` into a different directory to complete a task. You are stuck operating from '/test/path', so be sure to pass in the correct 'path' parameter when using tools that require a path. - Do not use the ~ character or $HOME to refer to the home directory. - Before using the execute_command tool, you must first think about the SYSTEM INFORMATION context provided to understand the user's environment and tailor your commands to ensure they are compatible with their system. You must also consider if the command you need to run should be executed in a specific directory outside of the current working directory '/test/path', and if so prepend with \`cd\`'ing into that directory && then executing the command (as one command since you are stuck operating from '/test/path'). For example, if you needed to run \`npm install\` in a project outside of '/test/path', you would need to prepend with a \`cd\` i.e. pseudocode for this would be \`cd (path to project) && (command, in this case npm install)\`. -- When using the search_files tool, craft your regex patterns carefully to balance specificity and flexibility. Based on the user's task you may use it to find code patterns, TODO comments, function definitions, or any text-based information across the project. The results include context, so analyze the surrounding code to better understand the matches. Leverage the search_files tool in combination with other tools for more comprehensive analysis. For example, use it to find specific code patterns, then use read_file to examine the full context of interesting matches before using create_file to make informed changes. -- When creating a new project (such as an app, website, or any software project), organize all new files within a dedicated project directory unless the user specifies otherwise. Use appropriate file paths when writing files, as the create_file tool will automatically create any necessary directories. Structure the project logically, adhering to best practices for the specific type of project being created. Unless otherwise specified, new projects should be easily run without additional setup, for example most projects can be built in HTML, CSS, and JavaScript - which you can open in a browser. -- When using the create_file tool to modify a file, use the tool directly with the desired content. You do not need to display the content before using the tool. ALWAYS provide the COMPLETE file content in your response. This is NON-NEGOTIABLE. Partial updates or placeholders like '// rest of code unchanged' are STRICTLY FORBIDDEN. You MUST include ALL parts of the file, even if they haven't been modified. Failure to do so will result in incomplete or broken code, severely impacting the user's project. +- When using the search_files tool, craft your regex patterns carefully to balance specificity and flexibility. Based on the user's task you may use it to find code patterns, TODO comments, function definitions, or any text-based information across the project. The results include context, so analyze the surrounding code to better understand the matches. Leverage the search_files tool in combination with other tools for more comprehensive analysis. For example, use it to find specific code patterns, then use read_file to examine the full context of interesting matches before using write_to_file to make informed changes. +- When creating a new project (such as an app, website, or any software project), organize all new files within a dedicated project directory unless the user specifies otherwise. Use appropriate file paths when writing files, as the write_to_file tool will automatically create any necessary directories. Structure the project logically, adhering to best practices for the specific type of project being created. Unless otherwise specified, new projects should be easily run without additional setup, for example most projects can be built in HTML, CSS, and JavaScript - which you can open in a browser. +- When using the write_to_file tool to modify a file, use the tool directly with the desired content. You do not need to display the content before using the tool. ALWAYS provide the COMPLETE file content in your response. This is NON-NEGOTIABLE. Partial updates or placeholders like '// rest of code unchanged' are STRICTLY FORBIDDEN. You MUST include ALL parts of the file, even if they haven't been modified. Failure to do so will result in incomplete or broken code, severely impacting the user's project. - Some modes have restrictions on which files they can edit. If you attempt to edit a restricted file, the operation will be rejected with a FileRestrictionError that will specify which file patterns are allowed for the current mode. - Be sure to consider the type of project (e.g. Python, JavaScript, web application) when determining the appropriate structure and files to include. Also consider what files may be most relevant to accomplishing the task, for example looking at a project's manifest file would help you understand the project's dependencies, which you could incorporate into any code you write. * For example, in architect mode trying to edit app.js would be rejected because architect mode can only edit files matching "\\.md$" @@ -3293,23 +3293,23 @@ Example: Requesting to list all top level source code definitions in the current . -## create_file +## write_to_file Description: Request to write full content to a file at the specified path. If the file exists, it will be overwritten with the provided content. If the file doesn't exist, it will be created. This tool will automatically create any directories needed to write the file. Parameters: - path: (required) The path of the file to write to (relative to the current working directory /test/path) - content: (required) The content to write to the file. ALWAYS provide the COMPLETE intended content of the file, without any truncation or omissions. You MUST include ALL parts of the file, even if they haven't been modified. Do NOT include the line numbers in the content though, just the actual content of the file. - line_count: (required) The number of lines in the file. Make sure to compute this based on the actual content of the file, not the number of lines in the content you're providing. Usage: - + File path here Your file content here total number of lines in the file, including empty lines - + Example: Requesting to write to frontend-config.json - + frontend-config.json { @@ -3328,7 +3328,7 @@ Example: Requesting to write to frontend-config.json } 14 - + ## execute_command Description: Request to execute a CLI command on the system. Use this when you need to perform system operations or run specific commands to accomplish any step in the user's task. You must tailor your command to the user's system and provide a clear explanation of what the command does. For command chaining, use the appropriate chaining syntax for the user's shell. Prefer to execute complex CLI commands over creating executable scripts, as they are more flexible and easier to run. Commands will be executed in the current working directory: /test/path @@ -3505,7 +3505,7 @@ CAPABILITIES - When the user initially gives you a task, a recursive list of all filepaths in the current working directory ('/test/path') will be included in environment_details. This provides an overview of the project's file structure, offering key insights into the project from directory/file names (how developers conceptualize and organize their code) and file extensions (the language used). This can also guide decision-making on which files to explore further. If you need to further explore directories such as outside the current working directory, you can use the list_files tool. If you pass 'true' for the recursive parameter, it will list files recursively. Otherwise, it will list files at the top level, which is better suited for generic directories where you don't necessarily need the nested structure, like the Desktop. - You can use search_files to perform regex searches across files in a specified directory, outputting context-rich results that include surrounding lines. This is particularly useful for understanding code patterns, finding specific implementations, or identifying areas that need refactoring. - You can use the list_code_definition_names tool to get an overview of source code definitions for all files at the top level of a specified directory. This can be particularly useful when you need to understand the broader context and relationships between certain parts of the code. You may need to call this tool multiple times to understand various parts of the codebase related to the task. - - For example, when asked to make edits or improvements you might analyze the file structure in the initial environment_details to get an overview of the project, then use list_code_definition_names to get further insight using source code definitions for files located in relevant directories, then read_file to examine the contents of relevant files, analyze the code and suggest improvements or make necessary edits, then use the create_file tool to apply the changes. If you refactored code that could affect other parts of the codebase, you could use search_files to ensure you update other files as needed. + - For example, when asked to make edits or improvements you might analyze the file structure in the initial environment_details to get an overview of the project, then use list_code_definition_names to get further insight using source code definitions for files located in relevant directories, then read_file to examine the contents of relevant files, analyze the code and suggest improvements or make necessary edits, then use the write_to_file tool to apply the changes. If you refactored code that could affect other parts of the codebase, you could use search_files to ensure you update other files as needed. - You can use the execute_command tool to run commands on the user's computer whenever you feel it can help accomplish the user's task. When you need to execute a CLI command, you must provide a clear explanation of what the command does. Prefer to execute complex CLI commands over creating executable scripts, since they are more flexible and easier to run. Interactive and long-running commands are allowed, since the commands are run in the user's VSCode terminal. The user may keep commands running in the background and you will be kept updated on their status along the way. Each command you execute is run in a new terminal instance. - You have access to MCP servers that may provide additional tools and resources. Each server may provide different capabilities that you can use to accomplish tasks more effectively. @@ -3524,9 +3524,9 @@ RULES - You cannot \`cd\` into a different directory to complete a task. You are stuck operating from '/test/path', so be sure to pass in the correct 'path' parameter when using tools that require a path. - Do not use the ~ character or $HOME to refer to the home directory. - Before using the execute_command tool, you must first think about the SYSTEM INFORMATION context provided to understand the user's environment and tailor your commands to ensure they are compatible with their system. You must also consider if the command you need to run should be executed in a specific directory outside of the current working directory '/test/path', and if so prepend with \`cd\`'ing into that directory && then executing the command (as one command since you are stuck operating from '/test/path'). For example, if you needed to run \`npm install\` in a project outside of '/test/path', you would need to prepend with a \`cd\` i.e. pseudocode for this would be \`cd (path to project) && (command, in this case npm install)\`. -- When using the search_files tool, craft your regex patterns carefully to balance specificity and flexibility. Based on the user's task you may use it to find code patterns, TODO comments, function definitions, or any text-based information across the project. The results include context, so analyze the surrounding code to better understand the matches. Leverage the search_files tool in combination with other tools for more comprehensive analysis. For example, use it to find specific code patterns, then use read_file to examine the full context of interesting matches before using create_file to make informed changes. -- When creating a new project (such as an app, website, or any software project), organize all new files within a dedicated project directory unless the user specifies otherwise. Use appropriate file paths when writing files, as the create_file tool will automatically create any necessary directories. Structure the project logically, adhering to best practices for the specific type of project being created. Unless otherwise specified, new projects should be easily run without additional setup, for example most projects can be built in HTML, CSS, and JavaScript - which you can open in a browser. -- When using the create_file tool to modify a file, use the tool directly with the desired content. You do not need to display the content before using the tool. ALWAYS provide the COMPLETE file content in your response. This is NON-NEGOTIABLE. Partial updates or placeholders like '// rest of code unchanged' are STRICTLY FORBIDDEN. You MUST include ALL parts of the file, even if they haven't been modified. Failure to do so will result in incomplete or broken code, severely impacting the user's project. +- When using the search_files tool, craft your regex patterns carefully to balance specificity and flexibility. Based on the user's task you may use it to find code patterns, TODO comments, function definitions, or any text-based information across the project. The results include context, so analyze the surrounding code to better understand the matches. Leverage the search_files tool in combination with other tools for more comprehensive analysis. For example, use it to find specific code patterns, then use read_file to examine the full context of interesting matches before using write_to_file to make informed changes. +- When creating a new project (such as an app, website, or any software project), organize all new files within a dedicated project directory unless the user specifies otherwise. Use appropriate file paths when writing files, as the write_to_file tool will automatically create any necessary directories. Structure the project logically, adhering to best practices for the specific type of project being created. Unless otherwise specified, new projects should be easily run without additional setup, for example most projects can be built in HTML, CSS, and JavaScript - which you can open in a browser. +- When using the write_to_file tool to modify a file, use the tool directly with the desired content. You do not need to display the content before using the tool. ALWAYS provide the COMPLETE file content in your response. This is NON-NEGOTIABLE. Partial updates or placeholders like '// rest of code unchanged' are STRICTLY FORBIDDEN. You MUST include ALL parts of the file, even if they haven't been modified. Failure to do so will result in incomplete or broken code, severely impacting the user's project. - Some modes have restrictions on which files they can edit. If you attempt to edit a restricted file, the operation will be rejected with a FileRestrictionError that will specify which file patterns are allowed for the current mode. - Be sure to consider the type of project (e.g. Python, JavaScript, web application) when determining the appropriate structure and files to include. Also consider what files may be most relevant to accomplishing the task, for example looking at a project's manifest file would help you understand the project's dependencies, which you could incorporate into any code you write. * For example, in architect mode trying to edit app.js would be rejected because architect mode can only edit files matching "\\.md$" @@ -3690,23 +3690,23 @@ Example: Requesting to list all top level source code definitions in the current . -## create_file +## write_to_file Description: Request to write full content to a file at the specified path. If the file exists, it will be overwritten with the provided content. If the file doesn't exist, it will be created. This tool will automatically create any directories needed to write the file. Parameters: - path: (required) The path of the file to write to (relative to the current working directory /test/path) - content: (required) The content to write to the file. ALWAYS provide the COMPLETE intended content of the file, without any truncation or omissions. You MUST include ALL parts of the file, even if they haven't been modified. Do NOT include the line numbers in the content though, just the actual content of the file. - line_count: (required) The number of lines in the file. Make sure to compute this based on the actual content of the file, not the number of lines in the content you're providing. Usage: - + File path here Your file content here total number of lines in the file, including empty lines - + Example: Requesting to write to frontend-config.json - + frontend-config.json { @@ -3725,7 +3725,7 @@ Example: Requesting to write to frontend-config.json } 14 - + ## ask_followup_question Description: Ask the user a question to gather additional information needed to complete the task. This tool should be used when you encounter ambiguities, need clarification, or require more details to proceed effectively. It allows for interactive problem-solving by enabling direct communication with the user. Use this tool judiciously to maintain a balance between gathering necessary information and avoiding excessive back-and-forth. @@ -3831,7 +3831,7 @@ CAPABILITIES - When the user initially gives you a task, a recursive list of all filepaths in the current working directory ('/test/path') will be included in environment_details. This provides an overview of the project's file structure, offering key insights into the project from directory/file names (how developers conceptualize and organize their code) and file extensions (the language used). This can also guide decision-making on which files to explore further. If you need to further explore directories such as outside the current working directory, you can use the list_files tool. If you pass 'true' for the recursive parameter, it will list files recursively. Otherwise, it will list files at the top level, which is better suited for generic directories where you don't necessarily need the nested structure, like the Desktop. - You can use search_files to perform regex searches across files in a specified directory, outputting context-rich results that include surrounding lines. This is particularly useful for understanding code patterns, finding specific implementations, or identifying areas that need refactoring. - You can use the list_code_definition_names tool to get an overview of source code definitions for all files at the top level of a specified directory. This can be particularly useful when you need to understand the broader context and relationships between certain parts of the code. You may need to call this tool multiple times to understand various parts of the codebase related to the task. - - For example, when asked to make edits or improvements you might analyze the file structure in the initial environment_details to get an overview of the project, then use list_code_definition_names to get further insight using source code definitions for files located in relevant directories, then read_file to examine the contents of relevant files, analyze the code and suggest improvements or make necessary edits, then use the create_file tool to apply the changes. If you refactored code that could affect other parts of the codebase, you could use search_files to ensure you update other files as needed. + - For example, when asked to make edits or improvements you might analyze the file structure in the initial environment_details to get an overview of the project, then use list_code_definition_names to get further insight using source code definitions for files located in relevant directories, then read_file to examine the contents of relevant files, analyze the code and suggest improvements or make necessary edits, then use the write_to_file tool to apply the changes. If you refactored code that could affect other parts of the codebase, you could use search_files to ensure you update other files as needed. - You can use the execute_command tool to run commands on the user's computer whenever you feel it can help accomplish the user's task. When you need to execute a CLI command, you must provide a clear explanation of what the command does. Prefer to execute complex CLI commands over creating executable scripts, since they are more flexible and easier to run. Interactive and long-running commands are allowed, since the commands are run in the user's VSCode terminal. The user may keep commands running in the background and you will be kept updated on their status along the way. Each command you execute is run in a new terminal instance. ==== @@ -3848,9 +3848,9 @@ RULES - You cannot \`cd\` into a different directory to complete a task. You are stuck operating from '/test/path', so be sure to pass in the correct 'path' parameter when using tools that require a path. - Do not use the ~ character or $HOME to refer to the home directory. - Before using the execute_command tool, you must first think about the SYSTEM INFORMATION context provided to understand the user's environment and tailor your commands to ensure they are compatible with their system. You must also consider if the command you need to run should be executed in a specific directory outside of the current working directory '/test/path', and if so prepend with \`cd\`'ing into that directory && then executing the command (as one command since you are stuck operating from '/test/path'). For example, if you needed to run \`npm install\` in a project outside of '/test/path', you would need to prepend with a \`cd\` i.e. pseudocode for this would be \`cd (path to project) && (command, in this case npm install)\`. -- When using the search_files tool, craft your regex patterns carefully to balance specificity and flexibility. Based on the user's task you may use it to find code patterns, TODO comments, function definitions, or any text-based information across the project. The results include context, so analyze the surrounding code to better understand the matches. Leverage the search_files tool in combination with other tools for more comprehensive analysis. For example, use it to find specific code patterns, then use read_file to examine the full context of interesting matches before using create_file to make informed changes. -- When creating a new project (such as an app, website, or any software project), organize all new files within a dedicated project directory unless the user specifies otherwise. Use appropriate file paths when writing files, as the create_file tool will automatically create any necessary directories. Structure the project logically, adhering to best practices for the specific type of project being created. Unless otherwise specified, new projects should be easily run without additional setup, for example most projects can be built in HTML, CSS, and JavaScript - which you can open in a browser. -- When using the create_file tool to modify a file, use the tool directly with the desired content. You do not need to display the content before using the tool. ALWAYS provide the COMPLETE file content in your response. This is NON-NEGOTIABLE. Partial updates or placeholders like '// rest of code unchanged' are STRICTLY FORBIDDEN. You MUST include ALL parts of the file, even if they haven't been modified. Failure to do so will result in incomplete or broken code, severely impacting the user's project. +- When using the search_files tool, craft your regex patterns carefully to balance specificity and flexibility. Based on the user's task you may use it to find code patterns, TODO comments, function definitions, or any text-based information across the project. The results include context, so analyze the surrounding code to better understand the matches. Leverage the search_files tool in combination with other tools for more comprehensive analysis. For example, use it to find specific code patterns, then use read_file to examine the full context of interesting matches before using write_to_file to make informed changes. +- When creating a new project (such as an app, website, or any software project), organize all new files within a dedicated project directory unless the user specifies otherwise. Use appropriate file paths when writing files, as the write_to_file tool will automatically create any necessary directories. Structure the project logically, adhering to best practices for the specific type of project being created. Unless otherwise specified, new projects should be easily run without additional setup, for example most projects can be built in HTML, CSS, and JavaScript - which you can open in a browser. +- When using the write_to_file tool to modify a file, use the tool directly with the desired content. You do not need to display the content before using the tool. ALWAYS provide the COMPLETE file content in your response. This is NON-NEGOTIABLE. Partial updates or placeholders like '// rest of code unchanged' are STRICTLY FORBIDDEN. You MUST include ALL parts of the file, even if they haven't been modified. Failure to do so will result in incomplete or broken code, severely impacting the user's project. - Some modes have restrictions on which files they can edit. If you attempt to edit a restricted file, the operation will be rejected with a FileRestrictionError that will specify which file patterns are allowed for the current mode. - Be sure to consider the type of project (e.g. Python, JavaScript, web application) when determining the appropriate structure and files to include. Also consider what files may be most relevant to accomplishing the task, for example looking at a project's manifest file would help you understand the project's dependencies, which you could incorporate into any code you write. * For example, in architect mode trying to edit app.js would be rejected because architect mode can only edit files matching "\\.md$" @@ -4108,7 +4108,7 @@ CAPABILITIES - When the user initially gives you a task, a recursive list of all filepaths in the current working directory ('/test/path') will be included in environment_details. This provides an overview of the project's file structure, offering key insights into the project from directory/file names (how developers conceptualize and organize their code) and file extensions (the language used). This can also guide decision-making on which files to explore further. If you need to further explore directories such as outside the current working directory, you can use the list_files tool. If you pass 'true' for the recursive parameter, it will list files recursively. Otherwise, it will list files at the top level, which is better suited for generic directories where you don't necessarily need the nested structure, like the Desktop. - You can use search_files to perform regex searches across files in a specified directory, outputting context-rich results that include surrounding lines. This is particularly useful for understanding code patterns, finding specific implementations, or identifying areas that need refactoring. - You can use the list_code_definition_names tool to get an overview of source code definitions for all files at the top level of a specified directory. This can be particularly useful when you need to understand the broader context and relationships between certain parts of the code. You may need to call this tool multiple times to understand various parts of the codebase related to the task. - - For example, when asked to make edits or improvements you might analyze the file structure in the initial environment_details to get an overview of the project, then use list_code_definition_names to get further insight using source code definitions for files located in relevant directories, then read_file to examine the contents of relevant files, analyze the code and suggest improvements or make necessary edits, then use the create_file tool to apply the changes. If you refactored code that could affect other parts of the codebase, you could use search_files to ensure you update other files as needed. + - For example, when asked to make edits or improvements you might analyze the file structure in the initial environment_details to get an overview of the project, then use list_code_definition_names to get further insight using source code definitions for files located in relevant directories, then read_file to examine the contents of relevant files, analyze the code and suggest improvements or make necessary edits, then use the write_to_file tool to apply the changes. If you refactored code that could affect other parts of the codebase, you could use search_files to ensure you update other files as needed. - You can use the execute_command tool to run commands on the user's computer whenever you feel it can help accomplish the user's task. When you need to execute a CLI command, you must provide a clear explanation of what the command does. Prefer to execute complex CLI commands over creating executable scripts, since they are more flexible and easier to run. Interactive and long-running commands are allowed, since the commands are run in the user's VSCode terminal. The user may keep commands running in the background and you will be kept updated on their status along the way. Each command you execute is run in a new terminal instance. ==== @@ -4125,9 +4125,9 @@ RULES - You cannot \`cd\` into a different directory to complete a task. You are stuck operating from '/test/path', so be sure to pass in the correct 'path' parameter when using tools that require a path. - Do not use the ~ character or $HOME to refer to the home directory. - Before using the execute_command tool, you must first think about the SYSTEM INFORMATION context provided to understand the user's environment and tailor your commands to ensure they are compatible with their system. You must also consider if the command you need to run should be executed in a specific directory outside of the current working directory '/test/path', and if so prepend with \`cd\`'ing into that directory && then executing the command (as one command since you are stuck operating from '/test/path'). For example, if you needed to run \`npm install\` in a project outside of '/test/path', you would need to prepend with a \`cd\` i.e. pseudocode for this would be \`cd (path to project) && (command, in this case npm install)\`. -- When using the search_files tool, craft your regex patterns carefully to balance specificity and flexibility. Based on the user's task you may use it to find code patterns, TODO comments, function definitions, or any text-based information across the project. The results include context, so analyze the surrounding code to better understand the matches. Leverage the search_files tool in combination with other tools for more comprehensive analysis. For example, use it to find specific code patterns, then use read_file to examine the full context of interesting matches before using create_file to make informed changes. -- When creating a new project (such as an app, website, or any software project), organize all new files within a dedicated project directory unless the user specifies otherwise. Use appropriate file paths when writing files, as the create_file tool will automatically create any necessary directories. Structure the project logically, adhering to best practices for the specific type of project being created. Unless otherwise specified, new projects should be easily run without additional setup, for example most projects can be built in HTML, CSS, and JavaScript - which you can open in a browser. -- When using the create_file tool to modify a file, use the tool directly with the desired content. You do not need to display the content before using the tool. ALWAYS provide the COMPLETE file content in your response. This is NON-NEGOTIABLE. Partial updates or placeholders like '// rest of code unchanged' are STRICTLY FORBIDDEN. You MUST include ALL parts of the file, even if they haven't been modified. Failure to do so will result in incomplete or broken code, severely impacting the user's project. +- When using the search_files tool, craft your regex patterns carefully to balance specificity and flexibility. Based on the user's task you may use it to find code patterns, TODO comments, function definitions, or any text-based information across the project. The results include context, so analyze the surrounding code to better understand the matches. Leverage the search_files tool in combination with other tools for more comprehensive analysis. For example, use it to find specific code patterns, then use read_file to examine the full context of interesting matches before using write_to_file to make informed changes. +- When creating a new project (such as an app, website, or any software project), organize all new files within a dedicated project directory unless the user specifies otherwise. Use appropriate file paths when writing files, as the write_to_file tool will automatically create any necessary directories. Structure the project logically, adhering to best practices for the specific type of project being created. Unless otherwise specified, new projects should be easily run without additional setup, for example most projects can be built in HTML, CSS, and JavaScript - which you can open in a browser. +- When using the write_to_file tool to modify a file, use the tool directly with the desired content. You do not need to display the content before using the tool. ALWAYS provide the COMPLETE file content in your response. This is NON-NEGOTIABLE. Partial updates or placeholders like '// rest of code unchanged' are STRICTLY FORBIDDEN. You MUST include ALL parts of the file, even if they haven't been modified. Failure to do so will result in incomplete or broken code, severely impacting the user's project. - Some modes have restrictions on which files they can edit. If you attempt to edit a restricted file, the operation will be rejected with a FileRestrictionError that will specify which file patterns are allowed for the current mode. - Be sure to consider the type of project (e.g. Python, JavaScript, web application) when determining the appropriate structure and files to include. Also consider what files may be most relevant to accomplishing the task, for example looking at a project's manifest file would help you understand the project's dependencies, which you could incorporate into any code you write. * For example, in architect mode trying to edit app.js would be rejected because architect mode can only edit files matching "\\.md$" @@ -4309,23 +4309,23 @@ Example: Requesting to list all top level source code definitions in the current . -## create_file +## write_to_file Description: Request to write full content to a file at the specified path. If the file exists, it will be overwritten with the provided content. If the file doesn't exist, it will be created. This tool will automatically create any directories needed to write the file. Parameters: - path: (required) The path of the file to write to (relative to the current working directory /test/path) - content: (required) The content to write to the file. ALWAYS provide the COMPLETE intended content of the file, without any truncation or omissions. You MUST include ALL parts of the file, even if they haven't been modified. Do NOT include the line numbers in the content though, just the actual content of the file. - line_count: (required) The number of lines in the file. Make sure to compute this based on the actual content of the file, not the number of lines in the content you're providing. Usage: - + File path here Your file content here total number of lines in the file, including empty lines - + Example: Requesting to write to frontend-config.json - + frontend-config.json { @@ -4344,7 +4344,7 @@ Example: Requesting to write to frontend-config.json } 14 - + ## execute_command Description: Request to execute a CLI command on the system. Use this when you need to perform system operations or run specific commands to accomplish any step in the user's task. You must tailor your command to the user's system and provide a clear explanation of what the command does. For command chaining, use the appropriate chaining syntax for the user's shell. Prefer to execute complex CLI commands over creating executable scripts, as they are more flexible and easier to run. Commands will be executed in the current working directory: /test/path @@ -4857,7 +4857,7 @@ IMPORTANT: Regardless of what else you see in the MCP settings file, you must de ## Editing MCP Servers -The user may ask to add tools or resources that may make sense to add to an existing MCP server (listed under 'Connected MCP Servers' above: (None running currently), e.g. if it would use the same API. This would be possible if you can locate the MCP server repository on the user's system by looking at the server arguments for a filepath. You might then use list_files and read_file to explore the files in the repository, and use create_file to make changes to the files. +The user may ask to add tools or resources that may make sense to add to an existing MCP server (listed under 'Connected MCP Servers' above: (None running currently), e.g. if it would use the same API. This would be possible if you can locate the MCP server repository on the user's system by looking at the server arguments for a filepath. You might then use list_files and read_file to explore the files in the repository, and use write_to_file to make changes to the files. However some MCP servers may be running from installed packages rather than a local repository, in which case it may make more sense to create a new MCP server. @@ -4875,7 +4875,7 @@ CAPABILITIES - When the user initially gives you a task, a recursive list of all filepaths in the current working directory ('/test/path') will be included in environment_details. This provides an overview of the project's file structure, offering key insights into the project from directory/file names (how developers conceptualize and organize their code) and file extensions (the language used). This can also guide decision-making on which files to explore further. If you need to further explore directories such as outside the current working directory, you can use the list_files tool. If you pass 'true' for the recursive parameter, it will list files recursively. Otherwise, it will list files at the top level, which is better suited for generic directories where you don't necessarily need the nested structure, like the Desktop. - You can use search_files to perform regex searches across files in a specified directory, outputting context-rich results that include surrounding lines. This is particularly useful for understanding code patterns, finding specific implementations, or identifying areas that need refactoring. - You can use the list_code_definition_names tool to get an overview of source code definitions for all files at the top level of a specified directory. This can be particularly useful when you need to understand the broader context and relationships between certain parts of the code. You may need to call this tool multiple times to understand various parts of the codebase related to the task. - - For example, when asked to make edits or improvements you might analyze the file structure in the initial environment_details to get an overview of the project, then use list_code_definition_names to get further insight using source code definitions for files located in relevant directories, then read_file to examine the contents of relevant files, analyze the code and suggest improvements or make necessary edits, then use the create_file tool to apply the changes. If you refactored code that could affect other parts of the codebase, you could use search_files to ensure you update other files as needed. + - For example, when asked to make edits or improvements you might analyze the file structure in the initial environment_details to get an overview of the project, then use list_code_definition_names to get further insight using source code definitions for files located in relevant directories, then read_file to examine the contents of relevant files, analyze the code and suggest improvements or make necessary edits, then use the write_to_file tool to apply the changes. If you refactored code that could affect other parts of the codebase, you could use search_files to ensure you update other files as needed. - You can use the execute_command tool to run commands on the user's computer whenever you feel it can help accomplish the user's task. When you need to execute a CLI command, you must provide a clear explanation of what the command does. Prefer to execute complex CLI commands over creating executable scripts, since they are more flexible and easier to run. Interactive and long-running commands are allowed, since the commands are run in the user's VSCode terminal. The user may keep commands running in the background and you will be kept updated on their status along the way. Each command you execute is run in a new terminal instance. - You have access to MCP servers that may provide additional tools and resources. Each server may provide different capabilities that you can use to accomplish tasks more effectively. @@ -4894,9 +4894,9 @@ RULES - You cannot \`cd\` into a different directory to complete a task. You are stuck operating from '/test/path', so be sure to pass in the correct 'path' parameter when using tools that require a path. - Do not use the ~ character or $HOME to refer to the home directory. - Before using the execute_command tool, you must first think about the SYSTEM INFORMATION context provided to understand the user's environment and tailor your commands to ensure they are compatible with their system. You must also consider if the command you need to run should be executed in a specific directory outside of the current working directory '/test/path', and if so prepend with \`cd\`'ing into that directory && then executing the command (as one command since you are stuck operating from '/test/path'). For example, if you needed to run \`npm install\` in a project outside of '/test/path', you would need to prepend with a \`cd\` i.e. pseudocode for this would be \`cd (path to project) && (command, in this case npm install)\`. -- When using the search_files tool, craft your regex patterns carefully to balance specificity and flexibility. Based on the user's task you may use it to find code patterns, TODO comments, function definitions, or any text-based information across the project. The results include context, so analyze the surrounding code to better understand the matches. Leverage the search_files tool in combination with other tools for more comprehensive analysis. For example, use it to find specific code patterns, then use read_file to examine the full context of interesting matches before using create_file to make informed changes. -- When creating a new project (such as an app, website, or any software project), organize all new files within a dedicated project directory unless the user specifies otherwise. Use appropriate file paths when writing files, as the create_file tool will automatically create any necessary directories. Structure the project logically, adhering to best practices for the specific type of project being created. Unless otherwise specified, new projects should be easily run without additional setup, for example most projects can be built in HTML, CSS, and JavaScript - which you can open in a browser. -- When using the create_file tool to modify a file, use the tool directly with the desired content. You do not need to display the content before using the tool. ALWAYS provide the COMPLETE file content in your response. This is NON-NEGOTIABLE. Partial updates or placeholders like '// rest of code unchanged' are STRICTLY FORBIDDEN. You MUST include ALL parts of the file, even if they haven't been modified. Failure to do so will result in incomplete or broken code, severely impacting the user's project. +- When using the search_files tool, craft your regex patterns carefully to balance specificity and flexibility. Based on the user's task you may use it to find code patterns, TODO comments, function definitions, or any text-based information across the project. The results include context, so analyze the surrounding code to better understand the matches. Leverage the search_files tool in combination with other tools for more comprehensive analysis. For example, use it to find specific code patterns, then use read_file to examine the full context of interesting matches before using write_to_file to make informed changes. +- When creating a new project (such as an app, website, or any software project), organize all new files within a dedicated project directory unless the user specifies otherwise. Use appropriate file paths when writing files, as the write_to_file tool will automatically create any necessary directories. Structure the project logically, adhering to best practices for the specific type of project being created. Unless otherwise specified, new projects should be easily run without additional setup, for example most projects can be built in HTML, CSS, and JavaScript - which you can open in a browser. +- When using the write_to_file tool to modify a file, use the tool directly with the desired content. You do not need to display the content before using the tool. ALWAYS provide the COMPLETE file content in your response. This is NON-NEGOTIABLE. Partial updates or placeholders like '// rest of code unchanged' are STRICTLY FORBIDDEN. You MUST include ALL parts of the file, even if they haven't been modified. Failure to do so will result in incomplete or broken code, severely impacting the user's project. - Some modes have restrictions on which files they can edit. If you attempt to edit a restricted file, the operation will be rejected with a FileRestrictionError that will specify which file patterns are allowed for the current mode. - Be sure to consider the type of project (e.g. Python, JavaScript, web application) when determining the appropriate structure and files to include. Also consider what files may be most relevant to accomplishing the task, for example looking at a project's manifest file would help you understand the project's dependencies, which you could incorporate into any code you write. * For example, in architect mode trying to edit app.js would be rejected because architect mode can only edit files matching "\\.md$" diff --git a/src/core/prompts/__tests__/sections.test.ts b/src/core/prompts/__tests__/sections.test.ts index 75af4ce6a6b..2100016e467 100644 --- a/src/core/prompts/__tests__/sections.test.ts +++ b/src/core/prompts/__tests__/sections.test.ts @@ -33,24 +33,24 @@ describe("getCapabilitiesSection", () => { const cwd = "/test/path" const mcpHub = undefined const mockDiffStrategy: DiffStrategy = { - getToolDescription: () => "edit_file tool description", + getToolDescription: () => "apply_diff tool description", applyDiff: async (originalContent: string, diffContent: string): Promise => { return { success: true, content: "mock result" } }, } - test("includes edit_file in capabilities when diffStrategy is provided", () => { + test("includes apply_diff in capabilities when diffStrategy is provided", () => { const result = getCapabilitiesSection(cwd, false, mcpHub, mockDiffStrategy) - expect(result).toContain("or create_file") - expect(result).toContain("then use the edit_file or create_file tool") + expect(result).toContain("or apply_diff") + expect(result).toContain("then use the write_to_file or apply_diff tool") }) - test("excludes edit_file from capabilities when diffStrategy is undefined", () => { + test("excludes apply_diff from capabilities when diffStrategy is undefined", () => { const result = getCapabilitiesSection(cwd, false, mcpHub, undefined) - expect(result).not.toContain("or edit_file") - expect(result).toContain("then use the create_file tool") - expect(result).not.toContain("create_file or edit_file") + expect(result).not.toContain("or apply_diff") + expect(result).toContain("then use the write_to_file tool") + expect(result).not.toContain("write_to_file or apply_diff") }) }) diff --git a/src/core/prompts/__tests__/system.test.ts b/src/core/prompts/__tests__/system.test.ts index 5f936fd4058..2adfa927eb6 100644 --- a/src/core/prompts/__tests__/system.test.ts +++ b/src/core/prompts/__tests__/system.test.ts @@ -288,7 +288,7 @@ describe("SYSTEM_PROMPT", () => { true, // enableMcpServerCreation ) - expect(prompt).toContain("edit_file") + expect(prompt).toContain("apply_diff") expect(prompt).toMatchSnapshot() }) @@ -310,7 +310,7 @@ describe("SYSTEM_PROMPT", () => { true, // enableMcpServerCreation ) - expect(prompt).not.toContain("edit_file") + expect(prompt).not.toContain("apply_diff") expect(prompt).toMatchSnapshot() }) @@ -332,7 +332,7 @@ describe("SYSTEM_PROMPT", () => { true, // enableMcpServerCreation ) - expect(prompt).not.toContain("edit_file") + expect(prompt).not.toContain("apply_diff") expect(prompt).toMatchSnapshot() }) @@ -562,8 +562,8 @@ describe("SYSTEM_PROMPT", () => { ) // Verify base instruction lists all available tools - expect(prompt).toContain("edit_file (for replacing lines in existing files)") - expect(prompt).toContain("create_file (for creating new files or complete file rewrites)") + expect(prompt).toContain("apply_diff (for replacing lines in existing files)") + expect(prompt).toContain("write_to_file (for creating new files or complete file rewrites)") expect(prompt).toContain("insert_content (for adding lines to existing files)") expect(prompt).toContain("search_and_replace (for finding and replacing individual pieces of text)") }) @@ -593,7 +593,7 @@ describe("SYSTEM_PROMPT", () => { // Verify detailed instructions for each tool expect(prompt).toContain( - "You should always prefer using other editing tools over create_file when making changes to existing files since create_file is much slower and cannot handle large files.", + "You should always prefer using other editing tools over write_to_file when making changes to existing files since write_to_file is much slower and cannot handle large files.", ) expect(prompt).toContain("The insert_content tool adds lines of text to files") expect(prompt).toContain("The search_and_replace tool finds and replaces text or regex in files") diff --git a/src/core/prompts/sections/capabilities.ts b/src/core/prompts/sections/capabilities.ts index 9cd39bde580..c292eeffbc3 100644 --- a/src/core/prompts/sections/capabilities.ts +++ b/src/core/prompts/sections/capabilities.ts @@ -17,7 +17,7 @@ CAPABILITIES - When the user initially gives you a task, a recursive list of all filepaths in the current working directory ('${cwd}') will be included in environment_details. This provides an overview of the project's file structure, offering key insights into the project from directory/file names (how developers conceptualize and organize their code) and file extensions (the language used). This can also guide decision-making on which files to explore further. If you need to further explore directories such as outside the current working directory, you can use the list_files tool. If you pass 'true' for the recursive parameter, it will list files recursively. Otherwise, it will list files at the top level, which is better suited for generic directories where you don't necessarily need the nested structure, like the Desktop. - You can use search_files to perform regex searches across files in a specified directory, outputting context-rich results that include surrounding lines. This is particularly useful for understanding code patterns, finding specific implementations, or identifying areas that need refactoring. - You can use the list_code_definition_names tool to get an overview of source code definitions for all files at the top level of a specified directory. This can be particularly useful when you need to understand the broader context and relationships between certain parts of the code. You may need to call this tool multiple times to understand various parts of the codebase related to the task. - - For example, when asked to make edits or improvements you might analyze the file structure in the initial environment_details to get an overview of the project, then use list_code_definition_names to get further insight using source code definitions for files located in relevant directories, then read_file to examine the contents of relevant files, analyze the code and suggest improvements or make necessary edits, then use ${diffStrategy ? "the edit_file or create_file" : "the create_file"} tool to apply the changes. If you refactored code that could affect other parts of the codebase, you could use search_files to ensure you update other files as needed. + - For example, when asked to make edits or improvements you might analyze the file structure in the initial environment_details to get an overview of the project, then use list_code_definition_names to get further insight using source code definitions for files located in relevant directories, then read_file to examine the contents of relevant files, analyze the code and suggest improvements or make necessary edits, then use the write_to_file${diffStrategy ? " or apply_diff" : ""} tool to apply the changes. If you refactored code that could affect other parts of the codebase, you could use search_files to ensure you update other files as needed. - You can use the execute_command tool to run commands on the user's computer whenever you feel it can help accomplish the user's task. When you need to execute a CLI command, you must provide a clear explanation of what the command does. Prefer to execute complex CLI commands over creating executable scripts, since they are more flexible and easier to run. Interactive and long-running commands are allowed, since the commands are run in the user's VSCode terminal. The user may keep commands running in the background and you will be kept updated on their status along the way. Each command you execute is run in a new terminal instance.${ supportsComputerUse ? "\n- You can use the browser_action tool to interact with websites (including html files and locally running development servers) through a Puppeteer-controlled browser when you feel it is necessary in accomplishing the user's task. This tool is particularly useful for web development tasks as it allows you to launch a browser, navigate to pages, interact with elements through clicks and keyboard input, and capture the results through screenshots and console logs. This tool may be useful at key stages of web development tasks-such as after implementing new features, making substantial changes, when troubleshooting issues, or to verify the result of your work. You can analyze the provided screenshots to ensure correct rendering or identify errors, and review console logs for runtime issues.\n - For example, if asked to add a component to a react website, you might create the necessary files, use execute_command to run the site locally, then use browser_action to launch the browser, navigate to the local server, and verify the component renders & functions correctly before closing the browser." diff --git a/src/core/prompts/sections/mcp-servers.ts b/src/core/prompts/sections/mcp-servers.ts index fd7f520ddd9..3f7ec88297c 100644 --- a/src/core/prompts/sections/mcp-servers.ts +++ b/src/core/prompts/sections/mcp-servers.ts @@ -414,7 +414,7 @@ The user may ask to add tools or resources that may make sense to add to an exis .getServers() .map((server) => server.name) .join(", ") || "(None running currently)" - }, e.g. if it would use the same API. This would be possible if you can locate the MCP server repository on the user's system by looking at the server arguments for a filepath. You might then use list_files and read_file to explore the files in the repository, and use create_file${diffStrategy ? " or edit_file" : ""} to make changes to the files. + }, e.g. if it would use the same API. This would be possible if you can locate the MCP server repository on the user's system by looking at the server arguments for a filepath. You might then use list_files and read_file to explore the files in the repository, and use write_to_file${diffStrategy ? " or apply_diff" : ""} to make changes to the files. However some MCP servers may be running from installed packages rather than a local repository, in which case it may make more sense to create a new MCP server. diff --git a/src/core/prompts/sections/modes.ts b/src/core/prompts/sections/modes.ts index de3cac9c947..eff950c2c2f 100644 --- a/src/core/prompts/sections/modes.ts +++ b/src/core/prompts/sections/modes.ts @@ -45,7 +45,7 @@ Both files should follow this structure: "roleDefinition": "You are Roo, a UI/UX expert specializing in design systems and frontend development. Your expertise includes:\\n- Creating and maintaining design systems\\n- Implementing responsive and accessible web interfaces\\n- Working with CSS, HTML, and modern frontend frameworks\\n- Ensuring consistent user experiences across platforms", // Required: non-empty "groups": [ // Required: array of tool groups (can be empty) "read", // Read files group (read_file, search_files, list_files, list_code_definition_names) - "edit", // Edit files group (edit_file, create_file) - allows editing any file + "edit", // Edit files group (write_to_file, apply_diff) - allows editing any file // Or with file restrictions: // ["edit", { fileRegex: "\\.md$", description: "Markdown files only" }], // Edit group that only allows editing markdown files "browser", // Browser group (browser_action) diff --git a/src/core/prompts/sections/rules.ts b/src/core/prompts/sections/rules.ts index e0d65976182..b6e19eb08c9 100644 --- a/src/core/prompts/sections/rules.ts +++ b/src/core/prompts/sections/rules.ts @@ -5,16 +5,11 @@ import * as path from "path" function getEditingInstructions(diffStrategy?: DiffStrategy, experiments?: Record): string { const instructions: string[] = [] - const availableTools: string[] = [] + const availableTools: string[] = ["write_to_file (for creating new files or complete file rewrites)"] // Collect available editing tools if (diffStrategy) { - availableTools.push( - "edit_file (for replacing lines in existing files)", - "create_file (for creating new files or complete file rewrites)", - ) - } else { - availableTools.push("create_file (for creating new files or complete file rewrites)") + availableTools.push("apply_diff (for replacing lines in existing files)") } if (experiments?.["insert_content"]) { availableTools.push("insert_content (for adding lines to existing files)") @@ -41,16 +36,16 @@ function getEditingInstructions(diffStrategy?: DiffStrategy, experiments?: Recor ) } + instructions.push( + "- When using the write_to_file tool to modify a file, use the tool directly with the desired content. You do not need to display the content before using the tool. ALWAYS provide the COMPLETE file content in your response. This is NON-NEGOTIABLE. Partial updates or placeholders like '// rest of code unchanged' are STRICTLY FORBIDDEN. You MUST include ALL parts of the file, even if they haven't been modified. Failure to do so will result in incomplete or broken code, severely impacting the user's project.", + ) + if (availableTools.length > 1) { instructions.push( - "- You should always prefer using other editing tools over create_file when making changes to existing files since create_file is much slower and cannot handle large files.", + "- You should always prefer using other editing tools over write_to_file when making changes to existing files since write_to_file is much slower and cannot handle large files.", ) } - instructions.push( - "- When using the create_file tool to modify a file, use the tool directly with the desired content. You do not need to display the content before using the tool. ALWAYS provide the COMPLETE file content in your response. This is NON-NEGOTIABLE. Partial updates or placeholders like '// rest of code unchanged' are STRICTLY FORBIDDEN. You MUST include ALL parts of the file, even if they haven't been modified. Failure to do so will result in incomplete or broken code, severely impacting the user's project.", - ) - return instructions.join("\n") } @@ -68,8 +63,8 @@ RULES - You cannot \`cd\` into a different directory to complete a task. You are stuck operating from '${cwd.toPosix()}', so be sure to pass in the correct 'path' parameter when using tools that require a path. - Do not use the ~ character or $HOME to refer to the home directory. - Before using the execute_command tool, you must first think about the SYSTEM INFORMATION context provided to understand the user's environment and tailor your commands to ensure they are compatible with their system. You must also consider if the command you need to run should be executed in a specific directory outside of the current working directory '${cwd.toPosix()}', and if so prepend with \`cd\`'ing into that directory && then executing the command (as one command since you are stuck operating from '${cwd.toPosix()}'). For example, if you needed to run \`npm install\` in a project outside of '${cwd.toPosix()}', you would need to prepend with a \`cd\` i.e. pseudocode for this would be \`cd (path to project) && (command, in this case npm install)\`. -- When using the search_files tool, craft your regex patterns carefully to balance specificity and flexibility. Based on the user's task you may use it to find code patterns, TODO comments, function definitions, or any text-based information across the project. The results include context, so analyze the surrounding code to better understand the matches. Leverage the search_files tool in combination with other tools for more comprehensive analysis. For example, use it to find specific code patterns, then use read_file to examine the full context of interesting matches before using ${diffStrategy ? "edit_file or create_file" : "create_file"} to make informed changes. -- When creating a new project (such as an app, website, or any software project), organize all new files within a dedicated project directory unless the user specifies otherwise. Use appropriate file paths when writing files, as the create_file tool will automatically create any necessary directories. Structure the project logically, adhering to best practices for the specific type of project being created. Unless otherwise specified, new projects should be easily run without additional setup, for example most projects can be built in HTML, CSS, and JavaScript - which you can open in a browser. +- When using the search_files tool, craft your regex patterns carefully to balance specificity and flexibility. Based on the user's task you may use it to find code patterns, TODO comments, function definitions, or any text-based information across the project. The results include context, so analyze the surrounding code to better understand the matches. Leverage the search_files tool in combination with other tools for more comprehensive analysis. For example, use it to find specific code patterns, then use read_file to examine the full context of interesting matches before using write_to_file to make informed changes. +- When creating a new project (such as an app, website, or any software project), organize all new files within a dedicated project directory unless the user specifies otherwise. Use appropriate file paths when writing files, as the write_to_file tool will automatically create any necessary directories. Structure the project logically, adhering to best practices for the specific type of project being created. Unless otherwise specified, new projects should be easily run without additional setup, for example most projects can be built in HTML, CSS, and JavaScript - which you can open in a browser. ${getEditingInstructions(diffStrategy, experiments)} - Some modes have restrictions on which files they can edit. If you attempt to edit a restricted file, the operation will be rejected with a FileRestrictionError that will specify which file patterns are allowed for the current mode. - Be sure to consider the type of project (e.g. Python, JavaScript, web application) when determining the appropriate structure and files to include. Also consider what files may be most relevant to accomplishing the task, for example looking at a project's manifest file would help you understand the project's dependencies, which you could incorporate into any code you write. diff --git a/src/core/prompts/tools/index.ts b/src/core/prompts/tools/index.ts index 6310620aac9..1b9b9a43d9d 100644 --- a/src/core/prompts/tools/index.ts +++ b/src/core/prompts/tools/index.ts @@ -23,7 +23,7 @@ import { ToolArgs } from "./types" const toolDescriptionMap: Record string | undefined> = { execute_command: (args) => getExecuteCommandDescription(args), read_file: (args) => getReadFileDescription(args), - create_file: (args) => getWriteToFileDescription(args), + write_to_file: (args) => getWriteToFileDescription(args), search_files: (args) => getSearchFilesDescription(args), list_files: (args) => getListFilesDescription(args), list_code_definition_names: (args) => getListCodeDefinitionNamesDescription(args), @@ -36,7 +36,7 @@ const toolDescriptionMap: Record string | undefined> new_task: (args) => getNewTaskDescription(args), insert_content: (args) => getInsertContentDescription(args), search_and_replace: (args) => getSearchAndReplaceDescription(args), - edit_file: (args) => + apply_diff: (args) => args.diffStrategy ? args.diffStrategy.getToolDescription({ cwd: args.cwd, toolOptions: args.toolOptions }) : "", } diff --git a/src/core/prompts/tools/write-to-file.ts b/src/core/prompts/tools/write-to-file.ts index 7a20e9b3f4f..c2a311cf361 100644 --- a/src/core/prompts/tools/write-to-file.ts +++ b/src/core/prompts/tools/write-to-file.ts @@ -1,23 +1,23 @@ import { ToolArgs } from "./types" export function getWriteToFileDescription(args: ToolArgs): string { - return `## create_file + return `## write_to_file Description: Request to write full content to a file at the specified path. If the file exists, it will be overwritten with the provided content. If the file doesn't exist, it will be created. This tool will automatically create any directories needed to write the file. Parameters: - path: (required) The path of the file to write to (relative to the current working directory ${args.cwd}) - content: (required) The content to write to the file. ALWAYS provide the COMPLETE intended content of the file, without any truncation or omissions. You MUST include ALL parts of the file, even if they haven't been modified. Do NOT include the line numbers in the content though, just the actual content of the file. - line_count: (required) The number of lines in the file. Make sure to compute this based on the actual content of the file, not the number of lines in the content you're providing. Usage: - + File path here Your file content here total number of lines in the file, including empty lines - + Example: Requesting to write to frontend-config.json - + frontend-config.json { @@ -36,5 +36,5 @@ Example: Requesting to write to frontend-config.json } 14 -` +` } diff --git a/src/core/webview/__tests__/ClineProvider.test.ts b/src/core/webview/__tests__/ClineProvider.test.ts index f8df84721da..6449cc93bec 100644 --- a/src/core/webview/__tests__/ClineProvider.test.ts +++ b/src/core/webview/__tests__/ClineProvider.test.ts @@ -107,7 +107,7 @@ jest.mock( // Mock DiffStrategy jest.mock("../../diff/DiffStrategy", () => ({ getDiffStrategy: jest.fn().mockImplementation(() => ({ - getToolDescription: jest.fn().mockReturnValue("edit_file tool description"), + getToolDescription: jest.fn().mockReturnValue("apply_diff tool description"), })), })) diff --git a/src/shared/ExtensionMessage.ts b/src/shared/ExtensionMessage.ts index 5d0e16e39cd..fe9fa394270 100644 --- a/src/shared/ExtensionMessage.ts +++ b/src/shared/ExtensionMessage.ts @@ -127,7 +127,7 @@ export interface ExtensionState { experiments: Record // Map of experiment IDs to their enabled state autoApprovalEnabled?: boolean customModes: ModeConfig[] - toolRequirements?: Record // Map of tool names to their requirements (e.g. {"edit_file": true} if diffEnabled) + toolRequirements?: Record // Map of tool names to their requirements (e.g. {"apply_diff": true} if diffEnabled) maxOpenTabsContext: number // Maximum number of VSCode open tabs to include in context (0-500) } diff --git a/src/shared/__tests__/modes.test.ts b/src/shared/__tests__/modes.test.ts index 52d26735a9e..3bd89c4ecb5 100644 --- a/src/shared/__tests__/modes.test.ts +++ b/src/shared/__tests__/modes.test.ts @@ -44,14 +44,14 @@ describe("isToolAllowedForMode", () => { describe("file restrictions", () => { it("allows editing matching files", () => { // Test markdown editor mode - const mdResult = isToolAllowedForMode("create_file", "markdown-editor", customModes, undefined, { + const mdResult = isToolAllowedForMode("write_to_file", "markdown-editor", customModes, undefined, { path: "test.md", content: "# Test", }) expect(mdResult).toBe(true) // Test CSS editor mode - const cssResult = isToolAllowedForMode("create_file", "css-editor", customModes, undefined, { + const cssResult = isToolAllowedForMode("write_to_file", "css-editor", customModes, undefined, { path: "styles.css", content: ".test { color: red; }", }) @@ -61,13 +61,13 @@ describe("isToolAllowedForMode", () => { it("rejects editing non-matching files", () => { // Test markdown editor mode with non-markdown file expect(() => - isToolAllowedForMode("create_file", "markdown-editor", customModes, undefined, { + isToolAllowedForMode("write_to_file", "markdown-editor", customModes, undefined, { path: "test.js", content: "console.log('test')", }), ).toThrow(FileRestrictionError) expect(() => - isToolAllowedForMode("create_file", "markdown-editor", customModes, undefined, { + isToolAllowedForMode("write_to_file", "markdown-editor", customModes, undefined, { path: "test.js", content: "console.log('test')", }), @@ -75,13 +75,13 @@ describe("isToolAllowedForMode", () => { // Test CSS editor mode with non-CSS file expect(() => - isToolAllowedForMode("create_file", "css-editor", customModes, undefined, { + isToolAllowedForMode("write_to_file", "css-editor", customModes, undefined, { path: "test.js", content: "console.log('test')", }), ).toThrow(FileRestrictionError) expect(() => - isToolAllowedForMode("create_file", "css-editor", customModes, undefined, { + isToolAllowedForMode("write_to_file", "css-editor", customModes, undefined, { path: "test.js", content: "console.log('test')", }), @@ -91,35 +91,35 @@ describe("isToolAllowedForMode", () => { it("handles partial streaming cases (path only, no content/diff)", () => { // Should allow path-only for matching files (no validation yet since content/diff not provided) expect( - isToolAllowedForMode("create_file", "markdown-editor", customModes, undefined, { + isToolAllowedForMode("write_to_file", "markdown-editor", customModes, undefined, { path: "test.js", }), ).toBe(true) expect( - isToolAllowedForMode("edit_file", "markdown-editor", customModes, undefined, { + isToolAllowedForMode("apply_diff", "markdown-editor", customModes, undefined, { path: "test.js", }), ).toBe(true) // Should allow path-only for architect mode too expect( - isToolAllowedForMode("create_file", "architect", [], undefined, { + isToolAllowedForMode("write_to_file", "architect", [], undefined, { path: "test.js", }), ).toBe(true) }) - it("applies restrictions to both create_file and edit_file", () => { - // Test create_file - const writeResult = isToolAllowedForMode("create_file", "markdown-editor", customModes, undefined, { + it("applies restrictions to both write_to_file and apply_diff", () => { + // Test write_to_file + const writeResult = isToolAllowedForMode("write_to_file", "markdown-editor", customModes, undefined, { path: "test.md", content: "# Test", }) expect(writeResult).toBe(true) - // Test edit_file - const diffResult = isToolAllowedForMode("edit_file", "markdown-editor", customModes, undefined, { + // Test apply_diff + const diffResult = isToolAllowedForMode("apply_diff", "markdown-editor", customModes, undefined, { path: "test.md", diff: "- old\n+ new", }) @@ -127,14 +127,14 @@ describe("isToolAllowedForMode", () => { // Test both with non-matching file expect(() => - isToolAllowedForMode("create_file", "markdown-editor", customModes, undefined, { + isToolAllowedForMode("write_to_file", "markdown-editor", customModes, undefined, { path: "test.js", content: "console.log('test')", }), ).toThrow(FileRestrictionError) expect(() => - isToolAllowedForMode("edit_file", "markdown-editor", customModes, undefined, { + isToolAllowedForMode("apply_diff", "markdown-editor", customModes, undefined, { path: "test.js", diff: "- old\n+ new", }), @@ -155,29 +155,29 @@ describe("isToolAllowedForMode", () => { }, ] - // Test create_file with non-matching file + // Test write_to_file with non-matching file expect(() => - isToolAllowedForMode("create_file", "docs-editor", customModesWithDescription, undefined, { + isToolAllowedForMode("write_to_file", "docs-editor", customModesWithDescription, undefined, { path: "test.js", content: "console.log('test')", }), ).toThrow(FileRestrictionError) expect(() => - isToolAllowedForMode("create_file", "docs-editor", customModesWithDescription, undefined, { + isToolAllowedForMode("write_to_file", "docs-editor", customModesWithDescription, undefined, { path: "test.js", content: "console.log('test')", }), ).toThrow(/Documentation files only/) - // Test edit_file with non-matching file + // Test apply_diff with non-matching file expect(() => - isToolAllowedForMode("edit_file", "docs-editor", customModesWithDescription, undefined, { + isToolAllowedForMode("apply_diff", "docs-editor", customModesWithDescription, undefined, { path: "test.js", diff: "- old\n+ new", }), ).toThrow(FileRestrictionError) expect(() => - isToolAllowedForMode("edit_file", "docs-editor", customModesWithDescription, undefined, { + isToolAllowedForMode("apply_diff", "docs-editor", customModesWithDescription, undefined, { path: "test.js", diff: "- old\n+ new", }), @@ -185,14 +185,14 @@ describe("isToolAllowedForMode", () => { // Test that matching files are allowed expect( - isToolAllowedForMode("create_file", "docs-editor", customModesWithDescription, undefined, { + isToolAllowedForMode("write_to_file", "docs-editor", customModesWithDescription, undefined, { path: "test.md", content: "# Test", }), ).toBe(true) expect( - isToolAllowedForMode("create_file", "docs-editor", customModesWithDescription, undefined, { + isToolAllowedForMode("write_to_file", "docs-editor", customModesWithDescription, undefined, { path: "test.txt", content: "Test content", }), @@ -200,7 +200,7 @@ describe("isToolAllowedForMode", () => { // Test partial streaming cases expect( - isToolAllowedForMode("create_file", "docs-editor", customModesWithDescription, undefined, { + isToolAllowedForMode("write_to_file", "docs-editor", customModesWithDescription, undefined, { path: "test.js", }), ).toBe(true) @@ -209,7 +209,7 @@ describe("isToolAllowedForMode", () => { it("allows architect mode to edit markdown files only", () => { // Should allow editing markdown files expect( - isToolAllowedForMode("create_file", "architect", [], undefined, { + isToolAllowedForMode("write_to_file", "architect", [], undefined, { path: "test.md", content: "# Test", }), @@ -217,7 +217,7 @@ describe("isToolAllowedForMode", () => { // Should allow applying diffs to markdown files expect( - isToolAllowedForMode("edit_file", "architect", [], undefined, { + isToolAllowedForMode("apply_diff", "architect", [], undefined, { path: "readme.md", diff: "- old\n+ new", }), @@ -225,13 +225,13 @@ describe("isToolAllowedForMode", () => { // Should reject non-markdown files expect(() => - isToolAllowedForMode("create_file", "architect", [], undefined, { + isToolAllowedForMode("write_to_file", "architect", [], undefined, { path: "test.js", content: "console.log('test')", }), ).toThrow(FileRestrictionError) expect(() => - isToolAllowedForMode("create_file", "architect", [], undefined, { + isToolAllowedForMode("write_to_file", "architect", [], undefined, { path: "test.js", content: "console.log('test')", }), @@ -245,15 +245,15 @@ describe("isToolAllowedForMode", () => { }) it("handles non-existent modes", () => { - expect(isToolAllowedForMode("create_file", "non-existent", customModes)).toBe(false) + expect(isToolAllowedForMode("write_to_file", "non-existent", customModes)).toBe(false) }) it("respects tool requirements", () => { const toolRequirements = { - create_file: false, + write_to_file: false, } - expect(isToolAllowedForMode("create_file", "markdown-editor", customModes, toolRequirements)).toBe(false) + expect(isToolAllowedForMode("write_to_file", "markdown-editor", customModes, toolRequirements)).toBe(false) }) describe("experimental tools", () => { @@ -312,7 +312,7 @@ describe("isToolAllowedForMode", () => { ).toBe(true) expect( isToolAllowedForMode( - "create_file", + "write_to_file", "markdown-editor", customModes, undefined, diff --git a/src/shared/tool-groups.ts b/src/shared/tool-groups.ts index 8a25e1400ee..2728d42319d 100644 --- a/src/shared/tool-groups.ts +++ b/src/shared/tool-groups.ts @@ -8,8 +8,8 @@ export type ToolGroupConfig = { export const TOOL_DISPLAY_NAMES = { execute_command: "run commands", read_file: "read files", - create_file: "write files", - edit_file: "apply changes", + write_to_file: "write files", + apply_diff: "apply changes", search_files: "search files", list_files: "list files", list_code_definition_names: "list definitions", @@ -28,7 +28,7 @@ export const TOOL_GROUPS: Record = { tools: ["read_file", "search_files", "list_files", "list_code_definition_names"], }, edit: { - tools: ["edit_file", "create_file", "insert_content", "search_and_replace"], + tools: ["write_to_file", "apply_diff", "insert_content", "search_and_replace"], }, browser: { tools: ["browser_action"], From c677a450d197bb9b7ee856d2c7353e00abe35b2c Mon Sep 17 00:00:00 2001 From: cte Date: Mon, 24 Feb 2025 11:30:15 -0800 Subject: [PATCH 044/585] Fix maxTokens --- src/shared/api.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/shared/api.ts b/src/shared/api.ts index 3598eb16ac9..129704ace26 100644 --- a/src/shared/api.ts +++ b/src/shared/api.ts @@ -96,7 +96,7 @@ export type AnthropicModelId = keyof typeof anthropicModels export const anthropicDefaultModelId: AnthropicModelId = "claude-3-5-sonnet-20241022" export const anthropicModels = { "claude-3-7-sonnet-20250219": { - maxTokens: 128_000, + maxTokens: 64_000, contextWindow: 200_000, supportsImages: true, supportsComputerUse: true, From 30b10c4e266c5ff10710fa697f5a4c989b96215a Mon Sep 17 00:00:00 2001 From: Matt Rubens Date: Mon, 24 Feb 2025 13:39:08 -0600 Subject: [PATCH 045/585] 3.7 --- .changeset/warm-kangaroos-give.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/warm-kangaroos-give.md diff --git a/.changeset/warm-kangaroos-give.md b/.changeset/warm-kangaroos-give.md new file mode 100644 index 00000000000..a95f6a1cf89 --- /dev/null +++ b/.changeset/warm-kangaroos-give.md @@ -0,0 +1,5 @@ +--- +"roo-cline": minor +--- + +3.7 From 158b9d4659fd8d48dccf02c1a81915dff0b8b667 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Mon, 24 Feb 2025 19:42:40 +0000 Subject: [PATCH 046/585] changeset version bump --- .changeset/warm-kangaroos-give.md | 5 ----- CHANGELOG.md | 6 ++++++ package-lock.json | 4 ++-- package.json | 2 +- 4 files changed, 9 insertions(+), 8 deletions(-) delete mode 100644 .changeset/warm-kangaroos-give.md diff --git a/.changeset/warm-kangaroos-give.md b/.changeset/warm-kangaroos-give.md deleted file mode 100644 index a95f6a1cf89..00000000000 --- a/.changeset/warm-kangaroos-give.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"roo-cline": minor ---- - -3.7 diff --git a/CHANGELOG.md b/CHANGELOG.md index 748f8c78f88..6b85eed8cac 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ # Roo Code Changelog +## 3.4.0 + +### Minor Changes + +- 3.7 + ## [3.3.26] - Adjust the default prompt for Debug mode to focus more on diagnosis and to require user confirmation before moving on to implementation diff --git a/package-lock.json b/package-lock.json index 9822548678d..f5648649b52 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "roo-cline", - "version": "3.3.26", + "version": "3.4.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "roo-cline", - "version": "3.3.26", + "version": "3.4.0", "dependencies": { "@anthropic-ai/bedrock-sdk": "^0.10.2", "@anthropic-ai/sdk": "^0.26.0", diff --git a/package.json b/package.json index c4c38e78571..d64f0c7b021 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.26", + "version": "3.4.0", "icon": "assets/icons/rocket.png", "galleryBanner": { "color": "#617A91", From 9a41763dbbde56256102deb691a1039bc040a1dc Mon Sep 17 00:00:00 2001 From: Matt Rubens Date: Mon, 24 Feb 2025 13:47:55 -0600 Subject: [PATCH 047/585] Update CHANGELOG.md --- CHANGELOG.md | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6b85eed8cac..2d4546a2e14 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,10 +1,8 @@ # Roo Code Changelog -## 3.4.0 +## [3.7.0] -### Minor Changes - -- 3.7 +- Introducing Roo Code 3.7, with support for the new Claude Sonnet 3.7. Because who cares about skipping version numbers anymore? Thanks @lupuletic and @cte for the PRs! ## [3.3.26] From 8b28189850959d86b961c045fb9fb7ed84e821f2 Mon Sep 17 00:00:00 2001 From: Matt Rubens Date: Mon, 24 Feb 2025 13:48:10 -0600 Subject: [PATCH 048/585] Update package.json --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index d64f0c7b021..0d9e545090c 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.4.0", + "version": "3.7.0", "icon": "assets/icons/rocket.png", "galleryBanner": { "color": "#617A91", From 3845338702500630c0ade45ddcee50c65667f412 Mon Sep 17 00:00:00 2001 From: Matt Rubens Date: Mon, 24 Feb 2025 13:48:53 -0600 Subject: [PATCH 049/585] Update package-lock.json --- package-lock.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index f5648649b52..7650f96ea58 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "roo-cline", - "version": "3.4.0", + "version": "3.7.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "roo-cline", - "version": "3.4.0", + "version": "3.7.0", "dependencies": { "@anthropic-ai/bedrock-sdk": "^0.10.2", "@anthropic-ai/sdk": "^0.26.0", From 35a56a991854e03f317e93e0390bc5556ef5814c Mon Sep 17 00:00:00 2001 From: Roo Code Date: Sat, 22 Feb 2025 16:38:46 -0700 Subject: [PATCH 050/585] Reorder tool groups and update tool usage instructions to always mention apply_diff before write_to_file --- src/core/prompts/sections/capabilities.ts | 2 +- src/core/prompts/sections/modes.ts | 2 +- src/core/prompts/sections/rules.ts | 19 ++++++++++++------- src/shared/tool-groups.ts | 2 +- 4 files changed, 15 insertions(+), 10 deletions(-) diff --git a/src/core/prompts/sections/capabilities.ts b/src/core/prompts/sections/capabilities.ts index c292eeffbc3..983d07bf761 100644 --- a/src/core/prompts/sections/capabilities.ts +++ b/src/core/prompts/sections/capabilities.ts @@ -17,7 +17,7 @@ CAPABILITIES - When the user initially gives you a task, a recursive list of all filepaths in the current working directory ('${cwd}') will be included in environment_details. This provides an overview of the project's file structure, offering key insights into the project from directory/file names (how developers conceptualize and organize their code) and file extensions (the language used). This can also guide decision-making on which files to explore further. If you need to further explore directories such as outside the current working directory, you can use the list_files tool. If you pass 'true' for the recursive parameter, it will list files recursively. Otherwise, it will list files at the top level, which is better suited for generic directories where you don't necessarily need the nested structure, like the Desktop. - You can use search_files to perform regex searches across files in a specified directory, outputting context-rich results that include surrounding lines. This is particularly useful for understanding code patterns, finding specific implementations, or identifying areas that need refactoring. - You can use the list_code_definition_names tool to get an overview of source code definitions for all files at the top level of a specified directory. This can be particularly useful when you need to understand the broader context and relationships between certain parts of the code. You may need to call this tool multiple times to understand various parts of the codebase related to the task. - - For example, when asked to make edits or improvements you might analyze the file structure in the initial environment_details to get an overview of the project, then use list_code_definition_names to get further insight using source code definitions for files located in relevant directories, then read_file to examine the contents of relevant files, analyze the code and suggest improvements or make necessary edits, then use the write_to_file${diffStrategy ? " or apply_diff" : ""} tool to apply the changes. If you refactored code that could affect other parts of the codebase, you could use search_files to ensure you update other files as needed. + - For example, when asked to make edits or improvements you might analyze the file structure in the initial environment_details to get an overview of the project, then use list_code_definition_names to get further insight using source code definitions for files located in relevant directories, then read_file to examine the contents of relevant files, analyze the code and suggest improvements or make necessary edits, then use ${diffStrategy ? "the apply_diff or write_to_file" : "the write_to_file"} tool to apply the changes. If you refactored code that could affect other parts of the codebase, you could use search_files to ensure you update other files as needed. - You can use the execute_command tool to run commands on the user's computer whenever you feel it can help accomplish the user's task. When you need to execute a CLI command, you must provide a clear explanation of what the command does. Prefer to execute complex CLI commands over creating executable scripts, since they are more flexible and easier to run. Interactive and long-running commands are allowed, since the commands are run in the user's VSCode terminal. The user may keep commands running in the background and you will be kept updated on their status along the way. Each command you execute is run in a new terminal instance.${ supportsComputerUse ? "\n- You can use the browser_action tool to interact with websites (including html files and locally running development servers) through a Puppeteer-controlled browser when you feel it is necessary in accomplishing the user's task. This tool is particularly useful for web development tasks as it allows you to launch a browser, navigate to pages, interact with elements through clicks and keyboard input, and capture the results through screenshots and console logs. This tool may be useful at key stages of web development tasks-such as after implementing new features, making substantial changes, when troubleshooting issues, or to verify the result of your work. You can analyze the provided screenshots to ensure correct rendering or identify errors, and review console logs for runtime issues.\n - For example, if asked to add a component to a react website, you might create the necessary files, use execute_command to run the site locally, then use browser_action to launch the browser, navigate to the local server, and verify the component renders & functions correctly before closing the browser." diff --git a/src/core/prompts/sections/modes.ts b/src/core/prompts/sections/modes.ts index eff950c2c2f..f3863870dbc 100644 --- a/src/core/prompts/sections/modes.ts +++ b/src/core/prompts/sections/modes.ts @@ -45,7 +45,7 @@ Both files should follow this structure: "roleDefinition": "You are Roo, a UI/UX expert specializing in design systems and frontend development. Your expertise includes:\\n- Creating and maintaining design systems\\n- Implementing responsive and accessible web interfaces\\n- Working with CSS, HTML, and modern frontend frameworks\\n- Ensuring consistent user experiences across platforms", // Required: non-empty "groups": [ // Required: array of tool groups (can be empty) "read", // Read files group (read_file, search_files, list_files, list_code_definition_names) - "edit", // Edit files group (write_to_file, apply_diff) - allows editing any file + "edit", // Edit files group (apply_diff, write_to_file) - allows editing any file // Or with file restrictions: // ["edit", { fileRegex: "\\.md$", description: "Markdown files only" }], // Edit group that only allows editing markdown files "browser", // Browser group (browser_action) diff --git a/src/core/prompts/sections/rules.ts b/src/core/prompts/sections/rules.ts index b6e19eb08c9..86e554a157e 100644 --- a/src/core/prompts/sections/rules.ts +++ b/src/core/prompts/sections/rules.ts @@ -5,11 +5,16 @@ import * as path from "path" function getEditingInstructions(diffStrategy?: DiffStrategy, experiments?: Record): string { const instructions: string[] = [] - const availableTools: string[] = ["write_to_file (for creating new files or complete file rewrites)"] + const availableTools: string[] = [] // Collect available editing tools if (diffStrategy) { - availableTools.push("apply_diff (for replacing lines in existing files)") + availableTools.push( + "apply_diff (for replacing lines in existing files)", + "write_to_file (for creating new files or complete file rewrites)", + ) + } else { + availableTools.push("write_to_file (for creating new files or complete file rewrites)") } if (experiments?.["insert_content"]) { availableTools.push("insert_content (for adding lines to existing files)") @@ -36,16 +41,16 @@ function getEditingInstructions(diffStrategy?: DiffStrategy, experiments?: Recor ) } - instructions.push( - "- When using the write_to_file tool to modify a file, use the tool directly with the desired content. You do not need to display the content before using the tool. ALWAYS provide the COMPLETE file content in your response. This is NON-NEGOTIABLE. Partial updates or placeholders like '// rest of code unchanged' are STRICTLY FORBIDDEN. You MUST include ALL parts of the file, even if they haven't been modified. Failure to do so will result in incomplete or broken code, severely impacting the user's project.", - ) - if (availableTools.length > 1) { instructions.push( "- You should always prefer using other editing tools over write_to_file when making changes to existing files since write_to_file is much slower and cannot handle large files.", ) } + instructions.push( + "- When using the write_to_file tool to modify a file, use the tool directly with the desired content. You do not need to display the content before using the tool. ALWAYS provide the COMPLETE file content in your response. This is NON-NEGOTIABLE. Partial updates or placeholders like '// rest of code unchanged' are STRICTLY FORBIDDEN. You MUST include ALL parts of the file, even if they haven't been modified. Failure to do so will result in incomplete or broken code, severely impacting the user's project.", + ) + return instructions.join("\n") } @@ -63,7 +68,7 @@ RULES - You cannot \`cd\` into a different directory to complete a task. You are stuck operating from '${cwd.toPosix()}', so be sure to pass in the correct 'path' parameter when using tools that require a path. - Do not use the ~ character or $HOME to refer to the home directory. - Before using the execute_command tool, you must first think about the SYSTEM INFORMATION context provided to understand the user's environment and tailor your commands to ensure they are compatible with their system. You must also consider if the command you need to run should be executed in a specific directory outside of the current working directory '${cwd.toPosix()}', and if so prepend with \`cd\`'ing into that directory && then executing the command (as one command since you are stuck operating from '${cwd.toPosix()}'). For example, if you needed to run \`npm install\` in a project outside of '${cwd.toPosix()}', you would need to prepend with a \`cd\` i.e. pseudocode for this would be \`cd (path to project) && (command, in this case npm install)\`. -- When using the search_files tool, craft your regex patterns carefully to balance specificity and flexibility. Based on the user's task you may use it to find code patterns, TODO comments, function definitions, or any text-based information across the project. The results include context, so analyze the surrounding code to better understand the matches. Leverage the search_files tool in combination with other tools for more comprehensive analysis. For example, use it to find specific code patterns, then use read_file to examine the full context of interesting matches before using write_to_file to make informed changes. +- When using the search_files tool, craft your regex patterns carefully to balance specificity and flexibility. Based on the user's task you may use it to find code patterns, TODO comments, function definitions, or any text-based information across the project. The results include context, so analyze the surrounding code to better understand the matches. Leverage the search_files tool in combination with other tools for more comprehensive analysis. For example, use it to find specific code patterns, then use read_file to examine the full context of interesting matches before using ${diffStrategy ? "apply_diff or write_to_file" : "write_to_file"} to make informed changes. - When creating a new project (such as an app, website, or any software project), organize all new files within a dedicated project directory unless the user specifies otherwise. Use appropriate file paths when writing files, as the write_to_file tool will automatically create any necessary directories. Structure the project logically, adhering to best practices for the specific type of project being created. Unless otherwise specified, new projects should be easily run without additional setup, for example most projects can be built in HTML, CSS, and JavaScript - which you can open in a browser. ${getEditingInstructions(diffStrategy, experiments)} - Some modes have restrictions on which files they can edit. If you attempt to edit a restricted file, the operation will be rejected with a FileRestrictionError that will specify which file patterns are allowed for the current mode. diff --git a/src/shared/tool-groups.ts b/src/shared/tool-groups.ts index 2728d42319d..50c7b80ca9e 100644 --- a/src/shared/tool-groups.ts +++ b/src/shared/tool-groups.ts @@ -28,7 +28,7 @@ export const TOOL_GROUPS: Record = { tools: ["read_file", "search_files", "list_files", "list_code_definition_names"], }, edit: { - tools: ["write_to_file", "apply_diff", "insert_content", "search_and_replace"], + tools: ["apply_diff", "write_to_file", "insert_content", "search_and_replace"], }, browser: { tools: ["browser_action"], From 0e644f102ff0b9e74213f502c9c41cda7e7776e5 Mon Sep 17 00:00:00 2001 From: cte Date: Mon, 24 Feb 2025 12:48:55 -0800 Subject: [PATCH 051/585] Default to Claude 3.7 where appropriate --- .github/ISSUE_TEMPLATE/bug_report.yml | 2 +- src/core/Cline.ts | 2 +- src/core/webview/ClineProvider.ts | 50 +++---------------- src/shared/api.ts | 24 +++++---- src/test/suite/index.ts | 2 +- .../components/settings/GlamaModelPicker.tsx | 2 +- .../settings/OpenRouterModelPicker.tsx | 2 +- .../settings/RequestyModelPicker.tsx | 2 +- .../src/components/settings/SettingsView.tsx | 2 +- 9 files changed, 26 insertions(+), 62 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml index 501180c3d53..dc66b4f390b 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.yml +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -33,7 +33,7 @@ body: id: model attributes: label: Which Model are you using? - description: Please specify the model you're using (e.g. Claude 3.5 Sonnet) + description: Please specify the model you're using (e.g. Claude 3.7 Sonnet) validations: required: true - type: textarea diff --git a/src/core/Cline.ts b/src/core/Cline.ts index 12cf062406b..f1f5e41b331 100644 --- a/src/core/Cline.ts +++ b/src/core/Cline.ts @@ -2792,7 +2792,7 @@ export class Cline { "mistake_limit_reached", this.api.getModel().id.includes("claude") ? `This may indicate a failure in his thought process or inability to use a tool properly, which can be mitigated with some user guidance (e.g. "Try breaking down the task into smaller steps").` - : "Roo Code uses complex prompts and iterative task execution that may be challenging for less capable models. For best results, it's recommended to use Claude 3.5 Sonnet for its advanced agentic coding capabilities.", + : "Roo Code uses complex prompts and iterative task execution that may be challenging for less capable models. For best results, it's recommended to use Claude 3.7 Sonnet for its advanced agentic coding capabilities.", ) if (response === "messageResponse") { userContent.push( diff --git a/src/core/webview/ClineProvider.ts b/src/core/webview/ClineProvider.ts index 6790224ecae..b4819d96833 100644 --- a/src/core/webview/ClineProvider.ts +++ b/src/core/webview/ClineProvider.ts @@ -1900,23 +1900,7 @@ export class ClineProvider implements vscode.WebviewViewProvider { } const response = await axios.get("https://router.requesty.ai/v1/models", config) - /* - { - "id": "anthropic/claude-3-5-sonnet-20240620", - "object": "model", - "created": 1738243330, - "owned_by": "system", - "input_price": 0.000003, - "caching_price": 0.00000375, - "cached_price": 3E-7, - "output_price": 0.000015, - "max_output_tokens": 8192, - "context_window": 200000, - "supports_caching": true, - "description": "Anthropic's most intelligent model. Highest level of intelligence and capability" - }, - } - */ + if (response.data) { const rawModels = response.data.data const parsePrice = (price: any) => { @@ -2116,34 +2100,10 @@ export class ClineProvider implements vscode.WebviewViewProvider { ) const models: Record = {} + try { const response = await axios.get("https://openrouter.ai/api/v1/models") - /* - { - "id": "anthropic/claude-3.5-sonnet", - "name": "Anthropic: Claude 3.5 Sonnet", - "created": 1718841600, - "description": "Claude 3.5 Sonnet delivers better-than-Opus capabilities, faster-than-Sonnet speeds, at the same Sonnet prices. Sonnet is particularly good at:\n\n- Coding: Autonomously writes, edits, and runs code with reasoning and troubleshooting\n- Data science: Augments human data science expertise; navigates unstructured data while using multiple tools for insights\n- Visual processing: excelling at interpreting charts, graphs, and images, accurately transcribing text to derive insights beyond just the text alone\n- Agentic tasks: exceptional tool use, making it great at agentic tasks (i.e. complex, multi-step problem solving tasks that require engaging with other systems)\n\n#multimodal", - "context_length": 200000, - "architecture": { - "modality": "text+image-\u003Etext", - "tokenizer": "Claude", - "instruct_type": null - }, - "pricing": { - "prompt": "0.000003", - "completion": "0.000015", - "image": "0.0048", - "request": "0" - }, - "top_provider": { - "context_length": 200000, - "max_completion_tokens": 8192, - "is_moderated": true - }, - "per_request_limits": null - }, - */ + if (response.data?.data) { const rawModels = response.data.data const parsePrice = (price: any) => { @@ -2152,6 +2112,7 @@ export class ClineProvider implements vscode.WebviewViewProvider { } return undefined } + for (const rawModel of rawModels) { const modelInfo: ModelInfo = { maxTokens: rawModel.top_provider?.max_completion_tokens, @@ -2164,9 +2125,10 @@ export class ClineProvider implements vscode.WebviewViewProvider { } switch (rawModel.id) { + case "anthropic/claude-3.7-sonnet": case "anthropic/claude-3.5-sonnet": case "anthropic/claude-3.5-sonnet:beta": - // NOTE: this needs to be synced with api.ts/openrouter default model info + // NOTE: this needs to be synced with api.ts/openrouter default model info. modelInfo.supportsComputerUse = true modelInfo.supportsPromptCache = true modelInfo.cacheWritesPrice = 3.75 diff --git a/src/shared/api.ts b/src/shared/api.ts index 23fe60696c9..4619d2930dc 100644 --- a/src/shared/api.ts +++ b/src/shared/api.ts @@ -93,7 +93,7 @@ export interface ModelInfo { // Anthropic // https://docs.anthropic.com/en/docs/about-claude/models export type AnthropicModelId = keyof typeof anthropicModels -export const anthropicDefaultModelId: AnthropicModelId = "claude-3-5-sonnet-20241022" +export const anthropicDefaultModelId: AnthropicModelId = "claude-3-7-sonnet-20250219" export const anthropicModels = { "claude-3-7-sonnet-20250219": { maxTokens: 64_000, @@ -355,9 +355,9 @@ export const bedrockModels = { // Glama // https://glama.ai/models -export const glamaDefaultModelId = "anthropic/claude-3-5-sonnet" +export const glamaDefaultModelId = "anthropic/claude-3-7-sonnet" export const glamaDefaultModelInfo: ModelInfo = { - maxTokens: 8192, + maxTokens: 64_000, contextWindow: 200_000, supportsImages: true, supportsComputerUse: true, @@ -367,11 +367,14 @@ export const glamaDefaultModelInfo: ModelInfo = { cacheWritesPrice: 3.75, cacheReadsPrice: 0.3, description: - "The new Claude 3.5 Sonnet delivers better-than-Opus capabilities, faster-than-Sonnet speeds, at the same Sonnet prices. Sonnet is particularly good at:\n\n- Coding: New Sonnet scores ~49% on SWE-Bench Verified, higher than the last best score, and without any fancy prompt scaffolding\n- Data science: Augments human data science expertise; navigates unstructured data while using multiple tools for insights\n- Visual processing: excelling at interpreting charts, graphs, and images, accurately transcribing text to derive insights beyond just the text alone\n- Agentic tasks: exceptional tool use, making it great at agentic tasks (i.e. complex, multi-step problem solving tasks that require engaging with other systems)\n\n#multimodal\n\n_This is a faster endpoint, made available in collaboration with Anthropic, that is self-moderated: response moderation happens on the provider's side instead of OpenRouter's. For requests that pass moderation, it's identical to the [Standard](/anthropic/claude-3.5-sonnet) variant._", + "Claude 3.7 Sonnet is an advanced large language model with improved reasoning, coding, and problem-solving capabilities. It introduces a hybrid reasoning approach, allowing users to choose between rapid responses and extended, step-by-step processing for complex tasks. The model demonstrates notable improvements in coding, particularly in front-end development and full-stack updates, and excels in agentic workflows, where it can autonomously navigate multi-step processes. Claude 3.7 Sonnet maintains performance parity with its predecessor in standard mode while offering an extended reasoning mode for enhanced accuracy in math, coding, and instruction-following tasks. Read more at the [blog post here](https://www.anthropic.com/news/claude-3-7-sonnet)", } +// Requesty +// https://requesty.ai/router-2 +export const requestyDefaultModelId = "anthropic/claude-3-7-sonnet-latest" export const requestyDefaultModelInfo: ModelInfo = { - maxTokens: 8192, + maxTokens: 64_000, contextWindow: 200_000, supportsImages: true, supportsComputerUse: true, @@ -381,15 +384,14 @@ export const requestyDefaultModelInfo: ModelInfo = { cacheWritesPrice: 3.75, cacheReadsPrice: 0.3, description: - "The new Claude 3.5 Sonnet delivers better-than-Opus capabilities, faster-than-Sonnet speeds, at the same Sonnet prices. Sonnet is particularly good at:\n\n- Coding: New Sonnet scores ~49% on SWE-Bench Verified, higher than the last best score, and without any fancy prompt scaffolding\n- Data science: Augments human data science expertise; navigates unstructured data while using multiple tools for insights\n- Visual processing: excelling at interpreting charts, graphs, and images, accurately transcribing text to derive insights beyond just the text alone\n- Agentic tasks: exceptional tool use, making it great at agentic tasks (i.e. complex, multi-step problem solving tasks that require engaging with other systems)\n\n#multimodal\n\n_This is a faster endpoint, made available in collaboration with Anthropic, that is self-moderated: response moderation happens on the provider's side instead of OpenRouter's. For requests that pass moderation, it's identical to the [Standard](/anthropic/claude-3.5-sonnet) variant._", + "Claude 3.7 Sonnet is an advanced large language model with improved reasoning, coding, and problem-solving capabilities. It introduces a hybrid reasoning approach, allowing users to choose between rapid responses and extended, step-by-step processing for complex tasks. The model demonstrates notable improvements in coding, particularly in front-end development and full-stack updates, and excels in agentic workflows, where it can autonomously navigate multi-step processes. Claude 3.7 Sonnet maintains performance parity with its predecessor in standard mode while offering an extended reasoning mode for enhanced accuracy in math, coding, and instruction-following tasks. Read more at the [blog post here](https://www.anthropic.com/news/claude-3-7-sonnet)", } -export const requestyDefaultModelId = "anthropic/claude-3-5-sonnet" // OpenRouter // https://openrouter.ai/models?order=newest&supported_parameters=tools -export const openRouterDefaultModelId = "anthropic/claude-3.5-sonnet:beta" // will always exist in openRouterModels +export const openRouterDefaultModelId = "anthropic/claude-3.7-sonnet" export const openRouterDefaultModelInfo: ModelInfo = { - maxTokens: 8192, + maxTokens: 64_000, contextWindow: 200_000, supportsImages: true, supportsComputerUse: true, @@ -399,13 +401,13 @@ export const openRouterDefaultModelInfo: ModelInfo = { cacheWritesPrice: 3.75, cacheReadsPrice: 0.3, description: - "The new Claude 3.5 Sonnet delivers better-than-Opus capabilities, faster-than-Sonnet speeds, at the same Sonnet prices. Sonnet is particularly good at:\n\n- Coding: New Sonnet scores ~49% on SWE-Bench Verified, higher than the last best score, and without any fancy prompt scaffolding\n- Data science: Augments human data science expertise; navigates unstructured data while using multiple tools for insights\n- Visual processing: excelling at interpreting charts, graphs, and images, accurately transcribing text to derive insights beyond just the text alone\n- Agentic tasks: exceptional tool use, making it great at agentic tasks (i.e. complex, multi-step problem solving tasks that require engaging with other systems)\n\n#multimodal\n\n_This is a faster endpoint, made available in collaboration with Anthropic, that is self-moderated: response moderation happens on the provider's side instead of OpenRouter's. For requests that pass moderation, it's identical to the [Standard](/anthropic/claude-3.5-sonnet) variant._", + "Claude 3.7 Sonnet is an advanced large language model with improved reasoning, coding, and problem-solving capabilities. It introduces a hybrid reasoning approach, allowing users to choose between rapid responses and extended, step-by-step processing for complex tasks. The model demonstrates notable improvements in coding, particularly in front-end development and full-stack updates, and excels in agentic workflows, where it can autonomously navigate multi-step processes. Claude 3.7 Sonnet maintains performance parity with its predecessor in standard mode while offering an extended reasoning mode for enhanced accuracy in math, coding, and instruction-following tasks. Read more at the [blog post here](https://www.anthropic.com/news/claude-3-7-sonnet)", } // Vertex AI // https://cloud.google.com/vertex-ai/generative-ai/docs/partner-models/use-claude export type VertexModelId = keyof typeof vertexModels -export const vertexDefaultModelId: VertexModelId = "claude-3-5-sonnet-v2@20241022" +export const vertexDefaultModelId: VertexModelId = "claude-3-7-sonnet@20250219" export const vertexModels = { "claude-3-7-sonnet@20250219": { maxTokens: 8192, diff --git a/src/test/suite/index.ts b/src/test/suite/index.ts index ffb8de7473e..540be7cef83 100644 --- a/src/test/suite/index.ts +++ b/src/test/suite/index.ts @@ -39,7 +39,7 @@ export async function run(): Promise { : await globalThis.extension.activate() globalThis.provider = globalThis.api.sidebarProvider await globalThis.provider.updateGlobalState("apiProvider", "openrouter") - await globalThis.provider.updateGlobalState("openRouterModelId", "anthropic/claude-3.5-sonnet") + await globalThis.provider.updateGlobalState("openRouterModelId", "anthropic/claude-3.7-sonnet") await globalThis.provider.storeSecret( "openRouterApiKey", process.env.OPENROUTER_API_KEY || "sk-or-v1-fake-api-key", diff --git a/webview-ui/src/components/settings/GlamaModelPicker.tsx b/webview-ui/src/components/settings/GlamaModelPicker.tsx index cb813a0d058..37e326d8f87 100644 --- a/webview-ui/src/components/settings/GlamaModelPicker.tsx +++ b/webview-ui/src/components/settings/GlamaModelPicker.tsx @@ -10,6 +10,6 @@ export const GlamaModelPicker = () => ( refreshMessageType="refreshGlamaModels" serviceName="Glama" serviceUrl="https://glama.ai/models" - recommendedModel="anthropic/claude-3-5-sonnet" + recommendedModel="anthropic/claude-3-7-sonnet" /> ) diff --git a/webview-ui/src/components/settings/OpenRouterModelPicker.tsx b/webview-ui/src/components/settings/OpenRouterModelPicker.tsx index 9111407cd61..c773478e542 100644 --- a/webview-ui/src/components/settings/OpenRouterModelPicker.tsx +++ b/webview-ui/src/components/settings/OpenRouterModelPicker.tsx @@ -10,6 +10,6 @@ export const OpenRouterModelPicker = () => ( refreshMessageType="refreshOpenRouterModels" serviceName="OpenRouter" serviceUrl="https://openrouter.ai/models" - recommendedModel="anthropic/claude-3.5-sonnet:beta" + recommendedModel="anthropic/claude-3.7-sonnet" /> ) diff --git a/webview-ui/src/components/settings/RequestyModelPicker.tsx b/webview-ui/src/components/settings/RequestyModelPicker.tsx index e0759a43ba1..c65067068aa 100644 --- a/webview-ui/src/components/settings/RequestyModelPicker.tsx +++ b/webview-ui/src/components/settings/RequestyModelPicker.tsx @@ -16,7 +16,7 @@ export const RequestyModelPicker = () => { }} serviceName="Requesty" serviceUrl="https://requesty.ai" - recommendedModel="anthropic/claude-3-5-sonnet-latest" + recommendedModel="anthropic/claude-3-7-sonnet-latest" /> ) } diff --git a/webview-ui/src/components/settings/SettingsView.tsx b/webview-ui/src/components/settings/SettingsView.tsx index 495bf49bd77..0d80580b491 100644 --- a/webview-ui/src/components/settings/SettingsView.tsx +++ b/webview-ui/src/components/settings/SettingsView.tsx @@ -765,7 +765,7 @@ const SettingsView = forwardRef(({ onDone }, color: "var(--vscode-descriptionForeground)", }}> When enabled, Roo will be able to edit files more quickly and will automatically reject - truncated full-file writes. Works best with the latest Claude 3.5 Sonnet model. + truncated full-file writes. Works best with the latest Claude 3.7 Sonnet model.

{diffEnabled && ( From d94067aba8b375fa14b76b04a0bb2e5774a43a06 Mon Sep 17 00:00:00 2001 From: cte Date: Mon, 24 Feb 2025 12:54:05 -0800 Subject: [PATCH 052/585] Revert maxTokens change for now --- src/api/providers/openrouter.ts | 1 + src/shared/api.ts | 8 ++++---- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/api/providers/openrouter.ts b/src/api/providers/openrouter.ts index af087226ebd..eb9e819d772 100644 --- a/src/api/providers/openrouter.ts +++ b/src/api/providers/openrouter.ts @@ -107,6 +107,7 @@ export class OpenRouterHandler implements ApiHandler, SingleCompletionHandler { // (models usually default to max tokens allowed) let maxTokens: number | undefined switch (this.getModel().id) { + case "anthropic/claude-3.7-sonnet": case "anthropic/claude-3.5-sonnet": case "anthropic/claude-3.5-sonnet:beta": case "anthropic/claude-3.5-sonnet-20240620": diff --git a/src/shared/api.ts b/src/shared/api.ts index 4619d2930dc..056a40c49ef 100644 --- a/src/shared/api.ts +++ b/src/shared/api.ts @@ -96,7 +96,7 @@ export type AnthropicModelId = keyof typeof anthropicModels export const anthropicDefaultModelId: AnthropicModelId = "claude-3-7-sonnet-20250219" export const anthropicModels = { "claude-3-7-sonnet-20250219": { - maxTokens: 64_000, + maxTokens: 8192, contextWindow: 200_000, supportsImages: true, supportsComputerUse: true, @@ -357,7 +357,7 @@ export const bedrockModels = { // https://glama.ai/models export const glamaDefaultModelId = "anthropic/claude-3-7-sonnet" export const glamaDefaultModelInfo: ModelInfo = { - maxTokens: 64_000, + maxTokens: 8192, contextWindow: 200_000, supportsImages: true, supportsComputerUse: true, @@ -374,7 +374,7 @@ export const glamaDefaultModelInfo: ModelInfo = { // https://requesty.ai/router-2 export const requestyDefaultModelId = "anthropic/claude-3-7-sonnet-latest" export const requestyDefaultModelInfo: ModelInfo = { - maxTokens: 64_000, + maxTokens: 8192, contextWindow: 200_000, supportsImages: true, supportsComputerUse: true, @@ -391,7 +391,7 @@ export const requestyDefaultModelInfo: ModelInfo = { // https://openrouter.ai/models?order=newest&supported_parameters=tools export const openRouterDefaultModelId = "anthropic/claude-3.7-sonnet" export const openRouterDefaultModelInfo: ModelInfo = { - maxTokens: 64_000, + maxTokens: 8192, contextWindow: 200_000, supportsImages: true, supportsComputerUse: true, From 01765995ada92491ef7576d10f8d7a8da22f4223 Mon Sep 17 00:00:00 2001 From: ShayBC Date: Mon, 24 Feb 2025 23:00:11 +0200 Subject: [PATCH 053/585] added task no indicator + improved deleteTask code --- src/core/Cline.ts | 13 ++++ src/core/__tests__/Cline.test.ts | 1 + src/core/webview/ClineProvider.ts | 72 +++++++++---------- src/shared/HistoryItem.ts | 1 + webview-ui/src/components/chat/TaskHeader.tsx | 5 +- .../src/components/history/HistoryPreview.tsx | 6 ++ .../history/__tests__/HistoryView.test.tsx | 2 + 7 files changed, 62 insertions(+), 38 deletions(-) diff --git a/src/core/Cline.ts b/src/core/Cline.ts index 35879a42913..bcec6243fff 100644 --- a/src/core/Cline.ts +++ b/src/core/Cline.ts @@ -75,6 +75,7 @@ type UserContent = Array< export class Cline { readonly taskId: string + private taskNumber: number // a flag that indicated if this Cline instance is a subtask (on finish return control to parent task) private isSubTask: boolean = false // a flag that indicated if this Cline instance is paused (waiting for provider to resume it after subtask completion) @@ -139,6 +140,7 @@ export class Cline { } this.taskId = crypto.randomUUID() + this.taskNumber = -1 this.api = buildApiHandler(apiConfiguration) this.terminalManager = new TerminalManager() this.urlContentFetcher = new UrlContentFetcher(provider.context) @@ -170,6 +172,16 @@ export class Cline { this.isSubTask = true } + // sets the task number (sequencial number of this task from all the subtask ran from this main task stack) + setTaskNumber(taskNumber: number) { + this.taskNumber = taskNumber + } + + // gets the task number, the sequencial number of this task from all the subtask ran from this main task stack + getTaskNumber() { + return this.taskNumber + } + // Add method to update diffStrategy async updateDiffStrategy(experimentalDiffStrategy?: boolean) { // If not provided, get from current state @@ -276,6 +288,7 @@ export class Cline { await this.providerRef.deref()?.updateTaskHistory({ id: this.taskId, + number: this.taskNumber, ts: lastRelevantMessage.ts, task: taskMessage.text ?? "", tokensIn: apiMetrics.totalTokensIn, diff --git a/src/core/__tests__/Cline.test.ts b/src/core/__tests__/Cline.test.ts index 3da0c8cdd3d..0afce81bce5 100644 --- a/src/core/__tests__/Cline.test.ts +++ b/src/core/__tests__/Cline.test.ts @@ -222,6 +222,7 @@ describe("Cline", () => { return [ { id: "123", + number: 0, ts: Date.now(), task: "historical task", tokensIn: 100, diff --git a/src/core/webview/ClineProvider.ts b/src/core/webview/ClineProvider.ts index a8f10f6fcd0..6e9e2025264 100644 --- a/src/core/webview/ClineProvider.ts +++ b/src/core/webview/ClineProvider.ts @@ -153,6 +153,7 @@ export class ClineProvider implements vscode.WebviewViewProvider { private latestAnnouncementId = "jan-21-2025-custom-modes" // update to some unique identifier when we add a new announcement configManager: ConfigManager customModesManager: CustomModesManager + private lastTaskNumber = -1 constructor( readonly context: vscode.ExtensionContext, @@ -180,6 +181,17 @@ export class ClineProvider implements vscode.WebviewViewProvider { // The instance is pushed to the top of the stack (LIFO order). // When the task is completed, the top instance is removed, reactivating the previous task. addClineToStack(cline: Cline): void { + // if cline.getTaskNumber() is -1, it means it is a new task + if (cline.getTaskNumber() === -1) { + // increase last cline number by 1 + this.lastTaskNumber = this.lastTaskNumber + 1 + cline.setTaskNumber(this.lastTaskNumber) + } + // if cline.getTaskNumber() > lastTaskNumber, set lastTaskNumber to cline.getTaskNumber() + else if (cline.getTaskNumber() > this.lastTaskNumber) { + this.lastTaskNumber = cline.getTaskNumber() + } + // push the cline instance to the stack this.clineStack.push(cline) } @@ -192,6 +204,10 @@ export class ClineProvider implements vscode.WebviewViewProvider { // make sure no reference kept, once promises end it will be garbage collected clineToBeRemoved = undefined } + // if the stack is empty, reset the last task number + if (this.clineStack.length === 0) { + this.lastTaskNumber = -1 + } } // remove the cline object with the received clineId, and all the cline objects bove it in the stack @@ -520,6 +536,8 @@ export class ClineProvider implements vscode.WebviewViewProvider { historyItem, experiments, ) + // get this cline task number id from the history item and set it to newCline + newCline.setTaskNumber(historyItem.number) this.addClineToStack(newCline) } @@ -2384,38 +2402,25 @@ export class ClineProvider implements vscode.WebviewViewProvider { await downloadTask(historyItem.ts, apiConversationHistory) } + // this function deletes a task from task hidtory, and deletes it's checkpoints and delete the task folder async deleteTaskWithId(id: string) { + // get the task directory full path + const { taskDirPath } = await this.getTaskWithId(id) + + // remove task from stack if it's the current task if (id === this.getCurrentCline()?.taskId) { await this.removeClineWithIdFromStack(id) } - const { taskDirPath, apiConversationHistoryFilePath, uiMessagesFilePath } = await this.getTaskWithId(id) - + // delete task from the task history state await this.deleteTaskFromState(id) - // Delete the task files. - const apiConversationHistoryFileExists = await fileExistsAtPath(apiConversationHistoryFilePath) - - if (apiConversationHistoryFileExists) { - await fs.unlink(apiConversationHistoryFilePath) - } - - const uiMessagesFileExists = await fileExistsAtPath(uiMessagesFilePath) - - if (uiMessagesFileExists) { - await fs.unlink(uiMessagesFilePath) - } - - const legacyMessagesFilePath = path.join(taskDirPath, "claude_messages.json") - - if (await fileExistsAtPath(legacyMessagesFilePath)) { - await fs.unlink(legacyMessagesFilePath) - } - + // check if checkpoints are enabled const { checkpointsEnabled } = await this.getState() + // get the base directory of the project const baseDir = vscode.workspace.workspaceFolders?.map((folder) => folder.uri.fsPath).at(0) - // Delete checkpoints branch. + // delete checkpoints branch from project git repo if (checkpointsEnabled && baseDir) { const branchSummary = await simpleGit(baseDir) .branch(["-D", `roo-code-checkpoints-${id}`]) @@ -2426,22 +2431,15 @@ export class ClineProvider implements vscode.WebviewViewProvider { } } - // Delete checkpoints directory - const checkpointsDir = path.join(taskDirPath, "checkpoints") - - if (await fileExistsAtPath(checkpointsDir)) { - try { - await fs.rm(checkpointsDir, { recursive: true, force: true }) - console.log(`[deleteTaskWithId${id}] removed checkpoints repo`) - } catch (error) { - console.error( - `[deleteTaskWithId${id}] failed to remove checkpoints repo: ${error instanceof Error ? error.message : String(error)}`, - ) - } + // delete the entire task directory including checkpoints and all content + try { + await fs.rm(taskDirPath, { recursive: true, force: true }) + console.log(`[deleteTaskWithId${id}] removed task directory`) + } catch (error) { + console.error( + `[deleteTaskWithId${id}] failed to remove task directory: ${error instanceof Error ? error.message : String(error)}`, + ) } - - // Succeeds if the dir is empty. - await fs.rmdir(taskDirPath) } async deleteTaskFromState(id: string) { diff --git a/src/shared/HistoryItem.ts b/src/shared/HistoryItem.ts index ef242cb9679..e6e2c09ed2b 100644 --- a/src/shared/HistoryItem.ts +++ b/src/shared/HistoryItem.ts @@ -1,5 +1,6 @@ export type HistoryItem = { id: string + number: number ts: number task: string tokensIn: number diff --git a/webview-ui/src/components/chat/TaskHeader.tsx b/webview-ui/src/components/chat/TaskHeader.tsx index b35be0cd2a6..90d050cf538 100644 --- a/webview-ui/src/components/chat/TaskHeader.tsx +++ b/webview-ui/src/components/chat/TaskHeader.tsx @@ -158,7 +158,10 @@ const TaskHeader: React.FC = ({ flexGrow: 1, minWidth: 0, // This allows the div to shrink below its content size }}> - Task{!isTaskExpanded && ":"} + + Task ({currentTaskItem?.number === 0 ? "Main" : currentTaskItem.number}) + {!isTaskExpanded && ":"} + {!isTaskExpanded && ( {highlightMentions(task.text, false)} )} diff --git a/webview-ui/src/components/history/HistoryPreview.tsx b/webview-ui/src/components/history/HistoryPreview.tsx index b2898fc6a8d..f0484b1dcc8 100644 --- a/webview-ui/src/components/history/HistoryPreview.tsx +++ b/webview-ui/src/components/history/HistoryPreview.tsx @@ -120,6 +120,12 @@ const HistoryPreview = ({ showHistoryView }: HistoryPreviewProps) => { }}> {formatDate(item.ts)} + + ({item.number === 0 ? "Main" : item.number}) + - {item?.size && ( + {!!item?.size && item.size > 0 && (
- ( - -) diff --git a/webview-ui/src/components/settings/ModelPicker.tsx b/webview-ui/src/components/settings/ModelPicker.tsx index b21b37ef0f4..8fd6d82daa7 100644 --- a/webview-ui/src/components/settings/ModelPicker.tsx +++ b/webview-ui/src/components/settings/ModelPicker.tsx @@ -1,185 +1,90 @@ import { VSCodeLink } from "@vscode/webview-ui-toolkit/react" -import debounce from "debounce" -import { useMemo, useState, useCallback, useEffect, useRef } from "react" -import { useMount } from "react-use" -import { CaretSortIcon, CheckIcon } from "@radix-ui/react-icons" +import { useMemo, useState, useCallback, useEffect } from "react" -import { cn } from "@/lib/utils" -import { - Button, - Command, - CommandEmpty, - CommandGroup, - CommandInput, - CommandItem, - CommandList, - Popover, - PopoverContent, - PopoverTrigger, -} from "@/components/ui" - -import { useExtensionState } from "../../context/ExtensionStateContext" -import { vscode } from "../../utils/vscode" import { normalizeApiConfiguration } from "./ApiOptions" import { ModelInfoView } from "./ModelInfoView" - -type ModelProvider = "glama" | "openRouter" | "unbound" | "requesty" | "openAi" - -type ModelKeys = `${T}Models` -type ConfigKeys = `${T}ModelId` -type InfoKeys = `${T}ModelInfo` -type RefreshMessageType = `refresh${Capitalize}Models` - -interface ModelPickerProps { - defaultModelId: string - modelsKey: ModelKeys - configKey: ConfigKeys - infoKey: InfoKeys - refreshMessageType: RefreshMessageType - refreshValues?: Record +import { ApiConfiguration, ModelInfo } from "../../../../src/shared/api" +import { Combobox, ComboboxContent, ComboboxEmpty, ComboboxInput, ComboboxItem } from "../ui/combobox" + +type ExtractType = NonNullable< + { [K in keyof ApiConfiguration]: Required[K] extends T ? K : never }[keyof ApiConfiguration] +> + +type ModelIdKeys = NonNullable< + { [K in keyof ApiConfiguration]: K extends `${string}ModelId` ? K : never }[keyof ApiConfiguration] +> +declare module "react" { + interface CSSProperties { + // Allow CSS variables + [key: `--${string}`]: string | number + } +} +interface ModelPickerProps { + defaultModelId?: string + models: Record | null + modelIdKey: ModelIdKeys + modelInfoKey: ExtractType serviceName: string serviceUrl: string recommendedModel: string - allowCustomModel?: boolean + apiConfiguration: ApiConfiguration + setApiConfigurationField: (field: K, value: ApiConfiguration[K]) => void + defaultModelInfo?: ModelInfo } export const ModelPicker = ({ defaultModelId, - modelsKey, - configKey, - infoKey, - refreshMessageType, - refreshValues, + models, + modelIdKey, + modelInfoKey, serviceName, serviceUrl, recommendedModel, - allowCustomModel = false, + apiConfiguration, + setApiConfigurationField, + defaultModelInfo, }: ModelPickerProps) => { - const [customModelId, setCustomModelId] = useState("") - const [isCustomModel, setIsCustomModel] = useState(false) - const [open, setOpen] = useState(false) - const [value, setValue] = useState(defaultModelId) const [isDescriptionExpanded, setIsDescriptionExpanded] = useState(false) - const prevRefreshValuesRef = useRef | undefined>() - - const { apiConfiguration, [modelsKey]: models, onUpdateApiConfig, setApiConfiguration } = useExtensionState() - const modelIds = useMemo( - () => (Array.isArray(models) ? models : Object.keys(models)).sort((a, b) => a.localeCompare(b)), - [models], - ) + const modelIds = useMemo(() => Object.keys(models ?? {}).sort((a, b) => a.localeCompare(b)), [models]) const { selectedModelId, selectedModelInfo } = useMemo( () => normalizeApiConfiguration(apiConfiguration), [apiConfiguration], ) - - const onSelectCustomModel = useCallback( - (modelId: string) => { - setCustomModelId(modelId) - const modelInfo = { id: modelId } - const apiConfig = { ...apiConfiguration, [configKey]: modelId, [infoKey]: modelInfo } - setApiConfiguration(apiConfig) - onUpdateApiConfig(apiConfig) - setValue(modelId) - setOpen(false) - setIsCustomModel(false) - }, - [apiConfiguration, configKey, infoKey, onUpdateApiConfig, setApiConfiguration], - ) - const onSelect = useCallback( (modelId: string) => { - const modelInfo = Array.isArray(models) - ? { id: modelId } // For OpenAI models which are just strings - : models[modelId] // For other models that have full info objects - const apiConfig = { ...apiConfiguration, [configKey]: modelId, [infoKey]: modelInfo } - setApiConfiguration(apiConfig) - onUpdateApiConfig(apiConfig) - setValue(modelId) - setOpen(false) + const modelInfo = models?.[modelId] + setApiConfigurationField(modelIdKey, modelId) + setApiConfigurationField(modelInfoKey, modelInfo ?? defaultModelInfo) }, - [apiConfiguration, configKey, infoKey, models, onUpdateApiConfig, setApiConfiguration], + [modelIdKey, modelInfoKey, models, setApiConfigurationField, defaultModelInfo], ) - - const debouncedRefreshModels = useMemo(() => { - return debounce(() => { - const message = refreshValues - ? { type: refreshMessageType, values: refreshValues } - : { type: refreshMessageType } - vscode.postMessage(message) - }, 100) - }, [refreshMessageType, refreshValues]) - - useMount(() => { - debouncedRefreshModels() - return () => debouncedRefreshModels.clear() - }) - useEffect(() => { - if (!refreshValues) { - prevRefreshValuesRef.current = undefined - return - } - - // Check if all values in refreshValues are truthy - if (Object.values(refreshValues).some((value) => !value)) { - prevRefreshValuesRef.current = undefined - return - } - - // Compare with previous values - const prevValues = prevRefreshValuesRef.current - if (prevValues && JSON.stringify(prevValues) === JSON.stringify(refreshValues)) { - return + if (apiConfiguration[modelIdKey] == null && defaultModelId) { + onSelect(defaultModelId) } - - prevRefreshValuesRef.current = refreshValues - debouncedRefreshModels() - }, [debouncedRefreshModels, refreshValues]) - - useEffect(() => setValue(selectedModelId), [selectedModelId]) + }, [apiConfiguration, defaultModelId, modelIdKey, onSelect]) return ( <>
Model
- - - - - - - - - No model found. - - {modelIds.map((model) => ( - - {model} - - - ))} - - {allowCustomModel && ( - - { - setIsCustomModel(true) - setOpen(false) - }}> - + Add custom model - - - )} - - - - + + + + No model found. + {modelIds.map((model) => ( + + {model} + + ))} + + + {selectedModelId && selectedModelInfo && ( onSelect(recommendedModel)}>{recommendedModel}. You can also try searching "free" for no-cost options currently available.

- {allowCustomModel && isCustomModel && ( -
-
-

Add Custom Model

- setCustomModelId(e.target.value)} - /> -
- - -
-
-
- )} ) } diff --git a/webview-ui/src/components/settings/OpenAiModelPicker.tsx b/webview-ui/src/components/settings/OpenAiModelPicker.tsx deleted file mode 100644 index 040da1d4210..00000000000 --- a/webview-ui/src/components/settings/OpenAiModelPicker.tsx +++ /dev/null @@ -1,27 +0,0 @@ -import React from "react" -import { useExtensionState } from "../../context/ExtensionStateContext" -import { ModelPicker } from "./ModelPicker" - -const OpenAiModelPicker: React.FC = () => { - const { apiConfiguration } = useExtensionState() - - return ( - - ) -} - -export default OpenAiModelPicker diff --git a/webview-ui/src/components/settings/OpenRouterModelPicker.tsx b/webview-ui/src/components/settings/OpenRouterModelPicker.tsx deleted file mode 100644 index c773478e542..00000000000 --- a/webview-ui/src/components/settings/OpenRouterModelPicker.tsx +++ /dev/null @@ -1,15 +0,0 @@ -import { ModelPicker } from "./ModelPicker" -import { openRouterDefaultModelId } from "../../../../src/shared/api" - -export const OpenRouterModelPicker = () => ( - -) diff --git a/webview-ui/src/components/settings/RequestyModelPicker.tsx b/webview-ui/src/components/settings/RequestyModelPicker.tsx deleted file mode 100644 index c65067068aa..00000000000 --- a/webview-ui/src/components/settings/RequestyModelPicker.tsx +++ /dev/null @@ -1,22 +0,0 @@ -import { ModelPicker } from "./ModelPicker" -import { requestyDefaultModelId } from "../../../../src/shared/api" -import { useExtensionState } from "@/context/ExtensionStateContext" - -export const RequestyModelPicker = () => { - const { apiConfiguration } = useExtensionState() - return ( - - ) -} diff --git a/webview-ui/src/components/settings/SettingsView.tsx b/webview-ui/src/components/settings/SettingsView.tsx index 761e8565214..75ba11107c4 100644 --- a/webview-ui/src/components/settings/SettingsView.tsx +++ b/webview-ui/src/components/settings/SettingsView.tsx @@ -1,4 +1,4 @@ -import { forwardRef, memo, useCallback, useEffect, useImperativeHandle, useRef, useState } from "react" +import { forwardRef, memo, useCallback, useEffect, useImperativeHandle, useMemo, useRef, useState } from "react" import { VSCodeButton, VSCodeCheckbox, VSCodeLink, VSCodeTextField } from "@vscode/webview-ui-toolkit/react" import { Dropdown, type DropdownOption } from "vscrui" @@ -45,7 +45,6 @@ const SettingsView = forwardRef(({ onDone }, // TODO: Reduce WebviewMessage/ExtensionState complexity const { currentApiConfigName } = extensionState const { - apiConfiguration, alwaysAllowReadOnly, allowedCommands, alwaysAllowBrowser, @@ -69,6 +68,9 @@ const SettingsView = forwardRef(({ onDone }, terminalOutputLineLimit, writeDelayMs, } = cachedState + + //Make sure apiConfiguration is initialized and managed by SettingsView + const apiConfiguration = useMemo(() => cachedState.apiConfiguration ?? {}, [cachedState.apiConfiguration]) useEffect(() => { // Update only when currentApiConfigName is changed diff --git a/webview-ui/src/components/settings/UnboundModelPicker.tsx b/webview-ui/src/components/settings/UnboundModelPicker.tsx deleted file mode 100644 index 4901884f1e6..00000000000 --- a/webview-ui/src/components/settings/UnboundModelPicker.tsx +++ /dev/null @@ -1,15 +0,0 @@ -import { ModelPicker } from "./ModelPicker" -import { unboundDefaultModelId } from "../../../../src/shared/api" - -export const UnboundModelPicker = () => ( - -) diff --git a/webview-ui/src/components/settings/__tests__/ModelPicker.test.tsx b/webview-ui/src/components/settings/__tests__/ModelPicker.test.tsx index 4e7c67c1872..49d60c55c48 100644 --- a/webview-ui/src/components/settings/__tests__/ModelPicker.test.tsx +++ b/webview-ui/src/components/settings/__tests__/ModelPicker.test.tsx @@ -3,7 +3,6 @@ import { screen, fireEvent, render } from "@testing-library/react" import { act } from "react" import { ModelPicker } from "../ModelPicker" -import { useExtensionState } from "../../../context/ExtensionStateContext" jest.mock("../../../context/ExtensionStateContext", () => ({ useExtensionState: jest.fn(), @@ -20,36 +19,40 @@ global.ResizeObserver = MockResizeObserver Element.prototype.scrollIntoView = jest.fn() describe("ModelPicker", () => { - const mockOnUpdateApiConfig = jest.fn() - const mockSetApiConfiguration = jest.fn() - + const mockSetApiConfigurationField = jest.fn() + const modelInfo = { + maxTokens: 8192, + contextWindow: 200_000, + supportsImages: true, + supportsComputerUse: true, + supportsPromptCache: true, + inputPrice: 3.0, + outputPrice: 15.0, + cacheWritesPrice: 3.75, + cacheReadsPrice: 0.3, + } + const mockModels = { + model1: { name: "Model 1", description: "Test model 1", ...modelInfo }, + model2: { name: "Model 2", description: "Test model 2", ...modelInfo }, + } const defaultProps = { + apiConfiguration: {}, defaultModelId: "model1", - modelsKey: "glamaModels" as const, - configKey: "glamaModelId" as const, - infoKey: "glamaModelInfo" as const, - refreshMessageType: "refreshGlamaModels" as const, + defaultModelInfo: modelInfo, + modelIdKey: "glamaModelId" as const, + modelInfoKey: "glamaModelInfo" as const, serviceName: "Test Service", serviceUrl: "https://test.service", recommendedModel: "recommended-model", - } - - const mockModels = { - model1: { name: "Model 1", description: "Test model 1" }, - model2: { name: "Model 2", description: "Test model 2" }, + models: mockModels, + setApiConfigurationField: mockSetApiConfigurationField, } beforeEach(() => { jest.clearAllMocks() - ;(useExtensionState as jest.Mock).mockReturnValue({ - apiConfiguration: {}, - setApiConfiguration: mockSetApiConfiguration, - glamaModels: mockModels, - onUpdateApiConfig: mockOnUpdateApiConfig, - }) }) - it("calls onUpdateApiConfig when a model is selected", async () => { + it("calls setApiConfigurationField when a model is selected", async () => { await act(async () => { render() }) @@ -67,20 +70,12 @@ describe("ModelPicker", () => { await act(async () => { // Find and click the model item by its value. - const modelItem = screen.getByRole("option", { name: "model2" }) - fireEvent.click(modelItem) + const modelItem = screen.getByTestId("model-input") + fireEvent.input(modelItem, { target: { value: "model2" } }) }) // Verify the API config was updated. - expect(mockSetApiConfiguration).toHaveBeenCalledWith({ - glamaModelId: "model2", - glamaModelInfo: mockModels["model2"], - }) - - // Verify onUpdateApiConfig was called with the new config. - expect(mockOnUpdateApiConfig).toHaveBeenCalledWith({ - glamaModelId: "model2", - glamaModelInfo: mockModels["model2"], - }) + expect(mockSetApiConfigurationField).toHaveBeenCalledWith(defaultProps.modelIdKey, "model2") + expect(mockSetApiConfigurationField).toHaveBeenCalledWith(defaultProps.modelInfoKey, mockModels.model2) }) }) diff --git a/webview-ui/src/utils/validate.ts b/webview-ui/src/utils/validate.ts index 19b13e2c6c2..97c702637c4 100644 --- a/webview-ui/src/utils/validate.ts +++ b/webview-ui/src/utils/validate.ts @@ -1,9 +1,4 @@ -import { - ApiConfiguration, - glamaDefaultModelId, - openRouterDefaultModelId, - unboundDefaultModelId, -} from "../../../src/shared/api" +import { ApiConfiguration } from "../../../src/shared/api" import { ModelInfo } from "../../../src/shared/api" export function validateApiConfiguration(apiConfiguration?: ApiConfiguration): string | undefined { if (apiConfiguration) { @@ -86,7 +81,7 @@ export function validateModelId( if (apiConfiguration) { switch (apiConfiguration.apiProvider) { case "glama": - const glamaModelId = apiConfiguration.glamaModelId || glamaDefaultModelId // in case the user hasn't changed the model id, it will be undefined by default + const glamaModelId = apiConfiguration.glamaModelId if (!glamaModelId) { return "You must provide a model ID." } @@ -96,7 +91,7 @@ export function validateModelId( } break case "openrouter": - const modelId = apiConfiguration.openRouterModelId || openRouterDefaultModelId // in case the user hasn't changed the model id, it will be undefined by default + const modelId = apiConfiguration.openRouterModelId if (!modelId) { return "You must provide a model ID." } @@ -106,7 +101,7 @@ export function validateModelId( } break case "unbound": - const unboundModelId = apiConfiguration.unboundModelId || unboundDefaultModelId + const unboundModelId = apiConfiguration.unboundModelId if (!unboundModelId) { return "You must provide a model ID." } From 1a3b8700ba2c8f93f31cdfed7aa0fadb38d584d0 Mon Sep 17 00:00:00 2001 From: System233 Date: Wed, 26 Feb 2025 06:42:29 +0800 Subject: [PATCH 108/585] Improved error message feedback in settings panel --- webview-ui/src/__mocks__/vscrui.ts | 3 + .../components/settings/ApiErrorMessage.tsx | 16 +++++ .../src/components/settings/ApiOptions.tsx | 69 ++++++++----------- .../src/components/settings/ModelPicker.tsx | 41 ++++++++--- .../src/components/settings/SettingsView.tsx | 53 ++++---------- .../settings/__tests__/ApiOptions.test.tsx | 4 ++ .../src/components/welcome/WelcomeView.tsx | 2 + 7 files changed, 98 insertions(+), 90 deletions(-) create mode 100644 webview-ui/src/components/settings/ApiErrorMessage.tsx diff --git a/webview-ui/src/__mocks__/vscrui.ts b/webview-ui/src/__mocks__/vscrui.ts index 76760ba5cce..9b4a20f4d6b 100644 --- a/webview-ui/src/__mocks__/vscrui.ts +++ b/webview-ui/src/__mocks__/vscrui.ts @@ -8,6 +8,9 @@ export const Dropdown = ({ children, value, onChange }: any) => export const Pane = ({ children }: any) => React.createElement("div", { "data-testid": "mock-pane" }, children) +export const Button = ({ children, ...props }: any) => + React.createElement("div", { "data-testid": "mock-button", ...props }, children) + export type DropdownOption = { label: string value: string diff --git a/webview-ui/src/components/settings/ApiErrorMessage.tsx b/webview-ui/src/components/settings/ApiErrorMessage.tsx new file mode 100644 index 00000000000..4b419957b6c --- /dev/null +++ b/webview-ui/src/components/settings/ApiErrorMessage.tsx @@ -0,0 +1,16 @@ +import React from "react" + +interface ApiErrorMessageProps { + errorMessage: string | undefined + children?: React.ReactNode +} +const ApiErrorMessage = ({ errorMessage, children }: ApiErrorMessageProps) => { + return ( +
+ + {errorMessage} + {children} +
+ ) +} +export default ApiErrorMessage diff --git a/webview-ui/src/components/settings/ApiOptions.tsx b/webview-ui/src/components/settings/ApiOptions.tsx index 594cd2fd5fb..f0c2b0e45fa 100644 --- a/webview-ui/src/components/settings/ApiOptions.tsx +++ b/webview-ui/src/components/settings/ApiOptions.tsx @@ -1,4 +1,4 @@ -import { memo, useCallback, useMemo, useState } from "react" +import React, { memo, useCallback, useEffect, useMemo, useState } from "react" import { useDebounce, useEvent } from "react-use" import { Checkbox, Dropdown, Pane, type DropdownOption } from "vscrui" import { VSCodeLink, VSCodeRadio, VSCodeRadioGroup, VSCodeTextField } from "@vscode/webview-ui-toolkit/react" @@ -42,23 +42,25 @@ import { ModelInfoView } from "./ModelInfoView" import { DROPDOWN_Z_INDEX } from "./styles" import { ModelPicker } from "./ModelPicker" import { TemperatureControl } from "./TemperatureControl" +import { validateApiConfiguration, validateModelId } from "@/utils/validate" +import ApiErrorMessage from "./ApiErrorMessage" interface ApiOptionsProps { uriScheme: string | undefined apiConfiguration: ApiConfiguration setApiConfigurationField: (field: K, value: ApiConfiguration[K]) => void - apiErrorMessage?: string - modelIdErrorMessage?: string fromWelcomeView?: boolean + errorMessage: string | undefined + setErrorMessage: React.Dispatch> } const ApiOptions = ({ uriScheme, apiConfiguration, setApiConfigurationField, - apiErrorMessage, - modelIdErrorMessage, fromWelcomeView, + errorMessage, + setErrorMessage, }: ApiOptionsProps) => { const [ollamaModels, setOllamaModels] = useState([]) const [lmStudioModels, setLmStudioModels] = useState([]) @@ -146,6 +148,13 @@ const ApiOptions = ({ ], ) + useEffect(() => { + const apiValidationResult = + validateApiConfiguration(apiConfiguration) || + validateModelId(apiConfiguration, glamaModels, openRouterModels, unboundModels) + setErrorMessage(apiValidationResult) + }, [apiConfiguration, glamaModels, openRouterModels, setErrorMessage, unboundModels]) + const handleMessage = useCallback((event: MessageEvent) => { const message: ExtensionMessage = event.data switch (message.type) { @@ -626,6 +635,7 @@ const ApiOptions = ({ ]} />
+ {errorMessage && }

{/* end Model Info Configuration */} - -

- - (Note: Roo Code uses complex prompts and works best - with Claude models. Less capable models may not work as expected.) - -

)} @@ -1100,6 +1099,7 @@ const ApiOptions = ({ placeholder={"e.g. meta-llama-3.1-8b-instruct"}> Model ID + {errorMessage && } {lmStudioModels.length > 0 && ( Model ID + {errorMessage && ( +
+ + {errorMessage} +
+ )} {ollamaModels.length > 0 && ( )} - {apiErrorMessage && ( -

- - {apiErrorMessage} -

- )} - {selectedProvider === "glama" && ( )} @@ -1364,6 +1360,7 @@ const ApiOptions = ({ serviceName="OpenRouter" serviceUrl="https://openrouter.ai/models" recommendedModel="anthropic/claude-3.7-sonnet" + errorMessage={errorMessage} /> )} {selectedProvider === "requesty" && ( @@ -1378,6 +1375,7 @@ const ApiOptions = ({ serviceName="Requesty" serviceUrl="https://requesty.ai" recommendedModel="anthropic/claude-3-7-sonnet-latest" + errorMessage={errorMessage} /> )} @@ -1401,6 +1399,7 @@ const ApiOptions = ({ {selectedProvider === "deepseek" && createDropdown(deepSeekModels)} {selectedProvider === "mistral" && createDropdown(mistralModels)} + {errorMessage && } )} - - {modelIdErrorMessage && ( -

- - {modelIdErrorMessage} -

- )} ) } diff --git a/webview-ui/src/components/settings/ModelPicker.tsx b/webview-ui/src/components/settings/ModelPicker.tsx index 8fd6d82daa7..fd62bfb97b6 100644 --- a/webview-ui/src/components/settings/ModelPicker.tsx +++ b/webview-ui/src/components/settings/ModelPicker.tsx @@ -5,6 +5,7 @@ import { normalizeApiConfiguration } from "./ApiOptions" import { ModelInfoView } from "./ModelInfoView" import { ApiConfiguration, ModelInfo } from "../../../../src/shared/api" import { Combobox, ComboboxContent, ComboboxEmpty, ComboboxInput, ComboboxItem } from "../ui/combobox" +import ApiErrorMessage from "./ApiErrorMessage" type ExtractType = NonNullable< { [K in keyof ApiConfiguration]: Required[K] extends T ? K : never }[keyof ApiConfiguration] @@ -30,6 +31,7 @@ interface ModelPickerProps { apiConfiguration: ApiConfiguration setApiConfigurationField: (field: K, value: ApiConfiguration[K]) => void defaultModelInfo?: ModelInfo + errorMessage?: string } export const ModelPicker = ({ @@ -43,6 +45,7 @@ export const ModelPicker = ({ apiConfiguration, setApiConfigurationField, defaultModelInfo, + errorMessage, }: ModelPickerProps) => { const [isDescriptionExpanded, setIsDescriptionExpanded] = useState(false) @@ -69,11 +72,16 @@ export const ModelPicker = ({ return ( <>
Model
- + No model found. @@ -85,13 +93,30 @@ export const ModelPicker = ({ - {selectedModelId && selectedModelInfo && ( - + {errorMessage ? ( + +

+ + Note: Roo Code uses complex prompts and works best + with Claude models. Less capable models may not work as expected. + +

+
+ ) : ( + selectedModelId && + selectedModelInfo && ( + + ) )}

The extension automatically fetches the latest list of models available on{" "} diff --git a/webview-ui/src/components/settings/SettingsView.tsx b/webview-ui/src/components/settings/SettingsView.tsx index 75ba11107c4..ee032c3ee06 100644 --- a/webview-ui/src/components/settings/SettingsView.tsx +++ b/webview-ui/src/components/settings/SettingsView.tsx @@ -1,6 +1,6 @@ import { forwardRef, memo, useCallback, useEffect, useImperativeHandle, useMemo, useRef, useState } from "react" import { VSCodeButton, VSCodeCheckbox, VSCodeLink, VSCodeTextField } from "@vscode/webview-ui-toolkit/react" -import { Dropdown, type DropdownOption } from "vscrui" +import { Button, Dropdown, type DropdownOption } from "vscrui" import { AlertDialog, @@ -14,7 +14,6 @@ import { } from "@/components/ui" import { vscode } from "../../utils/vscode" -import { validateApiConfiguration, validateModelId } from "../../utils/validate" import { ExtensionStateContextType, useExtensionState } from "../../context/ExtensionStateContext" import { EXPERIMENT_IDS, experimentConfigsMap, ExperimentId } from "../../../../src/shared/experiments" import { ApiConfiguration } from "../../../../src/shared/api" @@ -33,14 +32,13 @@ export interface SettingsViewRef { const SettingsView = forwardRef(({ onDone }, ref) => { const extensionState = useExtensionState() - const [apiErrorMessage, setApiErrorMessage] = useState(undefined) - const [modelIdErrorMessage, setModelIdErrorMessage] = useState(undefined) const [commandInput, setCommandInput] = useState("") const [isDiscardDialogShow, setDiscardDialogShow] = useState(false) const [cachedState, setCachedState] = useState(extensionState) const [isChangeDetected, setChangeDetected] = useState(false) const prevApiConfigName = useRef(extensionState.currentApiConfigName) const confirmDialogHandler = useRef<() => void>() + const [errorMessage, setErrorMessage] = useState(undefined) // TODO: Reduce WebviewMessage/ExtensionState complexity const { currentApiConfigName } = extensionState @@ -135,20 +133,9 @@ const SettingsView = forwardRef(({ onDone }, } }) }, []) - + const isSettingValid = !errorMessage const handleSubmit = () => { - const apiValidationResult = validateApiConfiguration(apiConfiguration) - - const modelIdValidationResult = validateModelId( - apiConfiguration, - extensionState.glamaModels, - extensionState.openRouterModels, - ) - - setApiErrorMessage(apiValidationResult) - setModelIdErrorMessage(modelIdValidationResult) - - if (!apiValidationResult && !modelIdValidationResult) { + if (isSettingValid) { vscode.postMessage({ type: "alwaysAllowReadOnly", bool: alwaysAllowReadOnly }) vscode.postMessage({ type: "alwaysAllowWrite", bool: alwaysAllowWrite }) vscode.postMessage({ type: "alwaysAllowExecute", bool: alwaysAllowExecute }) @@ -177,23 +164,6 @@ const SettingsView = forwardRef(({ onDone }, } } - useEffect(() => { - setApiErrorMessage(undefined) - setModelIdErrorMessage(undefined) - }, [apiConfiguration]) - - // Initial validation on mount - useEffect(() => { - const apiValidationResult = validateApiConfiguration(apiConfiguration) - const modelIdValidationResult = validateModelId( - apiConfiguration, - extensionState.glamaModels, - extensionState.openRouterModels, - ) - setApiErrorMessage(apiValidationResult) - setModelIdErrorMessage(modelIdValidationResult) - }, [apiConfiguration, extensionState.glamaModels, extensionState.openRouterModels]) - const checkUnsaveChanges = useCallback( (then: () => void) => { if (isChangeDetected) { @@ -287,13 +257,14 @@ const SettingsView = forwardRef(({ onDone }, justifyContent: "space-between", gap: "6px", }}> - + disabled={!isChangeDetected || !isSettingValid}> Save - + (({ onDone }, uriScheme={extensionState.uriScheme} apiConfiguration={apiConfiguration} setApiConfigurationField={setApiConfigurationField} - apiErrorMessage={apiErrorMessage} - modelIdErrorMessage={modelIdErrorMessage} + errorMessage={errorMessage} + setErrorMessage={setErrorMessage} /> diff --git a/webview-ui/src/components/settings/__tests__/ApiOptions.test.tsx b/webview-ui/src/components/settings/__tests__/ApiOptions.test.tsx index 8f2d0dff893..73394bae104 100644 --- a/webview-ui/src/components/settings/__tests__/ApiOptions.test.tsx +++ b/webview-ui/src/components/settings/__tests__/ApiOptions.test.tsx @@ -51,6 +51,8 @@ describe("ApiOptions", () => { render( {}} uriScheme={undefined} apiConfiguration={{}} setApiConfigurationField={() => {}} @@ -69,4 +71,6 @@ describe("ApiOptions", () => { renderApiOptions({ fromWelcomeView: true }) expect(screen.queryByTestId("temperature-control")).not.toBeInTheDocument() }) + + //TODO: More test cases needed }) diff --git a/webview-ui/src/components/welcome/WelcomeView.tsx b/webview-ui/src/components/welcome/WelcomeView.tsx index 858d2622f39..5d880efc0b9 100644 --- a/webview-ui/src/components/welcome/WelcomeView.tsx +++ b/webview-ui/src/components/welcome/WelcomeView.tsx @@ -42,6 +42,8 @@ const WelcomeView = () => { apiConfiguration={apiConfiguration || {}} uriScheme={uriScheme} setApiConfigurationField={(field, value) => setApiConfiguration({ [field]: value })} + errorMessage={errorMessage} + setErrorMessage={setErrorMessage} /> From 48975003afe593c975476acfd348ceb6110d7ced Mon Sep 17 00:00:00 2001 From: System233 Date: Wed, 26 Feb 2025 06:50:11 +0800 Subject: [PATCH 109/585] Remove ModelInfo related exports from ExtensionStateContext --- .../src/context/ExtensionStateContext.tsx | 84 +------------------ 1 file changed, 1 insertion(+), 83 deletions(-) diff --git a/webview-ui/src/context/ExtensionStateContext.tsx b/webview-ui/src/context/ExtensionStateContext.tsx index 3dca8d5f51c..c2c4d181e4a 100644 --- a/webview-ui/src/context/ExtensionStateContext.tsx +++ b/webview-ui/src/context/ExtensionStateContext.tsx @@ -1,18 +1,7 @@ import React, { createContext, useCallback, useContext, useEffect, useState } from "react" import { useEvent } from "react-use" import { ApiConfigMeta, ExtensionMessage, ExtensionState } from "../../../src/shared/ExtensionMessage" -import { - ApiConfiguration, - ModelInfo, - glamaDefaultModelId, - glamaDefaultModelInfo, - openRouterDefaultModelId, - openRouterDefaultModelInfo, - unboundDefaultModelId, - unboundDefaultModelInfo, - requestyDefaultModelId, - requestyDefaultModelInfo, -} from "../../../src/shared/api" +import { ApiConfiguration } from "../../../src/shared/api" import { vscode } from "../utils/vscode" import { convertTextMateToHljs } from "../utils/textMateToHljs" import { findLastIndex } from "../../../src/shared/array" @@ -26,11 +15,6 @@ export interface ExtensionStateContextType extends ExtensionState { didHydrateState: boolean showWelcome: boolean theme: any - glamaModels: Record - requestyModels: Record - openRouterModels: Record - unboundModels: Record - openAiModels: string[] mcpServers: McpServer[] currentCheckpoint?: string filePaths: string[] @@ -70,7 +54,6 @@ export interface ExtensionStateContextType extends ExtensionState { setRateLimitSeconds: (value: number) => void setCurrentApiConfigName: (value: string) => void setListApiConfigMeta: (value: ApiConfigMeta[]) => void - onUpdateApiConfig: (apiConfig: ApiConfiguration) => void mode: Mode setMode: (value: Mode) => void setCustomModePrompts: (value: CustomModePrompts) => void @@ -124,21 +107,8 @@ export const ExtensionStateContextProvider: React.FC<{ children: React.ReactNode const [showWelcome, setShowWelcome] = useState(false) const [theme, setTheme] = useState(undefined) const [filePaths, setFilePaths] = useState([]) - const [glamaModels, setGlamaModels] = useState>({ - [glamaDefaultModelId]: glamaDefaultModelInfo, - }) const [openedTabs, setOpenedTabs] = useState>([]) - const [openRouterModels, setOpenRouterModels] = useState>({ - [openRouterDefaultModelId]: openRouterDefaultModelInfo, - }) - const [unboundModels, setUnboundModels] = useState>({ - [unboundDefaultModelId]: unboundDefaultModelInfo, - }) - const [requestyModels, setRequestyModels] = useState>({ - [requestyDefaultModelId]: requestyDefaultModelInfo, - }) - const [openAiModels, setOpenAiModels] = useState([]) const [mcpServers, setMcpServers] = useState([]) const [currentCheckpoint, setCurrentCheckpoint] = useState() @@ -146,18 +116,6 @@ export const ExtensionStateContextProvider: React.FC<{ children: React.ReactNode (value: ApiConfigMeta[]) => setState((prevState) => ({ ...prevState, listApiConfigMeta: value })), [], ) - - const onUpdateApiConfig = useCallback((apiConfig: ApiConfiguration) => { - setState((currentState) => { - vscode.postMessage({ - type: "upsertApiConfiguration", - text: currentState.currentApiConfigName, - apiConfiguration: { ...currentState.apiConfiguration, ...apiConfig }, - }) - return currentState // No state update needed - }) - }, []) - const handleMessage = useCallback( (event: MessageEvent) => { const message: ExtensionMessage = event.data @@ -202,40 +160,6 @@ export const ExtensionStateContextProvider: React.FC<{ children: React.ReactNode }) break } - case "glamaModels": { - const updatedModels = message.glamaModels ?? {} - setGlamaModels({ - [glamaDefaultModelId]: glamaDefaultModelInfo, // in case the extension sent a model list without the default model - ...updatedModels, - }) - break - } - case "openRouterModels": { - const updatedModels = message.openRouterModels ?? {} - setOpenRouterModels({ - [openRouterDefaultModelId]: openRouterDefaultModelInfo, // in case the extension sent a model list without the default model - ...updatedModels, - }) - break - } - case "openAiModels": { - const updatedModels = message.openAiModels ?? [] - setOpenAiModels(updatedModels) - break - } - case "unboundModels": { - const updatedModels = message.unboundModels ?? {} - setUnboundModels(updatedModels) - break - } - case "requestyModels": { - const updatedModels = message.requestyModels ?? {} - setRequestyModels({ - [requestyDefaultModelId]: requestyDefaultModelInfo, // in case the extension sent a model list without the default model - ...updatedModels, - }) - break - } case "mcpServers": { setMcpServers(message.mcpServers ?? []) break @@ -264,11 +188,6 @@ export const ExtensionStateContextProvider: React.FC<{ children: React.ReactNode didHydrateState, showWelcome, theme, - glamaModels, - requestyModels, - openRouterModels, - openAiModels, - unboundModels, mcpServers, currentCheckpoint, filePaths, @@ -316,7 +235,6 @@ export const ExtensionStateContextProvider: React.FC<{ children: React.ReactNode setRateLimitSeconds: (value) => setState((prevState) => ({ ...prevState, rateLimitSeconds: value })), setCurrentApiConfigName: (value) => setState((prevState) => ({ ...prevState, currentApiConfigName: value })), setListApiConfigMeta, - onUpdateApiConfig, setMode: (value: Mode) => setState((prevState) => ({ ...prevState, mode: value })), setCustomModePrompts: (value) => setState((prevState) => ({ ...prevState, customModePrompts: value })), setCustomSupportPrompts: (value) => setState((prevState) => ({ ...prevState, customSupportPrompts: value })), From 05151ed2254e8698d4c380ceb8daa36e019f82aa Mon Sep 17 00:00:00 2001 From: Matt Rubens Date: Tue, 25 Feb 2025 18:30:37 -0500 Subject: [PATCH 110/585] Update package.json --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 84bec2645a5..28045436e6b 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "roo-cline", "displayName": "Roo Code (prev. Roo Cline)", - "description": "An AI-powered autonomous coding agent that lives in your editor.", + "description": "A whole dev team of AI agents in your editor.", "publisher": "RooVeterinaryInc", "version": "3.7.4", "icon": "assets/icons/rocket.png", From e56908f6c1b800bd2a2b4edd85f725c1a0055920 Mon Sep 17 00:00:00 2001 From: cte Date: Tue, 25 Feb 2025 15:48:20 -0800 Subject: [PATCH 111/585] Thinking settings tweaks --- src/api/providers/anthropic.ts | 23 +++++---- src/api/providers/openrouter.ts | 12 ++++- src/shared/api.ts | 14 +++++- .../src/components/settings/ApiOptions.tsx | 50 ++++++------------- 4 files changed, 51 insertions(+), 48 deletions(-) diff --git a/src/api/providers/anthropic.ts b/src/api/providers/anthropic.ts index 2d1f07f833f..c9073506072 100644 --- a/src/api/providers/anthropic.ts +++ b/src/api/providers/anthropic.ts @@ -14,8 +14,6 @@ import { ApiStream } from "../transform/stream" const ANTHROPIC_DEFAULT_TEMPERATURE = 0 -const THINKING_MODELS = ["claude-3-7-sonnet-20250219"] - export class AnthropicHandler implements ApiHandler, SingleCompletionHandler { private options: ApiHandlerOptions private client: Anthropic @@ -32,16 +30,19 @@ export class AnthropicHandler implements ApiHandler, SingleCompletionHandler { async *createMessage(systemPrompt: string, messages: Anthropic.Messages.MessageParam[]): ApiStream { let stream: AnthropicStream const cacheControl: CacheControlEphemeral = { type: "ephemeral" } - const modelId = this.getModel().id - const maxTokens = this.getModel().info.maxTokens || 8192 + let { id: modelId, info: modelInfo } = this.getModel() + const maxTokens = modelInfo.maxTokens || 8192 + const budgetTokens = this.options.anthropicThinking ?? Math.min(maxTokens - 1, 8192) let temperature = this.options.modelTemperature ?? ANTHROPIC_DEFAULT_TEMPERATURE let thinking: BetaThinkingConfigParam | undefined = undefined - if (THINKING_MODELS.includes(modelId)) { - thinking = this.options.anthropicThinking - ? { type: "enabled", budget_tokens: this.options.anthropicThinking } - : { type: "disabled" } - + // Anthropic "Thinking" models require a temperature of 1.0. + if (modelId === "claude-3-7-sonnet-20250219:thinking") { + // The `:thinking` variant is a virtual identifier for the + // `claude-3-7-sonnet-20250219` model with a thinking budget. + // We can handle this more elegantly in the future. + modelId = "claude-3-7-sonnet-20250219" + thinking = { type: "enabled", budget_tokens: budgetTokens } temperature = 1.0 } @@ -114,8 +115,8 @@ export class AnthropicHandler implements ApiHandler, SingleCompletionHandler { default: { stream = (await this.client.messages.create({ model: modelId, - max_tokens: this.getModel().info.maxTokens || 8192, - temperature: this.options.modelTemperature ?? ANTHROPIC_DEFAULT_TEMPERATURE, + max_tokens: maxTokens, + temperature, system: [{ text: systemPrompt, type: "text" }], messages, // tools, diff --git a/src/api/providers/openrouter.ts b/src/api/providers/openrouter.ts index 69c55b8e712..6bf4fa4a8cb 100644 --- a/src/api/providers/openrouter.ts +++ b/src/api/providers/openrouter.ts @@ -1,4 +1,5 @@ import { Anthropic } from "@anthropic-ai/sdk" +import { BetaThinkingConfigParam } from "@anthropic-ai/sdk/resources/beta" import axios from "axios" import OpenAI from "openai" import delay from "delay" @@ -17,6 +18,7 @@ const OPENROUTER_DEFAULT_TEMPERATURE = 0 type OpenRouterChatCompletionParams = OpenAI.Chat.ChatCompletionCreateParams & { transforms?: string[] include_reasoning?: boolean + thinking?: BetaThinkingConfigParam } // Add custom interface for OpenRouter usage chunk. @@ -57,7 +59,7 @@ export class OpenRouterHandler implements ApiHandler, SingleCompletionHandler { // prompt caching: https://openrouter.ai/docs/prompt-caching // this is specifically for claude models (some models may 'support prompt caching' automatically without this) switch (true) { - case this.getModel().id.startsWith("anthropic/"): + case modelId.startsWith("anthropic/"): openAiMessages[0] = { role: "system", content: [ @@ -108,8 +110,13 @@ export class OpenRouterHandler implements ApiHandler, SingleCompletionHandler { let temperature = this.options.modelTemperature ?? defaultTemperature + const maxTokens = modelInfo.maxTokens + const budgetTokens = this.options.anthropicThinking ?? Math.min((maxTokens ?? 8192) - 1, 8192) + let thinking: BetaThinkingConfigParam | undefined = undefined + // Anthropic "Thinking" models require a temperature of 1.0. if (modelInfo.thinking) { + thinking = { type: "enabled", budget_tokens: budgetTokens } temperature = 1.0 } @@ -118,8 +125,9 @@ export class OpenRouterHandler implements ApiHandler, SingleCompletionHandler { const completionParams: OpenRouterChatCompletionParams = { model: modelId, - max_tokens: modelInfo.maxTokens, + max_tokens: maxTokens, temperature, + thinking, // OpenRouter is temporarily supporting this. top_p: topP, messages: openAiMessages, stream: true, diff --git a/src/shared/api.ts b/src/shared/api.ts index 63707e52b4b..5d4b8b120d7 100644 --- a/src/shared/api.ts +++ b/src/shared/api.ts @@ -103,7 +103,7 @@ export const THINKING_BUDGET = { export type AnthropicModelId = keyof typeof anthropicModels export const anthropicDefaultModelId: AnthropicModelId = "claude-3-7-sonnet-20250219" export const anthropicModels = { - "claude-3-7-sonnet-20250219": { + "claude-3-7-sonnet-20250219:thinking": { maxTokens: 16384, contextWindow: 200_000, supportsImages: true, @@ -115,6 +115,18 @@ export const anthropicModels = { cacheReadsPrice: 0.3, // $0.30 per million tokens thinking: true, }, + "claude-3-7-sonnet-20250219": { + maxTokens: 16384, + contextWindow: 200_000, + supportsImages: true, + supportsComputerUse: true, + supportsPromptCache: true, + inputPrice: 3.0, // $3 per million input tokens + outputPrice: 15.0, // $15 per million output tokens + cacheWritesPrice: 3.75, // $3.75 per million tokens + cacheReadsPrice: 0.3, // $0.30 per million tokens + thinking: false, + }, "claude-3-5-sonnet-20241022": { maxTokens: 8192, contextWindow: 200_000, diff --git a/webview-ui/src/components/settings/ApiOptions.tsx b/webview-ui/src/components/settings/ApiOptions.tsx index 9d17cae4fa3..73dc4fd41f8 100644 --- a/webview-ui/src/components/settings/ApiOptions.tsx +++ b/webview-ui/src/components/settings/ApiOptions.tsx @@ -73,7 +73,7 @@ const ApiOptions = ({ const [openRouterBaseUrlSelected, setOpenRouterBaseUrlSelected] = useState(!!apiConfiguration?.openRouterBaseUrl) const [isDescriptionExpanded, setIsDescriptionExpanded] = useState(false) - const anthropicThinkingBudget = apiConfiguration?.anthropicThinking + const anthropicThinkingBudget = apiConfiguration?.anthropicThinking ?? THINKING_BUDGET.default const noTransform = (value: T) => value const inputEventTransform = (event: E) => (event as { target: HTMLInputElement })?.target?.value as any @@ -1272,39 +1272,21 @@ const ApiOptions = ({ )} {selectedModelInfo && selectedModelInfo.thinking && ( -

- - setApiConfigurationField( - "anthropicThinking", - checked - ? Math.min( - THINKING_BUDGET.default, - selectedModelInfo.maxTokens ?? THINKING_BUDGET.default, - ) - : undefined, - ) - }> - Thinking? - - {anthropicThinkingBudget && ( - <> -
- Number of tokens Claude is allowed to use for its internal reasoning process. -
-
- setApiConfigurationField("anthropicThinking", value[0])} - /> -
{anthropicThinkingBudget}
-
- - )} +
+
Thinking Budget
+
+ setApiConfigurationField("anthropicThinking", value[0])} + /> +
{anthropicThinkingBudget}
+
+
+ Number of tokens Claude is allowed to use for its internal reasoning process. +
)} From 8971e47b96ec7ae3a6de4ec5a95a2acab4cba1b7 Mon Sep 17 00:00:00 2001 From: cte Date: Tue, 25 Feb 2025 15:53:32 -0800 Subject: [PATCH 112/585] Add changeset --- .changeset/swift-kings-attack.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/swift-kings-attack.md diff --git a/.changeset/swift-kings-attack.md b/.changeset/swift-kings-attack.md new file mode 100644 index 00000000000..8a8a425611d --- /dev/null +++ b/.changeset/swift-kings-attack.md @@ -0,0 +1,5 @@ +--- +"roo-cline": patch +--- + +Pass "thinking" params to OpenRouter From 33fd3bd6b35caafce66fcd53b9070f60279fcc0d Mon Sep 17 00:00:00 2001 From: cte Date: Tue, 25 Feb 2025 16:26:40 -0800 Subject: [PATCH 113/585] Fix budgetTokens --- src/api/providers/anthropic.ts | 2 +- src/api/providers/openrouter.ts | 8 +++----- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/src/api/providers/anthropic.ts b/src/api/providers/anthropic.ts index c9073506072..ad58a1cf6b2 100644 --- a/src/api/providers/anthropic.ts +++ b/src/api/providers/anthropic.ts @@ -32,7 +32,6 @@ export class AnthropicHandler implements ApiHandler, SingleCompletionHandler { const cacheControl: CacheControlEphemeral = { type: "ephemeral" } let { id: modelId, info: modelInfo } = this.getModel() const maxTokens = modelInfo.maxTokens || 8192 - const budgetTokens = this.options.anthropicThinking ?? Math.min(maxTokens - 1, 8192) let temperature = this.options.modelTemperature ?? ANTHROPIC_DEFAULT_TEMPERATURE let thinking: BetaThinkingConfigParam | undefined = undefined @@ -42,6 +41,7 @@ export class AnthropicHandler implements ApiHandler, SingleCompletionHandler { // `claude-3-7-sonnet-20250219` model with a thinking budget. // We can handle this more elegantly in the future. modelId = "claude-3-7-sonnet-20250219" + const budgetTokens = this.options.anthropicThinking ?? Math.max(maxTokens * 0.8, 1024) thinking = { type: "enabled", budget_tokens: budgetTokens } temperature = 1.0 } diff --git a/src/api/providers/openrouter.ts b/src/api/providers/openrouter.ts index 6bf4fa4a8cb..0a9488e816f 100644 --- a/src/api/providers/openrouter.ts +++ b/src/api/providers/openrouter.ts @@ -109,13 +109,11 @@ export class OpenRouterHandler implements ApiHandler, SingleCompletionHandler { } let temperature = this.options.modelTemperature ?? defaultTemperature - - const maxTokens = modelInfo.maxTokens - const budgetTokens = this.options.anthropicThinking ?? Math.min((maxTokens ?? 8192) - 1, 8192) let thinking: BetaThinkingConfigParam | undefined = undefined - // Anthropic "Thinking" models require a temperature of 1.0. if (modelInfo.thinking) { + const maxTokens = modelInfo.maxTokens || 8192 + const budgetTokens = this.options.anthropicThinking ?? Math.max(maxTokens * 0.8, 1024) thinking = { type: "enabled", budget_tokens: budgetTokens } temperature = 1.0 } @@ -125,7 +123,7 @@ export class OpenRouterHandler implements ApiHandler, SingleCompletionHandler { const completionParams: OpenRouterChatCompletionParams = { model: modelId, - max_tokens: maxTokens, + max_tokens: modelInfo.maxTokens, temperature, thinking, // OpenRouter is temporarily supporting this. top_p: topP, From f3d02030ac47420d8a9735c0d02f70e561475dae Mon Sep 17 00:00:00 2001 From: System233 Date: Wed, 26 Feb 2025 08:37:28 +0800 Subject: [PATCH 114/585] Fix: Input/output prices should be parsed using parseFloat --- webview-ui/src/components/settings/ApiOptions.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/webview-ui/src/components/settings/ApiOptions.tsx b/webview-ui/src/components/settings/ApiOptions.tsx index 73dc4fd41f8..bfcf93256e8 100644 --- a/webview-ui/src/components/settings/ApiOptions.tsx +++ b/webview-ui/src/components/settings/ApiOptions.tsx @@ -900,7 +900,7 @@ const ApiOptions = ({ }} onChange={handleInputChange("openAiCustomModelInfo", (e) => { const value = (e.target as HTMLInputElement).value - const parsed = parseInt(value) + const parsed = parseFloat(value) return { ...(apiConfiguration?.openAiCustomModelInfo ?? openAiModelInfoSaneDefaults), @@ -945,7 +945,7 @@ const ApiOptions = ({ }} onChange={handleInputChange("openAiCustomModelInfo", (e) => { const value = (e.target as HTMLInputElement).value - const parsed = parseInt(value) + const parsed = parseFloat(value) return { ...(apiConfiguration?.openAiCustomModelInfo || openAiModelInfoSaneDefaults), From 41e75bc9890036674cd31ce2d9da23fcd5127956 Mon Sep 17 00:00:00 2001 From: cte Date: Tue, 25 Feb 2025 23:02:43 -0800 Subject: [PATCH 115/585] Model picker fixes --- .changeset/real-ties-destroy.md | 5 + src/api/providers/requesty.ts | 36 +- src/core/webview/ClineProvider.ts | 22 +- src/shared/ExtensionMessage.ts | 11 +- src/shared/WebviewMessage.ts | 5 +- webview-ui/package-lock.json | 2 + .../components/settings/ApiErrorMessage.tsx | 18 +- .../src/components/settings/ApiOptions.tsx | 435 ++++++++---------- .../src/components/settings/ModelPicker.tsx | 91 ++-- .../src/components/settings/SettingsView.tsx | 48 +- .../components/settings/ThinkingBudget.tsx | 29 ++ webview-ui/src/components/ui/alert-dialog.tsx | 151 +++--- webview-ui/src/components/ui/dialog.tsx | 156 ++++--- .../src/components/welcome/WelcomeView.tsx | 8 +- webview-ui/src/utils/validate.ts | 254 ++++++---- 15 files changed, 657 insertions(+), 614 deletions(-) create mode 100644 .changeset/real-ties-destroy.md create mode 100644 webview-ui/src/components/settings/ThinkingBudget.tsx diff --git a/.changeset/real-ties-destroy.md b/.changeset/real-ties-destroy.md new file mode 100644 index 00000000000..a2e9ba8eb04 --- /dev/null +++ b/.changeset/real-ties-destroy.md @@ -0,0 +1,5 @@ +--- +"roo-cline": patch +--- + +Fix model picker diff --git a/src/api/providers/requesty.ts b/src/api/providers/requesty.ts index 2151a7172d1..5e570ca2a2b 100644 --- a/src/api/providers/requesty.ts +++ b/src/api/providers/requesty.ts @@ -42,26 +42,33 @@ export class RequestyHandler extends OpenAiHandler { } } -export async function getRequestyModels({ apiKey }: { apiKey?: string }) { +export async function getRequestyModels() { const models: Record = {} - if (!apiKey) { - return models - } - try { - const config: Record = {} - config["headers"] = { Authorization: `Bearer ${apiKey}` } - - const response = await axios.get("https://router.requesty.ai/v1/models", config) + const response = await axios.get("https://router.requesty.ai/v1/models") const rawModels = response.data.data for (const rawModel of rawModels) { + // { + // id: "anthropic/claude-3-5-sonnet-20240620", + // object: "model", + // created: 1740552655, + // owned_by: "system", + // input_price: 0.0000028, + // caching_price: 0.00000375, + // cached_price: 3e-7, + // output_price: 0.000015, + // max_output_tokens: 8192, + // context_window: 200000, + // supports_caching: true, + // description: + // "Anthropic's previous most intelligent model. High level of intelligence and capability. Excells in coding.", + // } + const modelInfo: ModelInfo = { maxTokens: rawModel.max_output_tokens, contextWindow: rawModel.context_window, - supportsImages: rawModel.support_image, - supportsComputerUse: rawModel.support_computer_use, supportsPromptCache: rawModel.supports_caching, inputPrice: parseApiPrice(rawModel.input_price), outputPrice: parseApiPrice(rawModel.output_price), @@ -72,8 +79,15 @@ export async function getRequestyModels({ apiKey }: { apiKey?: string }) { switch (rawModel.id) { case rawModel.id.startsWith("anthropic/claude-3-7-sonnet"): + modelInfo.supportsComputerUse = true + modelInfo.supportsImages = true modelInfo.maxTokens = 16384 break + case rawModel.id.startsWith("anthropic/claude-3-5-sonnet-20241022"): + modelInfo.supportsComputerUse = true + modelInfo.supportsImages = true + modelInfo.maxTokens = 8192 + break case rawModel.id.startsWith("anthropic/"): modelInfo.maxTokens = 8192 break diff --git a/src/core/webview/ClineProvider.ts b/src/core/webview/ClineProvider.ts index 118bbddcf55..bc6f4578683 100644 --- a/src/core/webview/ClineProvider.ts +++ b/src/core/webview/ClineProvider.ts @@ -644,9 +644,7 @@ export class ClineProvider implements vscode.WebviewViewProvider { } }) - const requestyApiKey = await this.getSecret("requestyApiKey") - - getRequestyModels({ apiKey: requestyApiKey }).then(async (requestyModels) => { + getRequestyModels().then(async (requestyModels) => { if (Object.keys(requestyModels).length > 0) { await fs.writeFile( path.join(cacheDir, GlobalFileNames.requestyModels), @@ -838,17 +836,15 @@ export class ClineProvider implements vscode.WebviewViewProvider { break case "refreshRequestyModels": - if (message?.values?.apiKey) { - const requestyModels = await getRequestyModels({ apiKey: message.values.apiKey }) + const requestyModels = await getRequestyModels() - if (Object.keys(requestyModels).length > 0) { - const cacheDir = await this.ensureCacheDirectoryExists() - await fs.writeFile( - path.join(cacheDir, GlobalFileNames.requestyModels), - JSON.stringify(requestyModels), - ) - await this.postMessageToWebview({ type: "requestyModels", requestyModels }) - } + if (Object.keys(requestyModels).length > 0) { + const cacheDir = await this.ensureCacheDirectoryExists() + await fs.writeFile( + path.join(cacheDir, GlobalFileNames.requestyModels), + JSON.stringify(requestyModels), + ) + await this.postMessageToWebview({ type: "requestyModels", requestyModels }) } break diff --git a/src/shared/ExtensionMessage.ts b/src/shared/ExtensionMessage.ts index 8f64a9ba056..e87edffed16 100644 --- a/src/shared/ExtensionMessage.ts +++ b/src/shared/ExtensionMessage.ts @@ -27,10 +27,11 @@ export interface ExtensionMessage { | "workspaceUpdated" | "invoke" | "partialMessage" - | "glamaModels" | "openRouterModels" - | "openAiModels" + | "glamaModels" + | "unboundModels" | "requestyModels" + | "openAiModels" | "mcpServers" | "enhancedPrompt" | "commitSearchResults" @@ -43,8 +44,6 @@ export interface ExtensionMessage { | "autoApprovalEnabled" | "updateCustomMode" | "deleteCustomMode" - | "unboundModels" - | "refreshUnboundModels" | "currentCheckpointUpdated" text?: string action?: @@ -67,11 +66,11 @@ export interface ExtensionMessage { path?: string }> partialMessage?: ClineMessage + openRouterModels?: Record glamaModels?: Record + unboundModels?: Record requestyModels?: Record - openRouterModels?: Record openAiModels?: string[] - unboundModels?: Record mcpServers?: McpServer[] commits?: GitCommit[] listApiConfig?: ApiConfigMeta[] diff --git a/src/shared/WebviewMessage.ts b/src/shared/WebviewMessage.ts index 106e6d243b9..fde7442cc1d 100644 --- a/src/shared/WebviewMessage.ts +++ b/src/shared/WebviewMessage.ts @@ -40,11 +40,11 @@ export interface WebviewMessage { | "openFile" | "openMention" | "cancelTask" - | "refreshGlamaModels" | "refreshOpenRouterModels" - | "refreshOpenAiModels" + | "refreshGlamaModels" | "refreshUnboundModels" | "refreshRequestyModels" + | "refreshOpenAiModels" | "alwaysAllowBrowser" | "alwaysAllowMcp" | "alwaysAllowModeSwitch" @@ -71,7 +71,6 @@ export interface WebviewMessage { | "mcpEnabled" | "enableMcpServerCreation" | "searchCommits" - | "refreshGlamaModels" | "alwaysApproveResubmit" | "requestDelaySeconds" | "rateLimitSeconds" diff --git a/webview-ui/package-lock.json b/webview-ui/package-lock.json index 1d64f934dc2..22564d01a65 100644 --- a/webview-ui/package-lock.json +++ b/webview-ui/package-lock.json @@ -3674,6 +3674,7 @@ "version": "1.1.6", "resolved": "https://registry.npmjs.org/@radix-ui/react-alert-dialog/-/react-alert-dialog-1.1.6.tgz", "integrity": "sha512-p4XnPqgej8sZAAReCAKgz1REYZEBLR8hU9Pg27wFnCWIMc8g1ccCs0FjBcy05V15VTu8pAePw/VDYeOm/uZ6yQ==", + "license": "MIT", "dependencies": { "@radix-ui/primitive": "1.1.1", "@radix-ui/react-compose-refs": "1.1.1", @@ -4719,6 +4720,7 @@ "version": "1.1.2", "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.1.2.tgz", "integrity": "sha512-YAKxaiGsSQJ38VzKH86/BPRC4rh+b1Jpa+JneA5LRE7skmLPNAyeG8kPJj/oo4STLvlrs8vkf/iYyc3A5stYCQ==", + "license": "MIT", "dependencies": { "@radix-ui/react-compose-refs": "1.1.1" }, diff --git a/webview-ui/src/components/settings/ApiErrorMessage.tsx b/webview-ui/src/components/settings/ApiErrorMessage.tsx index 4b419957b6c..06764a1bfa0 100644 --- a/webview-ui/src/components/settings/ApiErrorMessage.tsx +++ b/webview-ui/src/components/settings/ApiErrorMessage.tsx @@ -4,13 +4,13 @@ interface ApiErrorMessageProps { errorMessage: string | undefined children?: React.ReactNode } -const ApiErrorMessage = ({ errorMessage, children }: ApiErrorMessageProps) => { - return ( -
- - {errorMessage} - {children} + +export const ApiErrorMessage = ({ errorMessage, children }: ApiErrorMessageProps) => ( +
+
+
+
{errorMessage}
- ) -} -export default ApiErrorMessage + {children} +
+) diff --git a/webview-ui/src/components/settings/ApiOptions.tsx b/webview-ui/src/components/settings/ApiOptions.tsx index 107f2a483ae..c30035cef01 100644 --- a/webview-ui/src/components/settings/ApiOptions.tsx +++ b/webview-ui/src/components/settings/ApiOptions.tsx @@ -4,8 +4,6 @@ import { Checkbox, Dropdown, Pane, type DropdownOption } from "vscrui" import { VSCodeLink, VSCodeRadio, VSCodeRadioGroup, VSCodeTextField } from "@vscode/webview-ui-toolkit/react" import * as vscodemodels from "vscode" -import { Slider } from "@/components/ui" - import { ApiConfiguration, ModelInfo, @@ -33,7 +31,6 @@ import { unboundDefaultModelInfo, requestyDefaultModelId, requestyDefaultModelInfo, - THINKING_BUDGET, } from "../../../../src/shared/api" import { ExtensionMessage } from "../../../../src/shared/ExtensionMessage" @@ -44,7 +41,18 @@ import { DROPDOWN_Z_INDEX } from "./styles" import { ModelPicker } from "./ModelPicker" import { TemperatureControl } from "./TemperatureControl" import { validateApiConfiguration, validateModelId } from "@/utils/validate" -import ApiErrorMessage from "./ApiErrorMessage" +import { ApiErrorMessage } from "./ApiErrorMessage" +import { ThinkingBudget } from "./ThinkingBudget" + +const modelsByProvider: Record> = { + anthropic: anthropicModels, + bedrock: bedrockModels, + vertex: vertexModels, + gemini: geminiModels, + "openai-native": openAiNativeModels, + deepseek: deepSeekModels, + mistral: mistralModels, +} interface ApiOptionsProps { uriScheme: string | undefined @@ -66,18 +74,23 @@ const ApiOptions = ({ const [ollamaModels, setOllamaModels] = useState([]) const [lmStudioModels, setLmStudioModels] = useState([]) const [vsCodeLmModels, setVsCodeLmModels] = useState([]) + const [openRouterModels, setOpenRouterModels] = useState>({ [openRouterDefaultModelId]: openRouterDefaultModelInfo, }) + const [glamaModels, setGlamaModels] = useState>({ [glamaDefaultModelId]: glamaDefaultModelInfo, }) + const [unboundModels, setUnboundModels] = useState>({ [unboundDefaultModelId]: unboundDefaultModelInfo, }) + const [requestyModels, setRequestyModels] = useState>({ [requestyDefaultModelId]: requestyDefaultModelInfo, }) + const [openAiModels, setOpenAiModels] = useState | null>(null) const [anthropicBaseUrlSelected, setAnthropicBaseUrlSelected] = useState(!!apiConfiguration?.anthropicBaseUrl) @@ -85,8 +98,6 @@ const ApiOptions = ({ const [openRouterBaseUrlSelected, setOpenRouterBaseUrlSelected] = useState(!!apiConfiguration?.openRouterBaseUrl) const [isDescriptionExpanded, setIsDescriptionExpanded] = useState(false) - const anthropicThinkingBudget = apiConfiguration?.anthropicThinking ?? THINKING_BUDGET.default - const noTransform = (value: T) => value const inputEventTransform = (event: E) => (event as { target: HTMLInputElement })?.target?.value as any const dropdownEventTransform = (event: DropdownOption | string | undefined) => @@ -103,62 +114,87 @@ const ApiOptions = ({ [setApiConfigurationField], ) - const { selectedProvider, selectedModelId, selectedModelInfo } = useMemo(() => { - return normalizeApiConfiguration(apiConfiguration) - }, [apiConfiguration]) + const { selectedProvider, selectedModelId, selectedModelInfo } = useMemo( + () => normalizeApiConfiguration(apiConfiguration), + [apiConfiguration], + ) - // Pull ollama/lmstudio models - // Debounced model updates, only executed 250ms after the user stops typing + // Debounced refresh model updates, only executed 250ms after the user + // stops typing. useDebounce( () => { - if (selectedProvider === "ollama") { - vscode.postMessage({ type: "requestOllamaModels", text: apiConfiguration?.ollamaBaseUrl }) - } else if (selectedProvider === "lmstudio") { - vscode.postMessage({ type: "requestLmStudioModels", text: apiConfiguration?.lmStudioBaseUrl }) - } else if (selectedProvider === "vscode-lm") { - vscode.postMessage({ type: "requestVsCodeLmModels" }) - } else if (selectedProvider === "openai") { - vscode.postMessage({ - type: "refreshOpenAiModels", - values: { - baseUrl: apiConfiguration?.openAiBaseUrl, - apiKey: apiConfiguration?.openAiApiKey, - }, - }) - } else if (selectedProvider === "openrouter") { - vscode.postMessage({ type: "refreshOpenRouterModels", values: {} }) + if (selectedProvider === "openrouter") { + vscode.postMessage({ type: "refreshOpenRouterModels" }) } else if (selectedProvider === "glama") { - vscode.postMessage({ type: "refreshGlamaModels", values: {} }) + vscode.postMessage({ type: "refreshGlamaModels" }) + } else if (selectedProvider === "unbound") { + vscode.postMessage({ type: "refreshUnboundModels" }) } else if (selectedProvider === "requesty") { vscode.postMessage({ type: "refreshRequestyModels", - values: { - apiKey: apiConfiguration?.requestyApiKey, - }, + values: { apiKey: apiConfiguration?.requestyApiKey }, + }) + } else if (selectedProvider === "openai") { + vscode.postMessage({ + type: "refreshOpenAiModels", + values: { baseUrl: apiConfiguration?.openAiBaseUrl, apiKey: apiConfiguration?.openAiApiKey }, }) + } else if (selectedProvider === "ollama") { + vscode.postMessage({ type: "requestOllamaModels", text: apiConfiguration?.ollamaBaseUrl }) + } else if (selectedProvider === "lmstudio") { + vscode.postMessage({ type: "requestLmStudioModels", text: apiConfiguration?.lmStudioBaseUrl }) + } else if (selectedProvider === "vscode-lm") { + vscode.postMessage({ type: "requestVsCodeLmModels" }) } }, 250, [ selectedProvider, - apiConfiguration?.ollamaBaseUrl, - apiConfiguration?.lmStudioBaseUrl, + apiConfiguration?.requestyApiKey, apiConfiguration?.openAiBaseUrl, apiConfiguration?.openAiApiKey, - apiConfiguration?.requestyApiKey, + apiConfiguration?.ollamaBaseUrl, + apiConfiguration?.lmStudioBaseUrl, ], ) useEffect(() => { const apiValidationResult = validateApiConfiguration(apiConfiguration) || - validateModelId(apiConfiguration, glamaModels, openRouterModels, unboundModels) + validateModelId(apiConfiguration, glamaModels, openRouterModels, unboundModels, requestyModels) + setErrorMessage(apiValidationResult) - }, [apiConfiguration, glamaModels, openRouterModels, setErrorMessage, unboundModels]) + }, [apiConfiguration, glamaModels, openRouterModels, setErrorMessage, unboundModels, requestyModels]) - const handleMessage = useCallback((event: MessageEvent) => { + const onMessage = useCallback((event: MessageEvent) => { const message: ExtensionMessage = event.data + switch (message.type) { + case "openRouterModels": { + const updatedModels = message.openRouterModels ?? {} + setOpenRouterModels({ [openRouterDefaultModelId]: openRouterDefaultModelInfo, ...updatedModels }) + break + } + case "glamaModels": { + const updatedModels = message.glamaModels ?? {} + setGlamaModels({ [glamaDefaultModelId]: glamaDefaultModelInfo, ...updatedModels }) + break + } + case "unboundModels": { + const updatedModels = message.unboundModels ?? {} + setUnboundModels({ [unboundDefaultModelId]: unboundDefaultModelInfo, ...updatedModels }) + break + } + case "requestyModels": { + const updatedModels = message.requestyModels ?? {} + setRequestyModels({ [requestyDefaultModelId]: requestyDefaultModelInfo, ...updatedModels }) + break + } + case "openAiModels": { + const updatedModels = message.openAiModels ?? [] + setOpenAiModels(Object.fromEntries(updatedModels.map((item) => [item, openAiModelInfoSaneDefaults]))) + break + } case "ollamaModels": { const newModels = message.ollamaModels ?? [] @@ -177,72 +213,30 @@ const ApiOptions = ({ setVsCodeLmModels(newModels) } break - case "glamaModels": { - const updatedModels = message.glamaModels ?? {} - setGlamaModels({ - [glamaDefaultModelId]: glamaDefaultModelInfo, // in case the extension sent a model list without the default model - ...updatedModels, - }) - break - } - case "openRouterModels": { - const updatedModels = message.openRouterModels ?? {} - setOpenRouterModels({ - [openRouterDefaultModelId]: openRouterDefaultModelInfo, // in case the extension sent a model list without the default model - ...updatedModels, - }) - break - } - case "openAiModels": { - const updatedModels = message.openAiModels ?? [] - setOpenAiModels(Object.fromEntries(updatedModels.map((item) => [item, openAiModelInfoSaneDefaults]))) - break - } - case "unboundModels": { - const updatedModels = message.unboundModels ?? {} - setUnboundModels(updatedModels) - break - } - case "requestyModels": { - const updatedModels = message.requestyModels ?? {} - setRequestyModels({ - [requestyDefaultModelId]: requestyDefaultModelInfo, // in case the extension sent a model list without the default model - ...updatedModels, - }) - break - } } }, []) - useEvent("message", handleMessage) - - const createDropdown = (models: Record) => { - const options: DropdownOption[] = [ - { value: "", label: "Select a model..." }, - ...Object.keys(models).map((modelId) => ({ - value: modelId, - label: modelId, - })), - ] - - return ( - { - setApiConfigurationField("apiModelId", typeof value == "string" ? value : value?.value) - }} - style={{ width: "100%" }} - options={options} - /> - ) - } + useEvent("message", onMessage) + + const selectedProviderModelOptions: DropdownOption[] = useMemo( + () => + modelsByProvider[selectedProvider] + ? [ + { value: "", label: "Select a model..." }, + ...Object.keys(modelsByProvider[selectedProvider]).map((modelId) => ({ + value: modelId, + label: modelId, + })), + ] + : [], + [selectedProvider], + ) return (
-
+ {errorMessage && } + {selectedProvider === "anthropic" && (
- Anthropic API Key + Anthropic API Key - Glama API Key + Glama API Key {!apiConfiguration?.glamaApiKey && ( - Requesty API Key + Requesty API Key

- OpenAI API Key + OpenAI API Key

- Mistral API Key + Mistral API Key

- Codestral Base URL (Optional) + Codestral Base URL (Optional)

- OpenRouter API Key + OpenRouter API Key {!apiConfiguration?.openRouterApiKey && (

@@ -530,7 +526,7 @@ const ApiOptions = ({ style={{ width: "100%" }} onInput={handleInputChange("awsProfile")} placeholder="Enter profile name"> - AWS Profile Name + AWS Profile Name ) : ( <> @@ -541,7 +537,7 @@ const ApiOptions = ({ type="password" onInput={handleInputChange("awsAccessKey")} placeholder="Enter Access Key..."> - AWS Access Key + AWS Access Key - AWS Secret Key + AWS Secret Key - AWS Session Token + AWS Session Token )}

- Google Cloud Project ID + Google Cloud Project ID
- {errorMessage && }

- Gemini API Key + Gemini API Key

- Base URL + Base URL - API Key + API Key

)} - -
+
- Max Output Tokens + Max Output Tokens
- Context Window Size + Context Window Size
- Image Support + Image Support - Computer Use + Computer Use
- Input Price + Input Price
- Output Price + Output Price - Base URL (optional) + Base URL (optional) - Model ID + Model ID - {errorMessage && } - {lmStudioModels.length > 0 && ( {" "} feature to use it with this extension.{" "} - (Note: Roo Code uses complex prompts and works best + (Note: Roo Code uses complex prompts and works best with Claude models. Less capable models may not work as expected.)

@@ -1154,7 +1141,7 @@ const ApiOptions = ({ type="password" onInput={handleInputChange("deepSeekApiKey")} placeholder="Enter API Key..."> - DeepSeek API Key + DeepSeek API Key

{vsCodeLmModels.length > 0 ? ( - Base URL (optional) + Base URL (optional) - Model ID + Model ID {errorMessage && (
@@ -1284,7 +1271,7 @@ const ApiOptions = ({ quickstart guide. - (Note: Roo Code uses complex prompts and works best + (Note: Roo Code uses complex prompts and works best with Claude models. Less capable models may not work as expected.)

@@ -1299,7 +1286,7 @@ const ApiOptions = ({ type="password" onChange={handleInputChange("unboundApiKey")} placeholder="Enter API Key..."> - Unbound API Key + Unbound API Key {!apiConfiguration?.unboundApiKey && ( This key is stored locally and only used to make API requests from this extension.

-
)} + {selectedProvider === "openrouter" && ( + + )} + {selectedProvider === "glama" && ( )} - {selectedProvider === "openrouter" && ( + {selectedProvider === "unbound" && ( )} + {selectedProvider === "requesty" && ( )} - {selectedProvider !== "glama" && - selectedProvider !== "openrouter" && - selectedProvider !== "requesty" && - selectedProvider !== "openai" && - selectedProvider !== "ollama" && - selectedProvider !== "lmstudio" && - selectedProvider !== "unbound" && ( - <> -
- - {selectedProvider === "anthropic" && createDropdown(anthropicModels)} - {selectedProvider === "bedrock" && createDropdown(bedrockModels)} - {selectedProvider === "vertex" && createDropdown(vertexModels)} - {selectedProvider === "gemini" && createDropdown(geminiModels)} - {selectedProvider === "openai-native" && createDropdown(openAiNativeModels)} - {selectedProvider === "deepseek" && createDropdown(deepSeekModels)} - {selectedProvider === "mistral" && createDropdown(mistralModels)} -
- {errorMessage && } - - - )} - - {selectedModelInfo && selectedModelInfo.thinking && ( -
-
Thinking Budget
-
- setApiConfigurationField("anthropicThinking", value[0])} + {selectedProviderModelOptions.length > 0 && ( + <> +
+ + { + setApiConfigurationField("apiModelId", typeof value == "string" ? value : value?.value) + }} + options={selectedProviderModelOptions} + className="w-full" /> -
{anthropicThinkingBudget}
-
-
- Number of tokens Claude is allowed to use for its internal reasoning process.
-
+ + + )} {!fromWelcomeView && ( @@ -1459,6 +1423,7 @@ export function normalizeApiConfiguration(apiConfiguration?: ApiConfiguration) { const getProviderData = (models: Record, defaultId: string) => { let selectedModelId: string let selectedModelInfo: ModelInfo + if (modelId && modelId in models) { selectedModelId = modelId selectedModelInfo = models[modelId] @@ -1466,8 +1431,10 @@ export function normalizeApiConfiguration(apiConfiguration?: ApiConfiguration) { selectedModelId = defaultId selectedModelInfo = models[defaultId] } + return { selectedProvider: provider, selectedModelId, selectedModelInfo } } + switch (provider) { case "anthropic": return getProviderData(anthropicModels, anthropicDefaultModelId) @@ -1481,19 +1448,31 @@ export function normalizeApiConfiguration(apiConfiguration?: ApiConfiguration) { return getProviderData(deepSeekModels, deepSeekDefaultModelId) case "openai-native": return getProviderData(openAiNativeModels, openAiNativeDefaultModelId) + case "mistral": + return getProviderData(mistralModels, mistralDefaultModelId) + case "openrouter": + return { + selectedProvider: provider, + selectedModelId: apiConfiguration?.openRouterModelId || openRouterDefaultModelId, + selectedModelInfo: apiConfiguration?.openRouterModelInfo || openRouterDefaultModelInfo, + } case "glama": return { selectedProvider: provider, selectedModelId: apiConfiguration?.glamaModelId || glamaDefaultModelId, selectedModelInfo: apiConfiguration?.glamaModelInfo || glamaDefaultModelInfo, } - case "mistral": - return getProviderData(mistralModels, mistralDefaultModelId) - case "openrouter": + case "unbound": return { selectedProvider: provider, - selectedModelId: apiConfiguration?.openRouterModelId || openRouterDefaultModelId, - selectedModelInfo: apiConfiguration?.openRouterModelInfo || openRouterDefaultModelInfo, + selectedModelId: apiConfiguration?.unboundModelId || unboundDefaultModelId, + selectedModelInfo: apiConfiguration?.unboundModelInfo || unboundDefaultModelInfo, + } + case "requesty": + return { + selectedProvider: provider, + selectedModelId: apiConfiguration?.requestyModelId || requestyDefaultModelId, + selectedModelInfo: apiConfiguration?.requestyModelInfo || requestyDefaultModelInfo, } case "openai": return { @@ -1521,21 +1500,9 @@ export function normalizeApiConfiguration(apiConfiguration?: ApiConfiguration) { : "", selectedModelInfo: { ...openAiModelInfoSaneDefaults, - supportsImages: false, // VSCode LM API currently doesn't support images + supportsImages: false, // VSCode LM API currently doesn't support images. }, } - case "unbound": - return { - selectedProvider: provider, - selectedModelId: apiConfiguration?.unboundModelId || unboundDefaultModelId, - selectedModelInfo: apiConfiguration?.unboundModelInfo || unboundDefaultModelInfo, - } - case "requesty": - return { - selectedProvider: provider, - selectedModelId: apiConfiguration?.requestyModelId || requestyDefaultModelId, - selectedModelInfo: apiConfiguration?.requestyModelInfo || requestyDefaultModelInfo, - } default: return getProviderData(anthropicModels, anthropicDefaultModelId) } diff --git a/webview-ui/src/components/settings/ModelPicker.tsx b/webview-ui/src/components/settings/ModelPicker.tsx index fd62bfb97b6..5a7737edd56 100644 --- a/webview-ui/src/components/settings/ModelPicker.tsx +++ b/webview-ui/src/components/settings/ModelPicker.tsx @@ -1,11 +1,13 @@ +import { useMemo, useState, useCallback, useEffect, useRef } from "react" import { VSCodeLink } from "@vscode/webview-ui-toolkit/react" -import { useMemo, useState, useCallback, useEffect } from "react" + +import { Combobox, ComboboxContent, ComboboxEmpty, ComboboxInput, ComboboxItem } from "@/components/ui/combobox" + +import { ApiConfiguration, ModelInfo } from "../../../../src/shared/api" import { normalizeApiConfiguration } from "./ApiOptions" +import { ThinkingBudget } from "./ThinkingBudget" import { ModelInfoView } from "./ModelInfoView" -import { ApiConfiguration, ModelInfo } from "../../../../src/shared/api" -import { Combobox, ComboboxContent, ComboboxEmpty, ComboboxInput, ComboboxItem } from "../ui/combobox" -import ApiErrorMessage from "./ApiErrorMessage" type ExtractType = NonNullable< { [K in keyof ApiConfiguration]: Required[K] extends T ? K : never }[keyof ApiConfiguration] @@ -14,24 +16,17 @@ type ExtractType = NonNullable< type ModelIdKeys = NonNullable< { [K in keyof ApiConfiguration]: K extends `${string}ModelId` ? K : never }[keyof ApiConfiguration] > -declare module "react" { - interface CSSProperties { - // Allow CSS variables - [key: `--${string}`]: string | number - } -} + interface ModelPickerProps { - defaultModelId?: string + defaultModelId: string + defaultModelInfo?: ModelInfo models: Record | null modelIdKey: ModelIdKeys modelInfoKey: ExtractType serviceName: string serviceUrl: string - recommendedModel: string apiConfiguration: ApiConfiguration setApiConfigurationField: (field: K, value: ApiConfiguration[K]) => void - defaultModelInfo?: ModelInfo - errorMessage?: string } export const ModelPicker = ({ @@ -41,13 +36,12 @@ export const ModelPicker = ({ modelInfoKey, serviceName, serviceUrl, - recommendedModel, apiConfiguration, setApiConfigurationField, defaultModelInfo, - errorMessage, }: ModelPickerProps) => { const [isDescriptionExpanded, setIsDescriptionExpanded] = useState(false) + const isInitialized = useRef(false) const modelIds = useMemo(() => Object.keys(models ?? {}).sort((a, b) => a.localeCompare(b)), [models]) @@ -55,6 +49,7 @@ export const ModelPicker = ({ () => normalizeApiConfiguration(apiConfiguration), [apiConfiguration], ) + const onSelect = useCallback( (modelId: string) => { const modelInfo = models?.[modelId] @@ -63,26 +58,23 @@ export const ModelPicker = ({ }, [modelIdKey, modelInfoKey, models, setApiConfigurationField, defaultModelInfo], ) + + const inputValue = apiConfiguration[modelIdKey] + useEffect(() => { - if (apiConfiguration[modelIdKey] == null && defaultModelId) { - onSelect(defaultModelId) + if (!inputValue && !isInitialized.current) { + const initialValue = modelIds.includes(selectedModelId) ? selectedModelId : defaultModelId + setApiConfigurationField(modelIdKey, initialValue) } - }, [apiConfiguration, defaultModelId, modelIdKey, onSelect]) + + isInitialized.current = true + }, [inputValue, modelIds, setApiConfigurationField, modelIdKey, selectedModelId, defaultModelId]) return ( <>
Model
- - + + No model found. {modelIds.map((model) => ( @@ -92,31 +84,18 @@ export const ModelPicker = ({ ))} - - {errorMessage ? ( - -

- - Note: Roo Code uses complex prompts and works best - with Claude models. Less capable models may not work as expected. - -

-
- ) : ( - selectedModelId && - selectedModelInfo && ( - - ) + + {selectedModelId && selectedModelInfo && selectedModelId === inputValue && ( + )}

The extension automatically fetches the latest list of models available on{" "} @@ -124,7 +103,7 @@ export const ModelPicker = ({ {serviceName}. If you're unsure which model to choose, Roo Code works best with{" "} - onSelect(recommendedModel)}>{recommendedModel}. + onSelect(defaultModelId)}>{defaultModelId}. You can also try searching "free" for no-cost options currently available.

diff --git a/webview-ui/src/components/settings/SettingsView.tsx b/webview-ui/src/components/settings/SettingsView.tsx index ee032c3ee06..d3e65a99ea8 100644 --- a/webview-ui/src/components/settings/SettingsView.tsx +++ b/webview-ui/src/components/settings/SettingsView.tsx @@ -66,21 +66,20 @@ const SettingsView = forwardRef(({ onDone }, terminalOutputLineLimit, writeDelayMs, } = cachedState - + //Make sure apiConfiguration is initialized and managed by SettingsView const apiConfiguration = useMemo(() => cachedState.apiConfiguration ?? {}, [cachedState.apiConfiguration]) useEffect(() => { - // Update only when currentApiConfigName is changed - // Expected to be triggered by loadApiConfiguration/upsertApiConfiguration + // Update only when currentApiConfigName is changed. + // Expected to be triggered by loadApiConfiguration/upsertApiConfiguration. if (prevApiConfigName.current === currentApiConfigName) { return } - setCachedState((prevCachedState) => ({ - ...prevCachedState, - ...extensionState, - })) + + setCachedState((prevCachedState) => ({ ...prevCachedState, ...extensionState })) prevApiConfigName.current = currentApiConfigName + // console.log("useEffect: currentApiConfigName changed, setChangeDetected -> false") setChangeDetected(false) }, [currentApiConfigName, extensionState, isChangeDetected]) @@ -90,11 +89,10 @@ const SettingsView = forwardRef(({ onDone }, if (prevState[field] === value) { return prevState } + + // console.log(`setCachedStateField(${field} -> ${value}): setChangeDetected -> true`) setChangeDetected(true) - return { - ...prevState, - [field]: value, - } + return { ...prevState, [field]: value } }) }, [], @@ -107,15 +105,10 @@ const SettingsView = forwardRef(({ onDone }, return prevState } + // console.log(`setApiConfigurationField(${field} -> ${value}): setChangeDetected -> true`) setChangeDetected(true) - return { - ...prevState, - apiConfiguration: { - ...prevState.apiConfiguration, - [field]: value, - }, - } + return { ...prevState, apiConfiguration: { ...prevState.apiConfiguration, [field]: value } } }) }, [], @@ -126,14 +119,19 @@ const SettingsView = forwardRef(({ onDone }, if (prevState.experiments?.[id] === enabled) { return prevState } + + // console.log("setExperimentEnabled: setChangeDetected -> true") setChangeDetected(true) + return { ...prevState, experiments: { ...prevState.experiments, [id]: enabled }, } }) }, []) + const isSettingValid = !errorMessage + const handleSubmit = () => { if (isSettingValid) { vscode.postMessage({ type: "alwaysAllowReadOnly", bool: alwaysAllowReadOnly }) @@ -160,6 +158,7 @@ const SettingsView = forwardRef(({ onDone }, vscode.postMessage({ type: "updateExperimental", values: experiments }) vscode.postMessage({ type: "alwaysAllowModeSwitch", bool: alwaysAllowModeSwitch }) vscode.postMessage({ type: "upsertApiConfiguration", text: currentApiConfigName, apiConfiguration }) + // console.log("handleSubmit: setChangeDetected -> false") setChangeDetected(false) } } @@ -176,13 +175,7 @@ const SettingsView = forwardRef(({ onDone }, [isChangeDetected], ) - useImperativeHandle( - ref, - () => ({ - checkUnsaveChanges, - }), - [checkUnsaveChanges], - ) + useImperativeHandle(ref, () => ({ checkUnsaveChanges }), [checkUnsaveChanges]) const onConfirmDialogResult = useCallback((confirm: boolean) => { if (confirm) { @@ -200,10 +193,7 @@ const SettingsView = forwardRef(({ onDone }, const newCommands = [...currentCommands, commandInput] setCachedStateField("allowedCommands", newCommands) setCommandInput("") - vscode.postMessage({ - type: "allowedCommands", - commands: newCommands, - }) + vscode.postMessage({ type: "allowedCommands", commands: newCommands }) } } diff --git a/webview-ui/src/components/settings/ThinkingBudget.tsx b/webview-ui/src/components/settings/ThinkingBudget.tsx new file mode 100644 index 00000000000..efaa90dc39a --- /dev/null +++ b/webview-ui/src/components/settings/ThinkingBudget.tsx @@ -0,0 +1,29 @@ +import { Slider } from "@/components/ui" + +import { ApiConfiguration, ModelInfo, THINKING_BUDGET } from "../../../../src/shared/api" + +interface ThinkingBudgetProps { + apiConfiguration: ApiConfiguration + setApiConfigurationField: (field: K, value: ApiConfiguration[K]) => void + modelInfo?: ModelInfo +} + +export const ThinkingBudget = ({ apiConfiguration, setApiConfigurationField, modelInfo }: ThinkingBudgetProps) => { + const budget = apiConfiguration?.anthropicThinking ?? THINKING_BUDGET.default + + return modelInfo && modelInfo.thinking ? ( +
+
Thinking Budget
+
+ setApiConfigurationField("anthropicThinking", value[0])} + /> +
{budget}
+
+
+ ) : null +} diff --git a/webview-ui/src/components/ui/alert-dialog.tsx b/webview-ui/src/components/ui/alert-dialog.tsx index 7530cae54d6..82a25bf8f70 100644 --- a/webview-ui/src/components/ui/alert-dialog.tsx +++ b/webview-ui/src/components/ui/alert-dialog.tsx @@ -4,94 +4,97 @@ import * as AlertDialogPrimitive from "@radix-ui/react-alert-dialog" import { cn } from "@/lib/utils" import { buttonVariants } from "@/components/ui/button" -const AlertDialog = AlertDialogPrimitive.Root - -const AlertDialogTrigger = AlertDialogPrimitive.Trigger +function AlertDialog({ ...props }: React.ComponentProps) { + return +} -const AlertDialogPortal = AlertDialogPrimitive.Portal +function AlertDialogTrigger({ ...props }: React.ComponentProps) { + return +} -const AlertDialogOverlay = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef ->(({ className, ...props }, ref) => ( - -)) -AlertDialogOverlay.displayName = AlertDialogPrimitive.Overlay.displayName +function AlertDialogPortal({ ...props }: React.ComponentProps) { + return +} -const AlertDialogContent = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef ->(({ className, ...props }, ref) => ( - - - ) { + return ( + - -)) -AlertDialogContent.displayName = AlertDialogPrimitive.Content.displayName + ) +} -const AlertDialogHeader = ({ className, ...props }: React.HTMLAttributes) => ( -
-) -AlertDialogHeader.displayName = "AlertDialogHeader" +function AlertDialogContent({ className, ...props }: React.ComponentProps) { + return ( + + + + + ) +} -const AlertDialogFooter = ({ className, ...props }: React.HTMLAttributes) => ( -
-) -AlertDialogFooter.displayName = "AlertDialogFooter" +function AlertDialogHeader({ className, ...props }: React.ComponentProps<"div">) { + return ( +
+ ) +} -const AlertDialogTitle = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef ->(({ className, ...props }, ref) => ( - -)) -AlertDialogTitle.displayName = AlertDialogPrimitive.Title.displayName +function AlertDialogFooter({ className, ...props }: React.ComponentProps<"div">) { + return ( +
+ ) +} + +function AlertDialogTitle({ className, ...props }: React.ComponentProps) { + return ( + + ) +} -const AlertDialogDescription = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef ->(({ className, ...props }, ref) => ( - -)) -AlertDialogDescription.displayName = AlertDialogPrimitive.Description.displayName +function AlertDialogDescription({ + className, + ...props +}: React.ComponentProps) { + return ( + + ) +} -const AlertDialogAction = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef ->(({ className, ...props }, ref) => ( - -)) -AlertDialogAction.displayName = AlertDialogPrimitive.Action.displayName +function AlertDialogAction({ className, ...props }: React.ComponentProps) { + return +} -const AlertDialogCancel = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef ->(({ className, ...props }, ref) => ( - -)) -AlertDialogCancel.displayName = AlertDialogPrimitive.Cancel.displayName +function AlertDialogCancel({ className, ...props }: React.ComponentProps) { + return +} export { AlertDialog, diff --git a/webview-ui/src/components/ui/dialog.tsx b/webview-ui/src/components/ui/dialog.tsx index 11d5e2d3b0c..ed3160f692a 100644 --- a/webview-ui/src/components/ui/dialog.tsx +++ b/webview-ui/src/components/ui/dialog.tsx @@ -1,96 +1,108 @@ -"use client" - import * as React from "react" import * as DialogPrimitive from "@radix-ui/react-dialog" -import { Cross2Icon } from "@radix-ui/react-icons" +import { XIcon } from "lucide-react" import { cn } from "@/lib/utils" -const Dialog = DialogPrimitive.Root - -const DialogTrigger = DialogPrimitive.Trigger +function Dialog({ ...props }: React.ComponentProps) { + return +} -const DialogPortal = DialogPrimitive.Portal +function DialogTrigger({ ...props }: React.ComponentProps) { + return +} -const DialogClose = DialogPrimitive.Close +function DialogPortal({ ...props }: React.ComponentProps) { + return +} -const DialogOverlay = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef ->(({ className, ...props }, ref) => ( - -)) -DialogOverlay.displayName = DialogPrimitive.Overlay.displayName +function DialogClose({ ...props }: React.ComponentProps) { + return +} -const DialogContent = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef ->(({ className, children, ...props }, ref) => ( - - - ) { + return ( + - {children} - - - Close - - - -)) -DialogContent.displayName = DialogPrimitive.Content.displayName + {...props} + /> + ) +} -const DialogHeader = ({ className, ...props }: React.HTMLAttributes) => ( -
-) -DialogHeader.displayName = "DialogHeader" +function DialogContent({ className, children, ...props }: React.ComponentProps) { + return ( + + + + {children} + + + Close + + + + ) +} -const DialogFooter = ({ className, ...props }: React.HTMLAttributes) => ( -
-) -DialogFooter.displayName = "DialogFooter" +function DialogHeader({ className, ...props }: React.ComponentProps<"div">) { + return ( +
+ ) +} -const DialogTitle = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef ->(({ className, ...props }, ref) => ( - -)) -DialogTitle.displayName = DialogPrimitive.Title.displayName +function DialogFooter({ className, ...props }: React.ComponentProps<"div">) { + return ( +
+ ) +} -const DialogDescription = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef ->(({ className, ...props }, ref) => ( - -)) -DialogDescription.displayName = DialogPrimitive.Description.displayName +function DialogTitle({ className, ...props }: React.ComponentProps) { + return ( + + ) +} + +function DialogDescription({ className, ...props }: React.ComponentProps) { + return ( + + ) +} export { Dialog, - DialogPortal, - DialogOverlay, - DialogTrigger, DialogClose, DialogContent, - DialogHeader, + DialogDescription, DialogFooter, + DialogHeader, + DialogOverlay, + DialogPortal, DialogTitle, - DialogDescription, + DialogTrigger, } diff --git a/webview-ui/src/components/welcome/WelcomeView.tsx b/webview-ui/src/components/welcome/WelcomeView.tsx index 5d880efc0b9..ae674c895f4 100644 --- a/webview-ui/src/components/welcome/WelcomeView.tsx +++ b/webview-ui/src/components/welcome/WelcomeView.tsx @@ -12,16 +12,14 @@ const WelcomeView = () => { const handleSubmit = useCallback(() => { const error = validateApiConfiguration(apiConfiguration) + if (error) { setErrorMessage(error) return } + setErrorMessage(undefined) - vscode.postMessage({ - type: "upsertApiConfiguration", - text: currentApiConfigName, - apiConfiguration, - }) + vscode.postMessage({ type: "upsertApiConfiguration", text: currentApiConfigName, apiConfiguration }) }, [apiConfiguration, currentApiConfigName]) return ( diff --git a/webview-ui/src/utils/validate.ts b/webview-ui/src/utils/validate.ts index 97c702637c4..82af23ab497 100644 --- a/webview-ui/src/utils/validate.ts +++ b/webview-ui/src/utils/validate.ts @@ -1,74 +1,83 @@ -import { ApiConfiguration } from "../../../src/shared/api" -import { ModelInfo } from "../../../src/shared/api" +import { ApiConfiguration, ModelInfo } from "../../../src/shared/api" + export function validateApiConfiguration(apiConfiguration?: ApiConfiguration): string | undefined { - if (apiConfiguration) { - switch (apiConfiguration.apiProvider) { - case "anthropic": - if (!apiConfiguration.apiKey) { - return "You must provide a valid API key or choose a different provider." - } - break - case "glama": - if (!apiConfiguration.glamaApiKey) { - return "You must provide a valid API key or choose a different provider." - } - break - case "bedrock": - if (!apiConfiguration.awsRegion) { - return "You must choose a region to use with AWS Bedrock." - } - break - case "openrouter": - if (!apiConfiguration.openRouterApiKey) { - return "You must provide a valid API key or choose a different provider." - } - break - case "vertex": - if (!apiConfiguration.vertexProjectId || !apiConfiguration.vertexRegion) { - return "You must provide a valid Google Cloud Project ID and Region." - } - break - case "gemini": - if (!apiConfiguration.geminiApiKey) { - return "You must provide a valid API key or choose a different provider." - } - break - case "openai-native": - if (!apiConfiguration.openAiNativeApiKey) { - return "You must provide a valid API key or choose a different provider." - } - break - case "mistral": - if (!apiConfiguration.mistralApiKey) { - return "You must provide a valid API key or choose a different provider." - } - break - case "openai": - if ( - !apiConfiguration.openAiBaseUrl || - !apiConfiguration.openAiApiKey || - !apiConfiguration.openAiModelId - ) { - return "You must provide a valid base URL, API key, and model ID." - } - break - case "ollama": - if (!apiConfiguration.ollamaModelId) { - return "You must provide a valid model ID." - } - break - case "lmstudio": - if (!apiConfiguration.lmStudioModelId) { - return "You must provide a valid model ID." - } - break - case "vscode-lm": - if (!apiConfiguration.vsCodeLmModelSelector) { - return "You must provide a valid model selector." - } - break - } + if (!apiConfiguration) { + return undefined + } + + switch (apiConfiguration.apiProvider) { + case "openrouter": + if (!apiConfiguration.openRouterApiKey) { + return "You must provide a valid API key." + } + break + case "glama": + if (!apiConfiguration.glamaApiKey) { + return "You must provide a valid API key." + } + break + case "unbound": + if (!apiConfiguration.unboundApiKey) { + return "You must provide a valid API key." + } + break + case "requesty": + if (!apiConfiguration.requestyApiKey) { + return "You must provide a valid API key." + } + break + case "anthropic": + if (!apiConfiguration.apiKey) { + return "You must provide a valid API key." + } + break + case "bedrock": + if (!apiConfiguration.awsRegion) { + return "You must choose a region to use with AWS Bedrock." + } + break + case "vertex": + if (!apiConfiguration.vertexProjectId || !apiConfiguration.vertexRegion) { + return "You must provide a valid Google Cloud Project ID and Region." + } + break + case "gemini": + if (!apiConfiguration.geminiApiKey) { + return "You must provide a valid API key." + } + break + case "openai-native": + if (!apiConfiguration.openAiNativeApiKey) { + return "You must provide a valid API key." + } + break + case "mistral": + if (!apiConfiguration.mistralApiKey) { + return "You must provide a valid API key." + } + break + case "openai": + if (!apiConfiguration.openAiBaseUrl || !apiConfiguration.openAiApiKey || !apiConfiguration.openAiModelId) { + return "You must provide a valid base URL, API key, and model ID." + } + break + case "ollama": + if (!apiConfiguration.ollamaModelId) { + return "You must provide a valid model ID." + } + break + case "lmstudio": + if (!apiConfiguration.lmStudioModelId) { + return "You must provide a valid model ID." + } + break + case "vscode-lm": + if (!apiConfiguration.vsCodeLmModelSelector) { + return "You must provide a valid model selector." + } + break } + return undefined } @@ -77,40 +86,81 @@ export function validateModelId( glamaModels?: Record, openRouterModels?: Record, unboundModels?: Record, + requestyModels?: Record, ): string | undefined { - if (apiConfiguration) { - switch (apiConfiguration.apiProvider) { - case "glama": - const glamaModelId = apiConfiguration.glamaModelId - if (!glamaModelId) { - return "You must provide a model ID." - } - if (glamaModels && !Object.keys(glamaModels).includes(glamaModelId)) { - // even if the model list endpoint failed, extensionstatecontext will always have the default model info - return "The model ID you provided is not available. Please choose a different model." - } - break - case "openrouter": - const modelId = apiConfiguration.openRouterModelId - if (!modelId) { - return "You must provide a model ID." - } - if (openRouterModels && !Object.keys(openRouterModels).includes(modelId)) { - // even if the model list endpoint failed, extensionstatecontext will always have the default model info - return "The model ID you provided is not available. Please choose a different model." - } - break - case "unbound": - const unboundModelId = apiConfiguration.unboundModelId - if (!unboundModelId) { - return "You must provide a model ID." - } - if (unboundModels && !Object.keys(unboundModels).includes(unboundModelId)) { - // even if the model list endpoint failed, extensionstatecontext will always have the default model info - return "The model ID you provided is not available. Please choose a different model." - } - break - } + if (!apiConfiguration) { + return undefined + } + + switch (apiConfiguration.apiProvider) { + case "openrouter": + const modelId = apiConfiguration.openRouterModelId + + if (!modelId) { + return "You must provide a model ID." + } + + if ( + openRouterModels && + Object.keys(openRouterModels).length > 1 && + !Object.keys(openRouterModels).includes(modelId) + ) { + return `The model ID (${modelId}) you provided is not available. Please choose a different model.` + } + + break + + case "glama": + const glamaModelId = apiConfiguration.glamaModelId + + if (!glamaModelId) { + return "You must provide a model ID." + } + + if ( + glamaModels && + Object.keys(glamaModels).length > 1 && + !Object.keys(glamaModels).includes(glamaModelId) + ) { + return `The model ID (${glamaModelId}) you provided is not available. Please choose a different model.` + } + + break + + case "unbound": + const unboundModelId = apiConfiguration.unboundModelId + + if (!unboundModelId) { + return "You must provide a model ID." + } + + if ( + unboundModels && + Object.keys(unboundModels).length > 1 && + !Object.keys(unboundModels).includes(unboundModelId) + ) { + return `The model ID (${unboundModelId}) you provided is not available. Please choose a different model.` + } + + break + + case "requesty": + const requestyModelId = apiConfiguration.requestyModelId + + if (!requestyModelId) { + return "You must provide a model ID." + } + + if ( + requestyModels && + Object.keys(requestyModels).length > 1 && + !Object.keys(requestyModels).includes(requestyModelId) + ) { + return `The model ID (${requestyModelId}) you provided is not available. Please choose a different model.` + } + + break } + return undefined } From 44724e5881ab8daefa7b80bb6c52e5f1bb8a828e Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Wed, 26 Feb 2025 07:06:25 +0000 Subject: [PATCH 116/585] changeset version bump --- .changeset/cold-poems-change.md | 5 ----- .changeset/real-ties-destroy.md | 5 ----- .changeset/shaggy-spies-kneel.md | 5 ----- .changeset/swift-kings-attack.md | 5 ----- CHANGELOG.md | 9 +++++++++ package-lock.json | 4 ++-- package.json | 2 +- 7 files changed, 12 insertions(+), 23 deletions(-) delete mode 100644 .changeset/cold-poems-change.md delete mode 100644 .changeset/real-ties-destroy.md delete mode 100644 .changeset/shaggy-spies-kneel.md delete mode 100644 .changeset/swift-kings-attack.md diff --git a/.changeset/cold-poems-change.md b/.changeset/cold-poems-change.md deleted file mode 100644 index 41693ccdfc7..00000000000 --- a/.changeset/cold-poems-change.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"roo-cline": patch ---- - -v3.7.5 diff --git a/.changeset/real-ties-destroy.md b/.changeset/real-ties-destroy.md deleted file mode 100644 index a2e9ba8eb04..00000000000 --- a/.changeset/real-ties-destroy.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"roo-cline": patch ---- - -Fix model picker diff --git a/.changeset/shaggy-spies-kneel.md b/.changeset/shaggy-spies-kneel.md deleted file mode 100644 index d137cf85efc..00000000000 --- a/.changeset/shaggy-spies-kneel.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"roo-cline": patch ---- - -Add drag-and-drop for files diff --git a/.changeset/swift-kings-attack.md b/.changeset/swift-kings-attack.md deleted file mode 100644 index 8a8a425611d..00000000000 --- a/.changeset/swift-kings-attack.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"roo-cline": patch ---- - -Pass "thinking" params to OpenRouter diff --git a/CHANGELOG.md b/CHANGELOG.md index 52fb7540976..7a9b4b57bf0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,14 @@ # Roo Code Changelog +## 3.7.5 + +### Patch Changes + +- v3.7.5 +- Fix model picker +- Add drag-and-drop for files +- Pass "thinking" params to OpenRouter + ## [3.7.4] - Fix a bug that prevented the "Thinking" setting from properly updating when switching profiles. diff --git a/package-lock.json b/package-lock.json index 4bcdf8136df..a6c75bd69b3 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "roo-cline", - "version": "3.7.4", + "version": "3.7.5", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "roo-cline", - "version": "3.7.4", + "version": "3.7.5", "dependencies": { "@anthropic-ai/bedrock-sdk": "^0.10.2", "@anthropic-ai/sdk": "^0.37.0", diff --git a/package.json b/package.json index 28045436e6b..40bb6a545dd 100644 --- a/package.json +++ b/package.json @@ -3,7 +3,7 @@ "displayName": "Roo Code (prev. Roo Cline)", "description": "A whole dev team of AI agents in your editor.", "publisher": "RooVeterinaryInc", - "version": "3.7.4", + "version": "3.7.5", "icon": "assets/icons/rocket.png", "galleryBanner": { "color": "#617A91", From 319f16eb0aabb8033ef94d006b983bdbb45a1eec Mon Sep 17 00:00:00 2001 From: cte Date: Tue, 25 Feb 2025 23:11:42 -0800 Subject: [PATCH 117/585] Update CHANGELOG --- CHANGELOG.md | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7a9b4b57bf0..ee107be67a4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,13 +1,11 @@ # Roo Code Changelog -## 3.7.5 +## [3.7.5] -### Patch Changes - -- v3.7.5 -- Fix model picker +- Fix context window truncation math (see [#1173](https://github.com/RooVetGit/Roo-Code/issues/1173)) +- Fix various issues with the model picker - Add drag-and-drop for files -- Pass "thinking" params to OpenRouter +- Enable the "Thinking Budget" slider for Claude 3.7 Sonnet on OpenRouter ## [3.7.4] From da1b31765ed05cab6c1d0e25cbae6748aa7ce89a Mon Sep 17 00:00:00 2001 From: cte Date: Tue, 25 Feb 2025 23:13:52 -0800 Subject: [PATCH 118/585] Update CHANGELOG --- CHANGELOG.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ee107be67a4..02a4a30cbd2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,7 +3,8 @@ ## [3.7.5] - Fix context window truncation math (see [#1173](https://github.com/RooVetGit/Roo-Code/issues/1173)) -- Fix various issues with the model picker +- Fix various issues with the model picker (thanks @System233!) +- Fix model input / output cost parsing (thanks @System233!) - Add drag-and-drop for files - Enable the "Thinking Budget" slider for Claude 3.7 Sonnet on OpenRouter From 36cfa31d20273880a0e1b84cca85d625d917cce7 Mon Sep 17 00:00:00 2001 From: ShayBC Date: Wed, 26 Feb 2025 12:28:01 +0200 Subject: [PATCH 119/585] try to fix test --- src/core/webview/__tests__/ClineProvider.test.ts | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/core/webview/__tests__/ClineProvider.test.ts b/src/core/webview/__tests__/ClineProvider.test.ts index e8b49cc7fc2..fe45aaa801c 100644 --- a/src/core/webview/__tests__/ClineProvider.test.ts +++ b/src/core/webview/__tests__/ClineProvider.test.ts @@ -860,7 +860,8 @@ describe("ClineProvider", () => { abortTask: jest.fn(), handleWebviewAskResponse: jest.fn(), } as unknown as Cline - provider.addClineToStack(mockCline) + const newMockCline = Object.create(mockCline) + provider.addClineToStack(newMockCline) // Mock getTaskWithId ;(provider as any).getTaskWithId = jest.fn().mockResolvedValue({ @@ -872,7 +873,7 @@ describe("ClineProvider", () => { await messageHandler({ type: "deleteMessage", value: 4000 }) // Verify correct messages were kept - expect(mockCline.overwriteClineMessages).toHaveBeenCalledWith([ + expect(newMockCline.overwriteClineMessages).toHaveBeenCalledWith([ mockMessages[0], mockMessages[1], mockMessages[4], @@ -880,7 +881,7 @@ describe("ClineProvider", () => { ]) // Verify correct API messages were kept - expect(mockCline.overwriteApiConversationHistory).toHaveBeenCalledWith([ + expect(newMockCline.overwriteApiConversationHistory).toHaveBeenCalledWith([ mockApiHistory[0], mockApiHistory[1], mockApiHistory[4], From 78d5af491aed08505afcfb80bd02a96cb0d287ae Mon Sep 17 00:00:00 2001 From: Joe Manley Date: Wed, 26 Feb 2025 09:32:13 -0800 Subject: [PATCH 120/585] Fix long strings correctly in ChatRow --- webview-ui/src/components/chat/ChatRow.tsx | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/webview-ui/src/components/chat/ChatRow.tsx b/webview-ui/src/components/chat/ChatRow.tsx index b139c68f963..4017ccf318e 100644 --- a/webview-ui/src/components/chat/ChatRow.tsx +++ b/webview-ui/src/components/chat/ChatRow.tsx @@ -617,8 +617,10 @@ export const ChatRowContent = ({ color: "var(--vscode-badge-foreground)", borderRadius: "3px", padding: "9px", - whiteSpace: "pre-line", - wordWrap: "break-word", + overflow: "hidden", + whiteSpace: "pre-wrap", + wordBreak: "break-word", + overflowWrap: "anywhere", }}>
Date: Wed, 26 Feb 2025 09:41:57 -0800 Subject: [PATCH 121/585] Add changeset --- .changeset/fluffy-apples-attack.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/fluffy-apples-attack.md diff --git a/.changeset/fluffy-apples-attack.md b/.changeset/fluffy-apples-attack.md new file mode 100644 index 00000000000..924a1b25057 --- /dev/null +++ b/.changeset/fluffy-apples-attack.md @@ -0,0 +1,5 @@ +--- +"roo-cline": patch +--- + +Handle really long text in the ChatRow similar to TaskHeader From d7266be3feb018c5bf207135cb54f74c94d198e5 Mon Sep 17 00:00:00 2001 From: Matt Rubens Date: Wed, 26 Feb 2025 13:52:38 -0500 Subject: [PATCH 122/585] Better OpenRouter error handling --- .changeset/tender-cycles-help.md | 5 +++++ src/core/Cline.ts | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) create mode 100644 .changeset/tender-cycles-help.md diff --git a/.changeset/tender-cycles-help.md b/.changeset/tender-cycles-help.md new file mode 100644 index 00000000000..d43e423ee61 --- /dev/null +++ b/.changeset/tender-cycles-help.md @@ -0,0 +1,5 @@ +--- +"roo-cline": patch +--- + +Better OpenRouter error handling diff --git a/src/core/Cline.ts b/src/core/Cline.ts index 073bd109117..2e29ad453cc 100644 --- a/src/core/Cline.ts +++ b/src/core/Cline.ts @@ -1010,7 +1010,7 @@ export class Cline { } catch (error) { // note that this api_req_failed ask is unique in that we only present this option if the api hasn't streamed any content yet (ie it fails on the first chunk due), as it would allow them to hit a retry button. However if the api failed mid-stream, it could be in any arbitrary state where some tools may have executed, so that error is handled differently and requires cancelling the task entirely. if (alwaysApproveResubmit) { - const errorMsg = error.message ?? "Unknown error" + const errorMsg = error.error?.metadata?.raw ?? error.message ?? "Unknown error" const baseDelay = requestDelaySeconds || 5 const exponentialDelay = Math.ceil(baseDelay * Math.pow(2, retryAttempt)) // Wait for the greater of the exponential delay or the rate limit delay From a4e58700ce7527ed486a96df6cce9709fb145074 Mon Sep 17 00:00:00 2001 From: Matt Rubens Date: Wed, 26 Feb 2025 16:04:32 -0500 Subject: [PATCH 123/585] Support multiple files in drag-and-drop --- .changeset/orange-zoos-train.md | 5 + .../src/components/chat/ChatTextArea.tsx | 37 ++- .../chat/__tests__/ChatTextArea.test.tsx | 238 ++++++++++++++++++ 3 files changed, 272 insertions(+), 8 deletions(-) create mode 100644 .changeset/orange-zoos-train.md diff --git a/.changeset/orange-zoos-train.md b/.changeset/orange-zoos-train.md new file mode 100644 index 00000000000..76c16f45671 --- /dev/null +++ b/.changeset/orange-zoos-train.md @@ -0,0 +1,5 @@ +--- +"roo-cline": patch +--- + +Support multiple files in drag-and-drop diff --git a/webview-ui/src/components/chat/ChatTextArea.tsx b/webview-ui/src/components/chat/ChatTextArea.tsx index dc78a3fdb3d..be2b2a97984 100644 --- a/webview-ui/src/components/chat/ChatTextArea.tsx +++ b/webview-ui/src/components/chat/ChatTextArea.tsx @@ -590,15 +590,36 @@ const ChatTextArea = forwardRef( const files = Array.from(e.dataTransfer.files) const text = e.dataTransfer.getData("text") if (text) { - // Convert the path to a mention-friendly format - const mentionText = convertToMentionPath(text, cwd) + // Split text on newlines to handle multiple files + const lines = text.split(/\r?\n/).filter((line) => line.trim() !== "") + + if (lines.length > 0) { + // Process each line as a separate file path + let newValue = inputValue.slice(0, cursorPosition) + let totalLength = 0 + + lines.forEach((line, index) => { + // Convert each path to a mention-friendly format + const mentionText = convertToMentionPath(line, cwd) + newValue += mentionText + totalLength += mentionText.length + + // Add space after each mention except the last one + if (index < lines.length - 1) { + newValue += " " + totalLength += 1 + } + }) - const newValue = - inputValue.slice(0, cursorPosition) + mentionText + " " + inputValue.slice(cursorPosition) - setInputValue(newValue) - const newCursorPosition = cursorPosition + mentionText.length + 1 - setCursorPosition(newCursorPosition) - setIntendedCursorPosition(newCursorPosition) + // Add space after the last mention and append the rest of the input + newValue += " " + inputValue.slice(cursorPosition) + totalLength += 1 + + setInputValue(newValue) + const newCursorPosition = cursorPosition + totalLength + setCursorPosition(newCursorPosition) + setIntendedCursorPosition(newCursorPosition) + } return } diff --git a/webview-ui/src/components/chat/__tests__/ChatTextArea.test.tsx b/webview-ui/src/components/chat/__tests__/ChatTextArea.test.tsx index 205912fc154..3241010e886 100644 --- a/webview-ui/src/components/chat/__tests__/ChatTextArea.test.tsx +++ b/webview-ui/src/components/chat/__tests__/ChatTextArea.test.tsx @@ -3,6 +3,7 @@ import ChatTextArea from "../ChatTextArea" import { useExtensionState } from "../../../context/ExtensionStateContext" import { vscode } from "../../../utils/vscode" import { defaultModeSlug } from "../../../../../src/shared/modes" +import * as pathMentions from "../../../utils/path-mentions" // Mock modules jest.mock("../../../utils/vscode", () => ({ @@ -12,9 +13,20 @@ jest.mock("../../../utils/vscode", () => ({ })) jest.mock("../../../components/common/CodeBlock") jest.mock("../../../components/common/MarkdownBlock") +jest.mock("../../../utils/path-mentions", () => ({ + convertToMentionPath: jest.fn((path, cwd) => { + // Simple mock implementation that mimics the real function's behavior + if (cwd && path.toLowerCase().startsWith(cwd.toLowerCase())) { + const relativePath = path.substring(cwd.length) + return "@" + (relativePath.startsWith("/") ? relativePath : "/" + relativePath) + } + return path + }), +})) // Get the mocked postMessage function const mockPostMessage = vscode.postMessage as jest.Mock +const mockConvertToMentionPath = pathMentions.convertToMentionPath as jest.Mock // Mock ExtensionStateContext jest.mock("../../../context/ExtensionStateContext") @@ -160,4 +172,230 @@ describe("ChatTextArea", () => { expect(setInputValue).toHaveBeenCalledWith("Enhanced test prompt") }) }) + + describe("multi-file drag and drop", () => { + const mockCwd = "/Users/test/project" + + beforeEach(() => { + jest.clearAllMocks() + ;(useExtensionState as jest.Mock).mockReturnValue({ + filePaths: [], + openedTabs: [], + cwd: mockCwd, + }) + mockConvertToMentionPath.mockClear() + }) + + it("should process multiple file paths separated by newlines", () => { + const setInputValue = jest.fn() + + const { container } = render( + , + ) + + // Create a mock dataTransfer object with text data containing multiple file paths + const dataTransfer = { + getData: jest.fn().mockReturnValue("/Users/test/project/file1.js\n/Users/test/project/file2.js"), + files: [], + } + + // Simulate drop event + fireEvent.drop(container.querySelector(".chat-text-area")!, { + dataTransfer, + preventDefault: jest.fn(), + }) + + // Verify convertToMentionPath was called for each file path + expect(mockConvertToMentionPath).toHaveBeenCalledTimes(2) + expect(mockConvertToMentionPath).toHaveBeenCalledWith("/Users/test/project/file1.js", mockCwd) + expect(mockConvertToMentionPath).toHaveBeenCalledWith("/Users/test/project/file2.js", mockCwd) + + // Verify setInputValue was called with the correct value + // The mock implementation of convertToMentionPath will convert the paths to @/file1.js and @/file2.js + expect(setInputValue).toHaveBeenCalledWith("@/file1.js @/file2.js Initial text") + }) + + it("should filter out empty lines in the dragged text", () => { + const setInputValue = jest.fn() + + const { container } = render( + , + ) + + // Create a mock dataTransfer object with text data containing empty lines + const dataTransfer = { + getData: jest.fn().mockReturnValue("/Users/test/project/file1.js\n\n/Users/test/project/file2.js\n\n"), + files: [], + } + + // Simulate drop event + fireEvent.drop(container.querySelector(".chat-text-area")!, { + dataTransfer, + preventDefault: jest.fn(), + }) + + // Verify convertToMentionPath was called only for non-empty lines + expect(mockConvertToMentionPath).toHaveBeenCalledTimes(2) + + // Verify setInputValue was called with the correct value + expect(setInputValue).toHaveBeenCalledWith("@/file1.js @/file2.js Initial text") + }) + + it("should correctly update cursor position after adding multiple mentions", () => { + const setInputValue = jest.fn() + const initialCursorPosition = 5 + + const { container } = render( + , + ) + + // Set the cursor position manually + const textArea = container.querySelector("textarea") + if (textArea) { + textArea.selectionStart = initialCursorPosition + textArea.selectionEnd = initialCursorPosition + } + + // Create a mock dataTransfer object with text data + const dataTransfer = { + getData: jest.fn().mockReturnValue("/Users/test/project/file1.js\n/Users/test/project/file2.js"), + files: [], + } + + // Simulate drop event + fireEvent.drop(container.querySelector(".chat-text-area")!, { + dataTransfer, + preventDefault: jest.fn(), + }) + + // The cursor position should be updated based on the implementation in the component + expect(setInputValue).toHaveBeenCalledWith("@/file1.js @/file2.js Hello world") + }) + + it("should handle very long file paths correctly", () => { + const setInputValue = jest.fn() + + const { container } = render() + + // Create a very long file path + const longPath = + "/Users/test/project/very/long/path/with/many/nested/directories/and/a/very/long/filename/with/extension.typescript" + + // Create a mock dataTransfer object with the long path + const dataTransfer = { + getData: jest.fn().mockReturnValue(longPath), + files: [], + } + + // Simulate drop event + fireEvent.drop(container.querySelector(".chat-text-area")!, { + dataTransfer, + preventDefault: jest.fn(), + }) + + // Verify convertToMentionPath was called with the long path + expect(mockConvertToMentionPath).toHaveBeenCalledWith(longPath, mockCwd) + + // The mock implementation will convert it to @/very/long/path/... + expect(setInputValue).toHaveBeenCalledWith( + "@/very/long/path/with/many/nested/directories/and/a/very/long/filename/with/extension.typescript ", + ) + }) + + it("should handle paths with special characters correctly", () => { + const setInputValue = jest.fn() + + const { container } = render() + + // Create paths with special characters + const specialPath1 = "/Users/test/project/file with spaces.js" + const specialPath2 = "/Users/test/project/file-with-dashes.js" + const specialPath3 = "/Users/test/project/file_with_underscores.js" + const specialPath4 = "/Users/test/project/file.with.dots.js" + + // Create a mock dataTransfer object with the special paths + const dataTransfer = { + getData: jest + .fn() + .mockReturnValue(`${specialPath1}\n${specialPath2}\n${specialPath3}\n${specialPath4}`), + files: [], + } + + // Simulate drop event + fireEvent.drop(container.querySelector(".chat-text-area")!, { + dataTransfer, + preventDefault: jest.fn(), + }) + + // Verify convertToMentionPath was called for each path + expect(mockConvertToMentionPath).toHaveBeenCalledTimes(4) + expect(mockConvertToMentionPath).toHaveBeenCalledWith(specialPath1, mockCwd) + expect(mockConvertToMentionPath).toHaveBeenCalledWith(specialPath2, mockCwd) + expect(mockConvertToMentionPath).toHaveBeenCalledWith(specialPath3, mockCwd) + expect(mockConvertToMentionPath).toHaveBeenCalledWith(specialPath4, mockCwd) + + // Verify setInputValue was called with the correct value + expect(setInputValue).toHaveBeenCalledWith( + "@/file with spaces.js @/file-with-dashes.js @/file_with_underscores.js @/file.with.dots.js ", + ) + }) + + it("should handle paths outside the current working directory", () => { + const setInputValue = jest.fn() + + const { container } = render() + + // Create paths outside the current working directory + const outsidePath = "/Users/other/project/file.js" + + // Mock the convertToMentionPath function to return the original path for paths outside cwd + mockConvertToMentionPath.mockImplementationOnce((path, cwd) => { + return path // Return original path for this test + }) + + // Create a mock dataTransfer object with the outside path + const dataTransfer = { + getData: jest.fn().mockReturnValue(outsidePath), + files: [], + } + + // Simulate drop event + fireEvent.drop(container.querySelector(".chat-text-area")!, { + dataTransfer, + preventDefault: jest.fn(), + }) + + // Verify convertToMentionPath was called with the outside path + expect(mockConvertToMentionPath).toHaveBeenCalledWith(outsidePath, mockCwd) + + // Verify setInputValue was called with the original path + expect(setInputValue).toHaveBeenCalledWith("/Users/other/project/file.js ") + }) + + it("should do nothing when dropped text is empty", () => { + const setInputValue = jest.fn() + + const { container } = render( + , + ) + + // Create a mock dataTransfer object with empty text + const dataTransfer = { + getData: jest.fn().mockReturnValue(""), + files: [], + } + + // Simulate drop event + fireEvent.drop(container.querySelector(".chat-text-area")!, { + dataTransfer, + preventDefault: jest.fn(), + }) + + // Verify convertToMentionPath was not called + expect(mockConvertToMentionPath).not.toHaveBeenCalled() + + // Verify setInputValue was not called + expect(setInputValue).not.toHaveBeenCalled() + }) + }) }) From 5e53d00ebcf0d2adf218a07452d0e15835bf3e64 Mon Sep 17 00:00:00 2001 From: Chris Estreich Date: Wed, 26 Feb 2025 14:23:11 -0800 Subject: [PATCH 124/585] Allow control over maxTokens for thinking models --- src/api/providers/anthropic.ts | 12 ++- src/api/providers/openrouter.ts | 13 ++- src/core/Cline.ts | 18 +++- .../__tests__/sliding-window.test.ts | 102 +++++++++++++++--- src/core/sliding-window/index.ts | 37 ++++--- src/core/webview/ClineProvider.ts | 5 + src/shared/api.ts | 11 +- src/shared/globalState.ts | 1 + .../components/settings/ThinkingBudget.tsx | 65 ++++++++--- 9 files changed, 198 insertions(+), 66 deletions(-) diff --git a/src/api/providers/anthropic.ts b/src/api/providers/anthropic.ts index ad58a1cf6b2..8c5a1795b1f 100644 --- a/src/api/providers/anthropic.ts +++ b/src/api/providers/anthropic.ts @@ -31,7 +31,7 @@ export class AnthropicHandler implements ApiHandler, SingleCompletionHandler { let stream: AnthropicStream const cacheControl: CacheControlEphemeral = { type: "ephemeral" } let { id: modelId, info: modelInfo } = this.getModel() - const maxTokens = modelInfo.maxTokens || 8192 + const maxTokens = this.options.modelMaxTokens || modelInfo.maxTokens || 8192 let temperature = this.options.modelTemperature ?? ANTHROPIC_DEFAULT_TEMPERATURE let thinking: BetaThinkingConfigParam | undefined = undefined @@ -41,7 +41,15 @@ export class AnthropicHandler implements ApiHandler, SingleCompletionHandler { // `claude-3-7-sonnet-20250219` model with a thinking budget. // We can handle this more elegantly in the future. modelId = "claude-3-7-sonnet-20250219" - const budgetTokens = this.options.anthropicThinking ?? Math.max(maxTokens * 0.8, 1024) + + // Clamp the thinking budget to be at most 80% of max tokens and at + // least 1024 tokens. + const maxBudgetTokens = Math.floor(maxTokens * 0.8) + const budgetTokens = Math.max( + Math.min(this.options.anthropicThinking ?? maxBudgetTokens, maxBudgetTokens), + 1024, + ) + thinking = { type: "enabled", budget_tokens: budgetTokens } temperature = 1.0 } diff --git a/src/api/providers/openrouter.ts b/src/api/providers/openrouter.ts index 0a9488e816f..69bcb0074c1 100644 --- a/src/api/providers/openrouter.ts +++ b/src/api/providers/openrouter.ts @@ -108,12 +108,19 @@ export class OpenRouterHandler implements ApiHandler, SingleCompletionHandler { topP = 0.95 } + const maxTokens = this.options.modelMaxTokens || modelInfo.maxTokens let temperature = this.options.modelTemperature ?? defaultTemperature let thinking: BetaThinkingConfigParam | undefined = undefined if (modelInfo.thinking) { - const maxTokens = modelInfo.maxTokens || 8192 - const budgetTokens = this.options.anthropicThinking ?? Math.max(maxTokens * 0.8, 1024) + // Clamp the thinking budget to be at most 80% of max tokens and at + // least 1024 tokens. + const maxBudgetTokens = Math.floor((maxTokens || 8192) * 0.8) + const budgetTokens = Math.max( + Math.min(this.options.anthropicThinking ?? maxBudgetTokens, maxBudgetTokens), + 1024, + ) + thinking = { type: "enabled", budget_tokens: budgetTokens } temperature = 1.0 } @@ -271,7 +278,7 @@ export async function getOpenRouterModels() { modelInfo.supportsPromptCache = true modelInfo.cacheWritesPrice = 3.75 modelInfo.cacheReadsPrice = 0.3 - modelInfo.maxTokens = 16384 + modelInfo.maxTokens = 64_000 break case rawModel.id.startsWith("anthropic/claude-3.5-sonnet-20240620"): modelInfo.supportsPromptCache = true diff --git a/src/core/Cline.ts b/src/core/Cline.ts index 073bd109117..fb123e0584b 100644 --- a/src/core/Cline.ts +++ b/src/core/Cline.ts @@ -87,6 +87,7 @@ export type ClineOptions = { export class Cline { readonly taskId: string + readonly apiConfiguration: ApiConfiguration api: ApiHandler private terminalManager: TerminalManager private urlContentFetcher: UrlContentFetcher @@ -148,6 +149,7 @@ export class Cline { } this.taskId = crypto.randomUUID() + this.apiConfiguration = apiConfiguration this.api = buildApiHandler(apiConfiguration) this.terminalManager = new TerminalManager() this.urlContentFetcher = new UrlContentFetcher(provider.context) @@ -961,13 +963,21 @@ export class Cline { cacheWrites = 0, cacheReads = 0, }: ClineApiReqInfo = JSON.parse(previousRequest) + const totalTokens = tokensIn + tokensOut + cacheWrites + cacheReads - const trimmedMessages = truncateConversationIfNeeded( - this.apiConversationHistory, + const modelInfo = this.api.getModel().info + const maxTokens = modelInfo.thinking + ? this.apiConfiguration.modelMaxTokens || modelInfo.maxTokens + : modelInfo.maxTokens + const contextWindow = modelInfo.contextWindow + + const trimmedMessages = truncateConversationIfNeeded({ + messages: this.apiConversationHistory, totalTokens, - this.api.getModel().info, - ) + maxTokens, + contextWindow, + }) if (trimmedMessages !== this.apiConversationHistory) { await this.overwriteApiConversationHistory(trimmedMessages) diff --git a/src/core/sliding-window/__tests__/sliding-window.test.ts b/src/core/sliding-window/__tests__/sliding-window.test.ts index 3dcf9e5fd25..cb897aa8cb0 100644 --- a/src/core/sliding-window/__tests__/sliding-window.test.ts +++ b/src/core/sliding-window/__tests__/sliding-window.test.ts @@ -119,11 +119,21 @@ describe("getMaxTokens", () => { // Max tokens = 100000 - 50000 = 50000 // Below max tokens - no truncation - const result1 = truncateConversationIfNeeded(messages, 49999, modelInfo) + const result1 = truncateConversationIfNeeded({ + messages, + totalTokens: 49999, + contextWindow: modelInfo.contextWindow, + maxTokens: modelInfo.maxTokens, + }) expect(result1).toEqual(messages) // Above max tokens - truncate - const result2 = truncateConversationIfNeeded(messages, 50001, modelInfo) + const result2 = truncateConversationIfNeeded({ + messages, + totalTokens: 50001, + contextWindow: modelInfo.contextWindow, + maxTokens: modelInfo.maxTokens, + }) expect(result2).not.toEqual(messages) expect(result2.length).toBe(3) // Truncated with 0.5 fraction }) @@ -133,11 +143,21 @@ describe("getMaxTokens", () => { // Max tokens = 100000 - (100000 * 0.2) = 80000 // Below max tokens - no truncation - const result1 = truncateConversationIfNeeded(messages, 79999, modelInfo) + const result1 = truncateConversationIfNeeded({ + messages, + totalTokens: 79999, + contextWindow: modelInfo.contextWindow, + maxTokens: modelInfo.maxTokens, + }) expect(result1).toEqual(messages) // Above max tokens - truncate - const result2 = truncateConversationIfNeeded(messages, 80001, modelInfo) + const result2 = truncateConversationIfNeeded({ + messages, + totalTokens: 80001, + contextWindow: modelInfo.contextWindow, + maxTokens: modelInfo.maxTokens, + }) expect(result2).not.toEqual(messages) expect(result2.length).toBe(3) // Truncated with 0.5 fraction }) @@ -147,11 +167,21 @@ describe("getMaxTokens", () => { // Max tokens = 50000 - 10000 = 40000 // Below max tokens - no truncation - const result1 = truncateConversationIfNeeded(messages, 39999, modelInfo) + const result1 = truncateConversationIfNeeded({ + messages, + totalTokens: 39999, + contextWindow: modelInfo.contextWindow, + maxTokens: modelInfo.maxTokens, + }) expect(result1).toEqual(messages) // Above max tokens - truncate - const result2 = truncateConversationIfNeeded(messages, 40001, modelInfo) + const result2 = truncateConversationIfNeeded({ + messages, + totalTokens: 40001, + contextWindow: modelInfo.contextWindow, + maxTokens: modelInfo.maxTokens, + }) expect(result2).not.toEqual(messages) expect(result2.length).toBe(3) // Truncated with 0.5 fraction }) @@ -161,11 +191,21 @@ describe("getMaxTokens", () => { // Max tokens = 200000 - 30000 = 170000 // Below max tokens - no truncation - const result1 = truncateConversationIfNeeded(messages, 169999, modelInfo) + const result1 = truncateConversationIfNeeded({ + messages, + totalTokens: 169999, + contextWindow: modelInfo.contextWindow, + maxTokens: modelInfo.maxTokens, + }) expect(result1).toEqual(messages) // Above max tokens - truncate - const result2 = truncateConversationIfNeeded(messages, 170001, modelInfo) + const result2 = truncateConversationIfNeeded({ + messages, + totalTokens: 170001, + contextWindow: modelInfo.contextWindow, + maxTokens: modelInfo.maxTokens, + }) expect(result2).not.toEqual(messages) expect(result2.length).toBe(3) // Truncated with 0.5 fraction }) @@ -194,7 +234,12 @@ describe("truncateConversationIfNeeded", () => { const maxTokens = 100000 - 30000 // 70000 const totalTokens = 69999 // Below threshold - const result = truncateConversationIfNeeded(messages, totalTokens, modelInfo) + const result = truncateConversationIfNeeded({ + messages, + totalTokens, + contextWindow: modelInfo.contextWindow, + maxTokens: modelInfo.maxTokens, + }) expect(result).toEqual(messages) // No truncation occurs }) @@ -207,7 +252,12 @@ describe("truncateConversationIfNeeded", () => { // With 4 messages after the first, 0.5 fraction means remove 2 messages const expectedResult = [messages[0], messages[3], messages[4]] - const result = truncateConversationIfNeeded(messages, totalTokens, modelInfo) + const result = truncateConversationIfNeeded({ + messages, + totalTokens, + contextWindow: modelInfo.contextWindow, + maxTokens: modelInfo.maxTokens, + }) expect(result).toEqual(expectedResult) }) @@ -218,14 +268,38 @@ describe("truncateConversationIfNeeded", () => { // Test below threshold const belowThreshold = 69999 - expect(truncateConversationIfNeeded(messages, belowThreshold, modelInfo1)).toEqual( - truncateConversationIfNeeded(messages, belowThreshold, modelInfo2), + expect( + truncateConversationIfNeeded({ + messages, + totalTokens: belowThreshold, + contextWindow: modelInfo1.contextWindow, + maxTokens: modelInfo1.maxTokens, + }), + ).toEqual( + truncateConversationIfNeeded({ + messages, + totalTokens: belowThreshold, + contextWindow: modelInfo2.contextWindow, + maxTokens: modelInfo2.maxTokens, + }), ) // Test above threshold const aboveThreshold = 70001 - expect(truncateConversationIfNeeded(messages, aboveThreshold, modelInfo1)).toEqual( - truncateConversationIfNeeded(messages, aboveThreshold, modelInfo2), + expect( + truncateConversationIfNeeded({ + messages, + totalTokens: aboveThreshold, + contextWindow: modelInfo1.contextWindow, + maxTokens: modelInfo1.maxTokens, + }), + ).toEqual( + truncateConversationIfNeeded({ + messages, + totalTokens: aboveThreshold, + contextWindow: modelInfo2.contextWindow, + maxTokens: modelInfo2.maxTokens, + }), ) }) }) diff --git a/src/core/sliding-window/index.ts b/src/core/sliding-window/index.ts index a0fff05ea55..8b646f933b9 100644 --- a/src/core/sliding-window/index.ts +++ b/src/core/sliding-window/index.ts @@ -1,7 +1,5 @@ import { Anthropic } from "@anthropic-ai/sdk" -import { ModelInfo } from "../../shared/api" - /** * Truncates a conversation by removing a fraction of the messages. * @@ -26,28 +24,29 @@ export function truncateConversation( } /** - * Conditionally truncates the conversation messages if the total token count exceeds the model's limit. + * Conditionally truncates the conversation messages if the total token count + * exceeds the model's limit. * * @param {Anthropic.Messages.MessageParam[]} messages - The conversation messages. * @param {number} totalTokens - The total number of tokens in the conversation. - * @param {ModelInfo} modelInfo - Model metadata including context window size. + * @param {number} contextWindow - The context window size. + * @param {number} maxTokens - The maximum number of tokens allowed. * @returns {Anthropic.Messages.MessageParam[]} The original or truncated conversation messages. */ -export function truncateConversationIfNeeded( - messages: Anthropic.Messages.MessageParam[], - totalTokens: number, - modelInfo: ModelInfo, -): Anthropic.Messages.MessageParam[] { - return totalTokens < getMaxTokens(modelInfo) ? messages : truncateConversation(messages, 0.5) + +type TruncateOptions = { + messages: Anthropic.Messages.MessageParam[] + totalTokens: number + contextWindow: number + maxTokens?: number } -/** - * Calculates the maximum allowed tokens - * - * @param {ModelInfo} modelInfo - The model information containing the context window size. - * @returns {number} The maximum number of tokens allowed - */ -function getMaxTokens(modelInfo: ModelInfo): number { - // The buffer needs to be at least as large as `modelInfo.maxTokens`, or 20% of the context window if for some reason it's not set. - return modelInfo.contextWindow - (modelInfo.maxTokens || modelInfo.contextWindow * 0.2) +export function truncateConversationIfNeeded({ + messages, + totalTokens, + contextWindow, + maxTokens, +}: TruncateOptions): Anthropic.Messages.MessageParam[] { + const allowedTokens = contextWindow - (maxTokens || contextWindow * 0.2) + return totalTokens < allowedTokens ? messages : truncateConversation(messages, 0.5) } diff --git a/src/core/webview/ClineProvider.ts b/src/core/webview/ClineProvider.ts index bc6f4578683..5e6170e2eee 100644 --- a/src/core/webview/ClineProvider.ts +++ b/src/core/webview/ClineProvider.ts @@ -1671,6 +1671,7 @@ export class ClineProvider implements vscode.WebviewViewProvider { requestyModelId, requestyModelInfo, modelTemperature, + modelMaxTokens, } = apiConfiguration await Promise.all([ this.updateGlobalState("apiProvider", apiProvider), @@ -1719,6 +1720,7 @@ export class ClineProvider implements vscode.WebviewViewProvider { this.updateGlobalState("requestyModelId", requestyModelId), this.updateGlobalState("requestyModelInfo", requestyModelInfo), this.updateGlobalState("modelTemperature", modelTemperature), + this.updateGlobalState("modelMaxTokens", modelMaxTokens), ]) if (this.cline) { this.cline.api = buildApiHandler(apiConfiguration) @@ -2210,6 +2212,7 @@ export class ClineProvider implements vscode.WebviewViewProvider { requestyModelId, requestyModelInfo, modelTemperature, + modelMaxTokens, maxOpenTabsContext, ] = await Promise.all([ this.getGlobalState("apiProvider") as Promise, @@ -2293,6 +2296,7 @@ export class ClineProvider implements vscode.WebviewViewProvider { this.getGlobalState("requestyModelId") as Promise, this.getGlobalState("requestyModelInfo") as Promise, this.getGlobalState("modelTemperature") as Promise, + this.getGlobalState("modelMaxTokens") as Promise, this.getGlobalState("maxOpenTabsContext") as Promise, ]) @@ -2358,6 +2362,7 @@ export class ClineProvider implements vscode.WebviewViewProvider { requestyModelId, requestyModelInfo, modelTemperature, + modelMaxTokens, }, lastShownAnnouncementId, customInstructions, diff --git a/src/shared/api.ts b/src/shared/api.ts index 5d4b8b120d7..e7e4c54db6a 100644 --- a/src/shared/api.ts +++ b/src/shared/api.ts @@ -68,6 +68,7 @@ export interface ApiHandlerOptions { requestyModelId?: string requestyModelInfo?: ModelInfo modelTemperature?: number + modelMaxTokens?: number } export type ApiConfiguration = ApiHandlerOptions & { @@ -92,19 +93,13 @@ export interface ModelInfo { thinking?: boolean } -export const THINKING_BUDGET = { - step: 1024, - min: 1024, - default: 8 * 1024, -} - // Anthropic // https://docs.anthropic.com/en/docs/about-claude/models export type AnthropicModelId = keyof typeof anthropicModels export const anthropicDefaultModelId: AnthropicModelId = "claude-3-7-sonnet-20250219" export const anthropicModels = { "claude-3-7-sonnet-20250219:thinking": { - maxTokens: 16384, + maxTokens: 64_000, contextWindow: 200_000, supportsImages: true, supportsComputerUse: true, @@ -116,7 +111,7 @@ export const anthropicModels = { thinking: true, }, "claude-3-7-sonnet-20250219": { - maxTokens: 16384, + maxTokens: 64_000, contextWindow: 200_000, supportsImages: true, supportsComputerUse: true, diff --git a/src/shared/globalState.ts b/src/shared/globalState.ts index 7b6b4f8274b..2cc90456a76 100644 --- a/src/shared/globalState.ts +++ b/src/shared/globalState.ts @@ -81,5 +81,6 @@ export type GlobalStateKey = | "requestyModelInfo" | "unboundModelInfo" | "modelTemperature" + | "modelMaxTokens" | "mistralCodestralUrl" | "maxOpenTabsContext" diff --git a/webview-ui/src/components/settings/ThinkingBudget.tsx b/webview-ui/src/components/settings/ThinkingBudget.tsx index efaa90dc39a..5b678744106 100644 --- a/webview-ui/src/components/settings/ThinkingBudget.tsx +++ b/webview-ui/src/components/settings/ThinkingBudget.tsx @@ -1,6 +1,8 @@ +import { useEffect } from "react" + import { Slider } from "@/components/ui" -import { ApiConfiguration, ModelInfo, THINKING_BUDGET } from "../../../../src/shared/api" +import { ApiConfiguration, ModelInfo } from "../../../../src/shared/api" interface ThinkingBudgetProps { apiConfiguration: ApiConfiguration @@ -9,21 +11,52 @@ interface ThinkingBudgetProps { } export const ThinkingBudget = ({ apiConfiguration, setApiConfigurationField, modelInfo }: ThinkingBudgetProps) => { - const budget = apiConfiguration?.anthropicThinking ?? THINKING_BUDGET.default - - return modelInfo && modelInfo.thinking ? ( -
-
Thinking Budget
-
- setApiConfigurationField("anthropicThinking", value[0])} - /> -
{budget}
+ const tokens = apiConfiguration?.modelMaxTokens || modelInfo?.maxTokens || 64_000 + const tokensMin = 8192 + const tokensMax = modelInfo?.maxTokens || 64_000 + + const thinkingTokens = apiConfiguration?.anthropicThinking || 8192 + const thinkingTokensMin = 1024 + const thinkingTokensMax = Math.floor(0.8 * tokens) + + useEffect(() => { + if (thinkingTokens > thinkingTokensMax) { + setApiConfigurationField("anthropicThinking", thinkingTokensMax) + } + }, [thinkingTokens, thinkingTokensMax, setApiConfigurationField]) + + if (!modelInfo || !modelInfo.thinking) { + return null + } + + return ( +
+
+
Max Tokens
+
+ setApiConfigurationField("modelMaxTokens", value)} + /> +
{tokens}
+
+
+
+
Max Thinking Tokens
+
+ setApiConfigurationField("anthropicThinking", value)} + /> +
{thinkingTokens}
+
- ) : null + ) } From 73c7fe777bb995acd384f719d8ef78d3aa238238 Mon Sep 17 00:00:00 2001 From: ShayBC Date: Thu, 27 Feb 2025 00:24:54 +0200 Subject: [PATCH 125/585] refactored ClineProvider.test --- .../webview/__tests__/ClineProvider.test.ts | 99 +++++++------------ 1 file changed, 35 insertions(+), 64 deletions(-) diff --git a/src/core/webview/__tests__/ClineProvider.test.ts b/src/core/webview/__tests__/ClineProvider.test.ts index fe45aaa801c..90d2da45f40 100644 --- a/src/core/webview/__tests__/ClineProvider.test.ts +++ b/src/core/webview/__tests__/ClineProvider.test.ts @@ -200,12 +200,15 @@ jest.mock("../../Cline", () => ({ .fn() .mockImplementation( (provider, apiConfiguration, customInstructions, diffEnabled, fuzzyMatchThreshold, task, taskId) => ({ + api: undefined, abortTask: jest.fn(), handleWebviewAskResponse: jest.fn(), clineMessages: [], apiConversationHistory: [], overwriteClineMessages: jest.fn(), overwriteApiConversationHistory: jest.fn(), + getTaskNumber: jest.fn().mockReturnValue(0), + setTaskNumber: jest.fn(), taskId: taskId || "test-task-id", }), ), @@ -407,16 +410,12 @@ describe("ClineProvider", () => { }) test("clearTask aborts current task", async () => { - // prepare the mock object - const mockAbortTask = jest.fn() - const clineMock = { - abortTask: mockAbortTask, - getTaskNumber: jest.fn(), - setTaskNumber: jest.fn(), - } as unknown as Cline + // Setup Cline instance with auto-mock from the top of the file + const { Cline } = require("../../Cline") // Get the mocked class + const mockCline = new Cline() // Create a new mocked instance // add the mock object to the stack - provider.addClineToStack(clineMock) + provider.addClineToStack(mockCline) // get the stack size before the abort call const stackSizeBeforeAbort = provider.getClineStackSize() @@ -428,24 +427,19 @@ describe("ClineProvider", () => { const stackSizeAfterAbort = provider.getClineStackSize() // check if the abort method was called - expect(mockAbortTask).toHaveBeenCalled() + expect(mockCline.abortTask).toHaveBeenCalled() // check if the stack size was decreased expect(stackSizeBeforeAbort - stackSizeAfterAbort).toBe(1) }) test("addClineToStack adds multiple Cline instances to the stack", () => { - // prepare test data - const mockCline1 = { - taskId: "test-task-id-1", - getTaskNumber: jest.fn(), - setTaskNumber: jest.fn(), - } as unknown as Cline - const mockCline2 = { - taskId: "test-task-id-2", - getTaskNumber: jest.fn(), - setTaskNumber: jest.fn(), - } as unknown as Cline + // Setup Cline instance with auto-mock from the top of the file + const { Cline } = require("../../Cline") // Get the mocked class + const mockCline1 = new Cline() // Create a new mocked instance + const mockCline2 = new Cline() // Create a new mocked instance + Object.defineProperty(mockCline1, "taskId", { value: "test-task-id-1", writable: true }) + Object.defineProperty(mockCline2, "taskId", { value: "test-task-id-2", writable: true }) // add Cline instances to the stack provider.addClineToStack(mockCline1) @@ -848,20 +842,12 @@ describe("ClineProvider", () => { const mockApiHistory = [{ ts: 1000 }, { ts: 2000 }, { ts: 3000 }, { ts: 4000 }, { ts: 5000 }, { ts: 6000 }] - // Setup Cline instance with mock data - const mockCline = { - clineMessages: mockMessages, - apiConversationHistory: mockApiHistory, - overwriteClineMessages: jest.fn(), - overwriteApiConversationHistory: jest.fn(), - getTaskNumber: jest.fn(), - setTaskNumber: jest.fn(), - taskId: "test-task-id", - abortTask: jest.fn(), - handleWebviewAskResponse: jest.fn(), - } as unknown as Cline - const newMockCline = Object.create(mockCline) - provider.addClineToStack(newMockCline) + // Setup Cline instance with auto-mock from the top of the file + const { Cline } = require("../../Cline") // Get the mocked class + const mockCline = new Cline() // Create a new mocked instance + mockCline.clineMessages = mockMessages // Set test-specific messages + mockCline.apiConversationHistory = mockApiHistory // Set API history + provider.addClineToStack(mockCline) // Add the mocked instance to the stack // Mock getTaskWithId ;(provider as any).getTaskWithId = jest.fn().mockResolvedValue({ @@ -873,7 +859,7 @@ describe("ClineProvider", () => { await messageHandler({ type: "deleteMessage", value: 4000 }) // Verify correct messages were kept - expect(newMockCline.overwriteClineMessages).toHaveBeenCalledWith([ + expect(mockCline.overwriteClineMessages).toHaveBeenCalledWith([ mockMessages[0], mockMessages[1], mockMessages[4], @@ -881,7 +867,7 @@ describe("ClineProvider", () => { ]) // Verify correct API messages were kept - expect(newMockCline.overwriteApiConversationHistory).toHaveBeenCalledWith([ + expect(mockCline.overwriteApiConversationHistory).toHaveBeenCalledWith([ mockApiHistory[0], mockApiHistory[1], mockApiHistory[4], @@ -903,18 +889,11 @@ describe("ClineProvider", () => { const mockApiHistory = [{ ts: 1000 }, { ts: 2000 }, { ts: 3000 }, { ts: 4000 }] - // Setup Cline instance with mock data - const mockCline = { - clineMessages: mockMessages, - apiConversationHistory: mockApiHistory, - overwriteClineMessages: jest.fn(), - overwriteApiConversationHistory: jest.fn(), - getTaskNumber: jest.fn(), - setTaskNumber: jest.fn(), - taskId: "test-task-id", - abortTask: jest.fn(), - handleWebviewAskResponse: jest.fn(), - } as unknown as Cline + // Setup Cline instance with auto-mock from the top of the file + const { Cline } = require("../../Cline") // Get the mocked class + const mockCline = new Cline() // Create a new mocked instance + mockCline.clineMessages = mockMessages + mockCline.apiConversationHistory = mockApiHistory provider.addClineToStack(mockCline) // Mock getTaskWithId @@ -937,15 +916,11 @@ describe("ClineProvider", () => { // Mock user selecting "Cancel" ;(vscode.window.showInformationMessage as jest.Mock).mockResolvedValue("Cancel") - const mockCline = { - clineMessages: [{ ts: 1000 }, { ts: 2000 }], - apiConversationHistory: [{ ts: 1000 }, { ts: 2000 }], - overwriteClineMessages: jest.fn(), - overwriteApiConversationHistory: jest.fn(), - getTaskNumber: jest.fn(), - setTaskNumber: jest.fn(), - taskId: "test-task-id", - } as unknown as Cline + // Setup Cline instance with auto-mock from the top of the file + const { Cline } = require("../../Cline") // Get the mocked class + const mockCline = new Cline() // Create a new mocked instance + mockCline.clineMessages = [{ ts: 1000 }, { ts: 2000 }] + mockCline.apiConversationHistory = [{ ts: 1000 }, { ts: 2000 }] provider.addClineToStack(mockCline) // Trigger message deletion @@ -1446,13 +1421,9 @@ describe("ClineProvider", () => { .mockResolvedValue([{ name: "test-config", id: "test-id", apiProvider: "anthropic" }]), } as any - // Setup mock Cline instance - const mockCline = { - api: undefined, - getTaskNumber: jest.fn(), - setTaskNumber: jest.fn(), - abortTask: jest.fn(), - } as unknown as Cline + // Setup Cline instance with auto-mock from the top of the file + const { Cline } = require("../../Cline") // Get the mocked class + const mockCline = new Cline() // Create a new mocked instance provider.addClineToStack(mockCline) const testApiConfig = { From cf69b0fff92e8b1cff23cf4b37d8e8a2f10a34dc Mon Sep 17 00:00:00 2001 From: Chris Estreich Date: Wed, 26 Feb 2025 14:26:27 -0800 Subject: [PATCH 126/585] Add changeset --- .changeset/wild-emus-dream.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/wild-emus-dream.md diff --git a/.changeset/wild-emus-dream.md b/.changeset/wild-emus-dream.md new file mode 100644 index 00000000000..19e5a4626b0 --- /dev/null +++ b/.changeset/wild-emus-dream.md @@ -0,0 +1,5 @@ +--- +"roo-cline": patch +--- + +Allow control over maxTokens for thinking models From dfa019e7f443bf9997ed2ac2556f597a59e4bf77 Mon Sep 17 00:00:00 2001 From: Matt Rubens Date: Wed, 26 Feb 2025 17:34:39 -0500 Subject: [PATCH 127/585] Truncate results from search_files to 500 chars max --- .changeset/stale-cooks-help.md | 5 ++ src/services/ripgrep/__tests__/index.test.ts | 51 ++++++++++++++++++++ src/services/ripgrep/index.ts | 30 ++++++++++-- 3 files changed, 82 insertions(+), 4 deletions(-) create mode 100644 .changeset/stale-cooks-help.md create mode 100644 src/services/ripgrep/__tests__/index.test.ts diff --git a/.changeset/stale-cooks-help.md b/.changeset/stale-cooks-help.md new file mode 100644 index 00000000000..8c9c7147381 --- /dev/null +++ b/.changeset/stale-cooks-help.md @@ -0,0 +1,5 @@ +--- +"roo-cline": patch +--- + +Truncate search_file output to avoid crashing the extension diff --git a/src/services/ripgrep/__tests__/index.test.ts b/src/services/ripgrep/__tests__/index.test.ts new file mode 100644 index 00000000000..7c3549a827b --- /dev/null +++ b/src/services/ripgrep/__tests__/index.test.ts @@ -0,0 +1,51 @@ +// npx jest src/services/ripgrep/__tests__/index.test.ts + +import { describe, expect, it } from "@jest/globals" +import { truncateLine } from "../index" + +describe("Ripgrep line truncation", () => { + // The default MAX_LINE_LENGTH is 500 in the implementation + const MAX_LINE_LENGTH = 500 + + it("should truncate lines longer than MAX_LINE_LENGTH", () => { + const longLine = "a".repeat(600) // Line longer than MAX_LINE_LENGTH + const truncated = truncateLine(longLine) + + expect(truncated).toContain("[truncated...]") + expect(truncated.length).toBeLessThan(longLine.length) + expect(truncated.length).toEqual(MAX_LINE_LENGTH + " [truncated...]".length) + }) + + it("should not truncate lines shorter than MAX_LINE_LENGTH", () => { + const shortLine = "Short line of text" + const truncated = truncateLine(shortLine) + + expect(truncated).toEqual(shortLine) + expect(truncated).not.toContain("[truncated...]") + }) + + it("should correctly truncate a line at exactly MAX_LINE_LENGTH characters", () => { + const exactLine = "a".repeat(MAX_LINE_LENGTH) + const exactPlusOne = exactLine + "x" + + // Should not truncate when exactly MAX_LINE_LENGTH + expect(truncateLine(exactLine)).toEqual(exactLine) + + // Should truncate when exceeding MAX_LINE_LENGTH by even 1 character + expect(truncateLine(exactPlusOne)).toContain("[truncated...]") + }) + + it("should handle empty lines without errors", () => { + expect(truncateLine("")).toEqual("") + }) + + it("should allow custom maximum length", () => { + const customLength = 100 + const line = "a".repeat(customLength + 50) + + const truncated = truncateLine(line, customLength) + + expect(truncated.length).toEqual(customLength + " [truncated...]".length) + expect(truncated).toContain("[truncated...]") + }) +}) diff --git a/src/services/ripgrep/index.ts b/src/services/ripgrep/index.ts index b48c60b5b2e..770c897e529 100644 --- a/src/services/ripgrep/index.ts +++ b/src/services/ripgrep/index.ts @@ -58,7 +58,19 @@ interface SearchResult { afterContext: string[] } +// Constants const MAX_RESULTS = 300 +const MAX_LINE_LENGTH = 500 + +/** + * Truncates a line if it exceeds the maximum length + * @param line The line to truncate + * @param maxLength The maximum allowed length (defaults to MAX_LINE_LENGTH) + * @returns The truncated line, or the original line if it's shorter than maxLength + */ +export function truncateLine(line: string, maxLength: number = MAX_LINE_LENGTH): string { + return line.length > maxLength ? line.substring(0, maxLength) + " [truncated...]" : line +} async function getBinPath(vscodeAppRoot: string): Promise { const checkPath = async (pkgFolder: string) => { @@ -140,7 +152,8 @@ export async function regexSearchFiles( let output: string try { output = await execRipgrep(rgPath, args) - } catch { + } catch (error) { + console.error("Error executing ripgrep:", error) return "No results found" } const results: SearchResult[] = [] @@ -154,19 +167,28 @@ export async function regexSearchFiles( if (currentResult) { results.push(currentResult as SearchResult) } + + // Safety check: truncate extremely long lines to prevent excessive output + const matchText = parsed.data.lines.text + const truncatedMatch = truncateLine(matchText) + currentResult = { file: parsed.data.path.text, line: parsed.data.line_number, column: parsed.data.submatches[0].start, - match: parsed.data.lines.text, + match: truncatedMatch, beforeContext: [], afterContext: [], } } else if (parsed.type === "context" && currentResult) { + // Apply the same truncation logic to context lines + const contextText = parsed.data.lines.text + const truncatedContext = truncateLine(contextText) + if (parsed.data.line_number < currentResult.line!) { - currentResult.beforeContext!.push(parsed.data.lines.text) + currentResult.beforeContext!.push(truncatedContext) } else { - currentResult.afterContext!.push(parsed.data.lines.text) + currentResult.afterContext!.push(truncatedContext) } } } catch (error) { From 247a50a6dcbb29512d20796b29a3240dc4b84463 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Wed, 26 Feb 2025 23:17:23 +0000 Subject: [PATCH 128/585] changeset version bump --- .changeset/fluffy-apples-attack.md | 5 ----- .changeset/orange-zoos-train.md | 5 ----- .changeset/stale-cooks-help.md | 5 ----- .changeset/tender-cycles-help.md | 5 ----- .changeset/wild-emus-dream.md | 5 ----- CHANGELOG.md | 10 ++++++++++ package-lock.json | 4 ++-- package.json | 2 +- 8 files changed, 13 insertions(+), 28 deletions(-) delete mode 100644 .changeset/fluffy-apples-attack.md delete mode 100644 .changeset/orange-zoos-train.md delete mode 100644 .changeset/stale-cooks-help.md delete mode 100644 .changeset/tender-cycles-help.md delete mode 100644 .changeset/wild-emus-dream.md diff --git a/.changeset/fluffy-apples-attack.md b/.changeset/fluffy-apples-attack.md deleted file mode 100644 index 924a1b25057..00000000000 --- a/.changeset/fluffy-apples-attack.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"roo-cline": patch ---- - -Handle really long text in the ChatRow similar to TaskHeader diff --git a/.changeset/orange-zoos-train.md b/.changeset/orange-zoos-train.md deleted file mode 100644 index 76c16f45671..00000000000 --- a/.changeset/orange-zoos-train.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"roo-cline": patch ---- - -Support multiple files in drag-and-drop diff --git a/.changeset/stale-cooks-help.md b/.changeset/stale-cooks-help.md deleted file mode 100644 index 8c9c7147381..00000000000 --- a/.changeset/stale-cooks-help.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"roo-cline": patch ---- - -Truncate search_file output to avoid crashing the extension diff --git a/.changeset/tender-cycles-help.md b/.changeset/tender-cycles-help.md deleted file mode 100644 index d43e423ee61..00000000000 --- a/.changeset/tender-cycles-help.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"roo-cline": patch ---- - -Better OpenRouter error handling diff --git a/.changeset/wild-emus-dream.md b/.changeset/wild-emus-dream.md deleted file mode 100644 index 19e5a4626b0..00000000000 --- a/.changeset/wild-emus-dream.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"roo-cline": patch ---- - -Allow control over maxTokens for thinking models diff --git a/CHANGELOG.md b/CHANGELOG.md index 02a4a30cbd2..0e5223231ae 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,15 @@ # Roo Code Changelog +## 3.7.6 + +### Patch Changes + +- Handle really long text in the ChatRow similar to TaskHeader +- Support multiple files in drag-and-drop +- Truncate search_file output to avoid crashing the extension +- Better OpenRouter error handling +- Allow control over maxTokens for thinking models + ## [3.7.5] - Fix context window truncation math (see [#1173](https://github.com/RooVetGit/Roo-Code/issues/1173)) diff --git a/package-lock.json b/package-lock.json index a6c75bd69b3..808e2f2f107 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "roo-cline", - "version": "3.7.5", + "version": "3.7.6", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "roo-cline", - "version": "3.7.5", + "version": "3.7.6", "dependencies": { "@anthropic-ai/bedrock-sdk": "^0.10.2", "@anthropic-ai/sdk": "^0.37.0", diff --git a/package.json b/package.json index 40bb6a545dd..463e9d597a2 100644 --- a/package.json +++ b/package.json @@ -3,7 +3,7 @@ "displayName": "Roo Code (prev. Roo Cline)", "description": "A whole dev team of AI agents in your editor.", "publisher": "RooVeterinaryInc", - "version": "3.7.5", + "version": "3.7.6", "icon": "assets/icons/rocket.png", "galleryBanner": { "color": "#617A91", From 4f578dc8262e03a2a665abcd6784610cc092cdb2 Mon Sep 17 00:00:00 2001 From: Matt Rubens Date: Wed, 26 Feb 2025 18:47:22 -0500 Subject: [PATCH 129/585] Update CHANGELOG.md --- CHANGELOG.md | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0e5223231ae..13b06953354 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,14 +1,12 @@ # Roo Code Changelog -## 3.7.6 +## [3.7.6] -### Patch Changes - -- Handle really long text in the ChatRow similar to TaskHeader +- Handle really long text better in the in the ChatRow similar to TaskHeader (thanks @joemanley201!) - Support multiple files in drag-and-drop - Truncate search_file output to avoid crashing the extension -- Better OpenRouter error handling -- Allow control over maxTokens for thinking models +- Better OpenRouter error handling (no more "Provider Error") +- Add slider to control max output tokens for thinking models ## [3.7.5] From 5c5bf8502094fb87397eebadda89acd3512dcf84 Mon Sep 17 00:00:00 2001 From: Matt Rubens Date: Wed, 26 Feb 2025 21:36:19 -0500 Subject: [PATCH 130/585] Stop removing commas from terminal output --- .changeset/sour-parents-hug.md | 5 +++++ src/integrations/terminal/TerminalProcess.ts | 3 --- 2 files changed, 5 insertions(+), 3 deletions(-) create mode 100644 .changeset/sour-parents-hug.md diff --git a/.changeset/sour-parents-hug.md b/.changeset/sour-parents-hug.md new file mode 100644 index 00000000000..a24286b6bbe --- /dev/null +++ b/.changeset/sour-parents-hug.md @@ -0,0 +1,5 @@ +--- +"roo-cline": patch +--- + +Stop removing commas from terminal output diff --git a/src/integrations/terminal/TerminalProcess.ts b/src/integrations/terminal/TerminalProcess.ts index 5597350db3c..4e85c10575d 100644 --- a/src/integrations/terminal/TerminalProcess.ts +++ b/src/integrations/terminal/TerminalProcess.ts @@ -110,9 +110,6 @@ export class TerminalProcess extends EventEmitter { data = lines.join("\n") } - // FIXME: right now it seems that data chunks returned to us from the shell integration stream contains random commas, which from what I can tell is not the expected behavior. There has to be a better solution here than just removing all commas. - data = data.replace(/,/g, "") - // 2. Set isHot depending on the command // Set to hot to stall API requests until terminal is cool again this.isHot = true From 4806ab5420048af6526348e5b128dd4724c9fcc8 Mon Sep 17 00:00:00 2001 From: dleffel Date: Wed, 26 Feb 2025 21:34:56 -0800 Subject: [PATCH 131/585] Fix missing tooltips in several components. --- .../src/components/chat/Announcement.tsx | 1 + .../src/components/chat/ChatTextArea.tsx | 5 +++ webview-ui/src/components/chat/ChatView.tsx | 33 ++++++++++++++++++- webview-ui/src/components/chat/TaskHeader.tsx | 13 ++++++-- 4 files changed, 49 insertions(+), 3 deletions(-) diff --git a/webview-ui/src/components/chat/Announcement.tsx b/webview-ui/src/components/chat/Announcement.tsx index a2e96606efc..93d0c9d7500 100644 --- a/webview-ui/src/components/chat/Announcement.tsx +++ b/webview-ui/src/components/chat/Announcement.tsx @@ -25,6 +25,7 @@ const Announcement = ({ version, hideAnnouncement }: AnnouncementProps) => { diff --git a/webview-ui/src/components/chat/ChatTextArea.tsx b/webview-ui/src/components/chat/ChatTextArea.tsx index be2b2a97984..dcbe0851477 100644 --- a/webview-ui/src/components/chat/ChatTextArea.tsx +++ b/webview-ui/src/components/chat/ChatTextArea.tsx @@ -798,6 +798,7 @@ const ChatTextArea = forwardRef( { const value = e.target.value if (value === "settings-action") { @@ -915,6 +917,7 @@ const ChatTextArea = forwardRef( role="button" aria-label="enhance prompt" data-testid="enhance-prompt-button" + title="Enhance prompt with additional context" className={`input-icon-button ${ textAreaDisabled ? "disabled" : "" } codicon codicon-sparkle`} @@ -927,11 +930,13 @@ const ChatTextArea = forwardRef( className={`input-icon-button ${ shouldDisableImages ? "disabled" : "" } codicon codicon-device-camera`} + title="Add images to message" onClick={() => !shouldDisableImages && onSelectImages()} style={{ fontSize: 16.5 }} /> !textAreaDisabled && onSend()} style={{ fontSize: 15 }} /> diff --git a/webview-ui/src/components/chat/ChatView.tsx b/webview-ui/src/components/chat/ChatView.tsx index 98369cf095c..fcd1ba9a3b4 100644 --- a/webview-ui/src/components/chat/ChatView.tsx +++ b/webview-ui/src/components/chat/ChatView.tsx @@ -1077,7 +1077,8 @@ const ChatView = ({ isHidden, showAnnouncement, hideAnnouncement, showHistoryVie onClick={() => { scrollToBottomSmooth() disableAutoScrollRef.current = false - }}> + }} + title="Scroll to bottom of chat">
@@ -1101,6 +1102,25 @@ const ChatView = ({ isHidden, showAnnouncement, hideAnnouncement, showHistoryVie flex: secondaryButtonText ? 1 : 2, marginRight: secondaryButtonText ? "6px" : "0", }} + title={ + primaryButtonText === "Retry" + ? "Try the operation again" + : primaryButtonText === "Save" + ? "Save the file changes" + : primaryButtonText === "Approve" + ? "Approve this action" + : primaryButtonText === "Run Command" + ? "Execute this command" + : primaryButtonText === "Start New Task" + ? "Begin a new task" + : primaryButtonText === "Resume Task" + ? "Continue the current task" + : primaryButtonText === "Proceed Anyways" + ? "Continue despite warnings" + : primaryButtonText === "Proceed While Running" + ? "Continue while command executes" + : undefined + } onClick={(e) => handlePrimaryButtonClick(inputValue, selectedImages)}> {primaryButtonText} @@ -1113,6 +1133,17 @@ const ChatView = ({ isHidden, showAnnouncement, hideAnnouncement, showHistoryVie flex: isStreaming ? 2 : 1, marginLeft: isStreaming ? 0 : "6px", }} + title={ + isStreaming + ? "Cancel the current operation" + : secondaryButtonText === "Start New Task" + ? "Begin a new task" + : secondaryButtonText === "Reject" + ? "Reject this action" + : secondaryButtonText === "Terminate" + ? "End the current task" + : undefined + } onClick={(e) => handleSecondaryButtonClick(inputValue, selectedImages)}> {isStreaming ? "Cancel" : secondaryButtonText} diff --git a/webview-ui/src/components/chat/TaskHeader.tsx b/webview-ui/src/components/chat/TaskHeader.tsx index 341855f796e..fb7db6f6173 100644 --- a/webview-ui/src/components/chat/TaskHeader.tsx +++ b/webview-ui/src/components/chat/TaskHeader.tsx @@ -180,7 +180,11 @@ const TaskHeader: React.FC = ({ ${totalCost?.toFixed(4)}
)} - +
@@ -348,13 +352,18 @@ export const highlightMentions = (text?: string, withShadow = true) => { const TaskActions = ({ item }: { item: HistoryItem | undefined }) => (
- {!!item?.size && item.size > 0 && (
) diff --git a/webview-ui/src/components/settings/SettingsView.tsx b/webview-ui/src/components/settings/SettingsView.tsx index d3e65a99ea8..51ef4fe81d6 100644 --- a/webview-ui/src/components/settings/SettingsView.tsx +++ b/webview-ui/src/components/settings/SettingsView.tsx @@ -52,7 +52,7 @@ const SettingsView = forwardRef(({ onDone }, alwaysAllowWrite, alwaysApproveResubmit, browserViewportSize, - checkpointsEnabled, + enableCheckpoints, diffEnabled, experiments, fuzzyMatchThreshold, @@ -143,7 +143,7 @@ const SettingsView = forwardRef(({ onDone }, vscode.postMessage({ type: "soundEnabled", bool: soundEnabled }) vscode.postMessage({ type: "soundVolume", value: soundVolume }) vscode.postMessage({ type: "diffEnabled", bool: diffEnabled }) - vscode.postMessage({ type: "checkpointsEnabled", bool: checkpointsEnabled }) + vscode.postMessage({ type: "enableCheckpoints", bool: enableCheckpoints }) vscode.postMessage({ type: "browserViewportSize", text: browserViewportSize }) vscode.postMessage({ type: "fuzzyMatchThreshold", value: fuzzyMatchThreshold ?? 1.0 }) vscode.postMessage({ type: "writeDelayMs", value: writeDelayMs }) @@ -706,6 +706,25 @@ const SettingsView = forwardRef(({ onDone },

+
+ { + setCachedStateField("enableCheckpoints", e.target.checked) + }}> + Enable automatic checkpoints + +

+ When enabled, Roo will automatically create checkpoints during task execution, making it + easy to review changes or revert to earlier states. +

+
+
(({ onDone },
)} -
-
- ⚠️ - { - setCachedStateField("checkpointsEnabled", e.target.checked) - }}> - Enable experimental checkpoints - -
-

- When enabled, Roo will save a checkpoint whenever a file in the workspace is modified, - added or deleted, letting you easily revert to a previous state. -

-
- {Object.entries(experimentConfigsMap) .filter((config) => config[0] !== "DIFF_STRATEGY") .map((config) => ( diff --git a/webview-ui/src/context/ExtensionStateContext.tsx b/webview-ui/src/context/ExtensionStateContext.tsx index ae5c5b95398..3dfc87de750 100644 --- a/webview-ui/src/context/ExtensionStateContext.tsx +++ b/webview-ui/src/context/ExtensionStateContext.tsx @@ -32,7 +32,7 @@ export interface ExtensionStateContextType extends ExtensionState { setSoundEnabled: (value: boolean) => void setSoundVolume: (value: number) => void setDiffEnabled: (value: boolean) => void - setCheckpointsEnabled: (value: boolean) => void + setEnableCheckpoints: (value: boolean) => void setBrowserViewportSize: (value: string) => void setFuzzyMatchThreshold: (value: number) => void preferredLanguage: string @@ -79,7 +79,7 @@ export const ExtensionStateContextProvider: React.FC<{ children: React.ReactNode soundEnabled: false, soundVolume: 0.5, diffEnabled: false, - checkpointsEnabled: false, + enableCheckpoints: true, fuzzyMatchThreshold: 1.0, preferredLanguage: "English", writeDelayMs: 1000, @@ -219,7 +219,7 @@ export const ExtensionStateContextProvider: React.FC<{ children: React.ReactNode setSoundEnabled: (value) => setState((prevState) => ({ ...prevState, soundEnabled: value })), setSoundVolume: (value) => setState((prevState) => ({ ...prevState, soundVolume: value })), setDiffEnabled: (value) => setState((prevState) => ({ ...prevState, diffEnabled: value })), - setCheckpointsEnabled: (value) => setState((prevState) => ({ ...prevState, checkpointsEnabled: value })), + setEnableCheckpoints: (value) => setState((prevState) => ({ ...prevState, enableCheckpoints: value })), setBrowserViewportSize: (value: string) => setState((prevState) => ({ ...prevState, browserViewportSize: value })), setFuzzyMatchThreshold: (value) => setState((prevState) => ({ ...prevState, fuzzyMatchThreshold: value })), From ea38d9ebbac80f4170f74b4c7d978e588b132d2d Mon Sep 17 00:00:00 2001 From: Aitor Oses Date: Thu, 27 Feb 2025 09:04:54 +0100 Subject: [PATCH 133/585] Enable prompt caching for Claude Sonnet 3.7 Vertex AI model --- src/shared/api.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/shared/api.ts b/src/shared/api.ts index 5cda3330318..cd6aead1a59 100644 --- a/src/shared/api.ts +++ b/src/shared/api.ts @@ -441,7 +441,7 @@ export const vertexModels = { contextWindow: 200_000, supportsImages: true, supportsComputerUse: true, - supportsPromptCache: false, + supportsPromptCache: true, inputPrice: 3.0, outputPrice: 15.0, }, From 3bb1d78c17f8caf6d57fb0ebd541c7ea87d9c3e8 Mon Sep 17 00:00:00 2001 From: Felix NyxJae <18661811993@163.com> Date: Thu, 27 Feb 2025 18:08:05 +0800 Subject: [PATCH 134/585] feat: Added human relay function and related message processing initial version --- .gitignore | 5 + src/activate/registerCommands.ts | 43 ++++- src/api/index.ts | 3 + src/api/providers/human-relay.ts | 162 ++++++++++++++++++ src/core/webview/ClineProvider.ts | 19 ++ src/extension.ts | 36 ++++ src/shared/ExtensionMessage.ts | 21 +++ src/shared/WebviewMessage.ts | 14 ++ src/shared/api.ts | 1 + webview-ui/src/App.tsx | 52 ++++++ .../human-relay/HumanRelayDialog.tsx | 105 ++++++++++++ .../src/components/settings/ApiOptions.tsx | 25 +++ 12 files changed, 483 insertions(+), 3 deletions(-) create mode 100644 src/api/providers/human-relay.ts create mode 100644 webview-ui/src/components/human-relay/HumanRelayDialog.tsx diff --git a/.gitignore b/.gitignore index 211d06aa199..bdae7b5b26a 100644 --- a/.gitignore +++ b/.gitignore @@ -28,3 +28,8 @@ docs/_site/ #Logging logs +.clinerules-architect +.clinerules-ask +.clinerules-code +MemoryBank +.github/copilot-instructions.md diff --git a/src/activate/registerCommands.ts b/src/activate/registerCommands.ts index 69e257e7a51..8cc895f291e 100644 --- a/src/activate/registerCommands.ts +++ b/src/activate/registerCommands.ts @@ -3,6 +3,19 @@ import delay from "delay" import { ClineProvider } from "../core/webview/ClineProvider" +// Add a global variable to store panel references +let panel: vscode.WebviewPanel | undefined = undefined + +// Get the panel function for command access +export function getPanel(): vscode.WebviewPanel | undefined { + return panel +} + +// Setting the function of the panel +export function setPanel(newPanel: vscode.WebviewPanel | undefined): void { + panel = newPanel +} + export type RegisterCommandOptions = { context: vscode.ExtensionContext outputChannel: vscode.OutputChannel @@ -15,6 +28,22 @@ export const registerCommands = (options: RegisterCommandOptions) => { for (const [command, callback] of Object.entries(getCommandsMap(options))) { context.subscriptions.push(vscode.commands.registerCommand(command, callback)) } + + // Human Relay Dialog Command + context.subscriptions.push( + vscode.commands.registerCommand( + "roo-code.showHumanRelayDialog", + (params: { requestId: string; promptText: string }) => { + if (getPanel()) { + getPanel()?.webview.postMessage({ + type: "showHumanRelayDialog", + requestId: params.requestId, + promptText: params.promptText, + }) + } + }, + ), + ) } const getCommandsMap = ({ context, outputChannel, provider }: RegisterCommandOptions) => { @@ -65,20 +94,28 @@ const openClineInNewTab = async ({ context, outputChannel }: Omit { + setPanel(undefined) + }) // Lock the editor group so clicking on files doesn't open them over the panel await delay(100) diff --git a/src/api/index.ts b/src/api/index.ts index f68c9acd1fb..85572632ec2 100644 --- a/src/api/index.ts +++ b/src/api/index.ts @@ -16,6 +16,7 @@ import { VsCodeLmHandler } from "./providers/vscode-lm" import { ApiStream } from "./transform/stream" import { UnboundHandler } from "./providers/unbound" import { RequestyHandler } from "./providers/requesty" +import { HumanRelayHandler } from "./providers/human-relay" export interface SingleCompletionHandler { completePrompt(prompt: string): Promise @@ -59,6 +60,8 @@ export function buildApiHandler(configuration: ApiConfiguration): ApiHandler { return new UnboundHandler(options) case "requesty": return new RequestyHandler(options) + case "human-relay": + return new HumanRelayHandler(options) default: return new AnthropicHandler(options) } diff --git a/src/api/providers/human-relay.ts b/src/api/providers/human-relay.ts new file mode 100644 index 00000000000..8454a7c9af8 --- /dev/null +++ b/src/api/providers/human-relay.ts @@ -0,0 +1,162 @@ +// filepath: e:\Project\Roo-Code\src\api\providers\human-relay.ts +import { Anthropic } from "@anthropic-ai/sdk" +import { ApiHandlerOptions, ModelInfo } from "../../shared/api" +import { ApiHandler, SingleCompletionHandler } from "../index" +import { ApiStream } from "../transform/stream" +import * as vscode from "vscode" +import { ExtensionMessage } from "../../shared/ExtensionMessage" + +/** + * Human Relay API processor + * This processor does not directly call the API, but interacts with the model through human operations copy and paste. + */ +export class HumanRelayHandler implements ApiHandler, SingleCompletionHandler { + private options: ApiHandlerOptions + + constructor(options: ApiHandlerOptions) { + this.options = options + } + + /** + * Create a message processing flow, display a dialog box to request human assistance + * @param systemPrompt System prompt words + * @param messages Message list + */ + async *createMessage(systemPrompt: string, messages: Anthropic.Messages.MessageParam[]): ApiStream { + // Get the most recent user message + const latestMessage = messages[messages.length - 1] + + if (!latestMessage) { + throw new Error("No message to relay") + } + + // If it is the first message, splice the system prompt word with the user message + let promptText = "" + if (messages.length === 1) { + promptText = `${systemPrompt}\n\n${getMessageContent(latestMessage)}` + } else { + promptText = getMessageContent(latestMessage) + } + + // Copy to clipboard + await vscode.env.clipboard.writeText(promptText) + + // A dialog box pops up to request user action + const response = await showHumanRelayDialog(promptText) + + if (!response) { + // The user canceled the operation + throw new Error("Human relay operation cancelled") + } + + // Return to the user input reply + yield { type: "text", text: response } + } + + /** + * Get model information + */ + getModel(): { id: string; info: ModelInfo } { + // Human relay does not depend on a specific model, here is a default configuration + return { + id: "human-relay", + info: { + maxTokens: 16384, + contextWindow: 100000, + supportsImages: true, + supportsPromptCache: false, + supportsComputerUse: true, + inputPrice: 0, + outputPrice: 0, + description: "Calling web-side AI model through human relay", + }, + } + } + + /** + * Implementation of a single prompt + * @param prompt Prompt content + */ + async completePrompt(prompt: string): Promise { + // Copy to clipboard + await vscode.env.clipboard.writeText(prompt) + + // A dialog box pops up to request user action + const response = await showHumanRelayDialog(prompt) + + if (!response) { + throw new Error("Human relay operation cancelled") + } + + return response + } +} + +/** + * Extract text content from message object + * @param message + */ +function getMessageContent(message: Anthropic.Messages.MessageParam): string { + if (typeof message.content === "string") { + return message.content + } else if (Array.isArray(message.content)) { + return message.content + .filter((item) => item.type === "text") + .map((item) => (item.type === "text" ? item.text : "")) + .join("\n") + } + return "" +} +/** + * Displays the human relay dialog and waits for user response. + * @param promptText The prompt text that needs to be copied. + * @returns The user's input response or undefined (if canceled). + */ +async function showHumanRelayDialog(promptText: string): Promise { + return new Promise((resolve) => { + // Create a unique request ID + const requestId = Date.now().toString() + + // Register callback to the global callback map + vscode.commands.executeCommand( + "roo-code.registerHumanRelayCallback", + requestId, + (response: string | undefined) => { + resolve(response) + }, + ) + + // Show the WebView dialog + vscode.commands.executeCommand("roo-code.showHumanRelayDialog", { + requestId, + promptText, + }) + + // Provide a temporary UI in case the WebView fails to load + vscode.window + .showInformationMessage( + "Please paste the copied message to the AI, then copy the response back into the dialog", + { + modal: true, + detail: "The message has been copied to the clipboard. If the dialog does not open, please try using the input box.", + }, + "Use Input Box", + ) + .then((selection) => { + if (selection === "Use Input Box") { + // Unregister the callback + vscode.commands.executeCommand("roo-code.unregisterHumanRelayCallback", requestId) + + vscode.window + .showInputBox({ + prompt: "Please paste the AI's response here", + placeHolder: "Paste the AI's response here...", + ignoreFocusOut: true, + }) + .then((input) => { + resolve(input || undefined) + }) + } + }) + }) +} diff --git a/src/core/webview/ClineProvider.ts b/src/core/webview/ClineProvider.ts index 5e6170e2eee..1c2ffea5508 100644 --- a/src/core/webview/ClineProvider.ts +++ b/src/core/webview/ClineProvider.ts @@ -1522,7 +1522,26 @@ export class ClineProvider implements vscode.WebviewViewProvider { // Switch back to default mode after deletion await this.updateGlobalState("mode", defaultModeSlug) await this.postStateToWebview() + break + } + case "humanRelayResponse": + if (message.requestId && message.text) { + vscode.commands.executeCommand("roo-code.handleHumanRelayResponse", { + requestId: message.requestId, + text: message.text, + cancelled: false, + }) + } + break + + case "humanRelayCancel": + if (message.requestId) { + vscode.commands.executeCommand("roo-code.handleHumanRelayResponse", { + requestId: message.requestId, + cancelled: true, + }) } + break } }, null, diff --git a/src/extension.ts b/src/extension.ts index a05afa46512..3b148a41ac9 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -19,6 +19,18 @@ import { McpServerManager } from "./services/mcp/McpServerManager" let outputChannel: vscode.OutputChannel let extensionContext: vscode.ExtensionContext +// Callback mapping of human relay response +const humanRelayCallbacks = new Map void>() + +/** + * Register a callback function for human relay response + * @param requestId + * @param callback + */ +export function registerHumanRelayCallback(requestId: string, callback: (response: string | undefined) => void): void { + humanRelayCallbacks.set(requestId, callback) +} + // This method is called when your extension is activated. // Your extension is activated the very first time the command is executed. export function activate(context: vscode.ExtensionContext) { @@ -45,6 +57,30 @@ export function activate(context: vscode.ExtensionContext) { registerCommands({ context, outputChannel, provider: sidebarProvider }) + // Register human relay response processing command + context.subscriptions.push( + vscode.commands.registerCommand( + "roo-code.handleHumanRelayResponse", + (response: { requestId: string; text?: string; cancelled?: boolean }) => { + const callback = humanRelayCallbacks.get(response.requestId) + if (callback) { + if (response.cancelled) { + callback(undefined) + } else { + callback(response.text) + } + humanRelayCallbacks.delete(response.requestId) + } + }, + ), + ) + + context.subscriptions.push( + vscode.commands.registerCommand("roo-code.unregisterHumanRelayCallback", (requestId: string) => { + humanRelayCallbacks.delete(requestId) + }), + ) + /** * We use the text document content provider API to show the left side for diff * view by creating a virtual document for the original content. This makes it diff --git a/src/shared/ExtensionMessage.ts b/src/shared/ExtensionMessage.ts index e87edffed16..16a18043f57 100644 --- a/src/shared/ExtensionMessage.ts +++ b/src/shared/ExtensionMessage.ts @@ -45,6 +45,9 @@ export interface ExtensionMessage { | "updateCustomMode" | "deleteCustomMode" | "currentCheckpointUpdated" + | "showHumanRelayDialog" + | "humanRelayResponse" + | "humanRelayCancel" text?: string action?: | "chatButtonClicked" @@ -239,4 +242,22 @@ export interface ClineApiReqInfo { streamingFailedMessage?: string } +// Human relay related message types +export interface ShowHumanRelayDialogMessage { + type: "showHumanRelayDialog" + requestId: string + promptText: string +} + +export interface HumanRelayResponseMessage { + type: "humanRelayResponse" + requestId: string + text: string +} + +export interface HumanRelayCancelMessage { + type: "humanRelayCancel" + requestId: string +} + export type ClineApiReqCancelReason = "streaming_failed" | "user_cancelled" diff --git a/src/shared/WebviewMessage.ts b/src/shared/WebviewMessage.ts index fde7442cc1d..2b0c68f7bea 100644 --- a/src/shared/WebviewMessage.ts +++ b/src/shared/WebviewMessage.ts @@ -94,6 +94,8 @@ export interface WebviewMessage { | "checkpointRestore" | "deleteMcpServer" | "maxOpenTabsContext" + | "HumanRelayResponseMessage" + | "HumanRelayCancelMessage" text?: string disabled?: boolean askResponse?: ClineAskResponse @@ -119,6 +121,18 @@ export interface WebviewMessage { source?: "global" | "project" } +// Human relay related message types +export interface HumanRelayResponseMessage { + type: "humanRelayResponse" + requestId: string + text: string +} + +export interface HumanRelayCancelMessage { + type: "humanRelayCancel" + requestId: string +} + export const checkoutDiffPayloadSchema = z.object({ ts: z.number(), commitHash: z.string(), diff --git a/src/shared/api.ts b/src/shared/api.ts index e7e4c54db6a..68b2f87c45f 100644 --- a/src/shared/api.ts +++ b/src/shared/api.ts @@ -16,6 +16,7 @@ export type ApiProvider = | "mistral" | "unbound" | "requesty" + | "human-relay" export interface ApiHandlerOptions { apiModelId?: string diff --git a/webview-ui/src/App.tsx b/webview-ui/src/App.tsx index 3ae441cd52f..5909a3eaef2 100644 --- a/webview-ui/src/App.tsx +++ b/webview-ui/src/App.tsx @@ -11,6 +11,7 @@ import SettingsView, { SettingsViewRef } from "./components/settings/SettingsVie import WelcomeView from "./components/welcome/WelcomeView" import McpView from "./components/mcp/McpView" import PromptsView from "./components/prompts/PromptsView" +import { HumanRelayDialog } from "./components/human-relay/HumanRelayDialog" type Tab = "settings" | "history" | "mcp" | "prompts" | "chat" @@ -28,6 +29,17 @@ const App = () => { const [tab, setTab] = useState("chat") const settingsRef = useRef(null) + // Human Relay Dialog Status + const [humanRelayDialogState, setHumanRelayDialogState] = useState<{ + isOpen: boolean + requestId: string + promptText: string + }>({ + isOpen: false, + requestId: "", + promptText: "", + }) + const switchTab = useCallback((newTab: Tab) => { if (settingsRef.current?.checkUnsaveChanges) { settingsRef.current.checkUnsaveChanges(() => setTab(newTab)) @@ -47,10 +59,36 @@ const App = () => { switchTab(newTab) } } + + // Processing displays human relay dialog messages + if (message.type === "showHumanRelayDialog" && message.requestId && message.promptText) { + setHumanRelayDialogState({ + isOpen: true, + requestId: message.requestId, + promptText: message.promptText, + }) + } }, [switchTab], ) + // Processing Human Relay Dialog Submission + const handleHumanRelaySubmit = (requestId: string, text: string) => { + vscode.postMessage({ + type: "humanRelayResponse", + requestId, + text, + }) + } + + // Handle Human Relay dialog box cancel + const handleHumanRelayCancel = (requestId: string) => { + vscode.postMessage({ + type: "humanRelayCancel", + requestId, + }) + } + useEvent("message", onMessage) useEffect(() => { @@ -60,6 +98,11 @@ const App = () => { } }, [shouldShowAnnouncement]) + // Tell Extension that we are ready to receive messages + useEffect(() => { + vscode.postMessage({ type: "webviewDidLaunch" }) + }, []) + if (!didHydrateState) { return null } @@ -80,6 +123,15 @@ const App = () => { hideAnnouncement={() => setShowAnnouncement(false)} showHistoryView={() => switchTab("history")} /> + {/* Human Relay Dialog */} + setHumanRelayDialogState((prev) => ({ ...prev, isOpen: false }))} + onSubmit={handleHumanRelaySubmit} + onCancel={handleHumanRelayCancel} + /> ) } diff --git a/webview-ui/src/components/human-relay/HumanRelayDialog.tsx b/webview-ui/src/components/human-relay/HumanRelayDialog.tsx new file mode 100644 index 00000000000..ea306d11d70 --- /dev/null +++ b/webview-ui/src/components/human-relay/HumanRelayDialog.tsx @@ -0,0 +1,105 @@ +import * as React from "react" +import { Button } from "../ui/button" +import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle } from "../ui/dialog" +import { Textarea } from "../ui/textarea" +import { useClipboard } from "../ui/hooks" +import { Check, Copy, X } from "lucide-react" + +interface HumanRelayDialogProps { + isOpen: boolean + onClose: () => void + requestId: string + promptText: string + onSubmit: (requestId: string, text: string) => void + onCancel: (requestId: string) => void +} + +/** + * Human Relay Dialog Component + * Displays the prompt text that needs to be copied and provides an input box for the user to paste the AI's response. + */ +export const HumanRelayDialog: React.FC = ({ + isOpen, + onClose, + requestId, + promptText, + onSubmit, + onCancel, +}) => { + const [response, setResponse] = React.useState("") + const { onCopy } = useClipboard(promptText) + const [isCopyClicked, setIsCopyClicked] = React.useState(false) + + // Copy to clipboard and show a success message + const handleCopy = () => { + onCopy() + setIsCopyClicked(true) + setTimeout(() => { + setIsCopyClicked(false) + }, 2000) + } + + // Submit the response + const handleSubmit = (e: React.FormEvent) => { + e.preventDefault() + if (response.trim()) { + onSubmit(requestId, response) + onClose() + } + } + + // Cancel the operation + const handleCancel = () => { + onCancel(requestId) + onClose() + } + + return ( + !open && handleCancel()}> + + + Human Relay - Please Help Copy and Paste Information + + Please copy the text below to the web AI, then paste the AI's response into the input box below. + + + +
+
+