A cooperative idle boss raid for your team. Each AI token your coding agents spend (Claude Code, Codex, etc.) becomes damage against the current boss — and chats on claude.ai / Claude Desktop can count too via an optional browser userscript. Watch hits land in real time on the battlefield, defeat bosses together, and celebrate kills in Slack.
- You sign in with Slack and grab a personal hook token.
- You paste the hook snippet into your agent config (Claude Code or Codex).
- Every time your agent finishes a turn, its token usage is posted to the server, broadcast over Reverb websockets, and rendered as a hit on the live battlefield.
- When the boss falls, the kill is announced in a Slack channel and a new boss spawns.
- Laravel 13 on PHP 8.4+
- Livewire 4 + Tailwind CSS 4 for the UI
- Phaser 3 for the battlefield rendering
- Laravel Reverb for websocket broadcasting
- Laravel Socialite for Slack OAuth
- PostgreSQL in deployed environments, SQLite for local dev
- Pest 4 for testing
composer install
npm install
cp .env.example .env
php artisan key:generate
touch database/database.sqlite
php artisan migrateFill in the env vars below, then start everything in one command:
composer run devcomposer run dev runs the Laravel server, queue worker, log tail, Reverb
(websockets), and Vite together.
| Var | Notes |
|---|---|
SLACK_CLIENT_ID |
From your Slack app's OAuth credentials. |
SLACK_CLIENT_SECRET |
Same Slack app. |
SLACK_REDIRECT_URI |
Typically ${APP_URL}/auth/slack/callback. |
SLACK_KILL_WEBHOOK_URL |
Optional. Incoming webhook for boss-kill announcements. |
GAME_BASE_HP |
Optional. Boss starting HP. Defaults to 1000000. |
GAME_IDLE_MINUTES |
Optional. Minutes of inactivity before a fighter is swept off the battlefield. Defaults to 30. |
- Open
/profilein a browser. - Click Sign in with Slack.
- Copy the displayed Claude Code hook config snippet.
- Paste it into
~/.claude/settings.json(merge with existing keys if any). - If you use Codex too, repeat with the Codex hook config snippet into
~/.codex/config.toml. - Open
/battlefieldand watch your hits register as you work.
Coding agents report exact token counts through hooks, but the claude.ai web app and Claude Desktop expose no usage data. The tracker userscript closes that gap with estimates:
- Install Tampermonkey (or Violentmonkey) in your browser.
- Open
/profileand click the userscript install link (served at/tracker.user.js) — your userscript manager will prompt you. - Open claude.ai and paste your hook token when the script asks (once; it's stored in the userscript manager).
The script estimates tokens from assistant reply length (~4 chars/token)
and posts them as provider=claude-ai damage through the same /api/events
pipeline as the hooks. Claude Desktop chats sync to your account, so they
are picked up by the script's periodic poll whenever claude.ai is open in
that browser. On first run it baselines your existing history without
dealing damage, so installing won't nuke the boss.
Caveats: counts are estimates (thinking tokens and system overhead are invisible), dedupe is per-browser, and the script reads claude.ai's undocumented internal endpoints, so it may need patching when those change.
| Path | Description |
|---|---|
/ |
Landing page. |
/battlefield |
Live battle view. Public. |
/history |
Defeated bosses. Public. |
/profile |
Your hook token, agent snippets, and tracker install link. Slack login required. |
/tracker.user.js |
The claude.ai tracker userscript. Public. |
php artisan test --compactBrowser tests use Pest 4's visit() driver and run headless by default.
Ships with a multi-stage Dockerfile (adapted from the serversideup/php
image) and three layered compose files.
docker compose -f docker-compose.yml -f docker-compose.dev.yml upBrings up php, reverb, and a pgsql container.
The staging stack runs php and reverb only — Postgres is expected to
run externally (reachable from the containers via host.docker.internal),
and a host-installed cloudflared handles public ingress.
cp .env.staging.example .env
# fill in APP_KEY, DB creds, Slack vars, Reverb keys, etc.
docker compose -f docker-compose.yml -f docker-compose.staging.yml up -d --buildIn the Cloudflare Zero Trust dashboard, add two public hostname routes on your tunnel:
app.<your-domain>→http://localhost:8000ws.<your-domain>→http://localhost:8080
Both ports are bound to 127.0.0.1 on the host, so the only inbound path
is via cloudflared.
MIT