fkill

Fabulously kill processes by PID, name, or port — one cross-platform API for macOS, Linux, and Windows.

Library
npm
v10.0.3
799stars
MIT License

Repository Health

Pre-computed score based on development activity, maintenance, community, maturity, and trend momentum.How we score it →
42/100Fair
Development Activity0
Maintenance20
Community60
Maturity60
Momentum28

Technical Analysis

AI-assessed by reading the actual repository — architecture, code quality, innovation, and documentation.How we score it →
74/100Good
Architecture78
Code Quality82
Innovation80
Learning Curve55

fkill is a small Node.js library for killing processes by PID, process name, or port number, with a single cross-platform API that works identically on macOS, Linux, and Windows. Instead of writing separate kill/taskkill/pkill shell-out logic for each platform, callers invoke one fkill() function and it handles process resolution, signal selection, and Windows’ taskkill peculiarities under the hood.

Beyond basic termination, it supports force-killing, waiting for a process to actually exit, escalating to a force-kill after a timeout, and killing by port by resolving the owning PID first. It’s maintained by Sindre Sorhus as part of a widely used ecosystem of small, focused npm utilities, and is the library underlying the fkill-cli command-line tool and the alfred-fkill Alfred workflow.

What You Get

  • Cross-platform process termination - one fkill() call resolves to the right OS-native kill mechanism (kill/pkill on POSIX, taskkill on Windows).
  • Port-to-PID resolution - pass a port prefixed with : (e.g. :8080) and fkill looks up and kills the process bound to it via pid-port.
  • Force-kill and timeout escalation - force sends SIGKILL immediately; forceAfterTimeout waits, then escalates if the process hasn’t exited.
  • Exit confirmation - waitForExit polls until the process is confirmed gone (via process-exists) instead of returning as soon as the kill signal is sent.
  • Batch killing with aggregated errors - accepts a single input or an array, kills them concurrently, and throws an AggregateError describing every failure rather than stopping at the first.

Common Use Cases

  • Killing a dev server left on a port - a CLI tool or test runner calls fkill(':3000') to free a port before restarting a server.
  • Cleaning up child/background processes in scripts - build tools and process managers use fkill to reliably terminate spawned Node or native processes across platforms.
  • Graceful-then-forceful shutdown - a supervisor calls fkill(pid, {waitForExit: 5000}) to ask nicely first, then relies on forceAfterTimeout to guarantee termination.
  • Killing processes by name in test suites - test fixtures spin up named processes and use fkill('processName') in teardown instead of writing OS-specific cleanup code.

Under The Hood

Architecture fkill is a single-file library (index.js, roughly 330 lines) exporting one default async function. It dispatches to platform-specific kill implementations (windowsKill, macosKill, and a default POSIX kill) based on process.platform, chosen via a small branch at call time rather than class-based polymorphism. Input normalization — including resolving ports to PIDs via pid-port — happens once upfront in the exported fkill() function; per-input handling then wraps the platform kill with special-casing for the node process name (killing sibling Node processes while explicitly excluding ancestors of the current process, computed via ps-list output) so a script can’t kill itself or its own process tree. Errors from concurrent kills are collected and thrown together as a single AggregateError rather than failing fast, and an optional forceAfterTimeout loop re-checks liveness with exponentially backed-off polling before escalating to a forced kill. There’s no dependency injection or class hierarchy — it’s a flat functional module — so swapping any OS-specific kill primitive (taskkill, pkill/kill, execa) is isolated to its own function.

Tech Stack Plain Node.js (ES modules, engines >=20) with no framework dependencies. Runtime deps: execa for shelling out, taskkill for Windows process termination, pid-port for port-to-PID resolution, process-exists for liveness checks, and ps-list for enumerating processes (used only for the special node-name handling). Dev tooling: xo (an opinionated ESLint config) for linting, Node’s built-in node:test runner (no Jest/Mocha), tsd for type-testing the shipped .d.ts, and delay/get-port/noop-process as test-only helpers. There is no build step — it ships plain ES module JS with a hand-written index.d.ts.

Code Quality test.js provides comprehensive coverage: pid-based kill, name/title-based kill, case-insensitivity, port-based kill, force kill, forceAfterTimeout escalation, waitForExit, silent mode, and batch/array input, using Node’s native node:test plus a noop-process helper for spawnable no-op test targets, with platform-conditional test blocks for Windows versus POSIX behavior. Error handling is explicit throughout — AggregateError with descriptive per-input messages, and a friendly “doesn’t seem to be installed” error when a required binary is missing — rather than swallowed. The implementation itself is plain JS, but the public API is fully typed via a hand-maintained index.d.ts checked against real usage with tsd. Naming is consistent and descriptive, and CI runs via GitHub Actions with xo enforcing lint rules alongside the test suite.

API Design The public surface is a single function with sane defaults — no class to instantiate, no setup — call fkill(pid | name | ':port' | array, options?) and get a promise back. Killing by port is transparent: there’s no separate lookup step, fkill resolves the PID itself. Platform-appropriate defaults (force: false, tree: true on Windows) mean most calls need no options at all, while power users get escalation (forceAfterTimeout) and confirmation (waitForExit) as explicit opt-ins. Errors are aggregated into human-readable messages instead of raw OS error codes bubbling up. The tradeoff is a single overloaded input type (number | string | array) requiring runtime type-sniffing rather than distinct methods, but that keeps call sites extremely terse — clearly the design’s intent.

Join founders buildingwith open source

Opinionated takes, migration guides, cost-saving tips, and insights from the open source ecosystem.

Subscribe on Substack
Join 750+ subscribers

Search