Use safe path comparison

This commit is contained in:
Saoud Rizwan
2024-09-22 20:05:38 -04:00
parent 69e681ebce
commit b5ca470ebf
5 changed files with 42 additions and 33 deletions

View File

@@ -29,6 +29,7 @@ import { regexSearchFiles } from "./utils/ripgrep"
import { parseMentions } from "./utils/context-mentions" import { parseMentions } from "./utils/context-mentions"
import { UrlContentFetcher } from "./utils/UrlContentFetcher" import { UrlContentFetcher } from "./utils/UrlContentFetcher"
import { diagnosticsToProblemsString, getNewDiagnostics } from "./utils/diagnostics" import { diagnosticsToProblemsString, getNewDiagnostics } from "./utils/diagnostics"
import { arePathsEqual } from "./utils/path-helpers"
const SYSTEM_PROMPT = async ( const SYSTEM_PROMPT = async (
supportsImages: boolean supportsImages: boolean
@@ -786,7 +787,9 @@ export class ClaudeDev {
// if the file is already open, ensure it's not dirty before getting its contents // if the file is already open, ensure it's not dirty before getting its contents
if (fileExists) { if (fileExists) {
const existingDocument = vscode.workspace.textDocuments.find((doc) => doc.uri.fsPath === absolutePath) const existingDocument = vscode.workspace.textDocuments.find((doc) =>
arePathsEqual(doc.uri.fsPath, absolutePath)
)
if (existingDocument && existingDocument.isDirty) { if (existingDocument && existingDocument.isDirty) {
await existingDocument.save() await existingDocument.save()
} }
@@ -861,7 +864,10 @@ export class ClaudeDev {
const tabs = vscode.window.tabGroups.all const tabs = vscode.window.tabGroups.all
.map((tg) => tg.tabs) .map((tg) => tg.tabs)
.flat() .flat()
.filter((tab) => tab.input instanceof vscode.TabInputText && tab.input.uri.fsPath === absolutePath) .filter(
(tab) =>
tab.input instanceof vscode.TabInputText && arePathsEqual(tab.input.uri.fsPath, absolutePath)
)
for (const tab of tabs) { for (const tab of tabs) {
await vscode.window.tabGroups.close(tab) await vscode.window.tabGroups.close(tab)
// console.log(`Closed tab for ${absolutePath}`) // console.log(`Closed tab for ${absolutePath}`)
@@ -1291,11 +1297,11 @@ export class ClaudeDev {
getReadablePath(relPath: string): string { getReadablePath(relPath: string): string {
// path.resolve is flexible in that it will resolve relative paths like '../../' to the cwd and even ignore the cwd if the relPath is actually an absolute path // path.resolve is flexible in that it will resolve relative paths like '../../' to the cwd and even ignore the cwd if the relPath is actually an absolute path
const absolutePath = path.resolve(cwd, relPath) const absolutePath = path.resolve(cwd, relPath)
if (cwd === path.join(os.homedir(), "Desktop")) { if (arePathsEqual(cwd, path.join(os.homedir(), "Desktop"))) {
// User opened vscode without a workspace, so cwd is the Desktop. Show the full absolute path to keep the user aware of where files are being created // User opened vscode without a workspace, so cwd is the Desktop. Show the full absolute path to keep the user aware of where files are being created
return absolutePath.toPosix() return absolutePath.toPosix()
} }
if (path.normalize(absolutePath) === path.normalize(cwd)) { if (arePathsEqual(path.normalize(absolutePath), path.normalize(cwd))) {
return path.basename(absolutePath).toPosix() return path.basename(absolutePath).toPosix()
} else { } else {
// show the relative path to the cwd // show the relative path to the cwd
@@ -2107,7 +2113,7 @@ ${this.customInstructions.trim()}
if (includeFileDetails) { if (includeFileDetails) {
details += `\n\n# Current Working Directory (${cwd.toPosix()}) Files\n` details += `\n\n# Current Working Directory (${cwd.toPosix()}) Files\n`
const isDesktop = cwd === path.join(os.homedir(), "Desktop") const isDesktop = arePathsEqual(cwd, path.join(os.homedir(), "Desktop"))
if (isDesktop) { if (isDesktop) {
// don't want to immediately access desktop since it would show permission popup // don't want to immediately access desktop since it would show permission popup
details += "(Desktop files not shown automatically. Use list_files to explore if needed.)" details += "(Desktop files not shown automatically. Use list_files to explore if needed.)"

View File

@@ -2,6 +2,7 @@ import { EventEmitter } from "events"
import pWaitFor from "p-wait-for" import pWaitFor from "p-wait-for"
import stripAnsi from "strip-ansi" import stripAnsi from "strip-ansi"
import * as vscode from "vscode" import * as vscode from "vscode"
import { arePathsEqual } from "../utils/path-helpers"
/* /*
TerminalManager: TerminalManager:
@@ -225,7 +226,7 @@ export class TerminalManager {
if (!terminalCwd) { if (!terminalCwd) {
return false return false
} }
return vscode.Uri.file(cwd).fsPath === terminalCwd.fsPath return arePathsEqual(vscode.Uri.file(cwd).fsPath, terminalCwd.fsPath)
}) })
if (availableTerminal) { if (availableTerminal) {
this.terminalIds.add(availableTerminal.id) this.terminalIds.add(availableTerminal.id)

View File

@@ -3,6 +3,7 @@ import { globby, Options } from "globby"
import os from "os" import os from "os"
import * as path from "path" import * as path from "path"
import { LanguageParser, loadRequiredLanguageParsers } from "./languageParser" import { LanguageParser, loadRequiredLanguageParsers } from "./languageParser"
import { arePathsEqual } from "../utils/path-helpers"
// TODO: implement caching behavior to avoid having to keep analyzing project for new tasks. // TODO: implement caching behavior to avoid having to keep analyzing project for new tasks.
export async function parseSourceCodeForDefinitionsTopLevel(dirPath: string): Promise<string> { export async function parseSourceCodeForDefinitionsTopLevel(dirPath: string): Promise<string> {
@@ -57,12 +58,12 @@ export async function listFiles(dirPath: string, recursive: boolean, limit: numb
const absolutePath = path.resolve(dirPath) const absolutePath = path.resolve(dirPath)
// Do not allow listing files in root or home directory, which Claude tends to want to do when the user's prompt is vague. // Do not allow listing files in root or home directory, which Claude tends to want to do when the user's prompt is vague.
const root = process.platform === "win32" ? path.parse(absolutePath).root : "/" const root = process.platform === "win32" ? path.parse(absolutePath).root : "/"
const isRoot = absolutePath === root const isRoot = arePathsEqual(absolutePath, root)
if (isRoot) { if (isRoot) {
return [[root], false] return [[root], false]
} }
const homeDir = os.homedir() const homeDir = os.homedir()
const isHomeDir = absolutePath === homeDir const isHomeDir = arePathsEqual(absolutePath, homeDir)
if (isHomeDir) { if (isHomeDir) {
return [[homeDir], false] return [[homeDir], false]
} }

View File

@@ -1,6 +1,7 @@
import * as path from "path" import * as path from "path"
import * as os from "os" import * as os from "os"
import * as vscode from "vscode" import * as vscode from "vscode"
import { arePathsEqual } from "./path-helpers"
export async function openImage(dataUri: string) { export async function openImage(dataUri: string) {
const matches = dataUri.match(/^data:image\/([a-zA-Z]+);base64,(.+)$/) const matches = dataUri.match(/^data:image\/([a-zA-Z]+);base64,(.+)$/)
@@ -27,7 +28,7 @@ export async function openFile(absolutePath: string) {
try { try {
for (const group of vscode.window.tabGroups.all) { for (const group of vscode.window.tabGroups.all) {
const existingTab = group.tabs.find( const existingTab = group.tabs.find(
(tab) => tab.input instanceof vscode.TabInputText && tab.input.uri.fsPath === uri.fsPath (tab) => tab.input instanceof vscode.TabInputText && arePathsEqual(tab.input.uri.fsPath, uri.fsPath)
) )
if (existingTab) { if (existingTab) {
const activeColumn = vscode.window.activeTextEditor?.viewColumn const activeColumn = vscode.window.activeTextEditor?.viewColumn

View File

@@ -46,30 +46,30 @@ String.prototype.toPosix = function (this: string): string {
} }
// Safe path comparison that works across different platforms // Safe path comparison that works across different platforms
// export function arePathsEqual(path1?: string, path2?: string): boolean { export function arePathsEqual(path1?: string, path2?: string): boolean {
// if (!path1 && !path2) { if (!path1 && !path2) {
// return true return true
// } }
// if (!path1 || !path2) { if (!path1 || !path2) {
// return false return false
// } }
// path1 = normalizePath(path1) path1 = normalizePath(path1)
// path2 = normalizePath(path2) path2 = normalizePath(path2)
// if (process.platform === "win32") { if (process.platform === "win32") {
// return path1.toLowerCase() === path2.toLowerCase() return path1.toLowerCase() === path2.toLowerCase()
// } }
// return path1 === path2 return path1 === path2
// } }
// function normalizePath(p: string): string { function normalizePath(p: string): string {
// // normalize resolve ./.. segments, removes duplicate slashes, and standardizes path separators // normalize resolve ./.. segments, removes duplicate slashes, and standardizes path separators
// let normalized = path.normalize(p) let normalized = path.normalize(p)
// // however it doesn't remove trailing slashes // however it doesn't remove trailing slashes
// // remove trailing slash, except for root paths // remove trailing slash, except for root paths
// if (normalized.length > 1 && (normalized.endsWith("/") || normalized.endsWith("\\"))) { if (normalized.length > 1 && (normalized.endsWith("/") || normalized.endsWith("\\"))) {
// normalized = normalized.slice(0, -1) normalized = normalized.slice(0, -1)
// } }
// return normalized return normalized
// } }