GitHub - system32-ai/wip: Watch and reload any process based on pluggable hooks like file changes, API changes, webhook calls

7 min read Original article ↗

Work in-process (wip)

A Rust tool that watches for changes and automatically restarts a managed process.

Recording 2026-01-03 at 00 59 10

Features

  • 📁 Filesystem watching - Watch directories recursively for file changes
  • 🔗 Webhook triggers - Restart via HTTP POST requests
  • 🌐 API polling - Monitor API endpoints and restart on changes
  • Script hooks - Run custom scripts to detect changes
  • �🔌 Pluggable hooks - Extensible hook system for custom change detection
  • 🔄 Automatically restart processes on changes
  • 🎯 Filter by file extensions
  • 🚫 Skip specific directories and file extensions
  • ⏱️ Configurable debounce delay to avoid excessive restarts
  • 🚀 Fast and efficient
  • 💻 Supports full shell commands with environment variables, pipes, and redirects
  • ⚙️ Configuration file support (wip.toml or .wip.toml)

Installation

Quick Install (Recommended)

Install the latest release using the install script:

curl -fsSL https://raw.githubusercontent.com/system32-ai/wip/refs/heads/master/scripts/install.sh | bash

This will download and install the latest release binary to ~/.local/bin/wip.

Install specific version:

curl -fsSL https://raw.githubusercontent.com/system32-ai/wip/refs/heads/master/scripts/install.sh | bash -s -- v0.1.9

Update to latest version:

curl -fsSL https://raw.githubusercontent.com/system32-ai/wip/main/scripts/update.sh | bash

Download Binary

Download pre-built binaries from the releases page:

  • Linux: x86_64, aarch64 (glibc and musl variants)
  • macOS: Intel (x86_64), Apple Silicon (aarch64), Universal binary
  • Windows: x86_64 (MSVC and GNU variants)

Build from Source

git clone https://github.com/system32-ai/wip.git
cd wip
cargo build --release

The binary will be available at target/release/wip

Usage

Basic syntax:

wip [OPTIONS] -- <COMMAND>

Everything after -- is treated as a full shell command, allowing you to use environment variables, pipes, redirects, and other shell features.

Examples

Watch current directory and restart a Go server:

Watch with environment variables:

wip -- PORT=3000 node server.js

Watch specific directory and filter by file extensions:

wip --watch ./src --extensions rs,toml -- cargo run

Watch with custom debounce delay (1 second):

wip --debounce 1000 -- python app.py

Limit restarts to when 5 or fewer files change:

wip --max-files 5 -- cargo run

Watch multiple file extensions:

wip --extensions js,ts,json -- npm start

Skip certain directories (e.g., node_modules, target):

wip --skip-dirs node_modules,target,.git -- cargo run

Skip certain file extensions (e.g., logs, temp files):

wip --skip-extensions log,tmp,swp -- python app.py

Combine watch and skip options:

wip --extensions rs,toml --skip-dirs target -- cargo run

Use pipes and redirects:

wip -- python app.py | tee output.log

Chain commands:

wip -- npm run build && npm start

Execute commands before starting and after stopping:

wip --before "npm run build" --after "rm -rf /tmp/cache" -- node server.js

Combine before/after hooks with other options:

wip --extensions js --before "echo 'Building...'" --after "echo 'Cleaned up'" -- npm start

Using Configuration Files

Create a wip.toml or .wip.toml file in your project directory:

# wip.toml - Default filesystem hook
watch = "."
extensions = ["rs", "toml"]
skip_dirs = ["target", ".git"]
skip_extensions = ["log", "tmp"]
debounce = 500
max_files = 10

Then simply run:

You can override config file settings with command-line arguments:

# Use config but override debounce
wip --debounce 1000 -- cargo run

# Use config but watch different directory
wip --watch ./src -- cargo run

Specify a custom config file:

wip --config my-config.toml -- go run main.go

Change Detection Hooks

wip supports multiple ways to detect changes through pluggable hooks:

1. Filesystem Hook (Default)

Watch filesystem for changes:

[hook]
type = "filesystem"
watch = "."
extensions = ["rs", "toml"]
skip_dirs = ["target", ".git"]
skip_extensions = ["log", "tmp"]

debounce = 500
max_files = 10

2. Webhook Hook

Trigger restarts via HTTP POST requests:

[hook]
type = "webhook"
port = 9000
path = "/hook"
secret = "your-secret-key"  # Optional

Trigger a restart:

curl -X POST http://localhost:9000/hook \
  -H "X-Hook-Secret: your-secret-key" \
  -H "Content-Type: application/json" \
  -d '{"reason": "deployment"}'

Use cases:

  • CI/CD pipeline triggers
  • GitHub webhooks
  • Manual deployment triggers
  • Integration with external systems

3. API Polling Hook

Poll an API endpoint and restart when the response changes:

[hook]
type = "api"
url = "https://api.example.com/deployment/status"
interval_ms = 5000  # Poll every 5 seconds

[hook.headers]
Authorization = "Bearer your-token"
X-API-Key = "your-key"

Use cases:

  • Monitor deployment APIs
  • Watch configuration services
  • Detect changes in external systems
  • Poll health check endpoints

4. Script Hook

Run a custom script and restart when the output changes:

[hook]
type = "script"
script = "git rev-parse HEAD"  # Any shell command
interval_ms = 2000  # Run every 2 seconds
shell = "sh"  # Optional: sh, bash, zsh, etc.

Use cases:

  • Monitor git commits: git rev-parse HEAD
  • Check file checksums: md5sum config.json
  • Query databases: psql -t -c 'SELECT version FROM config'
  • Check environment variables: echo $DEPLOYMENT_VERSION
  • Custom deployment logic: ./check-should-restart.sh
  • Monitor remote files: curl -s https://example.com/version.txt

The script hook runs your command periodically and restarts the process whenever the output changes. This provides maximum flexibility for custom change detection logic.

See examples/ directory for complete configuration examples.

Command-line Options

  • -- <COMMAND> - Full command to execute (required)
  • -c, --config <FILE> - Path to config file (default: auto-detect wip.toml or .wip.toml)
  • -w, --watch <PATH> - Path to watch for changes (default: current directory)
  • -e, --extensions <EXTS> - File extensions to watch (comma-separated, e.g., rs,toml)
  • --skip-extensions <EXTS> - File extensions to ignore (comma-separated, e.g., log,tmp)
  • --skip-dirs <DIRS> - Directories to ignore (comma-separated, e.g., target,node_modules,.git)
  • -d, --debounce <MS> - Debounce delay in milliseconds (default: 500)
  • --max-files <N> - Maximum number of files that can change before restarting (0 = unlimited)
  • --before <COMMAND> - Command to execute before starting the process
  • --after <COMMAND> - Command to execute after stopping the process

Note: Command-line arguments take precedence over config file settings.

How It Works

  1. The tool starts your specified command with the given arguments
  2. It looks for a configuration file (wip.toml or .wip.toml) in the current directory
  3. Merges config file settings with command-line arguments (CLI takes precedence)
  4. Watches the specified directory (and subdirectories) for file changes
  5. When a file change is detected:
    • If file extensions are specified, only matching files trigger a restart
    • The debounce delay prevents multiple rapid restarts
    • The current process is killed
    • A new instance of the process is started

Configuration File

Create a wip.toml or .wip.toml file in your project root:

# Path to watch (default: ".")
watch = "./src"

# File extensions to watch
extensions = ["rs", "toml"]

# Directories to skip
skip_dirs = ["target", "node_modules", ".git"]

# File extensions to skip
skip_extensions = ["log", "tmp", "swp"]

# Debounce delay in milliseconds
debounce = 500

# Command to run before starting the process (optional)
# Executes when changes are detected, before restarting
before = "echo 'Restarting...'"

# Command to run after stopping the process (optional)
# Executes when changes are detected, after the process is killed
after = "rm -rf /tmp/app-cache"

All fields are optional. See wip.toml.example for a complete example.

Before and After Hooks

The before and after commands are executed when file changes are detected:

  • before: Runs after changes are detected but before the process is started

    • Useful for: database migrations, building assets, clearing caches, starting dependencies
    • Example: before = "npm run build"
  • after: Runs after the process is stopped

    • Useful for: cleanup tasks, notifications, stopping dependencies, logging
    • Example: after = "docker-compose down"

These commands run in the shell (sh on Unix, cmd on Windows) and inherit the current environment.

Use Cases

  • Development servers: Automatically restart web servers when code changes
  • Build systems: Re-run builds when source files change
  • Scripts: Restart long-running scripts during development
  • Testing: Automatically re-run tests when code changes
  • CI/CD Integration: Trigger restarts via webhooks or API polling
  • Configuration Management: Restart on external config changes
  • Git-based Deployments: Monitor commits and redeploy

Development

Running Tests

Building for Release

Building for All Platforms

This requires cross for cross-compilation:

Creating a Release

To create a new release:

./scripts/release.sh 0.2.0

This will:

  1. Update version in Cargo.toml
  2. Update Cargo.lock
  3. Commit the changes
  4. Create a git tag

Then push to trigger the release workflow:

git push origin master && git push origin v0.2.0

The GitHub Actions workflow will automatically:

  • Build binaries for all supported platforms
  • Create a GitHub release
  • Upload all binaries as release assets

Dependencies

  • notify - Cross-platform file system notifications
  • clap - Command-line argument parsing
  • tokio - Async runtime
  • anyhow - Error handling
  • serde - Serialization/deserialization
  • reqwest - HTTP client for API hook
  • hyper - HTTP server for webhook hook

Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

License

MIT