Files
Your Name e969234ca5
gates / consistency-and-conventions (push) Successful in 1m28s
feat: command registry, alias wrapper scripts, config-ui readability
- lib/registry.sh: shared query API over POS_* headers (reg_scan, reg_list,
  reg_lookup, reg_tools_in, reg_each, reg_config_scopes/keys). Replaces
  per-consumer sed/grep header parsing.

- bin/pos-tree + bin/pos _pos_category_help(): migrated to registry API.
  Category help now shows [deps: ...] annotations. Tree output preserved.

- New optional headers # POS_DEPS: and # POS_EXAMPLES: in tool metadata.
  Added to pos-network-download (aria2c jq curl), pos-media-sync (lsblk jq),
  pos-system-backup (tar), pos-docker-ps (docker) as initial adopters.

- scripts/gen-docs.sh: extended tools array with deps/examples fields;
  conditional column rendering in gen_dispatch; deps annotation in gen_tree.
  Fixed URL-unsafe // joiner (→ middle dot ·) and \x1f caption delimiter
  collision in config-ui.

- bin/pos-ai-alias: rewrote activation from bash aliases (source-time-frozen)
  to executable wrapper scripts at ~/.local/bin. Staleness eliminated:
  edits apply on next invocation with no shell reload. _alias_sync()
  reconciliation on every subcommand, marker-guarded lifecycle, collision
  refusal, legacy .sh retirement. Fixed dup-table bug (option 4 no-op).

- lib/config-ui.sh: @caption/@[KEY=alt] conditional captions, *providers=<tag>
  tagged wildcards, uniform typography tier (bold/cyan/dim), honest prompt.
  Active provider keys bold, inactive dimmed with reason. Backward-compatible.

- bin/pos-system-uninstall: marker-scan for wrapper script cleanup.

- Docs synced: AGENTS.md (new headers + registry), DOC/SCRIPTS.md (registry
  section + lib list), DOC/POS.md (alias wrapper activation), MAINTENANCE.md
  (M-024). Lint fixed: pos-ai-alias registered in INTERACTIVE_CMDS.

Gates: make gen && make check && make lint = 0 FAIL, 0 WARN
2026-08-27 02:30:27 -04:00

119 lines
3.5 KiB
Bash
Executable File

#!/usr/bin/env bash
set -euo pipefail
# POS: tree — Show the pos CLI command tree: categories, commands, and subcommands
# POS_FLAGS: --depth
source "$(dirname "$0")/../lib/common.sh" 2>/dev/null || source "$(dirname "$0")/common.sh"
source "$(dirname "$0")/../lib/registry.sh" 2>/dev/null || source "$(dirname "$0")/registry.sh"
usage() {
cat <<EOF
Usage: pos tree [--depth N]
Print the `pos` command tree: every category, command, and subcommand the
dispatcher can reach, annotated with each tool's # POS: description.
pos tree Full command tree
pos tree --depth N Limit nesting depth (1 = root only)
pos tree --help This help
EOF
exit 0
}
depth=""
case "${1:-}" in
-h|--help) usage ;;
--depth)
[ $# -ge 2 ] || err "--depth requires a number"
depth="$2"
[[ "$depth" =~ ^[0-9]+$ ]] || err "--depth must be a number"
shift 2
;;
-*) err "unknown flag: $1" ;;
esac
self="$(cd "$(dirname "$0")" && pwd)"
declare -A children meta
maxw=0
# add <parent> <word> <type> <desc> — parent "/" is the root.
add() {
local parent="$1" word="$2" type="$3" desc="$4" path
if [ "$parent" = "/" ]; then path="$word"; else path="$parent/$word"; fi
children["$parent"]+=" $word"
meta["$path"]="$type|$desc"
}
reg_scan "$self"
for tool_key in $(reg_list); do
cat="$(reg_lookup "$tool_key" cat)"
desc="$(reg_lookup "$tool_key" desc)"
sc="$(reg_lookup "$tool_key" subcmds)"
deps="$(reg_lookup "$tool_key" deps)"
name="${tool_key}"
if [ -n "$deps" ]; then
desc="$desc [deps: $deps]"
fi
words=(${name//-/ })
parent="/"
for ((i=0; i<${#words[@]}; i++)); do
word="${words[$i]}"
if [ "$parent" = "/" ]; then path="$word"; else path="$parent/$word"; fi
if [ $((i + 1)) -eq ${#words[@]} ]; then
add "$parent" "$word" "tool" "$desc"
else
[ -n "${meta[$path]:-}" ] || add "$parent" "$word" "cat" ""
fi
parent="$path"
done
for s in $sc; do
add "$parent" "$s" "sub" ""
done
done
# pad = widest "pos <full path>" leaf, so descriptions align
for p in "${!meta[@]}"; do
case "${meta[$p]%%|*}" in
tool|sub)
len=$(( ${#p} + 4 ))
[ "$len" -gt "$maxw" ] && maxw=$len
;;
esac
done
render() {
local parent="$1" prefix="$2" lvl="$3"
if [ -n "$depth" ] && [ "$lvl" -ge "$depth" ]; then return; fi
local -a kids
mapfile -t kids < <(printf '%s\n' ${children[$parent]:-} | sort -u)
local i word path type desc conn nextpref last
for ((i=0; i<${#kids[@]}; i++)); do
word="${kids[$i]}"
if [ "$parent" = "/" ]; then path="$word"; else path="$parent/$word"; fi
last=$(( i == ${#kids[@]} - 1 ))
if [ "$last" -eq 1 ]; then conn="└── "; nextpref="$prefix "; else conn="├── "; nextpref="$prefix│ "; fi
type="${meta[$path]%%|*}"
desc="${meta[$path]#*|}"
case "$type" in
cat)
printf '%s%s%s/\n' "$prefix" "$conn" "$word"
render "$path" "$nextpref" $((lvl+1))
;;
tool)
printf '%s%s%-*s# %s\n' "$prefix" "$conn" "$((maxw+1))" "pos ${path//\// }" "$desc"
if [ -n "${children[$path]:-}" ]; then
render "$path" "$nextpref" $((lvl+1))
fi
;;
sub)
printf '%s%s%s\n' "$prefix" "$conn" "pos ${path//\// }"
;;
esac
done
}
printf 'pos\n'
render "/" "" 1