sxcli.dev/completion adds bash and zsh completion to any binary built
on sxcli.dev/fw v0.3.0+. Each shell package
catalogs one service; the composition names what it takes — the same
identity model as every other sxcli service, no blank-import magic:
import (
"sxcli.dev/completion/bash"
"sxcli.dev/completion/zsh"
)
fw.Builder().
Accept(bash.ID, zsh.ID /* , your services… */).
Main()(fw.Solo and fw.Main() compositions accept everything cataloged,
so single-applet binaries just import and go.)
That is the entire integration. There is nothing to declare beyond
what the framework already knows: completions are computed from the
same config structs, usage: tags and registration metadata that
drive the binary's arguments, environment variables and config files.
Declare once, get everything.
Each import registers a system applet (completionbash,
completionzsh) — machinery of the binary, never listed in usage,
invoked by the generated scripts rather than by humans. The script for
a command name is generated by the binary itself:
eval "$(mybin completionbash --script)" # bash
eval "$(mybin completionzsh --script)" # zsh, after compinitIn a busybox-style installation, generate through each symlink you actually created — each name gets its own registration with the target applet baked in:
eval "$(cat completionbash --script)" # ./cat -> mybin
eval "$(ls completionbash --script)"- Applet names — the first word of a multi-applet binary, public applets only.
- Argument names — long forms of the dispatched applet's whole closure, core arguments included; already-used scalars drop out, repeatable slice arguments stay.
- Declared value domains — a field with
Allowedmetadata completes exactly its legal values; the same declaration the framework already enforces at startup. - Files and directories — fields declaring
HintFile/HintDirectoryhand over to the shell's native file completion (the core's own--configdoes this). - Service ids — fields declaring
HintServiceIDcomplete from the binary's actual registry (--disable,--enable). --name=value— bools completetrue/falseafter the=.
Zsh additionally renders each candidate's description from the
usage: text and Doc metadata.
Everything is computed per keystroke against the real configuration:
an --enable already typed on the line changes the closure — and the
completions with it.
The framework core exposes a read-only introspection service; this module consumes it from the outside, with no privileged access — the completion engine sees exactly what any other module could see. The generated scripts are deliberately dumb transports: they forward the shell's raw completion state and every decision happens in Go, where it is tested (unit tests against a fake introspector, integration tests that re-exec a real framework binary, and the generated scripts executed under the real shells).
Bash and zsh are what this module ships — and the machinery they are built on is public API. A third-party adapter (fish, PowerShell, elvish, …) is two small pieces over two packages:
sxcli.dev/completion/engine—Complete(src, query)answers what completes: applet names, argument names, declared domains, file/directory directives. The adapter only decodes its shell's transport into aQueryand encodes theCandidates back.sxcli.dev/completion/script— the--scriptgeneration policy (which target to bake for the name the binary was invoked as), so dispatch semantics are never reimplemented per shell.
The bash and zsh packages are the reference implementations —
each is one template plus the Go that decodes its shell's transport
(bash's word-reassembly makes that the bigger half).
Apache-2.0