{"slug": "show-hn-adrafinil-keep-a-lid-closed-mac-awake-only-while-agents-work", "title": "Show HN: Adrafinil – keep a lid-closed Mac awake only while agents work", "summary": "Adrafinil, a macOS menu bar app, keeps a Mac awake only while AI coding agents have active sessions, preventing sleep during clamshell mode. It uses a privileged helper for root-level sleep control and supports nine agents including Claude Code and Cursor. The app ensures normal sleep resumes when no agents are working.", "body_md": "**rx no. 006 ・ a·draf·i·nil /əˈdræfɪnɪl/ ・ a eugeroic for machines ♡**\n\nawake ・ an agent is working |\nsleeping ・ no agents, normal sleep |\n\n服用注意 ・ 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.\n\n`caffeinate`\n\nand Amphetamine are stimulants: they keep the machine wiredforever, whether or not anyone's home. Adrafinil is the eugeroic. It doesnothinguntil 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. ♡\n\nKeep your Mac awake **only while AI agents are working**.\n\nAdrafinil is a macOS menu bar app that prevents the system from sleeping — including clamshell\n(lid-closed) sleep — **exclusively while an AI coding agent has an active session**. When no agent\nis working, sleep behavior is untouched: close the lid and the Mac sleeps normally.\n\nIt's the opposite of always-on wake utilities like `caffeinate`\n\nor Amphetamine. Adrafinil only\nintervenes when an agent (Claude Code, Codex, Cursor, …) is mid-task, and gets out of the way the\nmoment that work finishes.\n\n⚠️ Privileged sleep control.Overriding clamshell sleep requires root. Adrafinil isolates that in a tiny, audited helper that only exposes`setSleepBlocked(Bool)`\n\n— all policy lives in an unprivileged daemon. It holds a standard`IOPMAssertion`\n\nfor idle sleep and uses`pmset disablesleep`\n\nfor clamshell (lid-closed) sleep, after verifying on-device that the cleaner private`IOPMrootDomain`\n\npaths don't keep a displayless lid-closed Mac awake. See[Docs/ARCHITECTURE.md]§2.\n\n**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`\n\n/`release`\n\nare 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.\n\n**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\n`SMAppService`\n\n). A non-admin install path drops the CLI in`~/.local/bin`\n\ninstead of`/usr/local/bin`\n\n.\n\n** Download Adrafinil** — a signed, notarized disk image. Open it, drag\n\n**Adrafinil** to Applications, and launch. The first launch asks for admin rights once to register the privileged helper. Requires macOS 26.4 or later.\n\nPrefer to build it yourself? See [Building](#building).\n\n```\ngit clone https://github.com/kageroumado/adrafinil.git\ncd adrafinil\nopen Adrafinil.xcodeproj\n```\n\nIn Xcode, select the **Adrafinil** scheme and Run. You'll need to set a development team for code\nsigning — the daemon (LaunchAgent) and helper (LaunchDaemon) are embedded into the app bundle and\nregistered with the system when the app launches. (No Team ID is baked into the source; the XPC\ncaller check reads your *own* signing team at runtime, so a rebuild under any Developer ID\nauthorizes its own components without code changes.)\n\nFor a headless compile check without local signing identities:\n\n```\nxcodebuild -project Adrafinil.xcodeproj -scheme Adrafinil -configuration Debug \\\n  -destination 'generic/platform=macOS' \\\n  CODE_SIGNING_ALLOWED=NO CODE_SIGNING_REQUIRED=NO CODE_SIGN_IDENTITY='' build\n```\n\nThe shared logic builds and tests standalone as a Swift package:\n\n```\ncd AdrafinilShared\nswift test\n```\n\nAgents don't talk to Adrafinil directly. Each agent's hook system calls the bundled CLI:\n\n```\nadrafinil acquire <session-key> --tool claude-code --reason \"long build\"   # when a turn starts\nadrafinil release <session-key>                                            # when the agent goes idle\n```\n\nHolds are **activity-scoped**, not session-scoped: Claude Code acquires on `UserPromptSubmit`\n\nand\nreleases on `Stop`\n\n, so the Mac is only kept awake while the agent is actually working — an\nopen-but-idle session at the prompt lets it sleep normally.\n\nThe daemon refcounts by session key and asks the helper to block sleep while the count is non-zero.\n\nAn agent can also keep the Mac awake for a background task that outlives its reply (a long build or\ndeploy) with a time-boxed **hold** — either by calling `adrafinil hold`\n\ndirectly or, for MCP-capable\nagents, through the bundled MCP tool that `adrafinil mcp`\n\nserves:\n\n```\nadrafinil hold --for 30m --reason \"deploy\"   # keep awake up to 30 min, then auto-release\nadrafinil mcp                                 # speak the Model Context Protocol on stdio (for agents)\n```\n\nOther subcommands: `status`\n\n, `install-hooks`\n\n, `uninstall-hooks`\n\n, `daemon-status`\n\n, `version`\n\n.\n\nFour products across three privilege tiers (full detail, including the Xcode project layout, in\n[Docs/ARCHITECTURE.md](/kageroumado/adrafinil/blob/main/Docs/ARCHITECTURE.md)):\n\n```\n┌──────────────────────────────────────────────────────────────┐\n│  Adrafinil.app   (menu bar app, user-facing)                 │\n│  • Status item, settings, installer GUI, lid-open summary    │\n└─────────────────────────────┬────────────────────────────────┘\n                              │ XPC\n                              ▼\n┌──────────────────────────────────────────────────────────────┐\n│  AdrafinilDaemon  (LaunchAgent, runs as user, always-on)     │\n│  • Reference-counted assertion registry                      │\n│  • Process watchers (kqueue NOTE_EXIT + periodic sweep)      │\n│  • Thermal monitor (SMC)  • Lid-state monitor (IORegistry)   │\n│  • Lid-close chime  • CLI socket at …/Adrafinil/cli.sock     │\n└─────────────────────────────┬────────────────────────────────┘\n                              │ XPC (privileged Mach service)\n                              ▼\n┌──────────────────────────────────────────────────────────────┐\n│  AdrafinilHelper  (SMAppService LaunchDaemon, root)          │\n│  • The ONLY component that touches sleep-blocking APIs       │\n│  • setSleepBlocked(Bool) + read-only state/version           │\n│  • Verifies caller's code-signing requirement                │\n└──────────────────────────────────────────────────────────────┘\n\n  adrafinil  (CLI, ships inside the .app, symlinked onto PATH)\n  • acquire / release / hold / mcp / status / install-hooks / uninstall-hooks\n  • Connects to the daemon socket; <50ms round-trip\n```\n\n— a Swift package shared across every target: data models (`AdrafinilShared`\n\n`AgentKind`\n\n,`Assertion`\n\n), the IPC wire formats,`AssertionRegistry`\n\n,`CallerVerifier`\n\n, 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.\n\n**Public IOPM assertions don't beat clamshell sleep.**`IOPMAssertionCreateWithName`\n\nwith the public types (and therefore`caffeinate`\n\n) will not keep a lid-closed Mac awake. Adrafinil's v1 uses`pmset disablesleep 1`\n\n, which is blunt (it also disables idle sleep) and*must*be cleared on shutdown or it leaks — the helper resets to`disablesleep 0`\n\non 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`\n\n/`release`\n\nare in the hot path of every agent session, hence static lookups (e.g.`AgentKind.allBinaryNames`\n\n) and a thin socket protocol instead of full XPC for the CLI ↔ daemon hop.\n\n[MIT](/kageroumado/adrafinil/blob/main/LICENSE). Do whatever you want, no warranty.\n\nBuilt by [@kageroumado](https://x.com/kageroumado), dispensed at [kagerou.glass](https://kagerou.glass).\nThe name is a nod to [adrafinil](https://en.wikipedia.org/wiki/Adrafinil) — a wakefulness-promoting\nprodrug — because the app keeps your machine awake only when it actually has work to do.", "url": "https://wpnews.pro/news/show-hn-adrafinil-keep-a-lid-closed-mac-awake-only-while-agents-work", "canonical_source": "https://github.com/kageroumado/adrafinil", "published_at": "2026-06-27 20:34:53+00:00", "updated_at": "2026-06-27 21:03:19.611638+00:00", "lang": "en", "topics": ["ai-tools", "developer-tools", "ai-agents"], "entities": ["Adrafinil", "Claude Code", "Codex", "Cursor", "Gemini CLI", "Aider", "Hermes", "OpenCode"], "alternates": {"html": "https://wpnews.pro/news/show-hn-adrafinil-keep-a-lid-closed-mac-awake-only-while-agents-work", "markdown": "https://wpnews.pro/news/show-hn-adrafinil-keep-a-lid-closed-mac-awake-only-while-agents-work.md", "text": "https://wpnews.pro/news/show-hn-adrafinil-keep-a-lid-closed-mac-awake-only-while-agents-work.txt", "jsonld": "https://wpnews.pro/news/show-hn-adrafinil-keep-a-lid-closed-mac-awake-only-while-agents-work.jsonld"}}