7e2ecba219
- bin/pos: usage() CATEGORIES now auto-derived from pos-* filenames (kill the stale cheat-sheet bug class); only EXAMPLES stays hand-written - # POS: / # POS_FLAGS: headers on all 15 tools = single source of truth for generated docs; template updated - scripts/gen-docs.sh: regenerates GEN-marker sections (bin tree, dispatch table, no-common.sh list, line-count table, completion flags) - scripts/check-sync.sh: bash -n + exec bits + doc/code drift + smoke - Makefile: make gen / make check; scripts/install-hooks.sh: opt-in hook - DEV.md + AGENT_Context: add-a-tool flow is now header + make check
139 lines
4.4 KiB
Bash
Executable File
139 lines
4.4 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
set -euo pipefail
|
|
# Regenerate code-derived doc sections between GEN markers.
|
|
#
|
|
# scripts/gen-docs.sh rewrite files in place
|
|
# scripts/gen-docs.sh --check verify only; exit 1 on any drift
|
|
#
|
|
# Sources of truth:
|
|
# - bin/pos-* filenames → category, subcommand
|
|
# - "# POS:" header line → one-line description
|
|
# - "# POS_FLAGS:" line → flag completion list (flag-style tools only)
|
|
|
|
root="$(cd "$(dirname "$0")/.." && pwd)"
|
|
mode="write"
|
|
[ "${1:-}" = "--check" ] && mode="check"
|
|
|
|
ctx="$root/DOC/AGENT_Context_Project.md"
|
|
comp="$root/completions/pos.bash"
|
|
|
|
# ── Collect tools: "cat|sub|desc|flags" ────────────────────────
|
|
tools=()
|
|
for f in "$root"/bin/pos-*; do
|
|
[ -x "$f" ] || continue
|
|
name="${f##*/pos-}"
|
|
cat="${name%%-*}"
|
|
sub="${name#*-}"
|
|
desc="$(sed -n '/^# POS: /{s/^# POS: //;p;q}' "$f")"
|
|
[ -n "$desc" ] || { echo "gen-docs: no '# POS:' header in $f" >&2; exit 1; }
|
|
desc="${desc#*— }"
|
|
flags="$(sed -n '/^# POS_FLAGS: /{s/^# POS_FLAGS: //;p;q}' "$f")"
|
|
tools+=("$cat|$sub|$desc|$flags")
|
|
done
|
|
mapfile -t tools < <(printf '%s\n' "${tools[@]}" | sort)
|
|
|
|
# ── Block generators (emit inner content only, no markers) ──────
|
|
gen_tree() {
|
|
local width=0 cat sub desc flags name t
|
|
for t in "${tools[@]}"; do
|
|
IFS='|' read -r cat sub desc flags <<<"$t"
|
|
name="pos-$cat-$sub"
|
|
[ ${#name} -gt "$width" ] && width=${#name}
|
|
done
|
|
for t in "${tools[@]}"; do
|
|
IFS='|' read -r cat sub desc flags <<<"$t"
|
|
name="pos-$cat-$sub"
|
|
printf '│ ├── %-*s# %s\n' "$((width + 1))" "$name" "$desc"
|
|
done
|
|
}
|
|
|
|
gen_dispatch() {
|
|
local cat sub desc flags t
|
|
for t in "${tools[@]}"; do
|
|
IFS='|' read -r cat sub desc flags <<<"$t"
|
|
printf '| %s | %s | `pos-%s-%s` | %s |\n' "$cat" "$sub" "$cat" "$sub" "$desc"
|
|
done
|
|
}
|
|
|
|
gen_selfcontained() {
|
|
local list=() f base out=""
|
|
for f in "$root"/bin/pos "$root"/bin/pos-*; do
|
|
[ -x "$f" ] || continue
|
|
base="$(basename "$f")"
|
|
grep -q 'common\.sh' "$f" || list+=("$base")
|
|
done
|
|
for b in "${list[@]}"; do
|
|
out+="\`$b\`, "
|
|
done
|
|
echo "${out%, }."
|
|
}
|
|
|
|
gen_filetable() {
|
|
local cat sub desc flags name t
|
|
printf '| `bin/pos` | %s | CLI dispatcher with smart arg matching + logging + category help |\n' "$(wc -l < "$root/bin/pos")"
|
|
for t in "${tools[@]}"; do
|
|
IFS='|' read -r cat sub desc flags <<<"$t"
|
|
name="bin/pos-$cat-$sub"
|
|
printf '| `%s` | %s | %s |\n' "$name" "$(wc -l < "$root/$name")" "$desc"
|
|
done
|
|
printf '| `completions/pos.bash` | %s | Dynamic bash completion |\n' "$(wc -l < "$comp")"
|
|
}
|
|
|
|
gen_posflags() {
|
|
local cat sub desc flags t
|
|
echo "declare -A _pos_flags"
|
|
for t in "${tools[@]}"; do
|
|
IFS='|' read -r cat sub desc flags <<<"$t"
|
|
[ -n "$flags" ] || continue
|
|
printf '_pos_flags[%s-%s]="%s"\n' "$cat" "$sub" "$flags"
|
|
done
|
|
}
|
|
|
|
# ── Replace (write) or verify (check) one marker block ──────────
|
|
regen_block() {
|
|
local file="$1" name="$2"
|
|
local start end newfile tmp
|
|
case "$file" in
|
|
*.bash|*.sh) start="# GEN:START $name"; end="# GEN:END $name" ;;
|
|
*) start="<!-- GEN:START $name -->"; end="<!-- GEN:END $name -->" ;;
|
|
esac
|
|
newfile="$(mktemp)"
|
|
"gen_$name" > "$newfile"
|
|
|
|
grep -qF "$start" "$file" || { echo "gen-docs: missing marker '$start' in $file" >&2; rm -f "$newfile"; exit 1; }
|
|
|
|
if [ "$mode" = "check" ]; then
|
|
local cur
|
|
cur="$(sed -n "/^$start$/,/^$end$/p" "$file" | sed '1d;$d')"
|
|
if [ "$cur" != "$(cat "$newfile")" ]; then
|
|
echo "gen-docs: DRIFT in $file ($name block)" >&2
|
|
diff <(printf '%s\n' "$cur") <(cat "$newfile") >&2 || true
|
|
rm -f "$newfile"
|
|
exit 1
|
|
fi
|
|
else
|
|
tmp="$(mktemp)"
|
|
awk -v start="$start" -v end="$end" -v nf="$newfile" '
|
|
$0==start {
|
|
print
|
|
while ((getline line < nf) > 0) print line
|
|
skip=1
|
|
next
|
|
}
|
|
skip && $0==end { skip=0; print; next }
|
|
skip { next }
|
|
{ print }
|
|
' "$file" > "$tmp"
|
|
mv "$tmp" "$file"
|
|
fi
|
|
rm -f "$newfile"
|
|
}
|
|
|
|
regen_block "$ctx" tree
|
|
regen_block "$ctx" dispatch
|
|
regen_block "$ctx" selfcontained
|
|
regen_block "$ctx" filetable
|
|
regen_block "$comp" posflags
|
|
|
|
echo "gen-docs: $mode OK"
|