Refactor file names

This commit is contained in:
Saoud Rizwan
2024-09-29 02:35:08 -04:00
parent 2b63b91bfb
commit 9e01aaa3d3
3 changed files with 54 additions and 48 deletions

View File

@@ -41,7 +41,7 @@ import { parseMentions } from "./mentions"
import { formatResponse } from "./prompts/responses" import { formatResponse } from "./prompts/responses"
import { SYSTEM_PROMPT } from "./prompts/system" import { SYSTEM_PROMPT } from "./prompts/system"
import { truncateHalfConversation } from "./sliding-window" import { truncateHalfConversation } from "./sliding-window"
import { ClaudeDevProvider } from "./webview/ClaudeDevProvider" import { ClaudeDevProvider, GlobalFileNames } from "./webview/ClaudeDevProvider"
import { calculateApiCost } from "../utils/cost" import { calculateApiCost } from "../utils/cost"
import { createDirectoriesForFile, fileExistsAtPath } from "../utils/fs" import { createDirectoriesForFile, fileExistsAtPath } from "../utils/fs"
@@ -129,7 +129,7 @@ export class ClaudeDev {
} }
private async getSavedApiConversationHistory(): Promise<Anthropic.MessageParam[]> { private async getSavedApiConversationHistory(): Promise<Anthropic.MessageParam[]> {
const filePath = path.join(await this.ensureTaskDirectoryExists(), "api_conversation_history.json") const filePath = path.join(await this.ensureTaskDirectoryExists(), GlobalFileNames.apiConversationHistory)
const fileExists = await fileExistsAtPath(filePath) const fileExists = await fileExistsAtPath(filePath)
if (fileExists) { if (fileExists) {
return JSON.parse(await fs.readFile(filePath, "utf8")) return JSON.parse(await fs.readFile(filePath, "utf8"))
@@ -149,7 +149,7 @@ export class ClaudeDev {
private async saveApiConversationHistory() { private async saveApiConversationHistory() {
try { try {
const filePath = path.join(await this.ensureTaskDirectoryExists(), "api_conversation_history.json") const filePath = path.join(await this.ensureTaskDirectoryExists(), GlobalFileNames.apiConversationHistory)
await fs.writeFile(filePath, JSON.stringify(this.apiConversationHistory)) await fs.writeFile(filePath, JSON.stringify(this.apiConversationHistory))
} catch (error) { } catch (error) {
// in the off chance this fails, we don't want to stop the task // in the off chance this fails, we don't want to stop the task
@@ -158,7 +158,7 @@ export class ClaudeDev {
} }
private async getSavedClaudeMessages(): Promise<ClaudeMessage[]> { private async getSavedClaudeMessages(): Promise<ClaudeMessage[]> {
const filePath = path.join(await this.ensureTaskDirectoryExists(), "claude_messages.json") const filePath = path.join(await this.ensureTaskDirectoryExists(), GlobalFileNames.claudeMessages)
const fileExists = await fileExistsAtPath(filePath) const fileExists = await fileExistsAtPath(filePath)
if (fileExists) { if (fileExists) {
return JSON.parse(await fs.readFile(filePath, "utf8")) return JSON.parse(await fs.readFile(filePath, "utf8"))
@@ -178,7 +178,7 @@ export class ClaudeDev {
private async saveClaudeMessages() { private async saveClaudeMessages() {
try { try {
const filePath = path.join(await this.ensureTaskDirectoryExists(), "claude_messages.json") const filePath = path.join(await this.ensureTaskDirectoryExists(), GlobalFileNames.claudeMessages)
await fs.writeFile(filePath, JSON.stringify(this.claudeMessages)) await fs.writeFile(filePath, JSON.stringify(this.claudeMessages))
// combined as they are in ChatView // combined as they are in ChatView
const apiMetrics = getApiMetrics(combineApiRequests(combineCommandSequences(this.claudeMessages.slice(1)))) const apiMetrics = getApiMetrics(combineApiRequests(combineCommandSequences(this.claudeMessages.slice(1))))
@@ -1019,45 +1019,6 @@ export class ClaudeDev {
} }
} }
formatFilesList(absolutePath: string, files: string[], didHitLimit: boolean): string {
const sorted = files
.map((file) => {
// convert absolute path to relative path
const relativePath = path.relative(absolutePath, file).toPosix()
return file.endsWith("/") ? relativePath + "/" : relativePath
})
// Sort so files are listed under their respective directories to make it clear what files are children of what directories. Since we build file list top down, even if file list is truncated it will show directories that claude can then explore further.
.sort((a, b) => {
const aParts = a.split("/") // only works if we use toPosix first
const bParts = b.split("/")
for (let i = 0; i < Math.min(aParts.length, bParts.length); i++) {
if (aParts[i] !== bParts[i]) {
// If one is a directory and the other isn't at this level, sort the directory first
if (i + 1 === aParts.length && i + 1 < bParts.length) {
return -1
}
if (i + 1 === bParts.length && i + 1 < aParts.length) {
return 1
}
// Otherwise, sort alphabetically
return aParts[i].localeCompare(bParts[i], undefined, { numeric: true, sensitivity: "base" })
}
}
// If all parts are the same up to the length of the shorter path,
// the shorter one comes first
return aParts.length - bParts.length
})
if (didHitLimit) {
return `${sorted.join(
"\n"
)}\n\n(File list truncated. Use list_files on specific subdirectories if you need to explore further.)`
} else if (sorted.length === 0 || (sorted.length === 1 && sorted[0] === "")) {
return "No files found."
} else {
return sorted.join("\n")
}
}
async executeCommandTool( async executeCommandTool(
command: string, command: string,
returnEmptyStringOnSuccess: boolean = false returnEmptyStringOnSuccess: boolean = false
@@ -1739,7 +1700,7 @@ ${this.customInstructions.trim()}
this.consecutiveMistakeCount = 0 this.consecutiveMistakeCount = 0
const absolutePath = path.resolve(cwd, relDirPath) const absolutePath = path.resolve(cwd, relDirPath)
const [files, didHitLimit] = await listFiles(absolutePath, recursive, 200) const [files, didHitLimit] = await listFiles(absolutePath, recursive, 200)
const result = this.formatFilesList(absolutePath, files, didHitLimit) const result = formatResponse.formatFilesList(absolutePath, files, didHitLimit)
const completeMessage = JSON.stringify({ const completeMessage = JSON.stringify({
...sharedMessageProps, ...sharedMessageProps,
content: result, content: result,
@@ -2600,7 +2561,7 @@ ${this.customInstructions.trim()}
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.)"
} else { } else {
const [files, didHitLimit] = await listFiles(cwd, true, 200) const [files, didHitLimit] = await listFiles(cwd, true, 200)
const result = this.formatFilesList(cwd, files, didHitLimit) const result = formatResponse.formatFilesList(cwd, files, didHitLimit)
details += result details += result
} }
} }

View File

@@ -1,4 +1,5 @@
import { Anthropic } from "@anthropic-ai/sdk" import { Anthropic } from "@anthropic-ai/sdk"
import * as path from "path"
export const formatResponse = { export const formatResponse = {
toolDenied: () => `The user denied this operation.`, toolDenied: () => `The user denied this operation.`,
@@ -34,6 +35,45 @@ export const formatResponse = {
imageBlocks: (images?: string[]): Anthropic.ImageBlockParam[] => { imageBlocks: (images?: string[]): Anthropic.ImageBlockParam[] => {
return formatImagesIntoBlocks(images) return formatImagesIntoBlocks(images)
}, },
formatFilesList: (absolutePath: string, files: string[], didHitLimit: boolean): string => {
const sorted = files
.map((file) => {
// convert absolute path to relative path
const relativePath = path.relative(absolutePath, file).toPosix()
return file.endsWith("/") ? relativePath + "/" : relativePath
})
// Sort so files are listed under their respective directories to make it clear what files are children of what directories. Since we build file list top down, even if file list is truncated it will show directories that claude can then explore further.
.sort((a, b) => {
const aParts = a.split("/") // only works if we use toPosix first
const bParts = b.split("/")
for (let i = 0; i < Math.min(aParts.length, bParts.length); i++) {
if (aParts[i] !== bParts[i]) {
// If one is a directory and the other isn't at this level, sort the directory first
if (i + 1 === aParts.length && i + 1 < bParts.length) {
return -1
}
if (i + 1 === bParts.length && i + 1 < aParts.length) {
return 1
}
// Otherwise, sort alphabetically
return aParts[i].localeCompare(bParts[i], undefined, { numeric: true, sensitivity: "base" })
}
}
// If all parts are the same up to the length of the shorter path,
// the shorter one comes first
return aParts.length - bParts.length
})
if (didHitLimit) {
return `${sorted.join(
"\n"
)}\n\n(File list truncated. Use list_files on specific subdirectories if you need to explore further.)`
} else if (sorted.length === 0 || (sorted.length === 1 && sorted[0] === "")) {
return "No files found."
} else {
return sorted.join("\n")
}
},
} }
// to avoid circular dependency // to avoid circular dependency

View File

@@ -51,6 +51,11 @@ type GlobalStateKey =
| "anthropicBaseUrl" | "anthropicBaseUrl"
| "azureApiVersion" | "azureApiVersion"
export const GlobalFileNames = {
apiConversationHistory: "api_conversation_history.json",
claudeMessages: "claude_messages.json",
}
export class ClaudeDevProvider implements vscode.WebviewViewProvider { export class ClaudeDevProvider implements vscode.WebviewViewProvider {
public static readonly sideBarId = "claude-dev.SidebarProvider" // used in package.json as the view's id. This value cannot be changed due to how vscode caches views based on their id, and updating the id would break existing instances of the extension. public static readonly sideBarId = "claude-dev.SidebarProvider" // used in package.json as the view's id. This value cannot be changed due to how vscode caches views based on their id, and updating the id would break existing instances of the extension.
public static readonly tabPanelId = "claude-dev.TabPanelProvider" public static readonly tabPanelId = "claude-dev.TabPanelProvider"
@@ -504,8 +509,8 @@ export class ClaudeDevProvider implements vscode.WebviewViewProvider {
const historyItem = history.find((item) => item.id === id) const historyItem = history.find((item) => item.id === id)
if (historyItem) { if (historyItem) {
const taskDirPath = path.join(this.context.globalStorageUri.fsPath, "tasks", id) const taskDirPath = path.join(this.context.globalStorageUri.fsPath, "tasks", id)
const apiConversationHistoryFilePath = path.join(taskDirPath, "api_conversation_history.json") const apiConversationHistoryFilePath = path.join(taskDirPath, GlobalFileNames.apiConversationHistory)
const claudeMessagesFilePath = path.join(taskDirPath, "claude_messages.json") const claudeMessagesFilePath = path.join(taskDirPath, GlobalFileNames.claudeMessages)
const fileExists = await fileExistsAtPath(apiConversationHistoryFilePath) const fileExists = await fileExistsAtPath(apiConversationHistoryFilePath)
if (fileExists) { if (fileExists) {
const apiConversationHistory = JSON.parse(await fs.readFile(apiConversationHistoryFilePath, "utf8")) const apiConversationHistory = JSON.parse(await fs.readFile(apiConversationHistoryFilePath, "utf8"))