|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
import path from 'node:path'; |
|
|
import os from 'os'; |
|
|
import * as crypto from 'crypto'; |
|
|
|
|
|
export const GEMINI_DIR = '.gemini'; |
|
|
const TMP_DIR_NAME = 'tmp'; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
export function tildeifyPath(path: string): string { |
|
|
const homeDir = os.homedir(); |
|
|
if (path.startsWith(homeDir)) { |
|
|
return path.replace(homeDir, '~'); |
|
|
} |
|
|
return path; |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
export function shortenPath(filePath: string, maxLen: number = 35): string { |
|
|
if (filePath.length <= maxLen) { |
|
|
return filePath; |
|
|
} |
|
|
|
|
|
const parsedPath = path.parse(filePath); |
|
|
const root = parsedPath.root; |
|
|
const separator = path.sep; |
|
|
|
|
|
|
|
|
const relativePath = filePath.substring(root.length); |
|
|
const segments = relativePath.split(separator).filter((s) => s !== ''); |
|
|
|
|
|
|
|
|
if (segments.length <= 1) { |
|
|
|
|
|
const keepLen = Math.floor((maxLen - 3) / 2); |
|
|
|
|
|
if (keepLen <= 0) { |
|
|
return filePath.substring(0, maxLen - 3) + '...'; |
|
|
} |
|
|
const start = filePath.substring(0, keepLen); |
|
|
const end = filePath.substring(filePath.length - keepLen); |
|
|
return `${start}...${end}`; |
|
|
} |
|
|
|
|
|
const firstDir = segments[0]; |
|
|
const lastSegment = segments[segments.length - 1]; |
|
|
const startComponent = root + firstDir; |
|
|
|
|
|
const endPartSegments: string[] = []; |
|
|
|
|
|
let currentLength = separator.length + lastSegment.length; |
|
|
|
|
|
|
|
|
for (let i = segments.length - 2; i >= 0; i--) { |
|
|
const segment = segments[i]; |
|
|
|
|
|
const lengthWithSegment = currentLength + separator.length + segment.length; |
|
|
|
|
|
if (lengthWithSegment <= maxLen) { |
|
|
endPartSegments.unshift(segment); |
|
|
currentLength = lengthWithSegment; |
|
|
} else { |
|
|
break; |
|
|
} |
|
|
} |
|
|
|
|
|
let result = endPartSegments.join(separator) + separator + lastSegment; |
|
|
|
|
|
if (currentLength > maxLen) { |
|
|
return result; |
|
|
} |
|
|
|
|
|
|
|
|
result = startComponent + separator + result; |
|
|
|
|
|
|
|
|
|
|
|
if (result.length > maxLen) { |
|
|
return '...' + result.substring(result.length - maxLen - 3); |
|
|
} |
|
|
|
|
|
return result; |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
export function makeRelative( |
|
|
targetPath: string, |
|
|
rootDirectory: string, |
|
|
): string { |
|
|
const resolvedTargetPath = path.resolve(targetPath); |
|
|
const resolvedRootDirectory = path.resolve(rootDirectory); |
|
|
|
|
|
const relativePath = path.relative(resolvedRootDirectory, resolvedTargetPath); |
|
|
|
|
|
|
|
|
return relativePath || '.'; |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
export function escapePath(filePath: string): string { |
|
|
let result = ''; |
|
|
for (let i = 0; i < filePath.length; i++) { |
|
|
|
|
|
if (filePath[i] === ' ' && (i === 0 || filePath[i - 1] !== '\\')) { |
|
|
result += '\\ '; |
|
|
} else { |
|
|
result += filePath[i]; |
|
|
} |
|
|
} |
|
|
return result; |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
export function unescapePath(filePath: string): string { |
|
|
return filePath.replace(/\\ /g, ' '); |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
export function getProjectHash(projectRoot: string): string { |
|
|
return crypto.createHash('sha256').update(projectRoot).digest('hex'); |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
export function getProjectTempDir(projectRoot: string): string { |
|
|
const hash = getProjectHash(projectRoot); |
|
|
return path.join(os.homedir(), GEMINI_DIR, TMP_DIR_NAME, hash); |
|
|
} |
|
|
|