Zsh Support
Shuck supports zsh as a first-class shell dialect, including common startup files and zsh-specific plugin environments.
This guide focuses on the zsh-specific pieces that are easy to miss:
- which files Shuck treats as zsh automatically;
- how plugin resolution works for common
oh-my-zshsetups; - when to add config for dynamic plugin lists or custom layouts.
Automatic file detection
Shuck already treats these as shell inputs without requiring a shebang:
*.zsh.zshrc.zshenv.zprofile.zlogin.zlogout
If your zsh files use a custom name, tell Shuck explicitly with per-file-shell:
[lint]
per-file-shell = { "dotfiles/zsh/**" = "zsh" }See the Configuration guide and Settings Reference for the surrounding config surface.
What works automatically
Shuck can resolve real plugin entrypoints for common static oh-my-zsh configs.
For example, this works without extra config when the framework root and plugin list are statically visible:
ZSH="$HOME/.oh-my-zsh"
plugins=(git docker zsh-autosuggestions)
ZSH_THEME=agnoster
source "$ZSH/oh-my-zsh.sh"In that shape, Shuck resolves:
- plugins from
$ZSH/plugins/<name>/<name>.plugin.zsh - built-in themes from
$ZSH/themes/<name>.zsh-theme
Those resolved files are summarized through the normal semantic source-closure pipeline, so their functions, bindings, and reads become available to the analyzed zsh file.
That still only covers what Shuck can recover from real source files. If your zsh setup also relies on framework conventions or settings that are consumed outside the current file, use Contracts to describe that extra behavior.
When to add config
The source file is still the preferred source of truth. Add config when your real zsh setup is harder to recover statically.
Machine-local framework roots
If the file hides the framework root behind machine-local setup, add a root mapping:
[lint.zsh.plugins]
roots = { oh-my-zsh = "~/.oh-my-zsh" }Dynamic plugin lists
If the file computes plugins=(...) dynamically, declare the logical plugin loads you want Shuck to bring into scope:
[lint.zsh.plugins]
plugin-loads = [
{ pattern = "**/.zshrc", framework = "oh-my-zsh", name = "git" },
{ pattern = "**/.zshrc", framework = "oh-my-zsh", name = "docker" },
]Theme loads follow the same pattern:
[lint.zsh.plugins]
theme-loads = [
{ pattern = "**/.zshrc", framework = "oh-my-zsh", name = "agnoster" },
]Custom layouts
If your setup does not match the built-in framework layout, attach raw entrypoints directly:
[lint.zsh.plugins]
entrypoints = [
{ pattern = "**/.zshrc", paths = ["./vendor/prompt/prompt.plugin.zsh"] },
]Contracts for framework behavior
Plugin resolution tells Shuck which files to import. Contracts tell Shuck about the surrounding runtime behavior that those files do not fully expose.
For example, an oh-my-zsh plugin can consume settings from .zshrc even when
those settings are not read lexically in the startup file itself:
[[lint.contracts.custom]]
id = "oh-my-zsh-tmux"
when = { type = "zsh_plugin", framework = "oh-my-zsh", plugin = "tmux" }
files = ["**/.zshrc"]
consumes = { prefixes = ["ZSH_TMUX_"] }See the Contracts guide for the broader model and the Settings Reference for the exact key shape.
CLI equivalents
Everything under [lint.zsh.plugins] also has a matching shuck check flag.
The most useful ones are:
--zsh-plugin-resolutionand--no-zsh-plugin-resolution--zsh-plugin-rootand--extend-zsh-plugin-root--zsh-pluginand--extend-zsh-plugin--zsh-themeand--extend-zsh-theme--zsh-plugin-entrypointand--extend-zsh-plugin-entrypoint
Example:
shuck check \
--zsh-plugin-root 'oh-my-zsh=~/.oh-my-zsh' \
--zsh-plugin '**/.zshrc:oh-my-zsh:git' \
--zsh-theme '**/.zshrc:oh-my-zsh:agnoster' \
.Use the replace forms when you want to set the full value at the command line, and the extend- forms when you want to add on top of config that is already present.
Current scope
Today the built-in resolver is intentionally conservative.
Supported directly:
- static
oh-my-zshplugin resolution - static
oh-my-zshbuilt-in theme resolution - explicit plugin and theme loads from config or CLI
- explicit raw entrypoints from config or CLI
Not supported automatically:
- executing framework loaders or plugin-manager DSLs
- inferring plugin installs from managers such as
zinit,zplug, orantigen - inventing plugin loads when the source and config do not identify them statically
When your setup falls outside the automatic cases, use plugin-loads, theme-loads, or entrypoints to describe only the pieces Shuck should import.