From 502d088c9894072cd59f8ef8c94608aca9dabcb7 Mon Sep 17 00:00:00 2001 From: catlog22 Date: Sun, 18 Jan 2026 13:03:22 +0800 Subject: [PATCH] =?UTF-8?q?feat(cli):=20=E6=B7=BB=E5=8A=A0=E4=BA=A4?= =?UTF-8?q?=E4=BA=92=E5=BC=8F=E9=80=89=E6=8B=A9=E5=8A=9F=E8=83=BD=E4=BB=A5?= =?UTF-8?q?=E9=80=89=E6=8B=A9=20shell=20=E9=85=8D=E7=BD=AE=E6=96=87?= =?UTF-8?q?=E4=BB=B6=E5=B9=B6=E5=AE=89=E8=A3=85=20Git=20Bash=20=E4=BF=AE?= =?UTF-8?q?=E5=A4=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ccw/src/commands/install.ts | 118 +++++++++++++++++++++++++++--------- 1 file changed, 91 insertions(+), 27 deletions(-) diff --git a/ccw/src/commands/install.ts b/ccw/src/commands/install.ts index 6b711f8d..7f22f846 100644 --- a/ccw/src/commands/install.ts +++ b/ccw/src/commands/install.ts @@ -14,6 +14,14 @@ import type { Ora } from 'ora'; const GITBASH_FIX_START = '# >>> ccw gitbash fix'; const GITBASH_FIX_END = '# <<< ccw gitbash fix'; +// Supported shell configuration files +const SHELL_CONFIG_FILES = [ + { name: '.bashrc', description: 'Bash configuration (recommended for Git Bash)' }, + { name: '.bash_profile', description: 'Bash login shell configuration' }, + { name: '.profile', description: 'Generic shell profile' }, + { name: '.zshrc', description: 'Zsh configuration' } +]; + const __filename = fileURLToPath(import.meta.url); const __dirname = dirname(__filename); @@ -288,10 +296,13 @@ export async function installCommand(options: InstallOptions): Promise { }]); if (installFix) { - const fixResult = await installGitBashFix(); + // Let user select shell config file + const selectedConfig = await selectShellConfig(); + const fixResult = await installGitBashFix(selectedConfig); + if (fixResult.installed) { info(`Git Bash fix: ${fixResult.message}`); - info(' Run: source ~/.bashrc (to apply immediately)'); + info(` Run: source ${selectedConfig} (to apply immediately)`); } else { warning(`Git Bash fix skipped: ${fixResult.message}`); } @@ -353,6 +364,56 @@ async function selectPath(): Promise { return path; } +/** + * Interactive shell configuration file selection + * @returns {Promise} - Selected config file path + */ +async function selectShellConfig(): Promise { + const home = homedir(); + + // Build choices with status indicators + const choices = SHELL_CONFIG_FILES.map(config => { + const fullPath = join(home, config.name); + const exists = existsSync(fullPath); + const status = exists ? chalk.green('exists') : chalk.gray('will be created'); + const recommended = config.name === '.bashrc' ? chalk.yellow(' (recommended)') : ''; + + return { + name: `${chalk.cyan(config.name)}${recommended} - ${config.description} [${status}]`, + value: fullPath + }; + }); + + // Add custom path option + choices.push({ + name: chalk.yellow('Custom path...'), + value: 'custom' + }); + + const { configPath } = await inquirer.prompt([{ + type: 'list', + name: 'configPath', + message: 'Select shell configuration file to install Git Bash fix:', + choices + }]); + + if (configPath === 'custom') { + const { customPath } = await inquirer.prompt([{ + type: 'input', + name: 'customPath', + message: 'Enter custom shell config path:', + default: join(home, '.bashrc'), + validate: (input: string) => { + if (!input) return 'Path is required'; + return true; + } + }]); + return customPath; + } + + return configPath; +} + /** * Clean files based on manifest record * Only removes files that were installed by the previous installation @@ -581,9 +642,10 @@ ${GITBASH_FIX_END} /** * Install Git Bash fix to user's shell config + * @param targetConfigPath - Optional target config file path. If not provided, auto-detects. * @returns true if fix was installed, false otherwise */ -export async function installGitBashFix(): Promise<{ installed: boolean; message: string }> { +export async function installGitBashFix(targetConfigPath?: string): Promise<{ installed: boolean; message: string }> { // Only applicable on Windows if (platform() !== 'win32') { return { installed: false, message: 'Not Windows platform' }; @@ -594,28 +656,33 @@ export async function installGitBashFix(): Promise<{ installed: boolean; message return { installed: false, message: 'ccw not found in npm global modules' }; } - // Find shell config file - const home = homedir(); - const configFiles = [ - join(home, '.bashrc'), - join(home, '.bash_profile'), - join(home, '.profile') - ]; + let targetConfig: string; - let targetConfig: string | null = null; - for (const configFile of configFiles) { - if (existsSync(configFile)) { - targetConfig = configFile; - break; + if (targetConfigPath) { + // Use provided path + targetConfig = targetConfigPath; + } else { + // Auto-detect: find existing shell config file + const home = homedir(); + const configFiles = [ + join(home, '.bashrc'), + join(home, '.bash_profile'), + join(home, '.profile') + ]; + + let foundConfig: string | null = null; + for (const configFile of configFiles) { + if (existsSync(configFile)) { + foundConfig = configFile; + break; + } } + + // If no config exists, default to .bashrc + targetConfig = foundConfig || join(home, '.bashrc'); } - // If no config exists, create .bashrc - if (!targetConfig) { - targetConfig = join(home, '.bashrc'); - } - - // Check if fix already exists + // Check if fix already exists in target file if (existsSync(targetConfig)) { const content = readFileSync(targetConfig, 'utf8'); if (content.includes(GITBASH_FIX_START)) { @@ -628,7 +695,7 @@ export async function installGitBashFix(): Promise<{ installed: boolean; message } } - // Append fix to config file + // Append fix to config file (creates file if not exists) const fixContent = generateGitBashFix(ccwJsPath); appendFileSync(targetConfig, fixContent, 'utf8'); @@ -641,11 +708,8 @@ export async function installGitBashFix(): Promise<{ installed: boolean; message */ export function removeGitBashFix(): { removed: boolean; message: string } { const home = homedir(); - const configFiles = [ - join(home, '.bashrc'), - join(home, '.bash_profile'), - join(home, '.profile') - ]; + // Check all supported shell config files + const configFiles = SHELL_CONFIG_FILES.map(config => join(home, config.name)); let removed = false; let targetFile = '';