1 pointby Drew-Aetherwave4 hours ago1 comment
  • Drew-Aetherwave4 hours ago
    Claude Code is pull-based — it only acts when tools fire or the user sends CLI input. Built this to enable real-time two-way communication between a phone and a running autonomous session.

    *Architecture:*

    Inbound: Discord → WebSocket (discord.js v14) → ~/.claude/discord-inbox.jsonl → PostToolUse hook → Claude as hook feedback

    Outbound: Stop/Error hooks → Discord webhook → per-session named thread → phone push notification

    *Why local file over API polling:*

    First version polled the Discord API every 2 minutes via a PostToolUse hook. Worked, but latency was bounded by poll interval and Discord API rate limits. Replacing it with a persistent WebSocket bridge writing to a local file brought latency down to microseconds — bounded only by tool call frequency. JSONL with atomic truncation handles the race condition between bridge writes and hook reads.

    *Hook wiring:*

    - PreToolUse: auto-starts bridge on first tool call (silent no-op if already running) - PostToolUse: reads and clears local inbox - Stop/Notification: fires outbound webhook with STATUS block

    One thing worth noting: Discord's webhook execute endpoint returns 204 empty by default. Needed ?wait=true to get the response body containing channel_id for thread routing.

    *Honest limitation:*

    When Claude is idle at a decision prompt, it's not firing tools — PostToolUse doesn't run, inbox doesn't get read. Works for redirecting mid-active-run, not for answering stopped prompts.

    Tested overnight on 27K LOC across two parallel sessions