You ping. Code pongs.
An open-source messaging bridge connecting WeChat, Feishu, and other chat platforms to Claude Code, Codex, and more.
Lightweight, no build step. Pure Python, single asyncio process — no C extensions, no compiler toolchain, no Node sidecar, no Docker, no web framework, no browser automation.
Easy to use. A single
/cccommand surface covers querying, reviewing, and bidirectional file/image transfer. Everything is driven from the chat window — no extra client UI.Safe development loop. Opt-in remote dev loop: each write task runs in an isolated git worktree (main workspace untouched), sensitive tool calls go through a
IMapproval gate, a risk classifier allow/ask/deny each call, and one command decides keep or rollback.Bidirectional resume.
IMand the desktop Terminal share the same local Vibe Code sessions — resume a Terminal session from your phone, or hand aIMsession back to the computer.
One bridge process joins two halves: the iLink half (WeChat transport — long-polling, AES-encrypted CDN) and the Claude half (claude_agent_sdk.query() → bundled claude CLI → Anthropic-protocol gateway). The pingpong-bridge is the single orchestrator and the only place that calls Claude. Project/session/runtime state lives in SQLite; the Claude Code local transcript remains the single source of truth for session content, accessed only via the Agent SDK session API.
PingPongCode has three operating modes that layer on top of each other. All are off by default, so the read-only path is the unchanged baseline — you only turn on what you need.
Legacy read-only (default). Claude can only
Read/Glob/Grepagainst a singleWORKSPACE_DIR— no edits, no shell. The safe baseline for project analysis, send/fetch reports/figures. Nothing to configure; this is what you get out of the box.Managed mode (opt-in). Set
CC_ALLOWED_PROJECT_ROOTSto let WeChat and your desktop Terminal share the same local Claude Code sessions — resume a Terminal session from your phone (/cc resume), or hand a WeChat session back to the computer (/cc handoff→claude --resume <id>). Write-capable but confined to allowed roots under a project+session lock.Safe development loop (opt-in, requires managed mode). Set
CC_SAFE_DEV_LOOP_ENABLED=truefor a full remote dev loop: each write task runs in an isolated git worktree (main workspace untouched), sensitive tool calls go through a WeChat approval gate (/cc approve//cc deny), and a structured report is reconciled against Bridge-computed git/test facts. One command decides keep or rollback (/cc keep//cc rollback). Switch from WeChat with/cc mode readonly|edit|full.
For the full comparison table, exact
permission_mode/setting_sourcesmappings, and how the two opt-ins layer, seedocs/DETAIL.md#three-modes.
| Requirement | Notes |
|---|---|
| OS | Ubuntu / Linux (designed & tested) · macOS (tested). Windows not supported currently (os.kill liveness + 0600 perms). |
| Python | ≥ 3.11 |
claude CLI | On PATH — claude-agent-sdk spawns it. The SDK bundles a compatible CLI; the system claude may differ. |
git | Required by managed mode (git init) and essential for the safe-dev-loop (every write task uses a git worktree). |
| Gateway | An Anthropic-protocol endpoint. The SDK sets ANTHROPIC_BASE_URL and speaks the Anthropic Messages API. Image analysis needs a vision model (e.g. claude-sonnet-5, minimax-m3, etc.). |
| Via an iLink/ClawBot session. |
Installed via pip install -r requirements.txt:
| Package | Purpose |
|---|---|
claude-agent-sdk | spawns the bundled claude CLI subprocess |
httpx | iLink HTTP API (long-poll, send, CDN upload/download) |
pydantic / pydantic-settings / python-dotenv | config model + .env loading |
aiosqlite | async SQLite persistence |
qrcode / pillow | iLink QR login rendering |
rich | terminal output / QR display |
tenacity | polling backoff/retry |
From the repo root. Create a Python ≥ 3.11 environment, then install the dependencies — venv or conda both work (uv/poetry too, if you prefer):
# Option A — venv (built into Python, no extra install)
python3 -m venv .venv
source .venv/bin/activate
# Option B — conda
conda create -n pingpongcode python=3.11
conda activate pingpongcode
# then, in whichever env you activated:
pip install -r requirements.txt
pip install -e .Ubuntu's system Python is externally-managed (PEP 668) — use one of the environments above, or
--break-system-packages.
Recommended — the interactive wizard (grouped, validated, secrets masked):
python -m pingpong_bridge config initOr create .env by hand:
cp .env.example .env
$EDITOR .envFor the required variables (gateway, model,
WORKSPACE_DIR), managed-mode & safe-dev-loop variables, and how the wizard validates values, seedocs/DETAIL.md#configure.
Check config (secrets masked):
python -m pingpong_bridge show-configNote: running straight from source without a registered package, i.e., you prefer not using
pip install -e .? Prefix the module withPYTHONPATH=src— e.g.PYTHONPATH=src python -m pingpong_bridge login(and likewise forrun/check-*).
Four steps, in order:
A. Scan to log in — terminal prints a QR (also saved to artifacts/ilink-login.png); scan with WeChat iLink/ClawBot and confirm. Credentials save to data/ilink_credentials.json (0600).
python -m pingpong_bridge loginB. iLink bidirectional self-check — run it, then send /cc ping from your phone; expect [CC] pong.
python -m pingpong_bridge check-ilinkC. Gateway + Agent SDK self-check — no phone needed.
python -m pingpong_bridge check-gatewayD. Start the bridge — runs in the foreground; Ctrl+C to stop.
python -m pingpong_bridge runNote: on first run (if ILINK_ALLOWED_USER_ID isn't set) the terminal shows a pairing code — send /cc pair <code> from your phone to bind, then any /cc task.
Pairing details, a first-task example, and how to stop/restart:
docs/DETAIL.md#run.
All commands are /cc-prefixed (case-insensitive). Only messages from the bound user are executed; bot's own messages and plain chat are ignored. Commands are grouped by the mode that enables them.
| Command | Purpose |
|---|---|
/cc ping | connectivity test → [CC] pong |
/cc status | mode-aware status (mode, working dir, project/session, runtime counters, iLink state, model — no secrets) |
/cc help | command list |
/cc pair <code> | first-time user binding |
/cc <task> | run a task against the current project |
/cc ask <task> | explicit ask form (use for tasks starting with a keyword like resume) |
/cc send <relative-path> | send a workspace file/image back to WeChat (details) |
/cc clear | drop buffered images/files |
Command typos are not run as tasks.
/cc aproveor/cc reports(no further text) replies "unknown command" with a "did you mean" hint — retype the correct command. A typo with trailing text (/cc aproove fix the bug) still runs as a task.
| Command | Purpose |
|---|---|
/cc projects | list discovered/registered projects |
/cc project <name|index|new <name>|info> | select / create / inspect a project |
/cc sessions [limit|all] | list sessions for the active project |
/cc resume <index|id|prefix> | set the active session (next /cc <task> resumes it) |
/cc new-session [title] | start a fresh session in the active project |
/cc session | show the active project + session |
/cc rename-session <title> | rename the active session |
/cc handoff | get cd '<cwd>'\nclaude --resume '<id>' to continue in a Terminal |
/cc doctor | bridge self-check |
Bidirectional resume workflow (Terminal↔WeChat, restart persistence) and the path-safety model: docs/DETAIL.md#resume.
| Command | Purpose |
|---|---|
/cc mode readonly|edit|full | switch the active mode (persisted) |
/cc tasks | list recent task runs (numbered, like /cc sessions) |
/cc task [n|task-id] | show one task's state/report — by list index or id |
/cc stop | interrupt the running task (worktree preserved) |
/cc retry <task-id> | retry a failed/cancelled task |
/cc approvals | list pending approvals |
/cc approve|deny [code] [reason] | decide a pending approval (omit code when only one is pending; deny may take a reason that is fed back to the model to guide a fallback) |
/cc answer <code> <text> | answer a question the model asked mid-task (seen as [CC][提问 XXXX]; code required) |
/cc report|diff|files|tests|usage [task-id] | inspect a task's reconciled facts |
/cc keep | preserve the worktree + branch |
/cc continue [task-id] | next task chains off a kept task's branch (progressive dev) |
/cc rollback | delete the worktree + branch (second approval) |
/cc worktree | show the task's worktree path |
/cc checkpoints / /cc rewind <id> | (optional, capability-gated) file checkpointing |
Flow, the mode × tool permission matrix, hard invariants, and the SDK workarounds: docs/DETAIL.md#safe-dev-loop.
On by default, but inert unless the safe dev loop is enabled and the backend is Claude. When active, a bare /cc <task> (mode edit/full) splits into a multi-turn planning phase (plan mode, read-only) and a one-shot execution phase (same worktree, default mode). The model calls ExitPlanMode to submit its plan; you confirm or reject from WeChat. Set CC_TWO_PHASE_ENABLED=false to keep the single-phase safe-dev-loop path.
| Command | Purpose |
|---|---|
/cc say <msg> | append a follow-up message to the in-flight planning conversation (multi-turn) |
/cc confirm [code] | approve the proposed plan → proceed to execution |
/cc reject [code] [reason] | reject the plan → Claude revises it in the same planning conversation |
/cc cancel | end the in-flight planning/execution conversation |
Design and E2E findings: docs/DETAIL.md#two-phase.
Execution rules (all modes): only one task at a time (a second gets "try later"); long results are split into segments ≤ WECHAT_MAX_TEXT_CHARS; a reply send failure never re-runs the Claude task.
python -m pingpong_bridge doctor # bridge self-check
python -m pingpong_bridge doctor --live # also probe iLink send/receive
python -m pingpong_bridge logout# delete iLink credentials
python -m pingpong_bridge reset-state # delete the SQLite state DB
python -m pingpong_bridge show-config # masked config overview
python -m pingpong_bridge config init # interactive .env wizard
python -m pingpong_bridge config show # grouped masked config
python -m pingpong_bridge config edit <KEY> [VAL] # edit one field (validated)A task is the bridge's unit of remote work: one /cc <task> you send from your phone. The design is built on one idea — a task is an auditable, rollback-able execution unit + an isolated worktree of changes — and that idea shapes every task command.
- Isolated by default (worktree). Each write task runs in its own
git worktreeon its own branch (cc/task-…). The model edits files there; your main workspace is never touched. Two tasks run on two separate trees, so they can't stomp on each other. This is why/cc <task>never modifies your working directory directly, and why a failed task leaves nothing behind in your tree. - Auditable. A task is a first-class DB record (state machine:
received → … → awaiting_decision → kept/rolled_back), with an event trail, a reconciled report (the model's self-report checked against Bridge-computed git diff + test facts — bridge facts win), saved diff/test artifacts, and token/usage. This is what/cc tasks,/cc task,/cc report|diff|files|tests|usage, and/cc usageread from. - Rollback-able. Because the bridge never commits (it only edits the worktree's working tree), rolling back is just deleting the worktree + branch — nothing was ever written to your history, so
/cc rollbackloses nothing and leaves the main workspace untouched. The inverse,/cc keep, preserves the tree so you can hand off into it and commit yourself when satisfied.
This triad is why the commands are shaped the way they are:
| You want to… | Command | Why it works this way |
|---|---|---|
| run a fresh change | /cc <task> | new worktree off main HEAD; isolated |
| stop a runaway task | /cc stop | interrupt; worktree preserved so you can inspect before deciding |
| retry a failed task | /cc retry <id> | starts fresh (new worktree, new session) — a failed task's edits are bad, so they're discarded, not inherited |
| keep good work | /cc keep | preserve the worktree+branch; hand off into it to commit |
| build B on top of A | /cc keep A → /cc continue | B's worktree branches off A's branch tip and applies A's uncommitted diff, so B sees A's changes without a merge |
| throw it away | /cc rollback | delete worktree+branch; second approval because it's destructive |
| inspect what happened | /cc report|diff|tests | read the task's audited facts (not the model's claim) |
The bridge never commits or merges — /cc keep + /cc continue carry changes between worktrees via uncommitted-diff application, not git history.
In the safe development loop, one write task produces three distinct things that are easy to conflate:
- task — the execution record of this one task (state, result). Lives in the bridge DB; fixed when the task ends.
- worktree + branch (
cc/task-YYYYMMDD-…) — an isolated git working copy where the model's file edits land. The main workspace is never touched. Preserved by/cc keep, deleted by/cc rollback. - session (
019fd…) — the conversation history with the model. Persistent across tasks and restarts. A later task can fork a prior session to carry its context forward without polluting it (like a git branch off a commit).
task ↔ worktree ↔ branch is 1:1:1. task ↔ session is not 1:1 — sessions are reused via fork/resume.
The bridge never commits. It edits files in the worktree but never runs git add/commit/push. After /cc keep, you hand off into the worktree and commit yourself when satisfied. /cc rollback deletes the worktree outright (nothing was committed, so nothing is lost from your history).
Progressive development without leaving WeChat. Because each task starts a fresh worktree off the main HEAD, two independent tasks don't see each other's changes. To build feature A then feature B on top of it, use the keep → continue chain: /cc keep task A (preserves its branch), then /cc continue — the next /cc <task> creates its worktree off task A's branch tip (not main HEAD), so it sees A's changes. The session is forked too, so both code and conversation stay continuous. No Terminal merge needed. The chain can be extended: keep → continue → keep → continue …
Full explanation with a diagram and the why-behind-the-design:
docs/DETAIL.md#task-session-worktree-branch-how-they-relate.
PingPongCode targets claude_agent_sdk 0.2.128 + the bundled claude CLI. The bridge works around 9 verified SDK/CLI differences, and several features are cooperative (not OS-enforced) or deferred. The highlights:
- Execution locks +
claude agents --jsondetection are cooperative — a user can still manually runclaude --resume <same-session-id>in a Terminal despite the bridge holding a lock. - The gateway you use sees prompts and project content (read your gateway provider's data policy).
AskUserQuestiondoes not exist inclaude_agent_sdk0.2.128, so the spec's clarifying-question→WeChat flow is not implemented (deferred); only the approval flow is real./cc rewind//cc checkpointsare capability-gated (worktree-only; rewind only coversWrite/Edit/NotebookEdit, not Bash/subagent — use/cc rollbackto fully abandon).- Auto-merge is out of scope (
/cc keeppreserves the branch only); for progressive development use the/cc keep→/cc continueworktree chain instead of merging.
Full limitations:
docs/KNOWN_LIMITATIONS.md. SDK/CLI differences and the 9 workarounds:docs/SDK_CLI_COMPATIBILITY.md.
src/pingpong_bridge/
├── ilink/ # WeChat transport (poller, client, parser, protocol, media crypto)
├── projects/ # project discovery, PathPolicy, selection snapshots
├── sessions/ # SDK session API wrapper + backend Protocol
├── execution/ # execution locks, interrupt, checkpoint, live-session detector
├── permissions/ # risk classifier, PreToolUse hook, approval service, capability probe
├── tasks/ # task state machine, recovery, progress
├── worktrees/ # git worktree service (create/keep/rollback)
├── reports/ # diff/test services, reconciliation, reviewer stub
├── delivery/ # outbox (reliable send, retry, dead-letter)
├── media/ # bidirectional file/image transfer (CDN, AES)
├── bridge.py # the orchestrator
├── config.py # pydantic-settings (single chokepoint for all validators)
└── ...
docs/ # SDK/CLI compatibility report + known limitations (public)
| Category | Supported | Planned |
|---|---|---|
| IM | WeChat ✅ | Feishu ❌ · Telegram ❌ |
| Agent | Claude Code ✅ · Codex ✅ | OpenCode ❌ |
| OS | Linux ✅ · macOS ✅ | Windows ❌ |
See CHANGELOG.md for the per-version history of added
features, fixes, and security-relevant changes. Each release is tagged
identically on Gitee (dev) and GitHub (pingpongcode).
MIT, see LICENSE.


