source dump of claude code
at main 110 lines 3.2 kB view raw
1import chalk from 'chalk' 2import { stat } from 'fs/promises' 3import { dirname, resolve } from 'path' 4import type { ToolPermissionContext } from '../../Tool.js' 5import { getErrnoCode } from '../../utils/errors.js' 6import { expandPath } from '../../utils/path.js' 7import { 8 allWorkingDirectories, 9 pathInWorkingPath, 10} from '../../utils/permissions/filesystem.js' 11 12export type AddDirectoryResult = 13 | { 14 resultType: 'success' 15 absolutePath: string 16 } 17 | { 18 resultType: 'emptyPath' 19 } 20 | { 21 resultType: 'pathNotFound' | 'notADirectory' 22 directoryPath: string 23 absolutePath: string 24 } 25 | { 26 resultType: 'alreadyInWorkingDirectory' 27 directoryPath: string 28 workingDir: string 29 } 30 31export async function validateDirectoryForWorkspace( 32 directoryPath: string, 33 permissionContext: ToolPermissionContext, 34): Promise<AddDirectoryResult> { 35 if (!directoryPath) { 36 return { 37 resultType: 'emptyPath', 38 } 39 } 40 41 // resolve() strips the trailing slash expandPath can leave on absolute 42 // inputs, so /foo and /foo/ map to the same storage key (CC-33). 43 const absolutePath = resolve(expandPath(directoryPath)) 44 45 // Check if path exists and is a directory (single syscall) 46 try { 47 const stats = await stat(absolutePath) 48 if (!stats.isDirectory()) { 49 return { 50 resultType: 'notADirectory', 51 directoryPath, 52 absolutePath, 53 } 54 } 55 } catch (e: unknown) { 56 const code = getErrnoCode(e) 57 // Match prior existsSync() semantics: treat any of these as "not found" 58 // rather than re-throwing. EACCES/EPERM in particular must not crash 59 // startup when a settings-configured additional directory is inaccessible. 60 if ( 61 code === 'ENOENT' || 62 code === 'ENOTDIR' || 63 code === 'EACCES' || 64 code === 'EPERM' 65 ) { 66 return { 67 resultType: 'pathNotFound', 68 directoryPath, 69 absolutePath, 70 } 71 } 72 throw e 73 } 74 75 // Get current permission context 76 const currentWorkingDirs = allWorkingDirectories(permissionContext) 77 78 // Check if already within an existing working directory 79 for (const workingDir of currentWorkingDirs) { 80 if (pathInWorkingPath(absolutePath, workingDir)) { 81 return { 82 resultType: 'alreadyInWorkingDirectory', 83 directoryPath, 84 workingDir, 85 } 86 } 87 } 88 89 return { 90 resultType: 'success', 91 absolutePath, 92 } 93} 94 95export function addDirHelpMessage(result: AddDirectoryResult): string { 96 switch (result.resultType) { 97 case 'emptyPath': 98 return 'Please provide a directory path.' 99 case 'pathNotFound': 100 return `Path ${chalk.bold(result.absolutePath)} was not found.` 101 case 'notADirectory': { 102 const parentDir = dirname(result.absolutePath) 103 return `${chalk.bold(result.directoryPath)} is not a directory. Did you mean to add the parent directory ${chalk.bold(parentDir)}?` 104 } 105 case 'alreadyInWorkingDirectory': 106 return `${chalk.bold(result.directoryPath)} is already accessible within the existing working directory ${chalk.bold(result.workingDir)}.` 107 case 'success': 108 return `Added ${chalk.bold(result.absolutePath)} as a working directory.` 109 } 110}