1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
|
#!/usr/bin/env python3
# Copyright (C) 2026 Danilo M. <danix@danix.xyz> 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="")
|