Zippy is a small Linux CLI that watches a file or directory and reruns a command whenever changes are detected. It is designed for fast local development loops where you want immediate feedback after saving a file.
It works especially well for:
- C and C++ edit-build-run workflows
- small backend services
- scripts and CLI tools
- test commands
- local automation tasks
- any project where a command should rerun after file changes
Zippy uses a simple Zippy.json config file for command templates, debounce timing, and optional logging.
- Zippy
Many small projects require the same loop:
edit file → rebuild → rerun command → inspect output → repeat
Zippy automates that loop. Point it at a file or directory, define the command you want to run, and it will rerun the command after changes.
- Watch a file or directory
- Rerun a command after changes
- Debounce repeated saves
- Generate starter config with
zippy --generate - Use
{file}and{dir}placeholders in commands - Print active config with
zippy --config - Save child process output to a log file when enabled
- Print and clear logs from the CLI
- Lightweight C++20 implementation
- Linux/POSIX process model using
fork,exec,pipe, andstat
flowchart TD
CLI[CLI arguments] --> Settings[Load settings]
Config[Zippy.json] --> Settings
Settings --> Watcher[Polling watch loop]
Watcher --> Stat[stat file or directory]
Stat --> Change{modified time changed?}
Change -- yes --> Command[Expand command template]
Command --> Child[fork + exec /bin/sh -c]
Child --> Output[stdout/stderr]
Output --> Terminal[terminal output]
Output --> Log[optional log file]
Change -- no --> Sleep[delay]
Sleep --> Watcher
| Path | Purpose |
|---|---|
src/main.cpp |
Entrypoint, watch loop, and child process management |
src/commands.cpp |
CLI command output such as help, version, config, logs, and credits |
src/generate.cpp |
Generates starter Zippy.json config |
src/logger.cpp |
Terminal and optional file logging |
src/settings.cpp |
JSON config loading and defaults |
include/ |
Headers for source files |
library/nlohmann/json.hpp |
Vendored single-header JSON library |
test/ |
Example target projects used for manual verification |
- Linux or Linux-compatible environment
- CMake 3.16+
- C++20 compiler such as GCC or Clang
- Make
Zippy is POSIX-only and does not support Windows.
Build and run locally:
make build
./build/zippy ./path/to/file-or-dirGenerate a starter config:
./build/zippy --generateRun using a config command:
./build/zippy ./srcInstall globally:
sudo make install
zippy ./path/to/file-or-dirInstall for your user only:
make install PREFIX=$HOME/.local
export PATH="$HOME/.local/bin:$PATH"
zippy ./path/to/file-or-dir| Command | Description |
|---|---|
make build |
Configure and build the Release binary |
make run ARGS="." |
Build and run Zippy with arguments |
make install |
Install into PREFIX/bin, defaulting to /usr/local/bin |
make clean |
Remove the CMake build directory |
cmake -S . -B build -DCMAKE_BUILD_TYPE=Release
cmake --build build
./build/zippy --versionZippy loads Zippy.json from the current working directory.
Create one manually:
{
"delay": 1000000,
"ignore": [],
"save_log": false,
"log_path": "zippy.log",
"cmd": "make && ./build/out"
}Or generate one:
zippy --generate| Key | Type | Default | Description |
|---|---|---|---|
delay |
integer | 1000000 |
Debounce interval in microseconds between polling checks |
cmd |
string | "" |
Shell command to run after a change |
save_log |
boolean | false |
Whether to append Zippy and child-process output to a log file |
log_path |
string | zippy.log |
Log file path used when save_log=true |
ignore |
array | [] |
Parsed for compatibility and future use; not currently applied at runtime |
| Placeholder | Expands to |
|---|---|
{file} |
The watched file path |
{dir} |
The watched directory path |
Example:
{
"cmd": "g++ -std=c++20 {file} -o /tmp/zippy-out && /tmp/zippy-out"
}zippy ./path/to/file-or-dir # Watch a file or directory
zippy --help # Show help
zippy --h # Show help
zippy --version # Print version
zippy --v # Print version
zippy --config # Print active config summary
zippy --log # Print configured log file
zippy --clear # Truncate configured log file
zippy --credits # Show credits
zippy --generate # Write starter Zippy.json
zippy --gen # Alias for --generateZippy.json:
{
"delay": 500000,
"save_log": true,
"log_path": "zippy.log",
"cmd": "make build && ./build/app"
}Run:
zippy ./src{
"delay": 1000000,
"save_log": false,
"cmd": "npm test"
}Run:
zippy .{
"cmd": "python3 {file}"
}Run:
zippy script.py{
"cmd": "g++ -std=c++20 {file} -o /tmp/zippy-single && /tmp/zippy-single"
}Run:
zippy main.cppWhen save_log=true, Zippy appends logs and child-process output to log_path.
Print logs:
zippy --logClear logs:
zippy --clearExample output:
2026-03-11 12:45:28.038 [INFO] zippy Starting Zippy v1.3.1
2026-03-11 12:45:28.039 [INFO] zippy Watching file: /home/user/project/src/main.cpp
2026-03-11 12:45:29.201 [INFO] zippy Running: make && ./build/out
2026-03-11 12:45:33.812 [WARN] zippy File changed; re-running command...
Run a local build:
make buildRun with arguments:
make run ARGS="./src"Clean build artifacts:
make cleanSuggested development loop:
make build
./build/zippy --version
./build/zippy --generate
./build/zippy ./srcThe repository currently uses manual verification rather than an automated test framework.
Manual smoke test:
make clean
make build
./build/zippy --version
./build/zippy --generate
./build/zippy --config
./build/zippy ./srcRecommended checks before a release:
make build
./build/zippy --help
./build/zippy --version
./build/zippy --generate
./build/zippy --configFuture automated test coverage should include:
- config loading defaults
- command placeholder expansion
- log file creation and clearing
- CLI aliases
- invalid path handling
- child-process output capture
- debounce timing behaviour
Make sure the install path is on PATH.
export PATH="$HOME/.local/bin:$PATH"Check that Zippy.json exists in the current working directory and includes a non-empty cmd.
zippy --config
cat Zippy.jsonEnable logging:
{
"save_log": true,
"log_path": "zippy.log"
}Then rerun Zippy and inspect:
zippy --logIncrease delay:
{
"delay": 1500000
}The ignore field is parsed for compatibility and future use, but it is not currently applied at runtime.
- Linux/POSIX only
- Uses polling with
stat()rather thaninotifyorepoll - Watches modification time rather than semantic file contents
- The
ignoreconfig field is not currently enforced - No automated test target is currently included
- Commands are executed through
/bin/sh -c, so shell escaping matters
- Implement ignore pattern matching
- Add automated unit tests
- Add GitHub Actions CI
- Add
inotifybackend for lower-latency Linux watching - Add recursive directory filtering
- Add structured JSON output mode
- Add config validation with clear error messages
This project is licensed under the Apache 2.0 License. See LICENSE.
