I run several AI agents at once: a handful of Claude Code sessions, plus a small "company" of agents on Paperclip that works on our online store. Giving them work is easy. The hard part is knowing, at a glance, which one is working, which one is done, and which one has been waiting for my answer for twenty minutes.
Logs don't answer that at a glance. So I drew an office.
Live demo, no install · GitHub · npx @caglarutkuguler/cubicle
Every agent is a character:
The header keeps a count of how many agents need you. That single number turned out to be the most useful thing on the screen.
The server is a single Node file of under 200 lines, using only the standard library: it serves one HTML page and proxies a few requests. The page draws everything with fillRect on a 352×208 canvas. There are no image assets, no framework, and no build step.
This wasn't minimalism for its own sake. A tool that sits next to your agents all day should be something you can read in ten minutes and trust.
Cubicle only ever makes GET requests. In Paperclip mode it forwards exactly three endpoints and refuses everything else:
const ALLOWED = [
/^\/api\/health$/,
/^\/api\/companies$/,
/^\/api\/companies\/[\w-]+\/(agents|issues)$/,
];
POST, PATCH and DELETE get a 405, any other path gets a 403. If you point it at an authenticated Paperclip, the API key is added on the server side only: it never reaches the browser, and the browser's own cookies are never forwarded upstream. There is deliberately no --token flag, because flags show up in the process list.
For Claude Code, Cubicle uses the official hooks. A tiny script rewrites ~/.cubicle/claude-code.json on every event:
| Claude Code event | In the office |
|---|---|
PreToolUse ,PostToolUse |
Types at the desk; bubble shows e.g. Edit checkout.php |
PermissionRequest |
Raises its hand |
Stop |
Goes to the lounge |
SessionEnd |
Leaves the office |
The hook always exits 0 and prints nothing, so it can never block a tool call or add tokens to the context. One thing I had to handle: Claude Code also sends a notification after a minute of idling ("waiting for your input"). Treating that as "needs you" made every idle session raise its hand, so those are ignored.
For Paperclip, the interesting signal was hiding in the issue data. When an agent asks the board a question, Paperclip records it in the issue's reviewAttention.paths with the board as the responder. Often the agent is already busy with another issue, so its own status says running. Cubicle keeps it typing, but its bubble flashes the waiting issue's ID every few seconds and the card links straight to it. The first time I turned this on, it found two questions I didn't know were waiting for me.
npx @caglarutkuguler/cubicle
npx @caglarutkuguler/cubicle install-hooks
npx @caglarutkuguler/cubicle --source claude-code
npx @caglarutkuguler/cubicle --source ./agents.json
Open http://127.0.0.1:3200. Add ?kiosk for a full-screen view on a TV or second monitor.
A few issues are open and labelled for first-time contributors: a new UI language, per-agent spend for Paperclip, an example adapter for Codex or Gemini CLI, and a replay mode to watch a whole day in 30 seconds. PRs welcome, as long as they keep the two rules: no dependencies, and read-only.
It's MIT licensed: github.com/caglarutkuguler/cubicle