import type { Command } from 'commander'; import { mkdirSync, writeFileSync } from 'fs'; import { resolve } from 'path'; import { homedir } from 'os'; const SUBCOMMANDS = ['start', 'tui', 'send', 'sessions', 'doctor', 'config', 'setup', 'completion']; const SUBCOMMAND_OPTIONS: Record = { start: ['-c', '--config'], tui: ['-c', '--config', '-f', '--fullscreen'], send: ['-c', '--config', '--no-tools'], config: ['-c', '--config', '--raw'], setup: ['-c', '--config'], completion: ['--install'], }; export function generateBashCompletion(): string { const subcmds = SUBCOMMANDS.join(' '); const cases = Object.entries(SUBCOMMAND_OPTIONS) .map(([cmd, opts]) => ` ${cmd}) COMPREPLY=( $(compgen -W "${opts.join(' ')}" -- "$cur") ) ;;`) .join('\n'); return `# Flynn bash completion — generated by 'flynn completion bash' _flynn_completions() { local cur prev subcmd cur="\${COMP_WORDS[COMP_CWORD]}" prev="\${COMP_WORDS[COMP_CWORD-1]}" if [[ \${COMP_CWORD} -eq 1 ]]; then COMPREPLY=( $(compgen -W "${subcmds} --help --version" -- "$cur") ) return fi subcmd="\${COMP_WORDS[1]}" case "$subcmd" in ${cases} completion) COMPREPLY=( $(compgen -W "bash zsh fish --install" -- "$cur") ) ;; *) COMPREPLY=() ;; esac } complete -F _flynn_completions flynn `; } export function generateZshCompletion(): string { const subcmds = SUBCOMMANDS.map(c => `'${c}:${c} subcommand'`).join(' '); const cases = Object.entries(SUBCOMMAND_OPTIONS) .map(([cmd, opts]) => { const flags = opts.filter(o => o.startsWith('--')).map(o => `'${o}'`).join(' '); return ` ${cmd}) compadd ${flags} ;;`; }) .join('\n'); return `#compdef flynn # Flynn zsh completion — generated by 'flynn completion zsh' _flynn() { local -a subcmds subcmds=(${subcmds}) if (( CURRENT == 2 )); then _describe 'command' subcmds return fi case "\${words[2]}" in ${cases} completion) compadd bash zsh fish '--install' ;; *) ;; esac } compdef _flynn flynn `; } export function generateFishCompletion(): string { const lines = [ '# Flynn fish completion — generated by \'flynn completion fish\'', '# Disable file completions by default', 'complete -c flynn -f', '', '# Subcommands', ]; for (const cmd of SUBCOMMANDS) { lines.push(`complete -c flynn -n '__fish_use_subcommand' -a '${cmd}' -d '${cmd} subcommand'`); } lines.push('complete -c flynn -n \'__fish_use_subcommand\' -l help -d \'Show help\''); lines.push('complete -c flynn -n \'__fish_use_subcommand\' -l version -d \'Show version\''); lines.push(''); lines.push('# Subcommand options'); for (const [cmd, opts] of Object.entries(SUBCOMMAND_OPTIONS)) { for (const opt of opts) { const flag = opt.replace(/^-+/, ''); const short = opt.startsWith('--') ? '' : ` -s ${flag}`; const long = opt.startsWith('--') ? ` -l ${flag}` : ''; lines.push(`complete -c flynn -n '__fish_seen_subcommand_from ${cmd}'${short}${long}`); } } lines.push('complete -c flynn -n \'__fish_seen_subcommand_from completion\' -a \'bash zsh fish\' -d \'Shell type\''); lines.push(''); return lines.join('\n'); } function getInstallPath(shell: string): string { const home = homedir(); switch (shell) { case 'bash': return resolve(home, '.local/share/bash-completion/completions/flynn'); case 'zsh': return resolve(home, '.zfunc/_flynn'); case 'fish': return resolve(home, '.config/fish/completions/flynn.fish'); default: throw new Error(`Unknown shell: ${shell}`); } } export function registerCompletionCommand(program: Command): void { program .command('completion') .description('Generate shell completion script') .argument('', 'Shell type: bash, zsh, or fish') .option('--install', 'Install to standard location') .action((shell: string, opts: { install?: boolean }) => { const generators: Record string> = { bash: generateBashCompletion, zsh: generateZshCompletion, fish: generateFishCompletion, }; const generator = generators[shell]; if (!generator) { console.error(`Unknown shell: ${shell}. Supported: bash, zsh, fish`); process.exit(1); } const script = generator(); if (opts.install) { const installPath = getInstallPath(shell); mkdirSync(resolve(installPath, '..'), { recursive: true }); writeFileSync(installPath, script, 'utf-8'); console.log(`Completion script installed to: ${installPath}`); if (shell === 'zsh') { console.log('Ensure ~/.zfunc is in your fpath: fpath=(~/.zfunc $fpath)'); } } else { process.stdout.write(script); } }); }