A language-agnostic hot-reload CLI tool. Watches your source files, rebuilds on changes, and restarts the server automatically. Works with any project that has a build command and an executable to run.
go install github.com/sophic00/reload@latestOr build from source:
make build
# binary is at ./bin/reload# Go
reload --root=. --build='go build -o ./bin/server ./cmd/server' --exec='./bin/server'
# Rust
reload --root=. --build='cargo build' --exec='./target/debug/myapp' --include='*.rs'
# TypeScript
reload --root=. --build='npx tsc' --exec='node dist/index.js' --include='*.ts'Edit any source file, save, and the server is rebuilt and restarted automatically.
The tool is a channel-based pipeline with four stages:
filesystem events ──> watcher ──> debounce ──> builder ──> runner
(fsnotify) (200ms) (sh -c build) (sh -c exec)
Watcher recursively watches directories using fsnotify, filtering by include/exclude patterns. Directory events always pass through so new files in subdirectories are detected.
Debounce coalesces rapid filesystem events (editors like vim emit write+rename+chmod per save) by resetting a timer on each event and only firing after the configured quiet period.
Builder receives change signals, cancels any in-progress build (SIGTERM first, SIGKILL after grace period), drains stale signals, and starts a new build. Each build gets a trace ID for log correlation.
Runner stops the previous server process with graceful shutdown (SIGTERM, then SIGKILL after timeout), starts the new one, and detects crash loops (exits within a threshold) by applying a cooldown before restarting.
Stages communicate via capacity-1 buffered channels (chan struct{}), meaning "at least one event happened" without blocking the producer.
main.go entrypoint
internal/
cli/ flag parsing, config resolution, signal handling
config/ 3-tier config (defaults < TOML file < CLI flags)
reload/ pipeline orchestration
watcher/ recursive filesystem watching
builder/ build command execution with cancel/restart
runner/ server process lifecycle management
logger/ structured logging with ANSI color support
Configuration follows a 3-tier precedence: defaults < config file < CLI flags.
--root Project directory to watch
--build Shell command to build the project
--exec Shell command to start the server
--include File patterns to watch, e.g. "*.go,*.html" (default: all)
--exclude Paths to exclude from watching
--config Path to a TOML config file
--debounce Debounce delay before triggering a rebuild (default 200ms)
--build-grace-period Grace period for build cancel before force kill (default 3s)
--shutdown-timeout Timeout for graceful server shutdown (default 5s)
--crash-threshold Max uptime to consider an exit a crash (default 3s)
--crash-cooldown Cooldown before restarting after a crash (default 5s)
--no-color Disable colored log output
--log-json Output logs in JSON format
-v, --verbose Enable debug logging
If no --config flag is given, the tool looks for config.reload.toml in the current directory. See example.reload.toml for a fully commented example. To use it, copy it into your project:
cp example.reload.toml config.reload.tomlCLI flags override any values from the config file.
Process group management -- Both build and server commands run in their own process groups (Setpgid). Signals are sent to the entire group (kill(-pid, ...)), ensuring child processes don't leak.
Auto-exclusion -- The exec binary (parsed from --exec) and the config file are automatically excluded from the watcher to prevent infinite rebuild loops.
Graceful shutdown -- SIGTERM is sent first, with a configurable timeout before falling back to SIGKILL. This applies to both build cancellation and server restarts.
Crash detection -- If the server exits with a non-zero code within crash_threshold, the runner waits crash_cooldown before restarting to avoid a tight restart loop.
NO_COLOR support -- Respects the NO_COLOR environment variable in addition to the --no-color flag.