~kris/dots

srice

ref: c8d9c68937f2c331a6bac47481ed40147fe73cba srice/.local/bin/dev/kd -rwxr-xr-x 4.8 KiB
c8d9c689 — Kris Yotam shell migration: fish -> mksh, reorganize bin/, wallpaper cleanup 4 months ago
                                                                                
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
#!/usr/bin/env bash
# ==============================================================
#  kd — Kill Dev
# --------------------------------------------------------------
#  Author   : Kris Yotam (aka. khr1st)
#  Contact  : krisyotam@protonmail.com
#  License  : GNU GPLv3
#  Date     : 2026-03-25
# --------------------------------------------------------------
#  Description:
#    Kill dev servers and clean up stale lock files.
#    Detects Next.js, Vite, Webpack, Hugo, and other common
#    dev servers by process name and listening port.
# ==============================================================

set -eu

RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[0;33m'
BOLD='\033[1m'
RESET='\033[0m'

DEV_PORTS=(3000 3001 3080 3081 4000 4200 5000 5173 5174 8000 8080 8888)
DEV_PATTERNS=("next-server" "next dev" "vite" "webpack-dev" "hugo server" "gatsby" "nuxt" "remix" "astro" "turbopack")
LOCK_PATTERNS=(".next/dev/lock" ".next/server.lock")

usage() {
  cat <<EOF
${BOLD}kd${RESET} — kill dev servers

${BOLD}Usage:${RESET}
  kd                  Kill port 3000 + clean lock files (default)
  kd --port <port>    Kill a specific port
  kd --all            Find and kill all dev servers
  kd --clean          Remove stale lock files only
  kd --scan           Show running dev servers without killing
  kd --help           Show this help

${BOLD}Examples:${RESET}
  kd                  Quick kill 3000
  kd --port 5173      Kill Vite on 5173
  kd --all            Nuke everything
  kd --scan           See what's running
EOF
}

log() { printf "${GREEN}[kd]${RESET} %s\n" "$1"; }
warn() { printf "${YELLOW}[kd]${RESET} %s\n" "$1"; }
err() { printf "${RED}[kd]${RESET} %s\n" "$1"; }

kill_port() {
  local port="$1"
  local pids
  pids=$(fuser "$port/tcp" 2>/dev/null | tr -s ' ') || true

  if [ -z "$pids" ]; then
    warn "Nothing on port $port"
    return
  fi

  for pid in $pids; do
    local name
    name=$(ps -p "$pid" -o comm= 2>/dev/null || echo "unknown")
    log "Killing $name (PID $pid) on port $port"
    kill -9 "$pid" 2>/dev/null || true
  done
}

clean_locks() {
  local found=0
  # search common dev directories
  for dir in "$HOME"/dev/*/; do
    [ -d "$dir" ] || continue
    for pattern in "${LOCK_PATTERNS[@]}"; do
      local lockfile="$dir$pattern"
      if [ -f "$lockfile" ]; then
        rm -f "$lockfile"
        log "Removed $lockfile"
        found=1
      fi
    done
  done

  if [ "$found" -eq 0 ]; then
    warn "No stale lock files found"
  fi
}

scan_devs() {
  local found=0
  printf "${BOLD}%-8s %-8s %-20s %s${RESET}\n" "PORT" "PID" "PROCESS" "CMD"
  printf "%-8s %-8s %-20s %s\n" "----" "---" "-------" "---"

  for port in "${DEV_PORTS[@]}"; do
    local pids
    pids=$(fuser "$port/tcp" 2>/dev/null | tr -s ' ') || true
    [ -z "$pids" ] && continue

    for pid in $pids; do
      local name cmd
      name=$(ps -p "$pid" -o comm= 2>/dev/null || echo "unknown")
      cmd=$(ps -p "$pid" -o args= 2>/dev/null | head -c 60 || echo "")
      printf "%-8s %-8s %-20s %s\n" "$port" "$pid" "$name" "$cmd"
      found=1
    done
  done

  # also check for dev processes not on standard ports
  for pattern in "${DEV_PATTERNS[@]}"; do
    while IFS= read -r line; do
      local pid name
      pid=$(echo "$line" | awk '{print $1}')
      name=$(echo "$line" | awk '{for(i=2;i<=NF;i++) printf "%s ", $i; print ""}')
      # skip if we already found this pid above
      printf "%-8s %-8s %-20s %s\n" "?" "$pid" "${pattern}" "$name"
      found=1
    done < <(pgrep -af "$pattern" 2>/dev/null | grep -v "grep\|kd" || true)
  done

  if [ "$found" -eq 0 ]; then
    warn "No dev servers detected"
  fi
}

kill_all() {
  log "Scanning for dev servers..."

  # kill by known ports
  for port in "${DEV_PORTS[@]}"; do
    local pids
    pids=$(fuser "$port/tcp" 2>/dev/null | tr -s ' ') || true
    [ -z "$pids" ] && continue
    kill_port "$port"
  done

  # kill by process pattern
  for pattern in "${DEV_PATTERNS[@]}"; do
    local pids
    pids=$(pgrep -f "$pattern" 2>/dev/null | grep -v "$$" || true)
    [ -z "$pids" ] && continue

    for pid in $pids; do
      local name
      name=$(ps -p "$pid" -o comm= 2>/dev/null || echo "unknown")
      log "Killing $name (PID $pid) matching '$pattern'"
      kill -9 "$pid" 2>/dev/null || true
    done
  done

  clean_locks
  log "Done"
}

# --- main ---

case "${1:-}" in
  --port|-p)
    [ -z "${2:-}" ] && { err "Specify a port: kd --port 3000"; exit 1; }
    kill_port "$2"
    clean_locks
    ;;
  --all|-a)
    kill_all
    ;;
  --clean|-c)
    clean_locks
    ;;
  --scan|-s)
    scan_devs
    ;;
  --help|-h)
    usage
    ;;
  "")
    kill_port 3000
    clean_locks
    ;;
  *)
    # if it's a number, treat as port
    if [[ "$1" =~ ^[0-9]+$ ]]; then
      kill_port "$1"
      clean_locks
    else
      err "Unknown option: $1"
      usage
      exit 1
    fi
    ;;
esac