{"id":51312709,"url":"https://github.com/kageroumado/adrafinil","last_synced_at":"2026-07-01T05:04:32.611Z","repository":{"id":362801890,"uuid":"1259885475","full_name":"kageroumado/adrafinil","owner":"kageroumado","description":"Keep your Mac awake only while AI coding agents are working","archived":false,"fork":false,"pushed_at":"2026-06-29T00:22:08.000Z","size":787,"stargazers_count":219,"open_issues_count":0,"forks_count":1,"subscribers_count":1,"default_branch":"main","last_synced_at":"2026-06-29T02:12:27.277Z","etag":null,"topics":["caffeinate","macos"],"latest_commit_sha":null,"homepage":"https://kagerou.glass/adrafinil/","language":"Swift","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/kageroumado.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":".github/FUNDING.yml","license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":"SECURITY.md","support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null},"funding":{"ko_fi":"kageroumado"}},"created_at":"2026-06-05T00:46:00.000Z","updated_at":"2026-06-29T02:02:03.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/kageroumado/adrafinil","commit_stats":null,"previous_names":["kageroumado/adrafinil"],"tags_count":5,"template":false,"template_full_name":null,"purl":"pkg:github/kageroumado/adrafinil","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kageroumado%2Fadrafinil","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kageroumado%2Fadrafinil/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kageroumado%2Fadrafinil/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kageroumado%2Fadrafinil/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/kageroumado","download_url":"https://codeload.github.com/kageroumado/adrafinil/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kageroumado%2Fadrafinil/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34993451,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-07-01T02:00:05.325Z","response_time":130,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["caffeinate","macos"],"created_at":"2026-07-01T05:04:32.059Z","updated_at":"2026-07-01T05:04:32.593Z","avatar_url":"https://github.com/kageroumado.png","language":"Swift","funding_links":["https://ko-fi.com/kageroumado"],"categories":[],"sub_categories":[],"readme":"\u003cdiv align=\"center\"\u003e\n\n[![adrafinil](https://readme-typing-svg.demolab.com/?font=DotGothic16\u0026weight=400\u0026size=22\u0026duration=3800\u0026pause=900\u0026color=FF5FA6\u0026center=true\u0026vCenter=true\u0026width=820\u0026height=60\u0026lines=wakefulness%20only%20for%20machines%20with%20work%20left%20undone%20%E2%99%A1;stays%20awake%20while%20your%20agents%20work%20%E3%83%BB%20then%20sleeps;not%20caffeine%20%E3%83%BB%20the%20eugeroic;rx%20no.%20006%20%E3%83%BB%20%E6%9C%8D%E7%94%A8%E6%B3%A8%E6%84%8F%20%E3%83%BB%20best%20taken%20%40%203%20a.m.)](https://kagerou.glass)\n\n\u003cimg src=\".github/adrafinil-icon.png\" alt=\"Adrafinil icon\" width=\"128\" height=\"128\"\u003e\n\n# adrafinil\n\n**rx no. 006 ・ a·draf·i·nil /əˈdræfɪnɪl/ ・ a eugeroic for machines ♡**\n\n[![kagerou.glass](https://img.shields.io/badge/kagerou.glass-ff5fa6?style=for-the-badge\u0026logo=safari\u0026logoColor=white)](https://kagerou.glass/adrafinil/)\n[![@kageroumado](https://img.shields.io/badge/@kageroumado-76e6e0?style=for-the-badge\u0026logo=x\u0026logoColor=0d0a10)](https://x.com/kageroumado)\n[![macOS Tahoe](https://img.shields.io/badge/macOS-Tahoe_26%2B-0d0a10?style=for-the-badge\u0026logo=apple\u0026logoColor=white)](#requirements)\n\n\u003ctable\u003e\n  \u003ctr\u003e\n    \u003ctd align=\"center\"\u003e\u003cimg src=\".github/adrafinil-awake.png\" alt=\"Awake — kept awake while an agent works, with Keep awake / Let it sleep controls\" width=\"420\"\u003e\u003cbr\u003e\u003csub\u003e\u003cb\u003eawake\u003c/b\u003e ・ an agent is working — or keep it awake yourself\u003c/sub\u003e\u003c/td\u003e\n    \u003ctd align=\"center\"\u003e\u003cimg src=\".github/adrafinil-sleeping.png\" alt=\"Idle — no agents, normal sleep, with a Keep awake button to hold it open yourself\" width=\"420\"\u003e\u003cbr\u003e\u003csub\u003e\u003cb\u003eidle\u003c/b\u003e ・ no agents, normal sleep — or keep it awake yourself\u003c/sub\u003e\u003c/td\u003e\n  \u003c/tr\u003e\n\u003c/table\u003e\n\n\u003c/div\u003e\n\n\u003e **服用注意 ・ for machines that keep watch after you've gone to sleep.**\n\u003e\n\u003e It's 3 a.m. You're asleep. The agent isn't — it's still mid-thought in a session you started\n\u003e hours ago, and you've closed the lid over it like an eyelid that won't quite shut. `caffeinate`\n\u003e and Amphetamine are stimulants: they keep the machine wired *forever*, whether or not anyone's\n\u003e home. Adrafinil is the eugeroic. It does **nothing** until an agent acquires it, keeps your Mac\n\u003e awake through a closed lid only for as long as that work lives, and clears the moment the last\n\u003e session releases. It only ever wakes for the work — then you both sleep. ♡\n\n---\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` or 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**Three ways it stays awake:**\n\n- **Automatically** — agent hooks tell Adrafinil when a turn starts and ends, so it holds sleep only while an agent is actually working. (Optional process-sniffing can also spot a running agent that has no hooks installed.)\n- **Agent-driven** — an agent can deliberately keep the Mac awake *past its reply* for a long build or deploy, via the bundled MCP tool or the `adrafinil hold` CLI.\n- **Manually** — a menu-bar **Keep awake** button places a time-boxed hold yourself, even with **no agents running**; **Let it sleep** clears everything.\n\n\u003e ⚠️ **Privileged sleep control.** Overriding clamshell sleep requires root. Adrafinil isolates\n\u003e that in a tiny, audited helper that only exposes `setSleepBlocked(Bool)` — all policy lives in an\n\u003e unprivileged daemon. It holds a standard `IOPMAssertion` for idle sleep and uses\n\u003e `pmset disablesleep` for clamshell (lid-closed) sleep, after verifying on-device that the cleaner\n\u003e private `IOPMrootDomain` paths don't keep a displayless lid-closed Mac awake. See\n\u003e [Docs/ARCHITECTURE.md](Docs/ARCHITECTURE.md) §2.\n\n## Features\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.\n- **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.\n- **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.\n- **Reference-counted assertions.** Overlapping sessions stack cleanly; sleep unblocks only when the last one releases.\n- **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.\n- **Idle release.** Assertions whose owning process has died or gone CPU-idle for N minutes are dropped automatically.\n- **Process sniffing (optional).** The daemon can auto-acquire when it sees a known agent binary running, even without hooks installed.\n- **Manual keep-awake (no agent needed).** A menu-bar **Keep awake** button starts a time-boxed hold on demand — for a long download, a local job, anything off-agent — then **Let it sleep** releases it.\n- **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.\n- **Clean uninstall.** Removes every hook entry it added across all agent configs.\n\n## Requirements\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.\n- **Xcode 26+** to build, with Swift 6 strict concurrency enabled.\n- 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`.\n\n## Download\n\n**[Download Adrafinil](https://github.com/kageroumado/adrafinil/releases/latest)** — 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.\n\nPrefer to build it yourself? See [Building](#building).\n\n## Building\n\n```sh\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```sh\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```sh\ncd AdrafinilShared\nswift test\n```\n\n## How it works\n\nAgents don't talk to Adrafinil directly. Each agent's hook system calls the bundled CLI:\n\n```sh\nadrafinil acquire \u003csession-key\u003e --tool claude-code --reason \"long build\"   # when a turn starts\nadrafinil release \u003csession-key\u003e                                            # when the agent goes idle\n```\n\nHolds are **activity-scoped**, not session-scoped: Claude Code acquires on `UserPromptSubmit` and\nreleases on `Stop`, 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` directly or, for MCP-capable\nagents, through the bundled MCP tool that `adrafinil mcp` serves:\n\n```sh\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\nYou don't need an agent at all: the menu bar's **Keep awake** button places the same kind of\ntime-boxed hold by hand — for a long download or any off-agent task — and **Let it sleep** releases\neverything. So Adrafinil covers all three: it auto-detects agent activity through hooks, lets agents\ndrive it explicitly over MCP/CLI, and can be flipped on manually when you need it.\n\nOther subcommands: `status`, `install-hooks`, `uninstall-hooks`, `daemon-status`, `version`.\n\n## Architecture\n\nFour products across three privilege tiers (full detail, including the Xcode project layout, in\n[Docs/ARCHITECTURE.md](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; \u003c50ms round-trip\n```\n\n- **`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.\n- **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.\n- **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## Quirks worth knowing\n\n- **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.\n- **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.\n- **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.\n\n## License\n\n[MIT](LICENSE). Do whatever you want, no warranty.\n\n## Acknowledgements\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.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkageroumado%2Fadrafinil","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fkageroumado%2Fadrafinil","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkageroumado%2Fadrafinil/lists"}