| name | setup-spa |
| description | SPA (Spawn's Personal Agent) — Slack bot that pipes threads into Claude Code sessions |
| disable-model-invocation | true |
SPA — Spawn's Personal Agent
Slack bot that listens in #proj-spawn via Socket Mode. When @mentioned, it collects the full thread, pipes it into a claude -p session, and streams Claude Code's responses back to the Slack thread in real-time.
Subsequent thread replies in tracked threads auto-trigger new Claude Code runs.
Slack App Setup
Option A: Create from manifest (recommended)
- Go to https://api.slack.com/apps
- Click Create New App > From a manifest
- Select your workspace
- Paste the contents of
slack-manifest.yml from this directory
- Click Create
- Socket Mode: Settings > Basic Information > scroll to "App-Level Tokens" > Generate Token and Scopes > add
connections:write scope > copy the xapp-... token
- Install to Workspace: Features > OAuth & Permissions > Install > copy the
xoxb-... Bot User OAuth Token
- Invite the bot to the target channel:
/invite @spa
- Get the channel ID: Right-click channel name > View channel details > copy the ID (starts with
C)
Option B: Manual setup
- Go to https://api.slack.com/apps > Create New App > From scratch
- Name it
SPA, select the workspace
- Socket Mode: Settings > Socket Mode > Enable > generate app-level token with
connections:write scope > save xapp-...
- Event Subscriptions: Features > Event Subscriptions > Enable > subscribe to bot events:
app_mention, message.channels, message.groups
- OAuth Scopes: Features > OAuth & Permissions > Bot Token Scopes:
app_mentions:read, channels:history, channels:read, groups:history, groups:read, chat:write, reactions:write
- Install to Workspace > save
xoxb-... token
- Invite bot to channel, get channel ID
Env Vars
| Var | Description |
|---|
SLACK_BOT_TOKEN | Bot User OAuth Token (xoxb-...) |
SLACK_APP_TOKEN | App-Level Token for Socket Mode (xapp-...) |
SLACK_CHANNEL_ID | Channel ID to listen in (e.g. C0123456789) |
GITHUB_REPO | Target repo context (default: OpenRouterLabs/spawn) |
REPO_ROOT | Working directory for Claude Code (default: cwd) |
GitHub auth uses the gh CLI — run gh auth login before starting.
start-spa.sh Template
Create .claude/skills/setup-spa/start-spa.sh (gitignored):
#!/bin/bash
set -eo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
export SLACK_BOT_TOKEN="xoxb-YOUR-BOT-TOKEN"
export SLACK_APP_TOKEN="xapp-YOUR-APP-TOKEN"
export SLACK_CHANNEL_ID="C0000000000"
export GITHUB_REPO="OpenRouterLabs/spawn"
export REPO_ROOT="$(cd "${SCRIPT_DIR}/../../.." && pwd)"
exec bun run "${SCRIPT_DIR}/main.ts"
Install
bun install
Systemd Service
Create /etc/systemd/system/spawn-spa.service:
[Unit]
Description=SPA — Spawn's Personal Agent
After=network.target
[Service]
Type=simple
User=lab
Group=lab
WorkingDirectory=/home/lab/spawn/.claude/skills/setup-spa
ExecStart=/bin/bash /home/lab/spawn/.claude/skills/setup-spa/start-spa.sh
Restart=always
RestartSec=10
StandardOutput=journal
StandardError=journal
Environment="PATH=/home/lab/.local/bin:/home/lab/.bun/bin:/usr/local/bin:/usr/bin:/bin"
[Install]
WantedBy=multi-user.target
Then:
sudo systemctl daemon-reload
sudo systemctl enable --now spawn-spa
Verify
sudo systemctl status spawn-spa
journalctl -u spawn-spa -f
State
Thread-to-session mappings are persisted at ~/.config/spawn/slack-issues.json.
Troubleshooting
| Symptom | Fix |
|---|
ERROR: SLACK_BOT_TOKEN env var is required | Set all required env vars in start-spa.sh |
| Bot doesn't respond to @mentions | Verify bot is invited to the channel; check SLACK_CHANNEL_ID |
| Claude errors with permission denied | Ensure --dangerously-skip-permissions is working, or run in a sandbox |
| Responses truncated | Slack has ~4000 char limit per message; long responses show the tail |
| Hourglass reaction | A Claude run is already active for that thread; wait for it to finish |