Add Pebble watch webhook + skill framework#69
Merged
Conversation
Specifies a FastAPI webhook endpoint inside the Clayde container that receives speech-to-text messages from a Pebble watch app and dispatches them to the Claude CLI with a catalog of markdown-defined skills. Includes Traefik reverse proxy with Let's Encrypt for TLS. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
…ule, private docker network - Drop CLAYDE_SKILL_DIRS env var. Hardcode /skills/ as the in-container scan root; recursive discovery means subdirectory layout is free. - Drop startup scan; only scan per request (cheap, allows hot-add). - Add explicit "choose at most one skill" rule to system prompt. - Add two-network compose layout (web + internal). Traefik publishes ports on web, clayde joins only internal. No host ingress to clayde except via Traefik. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
14-task TDD plan with bite-sized steps. Adds FastAPI webhook (/webhook/pebble + /health), bearer auth, in-memory asyncio queue, single serial worker, fresh-session Claude CLI invocation, recursive markdown-skill discovery under /skills/, OTel enqueue+process spans cross-referenced by pebble.job_id, and a Traefik+private-network docker-compose setup. Existing GitHub poll loop runs in a thread to keep the asyncio event loop responsive. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…tter Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Wrap the communicate() await in a try/finally (via BaseException) so that proc.kill() + proc.wait() run on both TimeoutError->InvocationTimeoutError and external CancelledError, preventing leaked subprocesses. Add three tests: is_error output triggers UsageLimitError, auth failure triggers RuntimeError, and external cancellation kills the proc. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…nabled Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ounts Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
…etup Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds a FastAPI webhook to the Clayde container that receives speech-to-text messages from a Pebble watch app, plus a markdown-skill framework for dispatching them to the Claude CLI.
POST /webhook/pebblewithAuthorization: Bearer <token>accepts{text, timestamp}, enqueues into an in-memoryasyncio.Queue, returns 200./healthfor liveness. 401/422/503 on bad token / payload / queue full.name/descriptionfrontmatter mounted under the fixed in-container path/skills/. Recursive discovery on each request; deterministic alphabetical order; first-wins on duplicatename.clayde.pebble.processspan cross-references the enqueue span viapebble.job_id.asyncio.to_thread) + the worker. Behavior unchanged whenCLAYDE_PEBBLE_ENABLED=false.webnetwork;claydejoins only a privateinternalnetwork — no host ingress except via Traefik.CLAUDE.md,README.md,config.env.templateupdated with new env vars and operator instructions.Spec:
docs/superpowers/specs/2026-05-06-pebble-webhook-design.mdPlan:
docs/superpowers/plans/2026-05-07-pebble-webhook-framework.mdTest plan
uv run pytest -q— 335/335 passing (42 new tests added)CLAYDE_PEBBLE_ENABLED=true, verify/healthreturns 200 over HTTPS via Traefikpebble.job_idintraces.jsonl/skills/...dir; trigger a matching request; verify the skill's side effect (e.g. file written, calendar event, etc.)"No matching skill"andpebble.skill=noneon the process span🤖 Generated with Claude Code