WoW-inspired raid frames on a 64x64 LED panel for monitoring your coding agents.
lfg turns a $25 iDotMatrix LED panel into a real-time raid frame display for your AI coding agents. Each agent gets a sprite, a player ID, and a status icon — just like watching your party in a 10-man raid.
States:
- Working — agent is actively using tools (sword/potion/compass icon)
- Idle — agent has stopped (zzz with marching border)
- Requesting — agent needs approval, i.e. standing in fire (fire icon with marching border)
The most important thing lfg does is make idle and approval states impossible to miss. When an agent needs you, you see the fire icon in your peripheral vision without switching windows.
Claude Code / Cursor hooks
↓
boopifier (event normalizer)
↓
HTTP webhook POST
↓
lfg (Rust server)
↓
BLE → iDotMatrix 64x64 LED
lfg receives webhook events (PreToolUse, PostToolUse, PermissionRequest, Stop, etc.) and manages an agent state machine. The render pipeline:
- Every 250ms, the display state is hashed and compared to the last sent frame
- On change, a 2-second debounce timer starts — rapid tool calls don't spam the display
- After 2s of stability, lfg renders a 6-frame animated GIF from the current state
- The GIF is split into 4KB packets and sent over BLE to the iDotMatrix panel
- If any agent is in the Requesting state, the animation runs faster (0.5s/frame vs 1.25s/frame) so the fire icon pulses urgently
In --no-ble mode, lfg runs as an HTTP-only server — useful for testing the webhook pipeline and state machine without hardware. Use POST /debug/gif to export the current display as a GIF file.
- 10 agent slots across 5 columns, 2 rows — grouped by host (IDE/terminal)
- 11 sprite themes — Slimes, Ghosts, Space Invaders, Pac-Men, Mushrooms, Jumpman, Creepers, Frogger, Q*bert, Kirby, Zelda Hearts
- 5 ability icons — Star (subagent), Sword (write/edit/bash), Potion (think), Chest (read), Compass (search/web)
- BLE auto-discovery and reconnection with heartbeat keepalive
- SQLite persistence for cumulative stats across restarts
- Stats bar — agent-minutes, tool calls, unique agents
- Multi-IDE support — Claude Code and Cursor via boopifier
- HTTP API for status, reset, theme switching, and debug GIF export
You need an iDotMatrix 64x64 LED panel (~$25 on AliExpress). Any IDM-* BLE device should work.
# Homebrew (macOS)
brew tap terraboops/tap
brew install lfgOr build from source:
git clone https://github.com/terraboops/lfg.git
cd lfg
cargo build --release# Auto-discovers IDM-* BLE device
lfg
# Without BLE (HTTP-only, for testing)
lfg --no-blelfg receives events via HTTP webhooks. There are two ways to connect your IDE:
Copy the hook script and make it executable:
cp examples/hooks/lfg-hook.sh ~/.local/bin/lfg-hook.sh
chmod +x ~/.local/bin/lfg-hook.shClaude Code — add to ~/.claude/settings.json:
{
"hooks": {
"PreToolUse": [{ "type": "command", "command": "lfg-hook.sh" }],
"PostToolUse": [{ "type": "command", "command": "lfg-hook.sh" }],
"Stop": [{ "type": "command", "command": "lfg-hook.sh" }],
"SubagentStop": [{ "type": "command", "command": "lfg-hook.sh" }]
}
}Cursor — add to ~/.cursor/hooks.json:
{
"version": 1,
"hooks": {
"preToolUse": [{ "command": "lfg-hook.sh" }],
"postToolUse": [{ "command": "lfg-hook.sh" }],
"beforeShellExecution": [{ "command": "lfg-hook.sh" }],
"afterShellExecution": [{ "command": "lfg-hook.sh" }],
"afterFileEdit": [{ "command": "lfg-hook.sh" }],
"stop": [{ "command": "lfg-hook.sh" }],
"sessionStart": [{ "command": "lfg-hook.sh" }],
"sessionEnd": [{ "command": "lfg-hook.sh" }]
}
}Environment variables: LFG_URL (default http://localhost:5555/webhook), LFG_HOST (default claude). Set LFG_HOST=cursor for Cursor hooks.
Install boopifier, then copy the example configs:
mkdir -p ~/.config/lfg
cp examples/hooks/boopifier-claude.json ~/.config/lfg/
cp examples/hooks/boopifier-cursor.json ~/.config/lfg/Then point your IDE hooks at boopifier — see examples/hooks/claude-code-boopifier.json and examples/hooks/cursor-boopifier.json for the hook configs.
Boopifier adds features like sound alerts on Stop events, per-host sprite themes, and multi-destination routing.
curl -X POST http://localhost:5555/webhook \
-H 'Content-Type: application/json' \
-d '{"text": "PreToolUse|session-id-here|Bash"}' \
-G -d 'host=claude'See examples/hooks/ for all example configs.
curl localhost:5555/status # Current state
curl localhost:5555/hosts # Host/agent mapping
curl -X POST localhost:5555/reset # Clear everything
curl localhost:5555/theme # List themes
curl -X POST localhost:5555/theme/2 # Set theme (Space Invaders)Getting agent state right is the hard part. Hooks fire out of order and overlap — PermissionRequest and PreToolUse arrive ~100μs apart for the same tool, and PostToolUse fires after every tool call, not just approvals.
The key design decisions:
- Requesting is sticky — only cleared by
PostToolUse(approval granted + tool ran) orStop/SessionEnd - PreToolUse won't override Requesting — prevents the fire icon from flickering away while blocked on approval
- PostToolUse doesn't transition to Idle — tools fire rapidly in sequence; only
Stopmeans truly idle - Idle and Requesting always win from
Stop/SessionEndandPermissionRequestrespectively
src/
├── main.rs # CLI args, DB init, server startup
├── http.rs # Axum routes (webhook, status, themes)
├── event.rs # State machine, stale agent cleanup, stats
├── gateway.rs # Host/column allocation, agent join logic
├── state.rs # Shared state types (Agent, Column, Host, Stats)
├── render.rs # Canvas, sprite/icon drawing, GIF encoding
├── sprites.rs # 8x8 pixel art themes, icons, font, layout
├── ble.rs # BLE discovery, connection, GIF packet protocol
└── db.rs # SQLite persistence for cumulative stats
MIT
