oh-my-wrist displays real-time Claude Code and OpenCode activity on your Garmin smartwatch over Bluetooth Low Energy. See what your AI coding assistant is doing — right on your wrist.
Real-time BLE updates— tool calls, file edits, and session state streamed to your watch** Haptic alerts**— vibration patterns for idle, session done, destructive commands, and agent completion** CLI-styled watch UI**— terminal aesthetic with animated amber spinner, event stack, and per-provider stats** Multi-provider**— supports Claude Code and OpenCode simultaneously** Cross-platform**— runs on Linux, macOS, and Windows** Quiet hours**— suppress vibrations during configurable time windows** Connection ID filter**— optional 0–255 ID keeps nearby users' watches from pairing with each other
pip install oh-my-wrist
Or with uv:
uv pip install oh-my-wrist
oh-my-wrist install
This automatically:
-
Patches Claude Code hooks in
~/.claude/settings.json -
Configures a Claude Code statusLine that streams
/usage
quota to the watch (chaining any existing statusLine) - Installs the
OpenCode TypeScript plugin (whenopencode
is on PATH) - Registers a background system service(systemd / launchd / Task Scheduler)
To install a single provider only:
oh-my-wrist install --provider claude
oh-my-wrist install --provider opencode
Install it one of two ways:
- GitHub Releases: download
oh-my-wrist-prg-<version>.zip
from the latest release, unzip it, connect your Garmin device over USB, then copy the matchingoh-my-wrist-<device-id>.prg
file to/GARMIN/Apps/
. - Connect IQ Store:
DOWNLOAD - Build from source: install Connect IQ SDK 9.1+, then run
tools/build_garmin.sh release
and copy the generated device-specific.prg
file frombuild/garmin/
to/GARMIN/Apps/
. VS Code single-device builds fromgarmin/
writebin/oh-my-wrist.prg
.
oh-my-wrist start
Open the oh-my-wrist app on your watch — it connects automatically and updates in real time.
| Platform | Guide |
|---|---|
| 🐧 Linux | |
docs/INSTALL_MACOS.mddocs/INSTALL_WINDOWS.mdStart the daemon in the background (or foreground for debugging):
oh-my-wrist start
oh-my-wrist start --foreground
Start a Claude Code or OpenCode session — the watch updates automatically.
From a source checkout, run the one-minute diagnostic stream to verify the daemon-to-watch link without starting a real Claude Code or OpenCode session:
python tools/check_connection.py
Keep the daemon running and the watch app open. You should see history rows change, Claude/OpenCode stats increment, and Claude usage bars move. Useful options:
python tools/check_connection.py --duration 30
python tools/check_connection.py --dry-run
python tools/check_connection.py --provider claude
Four swipeable views (left/right or UP/DOWN on button watches). History is the initial view — swipe/press UP for the Claude usage screen, DOWN for the per-provider stats screens:
| View | Direction from History | Content |
|---|---|---|
| Claude Usage | UP | /usage -style quota bars: session (5h) and week (7d), htop-style |
| History | — (initial) | CLI-style event stack (3 visible rows with animated spinner) |
| Claude Stats | DOWN | Session duration, tool calls, files edited, bash count, idle time |
| OpenCode Stats | DOWN ×2 | Same metrics, isolated from Claude |
The usage screen is Claude-only and shows an empty bar with no percentage when quota data is unavailable (API-key users, or before the first API response in a session).
Press SELECT/START on any view to open the app menu. Use Set id to save the same 0–255 connection ID configured on your desktop daemon, then restart the watch app so the new BLE service UUID is registered cleanly.
| Command | Purpose |
|---|---|
| `oh-my-wrist start [-f | --foreground]` |
| Start the BLE daemon | |
oh-my-wrist stop |
|
| Stop the daemon | |
oh-my-wrist status |
|
| Show daemon PID, service status, hook/plugin state | |
| `oh-my-wrist install [--provider claude | opencode |
| Configure hooks, plugin, and OS service | |
oh-my-wrist uninstall [--provider …] |
|
| Remove hooks, plugin, and service | |
| `oh-my-wrist test [message] [--provider claude | opencode]` |
| Send a test message to the daemon | |
| `oh-my-wrist config [--show | --haptic on |
| View or update configuration | |
oh-my-wrist set-id ID |
|
Set BLE connection ID (0 –255 ) and queue an update for a running daemon |
|
| `oh-my-wrist opencode install | uninstall |
| Manage the OpenCode plugin | |
oh-my-wrist logs [-n LINES] |
|
| Tail the daemon log |
Settings are stored at ~/.oh-my-wrist/config.json
and take effect immediately (no restart needed).
oh-my-wrist config --show
oh-my-wrist config --haptic on
oh-my-wrist config --haptic off
oh-my-wrist config --quiet-start 22:00 --quiet-end 08:00
oh-my-wrist set-id 42
| Setting | Default | Description |
|---|---|---|
haptic_enabled |
||
true |
||
| Enable/disable all vibration alerts | ||
quiet_start |
||
22:00 |
||
| Start of quiet window (HH:MM) | ||
quiet_end |
||
08:00 |
||
| End of quiet window (HH:MM) | ||
connection_id |
||
0 |
||
| BLE scan filter ID. The watch only connects to daemons with the same ID. |
The connection ID is not authentication; it is a collision reducer for rooms
where multiple people run oh-my-wrist. ID 0
is the default and preserves the original BLE service UUID.
Requires a Garmin watch with Connect IQ Generic BLE support (API level alone is not enough):
Manifest product IDs: approachs50
, approachs7042mm
, approachs7047mm
, d2air
, d2airx10
, d2mach1
, d2mach2
, d2mach2pro
, descentg1
, descentg2
, descentmk2
, descentmk2s
, descentmk343mm
, descentmk351mm
, edge1030
, edge1030plus
, edge1040
, edge1050
, edge530
, edge540
, edge550
, edge830
, edge840
, edge850
, edgeexplore
, edgeexplore2
, edgemtb
, enduro
, enduro3
, epix2
, epix2pro42mm
, epix2pro47mm
, epix2pro51mm
, etrextouch
, fenix5plus
, fenix5splus
, fenix5xplus
, fenix6
, fenix6pro
, fenix6s
, fenix6spro
, fenix6xpro
, fenix7
, fenix7pro
, fenix7pronowifi
, fenix7s
, fenix7spro
, fenix7x
, fenix7xpro
, fenix7xpronowifi
, fenix843mm
, fenix847mm
, fenix8pro47mm
, fenix8solar47mm
, fenix8solar51mm
, fenixe
, fr165
, fr165m
, fr170
, fr170m
, fr245
, fr245m
, fr255
, fr255m
, fr255s
, fr255sm
, fr265
, fr265s
, fr55
, fr57042mm
, fr57047mm
, fr645m
, fr70
, fr745
, fr945
, fr945lte
, fr955
, fr965
, fr970
, gpsmap66
, gpsmap67
, gpsmaph1
, instinct2
, instinct2s
, instinct2x
, instinct3amoled45mm
, instinct3amoled50mm
, instinct3solar45mm
, instinctcrossover
, instinctcrossoveramoled
, instincte40mm
, instincte45mm
, legacyherocaptainmarvel
, legacyherofirstavenger
, legacysagadarthvader
, legacysagarey
, marq2
, marq2aviator
, marqadventurer
, marqathlete
, marqaviator
, marqcaptain
, marqcommander
, marqdriver
, marqexpedition
, marqgolfer
, montana7xx
, venu
, venu2
, venu2plus
, venu2s
, venu3
, venu3s
, venu441mm
, venu445mm
, venud
, venusq2m
, venusqm
, venux1
, vivoactive3m
, vivoactive3mlte
, vivoactive4
, vivoactive4s
, vivoactive5
, vivoactive6
.
| Problem | Solution |
|---|---|
| Watch can't find daemon | Run oh-my-wrist status — verify daemon is advertising and that desktop/watch connection IDs match. Restart with oh-my-wrist stop && oh-my-wrist start . From a source checkout, run python tools/check_connection.py with the watch app open to exercise live HISTORY, stats, and usage updates. |
| Garmin app does not connect to daemon on PC | Make sure your Bluetooth adapter is supported. Some adapters or OS settings, such as Windows random MAC behavior, can prevent stable BLE connections. If problems persist, macOS or Linux is preferred. |
| Garmin app does not start on watch | Create an empty /GARMIN/Apps/Logs/oh-my-wrist-YOUR_WATCH_ID.log (e.g oh-my-wrist-fenix7x.log) file on the device, run the app, wait for the issue to occur, reconnect the device to your PC, download log file, then create a GitHub issue with the log for debugging. |
| Hook not firing | Run oh-my-wrist status — confirm hooks in ~/.claude/settings.json . Re-run oh-my-wrist install . |
| OpenCode not updating | Check plugin: oh-my-wrist opencode status . Re-install: oh-my-wrist opencode install . |
| BLE permission errors | See your platform guide: |
oh-my-wrist start --foreground
to see the error. Check oh-my-wrist logs
.
oh-my-wrist uninstall
pip uninstall oh-my-wrist
To remove the watch app, delete it via Garmin Express or the Connect IQ app on your phone.
Contributions are more than welcome. Please contribute! See CONTRIBUTING.md for guidelines.
pip install -e ".[dev]"
pytest tests/
uv venv && uv pip install -e ".[dev]"
uv run pytest
oh-my-wrist start --foreground
This project is licensed under the BSD 3-Clause License. See the LICENSE file for details.