Hands-on ergonomics for Bryan's Gundersen testing. All three changes are backward-compatible — every old name still works. 1. `help` is now the canonical reference command (live, never-drifts table from bin/ + each tool's help block). `cheat` kept as a thin alias. 2. Prefix-free short commands: nc-table→table, nc-parse→parse, nc-msgs→msgs, nc-status→status, nc-engine→engine, nc-xlate→xlate, nc-inbound→inbound, plus the write tools (create-thread, set-field, insert-protocol, make-jump, provision-jumps, tclgen, document, revisions, diff-interface, smat-diff, regression). COLLISION GUARD: nc-find→`nfind` (NOT `find` — would shadow the system find on PATH); nc-paths keeps `paths`. Every nc-* name retained as a backward-compat alias. Tab-completion + the help/cheat table updated. 3. HCISITEDIR auto-init in the shared preflight (bin/_nc_common.sh): HCIROOT + HCISITE still required, but $HCIROOT/$HCISITE is created if missing rather than erroring. Conservative + idempotent; respects an operator-set HCISITEDIR. VERSION→0.9.6, MANIFEST regenerated (--check clean), bash -n clean. Co-Authored-By: Clover (Claude Opus 4.8) <noreply@anthropic.com>
193 lines
8.2 KiB
Bash
193 lines
8.2 KiB
Bash
# nc-completion.bash — dynamic bash tab-completion for the Cloverleaf-Larry
|
|
# short commands. Source this file (the installer adds it to your shell rc):
|
|
#
|
|
# source /path/to/cloverleaf-larry/bin/nc-completion.bash
|
|
#
|
|
# THREE LEVELS, all enumerated LIVE off the NetConfig site tree under $HCIROOT
|
|
# (never a static list — it reflects exactly what the tools see right now):
|
|
#
|
|
# 1. COMMAND NAMES — `tb<TAB>` → tbn tbp tbh tbpr ; `nc-<TAB>` → nc-find …
|
|
# 2. SITE NAMES — `tbn <TAB>` → adt ancout codamx epic …
|
|
# 3. THREAD NAMES — `paths <TAB>`, `route_test --source-thread <TAB>`,
|
|
# `where <TAB>` → IB_ADT_epic MUX_ADT_ancout …
|
|
#
|
|
# Sites/threads are read through the SAME parser the tools use (lib/nc-parse.sh
|
|
# via bin/_nc_common.sh), so completion can never drift from reality. Results
|
|
# are cached per (HCIROOT, mtime-of-tree) for the life of the shell so repeated
|
|
# TABs are instant; the cache invalidates automatically when a NetConfig under
|
|
# $HCIROOT changes.
|
|
|
|
# Locate _nc_common.sh relative to THIS completion file (works for both the repo
|
|
# layout and an installed copy). Sourced once at load.
|
|
if [ -z "${_NC_COMPLETION_COMMON:-}" ]; then
|
|
_ncc_self="${BASH_SOURCE[0]}"
|
|
_NC_COMPLETION_COMMON="$(cd "$(dirname "$_ncc_self")" 2>/dev/null && pwd)/_nc_common.sh"
|
|
unset _ncc_self
|
|
fi
|
|
|
|
# --- cache key = HCIROOT + newest NetConfig mtime (cheap staleness check) ----
|
|
_nc_comp_cachekey() {
|
|
local root="${HCIROOT:-}"
|
|
[ -n "$root" ] && [ -d "$root" ] || { printf 'none'; return; }
|
|
local newest
|
|
newest="$(find "$root" -maxdepth 2 -name NetConfig -printf '%T@\n' 2>/dev/null | sort -rn | head -1)"
|
|
printf '%s@%s' "$root" "${newest:-0}"
|
|
}
|
|
|
|
# --- live site list (cached) -------------------------------------------------
|
|
_NC_COMP_SITES_KEY=""; _NC_COMP_SITES=""
|
|
_nc_comp_sites() {
|
|
local key; key="$(_nc_comp_cachekey)"
|
|
if [ "$key" != "$_NC_COMP_SITES_KEY" ]; then
|
|
[ -f "$_NC_COMPLETION_COMMON" ] && . "$_NC_COMPLETION_COMMON"
|
|
_NC_COMP_SITES="$(_nc_sites 2>/dev/null | tr '\n' ' ')"
|
|
_NC_COMP_SITES_KEY="$key"
|
|
fi
|
|
printf '%s' "$_NC_COMP_SITES"
|
|
}
|
|
|
|
# --- live thread list (cached) -----------------------------------------------
|
|
_NC_COMP_THREADS_KEY=""; _NC_COMP_THREADS=""
|
|
_nc_comp_threads() {
|
|
local key; key="$(_nc_comp_cachekey)"
|
|
if [ "$key" != "$_NC_COMP_THREADS_KEY" ]; then
|
|
[ -f "$_NC_COMPLETION_COMMON" ] && . "$_NC_COMPLETION_COMMON"
|
|
_NC_COMP_THREADS="$(_nc_threads 2>/dev/null | tr '\n' ' ')"
|
|
_NC_COMP_THREADS_KEY="$key"
|
|
fi
|
|
printf '%s' "$_NC_COMP_THREADS"
|
|
}
|
|
|
|
# --- generic completer: offer sites + threads (union) for a name argument ----
|
|
# Most short commands take "a site OR a thread"; offering both satisfies every
|
|
# documented level (tbn→sites, paths/where→threads) without over-fitting.
|
|
_nc_complete_names() {
|
|
local cur="${COMP_WORDS[COMP_CWORD]}"
|
|
local pool="$(_nc_comp_sites) $(_nc_comp_threads)"
|
|
COMPREPLY=( $(compgen -W "$pool" -- "$cur") )
|
|
}
|
|
|
|
# --- threads-only completer (route_test --source-thread, paths, where) -------
|
|
_nc_complete_threads_only() {
|
|
local cur="${COMP_WORDS[COMP_CWORD]}"
|
|
COMPREPLY=( $(compgen -W "$(_nc_comp_threads)" -- "$cur") )
|
|
}
|
|
|
|
# --- sites-only completer ----------------------------------------------------
|
|
_nc_complete_sites_only() {
|
|
local cur="${COMP_WORDS[COMP_CWORD]}"
|
|
COMPREPLY=( $(compgen -W "$(_nc_comp_sites)" -- "$cur") )
|
|
}
|
|
|
|
# --- per-command completers ---------------------------------------------------
|
|
# tbn: thread-name search, but a site name is the natural first thing operators
|
|
# type (tbn adt) — so offer the union (sites + threads). Same for tbh/tbpr.
|
|
_nc_complete_tbn() { _nc_complete_names; }
|
|
_nc_complete_tbh() { _nc_complete_names; }
|
|
_nc_complete_tbpr() {
|
|
# process-name search → no live process index; fall back to threads+sites.
|
|
_nc_complete_names
|
|
}
|
|
|
|
# tbp: v1 "thread by port". The task wants this to surface thread names on TAB,
|
|
# so we complete against threads (the operator can still type a bare port).
|
|
_nc_complete_tbp() { _nc_complete_threads_only; }
|
|
|
|
# where: positional <thread>.
|
|
_nc_complete_where() { _nc_complete_threads_only; }
|
|
|
|
# paths: first arg <thread>, optional 2nd arg <site>, then flags.
|
|
_nc_complete_paths() {
|
|
local cur="${COMP_WORDS[COMP_CWORD]}" prev="${COMP_WORDS[COMP_CWORD-1]}"
|
|
case "$cur" in
|
|
-*) COMPREPLY=( $(compgen -W "--up --down --site-only --all --site --format --hciroot" -- "$cur") ); return ;;
|
|
esac
|
|
case "$prev" in
|
|
--site) _nc_complete_sites_only; return ;;
|
|
--format) COMPREPLY=( $(compgen -W "v1 table tsv jsonl nodes" -- "$cur") ); return ;;
|
|
esac
|
|
# 1st positional → thread; 2nd positional → site. Count prior non-flag words.
|
|
local i nonflag=0
|
|
for ((i=1; i<COMP_CWORD; i++)); do
|
|
case "${COMP_WORDS[i]}" in -*) ;; *) ((nonflag++)) ;; esac
|
|
done
|
|
if [ "$nonflag" -eq 0 ]; then _nc_complete_threads_only
|
|
elif [ "$nonflag" -eq 1 ]; then _nc_complete_sites_only
|
|
else _nc_complete_names; fi
|
|
}
|
|
|
|
# route_test: <thread> <file>, or --source-thread <thread> [--file <file>].
|
|
_nc_complete_route_test() {
|
|
local cur="${COMP_WORDS[COMP_CWORD]}" prev="${COMP_WORDS[COMP_CWORD-1]}"
|
|
case "$prev" in
|
|
--source-thread|--thread) _nc_complete_threads_only; return ;;
|
|
--file) COMPREPLY=( $(compgen -f -- "$cur") ); return ;;
|
|
esac
|
|
case "$cur" in
|
|
-*) COMPREPLY=( $(compgen -W "--source-thread --file --dry-run" -- "$cur") ); return ;;
|
|
esac
|
|
# bare first positional → thread; subsequent → file path.
|
|
local i nonflag=0
|
|
for ((i=1; i<COMP_CWORD; i++)); do
|
|
case "${COMP_WORDS[i]}" in --source-thread|--thread|--file) ((i++)) ;; -*) ;; *) ((nonflag++)) ;; esac
|
|
done
|
|
if [ "$nonflag" -eq 0 ]; then _nc_complete_threads_only
|
|
else COMPREPLY=( $(compgen -f -- "$cur") ); fi
|
|
}
|
|
|
|
# nc-find: mode-aware (after --name/--where/etc, offer names; after --port, nothing).
|
|
_nc_complete_nc_find() {
|
|
local cur="${COMP_WORDS[COMP_CWORD]}" prev="${COMP_WORDS[COMP_CWORD-1]}"
|
|
case "$prev" in
|
|
--name|--where) _nc_complete_names; return ;;
|
|
--process) _nc_complete_threads_only; return ;;
|
|
--format) COMPREPLY=( $(compgen -W "table tsv jsonl" -- "$cur") ); return ;;
|
|
--hciroot) COMPREPLY=( $(compgen -d -- "$cur") ); return ;;
|
|
esac
|
|
COMPREPLY=( $(compgen -W "--name --port --host --process --where --xlate --tclproc --format --hciroot --case-sensitive" -- "$cur") )
|
|
}
|
|
|
|
# nc-paths shares the paths completer.
|
|
_nc_complete_nc_paths() { _nc_complete_paths; }
|
|
|
|
# nfind is the prefix-free name for nc-find (NOT `find` — that would shadow the
|
|
# system find on PATH). Same mode-aware completer.
|
|
_nc_complete_nfind() { _nc_complete_nc_find; }
|
|
|
|
# The remaining prefix-free short commands (v0.9.6: table, parse, status, …) take
|
|
# "a site OR a thread" as their natural first argument, so the union completer is
|
|
# the right default. (Their nc-* aliases get the SAME completer below.)
|
|
_nc_complete_table() { _nc_complete_names; }
|
|
_nc_complete_parse() { _nc_complete_names; }
|
|
_nc_complete_msgs() { _nc_complete_threads_only; }
|
|
_nc_complete_status() { _nc_complete_names; }
|
|
_nc_complete_engine() { _nc_complete_names; }
|
|
_nc_complete_xlate() { _nc_complete_names; }
|
|
|
|
# Register. Canonical prefix-free short commands first (the headline ergonomics);
|
|
# then the kept nc-* aliases get the SAME completer so muscle memory still tabs.
|
|
complete -F _nc_complete_tbn tbn
|
|
complete -F _nc_complete_tbp tbp
|
|
complete -F _nc_complete_tbh tbh
|
|
complete -F _nc_complete_tbpr tbpr
|
|
complete -F _nc_complete_where where
|
|
complete -F _nc_complete_paths paths
|
|
complete -F _nc_complete_route_test route_test
|
|
complete -F _nc_complete_nfind nfind
|
|
complete -F _nc_complete_table table
|
|
complete -F _nc_complete_parse parse
|
|
complete -F _nc_complete_msgs msgs
|
|
complete -F _nc_complete_status status
|
|
complete -F _nc_complete_engine engine
|
|
complete -F _nc_complete_xlate xlate
|
|
|
|
# Backward-compat aliases — same completer as their canonical short command.
|
|
complete -F _nc_complete_nc_find nc-find
|
|
complete -F _nc_complete_nc_paths nc-paths
|
|
complete -F _nc_complete_table nc-table
|
|
complete -F _nc_complete_parse nc-parse
|
|
complete -F _nc_complete_msgs nc-msgs
|
|
complete -F _nc_complete_status nc-status
|
|
complete -F _nc_complete_engine nc-engine
|
|
complete -F _nc_complete_xlate nc-xlate
|