feat(examples): completion-gen — introspection-based completion generator - #47
Open
alfred-agent-broker[bot] wants to merge 1 commit into
Open
alfred-agent-broker[bot] wants to merge 1 commit into
alfred-agent-broker[bot] wants to merge 1 commit into
Conversation
…ator New shy plugin, board 8aa1d407: given an arbitrary binary, generate shell completions by introspecting it, rather than requiring the target to ship its own completion generator. Two tiers: - Tier 1 (native passthrough): 8 completion-generator conventions across cobra/clap/click/argcomplete, per requested shell — a strict superset of shy's own `completion add` (which tries only 3, hardcoded to bash). - Tier 2 (--help/-h introspection, only when tier 1 finds nothing): parses subcommand sections and scans for flag-shaped tokens with no header required, then recurses into discovered subcommands up to a configurable depth/call budget, emitting a context-aware bash COMPREPLY function (zsh via a bashcompinit shim reusing that same function; fish depth-limited to one subcommand level). Verified end to end against a real `shy` build: installs via `shy install`, dispatches via `shy completion-gen <tool>`, and — the load- bearing proof for the "no core change needed today" claim — a separate manifest's existing `[[completions]] generate = "shy completion-gen <tool>"` hook installs correctly through shy's UNMODIFIED install.go. Native tier-1 verified against a real cobra tool (kubectl); tier-2 verified against a synthetic multi-level fixture with simulated COMP_WORDS/COMP_CWORD at multiple depths, all producing correct candidates. Found and fixed three real bugs during that testing, documented inline where they'd bite again: bash's IFS-whitespace field-collapse silently eating the root node's empty-string tab-separated record fields (fixed by switching the record delimiter to the ASCII Unit Separator); bash outright refusing an empty string as an associative-array subscript, which would have broken every top-level completion (fixed via a `@root@` sentinel key); and a subshell dropping a side-channel global variable set inside a `$(...)` command substitution (fixed by returning that value through a plain global instead of stdout+state conflation). Positioned as a plugin, not a core change, per docs/04-agent- instructions.md's "plugins absorb feature pressure" — README.md documents the explicit core-graduation path (operator sign-off, field usage via --json diagnostics, a genuine Go port, and two product decisions the plugin deliberately does not presume: fallback-boundary and shell scope). Landed on `after` (mnab: experimental/parallel, not targeting `next` yet) per the board task's explicit routing. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016gewTtnAoXmfR4NnWzwT74
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.



Summary
Board task
8aa1d407. New shy plugin atexamples/plugins/completion-gen/:given an arbitrary binary, generate shell completions by introspecting
what it supports, rather than requiring the target to ship its own
completion generator. Built with an explicit eye toward eventually
replacing
shy completion add(internal/cmd/completion.go) — thisPR is the plugin-first proving step per
docs/04-agent-instructions.md's"plugins absorb feature pressure" discipline, not a core change.
What it does
Tier 1 (native passthrough) — tries 8 completion-generator
conventions across cobra/clap/click/argcomplete, per requested shell.
Strict superset of
shy completion add's own 3 (hardcoded to bash).Tier 2 (
--help/-hintrospection) — only when tier 1 findsnothing: parses subcommand sections (no assumption about which
framework), scans the whole text for flag-shaped tokens with no header
required (works for plain POSIX getopt tools), recurses into discovered
subcommands up to a configurable depth/call budget, and emits a
context-aware bash
COMPREPLYfunction (zsh via abashcompinitshimreusing that same function; fish depth-limited to one subcommand level
— documented, not hidden).
Verified, not just designed
shybinary and ranshy install ./examples/plugins/completion-gen— installs correctly, shows inshy list, dispatches viashy completion-gen <tool>.test manifest with
[[completions]] generate = "shy completion-gen <tool>"installed correctly through shy's unmodifiedinstall.go— its existing genericgeneratehook already acceptsthis plugin as a drop-in generator.
kubectl(cobra): detected viakubectl completion bash, returned kubectl's own real scriptunmodified.
headers, a comma-aliased subcommand, a 2-level subtree) — the
generated completion function was sourced and exercised with
simulated
COMP_WORDS/COMP_CWORDat multiple depths; all casesreturned the correct candidates.
Found and fixed three real bugs along the way (documented inline where
they'd bite again): bash's IFS-whitespace field-collapse silently
eating the root node's empty tab-separated record fields (switched the
record delimiter to the ASCII Unit Separator); bash flatly refusing an
empty string as an associative-array subscript, which would have broken
every top-level completion lookup (fixed via a
@root@sentinel key);and a subshell dropping a side-channel global set inside a
$(...)command substitution (fixed by returning that value through a plain
global instead of conflating it with stdout).
Core-graduation path
Documented in the plugin's own README: operator sign-off (new-subcommand
semantics change per the approval-boundaries doc), field usage evidenced
via the
--jsondiagnostics this plugin already emits, a genuine Goport (not a shell-out from the compiled binary), and two product
decisions this plugin deliberately does not presume — the
introspection-fallback boundary, and v1 shell scope.
Why
afterLanded on
after(mnab: experimental/parallel, not targetingnextyet) per the board task's explicit routing.
Note for reviewer:
afteritself is currently missing most of thebuildable source tree (no
cmd/,internal/,go.modat HEAD — only.claude/anddocs/, with a few.gofiles oddly flattened underdocs/). Unrelated to this PR and not fixed here, but flagged since itmeans this plugin's own manifest/entry-point integration could only be
verified by building
shyfrom a full-source checkout (this repo'smain), not fromafterdirectly — worth a look independent of this PR.Test plan
shellcheckclean on all 5 shell filesshy install+shy list+shy completion-gen <tool>against areal build
[[completions]] generate = "..."hook round-tripthrough unmodified
install.gokubectlagainst a synthetic fixture at multiple
COMP_WORDSdepths--depth 0,--max-calls 0, bad--shell,bad numeric flags, unknown tool
fallback should ever be default-on in a future core version
Co-Authored-By: Claude Sonnet 5 noreply@anthropic.com
https://claude.ai/code/session_016gewTtnAoXmfR4NnWzwT74