Skip to content

feat(examples): completion-gen — introspection-based completion generator - #47

Open
alfred-agent-broker[bot] wants to merge 1 commit into
afterfrom
feature/completion-gen-plugin
Open

alfred-agent-broker[bot] wants to merge 1 commit into
afterfrom
feature/completion-gen-plugin

Conversation

@alfred-agent-broker

Copy link
Copy Markdown

Summary

Board task 8aa1d407. New shy plugin at examples/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) — this
PR 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/-h introspection) — only when tier 1 finds
nothing: 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 COMPREPLY function (zsh via a bashcompinit shim
reusing that same function; fish depth-limited to one subcommand level
— documented, not hidden).

Verified, not just designed

  • Built the real shy binary and ran shy install ./examples/plugins/completion-gen — installs correctly, shows in
    shy list, dispatches via shy completion-gen <tool>.
  • Load-bearing proof for "no core change needed today": a separate
    test manifest with [[completions]] generate = "shy completion-gen <tool>" installed correctly through shy's unmodified
    install.go — its existing generic generate hook already accepts
    this plugin as a drop-in generator.
  • Native tier-1 verified against a real kubectl (cobra): detected via
    kubectl completion bash, returned kubectl's own real script
    unmodified.
  • Tier-2 verified against a synthetic multi-level fixture (qualified
    headers, a comma-aliased subcommand, a 2-level subtree) — the
    generated completion function was sourced and exercised with
    simulated COMP_WORDS/COMP_CWORD at multiple depths; all cases
    returned 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 --json diagnostics this plugin already emits, a genuine Go
port (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 after

Landed on after (mnab: experimental/parallel, not targeting next
yet) per the board task's explicit routing.

Note for reviewer: after itself is currently missing most of the
buildable source tree (no cmd/, internal/, go.mod at HEAD — only
.claude/ and docs/, with a few .go files oddly flattened under
docs/). Unrelated to this PR and not fixed here, but flagged since it
means this plugin's own manifest/entry-point integration could only be
verified by building shy from a full-source checkout (this repo's
main), not from after directly — worth a look independent of this PR.

Test plan

  • shellcheck clean on all 5 shell files
  • shy install + shy list + shy completion-gen <tool> against a
    real build
  • Existing [[completions]] generate = "..." hook round-trip
    through unmodified install.go
  • Native tier-1 against real kubectl
  • Tier-2 recursive walk + generated-completion runtime behavior
    against a synthetic fixture at multiple COMP_WORDS depths
  • Boundary conditions: --depth 0, --max-calls 0, bad --shell,
    bad numeric flags, unknown tool
  • Operator review of the core-graduation path / whether tier-2
    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

…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
@sonarqubecloud

Copy link
Copy Markdown

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

0 participants