Spaces:
Running
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, 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
cd server
uv run app.py # http://127.0.0.1:7777
- Serves
frontend/distat/if built (cd frontend && bun run build). - In dev, run
bun devinfrontend/instead — vite proxies/apito: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:
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:
{
"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.