A Claude Code plugin that prevents your computer from sleeping while Claude Code is working. Supports macOS (including closed-lid keep-awake on Apple Silicon), Linux, and Windows.
- Activates on prompt submit / tool use, releases on session end
- One shared daemon per machine — multiple Claude Code sessions reference-count automatically
- macOS Apple Silicon: stays awake with the lid closed without an external display, without
sudo, without code-signing - Audible lid feedback on macOS — Submarine on close, Bottle on open
- Self-monitor: daemon exits if all registered sessions die (covers Claude Code crashes), or if no hook fires for 2h (covers a hung-but-alive CLI)
- Cross-platform: macOS, Linux (systemd / gnome-session), Windows (Git Bash + PowerShell)
claude plugin add claude-keep-awake
Or manually:
git clone https://github.com/AxGord/claude-keep-awake.git
claude plugin add ./claude-keep-awakeTwo coordinated pieces:
scripts/keep-awake.sh— fires on everyUserPromptSubmitandPostToolUsehook. Registers the current session in~/.claude/keep-awake-state/sessions/<cli-pid>and starts a daemon if none is running. Sessions are keyed by the Claude CLI process PID (stable for the process lifetime), notsession_id(which changes on/clear,/compact, resume — UUID keying leaked one file per session_id thatStopnever reaped).scripts/stop-awake.sh— fires on theStophook. Removes the session file. If it was the last live session, terminates the daemon.
Kill-switch: create ~/.claude/keep-awake-state/disabled to make the hooks a no-op (Mac sleeps normally); delete it to resume. Takes effect on the next hook in any active session.
The daemon is platform-specific:
| OS | Daemon | What it prevents |
|---|---|---|
| macOS Apple Silicon / Intel | Swift binary compiled from src/keep-awake-daemon.swift |
IOPMAssertion (idle/display/system) + IOKit selector 12 (kPMSetClamshellSleepState) → blocks both idle sleep and lid-close sleep |
| macOS (no Xcode CLT) | Fallback: caffeinate -dis |
Idle/display/system sleep only; lid-close still triggers DarkWake |
| Linux | systemd-inhibit --what=sleep:idle (fallback: gnome-session-inhibit) |
Idle and suspend |
| Windows | SetThreadExecutionState via PowerShell |
Display and system sleep |
The Swift daemon uses an undocumented but publicly accessible IOKit selector (kPMSetClamshellSleepState = 12 on IOPMrootDomain). Setting it to 1 makes the kernel ignore the lid-close event, keeping the system fully awake instead of entering DarkWake. Verified on Apple Silicon + macOS Sequoia, on both AC and battery, without any external display.
AC↔battery transitions: the dark→full wake cycle triggered by plugging or unplugging power invalidates the clamshell-disable flag (Apple's own pmconfigd re-evaluates it on full wake). The daemon defends with three layers:
IORegisterForSystemPowercallback: onkIOMessageCanSystemSleepissuesIOCancelPowerChange(active veto); onkIOMessageSystemHasPoweredOnre-applies assertions and selector 12IOPSCreateLimitedPowerNotificationcallback: on every AC↔battery edge re-applies state- 30s heartbeat: re-issues selector 12 as a safety net
Main loop is CFRunLoopRun() (not dispatchMain()) so CFRunLoop sources used by these IOKit notifications actually fire.
Known limitation — AC-plug forced sleep on Apple Silicon: when AC is plugged in while the lid is closed, macOS schedules a brief forced sleep (~5s) that bypasses all userspace sleep-prevention paths. The kernel sends kIOMessageSystemWillSleep directly — there is no kIOMessageCanSystemSleep to veto. No IOPMAssertion type, selector 12, IOPMConnectionCreate, or pmset acwake setting prevents this on AS. The only known workaround is sudo pmset -a disablesleep 1 (root-only) — used by Amphetamine's "Power Protect" via a passwordless sudoers fragment. This plugin keeps the no-root, no-entitlement design and auto-recovers via kIOMessageSystemHasPoweredOn re-apply; the brief micro-sleep is unavoidable.
To warn the user before they close the lid in this unsafe window, the daemon writes ~/.claude/keep-awake-state/lid-unsafe-until (UNIX timestamp) on AC plug-in and removes it when full wake completes. Use the scripts/statusline-keep-awake.sh snippet in your Claude Code statusLine to display a countdown — see "Statusline integration" below.
When you close the lid:
- The built-in display brightness is set to 0 via the private
DisplayServicesframework (backlight off) - The system stays at full clock; Claude Code processes do not get throttled
- An audible Submarine chime confirms the keep-awake is active
- On open, Bottle chime confirms normal operation resumed; brightness fades back to its saved value over ~500ms (minimum restore floor 0.05 if saved was lower)
If swiftc is not available, the plugin falls back to plain caffeinate -dis — that still prevents idle sleep, but lid-close on Apple Silicon will throw the system into DarkWake (network limited, processes throttled).
~/.claude/keep-awake-state/
├── daemon.pid # current daemon PID
├── disabled # optional kill-switch; if present, hooks no-op
├── lid-unsafe-until # UNIX timestamp; present briefly after AC plug-in
├── sessions/
│ ├── <cli-pid> # Claude CLI process PID, one file per live CLI process
│ └── ...
└── .lock # atomic mkdir lock for state mutations
Show a countdown in your Claude Code statusLine while AC-plug forced sleep is pending:
{
"statusLine": {
"type": "command",
"command": "~/.claude/plugins/keep-awake/scripts/statusline-keep-awake.sh"
}
}Outputs ⚠ Don't close lid: 12s while the unsafe window is active, nothing otherwise. To combine with an existing statusline, call both commands and concatenate their output.
Log: /tmp/keep-awake-daemon.log (truncated on each daemon restart).
- macOS — Xcode Command Line Tools for
swiftc(xcode-select --install). Without it, falls back to plaincaffeinate(no closed-lid support). - Linux —
systemd-inhibit(standard on systemd distros) orgnome-session-inhibit - Windows — Git Bash with access to
powershell.exe