Monorepo for Aesthetic.Computer aesthetic.computer
core slab
32 folders · 9 files

README.md

slab #

A desk for running many coding agents at once. Slab is a macOS menu bar app (plus a handful of hooks and scripts) that gives every Claude Code, Codex or Easel session on the machine a colour, a name, a stone, and a place on the wall. Story page: https://aesthetic.computer/slab.

What the menu bar does today:

  • Theme by status. Each Terminal window is re-skinned by the session inside it: one tint while the agent works, another when it is waiting on you, a third when the turn is complete. Profiles live in seed/.
  • Title by task. The window title becomes what the session is doing.
  • Prompt rocks. One 3D pixel stone per live prompt, parked top-right of its terminal. Shape = identity, spin = status, pet name in bubble letters, a seeded percussion voice on hover, the session summary on click. Easel rocks double as a scannable code for the piece being made. (menubar-swift/Sources/SlabMenubar/PromptSigilOverlay.swift)
  • The wall. ⌘⌥T tiles the agent terminals, ⌘⌥S scatters them, ⌘⌥ arrows walk focus spatially (and across Macs over Deskflow), ⌘⌥X focuses the rocks, ⌃⌥⌘A flips dark mode fleet-wide.
  • Status sounds. Three synthesized cues (complete / awaiting / disconnected) that share one voice and differ only in contour.
  • Game Mode. GeForce NOW calls Tailscale a VPN on sight of the tunnel interface, even with no exit node routing a single packet. One switch pauses the tailnet and pins AirDrop's awdl0 radio down (it channel-hops the Wi-Fi card mid-stream), and puts both back on the way out — optionally by itself, the moment GeForce NOW opens. (menubar-swift/Sources/SlabMenubar/GameMode.swift)
  • Fleet. Every Mac runs the same menu bar; they share a ledger of live sessions over the tailnet (host:name), frame photographs any desk and puppet acts on it. Rocks are never drawn for remote machines.

Slab is not packaged: no download, no installer script. Clone the monorepo, run ./install.sh --prompt-host for hooks + markers, then menubar-swift/install.sh to build and launch the bar. The rest of this README is mostly about the lid daemon, which is where Slab began.

The lid (where it started) #

A lid-closed ambient + reactive audio system for the MacBook, gated on Claude Code activity, with a completion chime that sleeps the Mac when the last thread finishes.

Close the lid while Claude has active work in flight (with sleep suppressed) and the laptop becomes a "sleeping slab": an ambient C-major-pentatonic bed loops through the speakers while a mic-listener picks up claps, snaps, shushes, kisses, hums, and close-up sings — responding in real time with short pentatonic pluck-arpeggios that mirror each sound's pitch contour. Close the lid with nothing active and it's silent. When Claude finishes a prompt, it beeps once per other in-flight prompt or subagent; when the last piece of work completes, the "all-done" chime plays — and if the lid is closed, the Mac sleeps right after.

Active work is tracked as marker files in two directories:

  • $SLAB_HOME/state/active-prompts/<session_id> — created by the UserPromptSubmit hook, removed by Stop.
  • $SLAB_HOME/state/active-subagents/<ts>-<pid>-<n> — created by the PreToolUse hook (filtered to the Task tool), removed (oldest-first) by SubagentStop.

The daemon treats the total file count as "active work". When the Stop hook runs with zero remaining markers and the lid is closed, ambient stops, the all-done chime plays, and the Mac sleeps.

What it does #

Event Sound
Lid close (disablesleep=1) display sleep. Ambient only if a prompt or subagent is in flight.
Prompt or subagent starts while lid closed ascending C-arp + ambient loop starts
Mic transient while ambient is playing (> 2 kHz) 4-note pentatonic pluck arp, contour mirrors input
Lid open while ambient playing descending ding + smooth descending arp stinger + ambient stops
Lid open with no ambient silent
Subagent finishes (Task tool) single ping
Claude Stop, other active work remaining N ascending beeps (C6 D6 E6 G6 A6 C7 D7 E7)
Claude Stop, all work done, lid open "all-done" chime
Claude Stop, all work done, lid closed TTS "i'm tired" with cosine fade-out tail → pmset sleepnow
User submits new prompt touches active-prompts marker, sets disablesleep=1

Install #

git clone git@github.com:whistlegraph/aesthetic-computer.git
cd aesthetic-computer/slab
./install.sh

The installer will:

  1. Symlink scripts into ~/.local/bin/
  2. Copy sound assets into ~/.local/share/slab/sounds/
  3. Build a Python venv at ~/.local/share/slab/venv/ with numpy, sounddevice, and rumps
  4. Install two launchd agents:
    • ~/Library/LaunchAgents/computer.slab.daemon.plist — the lid-ambient daemon
    • ~/Library/LaunchAgents/computer.slab.menubar.plist — a menu-bar status item + transparent full-screen HUD in the top-right corner of the screen
  5. Merge hooks into ~/.claude/settings.json (Stop, SubagentStop, PreToolUse(Task), UserPromptSubmit, SessionStart)
  6. Install a passwordless-sudo rule at /etc/sudoers.d/slab-pmset for pmset (prompts for password once)

Opt-outs: --no-hooks, --no-sudoers.

For a Mac that should participate in the prompt-rock/prox fleet without the ambient audio, lid daemon, or sudoers changes, use:

./install.sh --prompt-host

This installs the agent wrappers, Claude hooks, and marker state only. Install the Swift menu bar separately with menubar-swift/install.sh.

Two Slabs: the studio one and the shipped one #

install.sh builds the Slab that belongs to this studio. It symlinks scripts back into the checkout so an edit takes effect immediately, merges hooks that shell out to jq, and signs the bundle with a stable self-signed certificate — chosen deliberately, because ad-hoc signing changes the code hash every build and would revoke the Accessibility grant each time.

menubar-swift/release-solo.sh builds the other one: the Slab a stranger can download.

cd menubar-swift
./release-solo.sh                 # build → sign → notarize → staple → DMG
./release-solo.sh --no-notarize   # local smoke test; the result will NOT open elsewhere

It produces menubar-swift/dist/Slab-<version>.dmg, Developer ID-signed with the hardened runtime, notarized and stapled so it opens offline on a Mac that has never heard of us. Three things differ from the studio build:

  • Hooks live in the binary. The shell hooks need jq thirteen times and python3 three times, and macOS ships neither. slab-menubar hook <event> (see Sources/SlabMenubar/HookCLI.swift) reimplements the marker protocol with JSONSerialization and sysctl, so the only dependency left is the app itself. Both halves write the same markers; change one shape and the rocks stop reading.
  • It sets itself up. On first launch FirstRun.swift offers to merge those hooks into ~/.claude/settings.json and seed the Terminal profiles from a copy bundled in Contents/Resources/solo. It refuses outright when ~/.local/bin/claude-prompt-log.sh is a symlink, which is how it knows a repo install owns the machine and must not be touched.
  • It is signed by Developer ID. A Mac holding both builds will be asked to grant Accessibility once more, because the designated requirement is part of what TCC remembers. Delete the old bundle when you switch, or the permission prompt returns on every launch.

bin/verify-solo-bundle.sh gates the signing step. It checks the parts are present, that the binary references no path inside a home directory, that it links only system libraries, and that the hook subcommand really writes a marker. That check exists because Menu Band once shipped a DMG that ran on the machine that built it and crashed everywhere else.

Requires a Mac holding the Developer ID key (blueberry or neo) and the vault's apple/app-specific-password.env for the notary service.

To publish, follow the Menu Band pattern: upload the DMG to assets.aesthetic.computer/slab/, then record version, size and sha256 in a manifest the /slab page reads. Do not publish a build that has not been launched on a Mac that never ran Slab — the first-run dialog and the TCC prompts are the two things no check here can prove.

Usage #

claude-sleep awake     # disable all sleep (stay awake with lid closed)
claude-sleep auto      # restore normal sleep behavior
claude-sleep now       # sleep the Mac immediately
claude-sleep status    # show SleepDisabled state

Before using lid-closed ambient mode: claude-sleep awake. The first time the mic listener runs you'll get a macOS microphone permission prompt.

Uninstall #

./uninstall.sh           # unload agent, remove symlinks + hooks + sudoers
./uninstall.sh --purge   # also delete ~/.local/share/slab (sessions, logs, venv, sounds)

Layout #

slab/
├── bin/                                 # scripts (symlinked into ~/.local/bin/)
│   ├── lid-ambient.sh                   # launchd daemon, polls lid + active prompts
│   ├── lid-reactive.py                  # mic → pluck-arp synth + noise voice (Python)
│   ├── lid-ambient-synth.swift          # live AVAudioEngine ambient drone + capture (Swift)
│   ├── lid-return-generate.py           # generate lid-return.wav (smooth descending arp)
│   ├── slab-menubar.py                  # rumps menu bar status item (no Dock icon)
│   ├── claude-sleep                     # sleep-state toggle
│   ├── claude-stop.sh                   # Stop-hook entry (prompts + subagents aware)
│   ├── claude-prompt-log.sh             # UserPromptSubmit hook (touches active-prompts marker)
│   ├── claude-tool-pre.sh               # PreToolUse(Task) hook (touches active-subagents marker)
│   ├── claude-subagent-stop.sh          # SubagentStop hook (pops oldest subagent marker + ping)
│   ├── claude-ping-repeat.sh            # legacy 30s pings (no longer invoked)
│   ├── claude-sleep-schedule.sh         # legacy delayed auto-sleep (no longer invoked)
│   ├── slab-monitor.sh                  # resource sampler
│   └── slab-zone                        # location-zone manager
├── sounds/                              # WAV assets (lid chimes, pings, beeps)
├── launchd/
│   ├── computer.slab.daemon.plist.template
│   └── computer.slab.menubar.plist.template
├── sudoers.d/
│   └── slab-pmset.template
├── settings-fragment.json               # Claude Code hooks, merged on install
├── install.sh
└── uninstall.sh

Runtime state lives under ~/.local/share/slab/:

sessions/<stamp>-<zone>.wav    per-lid-close mix of Python listener output (noise + plucks)
sessions/ambient-<stamp>.wav   per-lid-close recording of the live Swift ambient drone
sessions/<stamp>-<zone>.jsonl  trigger events + location metadata
logs/lidalive.log              daemon transitions
logs/reactive.log              reactive listener triggers
logs/resources.jsonl           CPU/RSS samples every 15s
logs/claude-stop.log           Stop-hook activity
config/zones.json              geofenced zones + per-zone scale/dynamics
state/active-prompts/<id>      one file per Claude session with a prompt in flight
state/active-subagents/<id>    one file per in-flight Task-tool subagent
state/last-location.json       cached coords (if Location Services unreachable)
venv/                          Python venv for the reactive listener
sounds/                        installed sound assets (lid chimes, pings, beeps)

Zones (location-aware ambient) #

Each lid-close queries Core Location and picks the closest zone in ~/.local/share/slab/config/zones.json. The matching zone overrides the reactive listener's scale, pitch mapping (div_factor), and amp. Coords + zone name are written into the session JSONL and appended to the WAV filename.

brew install corelocationcli                 # one-time (install.sh can run it)
# grant Location Services permission once
slab-zone where                              # show current coords + zone
slab-zone add home 150                       # pin current spot as 'home', 150m radius
slab-zone add studio 60
slab-zone list                               # dump zones.json
slab-zone remove home

Available scales: major_pentatonic, minor_pentatonic, blues, dorian, phrygian, lydian, whole_tone, chromatic. Edit zones.json directly to tweak a zone's scale, div_factor, or arp_amp.

Tuning #

Most knobs live at the top of each script:

  • Ambient — bin/lid-ambient-synth.swift: scale, note-gap range, duration/fade ranges, detuning, drone frequency, AU effects (reverb preset, delay time/feedback). Recompile via swiftc -O -o ~/.local/bin/lid-ambient-synth bin/lid-ambient-synth.swift (install.sh does this automatically).
  • Reactive listener — bin/lid-reactive.py: HIGH_BAND, TRIGGER_RATIO, MIN_GAP, DIV_FACTOR, NOTE_DUR, PLUCK_TAIL, ARP_NOTES, ARP_AMP. Scales live in SCALE_INTERVALS.
  • Lid-poll interval — bin/lid-ambient.sh: POLL (default 0.5s).
  • Resource poll — bin/slab-monitor.sh: INTERVAL (default 15s).

Edits to files in slab/bin/ take effect immediately because the installed copies are symlinks back into the repo.

Requirements #

  • macOS (Apple Silicon tested). Intel should work but the display-sleep-on-lid-close path depends on pmset displaysleepnow.
  • Homebrew, Python 3.11+, jq, swiftc (Xcode Command Line Tools).
  • Microphone permission for the reactive listener (prompted on first run).

Notes #

  • pmset disablesleep 1 suppresses all sleep, including explicit pmset sleepnow — the sudoers rule lets the scheduler toggle it back off before suspending.
  • The reactive listener writes 44.1 kHz mono int16 WAVs of only its own output (the pluck arps). The looping ambient afplay and the chimes aren't captured — that would need a loopback audio device like BlackHole.
  • Session files accumulate forever by default. Purge periodically: rm ~/.local/share/slab/sessions/*.
  • Apple Silicon MacBooks have no accelerometer, gyroscope, or accessible CPU temp via public APIs — so shake/gesture and temperature-modulation paths aren't implemented here yet.