Shell Integration Overview Shell integration covers the APIs and patterns for building tools that extend or interact with Unix shells. This includes completion systems, prompt hooks, key bindings, terminal control, and plugin distribution across Zsh, Bash, and Fish. When to use: Building CLI tool completions, shell plugins, prompt customizations, terminal UI, dotfile managers, installation scripts, or native binary wrappers. When NOT to use: General-purpose scripting unrelated to shell extension (use POSIX scripting reference for standalone scripts), GUI applications, or web server developmen…

--function _expand_dots\n\nfunction _expand_dots\n string repeat -n (math (string length -- $argv[1]) - 1) \"../\"\nend\n```\n\n## Functions\n\n### Autoloaded Function\n\n```fish\n# ~/.config/fish/functions/mytool.fish\nfunction mytool -d \"My custom tool\"\n switch $argv[1]\n case init\n echo \"Initializing...\"\n case build\n echo \"Building...\"\n case '*'\n echo \"Usage: mytool {init|build}\" >&2\n return 1\n end\nend\n```\n\n### Function with Options\n\n```fish\nfunction serve -d \"Start dev server\"\n argparse h/help 'p/port=!_validate_int' -- $argv\n or return\n\n if set -q _flag_help\n echo \"Usage: serve [-p PORT]\"\n return 0\n end\n\n set -l port ($_flag_port; or echo 3000)\n echo \"Serving on port $port\"\nend\n```\n\n## Universal Variables\n\nFish universal variables persist across sessions and sync between running instances:\n\n```fish\n# Set (persists and syncs across all fish sessions)\nset -U EDITOR nvim\nset -U fish_user_paths $HOME/.local/bin $fish_user_paths\n\n# Remove\nset -e -U fish_user_paths[1]\n```\n\n## Version Detection\n\n```fish\nif test (string match -r '\\d+' $FISH_VERSION) -ge 4\n echo \"Fish 4.x features available\"\nend\n\nset -l fish_major (string split . $FISH_VERSION)[1]\nif test \"$fish_major\" -lt 3\n echo \"Fish 3+ required\" >&2\n return 1\nend\n```\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":6784,"content_sha256":"734f59586a0a50cdc9007b215190027857731f85ddbcbf6ad8f9c7018189eb55"},{"filename":"references/plugin-distribution.md","content":"---\ntitle: Plugin Distribution\ndescription: Shell plugin installation scripts, dotfile management, sourcing strategies, version detection, and graceful degradation\ntags:\n [\n plugin,\n installation,\n dotfile,\n sourcing,\n version-detection,\n degradation,\n distribution,\n package-manager,\n ]\n---\n\n# Plugin Distribution\n\n## Installation Script Pattern\n\n```sh\n#!/bin/sh\nset -eu\n\nINSTALL_DIR=\"${HOME}/.local/share/mytool\"\nBIN_DIR=\"${HOME}/.local/bin\"\n\nmain() {\n detect_platform\n download_binary\n install_shell_integration\n print_instructions\n}\n\ndetect_platform() {\n OS=$(uname -s | tr '[:upper:]' '[:lower:]')\n ARCH=$(uname -m)\n case \"$ARCH\" in\n x86_64|amd64) ARCH=\"x86_64\" ;;\n aarch64|arm64) ARCH=\"arm64\" ;;\n *) die \"Unsupported architecture: $ARCH\" ;;\n esac\n}\n\ndownload_binary() {\n local url=\"https://github.com/org/mytool/releases/latest/download/mytool-${OS}-${ARCH}\"\n mkdir -p \"$BIN_DIR\"\n\n if command -v curl >/dev/null 2>&1; then\n curl -fsSL \"$url\" -o \"${BIN_DIR}/mytool\"\n elif command -v wget >/dev/null 2>&1; then\n wget -qO \"${BIN_DIR}/mytool\" \"$url\"\n else\n die \"curl or wget required\"\n fi\n chmod +x \"${BIN_DIR}/mytool\"\n}\n\ninstall_shell_integration() {\n mkdir -p \"$INSTALL_DIR\"\n \"${BIN_DIR}/mytool\" --generate-completions zsh > \"$INSTALL_DIR/completions.zsh\" 2>/dev/null || true\n \"${BIN_DIR}/mytool\" --generate-completions bash > \"$INSTALL_DIR/completions.bash\" 2>/dev/null || true\n \"${BIN_DIR}/mytool\" --generate-completions fish > \"$INSTALL_DIR/completions.fish\" 2>/dev/null || true\n}\n\nprint_instructions() {\n cat \u003c\u003c'EOF'\nInstallation complete!\n\nAdd to your shell config:\n\n # Bash (~/.bashrc)\n eval \"$(mytool init bash)\"\n\n # Zsh (~/.zshrc)\n eval \"$(mytool init zsh)\"\n\n # Fish (~/.config/fish/config.fish)\n mytool init fish | source\nEOF\n}\n\ndie() {\n printf 'Error: %s\\n' \"$1\" >&2\n exit 1\n}\n\nmain \"$@\"\n```\n\n## Shell Init Command Pattern\n\nMany tools generate shell integration via a `tool init \u003cshell>` command:\n\n```sh\n# The init command outputs shell code to stdout\nmytool_init_zsh() {\n cat \u003c\u003c'INIT'\n_mytool_hook() {\n emulate -L zsh\n eval \"$(mytool hook zsh)\"\n}\nadd-zsh-hook precmd _mytool_hook\n\nif [[ -f \"${MYTOOL_DIR}/completions.zsh\" ]]; then\n source \"${MYTOOL_DIR}/completions.zsh\"\nfi\nINIT\n}\n\nmytool_init_bash() {\n cat \u003c\u003c'INIT'\n_mytool_hook() {\n eval \"$(mytool hook bash)\"\n}\nPROMPT_COMMAND=\"${PROMPT_COMMAND:+$PROMPT_COMMAND;} _mytool_hook\"\n\nif [[ -f \"${MYTOOL_DIR}/completions.bash\" ]]; then\n source \"${MYTOOL_DIR}/completions.bash\"\nfi\nINIT\n}\n\nmytool_init_fish() {\n cat \u003c\u003c'INIT'\nfunction _mytool_hook --on-event fish_prompt\n mytool hook fish | source\nend\n\nif test -f \"$MYTOOL_DIR/completions.fish\"\n source \"$MYTOOL_DIR/completions.fish\"\nend\nINIT\n}\n```\n\n## Sourcing Strategies\n\n### Direct Sourcing\n\n```sh\n# Zsh: simple but runs every shell startup\nsource \"$HOME/.local/share/mytool/init.zsh\"\n```\n\n### Lazy Loading (Zsh)\n\nDefer loading until the command is first used:\n\n```zsh\nmytool() {\n unfunction mytool\n eval \"$(command mytool init zsh)\"\n mytool \"$@\"\n}\n```\n\n### Cached Eval\n\nCache the init output to avoid running the binary on every shell startup:\n\n```sh\n_mytool_cache=\"$HOME/.cache/mytool/init.zsh\"\n\nif [ ! -f \"$_mytool_cache\" ] || [ \"$(mytool --version 2>/dev/null)\" != \"$(head -1 \"$_mytool_cache\" 2>/dev/null)\" ]; then\n mkdir -p \"${_mytool_cache%/*}\"\n {\n mytool --version\n mytool init zsh\n } > \"$_mytool_cache\" 2>/dev/null\nfi\n\nsource \"$_mytool_cache\"\n```\n\n### Compile Zsh Scripts\n\n```zsh\n# Compile for faster loading\nif [[ ! -f \"${init_script}.zwc\" ]] || [[ \"$init_script\" -nt \"${init_script}.zwc\" ]]; then\n zcompile \"$init_script\"\nfi\nsource \"$init_script\"\n```\n\n## Shell Detection\n\n```sh\ndetect_shell() {\n local shell_name\n shell_name=$(basename \"$SHELL\")\n case \"$shell_name\" in\n zsh) echo \"zsh\" ;;\n bash) echo \"bash\" ;;\n fish) echo \"fish\" ;;\n *) echo \"unknown\" ;;\n esac\n}\n\n# More reliable: check current running shell\ndetect_current_shell() {\n if [ -n \"${ZSH_VERSION:-}\" ]; then\n echo \"zsh\"\n elif [ -n \"${BASH_VERSION:-}\" ]; then\n echo \"bash\"\n elif [ -n \"${FISH_VERSION:-}\" ]; then\n echo \"fish\"\n else\n echo \"sh\"\n fi\n}\n```\n\n## Version Detection and Feature Gating\n\n```sh\n# Zsh version check\ncheck_zsh_version() {\n if [ -n \"${ZSH_VERSION:-}\" ]; then\n autoload -Uz is-at-least\n if ! is-at-least 5.2; then\n echo \"Warning: Zsh 5.2+ recommended\" >&2\n return 1\n fi\n fi\n}\n\n# Bash version check\ncheck_bash_version() {\n if [ -n \"${BASH_VERSION:-}\" ]; then\n local major=\"${BASH_VERSINFO[0]}\"\n local minor=\"${BASH_VERSINFO[1]}\"\n if [ \"$major\" -lt 4 ] || { [ \"$major\" -eq 4 ] && [ \"$minor\" -lt 0 ]; }; then\n echo \"Warning: Bash 4+ recommended\" >&2\n return 1\n fi\n fi\n}\n\n# Feature gating\nsetup_completions() {\n if [ -n \"${ZSH_VERSION:-}\" ]; then\n setup_zsh_completions\n elif [ -n \"${BASH_VERSION:-}\" ]; then\n if [ \"${BASH_VERSINFO[0]}\" -ge 4 ]; then\n setup_bash4_completions\n else\n setup_bash3_completions\n fi\n fi\n}\n```\n\n## Dotfile Management\n\n### XDG Base Directory Compliance\n\n```sh\nconfig_dir=\"${XDG_CONFIG_HOME:-$HOME/.config}/mytool\"\ndata_dir=\"${XDG_DATA_HOME:-$HOME/.local/share}/mytool\"\ncache_dir=\"${XDG_CACHE_HOME:-$HOME/.cache}/mytool\"\nstate_dir=\"${XDG_STATE_HOME:-$HOME/.local/state}/mytool\"\n\nmkdir -p \"$config_dir\" \"$data_dir\" \"$cache_dir\" \"$state_dir\"\n```\n\n### Safe Config File Modification\n\n```sh\nadd_to_shell_config() {\n local config_file=\"$1\"\n local init_line=\"$2\"\n local marker=\"# mytool\"\n\n if [ ! -f \"$config_file\" ]; then\n return 1\n fi\n\n if grep -qF \"$marker\" \"$config_file\" 2>/dev/null; then\n return 0\n fi\n\n printf '\\n%s\\n%s\\n' \"$marker\" \"$init_line\" >> \"$config_file\"\n}\n\ninstall_to_shell() {\n local shell\n shell=$(detect_shell)\n\n case \"$shell\" in\n zsh)\n add_to_shell_config \"${ZDOTDIR:-$HOME}/.zshrc\" 'eval \"$(mytool init zsh)\"'\n ;;\n bash)\n add_to_shell_config \"$HOME/.bashrc\" 'eval \"$(mytool init bash)\"'\n ;;\n fish)\n local fish_conf=\"${XDG_CONFIG_HOME:-$HOME/.config}/fish/conf.d\"\n mkdir -p \"$fish_conf\"\n mytool init fish > \"$fish_conf/mytool.fish\"\n ;;\n esac\n}\n```\n\n## Uninstallation\n\n```sh\nuninstall() {\n rm -f \"${BIN_DIR}/mytool\"\n rm -rf \"${INSTALL_DIR}\"\n\n # Remove from shell configs\n for rc in \"$HOME/.bashrc\" \"$HOME/.zshrc\" \"${ZDOTDIR:-$HOME}/.zshrc\"; do\n if [ -f \"$rc\" ]; then\n sed -i.bak '/# mytool/d;/mytool init/d' \"$rc\"\n rm -f \"${rc}.bak\"\n fi\n done\n\n # Remove fish config\n rm -f \"${XDG_CONFIG_HOME:-$HOME/.config}/fish/conf.d/mytool.fish\"\n rm -f \"${XDG_CONFIG_HOME:-$HOME/.config}/fish/completions/mytool.fish\"\n}\n```\n\n## Plugin Manager Integration\n\n### Oh My Zsh\n\n```text\n# Place in ~/.oh-my-zsh/custom/plugins/mytool/\nmytool/\n├── mytool.plugin.zsh # Main entry point (auto-sourced)\n└── _mytool # Completion file (added to fpath)\n```\n\n```zsh\n# mytool.plugin.zsh\nif (( ! $+commands[mytool] )); then\n return\nfi\neval \"$(mytool init zsh)\"\n```\n\n### Fisher (Fish)\n\n```text\n# Repository structure for Fisher\nmytool/\n├── conf.d/\n│ └── mytool.fish # Auto-sourced config\n├── completions/\n│ └── mytool.fish # Auto-loaded completions\n└── functions/\n └── mytool_helper.fish # Auto-loaded functions\n```\n\n### Zinit / Sheldon / Antidote (Zsh)\n\n```zsh\n# Zinit\nzinit light org/mytool-zsh\n\n# Sheldon (sheldon.toml)\n# [plugins.mytool]\n# github = \"org/mytool-zsh\"\n\n# Antidote\n# org/mytool-zsh\n```\n\n## Graceful Degradation\n\n```sh\ninit_plugin() {\n if ! command -v mytool >/dev/null 2>&1; then\n _mytool_not_found() {\n echo \"mytool not found. Install: https://mytool.dev/install\" >&2\n return 127\n }\n alias mytool='_mytool_not_found'\n return\n fi\n\n local version\n version=$(mytool --version 2>/dev/null | head -1)\n\n case \"$version\" in\n 1.*)\n eval \"$(mytool init-v1 \"$current_shell\")\"\n ;;\n 2.*|3.*)\n eval \"$(mytool init \"$current_shell\")\"\n ;;\n *)\n echo \"Warning: Unknown mytool version ($version), attempting init\" >&2\n eval \"$(mytool init \"$current_shell\" 2>/dev/null)\" || true\n ;;\n esac\n}\n```\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":8156,"content_sha256":"f8d0352e63fffc6b6a074a5eefc20bf8df6b4a30f8000e21ec38853efb785af8"},{"filename":"references/posix-scripting.md","content":"---\ntitle: POSIX Scripting\ndescription: POSIX-compliant shell scripting, signal handling, process management, job control, traps, and portable patterns\ntags: [posix, sh, signal, trap, process, job-control, portable, ipc, socket]\n---\n\n# POSIX Scripting\n\n## Portable Shebang\n\n```sh\n#!/bin/sh\n```\n\nUse `/bin/sh` for POSIX scripts. Avoid `#!/bin/bash` unless Bash features are required.\n\n## POSIX-Compatible Constructs\n\n### Conditionals\n\n```sh\n# POSIX test (use [ ] not [[ ]])\nif [ \"$var\" = \"value\" ]; then\n echo \"match\"\nfi\n\n# String checks\n[ -n \"$var\" ] # Non-empty\n[ -z \"$var\" ] # Empty\n[ \"$a\" = \"$b\" ] # Equal (single =, not ==)\n[ \"$a\" != \"$b\" ] # Not equal\n\n# Numeric comparison\n[ \"$a\" -eq \"$b\" ] # Equal\n[ \"$a\" -lt \"$b\" ] # Less than\n[ \"$a\" -gt \"$b\" ] # Greater than\n\n# File tests\n[ -f \"$path\" ] # Regular file exists\n[ -d \"$path\" ] # Directory exists\n[ -r \"$path\" ] # Readable\n[ -w \"$path\" ] # Writable\n[ -x \"$path\" ] # Executable\n[ -s \"$path\" ] # Non-empty file\n[ -L \"$path\" ] # Symbolic link\n```\n\n### Functions (POSIX Syntax)\n\n```sh\n# POSIX: no \"function\" keyword\nmy_func() {\n local var=\"$1\"\n echo \"$var\"\n return 0\n}\n```\n\n### String Operations (No Bashisms)\n\n```sh\n# Parameter expansion (POSIX)\n${var#pattern} # Remove shortest prefix match\n${var##pattern} # Remove longest prefix match\n${var%pattern} # Remove shortest suffix match\n${var%%pattern} # Remove longest suffix match\n\n# Extract filename\nfilename=\"${path##*/}\"\n\n# Extract directory\ndirname=\"${path%/*}\"\n\n# Extract extension\next=\"${filename##*.}\"\n\n# Remove extension\nbase=\"${filename%.*}\"\n```\n\n### Loops\n\n```sh\n# Iterate over arguments\nfor arg in \"$@\"; do\n echo \"$arg\"\ndone\n\n# C-style not POSIX; use while instead\ni=0\nwhile [ \"$i\" -lt 10 ]; do\n echo \"$i\"\n i=$((i + 1))\ndone\n\n# Read lines from file\nwhile IFS= read -r line; do\n echo \"$line\"\ndone \u003c file.txt\n```\n\n## Signal Handling\n\n### Trap Syntax\n\n```sh\ntrap 'handler_commands' SIGNAL_LIST\n\n# Common signals\ntrap 'cleanup' EXIT # Always runs on exit\ntrap 'cleanup; exit 1' INT # Ctrl-C\ntrap 'cleanup; exit 1' TERM # kill (default signal)\ntrap '' HUP # Ignore hangup\ntrap 'reload_config' USR1 # Custom: reload\n```\n\n### Cleanup Pattern\n\n```sh\ncleanup() {\n rm -f \"$tmpfile\"\n [ -n \"$pid\" ] && kill \"$pid\" 2>/dev/null\n}\n\ntrap cleanup EXIT\n\ntmpfile=$(mktemp)\n```\n\n### Signal Reference\n\n| Signal | Number | Default | Common Use |\n| ------- | ------ | --------- | ------------------------------------ |\n| `HUP` | 1 | Terminate | Reload config, terminal closed |\n| `INT` | 2 | Terminate | Ctrl-C |\n| `QUIT` | 3 | Core dump | Ctrl-\\\\ |\n| `TERM` | 15 | Terminate | Graceful shutdown request |\n| `USR1` | 10 | Terminate | Custom (reload, log rotate) |\n| `USR2` | 12 | Terminate | Custom |\n| `PIPE` | 13 | Terminate | Broken pipe |\n| `WINCH` | 28 | Ignore | Terminal resize |\n| `EXIT` | N/A | N/A | Shell exit (trap-only pseudo-signal) |\n\n### Graceful Shutdown\n\n```sh\nshutdown_requested=0\n\nhandle_term() {\n shutdown_requested=1\n}\n\ntrap handle_term TERM INT\n\nwhile [ \"$shutdown_requested\" -eq 0 ]; do\n do_work\n sleep 1\ndone\n\ncleanup\n```\n\n## Process Management\n\n### Background Processes\n\n```sh\nlong_task &\nbg_pid=$!\n\n# Wait for specific process\nwait \"$bg_pid\"\nexit_code=$?\n\n# Wait for all background jobs\nwait\n```\n\n### Parallel Execution with Limits\n\n```sh\nmax_jobs=4\nrunning=0\n\nfor item in \"$@\"; do\n process_item \"$item\" &\n running=$((running + 1))\n if [ \"$running\" -ge \"$max_jobs\" ]; then\n wait -n 2>/dev/null || wait\n running=$((running - 1))\n fi\ndone\nwait\n```\n\n### PID File Management\n\n```sh\npidfile=\"/var/run/myservice.pid\"\n\nacquire_lock() {\n if [ -f \"$pidfile\" ]; then\n local old_pid\n old_pid=$(cat \"$pidfile\")\n if kill -0 \"$old_pid\" 2>/dev/null; then\n echo \"Already running (PID $old_pid)\" >&2\n return 1\n fi\n rm -f \"$pidfile\"\n fi\n echo $ > \"$pidfile\"\n}\n\nrelease_lock() {\n rm -f \"$pidfile\"\n}\n\ntrap release_lock EXIT\nacquire_lock || exit 1\n```\n\n### Subshell Isolation\n\n```sh\n# Changes inside subshell do not affect parent\n(\n cd /tmp || exit 1\n export MY_VAR=\"local\"\n do_work\n)\n# PWD and MY_VAR unchanged here\n```\n\n## IPC Patterns\n\n### Named Pipes (FIFOs)\n\n```sh\nfifo=\"/tmp/myfifo.$\"\nmkfifo \"$fifo\"\ntrap 'rm -f \"$fifo\"' EXIT\n\n# Writer\necho \"message\" > \"$fifo\" &\n\n# Reader\nwhile IFS= read -r msg; do\n echo \"Received: $msg\"\ndone \u003c \"$fifo\"\n```\n\n### Unix Socket IPC from Shell\n\n```sh\n# Send command via socat\necho '{\"command\":\"status\"}' | socat - UNIX-CONNECT:/tmp/myapp.sock\n\n# Listen on socket (simple server)\nsocat UNIX-LISTEN:/tmp/myapp.sock,fork EXEC:./handler.sh\n```\n\n### Here Document for Input\n\n```sh\ncommand \u003c\u003c'HEREDOC'\n Multi-line input\n No variable expansion with quoted delimiter\nHEREDOC\n\ncommand \u003c\u003cHEREDOC\n Expands $VARIABLES\n And $(commands)\nHEREDOC\n```\n\n## Portable Patterns\n\n### Safe Temporary Files\n\n```sh\ntmpdir=$(mktemp -d) || exit 1\ntrap 'rm -rf \"$tmpdir\"' EXIT\n\ntmpfile=\"$tmpdir/work\"\n```\n\n### Command Existence Check\n\n```sh\nhas_cmd() {\n command -v \"$1\" >/dev/null 2>&1\n}\n\nif has_cmd curl; then\n curl -fsSL \"$url\"\nelif has_cmd wget; then\n wget -qO- \"$url\"\nelse\n echo \"curl or wget required\" >&2\n exit 1\nfi\n```\n\n### Strict Mode\n\n```sh\nset -eu\n# -e: exit on error\n# -u: error on undefined variable\n\n# For pipelines (not POSIX but widely supported)\nset -o pipefail 2>/dev/null || true\n```\n\n### OS Detection\n\n```sh\ndetect_os() {\n case \"$(uname -s)\" in\n Linux*) echo \"linux\" ;;\n Darwin*) echo \"macos\" ;;\n MINGW*|MSYS*|CYGWIN*) echo \"windows\" ;;\n FreeBSD*) echo \"freebsd\" ;;\n *) echo \"unknown\" ;;\n esac\n}\n\ndetect_arch() {\n case \"$(uname -m)\" in\n x86_64|amd64) echo \"x86_64\" ;;\n aarch64|arm64) echo \"arm64\" ;;\n armv7*) echo \"armv7\" ;;\n *) echo \"unknown\" ;;\n esac\n}\n```\n\n### Logging\n\n```sh\nlog_info() { printf '[INFO] %s\\n' \"$*\" >&2; }\nlog_warn() { printf '[WARN] %s\\n' \"$*\" >&2; }\nlog_error() { printf '[ERROR] %s\\n' \"$*\" >&2; }\n\ndie() {\n log_error \"$@\"\n exit 1\n}\n```\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":6266,"content_sha256":"4ce21b76497396e2578c57504633dca08fa0730ba145afba582fc09b3c67d7f9"},{"filename":"references/terminal-control.md","content":"---\ntitle: Terminal Control\ndescription: ANSI/CSI escape sequences, tput, stty, TERM capabilities, cursor control, colors, and terminal queries\ntags:\n [ansi, csi, escape-sequences, tput, stty, terminal, colors, cursor, terminfo]\n---\n\n# Terminal Control\n\n## ANSI/CSI Escape Sequences\n\nCSI (Control Sequence Introducer) sequences start with `ESC [` (`\\e[` or `\\033[`).\n\n### Cursor Movement\n\n```sh\nprintf '\\e[H' # Move to home position (1,1)\nprintf '\\e[%d;%dH' 5 10 # Move to row 5, column 10\nprintf '\\e[A' # Up one line\nprintf '\\e[B' # Down one line\nprintf '\\e[C' # Forward one column\nprintf '\\e[D' # Back one column\nprintf '\\e[%dA' 3 # Up 3 lines\nprintf '\\e[s' # Save cursor position\nprintf '\\e[u' # Restore cursor position\nprintf '\\e[6n' # Query cursor position (response: ESC[row;colR)\n```\n\n### Screen Control\n\n```sh\nprintf '\\e[2J' # Clear entire screen\nprintf '\\e[0J' # Clear from cursor to end of screen\nprintf '\\e[1J' # Clear from start of screen to cursor\nprintf '\\e[2K' # Clear entire line\nprintf '\\e[0K' # Clear from cursor to end of line\nprintf '\\e[1K' # Clear from start of line to cursor\n```\n\n### Text Attributes\n\n```sh\nprintf '\\e[0m' # Reset all attributes\nprintf '\\e[1m' # Bold\nprintf '\\e[2m' # Dim\nprintf '\\e[3m' # Italic\nprintf '\\e[4m' # Underline\nprintf '\\e[7m' # Reverse (swap fg/bg)\nprintf '\\e[8m' # Hidden\nprintf '\\e[9m' # Strikethrough\n```\n\n### Colors (SGR)\n\n```sh\n# Standard colors (foreground: 30-37, background: 40-47)\nprintf '\\e[31m' # Red foreground\nprintf '\\e[42m' # Green background\nprintf '\\e[1;34m' # Bold blue\n\n# Bright colors (foreground: 90-97, background: 100-107)\nprintf '\\e[91m' # Bright red\n\n# 256-color mode\nprintf '\\e[38;5;%dm' 208 # Foreground: color 208 (orange)\nprintf '\\e[48;5;%dm' 236 # Background: color 236 (dark gray)\n\n# True color (24-bit)\nprintf '\\e[38;2;%d;%d;%dm' 255 128 0 # Foreground RGB\nprintf '\\e[48;2;%d;%d;%dm' 30 30 30 # Background RGB\n```\n\n### Color Utility Functions\n\n```sh\ncolor_fg() {\n printf '\\e[38;5;%dm' \"$1\"\n}\n\ncolor_bg() {\n printf '\\e[48;5;%dm' \"$1\"\n}\n\nrgb_fg() {\n printf '\\e[38;2;%d;%d;%dm' \"$1\" \"$2\" \"$3\"\n}\n\nreset_color() {\n printf '\\e[0m'\n}\n```\n\n## Tput (Portable Terminal Control)\n\n`tput` queries the terminfo database and outputs the correct escape sequences for the current terminal.\n\n### Common Capabilities\n\n```sh\ntput cols # Number of columns\ntput lines # Number of lines\ntput colors # Number of supported colors\n\ntput cup 5 10 # Move cursor to row 5, col 10\ntput home # Move to home position\ntput sc # Save cursor\ntput rc # Restore cursor\n\ntput clear # Clear screen\ntput el # Clear to end of line\ntput el1 # Clear to beginning of line\ntput ed # Clear to end of screen\n\ntput bold # Bold\ntput dim # Dim\ntput smul # Start underline\ntput rmul # End underline\ntput rev # Reverse video\ntput sgr0 # Reset all attributes\n\ntput setaf 1 # Set foreground color (0-255)\ntput setab 2 # Set background color (0-255)\n\ntput civis # Hide cursor\ntput cnorm # Show cursor (normal)\ntput smcup # Enter alternate screen\ntput rmcup # Exit alternate screen\n```\n\n### Alternate Screen Buffer\n\n```sh\nenter_fullscreen() {\n tput smcup\n tput civis\n tput clear\n}\n\nexit_fullscreen() {\n tput rmcup\n tput cnorm\n}\n\n# Ensure cleanup on exit\ntrap exit_fullscreen EXIT\nenter_fullscreen\n```\n\n### Feature Detection\n\n```sh\nhas_color() {\n local colors\n colors=$(tput colors 2>/dev/null) || return 1\n [ \"$colors\" -ge 8 ]\n}\n\nhas_truecolor() {\n case \"$COLORTERM\" in\n truecolor|24bit) return 0 ;;\n esac\n return 1\n}\n\nsupports_unicode() {\n case \"$LANG$LC_ALL$LC_CTYPE\" in\n *UTF-8*|*utf-8*|*utf8*) return 0 ;;\n esac\n return 1\n}\n```\n\n## Stty (Terminal Line Settings)\n\n```sh\n# Save current settings\nsaved_stty=$(stty -g)\n\n# Restore settings\nstty \"$saved_stty\"\n\n# Raw mode (no echo, no line buffering)\nstty raw -echo\n\n# Read single character\nread_char() {\n local old_stty\n old_stty=$(stty -g)\n stty raw -echo min 1\n local char\n char=$(dd bs=1 count=1 2>/dev/null)\n stty \"$old_stty\"\n printf '%s' \"$char\"\n}\n\n# Disable Ctrl-C (SIGINT)\nstty -isig\n\n# Disable flow control (Ctrl-S/Ctrl-Q)\nstty -ixon\n```\n\n## Terminal Queries\n\n### Window Size\n\n```sh\n# Via stty\nstty size # rows cols\n\n# Via tput\nrows=$(tput lines)\ncols=$(tput cols)\n\n# Via SIGWINCH handler\nhandle_resize() {\n LINES=$(tput lines)\n COLUMNS=$(tput cols)\n}\ntrap handle_resize WINCH\n```\n\n### Cursor Position\n\n```sh\nget_cursor_pos() {\n local pos\n printf '\\e[6n'\n IFS='[;' read -r -d R _ row col \u003c /dev/tty\n printf '%d %d' \"$row\" \"$col\"\n}\n```\n\n### Terminal Title\n\n```sh\n# Set title (works in most terminal emulators)\nset_title() {\n printf '\\e]0;%s\\a' \"$1\"\n}\n\n# Set title with icon name separately\nset_icon_title() {\n printf '\\e]1;%s\\a' \"$1\" # Icon name\n printf '\\e]2;%s\\a' \"$2\" # Window title\n}\n```\n\n## Progress Indicators\n\n### Spinner\n\n```sh\nspinner() {\n local pid=$1\n local frames='⠋⠙⠹⠸⠼⠴⠦⠧⠇⠏'\n local i=0\n while kill -0 \"$pid\" 2>/dev/null; do\n printf '\\r%s %s' \"${frames:i%${#frames}:1}\" \"$2\"\n i=$((i + 1))\n sleep 0.1\n done\n printf '\\r\\e[2K'\n}\n\nlong_running_task &\nspinner $! \"Processing...\"\n```\n\n### Progress Bar\n\n```sh\nprogress_bar() {\n local current=$1 total=$2 width=${3:-40}\n local pct=$((current * 100 / total))\n local filled=$((current * width / total))\n local empty=$((width - filled))\n\n printf '\\r['\n printf '%*s' \"$filled\" '' | tr ' ' '#'\n printf '%*s' \"$empty\" '' | tr ' ' '-'\n printf '] %3d%%' \"$pct\"\n}\n\nfor i in $(seq 1 100); do\n progress_bar \"$i\" 100\n sleep 0.05\ndone\nprintf '\\n'\n```\n\n## OSC (Operating System Command) Sequences\n\n```sh\n# Hyperlink (supported by many modern terminals)\nprintf '\\e]8;;https://example.com\\e\\\\Click here\\e]8;;\\e\\\\'\n\n# Set clipboard (OSC 52)\nprintf '\\e]52;c;%s\\a' \"$(printf '%s' \"text\" | base64)\"\n\n# Desktop notification (iTerm2, Kitty)\nprintf '\\e]9;%s\\a' \"Task complete\"\n```\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":6265,"content_sha256":"ef5b5f18c01ddfb21cb36a596f00a57c47f06258a3a2a461c901dd66c61ed622"},{"filename":"references/zsh-integration.md","content":"---\ntitle: Zsh Integration\ndescription: ZLE widgets, completion system (compdef/compadd/zstyle), hooks (precmd/preexec/chpwd), bindkey, parameter expansion, and ZDOTDIR loading order\ntags:\n [\n zsh,\n zle,\n compdef,\n compadd,\n zstyle,\n precmd,\n preexec,\n chpwd,\n bindkey,\n parameter-expansion,\n zdotdir,\n ]\n---\n\n# Zsh Integration\n\n## ZDOTDIR Loading Order\n\nZsh reads startup files in a specific order. Understanding this is critical for plugin installation.\n\n```text\n/etc/zshenv -> $ZDOTDIR/.zshenv (always, every shell)\n/etc/zprofile -> $ZDOTDIR/.zprofile (login shells only)\n/etc/zshrc -> $ZDOTDIR/.zshrc (interactive shells only)\n/etc/zlogin -> $ZDOTDIR/.zlogin (login shells only, after .zshrc)\n/etc/zlogout -> $ZDOTDIR/.zlogout (login shells only, on exit)\n```\n\n`ZDOTDIR` defaults to `$HOME` if unset. Plugins targeting interactive use belong in `.zshrc`.\n\n## ZLE Widgets\n\nZLE (Zsh Line Editor) widgets are functions bound to key sequences for custom line editing behavior.\n\n```zsh\nmy-widget() {\n emulate -L zsh\n BUFFER=\"modified: $BUFFER\"\n CURSOR=$#BUFFER\n}\n\nzle -N my-widget\nbindkey '^X^M' my-widget\n```\n\nKey ZLE variables available inside widgets:\n\n```zsh\n$BUFFER # Full command line contents\n$LBUFFER # Text left of cursor\n$RBUFFER # Text right of cursor\n$CURSOR # Cursor position (0-indexed)\n$WIDGET # Name of the widget being executed\n$KEYMAP # Current keymap name\n$KEYS # Keys that invoked the widget\n```\n\n### Accept-Line Wrapper\n\nIntercept Enter to add validation or transformation before execution:\n\n```zsh\ncustom-accept-line() {\n emulate -L zsh\n if [[ \"$BUFFER\" == *\"rm -rf /\"* ]]; then\n zle -M \"Blocked dangerous command\"\n return 1\n fi\n zle .accept-line\n}\nzle -N accept-line custom-accept-line\n```\n\n### Widget with Completion\n\n```zsh\nfzf-history-widget() {\n emulate -L zsh\n setopt localoptions pipefail\n local selected\n selected=$(fc -rln 1 | fzf --height 40% --reverse)\n if [[ -n \"$selected\" ]]; then\n BUFFER=\"$selected\"\n CURSOR=$#BUFFER\n fi\n zle reset-prompt\n}\nzle -N fzf-history-widget\nbindkey '^R' fzf-history-widget\n```\n\n## Completion System\n\n### Basic Completion Function\n\n```zsh\n#compdef mytool\n\n_mytool() {\n local -a commands\n commands=(\n 'init:Initialize a new project'\n 'build:Build the project'\n 'deploy:Deploy to production'\n )\n\n _arguments \\\n '(-h --help)'{-h,--help}'[Show help]' \\\n '(-v --verbose)'{-v,--verbose}'[Enable verbose output]' \\\n '--config[Config file]:file:_files -g \"*.toml\"' \\\n '1:command:->cmd'\n\n case \"$state\" in\n cmd)\n _describe 'command' commands\n ;;\n esac\n}\n\n_mytool\n```\n\n### Subcommand Completion\n\n```zsh\n#compdef mytool\n\n_mytool() {\n local curcontext=\"$curcontext\" state line\n typeset -A opt_args\n\n _arguments -C \\\n '1:command:->cmd' \\\n '*::arg:->args'\n\n case \"$state\" in\n cmd)\n local -a commands=(\n 'deploy:Deploy the application'\n 'config:Manage configuration'\n )\n _describe 'command' commands\n ;;\n args)\n case \"${line[1]}\" in\n deploy)\n _arguments \\\n '--env[Target environment]:env:(staging production)' \\\n '--dry-run[Preview changes]'\n ;;\n config)\n _arguments \\\n 'set:Set a value' \\\n 'get:Get a value'\n ;;\n esac\n ;;\n esac\n}\n\n_mytool\n```\n\n### Compadd Direct Usage\n\nFor low-level control over completion candidates:\n\n```zsh\n_mytool_branches() {\n local -a branches\n branches=(${(f)\"$(git branch --format='%(refname:short)' 2>/dev/null)\"})\n compadd -V branches -d branches -- \"${branches[@]}\"\n}\n```\n\n### Zstyle Configuration\n\n```zsh\n# Case-insensitive matching\nzstyle ':completion:*' matcher-list 'm:{a-zA-Z}={A-Za-z}'\n\n# Group completions by category\nzstyle ':completion:*' group-name ''\nzstyle ':completion:*:descriptions' format '%B%d%b'\n\n# Cache completions for expensive operations\nzstyle ':completion:*' use-cache on\nzstyle ':completion:*' cache-path \"$HOME/.zcompcache\"\n\n# Menu selection with highlighting\nzstyle ':completion:*' menu select\nzstyle ':completion:*:default' list-colors ${(s.:.)LS_COLORS}\n```\n\n## Hooks\n\n### Using add-zsh-hook\n\n```zsh\nautoload -Uz add-zsh-hook\n\nmy_precmd() {\n # Runs before each prompt display\n print -Pn \"\\e]0;%~\\a\"\n}\nadd-zsh-hook precmd my_precmd\n\nmy_preexec() {\n # Runs after command is read, before execution\n # $1 = the command string as typed\n # $2 = single-line expanded version\n # $3 = full expanded command\n timer_start=$EPOCHSECONDS\n}\nadd-zsh-hook preexec my_preexec\n\nmy_chpwd() {\n # Runs when working directory changes\n ls\n}\nadd-zsh-hook chpwd my_chpwd\n```\n\n### Available Hook Points\n\n| Hook | Trigger | Common Use |\n| --------------- | ------------------------------- | ------------------------- |\n| `precmd` | Before prompt display | Update prompt, set title |\n| `preexec` | After command read, before exec | Start timer, log command |\n| `chpwd` | Directory change | Auto-ls, update env |\n| `periodic` | Every `$PERIOD` seconds | Background checks |\n| `zshaddhistory` | Before history write | Filter sensitive commands |\n| `zshexit` | Shell exit | Cleanup |\n\n## Bindkey and Keymaps\n\n```zsh\n# List current bindings\nbindkey -L\n\n# Bind in specific keymap\nbindkey -M viins '^A' beginning-of-line\nbindkey -M vicmd 'k' up-line-or-history\n\n# Create custom keymap\nbindkey -N mymap\nbindkey -M mymap '^X' my-widget\n\n# Common key sequences\nbindkey '^[[A' up-line-or-search # Up arrow\nbindkey '^[[B' down-line-or-search # Down arrow\nbindkey '^[[3~' delete-char # Delete key\n```\n\n## Parameter Expansion\n\n### Flags\n\n```zsh\nstr=\"hello:world:foo\"\n\n# Split on delimiter\nprint -l ${(s.:.)str} # hello\\nworld\\nfoo\n\n# Join array\narr=(hello world foo)\nprint ${(j.:.)arr} # hello:world:foo\n\n# Uppercase / lowercase\nprint ${(U)str} # HELLO:WORLD:FOO\nprint ${(L)str} # hello:world:foo\n\n# Length\nprint ${#str} # 15\n\n# Unique elements\narr=(a b a c b)\nprint ${(u)arr} # a b c\n```\n\n### Modifiers\n\n```zsh\npath=\"/home/user/file.tar.gz\"\n\nprint ${path:h} # /home/user (head/dirname)\nprint ${path:t} # file.tar.gz (tail/basename)\nprint ${path:r} # /home/user/file.tar (remove extension)\nprint ${path:e} # gz (extension)\nprint ${path:A} # /home/user/file.tar.gz (absolute path)\n```\n\n### Defaults and Substitution\n\n```zsh\n# Default if unset\nprint ${var:-default}\n\n# Assign default if unset\n: ${var:=default}\n\n# Error if unset\n: ${var:?'var must be set'}\n\n# Substitute if set\nprint ${var:+is_set}\n\n# Pattern substitution\nstr=\"hello world\"\nprint ${str/world/zsh} # hello zsh\nprint ${str//o/0} # hell0 w0rld\n```\n\n## Emulate for Safety\n\nAlways use `emulate -L zsh` at the top of plugin functions to ensure consistent behavior regardless of the user's option settings:\n\n```zsh\nmy_plugin_func() {\n emulate -L zsh\n setopt extended_glob no_unset pipe_fail\n # Function body with known option state\n}\n```\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":7258,"content_sha256":"6ae41a952d238356f81cd9e424b38ec26af74725347039234a24ea9689ba4917"}],"content_json":{"type":"doc","content":[{"type":"heading","attrs":{"level":1},"content":[{"text":"Shell Integration","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Overview","type":"text"}]},{"type":"paragraph","content":[{"text":"Shell integration covers the APIs and patterns for building tools that extend or interact with Unix shells. This includes completion systems, prompt hooks, key bindings, terminal control, and plugin distribution across Zsh, Bash, and Fish.","type":"text"}]},{"type":"paragraph","content":[{"text":"When to use:","type":"text","marks":[{"type":"strong"}]},{"text":" Building CLI tool completions, shell plugins, prompt customizations, terminal UI, dotfile managers, installation scripts, or native binary wrappers.","type":"text"}]},{"type":"paragraph","content":[{"text":"When NOT to use:","type":"text","marks":[{"type":"strong"}]},{"text":" General-purpose scripting unrelated to shell extension (use POSIX scripting reference for standalone scripts), GUI applications, or web server development.","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Quick Reference","type":"text"}]},{"type":"table","attrs":{"layout":null},"content":[{"type":"tr","content":[{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Pattern","type":"text"}]}]},{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Shell","type":"text"}]}]},{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Key Points","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Completion function","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Zsh","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"compdef","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"compadd","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"zstyle","type":"text","marks":[{"type":"code_inline"}]},{"text":" for matcher configuration","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Completion function","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Bash","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"complete","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"compgen","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"COMP_WORDS","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"COMP_CWORD","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"COMPREPLY","type":"text","marks":[{"type":"code_inline"}]}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Completion function","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Fish","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"complete -c cmd -a args","type":"text","marks":[{"type":"code_inline"}]},{"text":", condition flags, subcommand patterns","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"ZLE widget","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Zsh","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"zle -N widget func","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"bindkey","type":"text","marks":[{"type":"code_inline"}]},{"text":" to map keys","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Prompt hook","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Zsh","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"precmd","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"preexec","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"chpwd","type":"text","marks":[{"type":"code_inline"}]},{"text":" via ","type":"text"},{"text":"add-zsh-hook","type":"text","marks":[{"type":"code_inline"}]}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Prompt hook","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Bash","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"PROMPT_COMMAND","type":"text","marks":[{"type":"code_inline"}]},{"text":" (string or array in Bash 5.1+)","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Event handler","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Fish","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"--on-event","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"--on-variable","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"--on-signal","type":"text","marks":[{"type":"code_inline"}]}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Abbreviation","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Fish","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"abbr -a name expansion","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"--function","type":"text","marks":[{"type":"code_inline"}]},{"text":" for dynamic","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Parameter expansion","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Zsh","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"${(s.:.)var}","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"${var:=default}","type":"text","marks":[{"type":"code_inline"}]},{"text":", flags and modifiers","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Terminal control","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"All","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"ANSI/CSI escape sequences, ","type":"text"},{"text":"tput","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"stty","type":"text","marks":[{"type":"code_inline"}]}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Signal handling","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"All","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"trap","type":"text","marks":[{"type":"code_inline"}]},{"text":" builtin, cleanup patterns, ","type":"text"},{"text":"EXIT","type":"text","marks":[{"type":"code_inline"}]},{"text":"/","type":"text"},{"text":"INT","type":"text","marks":[{"type":"code_inline"}]},{"text":"/","type":"text"},{"text":"TERM","type":"text","marks":[{"type":"code_inline"}]}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Process management","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"All","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Job control (","type":"text"},{"text":"&","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"wait","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"bg","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"fg","type":"text","marks":[{"type":"code_inline"}]},{"text":"), subshells, coprocesses","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Plugin installation","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"All","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Sourcing strategies, version detection, ","type":"text"},{"text":"ZDOTDIR","type":"text","marks":[{"type":"code_inline"}]},{"text":" loading order","type":"text"}]}]}]}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Common Mistakes","type":"text"}]},{"type":"table","attrs":{"layout":null},"content":[{"type":"tr","content":[{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Mistake","type":"text"}]}]},{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Correct Pattern","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Using ","type":"text"},{"text":"echo -e","type":"text","marks":[{"type":"code_inline"}]},{"text":" for escape sequences portably","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Use ","type":"text"},{"text":"printf","type":"text","marks":[{"type":"code_inline"}]},{"text":" or ","type":"text"},{"text":"tput","type":"text","marks":[{"type":"code_inline"}]},{"text":" for portability across shells and OSes","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Modifying ","type":"text"},{"text":"PROMPT_COMMAND","type":"text","marks":[{"type":"code_inline"}]},{"text":" with ","type":"text"},{"text":"=","type":"text","marks":[{"type":"code_inline"}]},{"text":" in Bash","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Append with ","type":"text"},{"text":"+=","type":"text","marks":[{"type":"code_inline"}]},{"text":" to avoid overwriting other tools","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Defining Fish event handlers in lazy-loaded functions","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Place event handlers in config.fish or source them explicitly","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Hardcoding terminal capabilities","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Query via ","type":"text"},{"text":"tput","type":"text","marks":[{"type":"code_inline"}]},{"text":" which respects ","type":"text"},{"text":"TERM","type":"text","marks":[{"type":"code_inline"}]},{"text":" and terminfo","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Missing ","type":"text"},{"text":"emulate -L zsh","type":"text","marks":[{"type":"code_inline"}]},{"text":" in Zsh functions","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Always set local options to avoid polluting caller environment","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Using ","type":"text"},{"text":"$COMP_LINE","type":"text","marks":[{"type":"code_inline"}]},{"text":" splitting instead of ","type":"text"},{"text":"COMP_WORDS","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Use ","type":"text"},{"text":"COMP_WORDS[$COMP_CWORD]","type":"text","marks":[{"type":"code_inline"}]},{"text":" for reliable word extraction","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Not quoting ","type":"text"},{"text":"$@","type":"text","marks":[{"type":"code_inline"}]},{"text":" in wrapper scripts","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Always use ","type":"text"},{"text":"\"$@\"","type":"text","marks":[{"type":"code_inline"}]},{"text":" to preserve argument boundaries","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Assuming ","type":"text"},{"text":"/bin/sh","type":"text","marks":[{"type":"code_inline"}]},{"text":" is Bash","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Target POSIX sh for portable scripts, test with ","type":"text"},{"text":"dash","type":"text","marks":[{"type":"code_inline"}]}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Using ","type":"text"},{"text":"function","type":"text","marks":[{"type":"code_inline"}]},{"text":" keyword in POSIX scripts","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Use ","type":"text"},{"text":"name() { ... }","type":"text","marks":[{"type":"code_inline"}]},{"text":" syntax for POSIX compatibility","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Ignoring ","type":"text"},{"text":"EXIT","type":"text","marks":[{"type":"code_inline"}]},{"text":" trap for cleanup","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Always set ","type":"text"},{"text":"trap cleanup EXIT","type":"text","marks":[{"type":"code_inline"}]},{"text":" for temp files and state","type":"text"}]}]}]}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Delegation","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Completion testing","type":"text","marks":[{"type":"strong"}]},{"text":": Use ","type":"text"},{"text":"Explore","type":"text","marks":[{"type":"code_inline"}]},{"text":" agent to verify completions interactively","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Script review","type":"text","marks":[{"type":"strong"}]},{"text":": Use ","type":"text"},{"text":"Task","type":"text","marks":[{"type":"code_inline"}]},{"text":" agent for cross-shell compatibility audits","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Code review","type":"text","marks":[{"type":"strong"}]},{"text":": Delegate to ","type":"text"},{"text":"code-reviewer","type":"text","marks":[{"type":"code_inline"}]},{"text":" agent","type":"text"}]}]}]},{"type":"blockquote","content":[{"type":"paragraph","content":[{"text":"If the ","type":"text"},{"text":"rust","type":"text","marks":[{"type":"code_inline"}]},{"text":" skill is available, delegate native binary compilation patterns to it. Shell wrappers often invoke Rust-compiled binaries for performance-critical operations. If the ","type":"text"},{"text":"cli-power-tools","type":"text","marks":[{"type":"code_inline"}]},{"text":" skill is available, delegate modern CLI utility patterns to it. Many shell plugins wrap tools like ","type":"text"},{"text":"fd","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"ripgrep","type":"text","marks":[{"type":"code_inline"}]},{"text":", and ","type":"text"},{"text":"fzf","type":"text","marks":[{"type":"code_inline"}]},{"text":".","type":"text"}]}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"References","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Zsh integration: ZLE, completions, hooks, parameter expansion","type":"text","marks":[{"type":"link","attrs":{"href":"references/zsh-integration.md","title":null}}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Bash integration: readline, completions, PROMPT_COMMAND, shopt","type":"text","marks":[{"type":"link","attrs":{"href":"references/bash-integration.md","title":null}}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Fish integration: completions, events, abbreviations, functions","type":"text","marks":[{"type":"link","attrs":{"href":"references/fish-integration.md","title":null}}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Terminal control: ANSI/CSI sequences, tput, stty, capabilities","type":"text","marks":[{"type":"link","attrs":{"href":"references/terminal-control.md","title":null}}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"POSIX scripting: portable patterns, signal handling, process management","type":"text","marks":[{"type":"link","attrs":{"href":"references/posix-scripting.md","title":null}}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Plugin distribution: installation scripts, dotfile management, version detection","type":"text","marks":[{"type":"link","attrs":{"href":"references/plugin-distribution.md","title":null}}]}]}]}]},{"type":"hr","attrs":{"markup":"---"}}]},"metadata":{"date":"2026-06-05","name":"shell-integration","author":"@skillopedia","source":{"stars":12,"repo_name":"agent-skills","origin_url":"https://github.com/oakoss/agent-skills/blob/HEAD/skills/shell-integration/SKILL.md","repo_owner":"oakoss","body_sha256":"65c2d670678729466dca1d5026b23fd87bbdfc45dfc48eb0d4e32af4f54fe9cd","cluster_key":"32a6d2478f9fc9aadf710a7f856c81388db4033a0dd814d3d28a5117443be558","clean_bundle":{"format":"clean-skill-bundle-v1","source":"oakoss/agent-skills/skills/shell-integration/SKILL.md","attachments":[{"id":"f81c4cbc-cb32-5c6e-a6a9-daed519a32f2","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/f81c4cbc-cb32-5c6e-a6a9-daed519a32f2/attachment.md","path":"references/bash-integration.md","size":5747,"sha256":"0eb71c6145c9543a88a0758e0f2d574795e7f31fb2b1fda835f350f3616660ef","contentType":"text/markdown; charset=utf-8"},{"id":"f51f6bcd-c547-557f-bc50-f80a969ecde8","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/f51f6bcd-c547-557f-bc50-f80a969ecde8/attachment.md","path":"references/fish-integration.md","size":6784,"sha256":"734f59586a0a50cdc9007b215190027857731f85ddbcbf6ad8f9c7018189eb55","contentType":"text/markdown; charset=utf-8"},{"id":"297031b9-c866-56b3-8095-487413f9f936","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/297031b9-c866-56b3-8095-487413f9f936/attachment.md","path":"references/plugin-distribution.md","size":8156,"sha256":"f8d0352e63fffc6b6a074a5eefc20bf8df6b4a30f8000e21ec38853efb785af8","contentType":"text/markdown; charset=utf-8"},{"id":"7409f9b0-5fa1-5968-8b92-ab1a47441313","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/7409f9b0-5fa1-5968-8b92-ab1a47441313/attachment.md","path":"references/posix-scripting.md","size":6266,"sha256":"4ce21b76497396e2578c57504633dca08fa0730ba145afba582fc09b3c67d7f9","contentType":"text/markdown; charset=utf-8"},{"id":"90908d13-9784-59c2-aef8-b6636f7cf18f","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/90908d13-9784-59c2-aef8-b6636f7cf18f/attachment.md","path":"references/terminal-control.md","size":6265,"sha256":"ef5b5f18c01ddfb21cb36a596f00a57c47f06258a3a2a461c901dd66c61ed622","contentType":"text/markdown; charset=utf-8"},{"id":"e03e2c88-eee7-5f8c-9a45-ec180c58d21e","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/e03e2c88-eee7-5f8c-9a45-ec180c58d21e/attachment.md","path":"references/zsh-integration.md","size":7258,"sha256":"6ae41a952d238356f81cd9e424b38ec26af74725347039234a24ea9689ba4917","contentType":"text/markdown; charset=utf-8"}],"bundle_sha256":"19e24421763963e869c4a87fd3fa0c8c382492da5c759544574c57b8f080b190","attachment_count":6,"text_attachments":6,"attachment_storage":"skillopedia-attachments-v1","binary_attachments":0,"excluded_attachments":[]},"cluster_size":1,"skill_md_path":"skills/shell-integration/SKILL.md","import_metadata":{"date":"2026-06-05","author":"@skillopedia","version":"v1","category":"web-development","category_label":"Web"},"exact_dupes_collapsed_into_this":0},"license":"MIT","version":"v1","category":"web-development","metadata":{"author":"oakoss","version":"1.0"},"import_tag":"clean-skills-v1","description":"Shell scripting and terminal integration patterns for building tools that integrate with Zsh, Bash, and Fish. Covers completion systems (compdef/compadd, complete/compgen, fish complete), ZLE widgets, hooks (precmd/preexec/chpwd, PROMPT_COMMAND), readline, bindkey, parameter expansion, ZDOTDIR loading order, event systems, abbreviations, POSIX shell scripting, terminal control codes (ANSI/CSI escape sequences), tput, stty, signal handling, process management (job control, traps), and shell plugin distribution patterns.\n\nUse when building shell plugins, writing completion scripts, implementing terminal UI with escape sequences, managing dotfiles, creating installation scripts, handling signals and process management, or integrating native binaries with shell wrappers.\n","user-invocable":false}},"renderedAt":1782986420650}

Shell Integration Overview Shell integration covers the APIs and patterns for building tools that extend or interact with Unix shells. This includes completion systems, prompt hooks, key bindings, terminal control, and plugin distribution across Zsh, Bash, and Fish. When to use: Building CLI tool completions, shell plugins, prompt customizations, terminal UI, dotfile managers, installation scripts, or native binary wrappers. When NOT to use: General-purpose scripting unrelated to shell extension (use POSIX scripting reference for standalone scripts), GUI applications, or web server developmen…