Compare commits

...

12 Commits

Author SHA1 Message Date
Sarah Fortune ebd49bcc3e Merge branch 'sjf-1' of https://github.com/cline/cline into sjf-1 2025-06-26 12:20:57 -07:00
Sarah Fortune c793dd5289 Use the host bridge getWorkspacePaths in FileContextTracker
Replace the vscode SDK getWorkspaceFolders with the util function getCwd (this is already switched to the host bridge).
2025-06-26 12:20:43 -07:00
Sarah Fortune 364881a3ae Replace vscode workspaceFolders in WorkspaceTracker
Make the cwd an instance property because await cannot be used at the top level.
2025-06-26 12:20:43 -07:00
Sarah Fortune 87a2b1586e Use the host bridge in utils/path.ts
Update utils/path.ts to use the host bridge to get the workspace folders, instead of the vscode SDK.
Update callers to use await as the functions are now async.
2025-06-26 12:20:43 -07:00
Sarah Fortune a84d37f0da Enable sources maps, dont compact and minify TS builds. 2025-06-26 12:20:04 -07:00
Sarah Fortune 2667b67832 Use the host bridge getWorkspacePaths in FileContextTracker
Replace the vscode SDK getWorkspaceFolders with the util function getCwd (this is already switched to the host bridge).
2025-06-24 23:27:43 -07:00
Sarah Fortune 667aa651f6 Replace vscode workspaceFolders in WorkspaceTracker
Make the cwd an instance property because await cannot be used at the top level.
2025-06-24 23:27:38 -07:00
Sarah Fortune 29d15570cc Use the host bridge in utils/path.ts
Update utils/path.ts to use the host bridge to get the workspace folders, instead of the vscode SDK.
Update callers to use await as the functions are now async.
2025-06-24 23:27:32 -07:00
Sarah Fortune 939c1f298c Update src/hosts/vscode/workspace/getWorkspacePaths.ts
Co-authored-by: ellipsis-dev[bot] <65095814+ellipsis-dev[bot]@users.noreply.github.com>
2025-06-24 23:26:31 -07:00
Sarah Fortune 20a228d6aa Add the vscode host implementation of getWorkspaceFolders 2025-06-24 23:26:25 -07:00
Sarah Fortune 2b5c16e6a7 Add workspace service to the host bridge.
Add a service for workspaces to the host bridge.
The service has one rpc getWorkspacePaths that will replace vscode.workspace.workspaceFolders
2025-06-24 23:26:14 -07:00
Sarah Fortune dbbe85a296 Enable sources maps, dont compact and minify TS builds. 2025-06-24 23:25:16 -07:00
11 changed files with 65 additions and 43 deletions
+4 -6
View File
@@ -122,14 +122,12 @@ const copyWasmFiles = {
// Base configuration shared between extension and standalone builds
const baseConfig = {
bundle: true,
minify: production,
minify: false,
sourcemap: !production,
logLevel: "silent",
define: production
? {
"process.env.IS_DEV": JSON.stringify(!production),
}
: undefined,
define: {
"process.env.IS_DEV": "true",
},
tsconfig: path.resolve(__dirname, "tsconfig.json"),
plugins: [
copyWasmFiles,
@@ -5,6 +5,8 @@ import { getWorkspaceState, updateWorkspaceState } from "@core/storage/state"
import { getGlobalState } from "@core/storage/state"
import type { FileMetadataEntry } from "./ContextTrackerTypes"
import type { ClineMessage } from "@shared/ExtensionMessage"
import { getHostBridgeProvider } from "@/hosts/host-providers"
import { getCwd } from "@/utils/path"
// This class is responsible for tracking file operations that may result in stale context.
// If a user modifies a file outside of Cline, the context may become stale and need to be updated.
@@ -37,8 +39,8 @@ export class FileContextTracker {
/**
* Gets the current working directory or returns undefined if it cannot be determined
*/
private getCwd(): string | undefined {
const cwd = vscode.workspace.workspaceFolders?.map((folder) => folder.uri.fsPath).at(0)
private async getCwd(): Promise<string | undefined> {
const cwd = await getCwd(undefined)
if (!cwd) {
console.info("No workspace folder available - cannot determine current working directory")
}
@@ -54,7 +56,7 @@ export class FileContextTracker {
return
}
const cwd = this.getCwd()
const cwd = await this.getCwd()
if (!cwd) {
return
}
@@ -85,7 +87,7 @@ export class FileContextTracker {
*/
async trackFileContext(filePath: string, operation: "read_tool" | "user_edited" | "cline_edited" | "file_mentioned") {
try {
const cwd = this.getCwd()
const cwd = await this.getCwd()
if (!cwd) {
return
}
+1 -1
View File
@@ -13,7 +13,7 @@ import { convertGitCommitsToProtoGitCommits } from "@shared/proto-conversions/fi
* @returns GitCommits containing the matching commits
*/
export const searchCommits: FileMethodHandler = async (controller: Controller, request: StringRequest): Promise<GitCommits> => {
const cwd = getWorkspacePath()
const cwd = await getWorkspacePath()
if (!cwd) {
return GitCommits.create({ commits: [] })
}
+2 -2
View File
@@ -12,10 +12,10 @@ import { convertSearchResultsToProtoFileInfos } from "@shared/proto-conversions/
* @returns Results containing matching files/folders
*/
export const searchFiles: FileMethodHandler = async (
controller: Controller,
_controller: Controller,
request: FileSearchRequest,
): Promise<FileSearchResults> => {
const workspacePath = getWorkspacePath()
const workspacePath = await getWorkspacePath()
if (!workspacePath) {
// Handle case where workspace path is not available
+1 -1
View File
@@ -56,7 +56,7 @@ export class ServiceRegistry {
}
this.methodMetadata[methodName] = { isStreaming, ...metadata }
console.log(`Registered ${this.serviceName} method: ${methodName}${isStreaming ? " (streaming)" : ""}`)
//console.log(`Registered ${this.serviceName} method: ${methodName}${isStreaming ? " (streaming)" : ""}`)
}
/**
+7 -3
View File
@@ -46,6 +46,9 @@ import { sendMcpMarketplaceCatalogEvent } from "./mcp/subscribeToMcpMarketplaceC
import { sendRelinquishControlEvent } from "./ui/subscribeToRelinquishControl"
import { handleTaskServiceRequest } from "./task"
import { BooleanRequest } from "@shared/proto/common"
import { getHostBridgeProvider } from "@/hosts/host-providers"
import { GetWorkspacePathsRequest } from "@/shared/proto/index.host"
import { getCwd } from "@/utils/path"
/*
https://github.com/microsoft/vscode-webview-ui-toolkit-samples/blob/main/default/weather-webview/src/providers/WeatherViewProvider.ts
@@ -203,6 +206,7 @@ export class Controller {
// Send any JSON serializable data to the react app
async postMessageToWebview(message: ExtensionMessage) {
console.log("postMessageToWebview: " + JSON.stringify(message).slice(0, 200))
await this.postMessage(message)
}
@@ -683,8 +687,8 @@ export class Controller {
// Context menus and code actions
getFileMentionFromPath(filePath: string) {
const cwd = vscode.workspace.workspaceFolders?.map((folder) => folder.uri.fsPath).at(0)
async getFileMentionFromPath(filePath: string) {
const cwd = await getCwd()
if (!cwd) {
return "@/" + filePath
}
@@ -988,7 +992,7 @@ export class Controller {
async generateGitCommitMessage() {
try {
// Check if there's a workspace folder open
const cwd = vscode.workspace.workspaceFolders?.[0]?.uri.fsPath
const cwd = await getCwd()
if (!cwd) {
vscode.window.showErrorMessage("No workspace folder open")
return
+1 -1
View File
@@ -15,7 +15,7 @@ export async function getTaskHistory(controller: Controller, request: GetTaskHis
// Get task history from global state
const taskHistory = ((await getGlobalState(controller.context, "taskHistory")) as any[]) || []
const workspacePath = getWorkspacePath()
const workspacePath = await getWorkspacePath()
// Apply filters
let filteredTasks = taskHistory.filter((item) => {
+10 -10
View File
@@ -574,7 +574,7 @@ export class ToolExecutor {
tool: fileExists ? "editedExistingFile" : "newFileCreated",
path: getReadablePath(this.cwd, this.removeClosingTag(block, "path", relPath)),
content: diff || content,
operationIsLocatedInWorkspace: isLocatedInWorkspace(relPath),
operationIsLocatedInWorkspace: await isLocatedInWorkspace(relPath),
}
if (block.partial) {
@@ -645,7 +645,7 @@ export class ToolExecutor {
const completeMessage = JSON.stringify({
...sharedMessageProps,
content: diff || content,
operationIsLocatedInWorkspace: isLocatedInWorkspace(relPath),
operationIsLocatedInWorkspace: await isLocatedInWorkspace(relPath),
// ? formatResponse.createPrettyPatch(
// relPath,
// this.diffViewProvider.originalContent,
@@ -788,7 +788,7 @@ export class ToolExecutor {
const partialMessage = JSON.stringify({
...sharedMessageProps,
content: undefined,
operationIsLocatedInWorkspace: isLocatedInWorkspace(relPath),
operationIsLocatedInWorkspace: await isLocatedInWorkspace(relPath),
} satisfies ClineSayTool)
if (this.shouldAutoApproveToolWithPath(block.name, block.params.path)) {
this.removeLastPartialMessageIfExistsWithType("ask", "tool")
@@ -819,7 +819,7 @@ export class ToolExecutor {
const completeMessage = JSON.stringify({
...sharedMessageProps,
content: absolutePath,
operationIsLocatedInWorkspace: isLocatedInWorkspace(relPath),
operationIsLocatedInWorkspace: await isLocatedInWorkspace(relPath),
} satisfies ClineSayTool)
if (this.shouldAutoApproveToolWithPath(block.name, block.params.path)) {
this.removeLastPartialMessageIfExistsWithType("ask", "tool")
@@ -870,7 +870,7 @@ export class ToolExecutor {
const partialMessage = JSON.stringify({
...sharedMessageProps,
content: "",
operationIsLocatedInWorkspace: isLocatedInWorkspace(block.params.path),
operationIsLocatedInWorkspace: await isLocatedInWorkspace(block.params.path),
} satisfies ClineSayTool)
if (this.shouldAutoApproveToolWithPath(block.name, block.params.path)) {
this.removeLastPartialMessageIfExistsWithType("ask", "tool")
@@ -902,7 +902,7 @@ export class ToolExecutor {
const completeMessage = JSON.stringify({
...sharedMessageProps,
content: result,
operationIsLocatedInWorkspace: isLocatedInWorkspace(block.params.path),
operationIsLocatedInWorkspace: await isLocatedInWorkspace(block.params.path),
} satisfies ClineSayTool)
if (this.shouldAutoApproveToolWithPath(block.name, block.params.path)) {
this.removeLastPartialMessageIfExistsWithType("ask", "tool")
@@ -945,7 +945,7 @@ export class ToolExecutor {
const partialMessage = JSON.stringify({
...sharedMessageProps,
content: "",
operationIsLocatedInWorkspace: isLocatedInWorkspace(block.params.path),
operationIsLocatedInWorkspace: await isLocatedInWorkspace(block.params.path),
} satisfies ClineSayTool)
if (this.shouldAutoApproveToolWithPath(block.name, block.params.path)) {
this.removeLastPartialMessageIfExistsWithType("ask", "tool")
@@ -974,7 +974,7 @@ export class ToolExecutor {
const completeMessage = JSON.stringify({
...sharedMessageProps,
content: result,
operationIsLocatedInWorkspace: isLocatedInWorkspace(block.params.path),
operationIsLocatedInWorkspace: await isLocatedInWorkspace(block.params.path),
} satisfies ClineSayTool)
if (this.shouldAutoApproveToolWithPath(block.name, block.params.path)) {
this.removeLastPartialMessageIfExistsWithType("ask", "tool")
@@ -1021,7 +1021,7 @@ export class ToolExecutor {
const partialMessage = JSON.stringify({
...sharedMessageProps,
content: "",
operationIsLocatedInWorkspace: isLocatedInWorkspace(block.params.path),
operationIsLocatedInWorkspace: await isLocatedInWorkspace(block.params.path),
} satisfies ClineSayTool)
if (this.shouldAutoApproveToolWithPath(block.name, block.params.path)) {
this.removeLastPartialMessageIfExistsWithType("ask", "tool")
@@ -1058,7 +1058,7 @@ export class ToolExecutor {
const completeMessage = JSON.stringify({
...sharedMessageProps,
content: results,
operationIsLocatedInWorkspace: isLocatedInWorkspace(block.params.path),
operationIsLocatedInWorkspace: await isLocatedInWorkspace(block.params.path),
} satisfies ClineSayTool)
if (this.shouldAutoApproveToolWithPath(block.name, block.params.path)) {
this.removeLastPartialMessageIfExistsWithType("ask", "tool")
+16 -11
View File
@@ -2,13 +2,22 @@ import * as vscode from "vscode"
import * as path from "path"
import { listFiles } from "@services/glob/list-files"
import { sendWorkspaceUpdateEvent } from "@core/controller/file/subscribeToWorkspaceUpdates"
const cwd = vscode.workspace.workspaceFolders?.map((folder) => folder.uri.fsPath).at(0)
import { getCwd } from "@/utils/path"
// Note: this is not a drop-in replacement for listFiles at the start of tasks, since that will be done for Desktops when there is no workspace selected
class WorkspaceTracker {
private disposables: vscode.Disposable[] = []
private filePaths: Set<string> = new Set()
private cwd: string = ""
constructor() {
this.initializeCwd()
this.registerListeners()
}
private async initializeCwd() {
this.cwd = await getCwd()
}
private get activeFiles() {
return new Set(
@@ -18,16 +27,12 @@ class WorkspaceTracker {
)
}
constructor() {
this.registerListeners()
}
async populateFilePaths() {
// should not auto get filepaths for desktop since it would immediately show permission popup before cline ever creates a file
if (!cwd) {
if (!this.cwd) {
return
}
const [files, _] = await listFiles(cwd, true, 1_000)
const [files, _] = await listFiles(this.cwd, true, 1_000)
files.forEach((file) => this.filePaths.add(this.normalizeFilePath(file)))
this.workspaceDidUpdate()
}
@@ -91,18 +96,18 @@ class WorkspaceTracker {
}
private async workspaceDidUpdate() {
if (!cwd) {
if (!this.cwd) {
return
}
const filePaths = Array.from(new Set([...this.activeFiles, ...this.filePaths])).map((file) => {
const relativePath = path.relative(cwd, file).toPosix()
const relativePath = path.relative(this.cwd, file).toPosix()
return file.endsWith("/") ? relativePath + "/" : relativePath
})
await sendWorkspaceUpdateEvent(filePaths)
}
private normalizeFilePath(filePath: string): string {
const resolvedPath = cwd ? path.resolve(cwd, filePath) : path.resolve(filePath)
const resolvedPath = this.cwd ? path.resolve(this.cwd, filePath) : path.resolve(filePath)
return filePath.endsWith("/") ? resolvedPath + "/" : resolvedPath
}
+13 -4
View File
@@ -1,6 +1,7 @@
import * as path from "path"
import os from "os"
import * as vscode from "vscode"
import { getHostBridgeProvider } from "@/hosts/host-providers"
/*
The Node.js 'path' module resolves and normalizes paths differently depending on the platform:
@@ -101,9 +102,17 @@ export function getReadablePath(cwd: string, relPath?: string): string {
}
}
export const getWorkspacePath = (defaultCwdPath = "") => {
const cwdPath = vscode.workspace.workspaceFolders?.map((folder) => folder.uri.fsPath).at(0) || defaultCwdPath
// Returns the path of the first workspace directory, or the defaultCwdPath if there is no workspace open.
export const getCwd = async (defaultCwdPath = ""): Promise<string> => {
const workspaceFolders = await getHostBridgeProvider().workspaceClient.getWorkspacePaths({})
return workspaceFolders.paths.shift() || defaultCwdPath
}
// Returns the workspace path of the file in the current editor.
// If there is no path, it returns the top level workspace directory.
export const getWorkspacePath = async (defaultCwdPath = "") => {
const currentFileUri = vscode.window.activeTextEditor?.document.uri
const cwdPath = await getCwd(defaultCwdPath)
if (currentFileUri) {
const workspaceFolder = vscode.workspace.getWorkspaceFolder(currentFileUri)
return workspaceFolder?.uri.fsPath || cwdPath
@@ -111,8 +120,8 @@ export const getWorkspacePath = (defaultCwdPath = "") => {
return cwdPath
}
export const isLocatedInWorkspace = (pathToCheck: string = ""): boolean => {
const workspacePath = getWorkspacePath()
export const isLocatedInWorkspace = async (pathToCheck: string = ""): Promise<boolean> => {
const workspacePath = await getWorkspacePath()
// Handle long paths in Windows
if (pathToCheck.startsWith("\\\\?\\") || workspacePath.startsWith("\\\\?\\")) {
+4
View File
@@ -39,7 +39,9 @@ export default defineConfig({
},
build: {
outDir: "build",
minify: false, // Disable minification for better debugging
reportCompressedSize: false,
sourcemap: true, // Generate source maps for easier debugging
rollupOptions: {
output: {
inlineDynamicImports: true,
@@ -54,6 +56,8 @@ export default defineConfig({
}
return "assets/[name][extname]"
},
// Preserve formatting
compact: false,
},
},
chunkSizeWarningLimit: 100000,