Files
cc/src/installationPaths.ts
T
BenjyGitHubcoderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
7ce5bcbe7c Modernize codebase by removing .js imports and changes to the tsconfig (#328)
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
2026-01-02 14:08:27 -07:00

270 lines
9.9 KiB
TypeScript

import { globbySync } from 'globby';
import os from 'node:os';
import { debug } from './utils';
export interface SearchPathInfo {
pattern: string;
isGlob: boolean;
expandedPaths: string[];
}
const getClijsSearchPathsWithInfo = (): SearchPathInfo[] => {
const pathInfos: SearchPathInfo[] = [];
const home =
process.platform == 'win32'
? os.homedir().replace(/\\/g, '/')
: os.homedir();
const mod = 'node_modules/@anthropic-ai/claude-code';
// Helper function to add a path or glob pattern
const addPath = (pattern: string, isGlob: boolean = false) => {
if (isGlob) {
try {
const expanded = globbySync(pattern, { onlyFiles: false });
pathInfos.push({ pattern, isGlob: true, expandedPaths: expanded });
} catch (error) {
// Handle permission errors gracefully - log in debug mode only
if (
error instanceof Error &&
'code' in error &&
(error.code === 'EACCES' || error.code === 'EPERM')
) {
debug(`Permission denied accessing: ${pattern} (${error.code})`);
} else {
// Log other unexpected errors in debug mode
debug(
`Error expanding glob pattern "${pattern}": ${
error instanceof Error ? error.message : String(error)
}`
);
}
// Return empty expanded paths - caller handles this gracefully
pathInfos.push({ pattern, isGlob: true, expandedPaths: [] });
}
} else {
pathInfos.push({ pattern, isGlob: false, expandedPaths: [pattern] });
}
};
// Local Claude Code installation (#42)
addPath(`${os.homedir()}/.claude/local/${mod}`);
// Search in custom paths for popular tools. These are cross-platform paths.
// prettier-ignore
{
if (process.env.NPM_PREFIX) addPath(`${process.env.NPM_PREFIX}/lib/${mod}`);
if (process.env.N_PREFIX) addPath(`${process.env.N_PREFIX}/lib/${mod}`);
if (process.env.VOLTA_HOME) addPath(`${process.env.VOLTA_HOME}/lib/${mod}`);
if (process.env.FNM_DIR) addPath(`${process.env.FNM_DIR}/lib/${mod}`);
if (process.env.NVM_DIR) addPath(`${process.env.NVM_DIR}/lib/${mod}`);
if (process.env.NODENV_ROOT) addPath(`${process.env.NODENV_ROOT}/versions/*/lib/${mod}`, true);
if (process.env.NVS_HOME) addPath(`${process.env.NVS_HOME}/node/*/*/lib/${mod}`, true);
if (process.env.ASDF_DATA_DIR) addPath(`${process.env.ASDF_DATA_DIR}/installs/nodejs/*/lib/${mod}`, true);
}
// Platform-specific paths.
// prettier-ignore
if (process.platform == "win32") {
// volta, npm, yarn, pnpm
addPath(`${home}/AppData/Local/Volta/tools/image/packages/@anthropic-ai/claude-code/${mod}`);
addPath(`${home}/AppData/Roaming/npm/${mod}`);
addPath(`${home}/AppData/Roaming/nvm/*/${mod}`, true);
addPath(`${home}/AppData/Local/Yarn/config/global/${mod}`);
addPath(`${home}/AppData/Local/pnpm/global/*/${mod}`, true);
// nvm4w (https://github.com/coreybutler/nvm-windows) (#118)
addPath(`C:/nvm4w/nodejs/${mod}`);
// n (https://github.com/tj/n)
addPath(`${home}/n/versions/node/*/lib/${mod}`, true);
// Yarn
addPath(`${home}/AppData/Roaming/Yarn/config/global/${mod}`);
// pnpm
addPath(`${home}/AppData/Roaming/pnpm-global/${mod}`);
addPath(`${home}/AppData/Roaming/pnpm-global/*/${mod}`, true);
// Bun (global CLI installations)
addPath(`${home}/.bun/install/global/${mod}`);
// Bun cache (used by bunx) - both default and XDG locations on Windows
addPath(`${home}/.bun/install/cache/@anthropic-ai/claude-code*@@@*`, true);
addPath(`${home}/AppData/Local/Bun/install/cache/@anthropic-ai/claude-code*@@@*`, true);
// fnm
addPath(`${home}/AppData/Local/fnm_multishells/*/node_modules/${mod}`, true);
// mise (global npm installation)
addPath(`${home}/AppData/Local/mise/installs/node/*/${mod}`, true);
// mise (npm backend) (https://mise.jdx.dev/dev-tools/backends/npm.html)
addPath(`${home}/AppData/Local/mise/installs/npm-anthropic-ai-claude-code/*/${mod}`, true);
} else {
// macOS-specific paths
if (process.platform == 'darwin') {
// macOS-specific potential user path
addPath(`${home}/Library/${mod}`);
// MacPorts
addPath(`/opt/local/lib/${mod}`);
// Bun cache (used by bunx on macOS) - both default and XDG locations
addPath(`${home}/.bun/install/cache/@anthropic-ai/claude-code*@@@*`, true);
addPath(`${home}/Library/Caches/bun/install/cache/@anthropic-ai/claude-code*@@@*`, true);
}
// Various user paths
addPath(`${home}/.local/lib/${mod}`);
addPath(`${home}/.local/share/${mod}`);
addPath(`${home}/.npm-global/lib/${mod}`);
addPath(`${home}/.npm-packages/lib/${mod}`);
addPath(`${home}/.npm/lib/${mod}`);
addPath(`${home}/npm/lib/${mod}`);
// Various system paths
addPath(`/etc/${mod}`);
addPath(`/lib/${mod}`);
addPath(`/opt/node/lib/${mod}`);
addPath(`/usr/lib/${mod}`);
addPath(`/usr/local/lib/${mod}`);
addPath(`/usr/share/${mod}`);
addPath(`/var/lib/${mod}`);
// Homebrew / Linuxbrew
addPath(`/opt/homebrew/lib/${mod}`);
addPath(`${home}/.linuxbrew/lib/${mod}`);
// Yarn
addPath(`${home}/.config/yarn/global/${mod}`);
addPath(`${home}/.yarn/global/${mod}`);
addPath(`${home}/.bun/install/global/${mod}`);
// pnpm
addPath(`${home}/.pnpm-global/${mod}`);
addPath(`${home}/.pnpm-global/*/${mod}`, true);
addPath(`${home}/pnpm-global/${mod}`);
addPath(`${home}/pnpm-global/*/${mod}`, true);
addPath(`${home}/.local/share/pnpm/global/${mod}`);
addPath(`${home}/.local/share/pnpm/global/*/${mod}`, true);
// Bun (global CLI installations)
addPath(`${home}/.bun/install/global/${mod}`);
// Bun cache (used by bunx) - both default and XDG locations
addPath(`${home}/.bun/install/cache/@anthropic-ai/claude-code*@@@*`, true);
addPath(`${home}/.local/share/bun/install/cache/@anthropic-ai/claude-code*@@@*`, true);
// n (https://github.com/tj/n) - system & user
addPath(`/usr/local/n/versions/node/*/lib/${mod}`, true);
addPath(`${home}/n/versions/node/*/lib/${mod}`, true);
addPath(`${home}/n/lib/${mod}`);
// volta (https://github.com/volta-cli/volta)
addPath(`${home}/.volta/tools/image/node/*/lib/${mod}`, true);
// fnm (https://github.com/Schniz/fnm)
addPath(`${home}/.fnm/node-versions/*/installation/lib/${mod}`, true);
addPath(`${home}/.local/state/fnm_multishells/*/lib/${mod}`, true);
// nvm (https://github.com/nvm-sh/nvm) - system & user
addPath(`/usr/local/nvm/versions/node/*/lib/${mod}`, true);
addPath(`/usr/local/share/nvm/versions/node/*/lib/${mod}`, true);
addPath(`${home}/.nvm/versions/node/*/lib/${mod}`, true);
// nodenv (https://github.com/nodenv/nodenv)
addPath(`${home}/.nodenv/versions/*/lib/${mod}`, true);
// nvs (https://github.com/jasongin/nvs)
addPath(`${home}/.nvs/*/lib/${mod}`, true);
// asdf (https://github.com/asdf-vm/asdf)
addPath(`${home}/.asdf/installs/nodejs/*/lib/${mod}`, true);
// mise (https://github.com/jdx/mise)
if (process.env.MISE_DATA_DIR) {
addPath(`${process.env.MISE_DATA_DIR}/installs/node/*/lib/${mod}`, true);
}
addPath(`${home}/.local/share/mise/installs/node/*/lib/${mod}`, true);
// mise (npm backend) (https://mise.jdx.dev/dev-tools/backends/npm.html)
if (process.env.MISE_DATA_DIR) {
addPath(`${process.env.MISE_DATA_DIR}/installs/npm-anthropic-ai-claude-code/*/lib/${mod}`, true);
}
addPath(`${home}/.local/share/mise/installs/npm-anthropic-ai-claude-code/*/lib/${mod}`, true);
}
// After we're done with globby, which required / even on Windows, convert / back to \\ for
// Windows.
if (process.platform == 'win32') {
pathInfos.forEach(info => {
info.pattern = info.pattern.replace(/\//g, '\\');
info.expandedPaths = info.expandedPaths.map(p => p.replace(/\//g, '\\'));
});
}
return pathInfos;
};
export const CLIJS_SEARCH_PATH_INFO: SearchPathInfo[] =
getClijsSearchPathsWithInfo();
export const CLIJS_SEARCH_PATHS: string[] = CLIJS_SEARCH_PATH_INFO.flatMap(
info => info.expandedPaths
);
const getNativeSearchPathsWithInfo = (): SearchPathInfo[] => {
const home =
process.platform === 'win32'
? os.homedir().replace(/\\/g, '/')
: os.homedir();
const pathInfos: SearchPathInfo[] = [];
// Helper function to add a path or glob pattern (same as cli.js version)
const addPath = (pattern: string, isGlob: boolean = false) => {
if (isGlob) {
try {
const expanded = globbySync(pattern, { onlyFiles: true });
pathInfos.push({ pattern, isGlob: true, expandedPaths: expanded });
} catch (error) {
if (
error instanceof Error &&
'code' in error &&
(error.code === 'EACCES' || error.code === 'EPERM')
) {
debug(`Permission denied accessing: ${pattern} (${error.code})`);
} else {
debug(
`Error expanding glob pattern "${pattern}": ${
error instanceof Error ? error.message : String(error)
}`
);
}
pathInfos.push({ pattern, isGlob: true, expandedPaths: [] });
}
} else {
pathInfos.push({ pattern, isGlob: false, expandedPaths: [pattern] });
}
};
// Direct binary path
addPath(`${home}/.local/bin/claude`);
// Versioned binaries (filenames are versions like 2.0.65)
addPath(`${home}/.local/share/claude/versions/*`, true);
// Convert to backslashes on Windows
if (process.platform === 'win32') {
pathInfos.forEach(info => {
info.pattern = info.pattern.replace(/\//g, '\\');
info.expandedPaths = info.expandedPaths.map(p => p.replace(/\//g, '\\'));
});
}
return pathInfos;
};
export const NATIVE_SEARCH_PATH_INFO: SearchPathInfo[] =
getNativeSearchPathsWithInfo();
export const NATIVE_SEARCH_PATHS: string[] = NATIVE_SEARCH_PATH_INFO.flatMap(
info => info.expandedPaths
);