import type { Command, Option } from "commander" /** Generate fish completions dynamically from the Commander program tree. */ export function action(program: Command) { const lines: string[] = [ "# Fish completions for sandlot (auto-generated)", "# Install: sandlot completions > ~/.config/fish/completions/sandlot.fish", "", "complete -c sandlot -f", "", "function __sandlot_sessions", ` command sandlot list --json 2>/dev/null | string match -r '"branch":\\s*"[^"]+"' | string replace -r '.*"branch":\\s*"([^"]+)".*' '$1'`, "end", "", ] const branchCommands: string[] = [] for (const cmd of program.commands) { const name = cmd.name() const desc = cmd.description() const subs = cmd.commands as Command[] if (subs.length > 0) { // Parent command (e.g. "vm") — register it plus its subcommands lines.push(`complete -c sandlot -n __fish_use_subcommand -a ${name} -d ${esc(desc)}`) const subNames = subs.filter(s => !(s as any)._hidden).map(s => s.name()) const guard = `"__fish_seen_subcommand_from ${name}; and not __fish_seen_subcommand_from ${subNames.join(" ")}"` for (const sub of subs) { if ((sub as any)._hidden) continue lines.push(`complete -c sandlot -n ${guard} -a ${sub.name()} -d ${esc(sub.description())}`) } // Options on subcommands for (const sub of subs) { if ((sub as any)._hidden) continue emitOptions(lines, `${name} ${sub.name()}`, sub.options) } } else { // Leaf command lines.push(`complete -c sandlot -n __fish_use_subcommand -a ${name} -d ${esc(desc)}`) emitOptions(lines, name, cmd.options) // Track commands that accept a / [branch] argument const hasBranch = cmd.registeredArguments.some(a => a.name() === "branch") if (hasBranch) branchCommands.push(name) } } // Session completions for all branch-taking commands if (branchCommands.length > 0) { lines.push("") lines.push(`complete -c sandlot -n "__fish_seen_subcommand_from ${branchCommands.join(" ")}" -xa "(__sandlot_sessions)"`) } lines.push("") process.stdout.write(lines.join("\n") + "\n") } function esc(s: string): string { return `"${s.replace(/"/g, '\\"')}"` } function emitOptions(lines: string[], cmdName: string, options: readonly Option[]) { for (const opt of options) { const parts = [`complete -c sandlot -n "__fish_seen_subcommand_from ${cmdName}"`] if (opt.short) parts.push(`-s ${opt.short.replace("-", "")}`) if (opt.long) parts.push(`-l ${opt.long.replace("--", "")}`) parts.push(`-d ${esc(opt.description)}`) if (opt.required) parts.push("-r") lines.push(parts.join(" ")) } }