TestFlightiOSSwift2026
Eily
A meeting assistant that understands the conversation on your phone, in real time, and turns it into tracked work.
A menu-bar cat that tells you when a Claude Code agent needs you.
The problem
You give an agent a long task, close the lid, walk away. Two things go wrong: the agent stops to ask a permission question and sits idle for an hour, or the Mac falls asleep and cuts the session mid-work.
The existing tools each solve half of it. caffeinate and Amphetamine keep the Mac awake but have no idea whether an agent is still working. Claude Code's own notification hook fires once and forgets. RunCat reacts to CPU, and a waiting agent uses none.
I wanted one thing on screen that knows the state of every session and reacts to it: keep the Mac awake while agents work, and get my attention the moment one needs me.
What I built
CodeCat is a native macOS menu-bar utility. It subscribes to Claude Code's hooks, keeps a live model of every session — CLI and desktop app — and drives three things from it: an IOKit power assertion, a menu-bar item, and a pixel cat.
The cat is the interface. It sleeps when nothing is running, works with a badge showing the session count, waves a paw with an orange badge when an agent is waiting for you, and stretches and lies down when everything is done. It lives either as a floating always-on-top window or as an "island" — a black plate that covers the MacBook notch, cat on the left, session counter on the right.
Clicking a session takes you to it. In Terminal and iTerm2 that is the exact tab, found by its tty. In the desktop Claude app it is the exact chat: the app keeps a record per session and registers a claude://code/continue link, and since 0.4.0 the row opens that chat instead of just bringing the app forward. A "while you were away" panel collects everything that happened between screen lock and unlock. Closed-lid mode uses pmset disablesleep behind a sudoers rule scoped to exactly two commands, with a LaunchDaemon that clears the flag if CodeCat ever dies.
In 0.4.0 the cat also learned to wear any pet drawn for the Codex pet format — a folder with a pet.json and an 8 × 9 sprite sheet, read from ~/.codex/pets or from CodeCat's own Pets folder. The format is someone else's, which set the rules: pets are read where they are, never copied or redistributed, and a broken folder is skipped and named once in the log. They often ship upscaled, so CodeCat finds the sheet's native pixel pitch and draws it as crisply as the built-in packs whenever the native drawing fits the cat's canvas. Sessions are still Claude Code only: wearing a Codex pet doesn't make CodeCat track Codex.
Next release
0.4.0 counted sessions. The next release, built on 18 September, draws one dot per session in its own colour — up to four, then a count — and lets the worst state tint the glow behind the cat. Hover, and each row says the task in your words, the step the agent is on and how far along its own plan it is, read from the TodoWrite list it keeps in the transcript. A finished turn hands back what it made — a local server, a pull request, a Figma link — as chips you can click or drag.




Design decisions
The menu bar on a notched display is 32 pt, not the 22 pt NSStatusBar reports. The notch is 185 pt wide. The island sits at window level 26 — above the menu bar (24) and status icons (25), below pop-up menus (101).
The floating panel sits on system material and uses semantic colours. The island sits on pure black to match the physical notch, and there the system colours lie: .secondary renders near-black on black in light mode. So the island's whites are fixed values (1.0 / 0.62 / 0.38 / 0.08), and selection is a white border, because system blue already means 'done'.


Left: the floating panel in 0.4.0 — one indicator drives the title, the rows and the badge; setting up Claude Code is the first thing in the settings, and it asks before it writes. Right: the island's menu in the next release, on black. Both at 1:1.
'Done' plays stretch twice → lie down → breathe, because a one-shot animation looks absurd on a loop. A skin missing a state reuses its own nearest animation rather than borrowing from another skin.
SessionStart fires on launch, on --resume and on /clear, before any work exists. Treating it as work left a permanent phantom '1' on the badge. Idle became its own state, excluded from the badge, the aggregate and the power policy.
The hard parts
The power policy read the display aggregate, where 'waiting' outranks 'working' — so a single permission prompt released the sleep assertion for every other running agent. It was the exact failure CodeCat exists to prevent, and no per-task test caught it. The review of the whole branch did. The fix is a separate anyWorking accessor that ignores display priority.
pmset disablesleep 0 dropped the Mac to sleep in 37 msClearing the flag on exit made the kernel re-read accumulated idle time and sleep immediately. The watchdog now handles cleanup asynchronously instead.
Hardened runtime — required for notarization — blocks the AppleScript that selects a terminal tab, with errAEEventNotPermitted. It is invisible on ad-hoc builds. The fix is an entitlement, plus a make sign guard that greps the signature for it rather than trusting the file.
The hook binary lives inside CodeCat.app, so walking the process tree from getpid() names CodeCat as the session owner. Walking from getppid() and collecting the outermost .app, bundle id and inherited tty finds the real one — and routes are cached on disk so a restart doesn't orphan live sessions.
How it was built
I wrote the spec at 23:10 on 28 August. The agents wrote their first code 17 minutes later. Working installed build on day two, signed and notarized on day five: 151 commits, roughly a third of them post-review fixes. I had every mascot state rendered to PNG and checked each one by eye. That is how I found that all the animations were attached to an EmptyView(): the cat had no body.
No analytics, no network calls, no dependencies. The download is a 2 MB notarized disk image.
TestFlightiOSSwift2026
A meeting assistant that understands the conversation on your phone, in real time, and turns it into tracked work.