# risv3-relay Relay daemon between Claude Code instances and a Claude.ai chat-equivalent session via the Anthropic API. ## What it does When CC produces output that would normally be pasted to a Claude.ai chat for review or a decision, the daemon does the relay automatically: 1. CC drops a JSON envelope into `queue/`. 2. Daemon picks oldest-first, appends to a running conversation history, calls the Anthropic API with prompt caching on the system prompt. 3. If the response contains `[NEEDS-JC]` in its first 200 characters, the daemon pauses and notifies via [ntfy.sh](https://ntfy.sh). 4. Otherwise, the response is written to `dispatch//input.txt` for the originating CC session to consume. JC can override at any time by writing to `state/jc_input.txt`. ## Install ```sh git clone git@localhost:AC/risv3-relay.git cd risv3-relay python3.14 -m venv .venv .venv/bin/pip install -e '.[dev]' cp .env.example .env # edit .env — add ANTHROPIC_API_KEY ``` ## Run ```sh .venv/bin/python -m relay run ``` On first boot the daemon generates a random ntfy topic, persists it back to `.env`, and prints the subscription URL: ``` ntfy topic: https://ntfy.sh/ Subscribe on phone/laptop to receive needs_jc + error alerts. ``` Subscribe at that URL on phone/laptop to get pings on `needs_jc` and errors. The topic is functionally a password — anyone subscribed receives the messages, so don't share it. ## CC-side protocol A CC session integrates with the daemon by **dropping queue envelopes** and **polling its dispatch input**. ### Sending CC output to chat-Claude Drop a JSON file into `queue/`: ```json { "session_id": "session-1", "timestamp": "2026-05-02T15:30:00Z", "content": "Sub-PR A is open at #438. Tests pass. Awaiting review." } ``` File name doesn't matter (use `--.json` for sortability). The daemon picks oldest-first by file mtime and processes one entry per loop tick. ### Receiving chat-Claude responses Poll your session's dispatch directory: ```sh while true; do if [ -s dispatch/session-1/input.txt ]; then cat dispatch/session-1/input.txt rm dispatch/session-1/input.txt fi sleep 1 done ``` Deletion is the acknowledgement. The daemon will not write a new `input.txt` until the previous one is consumed (deleted). ## JC operations Status: ```sh .venv/bin/python -m relay status ``` ntfy URL: ```sh .venv/bin/python -m relay topic ``` Override at any time — the daemon picks up `state/jc_input.txt` on the next tick (≤ 1 second): | Format | Effect | |---|---| | `@session-1: do X` | Direct dispatch to `session-1`. No API call. Body after the prefix becomes the input. Clears `needs_jc` if set. | | `(any text without @prefix)` | Treated as the next chat-side turn. The daemon sends it through the API, dispatches the response to the originating session (or `sessions[0]` if not in queue context). Clears `needs_jc` if set. | ## Project layout - `relay/` — Python package (config, state, conversation, anthropic_client, queue, dispatch, ntfy, daemon, __main__) - `tests/` — pytest tests; `pytest -m real_api` opts into live-API smoke - `queue/`, `dispatch/`, `state/`, `logs/` — runtime directories created on first run; gitignored - `config.yaml` — registered CC sessions, system prompt, summarization prompt; auto-seeded on first run - `.env` — secrets and per-host overrides; gitignored ## Trust model - The daemon is **pure transport** between CC and chat-Claude. It does not make merge decisions, override CC's existing rules, or run arbitrary commands. - The Anthropic API call is the only outbound integration besides ntfy. The system prompt and summarization prompt live in `config.yaml`; edit them to shape chat-Claude's behavior. - JC's `jc_input.txt` is authoritative — anything written there is treated as the next chat-side turn (or a direct dispatch with the `@session-N:` prefix). ## Status of the project First-PR scope (this repo's `main` after merge): daemon skeleton, queue + dispatch loop, single-CC-session integration, basic logging, ntfy notifications, conversation history with summarization, prompt caching on the system prompt, per-call cost estimation logged. Follow-up PRs will add: status web UI (Flask endpoint), multi-session config and per-session prompts, exponential backoff on transient API errors, systemd unit, cost-tracking dashboard. ## Development Run the unit suite: ```sh .venv/bin/python -m pytest ``` Run the live-API smoke (cost ~$0.0001 against Haiku 4.5; needs a billed `ANTHROPIC_API_KEY`): ```sh .venv/bin/python -m pytest -m real_api ``` Lint / format: ```sh .venv/bin/ruff check relay/ tests/ .venv/bin/ruff format relay/ tests/ ```