Răsfoiți Sursa

Fix linter errors (#3821)

Chris Estreich 7 luni în urmă
părinte
comite
39cd50b989
44 a modificat fișierele cu 218 adăugiri și 150 ștergeri
  1. 6 0
      .prettierignore
  2. 1 1
      e2e/package.json
  3. 2 2
      e2e/src/suite/index.ts
  4. 4 3
      e2e/src/suite/modes.test.ts
  5. 3 2
      e2e/src/suite/subtasks.test.ts
  6. 3 2
      e2e/src/suite/task.test.ts
  7. 1 1
      packages/build/package.json
  8. 5 2
      packages/build/src/esbuild.ts
  9. 4 2
      packages/build/src/git.ts
  10. 0 3
      pnpm-lock.yaml
  11. 2 1
      src/api/providers/anthropic.ts
  12. 5 1
      src/api/providers/bedrock.ts
  13. 78 83
      src/api/providers/lmstudio.ts
  14. 1 0
      src/api/providers/openai.ts
  15. 5 3
      src/api/providers/openrouter.ts
  16. 2 0
      src/core/assistant-message/__tests__/parseAssistantMessageBenchmark.ts
  17. 2 0
      src/core/diff/strategies/multi-search-replace.ts
  18. 2 1
      src/core/task/Task.ts
  19. 2 0
      src/core/task/__tests__/Task.test.ts
  20. 1 1
      src/core/webview/ClineProvider.ts
  21. 2 0
      src/esbuild.mjs
  22. 13 8
      src/eslint.config.mjs
  23. 18 17
      src/integrations/editor/DiffViewProvider.ts
  24. 4 2
      src/integrations/misc/export-markdown.ts
  25. 5 1
      src/integrations/misc/extract-text.ts
  26. 1 0
      src/integrations/terminal/TerminalProcess.ts
  27. 1 0
      src/integrations/terminal/__tests__/setupTerminalTests.ts
  28. 3 1
      src/jest.config.mjs
  29. 1 2
      src/package.json
  30. 1 0
      src/shared/support-prompt.ts
  31. 4 1
      src/utils/__tests__/git.test.ts
  32. 21 2
      webview-ui/eslint.config.mjs
  33. 1 1
      webview-ui/package.json
  34. 1 0
      webview-ui/src/components/chat/ChatRow.tsx
  35. 1 1
      webview-ui/src/components/chat/TaskHeader.tsx
  36. 2 0
      webview-ui/src/components/chat/__tests__/ChatView.test.tsx
  37. 2 0
      webview-ui/src/components/common/__tests__/CodeBlock.test.tsx
  38. 4 1
      webview-ui/src/components/history/CopyButton.tsx
  39. 0 1
      webview-ui/src/components/settings/providers/OpenRouter.tsx
  40. 1 0
      webview-ui/src/components/ui/command.tsx
  41. 0 1
      webview-ui/src/utils/__tests__/command-validation.test.ts
  42. 1 1
      webview-ui/src/utils/context-mentions.ts
  43. 1 1
      webview-ui/src/utils/highlighter.ts
  44. 1 1
      webview-ui/vite.config.ts

+ 6 - 0
.prettierignore

@@ -0,0 +1,6 @@
+dist
+build
+out
+.next
+.venv
+pnpm-lock.yaml

+ 1 - 1
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",

+ 2 - 2
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.

+ 4 - 3
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[] = []
 

+ 3 - 2
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<string, ClineMessage[]> = {}
 

+ 3 - 2
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[] = []
 

+ 1 - 1
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",

+ 5 - 2
packages/build/src/esbuild.ts

@@ -143,8 +143,8 @@ export function generatePackageJson({
 	overrideJson,
 	substitution,
 }: {
-	packageJson: Record<string, any>
-	overrideJson: Record<string, any>
+	packageJson: Record<string, any> // eslint-disable-line @typescript-eslint/no-explicit-any
+	overrideJson: Record<string, any> // 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<T>(obj: Record<string, any[]>, from: string, to: string, props: string[]): T {
 	return Object.entries(obj).reduce(
 		(acc, [key, ary]) => ({
@@ -187,6 +188,7 @@ function transformArrayRecord<T>(obj: Record<string, any[]>, from: string, to: s
 	)
 }
 
+// eslint-disable-next-line @typescript-eslint/no-explicit-any
 function transformArray<T>(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<T>(arr: any[], from: string, to: string, idProp: string)
 	}))
 }
 
+// eslint-disable-next-line @typescript-eslint/no-explicit-any
 function transformRecord<T>(obj: Record<string, any>, from: string, to: string): T {
 	return Object.entries(obj).reduce(
 		(acc, [key, value]) => ({

+ 4 - 2
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
 }

+ 0 - 3
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

+ 2 - 1
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.

+ 5 - 1
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
 					}
 				}

+ 78 - 83
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),
-	]
-
-	// -------------------------
-	// Track token usage
-	// -------------------------
-	const toContentBlocks = (
-		blocks: Anthropic.Messages.MessageParam[] | string,
-	): Anthropic.Messages.ContentBlockParam[] => {
-		if (typeof blocks === "string") {
-			return [{ type: "text", text: blocks }]
-		}
+		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 }]
+			}
 
-		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
 		}
-		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 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 = ""
+		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,
-		}
+		try {
+			const params: OpenAI.Chat.ChatCompletionCreateParamsStreaming & { draft_model?: string } = {
+				model: this.getModel().id,
+				messages: openAiMessages,
+				temperature: this.options.modelTemperature ?? LMSTUDIO_DEFAULT_TEMPERATURE,
+				stream: true,
+			}
 
-		if (this.options.lmStudioSpeculativeDecodingEnabled && this.options.lmStudioDraftModelId) {
-			params.draft_model = this.options.lmStudioDraftModelId
-		}
+			if (this.options.lmStudioSpeculativeDecodingEnabled && this.options.lmStudioDraftModelId) {
+				params.draft_model = this.options.lmStudioDraftModelId
+			}
 
-		const results = await this.client.chat.completions.create(params)
+			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,
-		)
+			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
+			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
+				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 {

+ 1 - 0
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 {

+ 5 - 3
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

+ 2 - 0
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"

+ 2 - 0
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"

+ 2 - 1
src/core/task/Task.ts

@@ -1212,7 +1212,7 @@ export class Task extends EventEmitter<ClineEvents> {
 							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<ClineEvents> {
 							// Present content to user.
 							presentAssistantMessage(this)
 							break
+						}
 					}
 
 					if (this.abort) {

+ 2 - 0
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
 					},

+ 1 - 1
src/core/webview/ClineProvider.ts

@@ -144,7 +144,7 @@ export class ClineProvider extends EventEmitter<ClineProviderEvents> 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`)

+ 2 - 0
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"
 

+ 13 - 8
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",
+			"@typescript-eslint/no-require-imports": "off",
+			"@typescript-eslint/ban-ts-comment": "off",
 		},
 	},
 	{
-		files: ["i18n/setup.ts", "utils/tts.ts"],
+		files: ["core/assistant-message/presentAssistantMessage.ts", "core/webview/webviewMessageHandler.ts"],
 		rules: {
-			"@typescript-eslint/no-require-imports": "off",
+			"no-case-declarations": "off",
 		},
 	},
 	{
-		files: ["shared/support-prompt.ts"],
+		files: ["__mocks__/**/*.js"],
 		rules: {
-			"no-prototype-builtins": "off",
+			"no-undef": "off",
 		},
 	},
 	{
-		files: ["shared/combineApiRequests.ts", "utils/tts.ts"],
-		rules: {
-			"no-empty": "off",
-		},
+		ignores: ["webview-ui", "out"],
 	},
 ]

+ 18 - 17
src/integrations/editor/DiffViewProvider.ts

@@ -303,22 +303,23 @@ export class DiffViewProvider {
 
 	private async closeAllDiffViews(): Promise<void> {
 		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<vscode.TextEditor> {
@@ -425,7 +426,7 @@ export class DiffViewProvider {
 		return result
 	}
 
-	async reset() : Promise<void> {
+	async reset(): Promise<void> {
 		await this.closeAllDiffViews()
 		this.editType = undefined
 		this.isEditing = false

+ 4 - 2
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]"
 	}

+ 5 - 1
src/integrations/misc/extract-text.ts

@@ -11,7 +11,9 @@ export async function extractTextFromFile(filePath: string): Promise<string> {
 	} 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<string> {
 			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}`)
 			}
+		}
 	}
 }
 

+ 1 - 0
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, ""))
 	}
 

+ 1 - 0
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__: {

+ 3 - 1
src/jest.config.js → 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"],

+ 1 - 2
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",

+ 1 - 0
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]

+ 4 - 1
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 = (

+ 21 - 2
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",
 		},
 	},
 ]

+ 1 - 1
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",

+ 1 - 0
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<ChatRowProps, "onHeightChange"> {}
 
 const ChatRow = memo(

+ 1 - 1
webview-ui/src/components/chat/TaskHeader.tsx

@@ -62,7 +62,7 @@ const TaskHeader = ({
 			<div
 				className={cn(
 					"rounded-xs p-2.5 flex flex-col gap-1.5 relative z-1 border",
-					!!isTaskExpanded
+					isTaskExpanded
 						? "border-vscode-panel-border text-vscode-foreground"
 						: "border-vscode-panel-border/80 text-vscode-foreground/80",
 				)}>

+ 2 - 0
webview-ui/src/components/chat/__tests__/ChatView.test.tsx

@@ -77,7 +77,9 @@ const mockInputRef = React.createRef<HTMLInputElement>()
 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(

+ 2 - 0
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 () => {

+ 4 - 1
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],
 	)

+ 0 - 1
webview-ui/src/components/settings/providers/OpenRouter.tsx

@@ -116,7 +116,6 @@ export const OpenRouter = ({
 						<Trans
 							i18nKey="settings:providers.openRouterTransformsText"
 							components={{
-								// eslint-disable-next-line jsx-a11y/anchor-has-content
 								a: <a href="https://openrouter.ai/docs/transforms" />,
 							}}
 						/>

+ 1 - 0
webview-ui/src/components/ui/command.tsx

@@ -38,6 +38,7 @@ const CommandInput = React.forwardRef<
 	React.ElementRef<typeof CommandPrimitive.Input>,
 	React.ComponentPropsWithoutRef<typeof CommandPrimitive.Input>
 >(({ className, ...props }, ref) => (
+	// eslint-disable-next-line react/no-unknown-property
 	<div className="flex items-center border-b border-vscode-dropdown-border px-3" cmdk-input-wrapper="">
 		<MagnifyingGlassIcon className="mr-2 h-4 w-4 shrink-0 opacity-50" />
 		<CommandPrimitive.Input

+ 0 - 1
webview-ui/src/utils/__tests__/command-validation.test.ts

@@ -1,5 +1,4 @@
 /* eslint-disable no-useless-escape */
-/* eslint-disable no-template-curly-in-string */
 
 // npx jest src/utils/__tests__/command-validation.test.ts
 

+ 1 - 1
webview-ui/src/utils/context-mentions.ts

@@ -255,7 +255,7 @@ export function getContextMenuOptions(
 	// Convert search results to queryItems format
 	const searchResultItems = dynamicSearchResults.map((result) => {
 		// 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

+ 1 - 1
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<Highlighter> | null
 	loadedLanguages: Set<ExtendedLanguage>

+ 1 - 1
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