#!/usr/bin/env python3 # Copyright (C) 2026 Danilo M. GPL-2.0-only """Generate completions/firefly.bash from the live command registry. python scripts/gen_completion.py > completions/firefly.bash No drift: groups, leaves, and per-leaf flags are read straight off the argparse subparsers the registry builds. """ import argparse from collections import defaultdict from firefly_cli import registry import firefly_cli.commands # noqa: F401 triggers registration GROUP_ORDER = ["auth", "account", "category", "tag", "tx"] def collect(): groups = defaultdict(dict) # group -> {leaf: [--flag, ...]} for c in registry.all_commands(): g, _, leaf = c.name.partition(" ") p = argparse.ArgumentParser() if c.args: c.args(p) opts = [] for a in p._actions: opts += [s for s in a.option_strings if s.startswith("--") and s != "--help"] groups[g][leaf] = sorted(opts) return groups def render(groups): order = [g for g in GROUP_ORDER if g in groups] order += [g for g in sorted(groups) if g not in GROUP_ORDER] leaf_cases, group_cases = [], [] for g in order: leaves = " ".join(sorted(groups[g])) group_cases.append(f' {g}) leaves="{leaves}";;') for leaf in sorted(groups[g]): flags = " ".join(groups[g][leaf]) if flags: leaf_cases.append( f' "{g} {leaf}")'.ljust(28) + f'leaf_opts="{flags}";;') return TEMPLATE.format( groups=" ".join(order), leaf_cases="\n".join(leaf_cases), group_cases="\n".join(group_cases), ) TEMPLATE = '''# bash completion for firefly (firefly-cli) # Install: source this file, or drop it in /etc/bash_completion.d/ or # /usr/share/bash-completion/completions/firefly # # Generated by scripts/gen_completion.py -- do not edit by hand. # Regenerate when commands change (see CLAUDE.md): # python scripts/gen_completion.py > completions/firefly.bash _firefly() {{ local cur prev words cword _init_completion 2>/dev/null || {{ cur="${{COMP_WORDS[COMP_CWORD]}}" prev="${{COMP_WORDS[COMP_CWORD-1]}}" words=("${{COMP_WORDS[@]}}") cword=$COMP_CWORD }} local global_opts="--human --url --token -h --help" local groups="{groups}" # Find the group and leaf among the words (skip the program name at 0). local group="" leaf="" i for ((i=1; i < cword; i++)); do local w="${{words[i]}}" case "$w" in -*) ;; # an option, skip --url|--token) ((i++));; # option that takes a value *) if [[ -z $group ]]; then group="$w" elif [[ -z $leaf ]]; then leaf="$w" fi ;; esac done # Leaf-specific options. local leaf_opts="" case "$group $leaf" in {leaf_cases} esac # Leaves per group. local leaves="" case "$group" in {group_cases} esac if [[ -z $group ]]; then COMPREPLY=($(compgen -W "$groups $global_opts" -- "$cur")) elif [[ -z $leaf ]]; then COMPREPLY=($(compgen -W "$leaves" -- "$cur")) else COMPREPLY=($(compgen -W "$leaf_opts --help" -- "$cur")) fi }} complete -F _firefly firefly ''' if __name__ == "__main__": print(render(collect()), end="")