#!/usr/bin/env bash # Bash completion for pos — dynamically discovers pos-* subcommands # Install: source this file in ~/.bashrc or place in /etc/bash_completion.d/ # GEN:START posflags declare -A _pos_flags _pos_flags[communication-telegram-listener]="--enable --disable --status --run" _pos_flags[communication-telegram]="--send --type --caption --parse-mode --no-preview --token --chat-id" _pos_flags[entertainment-send]="--print --markdown" _pos_flags[network-hotspot]="--foreground" _pos_flags[system-backup]="--service" _pos_flags[system-health]="--send --markdown" _pos_flags[usb-server]="--ls --ls-shared --share --unshare --auto-share --callback --close-callback --auto-connect --disconnect --nickname --timeout --port --info --version" # GEN:END posflags # GEN:START possubcmds declare -A _pos_subcmds _pos_subcmds[communication-telegram]="send test config listener" _pos_subcmds[docker-compose]="ls installed up down restart logs update config" _pos_subcmds[docker-vbox]="create enter stop start rm ls" # GEN:END possubcmds _pos() { local cur prev words cword # Manual init if bash-completion package is not loaded if declare -F _init_completion &>/dev/null; then _init_completion || return else COMPREPLY=() cur="${COMP_WORDS[COMP_CWORD]}" prev="${COMP_WORDS[COMP_CWORD-1]}" words=("${COMP_WORDS[@]}") cword=$COMP_CWORD fi local pos_bin="${COMP_WORDS[0]}" local pos_dir pos_dir="$(dirname "$(command -v "$pos_bin" 2>/dev/null || echo "$pos_bin")")" # ── Collect all pos-* subcommands ────────────────────────── local all_cmds=() local f for f in "$pos_dir"/pos-*; do [ -x "$f" ] || continue all_cmds+=("${f##*/pos-}") done # ── Build category→subcommand map ────────────────────────── # Nested sub-tools (pos--- where pos-- exists) are # offered under their parent tool, not at the category level. local -A cat_cmds local -A nested local cmd2 c2 cat2 sub2 for cmd in "${all_cmds[@]}"; do local cat="${cmd%%-*}" local sub="${cmd#*-}" if [ "$cat" != "$cmd" ]; then for cmd2 in "${all_cmds[@]}"; do cat2="${cmd2%%-*}" sub2="${cmd2#*-}" if [ "$cat2" = "$cat" ] && [ "$sub2" != "$sub" ] && [[ "$sub" == "$sub2-"* ]]; then nested["$cmd"]=1 break fi done fi done for cmd in "${all_cmds[@]}"; do local cat="${cmd%%-*}" local sub="${cmd#*-}" if [ "$cat" != "$cmd" ] && [ -z "${nested[$cmd]:-}" ]; then cat_cmds["$cat"]+="${sub} " fi done # ── Helpers ──────────────────────────────────────────────── _pos_complete_categories() { COMPREPLY=($(compgen -W "${!cat_cmds[*]}" -- "$cur")) } _pos_complete_subcats() { local cat="${words[1]}" COMPREPLY=($(compgen -W "${cat_cmds[$cat]:-} --help" -- "$cur")) } # Complete subcommands + flags + --help for a tool chain, walking up to # the nearest tool that declares anything (e.g. "telegram send" → telegram flags). _pos_complete_tool() { local key="$1" k opts k="$key" while [ -n "$k" ]; do if [ -n "${_pos_subcmds[$k]:-}" ] || [ -n "${_pos_flags[$k]:-}" ]; then if [ "$k" = "$key" ]; then opts="${_pos_subcmds[$k]:-} ${_pos_flags[$k]:-} --help" else opts="${_pos_flags[$k]:-} --help" fi COMPREPLY=($(compgen -W "$opts" -- "$cur")) return fi k="${k%-*}" done COMPREPLY=($(compgen -W "--help" -- "$cur")) } _pos_complete_compose_services() { local scale_dir="/usr/local/share/linux_post_install/scale-tail/services" if [ -d "$scale_dir" ]; then local svcs=() for d in "$scale_dir"/*/; do [ -d "$d" ] && svcs+=("$(basename "$d")") done COMPREPLY=($(compgen -W "${svcs[*]}" -- "$cur")) fi } _pos_complete_compose_cmds() { COMPREPLY=($(compgen -W "ls installed up down restart logs update config" -- "$cur")) } _pos_complete_docker_vbox_cmds() { COMPREPLY=($(compgen -W "create enter stop start rm ls" -- "$cur")) } _pos_complete_docker_vbox_names() { local names names=$(docker ps -a --filter label=linux_post_install.vbox=true --format '{{.Names}}' 2>/dev/null) COMPREPLY=($(compgen -W "$names" -- "$cur")) } _pos_entertainment_plugins() { local d f name out=() for d in /usr/local/bin "$pos_dir/../entertainment"; do [ -d "$d" ] || continue for f in "$d"/*.sh; do [ -f "$f" ] || continue name="$(grep -m1 '^# POS_PLUGIN:' "$f" 2>/dev/null | sed 's/^# POS_PLUGIN:[[:space:]]*//;s/[[:space:]]*$//')" [ -n "$name" ] && out+=("$name") done done COMPREPLY=($(compgen -W "${out[*]}" -- "$cur")) } # ── Dispatch ─────────────────────────────────────────────── case "${#words[@]}" in 2) _pos_complete_categories ;; 3) _pos_complete_subcats ;; 4) case "${words[1]}-${words[2]}" in docker-compose) case "${words[3]}" in up|down|restart|logs) _pos_complete_compose_services ;; *) _pos_complete_compose_cmds ;; esac ;; docker-vbox) _pos_complete_docker_vbox_cmds ;; entertainment-enable|entertainment-disable) _pos_entertainment_plugins ;; entertainment-config) COMPREPLY=($(compgen -W "set --help" -- "$cur")) ;; *) _pos_complete_tool "${words[1]}-${words[2]}" ;; esac ;; 5) case "${words[1]}-${words[2]}" in docker-compose) case "${words[3]}" in up|down|restart|logs) _pos_complete_compose_services ;; esac ;; docker-vbox) case "${words[3]}" in create|enter|stop|start|rm) _pos_complete_docker_vbox_names ;; esac ;; *) _pos_complete_tool "${words[1]}-${words[2]}-${words[3]}" ;; esac ;; 6) case "${words[1]}-${words[2]}" in communication-telegram) case "${words[3]}:${words[4]}" in send:--type|--send:--type) COMPREPLY=($(compgen -W "message file link sticker photo video audio voice animation" -- "$cur")) ;; send:--parse-mode|--send:--parse-mode) COMPREPLY=($(compgen -W "plain markdown html" -- "$cur")) ;; esac ;; esac ;; esac } complete -F _pos pos