Compare commits

...

5 Commits

Author SHA1 Message Date
abeatrix 0f548eda7f fix: cache CLI detection and hard-kill on timeout
- Add cached result and timestamp to `isClineCliInstalled` to avoid
  repeated checks within a 5-minute window
- Force-kill CLI version check on timeout with SIGKILL to prevent
  zombie processes
- Re-check CLI installation after install command completes to refresh
  cached state and timer
- Remove unused Logger error log in install flow
2026-01-22 22:12:59 -08:00
Robin Newhouse 8118e11596 fix(extract-text): strip notebook outputs to reduce context size (#8784)
* fix(extract-text): strip notebook outputs to reduce context size

* chore: add changeset for notebook outputs fix
2026-01-22 17:50:31 -08:00
Bee f7b593df35 chore: remove noisy log when checking file outside workspace (#8814)
* chore: remove noisy log when checking file outside workspace

Removes a `Logger.error` call in `ifFileExistsRelativePath` that triggered whenever a file path was checked without an active workspace. This log was creating excessive noise during long conversations where many files were mentioned but no workspace was open.

* update test
2026-01-22 17:01:22 -08:00
Saoud Rizwan 2e0358a7a1 fix: disable browser tool by default (#8815)
The browser tool conflicts with the new websearch tool. Disabling it by
default provides a better out-of-box experience.
2026-01-22 16:58:29 -08:00
Bee 0fbc10f807 chore: remove unhelpful and noisy log statements - part 1 (#8813)
* chore: remove unhelpful and noisy log statements - part 1

Removes excessive debug and info logs across several services to reduce console noise, specifically:
- Deletes `[DEBUG]` logs for request registration, subscription setup/cleanup, and event dispatching in the gRPC controller and UI handlers.
- Removes verbose file cleanup logs in `ClineTempManager` and process termination logs in `AudioRecordingService`.
- Simplifies the success log in `refreshOpenRouterModels` by removing the large JSON payload dump.
- Upgrades the log level from `debug` to `error` for request cleanup failures in `GrpcRequestRegistry` to ensure exceptions are properly highlighted.

* removes subscription logs
2026-01-22 16:44:05 -08:00
17 changed files with 36 additions and 47 deletions
+7
View File
@@ -0,0 +1,7 @@
---
"claude-dev": patch
---
fix(extract-text): strip notebook outputs to reduce context size
Strip notebook cell outputs when extracting text content from Jupyter notebooks, significantly reducing the amount of context sent to the LLM while preserving the essential code and markdown content.
@@ -4,14 +4,12 @@ import * as pathUtils from "@utils/path"
import { expect } from "chai"
import { afterEach, beforeEach, describe, it } from "mocha"
import * as sinon from "sinon"
import { Logger } from "@/shared/services/Logger"
import { ifFileExistsRelativePath } from "../ifFileExistsRelativePath"
describe("ifFileExistsRelativePath", () => {
let sandbox: sinon.SinonSandbox
let mockController: Controller
let getWorkspacePathStub: sinon.SinonStub
let consoleErrorStub: sinon.SinonStub
beforeEach(() => {
sandbox = sinon.createSandbox()
@@ -21,9 +19,6 @@ describe("ifFileExistsRelativePath", () => {
// Stub getWorkspacePath utility
getWorkspacePathStub = sandbox.stub(pathUtils, "getWorkspacePath")
// Stub Logger.error to prevent test output pollution
consoleErrorStub = sandbox.stub(Logger, "error")
})
afterEach(() => {
@@ -44,12 +39,11 @@ describe("ifFileExistsRelativePath", () => {
expect(typeof result.value).to.equal("boolean")
})
it("should return false and log error when no workspace path is available", async () => {
it("should return false when no workspace path is available", async () => {
const noWorkspaceScenarios = [null, undefined]
for (const workspaceValue of noWorkspaceScenarios) {
getWorkspacePathStub.resolves(workspaceValue)
consoleErrorStub.resetHistory()
const request = StringRequest.create({
value: "src/test.ts",
@@ -58,7 +52,6 @@ describe("ifFileExistsRelativePath", () => {
const result = await ifFileExistsRelativePath(mockController, request)
expect(result).to.deep.equal(BooleanResponse.create({ value: false }))
expect(consoleErrorStub.called).to.be.true
}
})
@@ -2,7 +2,6 @@ import { workspaceResolver } from "@core/workspace"
import { BooleanResponse, StringRequest } from "@shared/proto/cline/common"
import { getWorkspacePath } from "@utils/path"
import * as fs from "fs"
import { Logger } from "@/shared/services/Logger"
import { Controller } from ".."
/**
@@ -16,7 +15,6 @@ export async function ifFileExistsRelativePath(_controller: Controller, request:
if (!workspacePath) {
// If no workspace is open, return false
Logger.error("Error in ifFileExistsRelativePath: No workspace path available") // TODO
return BooleanResponse.create({ value: false })
}
+1 -3
View File
@@ -55,7 +55,6 @@ export class GrpcRequestRegistry {
timestamp: new Date(),
responseStream,
})
Logger.log(`[DEBUG] Registered request: ${requestId}`)
}
/**
@@ -70,9 +69,8 @@ export class GrpcRequestRegistry {
}
try {
requestInfo.cleanup()
Logger.debug(`Cleaned up request: ${requestId}`)
} catch (error) {
Logger.debug(`Error cleaning up request ${requestId}:`, error)
Logger.error(`Error cleaning up request ${requestId}:`, error)
}
this.activeRequests.delete(requestId)
return true
@@ -242,7 +242,7 @@ export async function refreshOpenRouterModels(controller: Controller): Promise<R
Logger.error("Invalid response from OpenRouter API")
}
await fs.writeFile(openRouterModelsFilePath, JSON.stringify(models))
Logger.log("OpenRouter models fetched and saved", JSON.stringify(models).slice(0, 300))
Logger.log("OpenRouter models fetched and saved")
} catch (error) {
Logger.error("Error fetching OpenRouter models:", error)
@@ -20,15 +20,12 @@ export async function subscribeToOpenRouterModels(
responseStream: StreamingResponseHandler<OpenRouterCompatibleModelInfo>,
requestId?: string,
): Promise<void> {
Logger.log("[DEBUG] set up OpenRouter models subscription")
// Add this subscription to the active subscriptions
activeOpenRouterModelsSubscriptions.add(responseStream)
// Register cleanup when the connection is closed
const cleanup = () => {
activeOpenRouterModelsSubscriptions.delete(responseStream)
Logger.log("[DEBUG] Cleaned up OpenRouter models subscription")
}
// Register the cleanup function with the request registry if we have a requestId
+4 -2
View File
@@ -2,7 +2,7 @@ import { Empty, EmptyRequest } from "@shared/proto/cline/common"
import { ShowMessageType } from "@shared/proto/host/window"
import { ExecuteCommandInTerminalRequest } from "@shared/proto/host/workspace"
import { HostProvider } from "@/hosts/host-provider"
import { Logger } from "@/shared/services/Logger"
import { isClineCliInstalled } from "@/utils/cli-detector"
import { Controller } from ".."
/**
@@ -26,8 +26,10 @@ export async function installClineCli(_controller: Controller, _request: EmptyRe
if (!response.success) {
throw new Error("Failed to execute command in terminal")
}
// Force a re-check and reset timeer
await isClineCliInstalled(true)
} catch (error) {
Logger.error("Error executing CLI installation:", error)
await HostProvider.window.showMessage({
type: ShowMessageType.ERROR,
message: `Failed to start CLI installation: ${error instanceof Error ? error.message : "Unknown error"}`,
@@ -27,7 +27,6 @@ export async function subscribeToState(
// Register cleanup when the connection is closed
const cleanup = () => {
activeStateSubscriptions.delete(responseStream)
//Logger.log(`[DEBUG] Cleaned up state subscription`)
}
// Register the cleanup function with the request registry if we have a requestId
@@ -39,8 +38,6 @@ export async function subscribeToState(
const initialState = await controller.getStateToPostToWebview()
const initialStateJson = JSON.stringify(initialState)
//Logger.log(`[DEBUG] set up state subscription`)
try {
await responseStream(
{
@@ -69,7 +66,6 @@ export async function sendStateUpdate(state: ExtensionState): Promise<void> {
},
false, // Not the last message
)
//Logger.log(`[DEBUG] sending followup state`, stateJson.length, "chars")
} catch (error) {
Logger.error("Error sending state update:", error)
// Remove the subscription if there was an error
@@ -19,8 +19,6 @@ export async function subscribeToAccountButtonClicked(
responseStream: StreamingResponseHandler<Empty>,
requestId?: string,
): Promise<void> {
Logger.log(`[DEBUG] set up accountButtonClicked subscription`)
// Add this subscription to the active subscriptions
activeAccountButtonClickedSubscriptions.add(responseStream)
@@ -19,15 +19,12 @@ export async function subscribeToAddToInput(
responseStream: StreamingResponseHandler<ProtoString>,
requestId?: string,
): Promise<void> {
Logger.log("[DEBUG] set up addToInput subscription")
// Add this subscription to the active subscriptions
activeAddToInputSubscriptions.add(responseStream)
// Register cleanup when the connection is closed
const cleanup = () => {
activeAddToInputSubscriptions.delete(responseStream)
Logger.log("[DEBUG] Cleaned up addToInput subscription")
}
// Register the cleanup function with the request registry if we have a requestId
@@ -51,7 +48,6 @@ export async function sendAddToInputEvent(text: string): Promise<void> {
event,
false, // Not the last message
)
Logger.log("[DEBUG] sending addToInput event", text.length, "chars")
} catch (error) {
Logger.error("Error sending addToInput event:", error)
// Remove the subscription if there was an error
@@ -19,8 +19,6 @@ export async function subscribeToChatButtonClicked(
responseStream: StreamingResponseHandler<Empty>,
requestId?: string,
): Promise<void> {
Logger.log(`[DEBUG] set up chatButtonClicked subscription`)
// Add this subscription to the active subscriptions
activeChatButtonClickedSubscriptions.add(responseStream)
@@ -19,8 +19,6 @@ export async function subscribeToMcpButtonClicked(
responseStream: StreamingResponseHandler<Empty>,
requestId?: string,
): Promise<void> {
Logger.log(`[DEBUG] set up mcpButtonClicked subscription`)
// Add this subscription to the active subscriptions
activeMcpButtonClickedSubscriptions.add(responseStream)
+4 -2
View File
@@ -79,8 +79,10 @@ async function extractTextFromIPYNB(filePath: string): Promise<string> {
const encoding = await detectEncoding(fileBuffer)
const data = iconv.decode(fileBuffer, encoding)
// Return sanitized JSON for proper editing (enhanced notebook behavior is now always enabled)
return sanitizeNotebookForLLM(data)
// Strip all outputs to reduce context size - outputs aren't needed for understanding
// notebook structure. For Jupyter commands, the specific cell's outputs are included
// separately via sanitizeCellForLLM which preserves text outputs.
return sanitizeNotebookForLLM(data, true)
}
/**
@@ -60,7 +60,6 @@ export class AudioRecordingService {
return
}
Logger.info("Terminating recording process...")
this.recordingProcess.kill("SIGINT")
// Wait for the process to finish with timeout
-6
View File
@@ -125,9 +125,6 @@ class ClineTempManagerImpl {
await fs.promises.unlink(fileInfo.path)
deletedCount++
freedBytes += fileInfo.size
Logger.info(
`Cleaned up old temp file: ${path.basename(fileInfo.path)} (age: ${Math.round(age / 3600000)}h)`,
)
} catch {
// File might have been deleted by another process
}
@@ -150,9 +147,6 @@ class ClineTempManagerImpl {
totalSize -= fileInfo.size
deletedCount++
freedBytes += fileInfo.size
Logger.info(
`Cleaned up temp file for space: ${path.basename(fileInfo.path)} (${Math.round(fileInfo.size / 1024)}KB)`,
)
} catch {
// File might have been deleted by another process
}
+1 -1
View File
@@ -22,7 +22,7 @@ export const DEFAULT_BROWSER_SETTINGS: BrowserSettings = {
remoteBrowserHost: "http://localhost:9222",
chromeExecutablePath: "", // Changed from undefined to empty string
// chromeType: "chromium",
disableToolUse: false,
disableToolUse: true,
customArgs: "",
}
+17 -4
View File
@@ -13,25 +13,38 @@ interface CliSubagentDetectionParams {
outputFormat?: string
}
let lastCheckTime = 0
let lastCheckResult = false
const CHECK_CACHE_DURATION_MS = 5 * 60 * 1000 // 5 minutes
/**
* Check if the Cline CLI tool is installed on the system
* @returns true if CLI is installed, false otherwise
*/
export async function isClineCliInstalled(): Promise<boolean> {
export async function isClineCliInstalled(force = false): Promise<boolean> {
try {
const current = Date.now()
if (!force && current - lastCheckTime < CHECK_CACHE_DURATION_MS) {
return lastCheckResult
}
// Try to get the version of the cline CLI tool
// This will fail if the tool is not installed
const { stdout } = await execAsync("cline version", {
timeout: 5000, // 5 second timeout
killSignal: "SIGKILL", // Force kill if timeout occurs
})
// If we get here, the CLI is installed
// We could also validate the version if needed
return stdout.includes("Cline CLI Version") || stdout.includes("Cline Core Version")
} catch (error) {
const result = stdout.includes("Cline CLI Version") || stdout.includes("Cline Core Version")
lastCheckResult = result
lastCheckTime = current
return result
} catch {
// Command failed, which likely means CLI is not installed
// or not in PATH
return false
return lastCheckResult
}
}