feat(help): header-driven --help for development functions, plus the wiring guard
bd-pull, branch, check_fish_deps, claude-docs, claude-pr, fast, poke, qr
and parur take the standard call site. fish-deps instead routes -h/--help
to its existing __fish_deps_help, which previously answered only via the
unknown-subcommand path, printing "Unknown subcommand: --help" and
exiting 1.
This completes the 40 conversions and lands
test_every_user_facing_function_has_help -- acceptance criterion 6, the
check that fails when a new user-facing function ships without help. It
was developed red against the pre-conversion tree and reported exactly
the 40 names of the design's CONVERT table:
bd-pull bkg branch check_fish_deps claude-docs claude-pr cleanup
fast fc fish-deps fzf-update gip gip4 gip6 hist lD ld limine-edit
lock lsr lss lstree lt ltr lx parur poke ports qr sbver screensleep
split spwin steam-dl sudo-toggle swapstat tab tmux-clean upgrade
wake-lock
each as "<name>: no -h/--help handling and not in $__help_exempt", with
the guard exiting 1. The count fell 40 -> 32 -> 24 -> 17 -> 10 -> 0
across the conversion commits. It is committed green so every commit on
this branch passes the suite.
$__help_exempt is the single machine-readable exemption record; a second
assertion fails if an exempt name stops being a published function, so a
rename or deletion upstream cannot silently exempt nothing.
This commit is contained in:
@@ -23,6 +23,8 @@
|
|||||||
# bd-pull myuser/myproject
|
# bd-pull myuser/myproject
|
||||||
# bd-pull rootiest/fish-config
|
# bd-pull rootiest/fish-config
|
||||||
function bd-pull --description 'Pull new Gitea issues into local Beads and link them'
|
function bd-pull --description 'Pull new Gitea issues into local Beads and link them'
|
||||||
|
__fish_help_header (status current-function) $argv; and return 0
|
||||||
|
|
||||||
if not set -q argv[1]; echo "Need repo owner/name"; return 1; end
|
if not set -q argv[1]; echo "Need repo owner/name"; return 1; end
|
||||||
if not set -q GITEA_TOKEN; echo "\$GITEA_TOKEN not set"; return 1; end
|
if not set -q GITEA_TOKEN; echo "\$GITEA_TOKEN not set"; return 1; end
|
||||||
|
|
||||||
|
|||||||
@@ -21,6 +21,8 @@
|
|||||||
# EXAMPLE
|
# EXAMPLE
|
||||||
# branch feature/new-ui
|
# branch feature/new-ui
|
||||||
function branch --description 'Switch to or create a git branch'
|
function branch --description 'Switch to or create a git branch'
|
||||||
|
__fish_help_header (status current-function) $argv; and return 0
|
||||||
|
|
||||||
if not git rev-parse --is-inside-work-tree >/dev/null 2>&1
|
if not git rev-parse --is-inside-work-tree >/dev/null 2>&1
|
||||||
echo "Not a git repo."
|
echo "Not a git repo."
|
||||||
return 1
|
return 1
|
||||||
|
|||||||
@@ -14,5 +14,7 @@
|
|||||||
# EXAMPLE
|
# EXAMPLE
|
||||||
# check_fish_deps
|
# check_fish_deps
|
||||||
function check_fish_deps --description 'Check all fish-related dependencies'
|
function check_fish_deps --description 'Check all fish-related dependencies'
|
||||||
|
__fish_help_header (status current-function) $argv; and return 0
|
||||||
|
|
||||||
fish-deps status
|
fish-deps status
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -15,5 +15,7 @@
|
|||||||
# EXAMPLE
|
# EXAMPLE
|
||||||
# claude-docs
|
# claude-docs
|
||||||
function claude-docs --description 'Claude-code: Sync README with recent changes'
|
function claude-docs --description 'Claude-code: Sync README with recent changes'
|
||||||
|
__fish_help_header (status current-function) $argv; and return 0
|
||||||
|
|
||||||
claude "Analyze the recent changes and update the README.md to ensure all features, setup instructions, and examples are 100% accurate. Prune any obsolete information."
|
claude "Analyze the recent changes and update the README.md to ensure all features, setup instructions, and examples are 100% accurate. Prune any obsolete information."
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -15,5 +15,7 @@
|
|||||||
# EXAMPLE
|
# EXAMPLE
|
||||||
# claude-pr
|
# claude-pr
|
||||||
function claude-pr --description 'Claude-code: New branch, commit, push, and PR'
|
function claude-pr --description 'Claude-code: New branch, commit, push, and PR'
|
||||||
|
__fish_help_header (status current-function) $argv; and return 0
|
||||||
|
|
||||||
claude "Act as a senior engineer. Execute this sequence: 1. Create a new git branch (kebab-case). 2. Stage changes and write a Conventional Commit message. 3. Self-verify the changes by running relevant build/test commands or linting. 4. Push to remote. 5. Create a PR to 'main' including a summary of changes and a 'Manual Verification' section containing a Markdown checklist (- [ ]) of specific, bite-sized steps required to manually verify the functionality."
|
claude "Act as a senior engineer. Execute this sequence: 1. Create a new git branch (kebab-case). 2. Stage changes and write a Conventional Commit message. 3. Self-verify the changes by running relevant build/test commands or linting. 4. Push to remote. 5. Create a PR to 'main' including a summary of changes and a 'Manual Verification' section containing a Markdown checklist (- [ ]) of specific, bite-sized steps required to manually verify the functionality."
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -36,6 +36,8 @@
|
|||||||
# EXAMPLE
|
# EXAMPLE
|
||||||
# fast
|
# fast
|
||||||
function fast --description 'Placeholder for future fast utility'
|
function fast --description 'Placeholder for future fast utility'
|
||||||
|
__fish_help_header (status current-function) $argv; and return 0
|
||||||
|
|
||||||
# ANSI Escape Codes (Standard 16-color palette)
|
# ANSI Escape Codes (Standard 16-color palette)
|
||||||
set -l bold "\e[1m"
|
set -l bold "\e[1m"
|
||||||
set -l italic "\e[3m"
|
set -l italic "\e[3m"
|
||||||
|
|||||||
@@ -66,6 +66,13 @@ function fish-deps --description 'Manage fish shell dependencies'
|
|||||||
_fish_deps_status
|
_fish_deps_status
|
||||||
case install
|
case install
|
||||||
_fish_deps_install $flags
|
_fish_deps_install $flags
|
||||||
|
case -h --help
|
||||||
|
# Reuse the existing menu rather than the header renderer: it
|
||||||
|
# is richer, and it is already the text the unknown-subcommand
|
||||||
|
# path prints. Previously --help fell into `case '*'` and
|
||||||
|
# exited 1 with "Unknown subcommand: --help".
|
||||||
|
__fish_deps_help
|
||||||
|
return 0
|
||||||
case update
|
case update
|
||||||
_fish_deps_update
|
_fish_deps_update
|
||||||
case sync
|
case sync
|
||||||
|
|||||||
@@ -19,6 +19,8 @@
|
|||||||
# EXAMPLE
|
# EXAMPLE
|
||||||
# parur
|
# parur
|
||||||
function parur --description 'Interactively search and remove an installed package using fzf'
|
function parur --description 'Interactively search and remove an installed package using fzf'
|
||||||
|
__fish_help_header (status current-function) $argv; and return 0
|
||||||
|
|
||||||
set -l aur ""
|
set -l aur ""
|
||||||
if type -q paru
|
if type -q paru
|
||||||
set aur paru
|
set aur paru
|
||||||
|
|||||||
@@ -21,6 +21,8 @@
|
|||||||
# EXAMPLE
|
# EXAMPLE
|
||||||
# poke ~/projects/new/src/main.fish
|
# poke ~/projects/new/src/main.fish
|
||||||
function poke --description 'touch with automatic parent directory creation'
|
function poke --description 'touch with automatic parent directory creation'
|
||||||
|
__fish_help_header (status current-function) $argv; and return 0
|
||||||
|
|
||||||
if test (count $argv) -eq 0
|
if test (count $argv) -eq 0
|
||||||
echo (set_color red)"poke: no file specified"(set_color normal) >&2
|
echo (set_color red)"poke: no file specified"(set_color normal) >&2
|
||||||
return 1
|
return 1
|
||||||
|
|||||||
@@ -19,6 +19,8 @@
|
|||||||
# qr "https://example.com"
|
# qr "https://example.com"
|
||||||
# echo "hello" | qr
|
# echo "hello" | qr
|
||||||
function qr --description 'Generate a QR code from text or pipe'
|
function qr --description 'Generate a QR code from text or pipe'
|
||||||
|
__fish_help_header (status current-function) $argv; and return 0
|
||||||
|
|
||||||
if type -q qrencode
|
if type -q qrencode
|
||||||
if set -q argv[1]
|
if set -q argv[1]
|
||||||
echo $argv | qrencode -t utf8
|
echo $argv | qrencode -t utf8
|
||||||
|
|||||||
@@ -289,6 +289,72 @@ function test_help_never_executes_destructive_path
|
|||||||
test $failed -eq 0
|
test $failed -eq 0
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Functions published in the manual that are exempt from the -h/--help
|
||||||
|
# rule. Rationale per entry: AGENTS/specs/2026-09-07-header-driven-help-design.md
|
||||||
|
# §4. This array is the ONLY machine-readable copy of the exempt set.
|
||||||
|
#
|
||||||
|
# EXEMPT-A -- shadows a same-named binary, or forwards $argv to one named
|
||||||
|
# tool that owns its own --help. Intercepting would hide that tool's help,
|
||||||
|
# and for the C1-guarded shadows it also breaks the disabled-fallback
|
||||||
|
# contract, where the bare tool is supposed to answer.
|
||||||
|
set -g __help_exempt \
|
||||||
|
agy antigravity-ide bash cat cdi cffetch cheat claude clone clonet \
|
||||||
|
config-toggle copy docker du dusize fast-cli ffetch gitui gitup jr \
|
||||||
|
joplin less ls mkdir mv paste ping rawfish rg rm search ssh top \
|
||||||
|
view yt-dlp
|
||||||
|
# EXEMPT-B -- invoked by fish, never typed by a user.
|
||||||
|
set -a __help_exempt fish_prompt fish_right_prompt fish_mode_prompt \
|
||||||
|
sponge_filter_secrets
|
||||||
|
|
||||||
|
function test_every_user_facing_function_has_help
|
||||||
|
set -l root (realpath (dirname (status filename))/..)
|
||||||
|
set -l failed 0
|
||||||
|
set -l published
|
||||||
|
|
||||||
|
for f in $root/functions/*.fish
|
||||||
|
set -l lines (string split \n -- (command cat $f))
|
||||||
|
# Published == carries a `# CATEGORY` block, matching
|
||||||
|
# manualtools.parse_functions.
|
||||||
|
contains -- "# CATEGORY" (string trim -- $lines); or continue
|
||||||
|
# Resolve the real defined name; the file stem can disagree
|
||||||
|
# (dops.fish defines `docker` -- see JOB-BRIEF-FINDINGS.md §1).
|
||||||
|
set -l name (string match -rg '^\s*function\s+(\S+)' -- $lines)[1]
|
||||||
|
test -n "$name"; or continue
|
||||||
|
set name (string trim -c "'\"" -- $name)
|
||||||
|
string match -q '_*' -- $name; and continue
|
||||||
|
set -a published $name
|
||||||
|
|
||||||
|
contains -- $name $__help_exempt; and continue
|
||||||
|
|
||||||
|
# Body == everything from the `function` line down, comment lines
|
||||||
|
# dropped, so a header that merely mentions --help cannot pass.
|
||||||
|
set -l body
|
||||||
|
set -l in_body 0
|
||||||
|
for l in $lines
|
||||||
|
test $in_body -eq 1; or string match -qr '^\s*function\s' -- $l; and set in_body 1
|
||||||
|
test $in_body -eq 1; or continue
|
||||||
|
string match -qr '^\s*#' -- $l; and continue
|
||||||
|
set -a body $l
|
||||||
|
end
|
||||||
|
if not string match -qr -- '__fish_help_header|_flag_help|h/help|--help' \
|
||||||
|
(string join \n -- $body)
|
||||||
|
echo " $name: no -h/--help handling and not in \$__help_exempt"
|
||||||
|
set failed 1
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Guard against a stale exempt list: every exempt name must still be a
|
||||||
|
# published function. Catches renames and deletions.
|
||||||
|
for e in $__help_exempt
|
||||||
|
if not contains -- $e $published
|
||||||
|
echo " \$__help_exempt lists '$e', which is no longer published"
|
||||||
|
set failed 1
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
test $failed -eq 0
|
||||||
|
end
|
||||||
|
|
||||||
function functional_test_main
|
function functional_test_main
|
||||||
set -l names (functions -a | string match 'test_*' | sort)
|
set -l names (functions -a | string match 'test_*' | sort)
|
||||||
set -l failed 0
|
set -l failed 0
|
||||||
|
|||||||
Reference in New Issue
Block a user