Local live preview
Works with the CLI alone. No gander.md account.
Local watch is a live-reload preview on this machine. Every save hot-swaps the HTML in your browser and rebuilds the TOC, keeping scroll position. You do not need a gander.md account. Nobody off this computer can open the page, unless they have the unguessable local token and can reach 127.0.0.1.
This is not gander watch. That subcommand is the hosted review loop and needs signup.
gander plan.md --watch
gander plan.md --watch --silent
The CLI hands the file to a long-lived runner daemon (gander _serve) and exits. The daemon owns the HTTP server, the filesystem watch, and the reload. By default it opens the URL once. --silent prints the URL and skips the browser. The URL stays valid across edits, runner restarts, and reboots, because the watch list is stored in ~/.gander/watches.json.
A local server listens on 127.0.0.1:7821. Multiple watches share that port under /w/<id>. Browsers subscribe over Server-Sent Events.
Local vs hosted
# CLI only - stays on this machine
gander plan.md --watch
gander --watch ~/reports
# Signed up - live gander.md link for a reviewer
gander watch plan.md
gander watch ~/reports
gander --watch is local preview. gander watch is the hosted review loop. If you need a teammate to comment, use hosted watch. If you are reading along with the agent in your own browser, local watch is enough.
Directory watch
Pass a folder. The runner registers a dir-local watch and auto-onboards new matching .md files as local previews under 127.0.0.1:7821/w/<id>. No gander.md account.
gander --watch ~/reports
Same rules as hosted directory watch: recursion is the default (including new subfolders); --no-recursive stays in that one folder; always skip .git, node_modules, .obsidian, vendor, and hidden directories; default glob **/*.md; --existing backfills unmatched files already in the folder; more than 50 existing files needs --yes.
gander --watch ~/reports --existing
gander --watch ~/reports --existing --yes
gander --watch ~/reports --no-recursive
gander --watch ~/reports --glob 'daily-*.md'
Auto-onboarded files do not open a browser. The URL goes to the runner log and an OS notification. --silent still skips the browser on a single-file watch; directory-adopted files never open a browser, with or without it.
gander status shows the directory row (MODE=dir-local, URL -) plus each child file watch. gander stop ~/reports stops adoption only. Already-onboarded local previews keep their URLs and keep live-updating.
Directory watches require the runner. --foreground is refused.
Runner commands
gander status
gander stop plan.md
gander stop ~/reports
gander stop <watch-id>
gander stop --all
gander logs
gander logs <watch-id>
gander logs --follow
gander logs --no-follow
status prints the runner version, each watch, and its URL. Directory rows print MODE=dir-local or dir-share with URL -. stop on a directory path stops adoption only; child file watches keep running. stop can also take a file, a watch id, or --all. logs tails reload events; pass an id to filter.
Auto-start at login
The first --watch installs a per-user supervisor so the daemon comes back after login: LaunchAgent on macOS, systemctl --user on Linux.
gander runner uninstall
gander runner install
Uninstall if you do not want a background process. Install again to restore auto-start. Uninstall does not delete watches.json; the next --watch can pick the list up.
Foreground (CI and debug)
The old blocking behavior is still there for a single file. Ctrl+C stops it. The daemon is not involved; this process owns a transient server.
gander --watch --foreground plan.md
--watch and -outfile cannot be combined. A directory path cannot use --foreground.
Same-machine access
Other local users cannot use your URL by guessing the path. Each watch has an unguessable ?t= token in ~/.gander/watches.json (mode 0600). The runner socket is mode 0600 inside a 0700 directory. On Linux the daemon also checks SO_PEERCRED against the connecting UID.