main
  1#!/usr/bin/env zsh
  2# shellcheck disable=all
  3# Simple zsh completion capture using _complete_help
  4# Usage: zsh-capture.zsh "command line" "/path/to/cwd"
  5
  6emulate -L zsh
  7setopt no_beep
  8
  9local cmdline="$1"
 10local cwd="$2"
 11
 12cd "$cwd" 2>/dev/null || exit 1
 13
 14# Initialize completion system (use user's zcompdump)
 15autoload -Uz compinit
 16compinit -C 2>/dev/null
 17
 18# Parse command line
 19local -a words
 20words=("${(@Q)${(z)cmdline}}")
 21
 22# If line ends with space, we're completing a new word
 23if [[ "$cmdline" == *" " ]]; then
 24    words+=("")
 25fi
 26
 27local cmd="${words[1]}"
 28local current="${words[-1]}"
 29
 30# Helper to output completions
 31output() {
 32    local val="$1" desc="$2"
 33    if [[ -n "$desc" ]]; then
 34        print -r -- "${val}"$'\t'"${desc}"
 35    else
 36        print -r -- "${val}"
 37    fi
 38}
 39
 40# Git completions
 41if [[ "$cmd" == "git" ]]; then
 42    if (( ${#words} == 2 )); then
 43        # Git subcommands
 44        git --list-cmds=main,others 2>/dev/null | while read -r subcmd; do
 45            [[ -z "$current" || "$subcmd" == "$current"* ]] && output "$subcmd"
 46        done
 47    else
 48        local subcmd="${words[2]}"
 49        case "$subcmd" in
 50            checkout|switch|merge|rebase|branch|log)
 51                # Branches
 52                git for-each-ref --format='%(refname:short)' refs/heads 2>/dev/null | while read -r b; do
 53                    [[ -z "$current" || "$b" == "$current"* ]] && output "$b" "branch"
 54                done
 55                git for-each-ref --format='%(refname:short)' refs/remotes 2>/dev/null | while read -r b; do
 56                    [[ "$b" == */HEAD ]] && continue
 57                    local short="${b#*/}"
 58                    [[ -z "$current" || "$short" == "$current"* ]] && output "$short" "remote"
 59                done
 60                ;;
 61            add|diff|restore|reset)
 62                # Modified files
 63                git diff --name-only 2>/dev/null | while read -r f; do
 64                    [[ -z "$current" || "$f" == "$current"* ]] && output "$f" "modified"
 65                done
 66                git diff --cached --name-only 2>/dev/null | while read -r f; do
 67                    [[ -z "$current" || "$f" == "$current"* ]] && output "$f" "staged"
 68                done
 69                ;;
 70            push|pull|fetch)
 71                if (( ${#words} == 3 )); then
 72                    git remote 2>/dev/null | while read -r r; do
 73                        [[ -z "$current" || "$r" == "$current"* ]] && output "$r" "remote"
 74                    done
 75                fi
 76                ;;
 77            stash)
 78                for sub in apply drop list pop show push; do
 79                    [[ -z "$current" || "$sub" == "$current"* ]] && output "$sub"
 80                done
 81                ;;
 82        esac
 83    fi
 84    exit 0
 85fi
 86
 87# SSH/SCP completions - hosts
 88if [[ "$cmd" == "ssh" || "$cmd" == "scp" || "$cmd" == "sftp" ]]; then
 89    {
 90        [[ -f ~/.ssh/config ]] && awk '/^Host / && !/\*/{for(i=2;i<=NF;i++)print $i}' ~/.ssh/config
 91        [[ -f ~/.ssh/known_hosts ]] && awk -F'[, ]' '{print $1}' ~/.ssh/known_hosts
 92    } 2>/dev/null | sort -u | while read -r h; do
 93        [[ -z "$current" || "$h" == "$current"* ]] && output "$h" "host"
 94    done
 95    exit 0
 96fi
 97
 98# Make completions
 99if [[ "$cmd" == "make" ]]; then
100    local mf
101    for f in GNUmakefile Makefile makefile; do
102        [[ -f "$f" ]] && mf="$f" && break
103    done
104    if [[ -n "$mf" ]]; then
105        awk -F: '/^[a-zA-Z_][a-zA-Z0-9_-]*:/ && !/^\./{print $1}' "$mf" 2>/dev/null | while read -r t; do
106            [[ -z "$current" || "$t" == "$current"* ]] && output "$t" "target"
107        done
108    fi
109    exit 0
110fi
111
112# NPM/Yarn/PNPM completions
113if [[ "$cmd" == "npm" || "$cmd" == "yarn" || "$cmd" == "pnpm" ]]; then
114    if (( ${#words} == 2 )); then
115        for sub in install add remove run build test start dev publish; do
116            [[ -z "$current" || "$sub" == "$current"* ]] && output "$sub"
117        done
118    elif [[ "${words[2]}" == "run" && -f package.json ]]; then
119        jq -r '.scripts // {} | keys[]' package.json 2>/dev/null | while read -r s; do
120            [[ -z "$current" || "$s" == "$current"* ]] && output "$s" "script"
121        done
122    fi
123    exit 0
124fi
125
126# Docker completions
127if [[ "$cmd" == "docker" ]]; then
128    if (( ${#words} == 2 )); then
129        for sub in build compose exec images logs ps pull push rm rmi run start stop; do
130            [[ -z "$current" || "$sub" == "$current"* ]] && output "$sub"
131        done
132    fi
133    exit 0
134fi
135
136# Fallback: file completion
137if [[ -n "$current" ]]; then
138    local -a matches
139    matches=( ${current}*(N) )
140    for f in "${matches[@]:0:20}"; do
141        [[ -d "$f" ]] && output "${f}/" "directory" || output "$f" "file"
142    done
143else
144    local -a matches
145    matches=( *(N) )
146    for f in "${matches[@]:0:20}"; do
147        [[ -d "$f" ]] && output "${f}/" "directory" || output "$f" "file"
148    done
149fi