Something went wrong. Try again.
source dump of claude code forked from oppi.li/claude-code
Something went wrong. Try again.
3.5 kB · 119 lines
TypeScript
at main
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120import { getHistory } from '../../history.js'import { logForDebugging } from '../debug.js'
/** * Result of shell history completion lookup */export type ShellHistoryMatch = { /** The full command from history */ fullCommand: string /** The suffix to display as ghost text (the part after user's input) */ suffix: string}
// Cache for shell history commands to avoid repeated async reads// History only changes when user submits a command, so a long TTL is finelet shellHistoryCache: string[] | null = nulllet shellHistoryCacheTimestamp = 0const CACHE_TTL_MS = 60000 // 60 seconds - history won't change while typing
/** * Get shell commands from history, with caching */async function getShellHistoryCommands(): Promise<string[]> { const now = Date.now()
// Return cached result if still fresh if (shellHistoryCache && now - shellHistoryCacheTimestamp < CACHE_TTL_MS) { return shellHistoryCache }
const commands: string[] = [] const seen = new Set<string>()
try { // Read history entries and filter for bash commands for await (const entry of getHistory()) { if (entry.display && entry.display.startsWith('!')) { // Remove the '!' prefix to get the actual command const command = entry.display.slice(1).trim() if (command && !seen.has(command)) { seen.add(command) commands.push(command) } } // Limit to 50 most recent unique commands if (commands.length >= 50) { break } } } catch (error) { logForDebugging(`Failed to read shell history: ${error}`) }
shellHistoryCache = commands shellHistoryCacheTimestamp = now return commands}
/** * Clear the shell history cache (useful when history is updated) */export function clearShellHistoryCache(): void { shellHistoryCache = null shellHistoryCacheTimestamp = 0}
/** * Add a command to the front of the shell history cache without * flushing the entire cache. If the command already exists in the * cache it is moved to the front (deduped). When the cache hasn't * been populated yet this is a no-op – the next lookup will read * the full history which already includes the new command. */export function prependToShellHistoryCache(command: string): void { if (!shellHistoryCache) { return } const idx = shellHistoryCache.indexOf(command) if (idx !== -1) { shellHistoryCache.splice(idx, 1) } shellHistoryCache.unshift(command)}
/** * Find the best matching shell command from history for the given input * * @param input The current user input (without '!' prefix) * @returns The best match, or null if no match found */export async function getShellHistoryCompletion( input: string,): Promise<ShellHistoryMatch | null> { // Don't suggest for empty or very short input if (!input || input.length < 2) { return null }
// Check the trimmed input to make sure there's actual content const trimmedInput = input.trim() if (!trimmedInput) { return null }
const commands = await getShellHistoryCommands()
// Find the first command that starts with the EXACT input (including spaces) // This ensures "ls " matches "ls -lah" but "ls " (2 spaces) does not for (const command of commands) { if (command.startsWith(input) && command !== input) { return { fullCommand: command, suffix: command.slice(input.length), } } }
return null}