(theme: ThemeName)
| 73 | * shell's rc file. Returns a user-facing status message. |
| 74 | */ |
| 75 | export async function setupShellCompletion(theme: ThemeName): Promise<string> { |
| 76 | const shell = detectShell() |
| 77 | if (!shell) { |
| 78 | return '' |
| 79 | } |
| 80 | |
| 81 | // Ensure the cache directory exists |
| 82 | try { |
| 83 | await mkdir(dirname(shell.cacheFile), { recursive: true }) |
| 84 | } catch (e: unknown) { |
| 85 | logError(e) |
| 86 | return `${EOL}${color('warning', theme)(`Could not write ${shell.name} completion cache`)}${EOL}${chalk.dim(`Run manually: claude completion ${shell.shellFlag} > ${shell.cacheFile}`)}${EOL}` |
| 87 | } |
| 88 | |
| 89 | // Generate the completion script by writing directly to the cache file. |
| 90 | // Using --output avoids piping through stdout where process.exit() can |
| 91 | // truncate output before the pipe buffer drains. |
| 92 | const claudeBin = process.argv[1] || 'claude' |
| 93 | const result = await execFileNoThrow(claudeBin, [ |
| 94 | 'completion', |
| 95 | shell.shellFlag, |
| 96 | '--output', |
| 97 | shell.cacheFile, |
| 98 | ]) |
| 99 | if (result.code !== 0) { |
| 100 | return `${EOL}${color('warning', theme)(`Could not generate ${shell.name} shell completions`)}${EOL}${chalk.dim(`Run manually: claude completion ${shell.shellFlag} > ${shell.cacheFile}`)}${EOL}` |
| 101 | } |
| 102 | |
| 103 | // Check if rc file already sources completions |
| 104 | let existing = '' |
| 105 | try { |
| 106 | existing = await readFile(shell.rcFile, { encoding: 'utf-8' }) |
| 107 | if ( |
| 108 | existing.includes('claude completion') || |
| 109 | existing.includes(shell.cacheFile) |
| 110 | ) { |
| 111 | return `${EOL}${color('success', theme)(`Shell completions updated for ${shell.name}`)}${EOL}${chalk.dim(`See ${formatPathLink(shell.rcFile)}`)}${EOL}` |
| 112 | } |
| 113 | } catch (e: unknown) { |
| 114 | if (!isENOENT(e)) { |
| 115 | logError(e) |
| 116 | return `${EOL}${color('warning', theme)(`Could not install ${shell.name} shell completions`)}${EOL}${chalk.dim(`Add this to ${formatPathLink(shell.rcFile)}:`)}${EOL}${chalk.dim(shell.completionLine)}${EOL}` |
| 117 | } |
| 118 | } |
| 119 | |
| 120 | // Append source line to rc file |
| 121 | try { |
| 122 | const configDir = dirname(shell.rcFile) |
| 123 | await mkdir(configDir, { recursive: true }) |
| 124 | |
| 125 | const separator = existing && !existing.endsWith('\n') ? '\n' : '' |
| 126 | const content = `${existing}${separator}\n# Claude Code shell completions\n${shell.completionLine}\n` |
| 127 | await writeFile(shell.rcFile, content, { encoding: 'utf-8' }) |
| 128 | |
| 129 | return `${EOL}${color('success', theme)(`Installed ${shell.name} shell completions`)}${EOL}${chalk.dim(`Added to ${formatPathLink(shell.rcFile)}`)}${EOL}${chalk.dim(`Run: source ${shell.rcFile}`)}${EOL}` |
| 130 | } catch (error) { |
| 131 | logError(error) |
| 132 | return `${EOL}${color('warning', theme)(`Could not install ${shell.name} shell completions`)}${EOL}${chalk.dim(`Add this to ${formatPathLink(shell.rcFile)}:`)}${EOL}${chalk.dim(shell.completionLine)}${EOL}` |
no test coverage detected