pi-espresso
Keeps the Mac awake (display + system idle) while the pi agent runs, with a ☕️ terminal title marker. A double shot over caffeinate -i: also prevents display sleep.
Package details
Install pi-espresso from npm and Pi will load the resources declared by the package manifest.
$ pi install npm:pi-espresso- Package
pi-espresso- Version
0.2.0- Published
- Aug 31, 2026
- Downloads
- 286/mo · 16/wk
- Author
- fxtack
- License
- MIT
- Types
- extension
- Size
- 19.4 KB
- Dependencies
- 0 dependencies · 0 peers
Pi manifest JSON
{
"extensions": [
"./extensions"
]
}Security note
Pi packages can execute code and influence agent behavior. Review the source before installing third-party packages.
README
pi-espresso ☕️
A pi extension that keeps your Mac awake — display included — while the agent is working, and marks the terminal title with ☕️ so you can see it at a glance.
The name is the feature list: most keep-awake extensions only run caffeinate -i (block system idle sleep). pi-espresso pulls a double shot — -d -i — so your screen stays on too. The moment the agent settles, your Mac goes right back to sleep on its own; nothing lingers.
Why
pi can run for minutes at a time — long refactors, test suites, multi-step tasks. If you look away, macOS dims the display or the machine idles to sleep mid-task. Closing the lid or sleeping manually should always work; only the unattended idle path should be suspended, and only while the agent is actually running.
How it works
The extension listens to pi's agent lifecycle events and manages a caffeinate child process around them:
| Event | Action |
|---|---|
agent_start |
Spawns caffeinate -di, sets the ☕️ title marker |
agent_settled |
Kills caffeinate (SIGTERM) and restores the title. agent_settled fires only when pi will not continue on its own — auto-retries, auto-compaction, and queued follow-up messages keep the assertion alive, with no flicker between them |
session_info_changed / session_start |
If caffeinate is still active, re-asserts the ☕️ title after a short delay (pi rewrites the title on /name renames and session switch/new/resume/fork) |
subagent:async-started / subagent:async-complete |
Tracks async subagents launched via pi-subagents on the in-process event bus — wake demand persists while any of them runs |
session_shutdown |
Final cleanup |
-dprevents display sleep;-iprevents system idle sleep.- On non-macOS platforms the extension is a complete no-op — safe to sync across machines.
- Title changes are guarded by
ctx.hasUIand skipped in print/JSON modes.
Subagent awareness
Subagents run in separate processes, but the main window still reflects them:
- Synchronous subagent calls are already covered — they execute inside the main agent run, so the assertion never drops.
- Async / detached subagents are tracked through pi-subagents' in-process lifecycle events (
subagent:async-started/subagent:async-complete). While any async run is active, the main window keeps the ☕️ marker and holds acaffeinateassertion — even if the main agent itself is idle. When the last subagent settles, both are released. - On
/reloador late pi-subagents readiness, the current active-run count is hydrated from pi-subagents' status RPC (fleet.totalActive), so a restarted extension instance picks up in-flight subagents.
Subagent counting is additive with the main run: multiple concurrent caffeinate assertions coexist harmlessly on macOS, and the assertion is only released when both sources are idle.
Terminal title marker
While caffeinate runs, the terminal title becomes:
☕️ π - [session name - ]cwd-basename
This mirrors pi's native title format exactly, only prefixed. The marker is set on agent_start and restored on agent_settled. Because pi rewrites the title itself on renames and session switches, the extension re-asserts the marker (delayed 50 ms, so it always lands after pi's own handler) on session_info_changed and session_start while the assertion is active.
Requirements
- macOS —
caffeinateships with macOS; nothing extra to install. - pi — any recent version; uses only the public extension API.
Install
# Option 1: install from a local path (records in ~/.pi/agent/settings.json)
pi install /path/to/pi-espresso
# Option 2: install from a git repo, pinned to a ref
pi install git:github.com/fxtack/pi-espresso@v0.1.0
# Option 3: symlink the single file into the global extension directory
ln -s /path/to/pi-espresso/extensions/espresso.ts ~/.pi/agent/extensions/espresso.ts
Then restart pi, or run /reload in an open session.
How is this different from pi-caffeinate?
pi-caffeinate is a solid cross-platform extension, but it makes the opposite trade-off on display sleep:
| pi-caffeinate | pi-espresso | |
|---|---|---|
| Platforms | macOS / Linux / Windows | macOS only (no-op elsewhere) |
| Display sleep | Unaffected by design | Prevented while the agent runs |
| Flags | caffeinate -i |
caffeinate -di |
| Stop event | agent_end (re-engages between queued messages and retries) |
agent_settled (no flicker) |
| Crash cleanup | process.on("exit") safety net |
session_shutdown only |
| Title marker | — | ☕️ with re-assert after pi rewrites |
If you want display sleep untouched, use pi-caffeinate. If your problem is the screen going dark mid-task, this is the one.
Known limitations
kill -9/ abrupt exit: solved — the caffeinate child is spawned with-w <pi-pid>, so it watches pi's process and exits whenever pi dies, no matter how (including SIGKILL, or exiting while async subagents are still running).- Custom-branded pi builds: the title base is composed as
π - …to match pi's defaultAPP_TITLE. If your pi build renames itself viapiConfig.name, the composed title will differ slightly until the next pi-driven rewrite self-heals it. - Subagents with a custom
extensionsallowlist: an agent config that declaresextensions: [...]disables normal discovered extensions, so pi-espresso will not load inside those child processes. Foreground runs are still covered by the parent's assertion; for background runs of such agents, add the espresso extension path to that agent'sextensionslist (orsubagentOnlyExtensions).
Development
pi-espresso/
├── extensions/
│ └── espresso.ts # the entire extension (single file, zero runtime deps)
├── package.json # pi package manifest
└── README.md
- Syntax check:
node --check extensions/espresso.ts - The extension only imports Node builtins at runtime; the
@earendil-works/pi-coding-agentimport is type-only. - For local use, symlink
extensions/espresso.tsinto~/.pi/agent/extensions/and edit in place — pi's loader follows symlinks.
License
MIT © fxtack