Something went wrong. Try again.
source dump of claude code forked from oppi.li/claude-code
Something went wrong. Try again.
4.7 kB · 167 lines
TypeScript
at main
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168/** * Utilities for managing shell configuration files (like .bashrc, .zshrc) * Used for managing claude aliases and PATH entries */
import { open, readFile, stat } from 'fs/promises'import { homedir as osHomedir } from 'os'import { join } from 'path'import { isFsInaccessible } from './errors.js'import { getLocalClaudePath } from './localInstaller.js'
export const CLAUDE_ALIAS_REGEX = /^\s*alias\s+claude\s*=/
type EnvLike = Record<string, string | undefined>
type ShellConfigOptions = { env?: EnvLike homedir?: string}
/** * Get the paths to shell configuration files * Respects ZDOTDIR for zsh users * @param options Optional overrides for testing (env, homedir) */export function getShellConfigPaths( options?: ShellConfigOptions,): Record<string, string> { const home = options?.homedir ?? osHomedir() const env = options?.env ?? process.env const zshConfigDir = env.ZDOTDIR || home return { zsh: join(zshConfigDir, '.zshrc'), bash: join(home, '.bashrc'), fish: join(home, '.config/fish/config.fish'), }}
/** * Filter out installer-created claude aliases from an array of lines * Only removes aliases pointing to $HOME/.claude/local/claude * Preserves custom user aliases that point to other locations * Returns the filtered lines and whether our default installer alias was found */export function filterClaudeAliases(lines: string[]): { filtered: string[] hadAlias: boolean} { let hadAlias = false const filtered = lines.filter(line => { // Check if this is a claude alias if (CLAUDE_ALIAS_REGEX.test(line)) { // Extract the alias target - handle spaces, quotes, and various formats // First try with quotes let match = line.match(/alias\s+claude\s*=\s*["']([^"']+)["']/) if (!match) { // Try without quotes (capturing until end of line or comment) match = line.match(/alias\s+claude\s*=\s*([^#\n]+)/) }
if (match && match[1]) { const target = match[1].trim() // Only remove if it points to the installer location // The installer always creates aliases with the full expanded path if (target === getLocalClaudePath()) { hadAlias = true return false // Remove this line } } // Keep custom aliases that don't point to the installer location } return true }) return { filtered, hadAlias }}
/** * Read a file and split it into lines * Returns null if file doesn't exist or can't be read */export async function readFileLines( filePath: string,): Promise<string[] | null> { try { const content = await readFile(filePath, { encoding: 'utf8' }) return content.split('\n') } catch (e: unknown) { if (isFsInaccessible(e)) return null throw e }}
/** * Write lines back to a file */export async function writeFileLines( filePath: string, lines: string[],): Promise<void> { const fh = await open(filePath, 'w') try { await fh.writeFile(lines.join('\n'), { encoding: 'utf8' }) await fh.datasync() } finally { await fh.close() }}
/** * Check if a claude alias exists in any shell config file * Returns the alias target if found, null otherwise * @param options Optional overrides for testing (env, homedir) */export async function findClaudeAlias( options?: ShellConfigOptions,): Promise<string | null> { const configs = getShellConfigPaths(options)
for (const configPath of Object.values(configs)) { const lines = await readFileLines(configPath) if (!lines) continue
for (const line of lines) { if (CLAUDE_ALIAS_REGEX.test(line)) { // Extract the alias target const match = line.match(/alias\s+claude=["']?([^"'\s]+)/) if (match && match[1]) { return match[1] } } } }
return null}
/** * Check if a claude alias exists and points to a valid executable * Returns the alias target if valid, null otherwise * @param options Optional overrides for testing (env, homedir) */export async function findValidClaudeAlias( options?: ShellConfigOptions,): Promise<string | null> { const aliasTarget = await findClaudeAlias(options) if (!aliasTarget) return null
const home = options?.homedir ?? osHomedir()
// Expand ~ to home directory const expandedPath = aliasTarget.startsWith('~') ? aliasTarget.replace('~', home) : aliasTarget
// Check if the target exists and is executable try { const stats = await stat(expandedPath) // Check if it's a file (could be executable or symlink) if (stats.isFile() || stats.isSymbolicLink()) { return aliasTarget } } catch { // Target doesn't exist or can't be accessed }
return null}