简体中文 | English
Turn Claude Code and Codex on your Mac into a wrist-first, phone-friendly remote workspace.
Read live output, answer approvals, and send voice prompts from Apple Watch, iPhone, and iPad.
Agent Watcher is designed for people who run AI coding agents on a Mac but do not want to stay in front of the computer all the time.
Typical scenarios:
- You are walking, commuting, or away from your desk and need to approve an agent action immediately.
- You want to check whether Claude Code or Codex is still running, waiting, or finished.
- You want to send a short follow-up prompt from your watch or phone without opening the Mac.
- You want one bridge that works on local Wi-Fi, a private remote network, and a public remote tunnel.
The project keeps the same approval flow and session ownership semantics across all transport modes. LAN, Direct, and Cloudflare only change how the client reaches the bridge. They do not redefine which session an approval belongs to.
- Apple Watch approval handling for Claude Code and Codex sessions
- Apple Watch voice input for quick follow-up prompts
- iPhone companion app for pairing, transport selection, status, terminal stream, and approvals
- iPad companion app with the same approval and session-routing logic as iPhone
- Multi-session bridge on macOS for Claude Code, Codex, tmux-backed sessions, and mirrored external terminals
- Persistent 6-digit pairing code support
- Three network entry modes:
LANfor the same Wi-Fi / office networkDirectfor private networking such as TailscaleCloudflarefor public HTTPS access over the Internet
Apple Watch <--WCSession--> iPhone / iPad <--HTTP / SSE--> Bridge Server on Mac
|
+--> Claude Code
+--> Codex
+--> tmux / mirrored terminals
| Mode | Best for | Latency | Internet exposure | Notes |
|---|---|---|---|---|
LAN |
Same home/office Wi-Fi | Lowest | No | Easiest local setup |
Direct |
Remote access with low latency | Usually best remote option | No | Recommended with Tailscale |
Cloudflare |
Reach from anywhere without VPN | Usually higher than Direct | Yes, via Cloudflare | Best when you need public HTTPS |
You need the following tools before building or deploying:
- Node.js 18+:
- Download: https://nodejs.org/en/download
- Xcode:
- Download: https://developer.apple.com/xcode/
- App Store page: https://apps.apple.com/us/app/xcode/id497799835
- XcodeGen:
- Download / releases: https://github.com/yonaskolb/XcodeGen/releases
- Homebrew:
brew install xcodegen
- Optional for
Directmode:- Tailscale: https://tailscale.com/download
- Optional for
Cloudflaremode:
Hardware / platform assumptions:
- A Mac running Claude Code or Codex
- An iPhone paired with an Apple Watch if you want watch approval / voice control
- An optional iPad if you want a larger remote control surface
This repository can ship three built app bundles as GitHub Release assets:
Agent Watcher.appfor iPhoneAgent Watcher Pad.appfor iPadAgent Watcher.appfor watchOS
Important:
- These builds are development artifacts. Real-device installation still depends on Apple signing and Developer Mode.
- For most users, the most reliable installation path is to build from source in Xcode with your own Apple ID / team selected.
git clone <your-repo-url>
cd claude-watch./skill/setup.shOr manually:
cd skill/bridge
npm install./skill/setup-hooks.shIf your bridge runs on a different local port:
./skill/setup-hooks.sh 7860To remove hooks later:
./skill/setup-hooks.sh --removeAuto-generate a pairing code for this bridge process:
node skill/bridge/server.jsOr define a fixed pairing code that never changes until you restart with a different one:
node skill/bridge/server.js --pairing-code 123456Environment variable form:
PAIRING_CODE=123456 node skill/bridge/server.jsThe bridge prints:
- Pairing code
- IP address
- Port
The pairing code is reusable:
- It is not consumed by the first device
- It does not expire automatically while the bridge keeps running
- One code can pair multiple iPhones / iPads / Watches
You have two ways to drive an agent session that the bridge can see:
./skill/setup-hooks.sh (step 3) installs two wrapper commands into ~/.local/bin:
claude-watch— drop-in replacement forclaude; registers the current terminal with the bridge before running Claude Code.codex-watch— drop-in replacement forcodex; additionally bridgescodex execevents.
Make sure ~/.local/bin is on your PATH:
echo 'export PATH="$HOME/.local/bin:$PATH"' >> ~/.zshrc
source ~/.zshrcVerify:
which claude-watch
which codex-watchStrongly recommended: run them inside tmux. The bridge adopts any tmux pane that hosts a claude-watch / codex-watch terminal, which means:
- The session survives a bridge restart (you can kill and relaunch
node server.jswithout losing context). - You can reattach from any terminal and the phone / iPad keeps streaming.
- Multiple panes can be tracked independently.
# One-time: create a work session
tmux new -s dev
# Inside tmux, start the agent
claude-watch # Claude Code, interactive
codex-watch # Codex, interactive
codex-watch exec "your prompt" # Codex, one-shot with streamed events
# Detach with Ctrl-b d, reattach later with:
tmux attach -t devWithout tmux the wrappers still work, but if you close the terminal the session ends.
If you do not want to open a terminal on the Mac, you can let the app do it:
- Pair the iPhone / iPad to the bridge (next section).
- On the home screen tap the
+button. - Choose
New Claude WindoworNew Codex Window. - The bridge opens a new Terminal.app window on the Mac with the selected agent, already bridged.
Use this when you are away from the Mac and need to start a fresh agent from the phone.
cd ios/ClaudeWatch
xcodegen generate
open ClaudeWatch.xcodeprojBuild targets:
ClaudeWatch: iPhone app with embedded Apple Watch companionClaudeWatchPad: iPad appClaudeWatchWatch: watch target if you need to run watchOS directly
This section is written for someone who has never deployed an iPhone / Watch app before.
- Download Xcode from Apple.
- Open Xcode and let it finish installing additional components if prompted.
- Sign in with your Apple ID in
Xcode > Settings > Accounts. - If Apple shows a developer agreement, accept it before continuing.
- Connect your iPhone to the Mac.
- Unlock the iPhone and tap
Trust This Computerif prompted. - Open
Window > Devices and Simulatorsin Xcode and confirm the iPhone appears. - If you use Apple Watch, make sure the watch is paired to that iPhone.
Apple notes that the watch becomes available to Xcode through the paired iPhone, and Developer Mode must be enabled on the watch too.
Developer Mode is required for running development-signed apps from Xcode.
On iPhone / iPad:
- In Xcode, choose your real device as the run destination and press
Runonce. - If Xcode says Developer Mode is required, cancel the run.
- On the device, open
Settings > Privacy & Security > Developer Mode. - Turn it on.
- Restart when prompted.
- After reboot, unlock the device and confirm
Enable Developer Mode.
On Apple Watch:
- Try to run the watch target from Xcode once.
- On the watch, open
Settings > Privacy & Security > Developer Mode. - Turn it on and confirm after restart if prompted.
In Xcode, open the Signing & Capabilities tab for each target and select your team:
ClaudeWatchClaudeWatchWatchClaudeWatchPad
The current bundle IDs in this project are:
- iPhone:
com.nightwatcherphone.han - Watch:
com.nightwatcherphone.han.watchkitapp - iPad:
com.han.agentwatch.ipad
If Xcode shows provisioning issues:
- Re-select your team
- Let Xcode manage signing automatically
- Try
Product > Clean Build Folder
iPhone + Apple Watch:
- Select the
ClaudeWatchscheme. - Choose the connected iPhone as the run destination.
- Press
Run. - Xcode installs the iPhone app and the embedded watch app.
iPad:
- Select the
ClaudeWatchPadscheme. - Choose the connected iPad as the run destination.
- Press
Run.
The app home screen lets you choose one of three connection modes before entering the pairing code:
LocalDirectCloudflare
General pairing flow:
- Start the bridge on the Mac.
- Read the 6-digit pairing code from the bridge banner.
- Open the iPhone or iPad app.
- Choose the transport mode.
- Enter the address if that mode requires one.
- Enter the pairing code.
- After the phone is paired, the watch should receive bridge credentials from the phone automatically.
If the watch is still waiting after the phone is paired, reopen the watch app and let it sync once more.
Best for:
- Working at home or in the office
- Mac, iPhone, and watch on the same Wi-Fi
- Lowest complexity and lowest local latency
What you need:
- Mac and iPhone on the same network
- Bridge running on the Mac
- Apple Watch ideally on the same Bonjour-compatible Wi-Fi if you expect local discovery behavior to be reliable
Steps:
- Start the bridge:
node skill/bridge/server.js --pairing-code 123456- Open the iPhone app.
- Choose
Local. - Leave the address blank for auto-discovery, or enter the Mac's LAN IP manually, for example:
192.168.1.21
- Enter the 6-digit pairing code.
- Wait for the status screen to show the active sessions.
- Open the watch app and confirm the watch leaves the waiting screen.
Use LAN when:
- You are physically near the Mac
- You do not need remote access over the Internet
- You want the most straightforward setup
Troubleshooting:
- If auto-discovery fails, enter the LAN IP manually.
- If the phone can reach
/statusin Safari but pairing fails, confirm the pairing code matches the currently running bridge. - If the watch cannot sync, first pair the phone successfully, then reopen the watch app.
Best for:
- Remote work over a private network
- Low-latency control without publishing the bridge to the public Internet
- The recommended remote mode for day-to-day use
Typical implementation:
- Tailscale
Why this mode is recommended:
- The bridge protocol, approvals, and session ownership logic remain unchanged.
- Only the network path changes.
- In the best case, Tailscale uses a direct private path instead of an HTTP proxy.
- Install Tailscale on the Mac:
- Install Tailscale on the iPhone / iPad:
- use the same Tailscale download page or the App Store listing linked from there
- Log in to the same tailnet on both devices.
- On the Mac, verify the private IP:
tailscale ip -4Example:
100.104.162.41
- Start the bridge:
node skill/bridge/server.js --pairing-code 123456- Open the iPhone or iPad app.
- Choose
Direct. - Enter one of the following:
100.x.y.z:7860http://100.x.y.z:7860- your Tailscale MagicDNS host if you use one
- Enter the pairing code.
Recommended validation:
tailscale ping <iphone-or-ipad-device-name>Interpretation:
directis idealDERP/ relay works, but latency will usually be higher
Use Direct when:
- You want the best remote latency
- You do not want the bridge to be publicly reachable
- You can install Tailscale on both sides
Direct mode troubleshooting:
- If you see good local bridge logs but slow mobile delivery, check whether
tailscale pingis using relay instead of direct. - If you use a VPN / proxy app such as Clash, exclude the Tailscale subnet
100.64.0.0/10from proxying. - If the app rejects plain
http://100.x.y.z:7860, rebuild with the current project settings so the bundled ATS exceptions are present.
Best for:
- Public remote access from anywhere
- Situations where you cannot install Tailscale on every client
- A setup where HTTPS and a domain name are preferred
Tradeoffs:
- Usually slower than
Direct - Long-lived interactive flows such as SSE can be less stable than a private direct path
- Recommended only when public remote access matters more than lowest latency
- Create or use an existing Cloudflare account.
- Install
cloudflared:- official downloads: https://developers.cloudflare.com/cloudflare-one/connections/connect-networks/downloads/
- Homebrew:
brew install cloudflared
- Authenticate once:
cloudflared tunnel login- Create a named tunnel:
cloudflared tunnel create agent-watcher- Route a hostname to the tunnel:
cloudflared tunnel route dns agent-watcher bridge.example.com- Create
~/.cloudflared/config.yml:
tunnel: YOUR_TUNNEL_ID
credentials-file: /Users/your-name/.cloudflared/YOUR_TUNNEL_ID.json
ingress:
- hostname: bridge.example.com
service: http://localhost:7860
- service: http_status:404- Start the local bridge:
node skill/bridge/server.js --pairing-code 123456- Start the Cloudflare tunnel:
cloudflared tunnel run agent-watcher- In the iPhone or iPad app:
- choose
Cloudflare - enter
https://bridge.example.com - if protected by Cloudflare Access, also enter:
CF-Access-Client-IdCF-Access-Client-Secret
- enter the 6-digit pairing code
- choose
If you do not want the bridge domain to be openly reachable:
- Open Cloudflare Zero Trust.
- Create a self-hosted application for
bridge.example.com. - Create a Service Token under
Access > Service Auth. - Copy the client ID and client secret into the app's Cloudflare fields.
Use Cloudflare when:
- You need reachability from arbitrary networks
- VPN installation is not possible
- You accept higher latency than a private direct path
Cloudflare troubleshooting:
- If requests randomly stall or disconnect, test the bridge locally first with
http://127.0.0.1:7860/status. - Prefer a named tunnel over an ad-hoc quick tunnel for day-to-day use.
- If approval screens or streaming feel delayed, test
Directmode before changing bridge logic.
This point matters for this project:
- Approvals are bound to the correct session by the bridge and shared client state
- Session ownership does not change when you switch between
LAN,Direct, andCloudflare - iPhone, iPad, and Apple Watch all consume the same bridge truth; transport mode only changes network reachability
claude-watch/
├── skill/
│ ├── bridge/server.js
│ ├── setup-hooks.sh
│ └── setup.sh
├── ios/ClaudeWatch/
│ ├── project.yml
│ ├── ClaudeWatch iOS/
│ ├── ClaudeWatch iPad/
│ ├── ClaudeWatch watchOS/
│ └── Shared/
└── README.zh-CN.md
- Start the bridge on the Mac:
node skill/bridge/server.js. - Start Claude Code or Codex on the Mac, preferably inside tmux:
tmux new -s dev && claude-watch(orcodex-watch)- Or skip this step and spawn the session later from the phone via the
+button.
- Open the iPhone app and pair once with the 6-digit code.
- Check the current session on phone, iPad, or watch.
- Approve or deny actions remotely when needed.
- Dictate a short follow-up prompt from the watch or type from phone / iPad.
Created and maintained by Hanmin Wang.
GitHub profile used in the current workspace:
This project is released under the MIT License. See LICENSE.
