chore: initialize recovered claude workspace
This commit is contained in:
110
src/commands/add-dir/validation.ts
Normal file
110
src/commands/add-dir/validation.ts
Normal file
@@ -0,0 +1,110 @@
|
||||
import chalk from 'chalk'
|
||||
import { stat } from 'fs/promises'
|
||||
import { dirname, resolve } from 'path'
|
||||
import type { ToolPermissionContext } from '../../Tool.js'
|
||||
import { getErrnoCode } from '../../utils/errors.js'
|
||||
import { expandPath } from '../../utils/path.js'
|
||||
import {
|
||||
allWorkingDirectories,
|
||||
pathInWorkingPath,
|
||||
} from '../../utils/permissions/filesystem.js'
|
||||
|
||||
export type AddDirectoryResult =
|
||||
| {
|
||||
resultType: 'success'
|
||||
absolutePath: string
|
||||
}
|
||||
| {
|
||||
resultType: 'emptyPath'
|
||||
}
|
||||
| {
|
||||
resultType: 'pathNotFound' | 'notADirectory'
|
||||
directoryPath: string
|
||||
absolutePath: string
|
||||
}
|
||||
| {
|
||||
resultType: 'alreadyInWorkingDirectory'
|
||||
directoryPath: string
|
||||
workingDir: string
|
||||
}
|
||||
|
||||
export async function validateDirectoryForWorkspace(
|
||||
directoryPath: string,
|
||||
permissionContext: ToolPermissionContext,
|
||||
): Promise<AddDirectoryResult> {
|
||||
if (!directoryPath) {
|
||||
return {
|
||||
resultType: 'emptyPath',
|
||||
}
|
||||
}
|
||||
|
||||
// resolve() strips the trailing slash expandPath can leave on absolute
|
||||
// inputs, so /foo and /foo/ map to the same storage key (CC-33).
|
||||
const absolutePath = resolve(expandPath(directoryPath))
|
||||
|
||||
// Check if path exists and is a directory (single syscall)
|
||||
try {
|
||||
const stats = await stat(absolutePath)
|
||||
if (!stats.isDirectory()) {
|
||||
return {
|
||||
resultType: 'notADirectory',
|
||||
directoryPath,
|
||||
absolutePath,
|
||||
}
|
||||
}
|
||||
} catch (e: unknown) {
|
||||
const code = getErrnoCode(e)
|
||||
// Match prior existsSync() semantics: treat any of these as "not found"
|
||||
// rather than re-throwing. EACCES/EPERM in particular must not crash
|
||||
// startup when a settings-configured additional directory is inaccessible.
|
||||
if (
|
||||
code === 'ENOENT' ||
|
||||
code === 'ENOTDIR' ||
|
||||
code === 'EACCES' ||
|
||||
code === 'EPERM'
|
||||
) {
|
||||
return {
|
||||
resultType: 'pathNotFound',
|
||||
directoryPath,
|
||||
absolutePath,
|
||||
}
|
||||
}
|
||||
throw e
|
||||
}
|
||||
|
||||
// Get current permission context
|
||||
const currentWorkingDirs = allWorkingDirectories(permissionContext)
|
||||
|
||||
// Check if already within an existing working directory
|
||||
for (const workingDir of currentWorkingDirs) {
|
||||
if (pathInWorkingPath(absolutePath, workingDir)) {
|
||||
return {
|
||||
resultType: 'alreadyInWorkingDirectory',
|
||||
directoryPath,
|
||||
workingDir,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
resultType: 'success',
|
||||
absolutePath,
|
||||
}
|
||||
}
|
||||
|
||||
export function addDirHelpMessage(result: AddDirectoryResult): string {
|
||||
switch (result.resultType) {
|
||||
case 'emptyPath':
|
||||
return 'Please provide a directory path.'
|
||||
case 'pathNotFound':
|
||||
return `Path ${chalk.bold(result.absolutePath)} was not found.`
|
||||
case 'notADirectory': {
|
||||
const parentDir = dirname(result.absolutePath)
|
||||
return `${chalk.bold(result.directoryPath)} is not a directory. Did you mean to add the parent directory ${chalk.bold(parentDir)}?`
|
||||
}
|
||||
case 'alreadyInWorkingDirectory':
|
||||
return `${chalk.bold(result.directoryPath)} is already accessible within the existing working directory ${chalk.bold(result.workingDir)}.`
|
||||
case 'success':
|
||||
return `Added ${chalk.bold(result.absolutePath)} as a working directory.`
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user