fyr
Watch files. Run commands. Stay in flow.
fyr runs a command every time you save a file, debounced, clean, and instant. No duplicate runs, no leftover processes, no noise.
- Tasks - save any watch+command pair by name and run it from anywhere with
fyr run <name> - Project config - drop a
fyr.tomlin your repo and commit your setup alongside your code - Language templates -
fyr init rust,fyr init go, and 14 more to get started in seconds - Zero-config mode - run
fyrwith no arguments and pick a task from an interactive menu - Quiet mode - strip fyr's output entirely so only your command speaks
Installation
macOS / Linux
|
Windows (PowerShell)
iwr https://raw.githubusercontent.com/opmr0/fyr/main/install.ps1 -UseBasicParsing | iex
Via cargo
From source
Quick Start
Every time you save a file inside src, fyr runs cargo run. That's it.
Watching Files
Tip: Always wrap your command in quotes so its flags go to your command, not to fyr.
Watch by extension
Watch all files with a given extension, recursively from the current directory.
Flags
| Flag | Short | Description |
|---|---|---|
--watch |
-w |
Files or directories to watch |
--run |
-r |
Command to run on change |
--extensions |
-e |
Watch files by extension |
--debounce |
-d |
Debounce window in ms (default: 150) |
--quiet |
-q |
Suppress fyr's own log output |
--no-clear |
- | Don't clear the screen between runs |
--no-ignore |
- | watch all files regardless of .gitignore rules |
fyr automatically ignore files thats in .gitignore. Use --no-ignore to watch all files regardless.
Tasks
Save a watch + command pair as a named task and run it from anywhere with one word.
Tasks are stored globally and available from any directory.
Task commands
You can also provide -e / --extensions when adding or editing a task. You must provide -w, -e, or both.
Override on run
Run a task with a different path or command without permanently changing it:
Run flags
| Flag | Short | Description |
|---|---|---|
--watch |
-w |
Override watch paths |
--run |
-r |
Override command |
--debounce |
-d |
Debounce window in ms |
--global |
-g |
Use global tasks even if fyr.toml exists |
--quiet |
-q |
Suppress fyr's own log output |
--no-clear |
- | Don't clear the screen between runs |
--no-ignore |
- | Don't respect .gitignore rules |
Project Config
fyr looks for a fyr.toml in your current directory first. If found, tasks are loaded from it instead of your global tasks, useful for committing your fyr setup alongside your project.
Supported templates: Rust, C, C++, Go, Zig, Swift, Haskell, Node.js, Ruby, PHP, Lua, Elixir, Java, Kotlin, CSS/SCSS, Shell
fyr.toml format
= "build"
[]
= ["src"]
= "cargo build --release"
[]
= ["src", "tests"]
= "cargo test"
Config resolution
| Situation | What fyr loads |
|---|---|
fyr.toml exists in current dir |
Local tasks |
No fyr.toml |
Global tasks |
--global / -g flag |
Global tasks (always) |
Zero-Config Mode
Run fyr or fyr run with no arguments. If a fyr.toml exists, fyr loads it and either runs the default task immediately or shows an interactive picker.
[fyr] loading tasks from 'fyr.toml'
[fyr] default task 'build', running it
[fyr] loading tasks from 'fyr.toml'
? which task do you want to run?
> build
test
lint
Use -g to skip fyr.toml and always load global tasks:
Quiet Mode
Suppresses fyr's own output and shows only your command's output. Works with fyr, fyr run, and fyr run <name>. Does not suppress errors or task management output.
Debounce
Editors often write to disk multiple times on a single save. fyr waits 150ms after the last detected change before running, so you always get exactly one run per save, no matter how fast you type.
Benchmarks
Benchmarked on an Intel i7-9850H against the most popular file watchers.
| Tool | Startup | Idle Memory | Commands fired (50 rapid changes) |
|---|---|---|---|
| fyr | 219ms | 7.6 MB | 27/50 ¹ |
| watchexec | 238ms | 13.5 MB | 51/50 |
| chokidar | 501ms | 37.6 MB | 1/50 ² |
| nodemon | 528ms | 41.2 MB | 102/50 ³ |
Versions tested: fyr v1.0.0, watchexec v2.5.0, chokidar v3.6.0, nodemon v3.1.14
¹ Intentional, fyr debounces and kills stale runs, so rapid saves collapse into one clean run per burst. Adjust with -d.
² chokidar's debounce is too aggressive for rapid changes, causing it to miss most events.
³ nodemon fires duplicate events per change.
Requires watchexec, nodemon, or chokidar for comparison. The script auto-detects what's installed and skips the rest.
How It Works
- fyr starts watching the paths you provide
- A file changes, fyr waits for the debounce window to pass
- If the previous command is still running, fyr kills it
- fyr runs your command fresh
Contributing
Found a bug or have an idea? Open an issue or submit a pull request.
License
MIT - LICENSE