Completion
Generate static shell completion scripts for bash, zsh, and fish.
Usage
import { Crust, defineCommand } from "@crustjs/core";
import { completion } from "@crustjs/extensions";
const app = new Crust("my-cli", { version: "1.2.3" })
.extend(completion())
.add(
defineCommand("build", (command) =>
command
.flags({ name: "target", type: "string", choices: ["browser", "bun", "node"] })
.action(() => {}),
),
)
.action(() => {});
await app.execute();
completion() adds a completion <shell> root command and a build hook. The generated bash, zsh, and fish scripts contain static commands, flags, and choices, so they do not call the CLI when the user presses Tab.
Quick shell setup
# Bash, in ~/.bashrc
source <(my-cli completion bash)
# Zsh, after compinit in ~/.zshrc
source <(my-cli completion zsh)
# Fish, in ~/.config/fish/config.fish
status --is-interactive; and my-cli completion fish | sourceHere, my-cli is the installed application executable. Source output only from a CLI you trust, and use one loading method per shell.
New shells generate completion from the installed CLI at startup. Open a new shell after updating the CLI to refresh the definitions.
API
const snapshot = await app.snapshot();
const bash = renderBashCompletion(snapshot);
const zsh = renderZshCompletion(snapshot);
const fish = renderFishCompletion(snapshot);Prop
Type
The renderers accept a root Command Snapshot plus a version override. The runtime command, renderers, and build hook require a version from the option or root metadata.
The positional <shell> accepts bash, zsh, or fish. command changes the contributed command name and the regenerate hint in script headers; the standalone renderers always name completion. Every script targets the root command name as the executable, which crust build requires to match the installed command, so it must follow the installed command name rule.
Value candidates
command.flags({
name: "target",
type: "string",
choices: ["browser", "bun", "node"],
});
// my-cli build --target <Tab> offers browser, bun, and nodeString choices become static candidates. path and unconstrained string values use file completion where supported, while url and json values do not.
Boolean flags do not consume a value. Hidden commands and their descendants are omitted.
Package-manager and advanced setup
$ my-cli completion bash --output-dir completions/
$ ls -1 completions
_my-cli
my-cli
my-cli.fishWith --output-dir, the required <shell> still selects a valid invocation, but the command writes all three files. It preserves unrelated files in that directory.
Build artifacts
.crust/artifacts/completions/my-cli
.crust/artifacts/completions/_my-cli
.crust/artifacts/completions/my-cli.fishThe build hook renders these three files; crust build writes them and records them as artifacts owned by crust:completion.
Saved files and autoload
mkdir -p "${XDG_DATA_HOME:-$HOME/.local/share}/bash-completion/completions"
my-cli completion bash > "${XDG_DATA_HOME:-$HOME/.local/share}/bash-completion/completions/my-cli"Saved files avoid running the generator at shell startup. Autoloading the Bash file requires bash-completion to be installed and initialized.
For Zsh, initialize its completion system and explicitly source the saved file:
mkdir -p ~/.zsh/completions
my-cli completion zsh > ~/.zsh/completions/_my-cli
source ~/.zsh/completions/_my-cli # after compinit or framework initializationCrust's generated Zsh script misses the first Tab completion when loaded from $fpath for binary names containing - or .. Explicit sourcing avoids that issue. Regenerate or replace saved files after CLI changes.