GitHub - kageroumado/adrafinil: Keep your Mac awake only while AI coding agents are working


**rx no. 006 ・ a·draf·i·nil /əˈdræfɪnɪl/ ・ a eugeroic for machines ♡**

> **服用注意 ・ for machines that keep watch after you've gone to sleep.** > >
> It's 3 a.m. You're asleep. The agent isn't — it's still mid-thought in a session you started hours ago, and you've closed the lid over it like an eyelid that won't quite shut. `caffeinate` and Amphetamine are stimulants: they keep the machine wired _forever_, whether or not anyone's home. Adrafinil is the eugeroic. It does **nothing** until an agent acquires it, keeps your Mac awake through a closed lid only for as long as that work lives, and clears the moment the last session releases. It only ever wakes for the work — then you both sleep. ♡
- * *
Keep your Mac awake **only while AI agents are working**.
Adrafinil is a macOS menu bar app that prevents the system from sleeping — including clamshell (lid-closed) sleep — **exclusively while an AI coding agent has an active session**. When no agent is working, sleep behavior is untouched: close the lid and the Mac sleeps normally.
It's the opposite of always-on wake utilities like `caffeinate` or Amphetamine. Adrafinil only intervenes when an agent (Claude Code, Codex, Cursor, …) is mid-task, and gets out of the way the moment that work finishes.
> ⚠️**Privileged sleep control.** Overriding clamshell sleep requires root. Adrafinil isolates that in a tiny, audited helper that only exposes `setSleepBlocked(Bool)` — all policy lives in an unprivileged daemon. It holds a standard `IOPMAssertion` for idle sleep and uses `pmset disablesleep` for clamshell (lid-closed) sleep, after verifying on-device that the cleaner private `IOPMrootDomain` paths don't keep a displayless lid-closed Mac awake. See Docs/ARCHITECTURE.md §2.
Features
[](https://github.com/kageroumado/adrafinil#features)
- **Agent-aware, not always-on.** Sleep is blocked only while ≥1 agent session holds an assertion. Zero sessions → normal sleep, including lid-close.
- **Hook integration for 9 agents.** One-click installer wires Adrafinil into the hook systems of Claude Code, Codex, Cursor, Gemini CLI, Aider, Hermes, OpenCode, Cline, and Pi.
- **Sub-50ms CLI.**`adrafinil acquire` / `release` are called from agent hooks and round-trip to the daemon in under 50ms, so they never stall an agent's workflow.
- **Reference-counted assertions.** Overlapping sessions stack cleanly; sleep unblocks only when the last one releases.
- **Thermal cutout.** If skin/CPU temperature crosses threshold while the lid is closed, all assertions are force-released so a bag-bound Mac can't cook itself.
- **Idle release.** Assertions whose owning process has died or gone CPU-idle for N minutes are dropped automatically.
- **Process sniffing (optional).** The daemon can auto-acquire when it sees a known agent binary running, even without hooks installed.
- **Lid-close audio + lid-open summary.** A chime confirms an assertion is held when you close the lid (the screen is off, so no notification); reopening shows what ran while you were away, peak temperature, and whether the thermal cutout fired.
- **Clean uninstall.** Removes every hook entry it added across all agent configs.
Requirements
[](https://github.com/kageroumado/adrafinil#requirements)
- **macOS Tahoe 26.4.** That's what I build and test on; it likely runs on earlier 26.x, but I haven't tested it there.
- **Xcode 26+** to build, with Swift 6 strict concurrency enabled.
- Admin rights for the standard install (the privileged helper installs via `SMAppService`). A non-admin install path drops the CLI in `~/.local/bin` instead of `/usr/local/bin`.
Download
[](https://github.com/kageroumado/adrafinil#download)
**Download Adrafinil** — a signed, notarized disk image. Open it, drag **Adrafinil** to Applications, and launch. The first launch asks for admin rights once to register the privileged helper. Requires macOS 26.4 or later.
Prefer to build it yourself? See Building.
Building
[](https://github.com/kageroumado/adrafinil#building)
git clone https://github.com/kageroumado/adrafinil.git cd adrafinil open Adrafinil.xcodeproj
In Xcode, select the **Adrafinil** scheme and Run. You'll need to set a development team for code signing — the daemon (LaunchAgent) and helper (LaunchDaemon) are embedded into the app bundle and registered with the system when the app launches. (No Team ID is baked into the source; the XPC caller check reads your _own_ signing team at runtime, so a rebuild under any Developer ID authorizes its own components without code changes.)
For a headless compile check without local signing identities:
xcodebuild -project Adrafinil.xcodeproj -scheme Adrafinil -configuration Debug \ -destination 'generic/platform=macOS' \ CODE_SIGNING_ALLOWED=NO CODE_SIGNING_REQUIRED=NO CODE_SIGN_IDENTITY='' build
The shared logic builds and tests standalone as a Swift package:
cd AdrafinilShared swift test
How it works
[](https://github.com/kageroumado/adrafinil#how-it-works) Agents don't talk to Adrafinil directly. Each agent's hook system calls the bundled CLI:
adrafinil acquire <session-key> --tool claude-code --reason "long build" # when a turn starts adrafinil release <session-key> # when the agent goes idle
Holds are **activity-scoped**, not session-scoped: Claude Code acquires on `UserPromptSubmit` and releases on `Stop`, so the Mac is only kept awake while the agent is actually working — an open-but-idle session at the prompt lets it sleep normally.
The daemon refcounts by session key and asks the helper to block sleep while the count is non-zero.
An agent can also keep the Mac awake for a background task that outlives its reply (a long build or deploy) with a time-boxed **hold** — either by calling `adrafinil hold` directly or, for MCP-capable agents, through the bundled MCP tool that `adrafinil mcp` serves:
adrafinil hold --for 30m --reason "deploy" # keep awake up to 30 min, then auto-release adrafinil mcp # speak the Model Context Protocol on stdio (for agents)
Other subcommands: `status`, `install-hooks`, `uninstall-hooks`, `daemon-status`, `version`.
Architecture
[](https://github.com/kageroumado/adrafinil#architecture) Four products across three privilege tiers (full detail, including the Xcode project layout, in Docs/ARCHITECTURE.md):
``` ┌──────────────────────────────────────────────────────────────┐ │ Adrafinil.app (menu bar app, user-facing) │ │ • Status item, settings, installer GUI, lid-open summary │ └─────────────────────────────┬────────────────────────────────┘ │ XPC ▼ ┌──────────────────────────────────────────────────────────────┐ │ AdrafinilDaemon (LaunchAgent, runs as user, always-on) │ │ • Reference-counted assertion registry │ │ • Process watchers (kqueue NOTE_EXIT + periodic sweep) │ │ • Thermal monitor (SMC) • Lid-state monitor (IORegistry) │ │ • Lid-close chime • CLI socket at …/Adrafinil/cli.sock │ └─────────────────────────────┬────────────────────────────────┘ │ XPC (privileged Mach service) ▼ ┌──────────────────────────────────────────────────────────────┐ │ AdrafinilHelper (SMAppService LaunchDaemon, root) │ │ • The ONLY component that touches sleep-blocking APIs │ │ • setSleepBlocked(Bool) + read-only state/version │ │ • Verifies caller's code-signing requirement │ └──────────────────────────────────────────────────────────────┘
adrafinil (CLI, ships inside the .app, symlinked onto PATH) • acquire / release / hold / mcp / status / install-hooks / uninstall-hooks • Connects to the daemon socket; <50ms round-trip ```
- **`AdrafinilShared`** — a Swift package shared across every target: data models (`AgentKind`, `Assertion`), the IPC wire formats, `AssertionRegistry`, `CallerVerifier`, the hook-install specs, and the CLI argument parser. This is where the unit tests live.
- **Helper stays trivial to audit.** It holds no policy — ref counting, thermal, idle, and lid logic all live in the daemon. The privileged surface is a single mutating endpoint plus read-only introspection.
- **Daemon is the source of truth.** The app is a pure view layer; it can quit and relaunch freely without affecting held assertions.
Quirks worth knowing
[](https://github.com/kageroumado/adrafinil#quirks-worth-knowing)
- **Public IOPM assertions don't beat clamshell sleep.**`IOPMAssertionCreateWithName` with the public types (and therefore `caffeinate`) will not keep a lid-closed Mac awake. Adrafinil's v1 uses `pmset disablesleep 1`, which is blunt (it also disables idle sleep) and _must_ be cleared on shutdown or it leaks — the helper resets to `disablesleep 0` on respawn before re-applying state.
- **Daemon handlers run on arbitrary queues.** XPC and socket callbacks can arrive on any dispatch queue, so the assertion registry and shared state are synchronized accordingly. Tread carefully around concurrency when modifying the daemon.
- **The CLI is on a latency budget.**`acquire`/`release` are in the hot path of every agent session, hence static lookups (e.g. `AgentKind.allBinaryNames`) and a thin socket protocol instead of full XPC for the CLI ↔ daemon hop.
License
[](https://github.com/kageroumado/adrafinil#license) MIT. Do whatever you want, no warranty.
Acknowledgements
[](https://github.com/kageroumado/adrafinil#acknowledgements)
Built by @kageroumado, dispensed at kagerou.glass. The name is a nod to adrafinil — a wakefulness-promoting prodrug — because the app keeps your machine awake only when it actually has work to do.