# Puck daemon `gradio.Server` (Gradio 6 server mode) that doubles as the **HF Space backend** and the **local daemon**. It is dumb pipes by design: validate wire events against [`/schema/event.schema.json`](../schema/event.schema.json), queue them, serve the built frontend. All policy lives in the TS engine (`frontend/src/engine`) — keeping it that way is what makes the eventual Tauri/Rust swap ~150 lines instead of a rewrite. ## Run ```bash cd server uv run app.py # http://127.0.0.1:7777 ``` - Serves `frontend/dist` at `/` if built (`cd frontend && bun run build`). - In dev, run `bun dev` in `frontend/` instead — vite proxies `/api` to `:7777`. ## API | Route | Purpose | |---|---| | `POST /api/events` | Ingest a wire event. 202 on accept, **422 with details** on schema violation | | `GET /api/events/pending` | Drain the queue (each event delivered once; frontend polls every 3s) | | `GET /api/health` | Liveness | Try it: ```bash curl -X POST http://127.0.0.1:7777/api/events \ -H 'Content-Type: application/json' \ -d '{"source":"claude","type":"task_completed","title":"Refactor memory garden"}' ``` Puck flies over and comments within ~3s. ## Claude Code hook `claude-hooks/puck-event.sh` turns Claude Code lifecycle events into Puck events. Register in `~/.claude/settings.json`: ```json { "hooks": { "Stop": [ { "hooks": [{ "type": "command", "command": "/Users/vuln/code/puck/server/claude-hooks/puck-event.sh" }] } ], "Notification": [ { "hooks": [{ "type": "command", "command": "/Users/vuln/code/puck/server/claude-hooks/puck-event.sh" }] } ] } } ``` Fire-and-forget: if the daemon is down, the hook exits 0 in ≤2s and Claude Code never notices. ## Adding a new watcher Write anything that POSTs the wire shape to `/api/events` — language doesn't matter, the schema is the contract. Give the engine a scoring template for your `(source, type)` pair in `frontend/src/engine/wire.ts` (`TEMPLATES`), or accept the middling fallback scores.