p.enthalabs

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

![Image 1: adrafinil](https://kagerou.glass/)

![Image 2: Adrafinil icon](https://github.com/kageroumado/adrafinil/blob/main/.github/adrafinil-icon.png)

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

![Image 3: kagerou.glass](https://kagerou.glass/adrafinil/)![Image 4: @kageroumado](https://x.com/kageroumado)![Image 5: macOS Tahoe](https://github.com/kageroumado/adrafinil#requirements)

> **服用注意 ・ 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.