diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 0000000000..7d8675f8cb --- /dev/null +++ b/.prettierignore @@ -0,0 +1,6 @@ +dist +build +out +.next +.venv +pnpm-lock.yaml diff --git a/e2e/package.json b/e2e/package.json index e3642a18e8..92278b3fa1 100644 --- a/e2e/package.json +++ b/e2e/package.json @@ -2,7 +2,7 @@ "name": "@roo-code/vscode-e2e", "private": true, "scripts": { - "lint": "eslint **/*.ts --max-warnings=0", + "lint": "eslint src --ext=ts --max-warnings=0", "check-types": "tsc --noEmit", "format": "prettier --write src", "test:ci": "pnpm --filter roo-cline build:development && pnpm test:run", diff --git a/e2e/src/suite/index.ts b/e2e/src/suite/index.ts index 5331ef37c3..009b7d2777 100644 --- a/e2e/src/suite/index.ts +++ b/e2e/src/suite/index.ts @@ -8,7 +8,7 @@ import { type RooCodeAPI, Package } from "@roo-code/types" import { waitFor } from "./utils" declare global { - var api: RooCodeAPI + let api: RooCodeAPI } export async function run() { @@ -29,7 +29,7 @@ export async function run() { await vscode.commands.executeCommand(`${Package.name}.SidebarProvider.focus`) await waitFor(() => api.isReady()) - // Expose the API to the tests. + // @ts-expect-error - Expose the API to the tests. globalThis.api = api // Add all the tests to the runner. diff --git a/e2e/src/suite/modes.test.ts b/e2e/src/suite/modes.test.ts index 286ab2ce8c..f022f344a7 100644 --- a/e2e/src/suite/modes.test.ts +++ b/e2e/src/suite/modes.test.ts @@ -1,12 +1,13 @@ import * as assert from "assert" -import type { ClineMessage } from "@roo-code/types" +import type { RooCodeAPI, ClineMessage } from "@roo-code/types" import { waitUntilCompleted } from "./utils" suite("Roo Code Modes", () => { test("Should handle switching modes correctly", async () => { - const api = globalThis.api + // @ts-expect-error - Expose the API to the tests. + const api = globalThis.api as RooCodeAPI /** * Switch modes. @@ -15,7 +16,7 @@ suite("Roo Code Modes", () => { const switchModesPrompt = "For each mode (Architect, Ask, Debug) respond with the mode name and what it specializes in after switching to that mode." - let messages: ClineMessage[] = [] + const messages: ClineMessage[] = [] const modeSwitches: string[] = [] diff --git a/e2e/src/suite/subtasks.test.ts b/e2e/src/suite/subtasks.test.ts index b5ff033d12..00de623f34 100644 --- a/e2e/src/suite/subtasks.test.ts +++ b/e2e/src/suite/subtasks.test.ts @@ -1,12 +1,13 @@ import * as assert from "assert" -import type { ClineMessage } from "@roo-code/types" +import type { RooCodeAPI, ClineMessage } from "@roo-code/types" import { sleep, waitFor, waitUntilCompleted } from "./utils" suite.skip("Roo Code Subtasks", () => { test("Should handle subtask cancellation and resumption correctly", async () => { - const api = globalThis.api + // @ts-expect-error - Expose the API to the tests. + const api = globalThis.api as RooCodeAPI const messages: Record = {} diff --git a/e2e/src/suite/task.test.ts b/e2e/src/suite/task.test.ts index e97c3b4f1e..96fb51fe53 100644 --- a/e2e/src/suite/task.test.ts +++ b/e2e/src/suite/task.test.ts @@ -1,12 +1,13 @@ import * as assert from "assert" -import type { ClineMessage } from "@roo-code/types" +import type { RooCodeAPI, ClineMessage } from "@roo-code/types" import { waitUntilCompleted } from "./utils" suite("Roo Code Task", () => { test("Should handle prompt and response correctly", async () => { - const api = globalThis.api + // @ts-expect-error - Expose the API to the tests. + const api = globalThis.api as RooCodeAPI const messages: ClineMessage[] = [] diff --git a/packages/build/package.json b/packages/build/package.json index e98f455294..578f5e1f33 100644 --- a/packages/build/package.json +++ b/packages/build/package.json @@ -6,7 +6,7 @@ "main": "./dist/index.js", "types": "./src/index.ts", "scripts": { - "lint": "eslint src/**/*.ts --max-warnings=0", + "lint": "eslint src --ext=ts --max-warnings=0", "check-types": "tsc --noEmit", "test": "vitest --globals --run", "build": "tsc", diff --git a/packages/build/src/esbuild.ts b/packages/build/src/esbuild.ts index adb848e673..898b7417a7 100644 --- a/packages/build/src/esbuild.ts +++ b/packages/build/src/esbuild.ts @@ -143,8 +143,8 @@ export function generatePackageJson({ overrideJson, substitution, }: { - packageJson: Record - overrideJson: Record + packageJson: Record // eslint-disable-line @typescript-eslint/no-explicit-any + overrideJson: Record // eslint-disable-line @typescript-eslint/no-explicit-any substitution: [string, string] }) { const { viewsContainers, views, commands, menus, submenus, configuration } = contributesSchema.parse(contributes) @@ -167,6 +167,7 @@ export function generatePackageJson({ } } +// eslint-disable-next-line @typescript-eslint/no-explicit-any function transformArrayRecord(obj: Record, from: string, to: string, props: string[]): T { return Object.entries(obj).reduce( (acc, [key, ary]) => ({ @@ -187,6 +188,7 @@ function transformArrayRecord(obj: Record, from: string, to: s ) } +// eslint-disable-next-line @typescript-eslint/no-explicit-any function transformArray(arr: any[], from: string, to: string, idProp: string): T[] { return arr.map(({ [idProp]: id, ...rest }) => ({ [idProp]: id.replace(from, to), @@ -194,6 +196,7 @@ function transformArray(arr: any[], from: string, to: string, idProp: string) })) } +// eslint-disable-next-line @typescript-eslint/no-explicit-any function transformRecord(obj: Record, from: string, to: string): T { return Object.entries(obj).reduce( (acc, [key, value]) => ({ diff --git a/packages/build/src/git.ts b/packages/build/src/git.ts index 09b2cf6735..bafe96b65d 100644 --- a/packages/build/src/git.ts +++ b/packages/build/src/git.ts @@ -1,11 +1,13 @@ import { execSync } from "child_process" export function getGitSha() { - let gitSha = undefined + let gitSha: string | undefined = undefined try { gitSha = execSync("git rev-parse HEAD").toString().trim() - } catch (e) {} + } catch (_e) { + // Do nothing. + } return gitSha } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index b028cdc06f..146a7b261e 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -426,9 +426,6 @@ importers: ovsx: specifier: 0.10.2 version: 0.10.2 - prettier: - specifier: ^3.4.2 - version: 3.5.3 rimraf: specifier: ^6.0.1 version: 6.0.1 diff --git a/src/api/providers/anthropic.ts b/src/api/providers/anthropic.ts index 5489b32609..dfe5e45922 100644 --- a/src/api/providers/anthropic.ts +++ b/src/api/providers/anthropic.ts @@ -128,7 +128,7 @@ export class AnthropicHandler extends BaseProvider implements SingleCompletionHa for await (const chunk of stream) { switch (chunk.type) { - case "message_start": + case "message_start": { // Tells us cache reads/writes/input/output. const usage = chunk.message.usage @@ -141,6 +141,7 @@ export class AnthropicHandler extends BaseProvider implements SingleCompletionHa } break + } case "message_delta": // Tells us stop_reason, stop_sequence, and output tokens // along the way and at the end of the message. diff --git a/src/api/providers/bedrock.ts b/src/api/providers/bedrock.ts index fe3b5c09e0..2946dd052a 100644 --- a/src/api/providers/bedrock.ts +++ b/src/api/providers/bedrock.ts @@ -161,7 +161,10 @@ export class AwsBedrockHandler extends BaseProvider implements SingleCompletionH if (this.arnInfo.awsUseCrossRegionInference) this.options.awsUseCrossRegionInference = true } - this.options.modelTemperature ?? BEDROCK_DEFAULT_TEMPERATURE + if (!this.options.modelTemperature) { + this.options.modelTemperature = BEDROCK_DEFAULT_TEMPERATURE + } + this.costModelConfig = this.getModel() const clientConfig: BedrockRuntimeClientConfig = { @@ -316,6 +319,7 @@ export class AwsBedrockHandler extends BaseProvider implements SingleCompletionH error: error instanceof Error ? error : String(error), }) } finally { + // eslint-disable-next-line no-unsafe-finally continue } } diff --git a/src/api/providers/lmstudio.ts b/src/api/providers/lmstudio.ts index 0901cb2768..c750c32a26 100644 --- a/src/api/providers/lmstudio.ts +++ b/src/api/providers/lmstudio.ts @@ -25,108 +25,103 @@ export class LmStudioHandler extends BaseProvider implements SingleCompletionHan } override async *createMessage(systemPrompt: string, messages: Anthropic.Messages.MessageParam[]): ApiStream { - const openAiMessages: OpenAI.Chat.ChatCompletionMessageParam[] = [ - { role: "system", content: systemPrompt }, - ...convertToOpenAiMessages(messages), - ] + const openAiMessages: OpenAI.Chat.ChatCompletionMessageParam[] = [ + { role: "system", content: systemPrompt }, + ...convertToOpenAiMessages(messages), + ] - // ------------------------- - // Track token usage - // ------------------------- - const toContentBlocks = ( - blocks: Anthropic.Messages.MessageParam[] | string, - ): Anthropic.Messages.ContentBlockParam[] => { - if (typeof blocks === "string") { - return [{ type: "text", text: blocks }] - } + // ------------------------- + // Track token usage + // ------------------------- + const toContentBlocks = ( + blocks: Anthropic.Messages.MessageParam[] | string, + ): Anthropic.Messages.ContentBlockParam[] => { + if (typeof blocks === "string") { + return [{ type: "text", text: blocks }] + } - const result: Anthropic.Messages.ContentBlockParam[] = [] - for (const msg of blocks) { - if (typeof msg.content === "string") { - result.push({ type: "text", text: msg.content }) - } else if (Array.isArray(msg.content)) { - for (const part of msg.content) { - if (part.type === "text") { - result.push({ type: "text", text: part.text }) + const result: Anthropic.Messages.ContentBlockParam[] = [] + for (const msg of blocks) { + if (typeof msg.content === "string") { + result.push({ type: "text", text: msg.content }) + } else if (Array.isArray(msg.content)) { + for (const part of msg.content) { + if (part.type === "text") { + result.push({ type: "text", text: part.text }) + } } } } - } - return result - } - - let inputTokens = 0 - try { - inputTokens = await this.countTokens([ - { type: "text", text: systemPrompt }, - ...toContentBlocks(messages), - ]) - } catch (err) { - console.error("[LmStudio] Failed to count input tokens:", err) - inputTokens = 0 - } - - let assistantText = "" - - try { - const params: OpenAI.Chat.ChatCompletionCreateParamsStreaming & { draft_model?: string } = { - model: this.getModel().id, - messages: openAiMessages, - temperature: this.options.modelTemperature ?? LMSTUDIO_DEFAULT_TEMPERATURE, - stream: true, + return result } - if (this.options.lmStudioSpeculativeDecodingEnabled && this.options.lmStudioDraftModelId) { - params.draft_model = this.options.lmStudioDraftModelId + let inputTokens = 0 + try { + inputTokens = await this.countTokens([{ type: "text", text: systemPrompt }, ...toContentBlocks(messages)]) + } catch (err) { + console.error("[LmStudio] Failed to count input tokens:", err) + inputTokens = 0 } - const results = await this.client.chat.completions.create(params) + let assistantText = "" - const matcher = new XmlMatcher( - "think", - (chunk) => - ({ - type: chunk.matched ? "reasoning" : "text", - text: chunk.data, - }) as const, - ) + try { + const params: OpenAI.Chat.ChatCompletionCreateParamsStreaming & { draft_model?: string } = { + model: this.getModel().id, + messages: openAiMessages, + temperature: this.options.modelTemperature ?? LMSTUDIO_DEFAULT_TEMPERATURE, + stream: true, + } - for await (const chunk of results) { - const delta = chunk.choices[0]?.delta + if (this.options.lmStudioSpeculativeDecodingEnabled && this.options.lmStudioDraftModelId) { + params.draft_model = this.options.lmStudioDraftModelId + } - if (delta?.content) { - assistantText += delta.content - for (const processedChunk of matcher.update(delta.content)) { - yield processedChunk + const results = await this.client.chat.completions.create(params) + + const matcher = new XmlMatcher( + "think", + (chunk) => + ({ + type: chunk.matched ? "reasoning" : "text", + text: chunk.data, + }) as const, + ) + + for await (const chunk of results) { + const delta = chunk.choices[0]?.delta + + if (delta?.content) { + assistantText += delta.content + for (const processedChunk of matcher.update(delta.content)) { + yield processedChunk + } } } - } - for (const processedChunk of matcher.final()) { - yield processedChunk - } + for (const processedChunk of matcher.final()) { + yield processedChunk + } - - let outputTokens = 0 - try { - outputTokens = await this.countTokens([{ type: "text", text: assistantText }]) - } catch (err) { - console.error("[LmStudio] Failed to count output tokens:", err) - outputTokens = 0 - } + let outputTokens = 0 + try { + outputTokens = await this.countTokens([{ type: "text", text: assistantText }]) + } catch (err) { + console.error("[LmStudio] Failed to count output tokens:", err) + outputTokens = 0 + } - yield { - type: "usage", - inputTokens, - outputTokens, - } as const - } catch (error) { - throw new Error( - "Please check the LM Studio developer logs to debug what went wrong. You may need to load the model with a larger context length to work with Roo Code's prompts.", - ) + yield { + type: "usage", + inputTokens, + outputTokens, + } as const + } catch (error) { + throw new Error( + "Please check the LM Studio developer logs to debug what went wrong. You may need to load the model with a larger context length to work with Roo Code's prompts.", + ) + } } -} - override getModel(): { id: string; info: ModelInfo } { return { diff --git a/src/api/providers/openai.ts b/src/api/providers/openai.ts index 64932b0392..39d26c1544 100644 --- a/src/api/providers/openai.ts +++ b/src/api/providers/openai.ts @@ -19,6 +19,7 @@ import { DEFAULT_HEADERS, DEEP_SEEK_DEFAULT_TEMPERATURE } from "./constants" export const AZURE_AI_INFERENCE_PATH = "/models/chat/completions" +// eslint-disable-next-line @typescript-eslint/no-empty-object-type export interface OpenAiHandlerOptions extends ApiHandlerOptions {} export class OpenAiHandler extends BaseProvider implements SingleCompletionHandler { diff --git a/src/api/providers/openrouter.ts b/src/api/providers/openrouter.ts index 2d9c7f8b8a..88b2729d65 100644 --- a/src/api/providers/openrouter.ts +++ b/src/api/providers/openrouter.ts @@ -99,9 +99,11 @@ export class OpenRouterHandler extends BaseProvider implements SingleCompletionH // https://openrouter.ai/docs/features/prompt-caching if (isCacheAvailable) { - modelId.startsWith("google") - ? addGeminiCacheBreakpoints(systemPrompt, openAiMessages) - : addAnthropicCacheBreakpoints(systemPrompt, openAiMessages) + if (modelId.startsWith("google")) { + addGeminiCacheBreakpoints(systemPrompt, openAiMessages) + } else { + addAnthropicCacheBreakpoints(systemPrompt, openAiMessages) + } } // https://openrouter.ai/docs/transforms diff --git a/src/core/assistant-message/__tests__/parseAssistantMessageBenchmark.ts b/src/core/assistant-message/__tests__/parseAssistantMessageBenchmark.ts index bea161330b..d5450988c9 100644 --- a/src/core/assistant-message/__tests__/parseAssistantMessageBenchmark.ts +++ b/src/core/assistant-message/__tests__/parseAssistantMessageBenchmark.ts @@ -1,3 +1,5 @@ +/* eslint-disable @typescript-eslint/no-unsafe-function-type */ + // node --expose-gc --import tsx src/core/assistant-message/__tests__/parseAssistantMessageBenchmark.ts import { performance } from "perf_hooks" diff --git a/src/core/diff/strategies/multi-search-replace.ts b/src/core/diff/strategies/multi-search-replace.ts index 144b794f75..8897a2f7e8 100644 --- a/src/core/diff/strategies/multi-search-replace.ts +++ b/src/core/diff/strategies/multi-search-replace.ts @@ -1,3 +1,5 @@ +/* eslint-disable no-irregular-whitespace */ + import { distance } from "fastest-levenshtein" import { addLineNumbers, everyLineHasLineNumbers, stripLineNumbers } from "../../../integrations/misc/extract-text" diff --git a/src/core/task/Task.ts b/src/core/task/Task.ts index f315e6bd89..edee92388a 100644 --- a/src/core/task/Task.ts +++ b/src/core/task/Task.ts @@ -1212,7 +1212,7 @@ export class Task extends EventEmitter { cacheReadTokens += chunk.cacheReadTokens ?? 0 totalCost = chunk.totalCost break - case "text": + case "text": { assistantMessage += chunk.text // Parse raw assistant message into content blocks. @@ -1228,6 +1228,7 @@ export class Task extends EventEmitter { // Present content to user. presentAssistantMessage(this) break + } } if (this.abort) { diff --git a/src/core/task/__tests__/Task.test.ts b/src/core/task/__tests__/Task.test.ts index f21499bda0..c472355744 100644 --- a/src/core/task/__tests__/Task.test.ts +++ b/src/core/task/__tests__/Task.test.ts @@ -548,6 +548,7 @@ describe("Cline", () => { // Create a stream that fails on first chunk const mockError = new Error("API Error") const mockFailedStream = { + // eslint-disable-next-line require-yield async *[Symbol.asyncIterator]() { throw mockError }, @@ -672,6 +673,7 @@ describe("Cline", () => { // Create a stream that fails on first chunk const mockError = new Error("API Error") const mockFailedStream = { + // eslint-disable-next-line require-yield async *[Symbol.asyncIterator]() { throw mockError }, diff --git a/src/core/webview/ClineProvider.ts b/src/core/webview/ClineProvider.ts index a6c80f3b75..155609eeeb 100644 --- a/src/core/webview/ClineProvider.ts +++ b/src/core/webview/ClineProvider.ts @@ -144,7 +144,7 @@ export class ClineProvider extends EventEmitter implements } // Pop the top Cline instance from the stack. - var cline = this.clineStack.pop() + let cline = this.clineStack.pop() if (cline) { console.log(`[subtasks] removing task ${cline.taskId}.${cline.instanceId} from stack`) diff --git a/src/esbuild.mjs b/src/esbuild.mjs index 930bc91f3d..67753c5b8a 100644 --- a/src/esbuild.mjs +++ b/src/esbuild.mjs @@ -1,6 +1,8 @@ import * as esbuild from "esbuild" import * as path from "path" import { fileURLToPath } from "url" +import process from "node:process" +import * as console from "node:console" import { copyPaths, copyWasms, copyLocales, setupLocaleWatcher } from "@roo-code/build" diff --git a/src/eslint.config.mjs b/src/eslint.config.mjs index 4d3c0169b2..d0813406d9 100644 --- a/src/eslint.config.mjs +++ b/src/eslint.config.mjs @@ -5,26 +5,31 @@ export default [ ...config, { rules: { + // TODO: These should be fixed and the rules re-enabled. + "no-regex-spaces": "off", + "no-useless-escape": "off", + "no-empty": "off", + "prefer-const": "off", + "@typescript-eslint/no-unused-vars": "off", "@typescript-eslint/no-explicit-any": "off", - }, - }, - { - files: ["i18n/setup.ts", "utils/tts.ts"], - rules: { "@typescript-eslint/no-require-imports": "off", + "@typescript-eslint/ban-ts-comment": "off", }, }, { - files: ["shared/support-prompt.ts"], + files: ["core/assistant-message/presentAssistantMessage.ts", "core/webview/webviewMessageHandler.ts"], rules: { - "no-prototype-builtins": "off", + "no-case-declarations": "off", }, }, { - files: ["shared/combineApiRequests.ts", "utils/tts.ts"], + files: ["__mocks__/**/*.js"], rules: { - "no-empty": "off", + "no-undef": "off", }, }, + { + ignores: ["webview-ui", "out"], + }, ] diff --git a/src/integrations/editor/DiffViewProvider.ts b/src/integrations/editor/DiffViewProvider.ts index bbffe9014b..dc812eab6d 100644 --- a/src/integrations/editor/DiffViewProvider.ts +++ b/src/integrations/editor/DiffViewProvider.ts @@ -303,22 +303,23 @@ export class DiffViewProvider { private async closeAllDiffViews(): Promise { const closeOps = vscode.window.tabGroups.all - .flatMap(group => group.tabs) - .filter( - tab => - tab.input instanceof vscode.TabInputTextDiff && - tab.input.original.scheme === DIFF_VIEW_URI_SCHEME && - !tab.isDirty - ) - .map(tab => - vscode.window.tabGroups.close(tab).then( - () => undefined, - err => { - console.error(`Failed to close diff tab ${tab.label}`, err); - } - )); - - await Promise.all(closeOps); + .flatMap((group) => group.tabs) + .filter( + (tab) => + tab.input instanceof vscode.TabInputTextDiff && + tab.input.original.scheme === DIFF_VIEW_URI_SCHEME && + !tab.isDirty, + ) + .map((tab) => + vscode.window.tabGroups.close(tab).then( + () => undefined, + (err) => { + console.error(`Failed to close diff tab ${tab.label}`, err) + }, + ), + ) + + await Promise.all(closeOps) } private async openDiffEditor(): Promise { @@ -425,7 +426,7 @@ export class DiffViewProvider { return result } - async reset() : Promise { + async reset(): Promise { await this.closeAllDiffViews() this.editType = undefined this.isEditing = false diff --git a/src/integrations/misc/export-markdown.ts b/src/integrations/misc/export-markdown.ts index 962f761e7f..2d493ce50c 100644 --- a/src/integrations/misc/export-markdown.ts +++ b/src/integrations/misc/export-markdown.ts @@ -47,7 +47,7 @@ export function formatContentBlockToMarkdown(block: Anthropic.Messages.ContentBl return block.text case "image": return `[Image]` - case "tool_use": + case "tool_use": { let input: string if (typeof block.input === "object" && block.input !== null) { input = Object.entries(block.input) @@ -57,7 +57,8 @@ export function formatContentBlockToMarkdown(block: Anthropic.Messages.ContentBl input = String(block.input) } return `[Tool Use: ${block.name}]\n${input}` - case "tool_result": + } + case "tool_result": { // For now we're not doing tool name lookup since we don't use tools anymore // const toolName = findToolName(block.tool_use_id, messages) const toolName = "Tool" @@ -70,6 +71,7 @@ export function formatContentBlockToMarkdown(block: Anthropic.Messages.ContentBl } else { return `[${toolName}${block.is_error ? " (Error)" : ""}]` } + } default: return "[Unexpected content type]" } diff --git a/src/integrations/misc/extract-text.ts b/src/integrations/misc/extract-text.ts index e257e1c8e3..f2f1d094b6 100644 --- a/src/integrations/misc/extract-text.ts +++ b/src/integrations/misc/extract-text.ts @@ -11,7 +11,9 @@ export async function extractTextFromFile(filePath: string): Promise { } catch (error) { throw new Error(`File not found: ${filePath}`) } + const fileExtension = path.extname(filePath).toLowerCase() + switch (fileExtension) { case ".pdf": return extractTextFromPDF(filePath) @@ -19,13 +21,15 @@ export async function extractTextFromFile(filePath: string): Promise { return extractTextFromDOCX(filePath) case ".ipynb": return extractTextFromIPYNB(filePath) - default: + default: { const isBinary = await isBinaryFile(filePath).catch(() => false) + if (!isBinary) { return addLineNumbers(await fs.readFile(filePath, "utf8")) } else { throw new Error(`Cannot read text for file type: ${fileExtension}`) } + } } } diff --git a/src/integrations/terminal/TerminalProcess.ts b/src/integrations/terminal/TerminalProcess.ts index b027af4cf8..eb0424fe8d 100644 --- a/src/integrations/terminal/TerminalProcess.ts +++ b/src/integrations/terminal/TerminalProcess.ts @@ -392,6 +392,7 @@ export class TerminalProcess extends BaseTerminalProcess { // should be carefully considered to ensure they only remove control codes and don't // alter the actual content or behavior of the output stream. private removeEscapeSequences(str: string): string { + // eslint-disable-next-line no-control-regex return stripAnsi(str.replace(/\x1b\]633;[^\x07]+\x07/gs, "").replace(/\x1b\]133;[^\x07]+\x07/gs, "")) } diff --git a/src/integrations/terminal/__tests__/setupTerminalTests.ts b/src/integrations/terminal/__tests__/setupTerminalTests.ts index 57d87624e3..5a5db9871b 100644 --- a/src/integrations/terminal/__tests__/setupTerminalTests.ts +++ b/src/integrations/terminal/__tests__/setupTerminalTests.ts @@ -24,6 +24,7 @@ const hasPwsh = isPowerShellCoreAvailable() // Define interface for global test environment declare global { + // eslint-disable-next-line @typescript-eslint/no-namespace namespace NodeJS { interface Global { __TEST_ENV__: { diff --git a/src/jest.config.js b/src/jest.config.mjs similarity index 97% rename from src/jest.config.js rename to src/jest.config.mjs index 63434b68d5..469988287a 100644 --- a/src/jest.config.js +++ b/src/jest.config.mjs @@ -1,5 +1,7 @@ +import process from "node:process" + /** @type {import('ts-jest').JestConfigWithTsJest} */ -module.exports = { +export default { preset: "ts-jest", testEnvironment: "node", moduleFileExtensions: ["ts", "tsx", "js", "jsx", "json", "node"], diff --git a/src/package.json b/src/package.json index c804eb6441..fea841cea8 100644 --- a/src/package.json +++ b/src/package.json @@ -318,7 +318,7 @@ } }, "scripts": { - "lint": "eslint **/*.ts --max-warnings=0", + "lint": "eslint . --ext=ts --max-warnings=0", "check-types": "tsc --noEmit", "pretest": "pnpm bundle", "test": "jest -w=40% && vitest run", @@ -429,7 +429,6 @@ "nock": "^14.0.4", "npm-run-all2": "^8.0.1", "ovsx": "0.10.2", - "prettier": "^3.4.2", "rimraf": "^6.0.1", "ts-jest": "^29.2.5", "tsup": "^8.4.0", diff --git a/src/shared/support-prompt.ts b/src/shared/support-prompt.ts index 4f9751e7a8..1767a20753 100644 --- a/src/shared/support-prompt.ts +++ b/src/shared/support-prompt.ts @@ -12,6 +12,7 @@ export const createPrompt = (template: string, params: PromptParams): string => return template.replace(/\${(.*?)}/g, (_, key) => { if (key === "diagnosticText") { return generateDiagnosticText(params["diagnostics"] as any[]) + // eslint-disable-next-line no-prototype-builtins } else if (params.hasOwnProperty(key)) { // Ensure the value is treated as a string for replacement const value = params[key] diff --git a/src/utils/__tests__/git.test.ts b/src/utils/__tests__/git.test.ts index f2814339fc..68ed1f30b6 100644 --- a/src/utils/__tests__/git.test.ts +++ b/src/utils/__tests__/git.test.ts @@ -1,6 +1,9 @@ -import { jest } from "@jest/globals" +/* eslint-disable @typescript-eslint/no-unsafe-function-type */ + import { ExecException } from "child_process" +import { jest } from "@jest/globals" + import { searchCommits, getCommitInfo, getWorkingState } from "../git" type ExecFunction = ( diff --git a/webview-ui/eslint.config.mjs b/webview-ui/eslint.config.mjs index 7dfbc973c2..208700018b 100644 --- a/webview-ui/eslint.config.mjs +++ b/webview-ui/eslint.config.mjs @@ -7,12 +7,31 @@ export default [ rules: { "@typescript-eslint/no-unused-vars": "off", "@typescript-eslint/no-explicit-any": "off", + "react/prop-types": "off", + "react/display-name": "off", }, }, { - files: ["src/utils/context-mentions.ts", "src/utils/highlighter.ts"], + files: ["src/components/chat/ChatRow.tsx", "src/components/settings/ModelInfoView.tsx"], rules: { - "prefer-const": "off", + "react/jsx-key": "off", + }, + }, + { + files: [ + "src/components/chat/ChatRow.tsx", + "src/components/chat/ChatView.tsx", + "src/components/chat/BrowserSessionRow.tsx", + "src/components/history/useTaskSearch.ts", + ], + rules: { + "no-case-declarations": "off", + }, + }, + { + files: ["src/__mocks__/**/*.js"], + rules: { + "no-undef": "off", }, }, ] diff --git a/webview-ui/package.json b/webview-ui/package.json index faacb27cc8..cb9e5c91e3 100644 --- a/webview-ui/package.json +++ b/webview-ui/package.json @@ -3,7 +3,7 @@ "private": true, "type": "module", "scripts": { - "lint": "eslint src/**/*.{ts,tsx} --max-warnings=0", + "lint": "eslint src --ext=ts,tsx --max-warnings=0", "check-types": "tsc", "test": "jest -w=40%", "format": "prettier --write src", diff --git a/webview-ui/src/components/chat/ChatRow.tsx b/webview-ui/src/components/chat/ChatRow.tsx index 33d6807202..328e917a4b 100644 --- a/webview-ui/src/components/chat/ChatRow.tsx +++ b/webview-ui/src/components/chat/ChatRow.tsx @@ -46,6 +46,7 @@ interface ChatRowProps { onSuggestionClick?: (answer: string, event?: React.MouseEvent) => void } +// eslint-disable-next-line @typescript-eslint/no-empty-object-type interface ChatRowContentProps extends Omit {} const ChatRow = memo( diff --git a/webview-ui/src/components/chat/TaskHeader.tsx b/webview-ui/src/components/chat/TaskHeader.tsx index 5270db1107..61ef6a8f91 100644 --- a/webview-ui/src/components/chat/TaskHeader.tsx +++ b/webview-ui/src/components/chat/TaskHeader.tsx @@ -62,7 +62,7 @@ const TaskHeader = ({
diff --git a/webview-ui/src/components/chat/__tests__/ChatView.test.tsx b/webview-ui/src/components/chat/__tests__/ChatView.test.tsx index 1adffb298f..5b618378c4 100644 --- a/webview-ui/src/components/chat/__tests__/ChatView.test.tsx +++ b/webview-ui/src/components/chat/__tests__/ChatView.test.tsx @@ -77,7 +77,9 @@ const mockInputRef = React.createRef() const mockFocus = jest.fn() jest.mock("../ChatTextArea", () => { + // eslint-disable-next-line @typescript-eslint/no-require-imports const mockReact = require("react") + return { __esModule: true, default: mockReact.forwardRef(function MockChatTextArea( diff --git a/webview-ui/src/components/common/__tests__/CodeBlock.test.tsx b/webview-ui/src/components/common/__tests__/CodeBlock.test.tsx index 28576c3c7d..5c84587110 100644 --- a/webview-ui/src/components/common/__tests__/CodeBlock.test.tsx +++ b/webview-ui/src/components/common/__tests__/CodeBlock.test.tsx @@ -140,6 +140,7 @@ describe("CodeBlock", () => { it("handles WASM loading errors", async () => { const mockError = new Error("WASM load failed") + // eslint-disable-next-line @typescript-eslint/no-require-imports const highlighterUtil = require("../../../utils/highlighter") highlighterUtil.getHighlighter.mockRejectedValueOnce(mockError) @@ -163,6 +164,7 @@ describe("CodeBlock", () => { it("verifies highlighter utility is used correctly", async () => { const code = "const x = 1;" + // eslint-disable-next-line @typescript-eslint/no-require-imports const highlighterUtil = require("../../../utils/highlighter") await act(async () => { diff --git a/webview-ui/src/components/history/CopyButton.tsx b/webview-ui/src/components/history/CopyButton.tsx index 2ac8d2157e..b6fff490a7 100644 --- a/webview-ui/src/components/history/CopyButton.tsx +++ b/webview-ui/src/components/history/CopyButton.tsx @@ -19,7 +19,10 @@ export const CopyButton = ({ itemTask }: CopyButtonProps) => { const tempDiv = document.createElement("div") tempDiv.innerHTML = itemTask const text = tempDiv.textContent || tempDiv.innerText || "" - !isCopied && copy(text) + + if (!isCopied) { + copy(text) + } }, [isCopied, copy, itemTask], ) diff --git a/webview-ui/src/components/settings/providers/OpenRouter.tsx b/webview-ui/src/components/settings/providers/OpenRouter.tsx index 7bb7c30095..35cf34b6c9 100644 --- a/webview-ui/src/components/settings/providers/OpenRouter.tsx +++ b/webview-ui/src/components/settings/providers/OpenRouter.tsx @@ -116,7 +116,6 @@ export const OpenRouter = ({ , }} /> diff --git a/webview-ui/src/components/ui/command.tsx b/webview-ui/src/components/ui/command.tsx index b69e8e53a9..57735d1194 100644 --- a/webview-ui/src/components/ui/command.tsx +++ b/webview-ui/src/components/ui/command.tsx @@ -38,6 +38,7 @@ const CommandInput = React.forwardRef< React.ElementRef, React.ComponentPropsWithoutRef >(({ className, ...props }, ref) => ( + // eslint-disable-next-line react/no-unknown-property
{ // Ensure paths start with / for consistency - let formattedPath = result.path.startsWith("/") ? result.path : `/${result.path}` + const formattedPath = result.path.startsWith("/") ? result.path : `/${result.path}` // For display purposes, we don't escape spaces in the label or description const displayPath = formattedPath diff --git a/webview-ui/src/utils/highlighter.ts b/webview-ui/src/utils/highlighter.ts index 1d2bb6e5dd..a77b493dbd 100644 --- a/webview-ui/src/utils/highlighter.ts +++ b/webview-ui/src/utils/highlighter.ts @@ -128,7 +128,7 @@ const LANGUAGE_LOAD_DELAY = 0 const initialLanguages: BundledLanguage[] = ["shell", "log"] // Singleton state -let state: { +const state: { instance: Highlighter | null instanceInitPromise: Promise | null loadedLanguages: Set diff --git a/webview-ui/vite.config.ts b/webview-ui/vite.config.ts index 35bc6912d8..46afaf06dd 100644 --- a/webview-ui/vite.config.ts +++ b/webview-ui/vite.config.ts @@ -12,7 +12,7 @@ function getGitSha() { try { gitSha = execSync("git rev-parse HEAD").toString().trim() } catch (_error) { - // NO-OP + // Do nothing. } return gitSha