documentation
Troubleshooting and security
When a wake did not happen, start with unsnooze doctor. The security model and the local dev loop follow.
Troubleshooting
- Something looks off?
unsnooze doctorchecks the whole install;--fixrepairs what it can (including retiring leftovers of the old claude-session-guard install). - What has it been doing?
unsnooze logs -ffollows the log live; the dashboard's logs tab scrolls back with the mouse wheel. - A wake didn't happen?
unsnooze preview <id>tells you exactly what's holding it back. After every real wake the pane is re-captured; if the limit banner reappears, unsnooze reschedules from the fresh banner, capped at five attempts. - A banner wasn't detected?
unsnooze report [agent]captures the pane so you can paste it into an issue — that's how the experimental adapters get good. - Panes piling up?
unsnooze reaplists finished panes and empty sessions (dry-run);--yescloses them. - Leaving?
unsnooze uninstallremoves wrappers and hooks;--purgeremoves state too.
Start with the symptom
Almost every report falls into one of these. The distinction that matters most is whether the session was recorded at all — that separates a detection problem from a wake problem, and they have different fixes.
- Typing
claudestarts nothing watched. The shell wrapper lives in~/.zshrcor~/.bashrc, so it only applies to shells started afterunsnooze setupran. Open a new terminal, then confirm withunsnooze doctor. Nothing is protected until the wrapper is loaded, because the wrapper is the entry point — you never invoke unsnooze directly. - The limit hit but nothing was recorded. A detection problem. Either the
StopFailurehook is not installed (doctorreports it) or the banner wording was not recognised. Capture it withunsnooze report— an unmatched banner is a one-release fix, but only if someone sends the text. - It was recorded but never woke. A wake problem, and
unsnooze preview <id>names the reason rather than guessing. The usual answers are a guard deliberately holding it — see guards — or the five-attempt cap having been reached. - It woke, but at the wrong time. Read the provenance in
unsnooze status: a reset shown as(absolute, from hook)came from the agent itself, while(absolute, from scrape)was read off the pane. A reset that looks hours out is a parsing bug worth reporting with the banner text attached — reset times are always absolute, never a relative countdown. - It resumed while still rate-limited. Expected and handled: the pane is re-captured after every wake, and if the banner is still there unsnooze reschedules from the fresh one. Overload is not a limit, so a transient overload message is not treated as one.
- The machine was asleep at reset time. Wakes are dispatched by the daemon — a launchd agent on macOS, a systemd user unit on Linux. If you declined it during setup, nothing runs while the terminal is closed;
doctorreports whether it is running and under which pid.
Security model
unsnooze is a scheduler that presses your keys — not an auto-approver. The short version of the contract:
- Keys are typed only after proving the pane is yours — identity (ownership stamp or process-id + birth-time lease; pane ids get recycled, so a mismatch vetoes) and liveness (your agent foreground, not mid-stream). Unprovable → a fresh session is opened instead of typing.
- Claude's limit menu is read before any key is sent; unreadable → nothing is pressed. It will never select "Upgrade your plan."
- No
--dangerously-skip-permissions, no auto-trust, no auto-approve, no touching MCP config — your agent's own permission model governs everything after the wake. - Nearly zero network: one daily version check, plus ntfy only if you configure it. Zero telemetry; state stays in
~/.unsnooze. - Releases are published to npm by CI with provenance.
Honest limits: unsnooze does inject keystrokes into your live terminal, and it does not sandbox your agent or defend against prompt injection — that's your agent's job. Full threat model and vulnerability reporting: SECURITY.md.
Development
$ npm test # unit tests (node:test)
$ ./scripts/e2e-simulate.sh # full detect → wait → re-open cycle in a
# scratch tmux session (no real limits needed)
$ bash -n scripts/e2e-zellij.sh # syntax-check the Zellij smoke test
$ vhs demo/demo.tape # regenerate the demo gif (brew install vhs)Releases are tagged (git tag v<version>, then git push origin v<version>) and published to npm by CI with provenance via trusted publishing — see .github/workflows/release.yml. Contributions: open an issue first for anything behavioral; adapter banner captures (unsnooze report) are always welcome.