ソースを参照

Fix linter errors (#3821)

Chris Estreich 7 ヶ月 前
コミット
39cd50b989
44 ファイル変更218 行追加150 行削除
  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",
 	"name": "@roo-code/vscode-e2e",
 	"private": true,
 	"private": true,
 	"scripts": {
 	"scripts": {
-		"lint": "eslint **/*.ts --max-warnings=0",
+		"lint": "eslint src --ext=ts --max-warnings=0",
 		"check-types": "tsc --noEmit",
 		"check-types": "tsc --noEmit",
 		"format": "prettier --write src",
 		"format": "prettier --write src",
 		"test:ci": "pnpm --filter roo-cline build:development && pnpm test:run",
 		"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"
 import { waitFor } from "./utils"
 
 
 declare global {
 declare global {
-	var api: RooCodeAPI
+	let api: RooCodeAPI
 }
 }
 
 
 export async function run() {
 export async function run() {
@@ -29,7 +29,7 @@ export async function run() {
 	await vscode.commands.executeCommand(`${Package.name}.SidebarProvider.focus`)
 	await vscode.commands.executeCommand(`${Package.name}.SidebarProvider.focus`)
 	await waitFor(() => api.isReady())
 	await waitFor(() => api.isReady())
 
 
-	// Expose the API to the tests.
+	// @ts-expect-error - Expose the API to the tests.
 	globalThis.api = api
 	globalThis.api = api
 
 
 	// Add all the tests to the runner.
 	// 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 * as assert from "assert"
 
 
-import type { ClineMessage } from "@roo-code/types"
+import type { RooCodeAPI, ClineMessage } from "@roo-code/types"
 
 
 import { waitUntilCompleted } from "./utils"
 import { waitUntilCompleted } from "./utils"
 
 
 suite("Roo Code Modes", () => {
 suite("Roo Code Modes", () => {
 	test("Should handle switching modes correctly", async () => {
 	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.
 		 * Switch modes.
@@ -15,7 +16,7 @@ suite("Roo Code Modes", () => {
 		const switchModesPrompt =
 		const switchModesPrompt =
 			"For each mode (Architect, Ask, Debug) respond with the mode name and what it specializes in after switching to that mode."
 			"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[] = []
 		const modeSwitches: string[] = []
 
 

+ 3 - 2
e2e/src/suite/subtasks.test.ts

@@ -1,12 +1,13 @@
 import * as assert from "assert"
 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"
 import { sleep, waitFor, waitUntilCompleted } from "./utils"
 
 
 suite.skip("Roo Code Subtasks", () => {
 suite.skip("Roo Code Subtasks", () => {
 	test("Should handle subtask cancellation and resumption correctly", async () => {
 	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[]> = {}
 		const messages: Record<string, ClineMessage[]> = {}
 
 

+ 3 - 2
e2e/src/suite/task.test.ts

@@ -1,12 +1,13 @@
 import * as assert from "assert"
 import * as assert from "assert"
 
 
-import type { ClineMessage } from "@roo-code/types"
+import type { RooCodeAPI, ClineMessage } from "@roo-code/types"
 
 
 import { waitUntilCompleted } from "./utils"
 import { waitUntilCompleted } from "./utils"
 
 
 suite("Roo Code Task", () => {
 suite("Roo Code Task", () => {
 	test("Should handle prompt and response correctly", async () => {
 	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[] = []
 		const messages: ClineMessage[] = []
 
 

+ 1 - 1
packages/build/package.json

@@ -6,7 +6,7 @@
 	"main": "./dist/index.js",
 	"main": "./dist/index.js",
 	"types": "./src/index.ts",
 	"types": "./src/index.ts",
 	"scripts": {
 	"scripts": {
-		"lint": "eslint src/**/*.ts --max-warnings=0",
+		"lint": "eslint src --ext=ts --max-warnings=0",
 		"check-types": "tsc --noEmit",
 		"check-types": "tsc --noEmit",
 		"test": "vitest --globals --run",
 		"test": "vitest --globals --run",
 		"build": "tsc",
 		"build": "tsc",

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

@@ -143,8 +143,8 @@ export function generatePackageJson({
 	overrideJson,
 	overrideJson,
 	substitution,
 	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]
 	substitution: [string, string]
 }) {
 }) {
 	const { viewsContainers, views, commands, menus, submenus, configuration } = contributesSchema.parse(contributes)
 	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 {
 function transformArrayRecord<T>(obj: Record<string, any[]>, from: string, to: string, props: string[]): T {
 	return Object.entries(obj).reduce(
 	return Object.entries(obj).reduce(
 		(acc, [key, ary]) => ({
 		(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[] {
 function transformArray<T>(arr: any[], from: string, to: string, idProp: string): T[] {
 	return arr.map(({ [idProp]: id, ...rest }) => ({
 	return arr.map(({ [idProp]: id, ...rest }) => ({
 		[idProp]: id.replace(from, to),
 		[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 {
 function transformRecord<T>(obj: Record<string, any>, from: string, to: string): T {
 	return Object.entries(obj).reduce(
 	return Object.entries(obj).reduce(
 		(acc, [key, value]) => ({
 		(acc, [key, value]) => ({

+ 4 - 2
packages/build/src/git.ts

@@ -1,11 +1,13 @@
 import { execSync } from "child_process"
 import { execSync } from "child_process"
 
 
 export function getGitSha() {
 export function getGitSha() {
-	let gitSha = undefined
+	let gitSha: string | undefined = undefined
 
 
 	try {
 	try {
 		gitSha = execSync("git rev-parse HEAD").toString().trim()
 		gitSha = execSync("git rev-parse HEAD").toString().trim()
-	} catch (e) {}
+	} catch (_e) {
+		// Do nothing.
+	}
 
 
 	return gitSha
 	return gitSha
 }
 }

+ 0 - 3
pnpm-lock.yaml

@@ -426,9 +426,6 @@ importers:
       ovsx:
       ovsx:
         specifier: 0.10.2
         specifier: 0.10.2
         version: 0.10.2
         version: 0.10.2
-      prettier:
-        specifier: ^3.4.2
-        version: 3.5.3
       rimraf:
       rimraf:
         specifier: ^6.0.1
         specifier: ^6.0.1
         version: 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) {
 		for await (const chunk of stream) {
 			switch (chunk.type) {
 			switch (chunk.type) {
-				case "message_start":
+				case "message_start": {
 					// Tells us cache reads/writes/input/output.
 					// Tells us cache reads/writes/input/output.
 					const usage = chunk.message.usage
 					const usage = chunk.message.usage
 
 
@@ -141,6 +141,7 @@ export class AnthropicHandler extends BaseProvider implements SingleCompletionHa
 					}
 					}
 
 
 					break
 					break
+				}
 				case "message_delta":
 				case "message_delta":
 					// Tells us stop_reason, stop_sequence, and output tokens
 					// Tells us stop_reason, stop_sequence, and output tokens
 					// along the way and at the end of the message.
 					// 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
 			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()
 		this.costModelConfig = this.getModel()
 
 
 		const clientConfig: BedrockRuntimeClientConfig = {
 		const clientConfig: BedrockRuntimeClientConfig = {
@@ -316,6 +319,7 @@ export class AwsBedrockHandler extends BaseProvider implements SingleCompletionH
 							error: error instanceof Error ? error : String(error),
 							error: error instanceof Error ? error : String(error),
 						})
 						})
 					} finally {
 					} finally {
+						// eslint-disable-next-line no-unsafe-finally
 						continue
 						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 {
 	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 } {
 	override getModel(): { id: string; info: ModelInfo } {
 		return {
 		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"
 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 interface OpenAiHandlerOptions extends ApiHandlerOptions {}
 
 
 export class OpenAiHandler extends BaseProvider implements SingleCompletionHandler {
 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
 		// https://openrouter.ai/docs/features/prompt-caching
 		if (isCacheAvailable) {
 		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
 		// 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
 // node --expose-gc --import tsx src/core/assistant-message/__tests__/parseAssistantMessageBenchmark.ts
 
 
 import { performance } from "perf_hooks"
 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 { distance } from "fastest-levenshtein"
 
 
 import { addLineNumbers, everyLineHasLineNumbers, stripLineNumbers } from "../../../integrations/misc/extract-text"
 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
 							cacheReadTokens += chunk.cacheReadTokens ?? 0
 							totalCost = chunk.totalCost
 							totalCost = chunk.totalCost
 							break
 							break
-						case "text":
+						case "text": {
 							assistantMessage += chunk.text
 							assistantMessage += chunk.text
 
 
 							// Parse raw assistant message into content blocks.
 							// Parse raw assistant message into content blocks.
@@ -1228,6 +1228,7 @@ export class Task extends EventEmitter<ClineEvents> {
 							// Present content to user.
 							// Present content to user.
 							presentAssistantMessage(this)
 							presentAssistantMessage(this)
 							break
 							break
+						}
 					}
 					}
 
 
 					if (this.abort) {
 					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
 				// Create a stream that fails on first chunk
 				const mockError = new Error("API Error")
 				const mockError = new Error("API Error")
 				const mockFailedStream = {
 				const mockFailedStream = {
+					// eslint-disable-next-line require-yield
 					async *[Symbol.asyncIterator]() {
 					async *[Symbol.asyncIterator]() {
 						throw mockError
 						throw mockError
 					},
 					},
@@ -672,6 +673,7 @@ describe("Cline", () => {
 				// Create a stream that fails on first chunk
 				// Create a stream that fails on first chunk
 				const mockError = new Error("API Error")
 				const mockError = new Error("API Error")
 				const mockFailedStream = {
 				const mockFailedStream = {
+					// eslint-disable-next-line require-yield
 					async *[Symbol.asyncIterator]() {
 					async *[Symbol.asyncIterator]() {
 						throw mockError
 						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.
 		// Pop the top Cline instance from the stack.
-		var cline = this.clineStack.pop()
+		let cline = this.clineStack.pop()
 
 
 		if (cline) {
 		if (cline) {
 			console.log(`[subtasks] removing task ${cline.taskId}.${cline.instanceId} from stack`)
 			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 esbuild from "esbuild"
 import * as path from "path"
 import * as path from "path"
 import { fileURLToPath } from "url"
 import { fileURLToPath } from "url"
+import process from "node:process"
+import * as console from "node:console"
 
 
 import { copyPaths, copyWasms, copyLocales, setupLocaleWatcher } from "@roo-code/build"
 import { copyPaths, copyWasms, copyLocales, setupLocaleWatcher } from "@roo-code/build"
 
 

+ 13 - 8
src/eslint.config.mjs

@@ -5,26 +5,31 @@ export default [
 	...config,
 	...config,
 	{
 	{
 		rules: {
 		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-unused-vars": "off",
 			"@typescript-eslint/no-explicit-any": "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: {
 		rules: {
-			"@typescript-eslint/no-require-imports": "off",
+			"no-case-declarations": "off",
 		},
 		},
 	},
 	},
 	{
 	{
-		files: ["shared/support-prompt.ts"],
+		files: ["__mocks__/**/*.js"],
 		rules: {
 		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> {
 	private async closeAllDiffViews(): Promise<void> {
 		const closeOps = vscode.window.tabGroups.all
 		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> {
 	private async openDiffEditor(): Promise<vscode.TextEditor> {
@@ -425,7 +426,7 @@ export class DiffViewProvider {
 		return result
 		return result
 	}
 	}
 
 
-	async reset() : Promise<void> {
+	async reset(): Promise<void> {
 		await this.closeAllDiffViews()
 		await this.closeAllDiffViews()
 		this.editType = undefined
 		this.editType = undefined
 		this.isEditing = false
 		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
 			return block.text
 		case "image":
 		case "image":
 			return `[Image]`
 			return `[Image]`
-		case "tool_use":
+		case "tool_use": {
 			let input: string
 			let input: string
 			if (typeof block.input === "object" && block.input !== null) {
 			if (typeof block.input === "object" && block.input !== null) {
 				input = Object.entries(block.input)
 				input = Object.entries(block.input)
@@ -57,7 +57,8 @@ export function formatContentBlockToMarkdown(block: Anthropic.Messages.ContentBl
 				input = String(block.input)
 				input = String(block.input)
 			}
 			}
 			return `[Tool Use: ${block.name}]\n${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
 			// 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 = findToolName(block.tool_use_id, messages)
 			const toolName = "Tool"
 			const toolName = "Tool"
@@ -70,6 +71,7 @@ export function formatContentBlockToMarkdown(block: Anthropic.Messages.ContentBl
 			} else {
 			} else {
 				return `[${toolName}${block.is_error ? " (Error)" : ""}]`
 				return `[${toolName}${block.is_error ? " (Error)" : ""}]`
 			}
 			}
+		}
 		default:
 		default:
 			return "[Unexpected content type]"
 			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) {
 	} catch (error) {
 		throw new Error(`File not found: ${filePath}`)
 		throw new Error(`File not found: ${filePath}`)
 	}
 	}
+
 	const fileExtension = path.extname(filePath).toLowerCase()
 	const fileExtension = path.extname(filePath).toLowerCase()
+
 	switch (fileExtension) {
 	switch (fileExtension) {
 		case ".pdf":
 		case ".pdf":
 			return extractTextFromPDF(filePath)
 			return extractTextFromPDF(filePath)
@@ -19,13 +21,15 @@ export async function extractTextFromFile(filePath: string): Promise<string> {
 			return extractTextFromDOCX(filePath)
 			return extractTextFromDOCX(filePath)
 		case ".ipynb":
 		case ".ipynb":
 			return extractTextFromIPYNB(filePath)
 			return extractTextFromIPYNB(filePath)
-		default:
+		default: {
 			const isBinary = await isBinaryFile(filePath).catch(() => false)
 			const isBinary = await isBinaryFile(filePath).catch(() => false)
+
 			if (!isBinary) {
 			if (!isBinary) {
 				return addLineNumbers(await fs.readFile(filePath, "utf8"))
 				return addLineNumbers(await fs.readFile(filePath, "utf8"))
 			} else {
 			} else {
 				throw new Error(`Cannot read text for file type: ${fileExtension}`)
 				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
 	// should be carefully considered to ensure they only remove control codes and don't
 	// alter the actual content or behavior of the output stream.
 	// alter the actual content or behavior of the output stream.
 	private removeEscapeSequences(str: string): string {
 	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, ""))
 		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
 // Define interface for global test environment
 declare global {
 declare global {
+	// eslint-disable-next-line @typescript-eslint/no-namespace
 	namespace NodeJS {
 	namespace NodeJS {
 		interface Global {
 		interface Global {
 			__TEST_ENV__: {
 			__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} */
 /** @type {import('ts-jest').JestConfigWithTsJest} */
-module.exports = {
+export default {
 	preset: "ts-jest",
 	preset: "ts-jest",
 	testEnvironment: "node",
 	testEnvironment: "node",
 	moduleFileExtensions: ["ts", "tsx", "js", "jsx", "json", "node"],
 	moduleFileExtensions: ["ts", "tsx", "js", "jsx", "json", "node"],

+ 1 - 2
src/package.json

@@ -318,7 +318,7 @@
 		}
 		}
 	},
 	},
 	"scripts": {
 	"scripts": {
-		"lint": "eslint **/*.ts --max-warnings=0",
+		"lint": "eslint . --ext=ts --max-warnings=0",
 		"check-types": "tsc --noEmit",
 		"check-types": "tsc --noEmit",
 		"pretest": "pnpm bundle",
 		"pretest": "pnpm bundle",
 		"test": "jest -w=40% && vitest run",
 		"test": "jest -w=40% && vitest run",
@@ -429,7 +429,6 @@
 		"nock": "^14.0.4",
 		"nock": "^14.0.4",
 		"npm-run-all2": "^8.0.1",
 		"npm-run-all2": "^8.0.1",
 		"ovsx": "0.10.2",
 		"ovsx": "0.10.2",
-		"prettier": "^3.4.2",
 		"rimraf": "^6.0.1",
 		"rimraf": "^6.0.1",
 		"ts-jest": "^29.2.5",
 		"ts-jest": "^29.2.5",
 		"tsup": "^8.4.0",
 		"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) => {
 	return template.replace(/\${(.*?)}/g, (_, key) => {
 		if (key === "diagnosticText") {
 		if (key === "diagnosticText") {
 			return generateDiagnosticText(params["diagnostics"] as any[])
 			return generateDiagnosticText(params["diagnostics"] as any[])
+			// eslint-disable-next-line no-prototype-builtins
 		} else if (params.hasOwnProperty(key)) {
 		} else if (params.hasOwnProperty(key)) {
 			// Ensure the value is treated as a string for replacement
 			// Ensure the value is treated as a string for replacement
 			const value = params[key]
 			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 { ExecException } from "child_process"
 
 
+import { jest } from "@jest/globals"
+
 import { searchCommits, getCommitInfo, getWorkingState } from "../git"
 import { searchCommits, getCommitInfo, getWorkingState } from "../git"
 
 
 type ExecFunction = (
 type ExecFunction = (

+ 21 - 2
webview-ui/eslint.config.mjs

@@ -7,12 +7,31 @@ export default [
 		rules: {
 		rules: {
 			"@typescript-eslint/no-unused-vars": "off",
 			"@typescript-eslint/no-unused-vars": "off",
 			"@typescript-eslint/no-explicit-any": "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: {
 		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,
 	"private": true,
 	"type": "module",
 	"type": "module",
 	"scripts": {
 	"scripts": {
-		"lint": "eslint src/**/*.{ts,tsx} --max-warnings=0",
+		"lint": "eslint src --ext=ts,tsx --max-warnings=0",
 		"check-types": "tsc",
 		"check-types": "tsc",
 		"test": "jest -w=40%",
 		"test": "jest -w=40%",
 		"format": "prettier --write src",
 		"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
 	onSuggestionClick?: (answer: string, event?: React.MouseEvent) => void
 }
 }
 
 
+// eslint-disable-next-line @typescript-eslint/no-empty-object-type
 interface ChatRowContentProps extends Omit<ChatRowProps, "onHeightChange"> {}
 interface ChatRowContentProps extends Omit<ChatRowProps, "onHeightChange"> {}
 
 
 const ChatRow = memo(
 const ChatRow = memo(

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

@@ -62,7 +62,7 @@ const TaskHeader = ({
 			<div
 			<div
 				className={cn(
 				className={cn(
 					"rounded-xs p-2.5 flex flex-col gap-1.5 relative z-1 border",
 					"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 text-vscode-foreground"
 						: "border-vscode-panel-border/80 text-vscode-foreground/80",
 						: "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()
 const mockFocus = jest.fn()
 
 
 jest.mock("../ChatTextArea", () => {
 jest.mock("../ChatTextArea", () => {
+	// eslint-disable-next-line @typescript-eslint/no-require-imports
 	const mockReact = require("react")
 	const mockReact = require("react")
+
 	return {
 	return {
 		__esModule: true,
 		__esModule: true,
 		default: mockReact.forwardRef(function MockChatTextArea(
 		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 () => {
 	it("handles WASM loading errors", async () => {
 		const mockError = new Error("WASM load failed")
 		const mockError = new Error("WASM load failed")
+		// eslint-disable-next-line @typescript-eslint/no-require-imports
 		const highlighterUtil = require("../../../utils/highlighter")
 		const highlighterUtil = require("../../../utils/highlighter")
 		highlighterUtil.getHighlighter.mockRejectedValueOnce(mockError)
 		highlighterUtil.getHighlighter.mockRejectedValueOnce(mockError)
 
 
@@ -163,6 +164,7 @@ describe("CodeBlock", () => {
 
 
 	it("verifies highlighter utility is used correctly", async () => {
 	it("verifies highlighter utility is used correctly", async () => {
 		const code = "const x = 1;"
 		const code = "const x = 1;"
+		// eslint-disable-next-line @typescript-eslint/no-require-imports
 		const highlighterUtil = require("../../../utils/highlighter")
 		const highlighterUtil = require("../../../utils/highlighter")
 
 
 		await act(async () => {
 		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")
 			const tempDiv = document.createElement("div")
 			tempDiv.innerHTML = itemTask
 			tempDiv.innerHTML = itemTask
 			const text = tempDiv.textContent || tempDiv.innerText || ""
 			const text = tempDiv.textContent || tempDiv.innerText || ""
-			!isCopied && copy(text)
+
+			if (!isCopied) {
+				copy(text)
+			}
 		},
 		},
 		[isCopied, copy, itemTask],
 		[isCopied, copy, itemTask],
 	)
 	)

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

@@ -116,7 +116,6 @@ export const OpenRouter = ({
 						<Trans
 						<Trans
 							i18nKey="settings:providers.openRouterTransformsText"
 							i18nKey="settings:providers.openRouterTransformsText"
 							components={{
 							components={{
-								// eslint-disable-next-line jsx-a11y/anchor-has-content
 								a: <a href="https://openrouter.ai/docs/transforms" />,
 								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.ElementRef<typeof CommandPrimitive.Input>,
 	React.ComponentPropsWithoutRef<typeof CommandPrimitive.Input>
 	React.ComponentPropsWithoutRef<typeof CommandPrimitive.Input>
 >(({ className, ...props }, ref) => (
 >(({ 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="">
 	<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" />
 		<MagnifyingGlassIcon className="mr-2 h-4 w-4 shrink-0 opacity-50" />
 		<CommandPrimitive.Input
 		<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-useless-escape */
-/* eslint-disable no-template-curly-in-string */
 
 
 // npx jest src/utils/__tests__/command-validation.test.ts
 // 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
 	// Convert search results to queryItems format
 	const searchResultItems = dynamicSearchResults.map((result) => {
 	const searchResultItems = dynamicSearchResults.map((result) => {
 		// Ensure paths start with / for consistency
 		// 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
 		// For display purposes, we don't escape spaces in the label or description
 		const displayPath = formattedPath
 		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"]
 const initialLanguages: BundledLanguage[] = ["shell", "log"]
 
 
 // Singleton state
 // Singleton state
-let state: {
+const state: {
 	instance: Highlighter | null
 	instance: Highlighter | null
 	instanceInitPromise: Promise<Highlighter> | null
 	instanceInitPromise: Promise<Highlighter> | null
 	loadedLanguages: Set<ExtendedLanguage>
 	loadedLanguages: Set<ExtendedLanguage>

+ 1 - 1
webview-ui/vite.config.ts

@@ -12,7 +12,7 @@ function getGitSha() {
 	try {
 	try {
 		gitSha = execSync("git rev-parse HEAD").toString().trim()
 		gitSha = execSync("git rev-parse HEAD").toString().trim()
 	} catch (_error) {
 	} catch (_error) {
-		// NO-OP
+		// Do nothing.
 	}
 	}
 
 
 	return gitSha
 	return gitSha