Skip to content

Shell Completions

Tab completions are configured per-shell using .completions():

.zsh((shell) => shell.completions('completions/_tool.zsh'))
.bash((shell) => shell.completions('completions/tool.bash'))

Lifecycle: All completions are generated only after dotfiles install <tool> succeeds, not during dotfiles generate. This ensures cmd-based completions can execute the installed binary and callbacks receive the actual installed version in ctx.version.

URL completions and hooks: URL-based completion assets are downloaded or extracted during installation before after-install hooks run. This means after-install hooks can patch or sanitize those files in ctx.installedDir / ctx.currentDir before the completion symlink is emitted into the generated shell scripts directory.

PropertyDescription
sourcePath to completion file (relative to toolDir, or absolute path within extracted archive)
urlURL to download completion file or archive from
cmdCommand to generate completions dynamically
binBinary name for completion filename (when different from tool name)

Note: Use one of these combinations:

  • '_tool.zsh' - String path (relative to toolDir or absolute)
  • { source } - Static file (relative to toolDir or absolute)
  • { cmd } - Generate dynamically by running a command
  • { url } - Download direct completion file from URL (filename derived from URL)
  • { url, source } - Download archive, extract, use source as path to file within

The shell callback receives two parameters:

  • shell - The shell configurator for setting up completions, aliases, etc.
  • ctx - Context with version property (only available after installation)

For other context properties (toolDir, currentDir, projectConfig, etc.), use the outer ctx from defineTool.

export default defineTool((install, ctx) =>
install("github-release", { repo: "owner/tool" })
.bin("tool")
.zsh((shell) => shell.completions("completions/_tool.zsh")),
);

For completion files bundled in tool archives:

// Simple path relative to extracted archive
.zsh((shell) => shell.completions('completions/_tool.zsh'))
// Glob pattern for versioned directories
.zsh((shell) => shell.completions('*/complete/_rg'))

Supported glob patterns: *, **, ?, [abc]

For downloading completions from external sources. Supports both direct files and archives.

// Direct completion file download (source is optional - derived from URL)
.zsh((shell) => shell.completions({
url: 'https://raw.githubusercontent.com/user/repo/main/completions/_tool'
}))
// Archive download with source path to file within
.zsh((shell) => shell.completions({
url: 'https://github.com/user/repo/releases/download/v1.0/completions.tar.gz',
source: `${ctx.currentDir}/completions/_tool.zsh`
}))

Note: For archives, source specifies the absolute path to the completion file within the extracted archive. For direct files, source is optional - the filename is derived from the URL.

For completions that need the installed version in the URL, use a callback:

// Direct file with version in URL
.zsh((shell) => shell.completions((ctx) => ({
url: `https://raw.githubusercontent.com/user/repo/${ctx.version}/completions/_tool`
})))
// Archive with version in URL (requires source)
.zsh((shell) => shell.completions((ctx) => ({
url: `https://github.com/user/repo/releases/download/${ctx.version}/completions.tar.gz`,
source: `${ctx.currentDir}/completions/_tool.zsh`
})))

The callback receives ctx with:

  • version - The installed version of the tool (e.g., 'v10.3.0', '15.1.0'), only available after installation completes

URL-based completions are downloaded to ctx.currentDir. For archives, they are automatically extracted and source specifies the path to the completion file within.

Because the download/extract step happens before after-install, hook code can modify the downloaded file in place before the generated shell completion symlink points at it.

Supported archive formats: .tar.gz, .tar.xz, .tar.bz2, .zip, .tar, .tar.lzma, .7z

For tools that generate completions at runtime (recommended for version-dependent completions):

.zsh((shell) => shell.completions({ cmd: 'tool completion zsh' }))
.bash((shell) => shell.completions({ cmd: 'tool completion bash' }))

When tool filename differs from binary name (e.g., curl-script--fnm.tool.ts for binary fnm):

.zsh((shell) => shell.completions({
cmd: 'fnm completions --shell zsh',
bin: 'fnm' // Results in '_fnm' instead of '_curl-script--fnm'
}))

The CLI generates its own completions to <generatedDir>/shell-scripts/zsh/completions/_dotfiles. Commands that accept tool names include all configured tools in their completions.

Reload completions after running dotfiles generate:

Terminal window
autoload -U compinit && compinit