#!/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 < 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