CLI tool
bokwoon95/wgo avatar
bokwoon95/wgo

wgo reruns any command when a file changes, in about two files of Go

Live reload for Go apps. Watch arbitrary files and respond with arbitrary commands. Supports running multiple commands in parallel.

578 stars23 forksGoMIT

At a glance

What is it?
wgo is a live reload watcher for Go apps that wraps any command, so the same tool works for gcc, sass or go test as well as for `go run`, with regex include and exclude rules and no config file. The whole implementation is small enough to read in one sitting, it is MIT licensed, and the flag set is where the real behaviour lives.
Who is it for?
wgo suits a Go developer who wants a live reload loop without a configuration file and without a framework's opinions about how the project is laid out, since the tool is a command prefix and the rules are regexes passed as flags.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 14 days ago.
What is it written in?
Mainly Go, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 22, 2026, and from our analysis. They are not legal advice.

Editorial analysis

The problem: watchers that want your project to be a config file

The README states the motivation directly, and the complaints are specific enough to be worth quoting in short.

Too many file watchers either force you to wrap your commands into strings, require config files or log tons of noisy output to your stdout.

The README's framing is that those are the three things wgo avoids.

Each of those three is a real design choice with a real cost. Wrapping commands in strings means shell quoting becomes part of your tool's interface, and the difference between the string the watcher runs and the command you would have typed is a source of subtle bugs. A config file means a second place to describe the build, and the watcher becomes a project-specific thing that cannot be dropped into an existing repository without adding a file to it. And noisy output means the tool competes with your program's own output for the terminal.

wgo's answer to all three is to be a prefix. You put wgo in front of a command and it reruns that command whenever a file changes. The README is blunt about how little you need to learn: You already know how to use wgo, and the whole idea is to slap it in front of any command so that the command reruns whenever a file changes.

The other claim is about size. The implementation is also short: most of it resides in just two files, wgo_cmd.go and main.go, and the README says you can read the entire codebase in one sitting and points to START_HERE.md as the place to start. That claim is checkable from the repository layout, and the layout backs it up: main.go, wgo_cmd.go, a main_test.go and wgo_cmd_test.go, the platform-specific util_unix.go and util_windows.go pair with their tests, go.mod and go.sum, a Dockerfile, a testdata/ directory and an AGENTS.md.

The topic list names file-watcher, go and live-reload, the primary language is Go, the licence is MIT, and the last push was on 2026-10-09 with a matching v0.7.2 release.

wgo run as a drop-in for go run

The most used mode is the one that mirrors an existing Go command, and the README describes it in one line.

wgo run behaves exactly like go run except it runs again the moment any Go file changes. This can be used to live-reload Go servers. wgo run accepts the same flags as go run.

That last clause is the design point. Because the flags are forwarded, the tool does not need its own build flag vocabulary, and the usual Go flags still work:

shell
wgo run -tags=fts5 -race -trimpath main.go

By default wgo run only watches .go files. To include additional file types such as .html, use the -file flag. That default is a considered choice rather than an omission: for a Go server the source files are what change during development, and watching everything would mean an edit to a log file or a temporary artifact restarts the process.

The usage block in the README also shows the general form, where wgo takes a command and its arguments directly:

shell
wgo gcc -o main main.c
wgo go build -o main main.go
wgo -file .c gcc -o main main.c
wgo -file=.go go build -o main main.go

Note the two syntaxes for the same flag, both the separated form and the equals form. Both are supported for -file and for the other flags, and the README shows that so you can use whichever reads better in a script.

The run subcommand takes its own arguments too, including a package path rather than a file, and the usage block shows the form that passes arguments through to the program: wgo run -file=.css -file=.js -tags=fts5 ./cmd/my_project arg1 arg2 arg3.

Include and exclude rules are regexes, with one deliberate deviation

The file and directory rules are the part of wgo with real behaviour in it, and the README is precise about the semantics.

To include specific files eligible for triggering a reload, use the -file flag. It takes in a regex, and files whose paths, relative to the root directory, match the regex are included. You can provide multiple -file flags. Path separators are always forward slash, even on Windows.

If no -file flag is provided, every file is included by default unless it is explicitly excluded by the -xfile flag.

That default is worth noticing: wgo watches everything unless you narrow it. It is a permissive default rather than a restrictive one, which is convenient for a quick session and dangerous for a long one in a directory with generated output.

The exclusion flag takes a regex as well, and -xfile takes higher precedence than -file, so you can include a large group of files and surgically ignore specific files from that group. The README gives the case that motivates this:

shell
wgo -xfile coverage.out go test . -race -coverprofile=coverage.out

The comment in the README explains the loop it prevents: go test writes to coverage.out, which counts as a changed file, which triggers go test to run again, which generates coverage.out again, in an infinite loop. The suggested alternative is to narrow the watch instead, with wgo -file .go go test . -race -coverprofile=coverage.out.

The directory equivalent works the same way. To only watch specific directories, use the -dir flag, which takes a regex against directory paths relative to the root, and -dir assets -file .scss sass assets/styles.scss assets/styles.css is the example given.

And then there is the deviation from standard regex behaviour, which the README explains with the reason. The -file flag takes in regexes like .html or .css, and technically the dot matches any character, but wgo includes a slight modification to the regex matching rules: any dot immediately followed by a letter is treated as a dot literal.

So .css really means \.css, but .* still means .* because the star is not a letter. If you really want the dot wildcard followed by a letter, wrap it in a bracket group, for example (.)abc.

Installing it and running the first reload

There are two supported install routes and one binary download, and the README gives each one exactly.

From the release page you can download binaries directly, or use the Go command:

shell
go install github.com/bokwoon95/wgo@latest

On macOS there is a Homebrew formula:

shell
brew install wgo

The module path in that install command is the repository path, github.com/bokwoon95/wgo, and the go.mod in the repository confirms the module name and requires Go 1.25.0 or later. The only two runtime dependencies are fsnotify at v1.10.1, which is the cross-platform file notification library doing the actual watching, and golang.org/x/sys, which is what lets the same codebase handle Unix and Windows.

With wgo on the path, the first use is the shortest possible one. A plain gcc invocation:

shell
gcc -Wall -o main main.c

becomes, with the prefix added:

shell
wgo gcc -Wall -o main main.c

The behaviour you should see is that the command runs once, and then runs again each time a watched file changes, with no configuration file and no output of its own unless you ask for it.

The repository also carries a Dockerfile for working on the tool itself, and its comments give the two commands verbatim:

shell
docker build --tag wgotest .
docker run --interactive --tty --volume "$(pwd)":/wgo wgotest

That image is golang:latest with bash installed and a work directory of /wgo, and it exists so you can run the test suite against the working tree mounted from the host rather than copying sources into the image.

The flags that change the behaviour, not just the scope

Beyond -file, -xfile, -dir and -xdir, the rest of the flag list is about how the watcher behaves rather than what it watches.

The timing ones come first. -debounce takes a debounce duration, and lower debounce values will react quicker. That is the setting that separates a usable watcher from a frustrating one when an editor writes a file in several steps, because without debouncing each intermediate write can trigger a rebuild. -poll takes how often to poll for file changes, and zero or no value means no polling. That is the escape hatch for the network filesystems and container mounts where change notifications either do not arrive or arrive for a different set of files than you expect.

The lifecycle ones are -exit, which exits when the last command exits, and -postpone, which postpones the first execution of the command until a file is modified. -postpone is useful when you want wgo up and watching before the program starts, so that an edit during startup is not lost.

Then there is -stdin, which enables stdin for the last command. That is the flag that turns a watcher into something usable for an interactive program, and it is only applied to the last command, which is consistent with the model of wgo holding one terminal session for the running process.

The location flags are -cd for changing to a different directory to run the commands, and -root to specify additional root directories to watch. The distinction between them matters: -cd moves where the command runs, while -root widens what is watched, so a project that reads templates or assets from a sibling directory can watch it without changing where the program is executed.

And -verbose logs file events, which is the debugging flag when a change you expect does not trigger a rebuild. The README notes that the flags must be passed in directly after wgo or wgo run, before invoking your command, and that wgo -h and wgo run -h will list them for your version.

Where wgo fits, and where it stops

The comparison worth making is with the other tools in the same category, and the README's own complaint list is the sharpest statement of the differences available.

A watcher that requires a config file describes your build in a format only that tool understands, so moving to another tool means rewriting the description. A watcher that wraps commands into strings moves the shell into the interface, and the quoting rules become part of what you learn. A watcher that logs heavily competes with the program you are running for the same terminal. wgo takes a different position on each: it is a prefix, it passes arguments through without turning them into a string you assemble, and it is silent by default.

Against a framework-style tool, the difference is that wgo has no opinion about your project. There is no manifest, no project type detection, no template directory. The tool reruns a command when a path matches a regex you gave it, and that is the entire contract. For a Go project with a conventional layout, that is enough. For a project whose build is a sequence of dependent steps, a single command prefix is the wrong shape, because wgo restarts the one command it knows about rather than orchestrating a pipeline.

The limits are concrete. There is no dependency graph and no partial rebuild, so every change runs the whole command, which for a large Go project means a full rebuild and a process restart rather than an incremental compile. The filesystem dependency is fsnotify plus an optional -poll mode, so on a network mount or a virtualised mount you will need to set the poll interval and accept the CPU cost. The regex model with the dot-literal modification is deliberately non-standard, which is convenient for extensions and would surprise anyone porting a pattern from elsewhere.

On the project itself, the version history suggests a steady pace with a recent push: v0.7.0 and v0.7.1 both on 2026-08-20 and v0.7.2 on 2026-10-09, alongside the last push on 2026-10-09. The repository is MIT licensed with a LICENSE file, and the AGENTS.md at the root is worth reading before contributing, since it presumably describes how the maintainer expects changes to be made.

For a Go developer who wants a live reload loop today, the README's own framing is the accurate summary: you already know how to use it, and you can read the whole implementation in one sitting.

Editorial conclusion

wgo suits a Go developer who wants a live reload loop without a configuration file and without a framework's opinions about how the project is laid out, since the tool is a command prefix and the rules are regexes passed as flags. It is the wrong tool if you need a project-specific rebuild pipeline, since wgo only knows how to rerun a command and restart it, and it is the wrong tool on a filesystem where filesystem events are unreliable unless you pass -poll, which trades responsiveness for CPU. Before adopting it, run wgo -h and wgo run -h to see the flag list for your version, and read START_HERE.md, which the README points at as the entry point for reading the two-file implementation.

Frequently asked questions

What is wgo and what does it do?

wgo, short for watcher-go, is a live reload tool for Go apps and other commands. You put it in front of any command and it reruns that command whenever a watched file changes, without a config file and without printing output of its own.

How do I install wgo?

Download a binary from the GitHub releases page, install it with the Go command go install github.com/bokwoon95/wgo@latest, or on macOS use brew install wgo. The module requires Go 1.25.0 or later.

How do I make wgo watch only Go and HTML files?

Pass the -file flag with a regular expression for each type you want, for example wgo run -file .html -file .css main.go. By default wgo run watches only .go files, and paths are always matched with forward slashes even on Windows.

Why does wgo keep restarting my go test run in a loop?

The coverage file your test writes counts as a changed file and triggers the test again. Exclude it with -xfile coverage.out, or narrow the watch to Go sources with -file .go, since -xfile takes precedence over -file.

What happens if file change events do not arrive on my filesystem?

Pass the -poll flag with an interval, which is how often wgo polls for file changes. Zero or no value means no polling, so polling is opt-in for filesystems where notifications are unreliable.

Which flags does wgo take for timing and process behaviour?

The list includes -debounce for how quickly it reacts to file events, -poll for the polling interval, -postpone to hold back the first execution until a file is modified, -exit to exit when the last command exits, -stdin to enable stdin for the last command, -cd to run in a different directory, -root to add watched directories, and -verbose to log file events.

Official sources

  1. bokwoon95/wgo on GitHub
  2. Issues
  3. License: MIT
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/bokwoon95-wgo.svg)](https://hysenlabs.com/projects/bokwoon95-wgo)