diff --git a/docs/AGENTS.default.md b/docs/AGENTS.default.md index a3e11db60..8890c5638 100644 --- a/docs/AGENTS.default.md +++ b/docs/AGENTS.default.md @@ -1,7 +1,13 @@ +--- +summary: "Default Clawdis agent instructions and tool roster for the personal assistant setup" +read_when: + - Starting a new Clawdis agent session + - Enabling or auditing default tools +--- # AGENTS.md — Clawdis Personal Assistant (default) ## What Clawdis Does -- Runs WhatsApp relay + Pi/Tau coding agent so the assistant can read/write chats, fetch context, and run tools via the host Mac. +- Runs WhatsApp gateway + Pi/Tau coding agent so the assistant can read/write chats, fetch context, and run tools via the host Mac. - macOS app manages permissions (screen recording, notifications, microphone) and exposes a CLI helper `clawdis-mac` for scripts. - Sessions are per-sender; heartbeats keep background tasks alive. diff --git a/docs/RELEASING.md b/docs/RELEASING.md index de8a3efa4..33921c7e4 100644 --- a/docs/RELEASING.md +++ b/docs/RELEASING.md @@ -1,3 +1,10 @@ +--- +summary: "Step-by-step npm release checklist for the Clawdis CLI" +read_when: + - Cutting a new npm release + - Verifying metadata before publishing +--- + # Release Checklist (npm) Use `pnpm` (Node 22+) from the repo root. Keep the working tree clean before tagging/publishing. @@ -20,7 +27,7 @@ Use `pnpm` (Node 22+) from the repo root. Keep the working tree clean before tag - [ ] `pnpm lint` - [ ] `pnpm test` (or `pnpm test:coverage` if you need coverage output) - [ ] `pnpm run build` (last sanity check after tests) -- [ ] (Optional) Spot-check the web relay if your changes affect send/receive paths. +- [ ] (Optional) Spot-check the web gateway if your changes affect send/receive paths. 5) **Publish** - [ ] Confirm git status is clean; commit and push as needed. diff --git a/docs/agent-send.md b/docs/agent-send.md index 27b9c84ff..8a9f0aa04 100644 --- a/docs/agent-send.md +++ b/docs/agent-send.md @@ -1,10 +1,15 @@ +--- +summary: "Design notes for a direct `clawdis agent` CLI subcommand without WhatsApp delivery" +read_when: + - Adding or modifying the agent CLI entrypoint +--- # Plan: `clawdis agent` (direct-to-agent invocation) Goal: Add a CLI subcommand that talks directly to the configured agent command (no WhatsApp send), while reusing the same session handling and config clawdis already uses for auto-replies. ## Why - Sometimes we want to poke the agent directly (same prompt templates/sessions) without sending a WhatsApp message. -- Current flows (`send`, relay, directives) always route through WhatsApp or add wrapping text; we need a clean “talk to agent now” tool. +- Current flows (`send`, gateway, directives) always route through WhatsApp or add wrapping text; we need a clean “talk to agent now” tool. ## Behavior - Command: `clawdis agent` diff --git a/docs/agents.md b/docs/agents.md index 5b462f8f2..e0f4baf36 100644 --- a/docs/agents.md +++ b/docs/agents.md @@ -1,3 +1,8 @@ +--- +summary: "Current agent integration: Pi/Tau as the sole coding agent with config examples" +read_when: + - Changing agent invocation or defaults +--- # Agent Integration 🤖 CLAWDIS now ships with a single coding agent: Pi (the Tau CLI). Legacy Claude/Codex/Gemini/Opencode paths have been removed. diff --git a/docs/architecture.md b/docs/architecture.md index c6aaedf54..47dbe74cb 100644 --- a/docs/architecture.md +++ b/docs/architecture.md @@ -1,3 +1,8 @@ +--- +summary: "Target WebSocket gateway architecture, components, and client flows" +read_when: + - Working on gateway protocol, clients, or transports +--- # Gateway Architecture (target state) Last updated: 2025-12-09 diff --git a/docs/audio.md b/docs/audio.md index a0c9a059e..6322ee2a0 100644 --- a/docs/audio.md +++ b/docs/audio.md @@ -1,3 +1,8 @@ +--- +summary: "How inbound audio/voice notes are downloaded, transcribed, and injected into replies" +read_when: + - Changing audio transcription or media handling +--- # Audio / Voice Notes — 2025-12-05 ## What works diff --git a/docs/clawd.md b/docs/clawd.md index e4b1a93f6..d12a8a06b 100644 --- a/docs/clawd.md +++ b/docs/clawd.md @@ -1,3 +1,9 @@ +--- +summary: "End-to-end guide for running Clawdis as a personal assistant with safety cautions" +read_when: + - Onboarding a new assistant instance + - Reviewing safety/permission implications +--- # Building Your Own AI Personal Assistant with clawdis > **TL;DR:** CLAWDIS (Pi/Tau only) lets you run a proactive assistant over WhatsApp. It can check in on you, remember context across conversations, run commands on your Mac, and even wake you up with music. This doc was originally written for Claude Code; where you see `claude ...`, use `pi --mode rpc ...` instead. A Pi-specific rewrite is coming soon. @@ -205,7 +211,7 @@ clawdis heartbeat --provider web --to +1234567890 --verbose ``` ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │ WhatsApp │────▶│ clawdis │────▶│ Claude │────▶│ Your Mac │ -│ (phone) │◀────│ relay │◀────│ CLI │◀────│ (commands) │ +│ (phone) │◀────│ gateway │◀────│ CLI │◀────│ (commands) │ └─────────────┘ └─────────────┘ └─────────────┘ └─────────────┘ ``` @@ -234,17 +240,17 @@ Inbound images/audio/video are downloaded and available as `{{MediaPath}}`. Voic ### Sending Media Include `MEDIA:/path/to/file.png` in Claude's output to attach images. clawdis handles resizing and format conversion automatically. -## Starting the Relay +## Starting the Gateway ```sh # Foreground (see all logs) -clawdis relay --provider web --verbose +clawdis gateway --provider web --verbose # With immediate heartbeat on startup -clawdis relay --heartbeat-now +clawdis gateway --heartbeat-now ``` -For backgrounding, run the relay under your preferred supervisor (e.g., launchd/systemd) and point it at the same `clawdis relay --provider web --verbose` command. +For backgrounding, run the gateway under your preferred supervisor (e.g., launchd/systemd) and point it at the same `clawdis gateway --provider web --verbose` command. ## Tips for a Great Personal Assistant diff --git a/docs/clawdis-mac.md b/docs/clawdis-mac.md index 9d15fa953..f992dfdd6 100644 --- a/docs/clawdis-mac.md +++ b/docs/clawdis-mac.md @@ -1,3 +1,9 @@ +--- +summary: "Spec for the Clawdis macOS companion menu bar app and XPC broker" +read_when: + - Implementing macOS app features + - Touching XPC/CLI bridging +--- # Clawdis macOS Companion (menu bar + XPC broker) Author: steipete · Status: draft spec · Date: 2025-12-05 diff --git a/docs/configuration.md b/docs/configuration.md index ef586bbe7..977a87d86 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -1,3 +1,8 @@ +--- +summary: "All configuration options for ~/.clawdis/clawdis.json with examples" +read_when: + - Adding or modifying config fields +--- # Configuration 🔧 CLAWDIS uses a JSON configuration file at `~/.clawdis/clawdis.json`. @@ -147,7 +152,7 @@ export CLAWDIS_LOG_LEVEL=debug export CLAWDIS_CONFIG_PATH=~/.clawdis/clawdis.json ``` -## Migrating from Warelay +## Migrating from Clawdis If you're upgrading from the old `clawdis` name: diff --git a/docs/control-api.md b/docs/control-api.md index a56414e27..6458852f0 100644 --- a/docs/control-api.md +++ b/docs/control-api.md @@ -1,3 +1,8 @@ +--- +summary: "Deprecated newline-delimited control channel API (pre-gateway)" +read_when: + - Maintaining legacy control channel support +--- # Control channel API (newline-delimited JSON) **Deprecated:** superseded by the WebSocket Gateway protocol (`clawdis gateway`, see `docs/architecture.md` and `docs/gateway.md`). Use only for legacy builds predating the Gateway rollout. @@ -8,13 +13,13 @@ Endpoint: `127.0.0.1:18789` (TCP, localhost only). Clients reach it via SSH port Each line is a JSON object. Two shapes exist: - **Request**: `{ "type": "request", "id": "", "method": "health" | "status" | "last-heartbeat" | "set-heartbeats" | "ping", "params"?: { ... } }` - **Response**: `{ "type": "response", "id": "", "ok": true, "payload"?: { ... } }` or `{ "type": "response", "id": "", "ok": false, "error": "message" }` -- **Event**: `{ "type": "event", "event": "heartbeat" | "relay-status" | "log", "payload": { ... } }` +- **Event**: `{ "type": "event", "event": "heartbeat" | "gateway-status" | "log", "payload": { ... } }` ## Methods - `ping`: sanity check. Payload: `{ pong: true, ts }`. -- `health`: returns the relay health snapshot (same shape as `clawdis health --json`). +- `health`: returns the gateway health snapshot (same shape as `clawdis health --json`). - `status`: shorter summary (linked/authAge/heartbeatSeconds, session counts). -- `last-heartbeat`: returns the most recent heartbeat event the relay has seen. +- `last-heartbeat`: returns the most recent heartbeat event the gateway has seen. - `set-heartbeats { enabled: boolean }`: toggle heartbeat scheduling. ## Events @@ -30,7 +35,7 @@ Each line is a JSON object. Two shapes exist: "reason": "" // only on failed/skipped } ``` -- `relay-status` payload: `{ "state": "starting" | "running" | "restarting" | "failed" | "stopped", "pid"?: number, "reason"?: string }` +- `gateway-status` payload: `{ "state": "starting" | "running" | "restarting" | "failed" | "stopped", "pid"?: number, "reason"?: string }` - `log` payload: arbitrary log line; optional, can be disabled. ## Suggested client flow @@ -40,4 +45,4 @@ Each line is a JSON object. Two shapes exist: 4) For user toggles, send `set-heartbeats` and await response. ## Backward compatibility -- If the control port is unavailable (older relay), the client may fall back to the legacy CLI path, but the intended path is to rely solely on this API. +- If the control port is unavailable (older gateway), the client may fall back to the legacy CLI path, but the intended path is to rely solely on this API. diff --git a/docs/gateway.md b/docs/gateway.md index b28545c66..65c5e63b3 100644 --- a/docs/gateway.md +++ b/docs/gateway.md @@ -1,10 +1,15 @@ +--- +summary: "Runbook for the Gateway daemon, lifecycle, and operations" +read_when: + - Running or debugging the gateway process +--- # Gateway (daemon) runbook Last updated: 2025-12-09 ## What it is - The always-on process that owns the single Baileys/Telegram connection and the control/event plane. -- Replaces the legacy `relay` command. CLI entry point: `clawdis gateway`. +- Replaces the legacy `gateway` command. CLI entry point: `clawdis gateway`. - Runs until stopped; exits non-zero on fatal errors so the supervisor restarts it. ## How to run (local) @@ -128,5 +133,5 @@ Enable with `systemctl enable --now clawdis-gateway.service`. - `clawdis gw:call --params '{"k":"v"}'` — raw method invoker for debugging. ## Migration guidance -- Retire uses of `clawdis relay` and the legacy TCP control port. +- Retire uses of `clawdis gateway` and the legacy TCP control port. - Update clients to speak the WS protocol with mandatory hello and structured presence. diff --git a/docs/grammy.md b/docs/grammy.md index 80926b67b..c0a2f46d4 100644 --- a/docs/grammy.md +++ b/docs/grammy.md @@ -1,3 +1,8 @@ +--- +summary: "Telegram Bot API integration via grammY with setup notes" +read_when: + - Working on Telegram or grammY pathways +--- # grammY Integration (Telegram Bot API) Updated: 2025-12-07 @@ -8,8 +13,8 @@ Updated: 2025-12-07 - Extensible: proxy support via custom fetch, session middleware (optional), type-safe context. # What we shipped -- **Single client path:** fetch-based implementation removed; grammY is now the sole Telegram client (send + relay) with the grammY throttler enabled by default. -- **Relay:** `monitorTelegramProvider` builds a grammY `Bot`, wires mention/allowlist gating, media download via `getFile`/`download`, and delivers replies with `sendMessage/sendPhoto/sendVideo/sendAudio/sendDocument`. Supports long-poll or webhook via `webhookCallback`. +- **Single client path:** fetch-based implementation removed; grammY is now the sole Telegram client (send + gateway) with the grammY throttler enabled by default. +- **Gateway:** `monitorTelegramProvider` builds a grammY `Bot`, wires mention/allowlist gating, media download via `getFile`/`download`, and delivers replies with `sendMessage/sendPhoto/sendVideo/sendAudio/sendDocument`. Supports long-poll or webhook via `webhookCallback`. - **Proxy:** optional `telegram.proxy` uses `undici.ProxyAgent` through grammY’s `client.baseFetch`. - **Webhook helpers:** `webhook-set.ts` wraps `setWebhook/deleteWebhook`; `webhook.ts` hosts the callback with health + graceful shutdown and optional `--webhook-url` override. - **Sessions:** direct chats map to `main`; groups map to `group:`; replies route back to the same surface. diff --git a/docs/group-messages.md b/docs/group-messages.md index 8fcc8029d..28d56f662 100644 --- a/docs/group-messages.md +++ b/docs/group-messages.md @@ -1,3 +1,8 @@ +--- +summary: "Behavior and config for WhatsApp group message handling" +read_when: + - Changing group message rules or mentions +--- # Group messages (web provider) Goal: let Clawd sit in WhatsApp groups, wake up only when pinged, and keep that thread separate from the personal DM session. @@ -46,7 +51,7 @@ Notes: - Manual smoke: - Send an `@clawd` ping in the group and confirm a reply that references the sender name. - Send a second ping and verify the history block is included then cleared on the next turn. - - Check relay logs (run with `--verbose`) to see `inbound web message (batched)` entries showing `from: ` and the `[from: …]` suffix. + - Check gateway logs (run with `--verbose`) to see `inbound web message (batched)` entries showing `from: ` and the `[from: …]` suffix. ## Known considerations - Heartbeats are intentionally skipped for groups to avoid noisy broadcasts. diff --git a/docs/health.md b/docs/health.md index 55890f5f0..7676aab53 100644 --- a/docs/health.md +++ b/docs/health.md @@ -1,3 +1,8 @@ +--- +summary: "Health check steps for Baileys/WhatsApp connectivity" +read_when: + - Diagnosing web provider health +--- # Health Checks (CLI) Short guide to verify the WhatsApp Web / Baileys stack without guessing. @@ -12,12 +17,12 @@ Short guide to verify the WhatsApp Web / Baileys stack without guessing. ## Deep diagnostics - Creds on disk: `ls -l ~/.clawdis/credentials/creds.json` (mtime should be recent). - Session store: `ls -l ~/.clawdis/sessions.json` (path can be overridden in config). Count and recent recipients are surfaced via `status`. -- IPC socket (if relay is running): `ls -l ~/.clawdis/clawdis.sock`. +- IPC socket (if gateway is running): `ls -l ~/.clawdis/clawdis.sock`. - Relink flow: `pnpm clawdis logout && pnpm clawdis login --provider web --verbose` when status codes 409–515 or `loggedOut` appear in logs. ## When something fails - `logged out` or status 409–515 → relink with `clawdis logout` then `clawdis login --provider web`. -- Repeated reconnect exits → tune `web.reconnect` (flags: `--web-retries`, `--web-retry-initial`, `--web-retry-max`) and rerun relay. +- Repeated reconnect exits → tune `web.reconnect` (flags: `--web-retries`, `--web-retry-initial`, `--web-retry-max`) and rerun gateway. - No inbound messages → confirm linked phone is online and sender is allowed; use `pnpm clawdis heartbeat --all --verbose` to test each known recipient. ## Dedicated "health" command diff --git a/docs/heartbeat.md b/docs/heartbeat.md index 1719197dc..e0ee938ed 100644 --- a/docs/heartbeat.md +++ b/docs/heartbeat.md @@ -1,3 +1,8 @@ +--- +summary: "Plan for heartbeat polling messages and notification rules" +read_when: + - Adjusting heartbeat cadence or messaging +--- # Heartbeat polling plan (2025-11-26) Goal: add a simple heartbeat poll for command-based auto-replies (Pi/Tau) that only notifies users when something matters, using the `HEARTBEAT_OK` sentinel. The heartbeat body we send is `HEARTBEAT /think:high` so the model can easily spot it. @@ -12,10 +17,10 @@ Goal: add a simple heartbeat poll for command-based auto-replies (Pi/Tau) that o - New optional idle override for heartbeats: `inbound.reply.session.heartbeatIdleMinutes` (defaults to `idleMinutes`). Heartbeat skips do **not** update the session `updatedAt` so idle expiry still works. ## Poller behavior -- When relay runs with command-mode auto-reply, start a timer with the resolved heartbeat interval. +- When gateway runs with command-mode auto-reply, start a timer with the resolved heartbeat interval. - Each tick invokes the configured command with a short heartbeat body (e.g., “(heartbeat) summarize any important changes since last turn”) while reusing the active session args so Pi context stays warm. - Heartbeats never create a new session implicitly: if there’s no stored session for the target (fallback path), the heartbeat is skipped instead of starting a fresh Pi session. -- Abort timer on SIGINT/abort of the relay. +- Abort timer on SIGINT/abort of the gateway. ## Sentinel handling - Trim output. If the trimmed text equals `HEARTBEAT_OK` (case-sensitive) -> skip outbound message. @@ -39,6 +44,6 @@ Goal: add a simple heartbeat poll for command-based auto-replies (Pi/Tau) that o - Expose CLI triggers: - `clawdis heartbeat` (web provider, defaults to first `allowFrom`; optional `--to` override) - `--session-id ` forces resuming a specific session for that heartbeat - - `clawdis relay --heartbeat-now` to run the relay loop with an immediate heartbeat - - Relay supports `--heartbeat-now` to fire once at startup. + - `clawdis gateway --heartbeat-now` to run the gateway loop with an immediate heartbeat + - Gateway supports `--heartbeat-now` to fire once at startup. - When multiple sessions are active or `allowFrom` is only `"*"`, require `--to ` or `--all` for manual heartbeats to avoid ambiguous targets. diff --git a/docs/images.md b/docs/images.md index 0afd41a87..023ba3f82 100644 --- a/docs/images.md +++ b/docs/images.md @@ -1,6 +1,11 @@ +--- +summary: "Image and media handling rules for send, gateway, and agent replies" +read_when: + - Modifying media pipeline or attachments +--- # Image & Media Support — 2025-12-05 -CLAWDIS is now **web-only** (Baileys). This document captures the current media handling rules for send, relay, and agent replies. +CLAWDIS is now **web-only** (Baileys). This document captures the current media handling rules for send, gateway, and agent replies. ## Goals - Send media with optional captions via `clawdis send --media`. diff --git a/docs/index.md b/docs/index.md index 87990c7bd..d99adf901 100644 --- a/docs/index.md +++ b/docs/index.md @@ -1,3 +1,8 @@ +--- +summary: "Top-level overview of Clawdis, features, and purpose" +read_when: + - Introducing Clawdis to newcomers +--- # CLAWDIS 🦞 > *"EXFOLIATE! EXFOLIATE!"* — A space lobster, probably @@ -8,7 +13,7 @@ ## What is this? -CLAWDIS (née Warelay) bridges WhatsApp to AI coding agents like [Tau/Pi](https://github.com/badlogic/pi-mono). Send a message, get an AI response. It's like having a genius lobster on call 24/7. +CLAWDIS bridges WhatsApp to AI coding agents like [Tau/Pi](https://github.com/badlogic/pi-mono). Send a message, get an AI response. It's like having a genius lobster on call 24/7. ``` ┌─────────────┐ ┌──────────┐ ┌─────────────┐ @@ -66,9 +71,9 @@ clawdis status - [Security](./security.md) — Keeping your lobster safe - [Troubleshooting](./troubleshooting.md) — When the CLAWDIS misbehaves -## Why "Warelay"? +## Why "Clawdis"? -The original name was **Warelay** (WhatsApp + Relay). It worked. It was fine. +The original name was **Clawdis** (WhatsApp + Gateway). It worked. It was fine. But then Clawd happened, and suddenly we needed something with more... *personality*. diff --git a/docs/lore.md b/docs/lore.md index a3306d09e..ab408f27d 100644 --- a/docs/lore.md +++ b/docs/lore.md @@ -1,10 +1,15 @@ +--- +summary: "Backstory and lore of Clawdis for context and tone" +read_when: + - Writing docs or UX copy that reference lore +--- # The Lore of CLAWDIS 🦞📖 *A tale of lobsters, time machines, and too many tokens.* ## The Origin Story -In the beginning, there was **Warelay** — a sensible name for a WhatsApp relay. It did its job. It was fine. +In the beginning, there was **Clawdis** — a sensible name for a WhatsApp gateway. It did its job. It was fine. But then came **Clawd**. diff --git a/docs/mac/child-process.md b/docs/mac/child-process.md index 4f87f9d3c..2719fdc99 100644 --- a/docs/mac/child-process.md +++ b/docs/mac/child-process.md @@ -1,18 +1,23 @@ -# Clawdis relay as a child process of the macOS app +--- +summary: "Running the gateway as a child process of the macOS app and why" +read_when: + - Integrating the mac app with the gateway lifecycle +--- +# Clawdis gateway as a child process of the macOS app Date: 2025-12-06 · Status: draft · Owner: steipete ## Goal -Run the Node-based Clawdis/clawdis relay as a direct child of the LSUIElement app (instead of a launchd agent) while keeping all TCC-sensitive work inside the Swift app/XPC and wiring the existing “Clawdis Active” toggle to start/stop the child. +Run the Node-based Clawdis/clawdis gateway as a direct child of the LSUIElement app (instead of a launchd agent) while keeping all TCC-sensitive work inside the Swift app/XPC and wiring the existing “Clawdis Active” toggle to start/stop the child. ## When to prefer the child-process mode -- You want relay lifetime strictly coupled to the menu-bar app (dies when the app quits) and controlled by the “Clawdis Active” toggle without touching launchd. +- You want gateway lifetime strictly coupled to the menu-bar app (dies when the app quits) and controlled by the “Clawdis Active” toggle without touching launchd. - You’re okay giving up login persistence/auto-restart that launchd provides, or you’ll add your own backoff loop. - You want simpler log capture and supervision inside the app (no external plist or user-visible LaunchAgent). ## Tradeoffs vs. launchd - **Pros:** tighter coupling to UI state; simpler surface (no plist install/bootout); easier to stream stdout/stderr; fewer moving parts for beta users. -- **Cons:** no built-in KeepAlive/login auto-start; app crash kills relay; you must build your own restart/backoff; Activity Monitor will show both processes under the app; still need correct TCC handling (see below). +- **Cons:** no built-in KeepAlive/login auto-start; app crash kills gateway; you must build your own restart/backoff; Activity Monitor will show both processes under the app; still need correct TCC handling (see below). - **TCC:** behaviorally, child processes often inherit the parent app’s “responsible process” for TCC, but this is *not a contract*. Continue to route all protected actions through the Swift app/XPC so prompts stay tied to the signed app bundle. ## TCC guardrails (must keep) @@ -29,10 +34,10 @@ Run the Node-based Clawdis/clawdis relay as a direct child of the LSUIElement ap - Add a small `RelayProcessManager` (Swift) that owns: - `execution: Execution?` from `Swift Subprocess` to track the child. - `start(config)` called when “Clawdis Active” flips ON: - - binary: host Node running the bundled relay under `Clawdis.app/Contents/Resources/Relay/` + - binary: host Node running the bundled gateway under `Clawdis.app/Contents/Resources/Gateway/` - args: current clawdis entrypoint and flags - cwd/env: point to `~/.clawdis` as today; inject the expanded PATH so Homebrew Node resolves under launchd - - output: stream stdout/stderr to `/tmp/clawdis-relay.log` (cap buffer via Subprocess OutputLimits) + - output: stream stdout/stderr to `/tmp/clawdis-gateway.log` (cap buffer via Subprocess OutputLimits) - restart: optional linear/backoff restart if exit was non-zero and Active is still true - `stop()` called when Active flips OFF or app terminates: cancel the execution and `waitUntilExit`. - Wire SwiftUI toggle: @@ -41,19 +46,19 @@ Run the Node-based Clawdis/clawdis relay as a direct child of the LSUIElement ap - Keep the existing `LaunchdManager` around so we can switch back if needed; the toggle can choose between launchd or child mode with a flag if we want both. ## Packaging and signing -- Bundle the relay payload (dist + production node_modules) under `Contents/Resources/Relay/`; rely on host Node ≥22 instead of embedding a runtime. +- Bundle the gateway payload (dist + production node_modules) under `Contents/Resources/Gateway/`; rely on host Node ≥22 instead of embedding a runtime. - Codesign native addons and dylibs inside the bundle; no nested runtime binary to sign now. - Host runtime should not call TCC APIs directly; keep privileged work inside the app/XPC. ## Logging and observability -- Stream child stdout/stderr to `/tmp/clawdis-relay.log`; surface the last N lines in the Debug tab. +- Stream child stdout/stderr to `/tmp/clawdis-gateway.log`; surface the last N lines in the Debug tab. - Emit a user notification (via existing NotificationManager) on crash/exit while Active is true. - Add a lightweight heartbeat from Node → app (e.g., ping over stdout) so the app can show status in the menu. ## Failure/edge cases -- App crash/quit kills the relay. Decide if that is acceptable for the deployment tier; otherwise, stick with launchd for production and keep child-process for dev/experiments. -- If the relay exits repeatedly, back off (e.g., 1s/2s/5s/10s) and give up after N attempts with a menu warning. -- Respect the existing pause semantics: when paused, the XPC should return `ok=false, "clawdis paused"`; the relay should avoid calling privileged routes while paused. +- App crash/quit kills the gateway. Decide if that is acceptable for the deployment tier; otherwise, stick with launchd for production and keep child-process for dev/experiments. +- If the gateway exits repeatedly, back off (e.g., 1s/2s/5s/10s) and give up after N attempts with a menu warning. +- Respect the existing pause semantics: when paused, the XPC should return `ok=false, "clawdis paused"`; the gateway should avoid calling privileged routes while paused. ## Open questions / follow-ups - Do we need dual-mode (launchd for prod, child for dev)? If yes, gate via a setting or build flag. @@ -62,5 +67,5 @@ Run the Node-based Clawdis/clawdis relay as a direct child of the LSUIElement ap ## Decision snapshot (current recommendation) - Keep all TCC surfaces in the Swift app/XPC. -- Implement `RelayProcessManager` with Swift Subprocess to start/stop the relay on the “Clawdis Active” toggle. +- Implement `RelayProcessManager` with Swift Subprocess to start/stop the gateway on the “Clawdis Active” toggle. - Maintain the launchd path as a fallback for uptime/login persistence until child-mode proves stable. diff --git a/docs/mac/health.md b/docs/mac/health.md index 5f0b5a20f..9f96cf5ff 100644 --- a/docs/mac/health.md +++ b/docs/mac/health.md @@ -1,3 +1,8 @@ +--- +summary: "How the macOS app reports gateway/Baileys health states" +read_when: + - Debugging mac app health indicators +--- # Health Checks on macOS How to see whether the WhatsApp Web/Baileys bridge is healthy from the menu bar app. @@ -19,4 +24,4 @@ How to see whether the WhatsApp Web/Baileys bridge is healthy from the menu bar - Cache the last good snapshot and the last error separately to avoid flicker; show the timestamp of each. ## When in doubt -- You can still use the CLI flow in `docs/health.md` (status, heartbeat dry-run, relay heartbeat) and tail `/tmp/clawdis/clawdis.log` for `web-heartbeat` / `web-reconnect`. +- You can still use the CLI flow in `docs/health.md` (status, heartbeat dry-run, gateway heartbeat) and tail `/tmp/clawdis/clawdis.log` for `web-heartbeat` / `web-reconnect`. diff --git a/docs/mac/icon.md b/docs/mac/icon.md index 1d238a808..bdb51083c 100644 --- a/docs/mac/icon.md +++ b/docs/mac/icon.md @@ -1,3 +1,8 @@ +--- +summary: "Menu bar icon states and animations for Clawdis on macOS" +read_when: + - Changing menu bar icon behavior +--- # Menu Bar Icon States Author: steipete · Updated: 2025-12-06 · Scope: macOS app (`apps/macos`) diff --git a/docs/mac/logging.md b/docs/mac/logging.md index be48a36dc..93a398094 100644 --- a/docs/mac/logging.md +++ b/docs/mac/logging.md @@ -1,3 +1,8 @@ +--- +summary: "Enabling verbose macOS unified logging for Clawdis with privacy flags" +read_when: + - Capturing macOS logs or investigating private data logging +--- # Logging private data on macOS Unified logging redacts most payloads unless a subsystem opts into `privacy -off`. Per Peter's write-up on macOS [logging privacy shenanigans](https://steipete.me/posts/2025/logging-privacy-shenanigans) (2025) this is controlled by a plist in `/Library/Preferences/Logging/Subsystems/` keyed by the subsystem name. Only new log entries pick up the flag, so enable it before reproducing an issue. diff --git a/docs/mac/menu-bar.md b/docs/mac/menu-bar.md index c7ce265a5..b4a672629 100644 --- a/docs/mac/menu-bar.md +++ b/docs/mac/menu-bar.md @@ -1,3 +1,8 @@ +--- +summary: "Menu bar status logic and what is surfaced to users" +read_when: + - Tweaking mac menu UI or status logic +--- # Menu Bar Status Logic ## What is shown diff --git a/docs/mac/remote.md b/docs/mac/remote.md index b9864c0b6..86bb01d6f 100644 --- a/docs/mac/remote.md +++ b/docs/mac/remote.md @@ -1,8 +1,13 @@ +--- +summary: "macOS app flow for controlling a remote Clawdis gateway over SSH" +read_when: + - Setting up or debugging remote mac control +--- # Remote Clawdis (macOS ⇄ remote host) Updated: 2025-12-08 -This flow lets the macOS app act as a full remote control for a Clawdis relay running on another host (e.g. a Mac Studio). All features—health checks, permissions bootstrapping via the helper CLI, Voice Wake forwarding, and Web Chat—reuse the same remote SSH configuration from *Settings → General*. +This flow lets the macOS app act as a full remote control for a Clawdis gateway running on another host (e.g. a Mac Studio). All features—health checks, permissions bootstrapping via the helper CLI, Voice Wake forwarding, and Web Chat—reuse the same remote SSH configuration from *Settings → General*. ## Modes - **Local (this Mac)**: Everything runs on the laptop. No SSH involved. @@ -23,8 +28,8 @@ This flow lets the macOS app act as a full remote control for a Clawdis relay ru 4) Health checks and Web Chat will now run through this SSH tunnel automatically. ## Web Chat over SSH -- The relay hosts a loopback-only HTTP server (default 18788, see `webchat.port`). -- The mac app forwards `127.0.0.1:` over SSH (`ssh -L :127.0.0.1:`), then loads `/webchat/?session=` in-app. Sends go in-process on the relay (no CLI spawn/PATH issues). +- The gateway hosts a loopback-only HTTP server (default 18788, see `webchat.port`). +- The mac app forwards `127.0.0.1:` over SSH (`ssh -L :127.0.0.1:`), then loads `/webchat/?session=` in-app. Sends go in-process on the gateway (no CLI spawn/PATH issues). - Keep the feature enabled in *Settings → Config → Web chat*. Disable it to hide the menu entry entirely. ## Permissions @@ -38,14 +43,14 @@ This flow lets the macOS app act as a full remote control for a Clawdis relay ru ## Troubleshooting - **exit 127 / not found**: `clawdis` isn’t on PATH for non-login shells. Add it to `/etc/paths`, your shell rc, or symlink into `/usr/local/bin`/`/opt/homebrew/bin`. - **Health probe failed**: check SSH reachability, PATH, and that Baileys is logged in (`clawdis status --json`). -- **Web Chat stuck**: confirm the relay is running on the remote host and `webchat.enabled` is true; ensure the forwarded port matches *Settings → Config*. Since RPC is in-process, PATH is no longer a factor. +- **Web Chat stuck**: confirm the gateway is running on the remote host and `webchat.enabled` is true; ensure the forwarded port matches *Settings → Config*. Since RPC is in-process, PATH is no longer a factor. - **Voice Wake**: trigger phrases are forwarded automatically in remote mode; no separate forwarder is needed. ## Notification sounds Pick sounds per notification from scripts with the helper CLI, e.g.: ```bash -clawdis-mac notify --title "Ping" --body "Remote relay ready" --sound Glass +clawdis-mac notify --title "Ping" --body "Remote gateway ready" --sound Glass ``` There is no global “default sound” toggle in the app anymore; callers choose a sound (or none) per request. diff --git a/docs/mac/signing.md b/docs/mac/signing.md index cb109da30..02e68807e 100644 --- a/docs/mac/signing.md +++ b/docs/mac/signing.md @@ -1,3 +1,8 @@ +--- +summary: "Signing steps for macOS debug builds generated by packaging scripts" +read_when: + - Building or signing mac debug builds +--- # mac signing (debug builds) This app is usually built from `scripts/package-mac-app.sh`, which now: diff --git a/docs/mac/voice-overlay.md b/docs/mac/voice-overlay.md index dd96fb0f1..af79892eb 100644 --- a/docs/mac/voice-overlay.md +++ b/docs/mac/voice-overlay.md @@ -1,3 +1,8 @@ +--- +summary: "Voice overlay lifecycle when wake-word and push-to-talk overlap" +read_when: + - Adjusting voice overlay behavior +--- ## Voice Overlay Lifecycle (macOS) Audience: macOS app contributors. Goal: keep the voice overlay predictable when wake-word and push-to-talk overlap. diff --git a/docs/mac/voicewake.md b/docs/mac/voicewake.md index 279d85fbf..156cf685f 100644 --- a/docs/mac/voicewake.md +++ b/docs/mac/voicewake.md @@ -1,3 +1,8 @@ +--- +summary: "Voice wake and push-to-talk modes plus routing details in the mac app" +read_when: + - Working on voice wake or PTT pathways +--- # Voice Wake & Push-to-Talk Updated: 2025-12-08 · Owners: mac app diff --git a/docs/mac/webchat.md b/docs/mac/webchat.md index 9eeb833c4..c2a045aae 100644 --- a/docs/mac/webchat.md +++ b/docs/mac/webchat.md @@ -1,6 +1,11 @@ +--- +summary: "How the mac app embeds the gateway WebChat and how to debug it" +read_when: + - Debugging mac WebChat view or loopback port +--- # Web Chat (macOS app) -The macOS menu bar app opens the relay’s loopback web chat server in a WKWebView. It reuses the **primary Clawd session** (`main` by default, configurable via `inbound.reply.session.mainKey`). The server is started by the Node relay (default port 18788, see `webchat.port`). +The macOS menu bar app opens the gateway’s loopback web chat server in a WKWebView. It reuses the **primary Clawd session** (`main` by default, configurable via `inbound.reply.session.mainKey`). The server is started by the Node gateway (default port 18788, see `webchat.port`). ## Launch & debugging - Manual: Lobster menu → “Open Chat”. @@ -9,12 +14,12 @@ The macOS menu bar app opens the relay’s loopback web chat server in a WKWebVi - WK logs: navigation lifecycle, readyState, js location, and JS errors/unhandled rejections are mirrored to OSLog for easier diagnosis. ## How it’s wired -- Assets: `apps/macos/Sources/Clawdis/Resources/WebChat/` contains the `pi-web-ui` dist plus a local import map pointing at bundled vendor modules and a tiny `pi-ai` stub. Everything is served from the relay at `/webchat/*`. -- Bridge: none. The web UI calls `/webchat/rpc` directly; Swift no longer proxies messages. RPC is handled in-process inside the relay (no CLI spawn/PATH dependency). +- Assets: `apps/macos/Sources/Clawdis/Resources/WebChat/` contains the `pi-web-ui` dist plus a local import map pointing at bundled vendor modules and a tiny `pi-ai` stub. Everything is served from the gateway at `/webchat/*`. +- Bridge: none. The web UI calls `/webchat/rpc` directly; Swift no longer proxies messages. RPC is handled in-process inside the gateway (no CLI spawn/PATH dependency). - Session: always primary; multiple transports (WhatsApp/Telegram/Desktop) share the same session key so context is unified. ## Security / surface area -- Loopback server only; remote mode uses SSH port-forwarding from the relay host to the Mac. CSP is set to `default-src 'self' 'unsafe-inline' data: blob:`. +- Loopback server only; remote mode uses SSH port-forwarding from the gateway host to the Mac. CSP is set to `default-src 'self' 'unsafe-inline' data: blob:`. - Web Inspector is opt-in via right-click; otherwise WKWebView stays in the app sandbox. ## Known limitations diff --git a/docs/mac/xpc.md b/docs/mac/xpc.md index 334ff68af..02fe528f2 100644 --- a/docs/mac/xpc.md +++ b/docs/mac/xpc.md @@ -1,8 +1,13 @@ +--- +summary: "macOS XPC architecture for Clawdis app, CLI helper, and gateway bridge" +read_when: + - Editing XPC contracts or menu bar app IPC +--- # Clawdis macOS XPC architecture (Dec 2025) ## Goals - Single GUI app instance that owns all TCC-facing work (notifications, screen recording, mic, speech, AppleScript). -- A small surface for automation: the `clawdis-mac` CLI and the Node relay talk to the app via a local XPC channel. +- A small surface for automation: the `clawdis-mac` CLI and the Node gateway talk to the app via a local XPC channel. - Predictable permissions: always the same signed bundle ID, launched by launchd, so TCC grants stick. - Limit who can connect: only signed clients from our team (with a same-UID fallback for development). @@ -10,7 +15,7 @@ - The app registers a Mach service named `com.steipete.clawdis.xpc` via a user LaunchAgent at `~/Library/LaunchAgents/com.steipete.clawdis.plist`. - The launch agent runs `dist/Clawdis.app/Contents/MacOS/Clawdis` with `RunAtLoad=true`, `KeepAlive=false`, and a `MachServices` entry for the XPC name. - The app hosts the XPC listener (`NSXPCListener(machServiceName:)`) and exports `ClawdisXPCService`. -- The CLI (`clawdis-mac`) connects with `NSXPCConnection(machServiceName:)`; the Node relay shells out to the CLI. +- The CLI (`clawdis-mac`) connects with `NSXPCConnection(machServiceName:)`; the Node gateway shells out to the CLI. - Security: on incoming connections we read the audit token (or PID) and allow only: - Code-signed clients with team ID `Y5PE65HELJ`; or - Same-UID processes (fallback to avoid blocking local dev). diff --git a/docs/queue.md b/docs/queue.md index 8faf853a7..a3db8fed3 100644 --- a/docs/queue.md +++ b/docs/queue.md @@ -1,3 +1,8 @@ +--- +summary: "Command queue design that serializes auto-reply command execution" +read_when: + - Changing auto-reply execution or concurrency +--- # Command Queue (2025-11-25) We now serialize all command-based auto-replies (WhatsApp Web listener) through a tiny in-process queue to prevent multiple commands from running at once. diff --git a/docs/refactor/new-arch.md b/docs/refactor/new-arch.md index 266b0a85b..0979e3281 100644 --- a/docs/refactor/new-arch.md +++ b/docs/refactor/new-arch.md @@ -1,8 +1,13 @@ +--- +summary: "Implementation plan for the new gateway architecture and protocol" +read_when: + - Executing the gateway refactor +--- # New Gateway Architecture – Implementation Plan (detailed) Last updated: 2025-12-09 -Goal: replace legacy relay/stdin/TCP control with a single WebSocket Gateway, typed protocol, and first-frame snapshot. No backward compatibility. +Goal: replace legacy gateway/stdin/TCP control with a single WebSocket Gateway, typed protocol, and first-frame snapshot. No backward compatibility. --- diff --git a/docs/refactor/web-relay-troubleshooting.md b/docs/refactor/web-relay-troubleshooting.md index 3e29da8fa..9d2ce3783 100644 --- a/docs/refactor/web-relay-troubleshooting.md +++ b/docs/refactor/web-relay-troubleshooting.md @@ -1,14 +1,19 @@ -# Web Relay Troubleshooting (Nov 26, 2025) +--- +summary: "Troubleshooting guide for the web gateway/Baileys relay" +read_when: + - Diagnosing web relay socket or login issues +--- +# Web Gateway Troubleshooting (Nov 26, 2025) ## Symptoms & quick fixes -- **Stream Errored / Conflict / status 409–515:** WhatsApp closed the socket because another session is active or creds went stale. Run `clawdis logout` then `clawdis login --provider web` and restart the relay. +- **Stream Errored / Conflict / status 409–515:** WhatsApp closed the socket because another session is active or creds went stale. Run `clawdis logout` then `clawdis login --provider web` and restart the gateway. - **Logged out:** Console prints “session logged out”; re-link with `clawdis login --provider web`. - **Repeated retries then exit:** Reconnects are capped (default 12 attempts). Tune with `--web-retries`, `--web-retry-initial`, `--web-retry-max`, or config `web.reconnect`. - **No inbound messages:** Ensure the QR-linked account is online in WhatsApp, and check logs for `web-heartbeat` to confirm auth age/connection. - **Fast nuke:** From an allowed WhatsApp sender you can send `/restart` to kick `com.steipete.clawdis` via launchd; wait a few seconds for it to relink. ## Helpful commands -- Start relay web-only: `pnpm clawdis gateway --provider web --verbose` +- Start gateway web-only: `pnpm clawdis gateway --provider web --verbose` - Show who is linked: `pnpm clawdis gateway --provider web --verbose` (first line prints the linked E.164) - Logout (clear creds): `pnpm clawdis logout` - Relink: `pnpm clawdis login --provider web` diff --git a/docs/remote.md b/docs/remote.md index ba724d8eb..b67dcfe8d 100644 --- a/docs/remote.md +++ b/docs/remote.md @@ -1,9 +1,14 @@ +--- +summary: "Remote mode topology using SSH control channels between gateway and mac app" +read_when: + - Running or troubleshooting remote gateway setups +--- # Remote mode with control channel -This repo supports “remote over SSH” by keeping a single relay (the master) running on a host (e.g., your Mac Studio) and connecting one or more macOS menu bar clients to it. The menu app no longer shells out to `pnpm clawdis …`; it talks to the relay over a persistent control channel that is tunneled through SSH. +This repo supports “remote over SSH” by keeping a single gateway (the master) running on a host (e.g., your Mac Studio) and connecting one or more macOS menu bar clients to it. The menu app no longer shells out to `pnpm clawdis …`; it talks to the gateway over a persistent control channel that is tunneled through SSH. ## Topology -- Master: runs the relay + control server on `127.0.0.1:18789` (in-process TCP server). +- Master: runs the gateway + control server on `127.0.0.1:18789` (in-process TCP server). - Clients: when “Remote over SSH” is selected, the app opens one SSH tunnel: - `ssh -N -L :127.0.0.1:18789 @` - The app then connects to `localhost:` and keeps that socket open. @@ -14,10 +19,10 @@ This repo supports “remote over SSH” by keeping a single relay (the master) 2) Open TCP socket to the local forwarded port. 3) Send `ping` to verify connectivity. 4) Issue `health`, `status`, and `last-heartbeat` requests to seed UI. -5) Listen for `event` frames (heartbeat updates, relay status). +5) Listen for `event` frames (heartbeat updates, gateway status). ## Heartbeats -- Heartbeats always run on the master relay. +- Heartbeats always run on the master gateway. - The control server emits `event: "heartbeat"` after each heartbeat attempt and keeps the latest in memory for `last-heartbeat` requests. - No file-based heartbeat logs/state are required when the control stream is available. @@ -26,7 +31,7 @@ This repo supports “remote over SSH” by keeping a single relay (the master) ## Failure handling - If the tunnel drops, the client reconnects and re-issues `ping`, `health`, and `last-heartbeat` to refresh state (the mac app shows “Control channel disconnected”). -- If the control port is unavailable (older relay), the app can optionally fall back to the legacy CLI path, but the goal is to rely solely on the control channel. +- If the control port is unavailable (older gateway), the app can optionally fall back to the legacy CLI path, but the goal is to rely solely on the control channel. ## Test Remote (in the mac app) 1) SSH reachability check (`ssh -o BatchMode=yes … echo ok`). @@ -39,4 +44,4 @@ This repo supports “remote over SSH” by keeping a single relay (the master) ## Files to keep in sync - Protocol definition: `docs/control-api.md`. - App connection logic: macOS `Remote over SSH` plumbing. -- Relay control server: lives inside the Node relay process. +- Gateway control server: lives inside the Node gateway process. diff --git a/docs/rpc.md b/docs/rpc.md index 147780bb1..cdf31df4f 100644 --- a/docs/rpc.md +++ b/docs/rpc.md @@ -1,6 +1,11 @@ +--- +summary: "JSON RPC contract used by the mac app to talk to the gateway" +read_when: + - Changing mac app RPC or agent toggles +--- # Clawdis Agent RPC -Live, stdin/stdout JSON RPC used by the mac app (XPC) to avoid spawning `clawdis agent --json` for every send and to toggle runtime features (e.g., heartbeats) without restarting the relay. +Live, stdin/stdout JSON RPC used by the mac app (XPC) to avoid spawning `clawdis agent --json` for every send and to toggle runtime features (e.g., heartbeats) without restarting the gateway. ## How it is launched - The mac app starts `clawdis rpc` in the configured project root (`CommandResolver.projectRoot()`, defaults to `~/Projects/clawdis`). @@ -11,7 +16,7 @@ Live, stdin/stdout JSON RPC used by the mac app (XPC) to avoid spawning `clawdis ### Requests (stdin) - `{"type":"status"}` → health ping. - `{"type":"send","text":"hi","session":"main","thinking":"low","deliver":false,"to":"+1555..."}` → invokes existing agent send path. -- `{"type":"set-heartbeats","enabled":true|false}` → enables/disables web heartbeat timers in the running relay process. +- `{"type":"set-heartbeats","enabled":true|false}` → enables/disables web heartbeat timers in the running gateway process. ### Responses (stdout) - `{"type":"result","ok":true,"payload":{...}}` on success. @@ -23,8 +28,8 @@ Notes: ## Heartbeat control (new) - The mac menu exposes “Send heartbeats” toggle (persisted in UserDefaults). -- On change, mac sends `set-heartbeats` RPC; the relay updates an in-memory flag and short-circuits its heartbeat timers (`web-heartbeat` logging + reply heartbeats). -- No relay restart required. +- On change, mac sends `set-heartbeats` RPC; the gateway updates an in-memory flag and short-circuits its heartbeat timers (`web-heartbeat` logging + reply heartbeats). +- No gateway restart required. ## Fallbacks / safety - If the RPC process is not running, mac-side RPC calls fail fast and the app logs/clears state; callers may fall back to one-shot CLI where appropriate. @@ -32,5 +37,5 @@ Notes: ## Future extensions - Add `abort` to cancel in-flight sends. -- Add `compact` / `status --verbose` to return relay internals (queue depth, session info). +- Add `compact` / `status --verbose` to return gateway internals (queue depth, session info). - Add a JSON schema test for the RPC contract. diff --git a/docs/security.md b/docs/security.md index 26d2a8cdd..6a31ea583 100644 --- a/docs/security.md +++ b/docs/security.md @@ -1,3 +1,8 @@ +--- +summary: "Security considerations and threat model for running an AI gateway with shell access" +read_when: + - Adding features that widen access or automation +--- # Security 🔒 Running an AI agent with shell access on your machine is... *spicy*. Here's how to not get pwned. diff --git a/docs/session.md b/docs/session.md index d69f28fa2..acb8cdf49 100644 --- a/docs/session.md +++ b/docs/session.md @@ -1,3 +1,8 @@ +--- +summary: "Session management rules, keys, and persistence for chats" +read_when: + - Modifying session handling or storage +--- # Session Management Clawdis treats **one session as primary**. By default the canonical key is `main` for every direct chat; no configuration is required. You can rename it via `inbound.reply.session.mainKey` if you really want, but there is still only a single primary session. Older/local sessions can stay on disk, but only the primary key is used for desktop/web chat and direct agent calls. diff --git a/docs/surface.md b/docs/surface.md index 3e6f5a08b..d5401b2ce 100644 --- a/docs/surface.md +++ b/docs/surface.md @@ -1,3 +1,8 @@ +--- +summary: "Routing rules per surface (WhatsApp, Telegram, web) and shared context" +read_when: + - Changing surface routing or inbox behavior +--- # Surfaces & Routing Updated: 2025-12-07 @@ -9,6 +14,6 @@ Goal: make replies deterministic per channel while keeping one shared context fo - **Session store:** Keys are resolved via `resolveSessionKey(scope, ctx, mainKey)`; the Tau JSONL path still lives under `~/.clawdis/sessions/.jsonl`. - **WebChat:** Always attaches to `main`, loads the full Tau transcript so desktop reflects cross-surface history, and writes new turns back to the same session. - **Implementation hints:** - - Set `Surface` in each ingress (WhatsApp relay, WebChat bridge, future Telegram). + - Set `Surface` in each ingress (WhatsApp gateway, WebChat bridge, future Telegram). - Keep routing deterministic: originate → same surface. Use IPC/web senders accordingly. - Do not let the agent emit “send to X” decisions; keep that policy in the host code. diff --git a/docs/telegram.md b/docs/telegram.md index c9eac79b0..cd5250278 100644 --- a/docs/telegram.md +++ b/docs/telegram.md @@ -1,3 +1,8 @@ +--- +summary: "Telegram bot support status, capabilities, and configuration" +read_when: + - Working on Telegram features or webhooks +--- # Telegram (Bot API) Updated: 2025-12-07 @@ -12,7 +17,7 @@ Status: ready for bot-mode use with grammY (long-poll + webhook). Text + media s ## How it will work (Bot API) 1) Create a bot with @BotFather and grab the token. 2) Configure Clawdis with `TELEGRAM_BOT_TOKEN` (or `telegram.botToken` in `~/.clawdis/clawdis.json`). -3) Run the relay; it auto-starts Telegram when the bot token is set. To force Telegram-only: `clawdis relay --provider telegram`. Webhook mode: `clawdis relay --provider telegram --webhook --port 8787 --webhook-secret ` (optionally `--webhook-url` when the public URL differs). +3) Run the gateway; it auto-starts Telegram when the bot token is set. To force Telegram-only: `clawdis gateway --provider telegram`. Webhook mode: `clawdis gateway --provider telegram --webhook --port 8787 --webhook-secret ` (optionally `--webhook-url` when the public URL differs). 4) Direct chats: user sends the first message; all subsequent turns land in the shared `main` session (default, no extra config). 5) Groups: add the bot, disable privacy mode (or make it admin) so it can read messages; group threads stay on `group:` and require mention/command to trigger replies. 6) Optional allowlist: reuse `inbound.allowFrom` for direct chats by chat id (`123456789` or `telegram:123456789`). @@ -24,7 +29,7 @@ Status: ready for bot-mode use with grammY (long-poll + webhook). Text + media s - Typing indicators (`sendChatAction`) supported; inline reply/threading supported where Telegram allows. ## Planned implementation details -- Library: grammY is the only client for send + relay (fetch fallback removed); grammY throttler is enabled by default to stay under Bot API limits. +- Library: grammY is the only client for send + gateway (fetch fallback removed); grammY throttler is enabled by default to stay under Bot API limits. - Inbound normalization: maps Bot API updates to `MsgContext` with `Surface: "telegram"`, `ChatType: direct|group`, `SenderName`, `MediaPath`/`MediaType` when attachments arrive, and `Timestamp`; groups require @bot mention by default. - Outbound: text and media (photo/video/audio/document) with optional caption; chunked to limits. Typing cue sent best-effort. - Config: `TELEGRAM_BOT_TOKEN` env or `telegram.botToken` required; `telegram.requireMention`, `telegram.allowFrom`, `telegram.mediaMaxMb`, `telegram.proxy`, `telegram.webhookSecret`, `telegram.webhookUrl` supported. @@ -52,7 +57,7 @@ Example config: ## Roadmap - ✅ Design and defaults (this doc) -- ✅ grammY long-poll relay + text/media send +- ✅ grammY long-poll gateway + text/media send - ✅ Proxy + webhook helpers (setWebhook/deleteWebhook, health endpoint, optional public URL) - ⏳ Add more grammY coverage (webhook payloads, media edge cases) diff --git a/docs/thinking.md b/docs/thinking.md index d2efd8f6e..6f1740b7a 100644 --- a/docs/thinking.md +++ b/docs/thinking.md @@ -1,3 +1,8 @@ +--- +summary: "Directive syntax for /think levels and how they affect model reasoning" +read_when: + - Adjusting thinking level parsing or defaults +--- # Thinking Levels (/think directives) ## What it does diff --git a/docs/troubleshooting.md b/docs/troubleshooting.md index 479bceb1a..1658e81ca 100644 --- a/docs/troubleshooting.md +++ b/docs/troubleshooting.md @@ -1,3 +1,8 @@ +--- +summary: "Quick troubleshooting guide for common Clawdis failures" +read_when: + - Investigating runtime issues or failures +--- # Troubleshooting 🔧 When your CLAWDIS misbehaves, here's how to fix it. diff --git a/docs/typebox.md b/docs/typebox.md index a51abc433..c510d2d12 100644 --- a/docs/typebox.md +++ b/docs/typebox.md @@ -1,3 +1,8 @@ +--- +summary: "TypeBox schemas as the single source of truth for the gateway protocol" +read_when: + - Updating protocol schemas or codegen +--- # TypeBox as Protocol Source of Truth Last updated: 2025-12-09 diff --git a/docs/webchat.md b/docs/webchat.md index 0fc364721..f368fd1e5 100644 --- a/docs/webchat.md +++ b/docs/webchat.md @@ -1,3 +1,8 @@ +--- +summary: "Loopback WebChat server and SSH tunnel usage for chat UI" +read_when: + - Debugging or configuring WebChat access +--- # WebChat (loopback + SSH tunnel) Updated: 2025-12-09 diff --git a/package.json b/package.json index 461dd7cac..9e6f306e8 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "clawdis", "version": "2.0.0", - "description": "WhatsApp relay CLI (Baileys web) with Pi RPC agent", + "description": "WhatsApp gateway CLI (Baileys web) with Pi RPC agent", "type": "module", "main": "dist/index.js", "bin": { @@ -9,6 +9,7 @@ }, "scripts": { "dev": "tsx src/index.ts", + "docs:list": "tsx scripts/docs-list.ts", "build": "tsc -p tsconfig.json", "start": "tsx src/index.ts", "clawdis": "tsx src/index.ts", diff --git a/scripts/docs-list.ts b/scripts/docs-list.ts new file mode 100644 index 000000000..7ccbb39e0 --- /dev/null +++ b/scripts/docs-list.ts @@ -0,0 +1,146 @@ +#!/usr/bin/env tsx + +import { readdirSync, readFileSync } from 'node:fs'; +import { dirname, join, relative } from 'node:path'; +import { fileURLToPath } from 'node:url'; + +const docsListFile = fileURLToPath(import.meta.url); +const docsListDir = dirname(docsListFile); +const DOCS_DIR = join(docsListDir, '..', 'docs'); + +const EXCLUDED_DIRS = new Set(['archive', 'research']); + +function compactStrings(values: unknown[]): string[] { + const result: string[] = []; + for (const value of values) { + if (value === null || value === undefined) { + continue; + } + const normalized = String(value).trim(); + if (normalized.length > 0) { + result.push(normalized); + } + } + return result; +} + +function walkMarkdownFiles(dir: string, base: string = dir): string[] { + const entries = readdirSync(dir, { withFileTypes: true }); + const files: string[] = []; + for (const entry of entries) { + if (entry.name.startsWith('.')) { + continue; + } + const fullPath = join(dir, entry.name); + if (entry.isDirectory()) { + if (EXCLUDED_DIRS.has(entry.name)) { + continue; + } + files.push(...walkMarkdownFiles(fullPath, base)); + } else if (entry.isFile() && entry.name.endsWith('.md')) { + files.push(relative(base, fullPath)); + } + } + return files.sort((a, b) => a.localeCompare(b)); +} + +function extractMetadata(fullPath: string): { + summary: string | null; + readWhen: string[]; + error?: string; +} { + const content = readFileSync(fullPath, 'utf8'); + + if (!content.startsWith('---')) { + return { summary: null, readWhen: [], error: 'missing front matter' }; + } + + const endIndex = content.indexOf('\n---', 3); + if (endIndex === -1) { + return { summary: null, readWhen: [], error: 'unterminated front matter' }; + } + + const frontMatter = content.slice(3, endIndex).trim(); + const lines = frontMatter.split('\n'); + + let summaryLine: string | null = null; + const readWhen: string[] = []; + let collectingField: 'read_when' | null = null; + + for (const rawLine of lines) { + const line = rawLine.trim(); + + if (line.startsWith('summary:')) { + summaryLine = line; + collectingField = null; + continue; + } + + if (line.startsWith('read_when:')) { + collectingField = 'read_when'; + const inline = line.slice('read_when:'.length).trim(); + if (inline.startsWith('[') && inline.endsWith(']')) { + try { + const parsed = JSON.parse(inline.replace(/'/g, '"')) as unknown; + if (Array.isArray(parsed)) { + readWhen.push(...compactStrings(parsed)); + } + } catch { + // ignore malformed inline arrays + } + } + continue; + } + + if (collectingField === 'read_when') { + if (line.startsWith('- ')) { + const hint = line.slice(2).trim(); + if (hint) { + readWhen.push(hint); + } + } else if (line === '') { + // allow blank lines inside the list + } else { + collectingField = null; + } + } + } + + if (!summaryLine) { + return { summary: null, readWhen, error: 'summary key missing' }; + } + + const summaryValue = summaryLine.slice('summary:'.length).trim(); + const normalized = summaryValue + .replace(/^['"]|['"]$/g, '') + .replace(/\s+/g, ' ') + .trim(); + + if (!normalized) { + return { summary: null, readWhen, error: 'summary is empty' }; + } + + return { summary: normalized, readWhen }; +} + +console.log('Listing all markdown files in docs folder:'); + +const markdownFiles = walkMarkdownFiles(DOCS_DIR); + +for (const relativePath of markdownFiles) { + const fullPath = join(DOCS_DIR, relativePath); + const { summary, readWhen, error } = extractMetadata(fullPath); + if (summary) { + console.log(`${relativePath} - ${summary}`); + if (readWhen.length > 0) { + console.log(` Read when: ${readWhen.join('; ')}`); + } + } else { + const reason = error ? ` - [${error}]` : ''; + console.log(`${relativePath}${reason}`); + } +} + +console.log( + '\nReminder: keep docs up to date as behavior changes. When your task matches any "Read when" hint above (React hooks, cache directives, database work, tests, etc.), read that doc before coding, and suggest new coverage when it is missing.' +);