VibeMon

Real-time AI Status Monitor

Display your AI coding assistant's state on ESP32 devices, Desktop App, or cloud service. See when Claude, Codex, Kiro, or OpenClaw is thinking, working, or done.

Go to VibeMon.io
💻
Install the VibeMon App

Recommended — desktop viewer that also sets up your AI tools for you

$ brew tap opspresso/tap
$ brew install opspresso/tap/vibemon

Install with Homebrew, or download the .dmg from the latest release.

Then open Settings > AI Tools to connect Claude Code, Codex, Kiro, or OpenClaw with one click.

🤖
I'm an AI Agent

I want to connect to VibeMon and send my status

Tell your AI agent:

Read https://docs.vibemon.io/setup.md and follow the instructions to join VibeMon

Installation

VibeMon App (Recommended)

Install the desktop app, then let it configure everything else for you — no separate script needed.

brew tap opspresso/tap
brew install opspresso/tap/vibemon

Or via npm: npx vibemon@latest

Open the app, go to Settings > AI Tools, and click Install for Claude Code, Codex CLI, Kiro IDE, or OpenClaw.

Non-interactive Install (AI agents, CI)

For headless setups where a GUI app isn't available:

# Choose exactly one platform + your token
curl -fsSL https://docs.vibemon.io/install.py | python3 - --claude --token my_token
# --codex / --kiro / --openclaw for other tools, --all for every detected tool

Interactive prompt version:

curl -fsSL https://docs.vibemon.io/install.py | python3

Or point your AI agent at docs/setup.md (https://docs.vibemon.io/setup.md) and have it follow the instructions directly.

Local Install

git clone https://github.com/opspresso/vibemon-docs.git
cd vibemon-docs
python3 docs/install.py

Configuration

After installation, edit ~/.vibemon/config.json to configure your targets:

{
  "debug": false,
  "cache_path": "~/.vibemon/cache/projects.json",
  "auto_launch": true,
  "http_urls": [],
  "serial_port": null,
  "vibemon_token": "",
  "vibemon_url": "https://vibemon.io"
}
FieldDescriptionExample
debugEnable debug loggingtrue
cache_pathCache file path for project metadata~/.vibemon/cache/projects.json
auto_launchAuto-launch Desktop App on session starttrue
http_urlsHTTP targets (Desktop App, ESP32 WiFi)["http://127.0.0.1:19280"]
serial_portESP32 USB serial port (wildcard supported)/dev/cu.usbmodem*
vibemon_urlVibeMon cloud API URLhttps://vibemon.io
vibemon_tokenVibeMon API access token (from dashboard)

Claude Code's statusline reads a separate ~/.vibemon/statusline.json for display toggles (e.g. show_cost, show_git, show_model, show_tokens) and the fallback token_reset_hours setting — see statusline.example.json for the full set of defaults. This file is optional; statusline.py falls back to sensible defaults (and to any matching keys still in config.json) when it's absent.

The Claude Code installer also places a standalone refresher at ~/.vibemon/usage.py. For Claude, it fetches plan usage directly from Anthropic's OAuth usage API using the local Claude Code login token (no active session required), falling back to a claude -p "/usage" subprocess when a token isn't available or the API call fails. For Codex, it queries the same account-level usage API Codex CLI's own /status polls using the local Codex login token, falling back to the newest local session log. Either way it writes the shared ~/.vibemon/cache/usage.json, so the Desktop app can run it (python3 ~/.vibemon/usage.py --max-age 600) on startup or on a schedule to keep usage data fresh even when no Claude Code or Codex session is active. Since the claude -p "/usage" fallback is itself a real Claude Code session, the Desktop app sets VIBEMON_SUPPRESS_HOOKS=1 in its environment so the spawned session's own hooks don't report status back. Independently of that env var, the hooks also skip any session whose cwd is ~/.vibemon itself, so spawners that don't set the variable (older Desktop app versions, manual usage.py runs) can't surface a phantom .vibemon project either.

The reset-countdown fields the hooks attach (usage5hResetsIn/usageWeekResetsIn) are populated whenever the cache was refreshed via a resets_at epoch — either an active Claude Code session's statusline (the official rate_limits path), usage.py's direct Anthropic/Codex API queries, or a Codex session log. Only the last-resort claude -p "/usage" text fallback lacks a machine-parseable reset time, so the reset countdown is omitted in that case while the usage percentages still update.

Note that the plan-usage fields (usage5h/usageWeek and their reset countdowns) are sent by the Claude Code and Codex hooks, since they both read from the same usage.py-refreshed cache (under separate claude/codex cache keys). The Kiro hook doesn't report usage, and OpenClaw reports context-window usage as memory instead.

Codex Configuration

Codex uses the same ~/.vibemon/config.json as Claude Code and Kiro. Enable Codex hooks in ~/.codex/config.toml:

[features]
hooks = true

Then merge codex/hooks.json into your existing ~/.codex/hooks.json (do not overwrite). Codex hooks are experimental, and Codex CLI's own hooks documentation currently lists Windows as unsupported.

OpenClaw Configuration

The OpenClaw plugin reads transmission settings (http_urls, serial_port, vibemon_url, vibemon_token) from the same ~/.vibemon/config.json as the other tools. It only needs to be registered and enabled in ~/.openclaw/openclaw.json — OpenClaw doesn't auto-discover extension directories, so the plugin path must also be registered under plugins.load.paths or the manifest/entries config alone won't load it:

{
  "plugins": {
    "load": {
      "paths": ["~/.openclaw/extensions/vibemon-bridge"]
    },
    "entries": {
      "vibemon-bridge": {
        "enabled": true,
        "hooks": { "allowConversationAccess": true }
      }
    }
  }
}

To override the shared settings for OpenClaw only, add a config object to the entry (projectName, character, httpEnabled, httpUrls, serialEnabled, vibemonUrl, vibemonToken, autoLaunch, debug) — plugin config always wins over ~/.vibemon/config.json.

After installing or updating the plugin, rebuild OpenClaw's persisted plugin registry and restart the gateway (openclaw plugins registry --refresh && openclaw gateway restart) — the gateway boots from a registry snapshot and won't pick up the plugin's hooks otherwise. The installer runs the refresh automatically when the openclaw CLI is available.

CLI Commands

The hook script supports these commands:

# Lock monitor to current project
python3 ~/.claude/hooks/vibemon.py --lock [project_name]

# Unlock monitor
python3 ~/.claude/hooks/vibemon.py --unlock

# Get current status
python3 ~/.claude/hooks/vibemon.py --status

# Get/set lock mode (first-project, on-thinking)
python3 ~/.claude/hooks/vibemon.py --lock-mode [mode]

# Reboot ESP32 device
python3 ~/.claude/hooks/vibemon.py --reboot

Apps

Desktop App

Electron app with system tray for macOS, Windows, Linux. See Installation above to install.

Token can be configured via the system tray menu.

It shows a single character window with a speech bubble that follows it. The window retargets to whichever project is currently active instead of opening one window per project.

Features: frameless floating window, always on top, system tray integration, snap to screen corners, click to focus terminal (macOS).

ESP32 Hardware

Dedicated LCD display (172×320, ST7789V2). Hardware: ESP32-C6-LCD-1.47 board, USB-C cable.

Required libraries: LovyanGFX (lovyan03), ArduinoJson (Benoit Blanchon), WebSockets (Markus Sattler, for WebSocket mode).

Arduino IDE setup: add the ESP32 Board Manager URL below, install the ESP32 board and required libraries, select the ESP32C6 Dev Module, then upload.

https://raw.githubusercontent.com/espressif/arduino-esp32/gh-pages/package_esp32_index.json

WiFi configuration (credentials.h):

#define USE_WIFI
#define WIFI_SSID "YOUR_SSID"
#define WIFI_PASSWORD "YOUR_PASSWORD"

Optional WebSocket mode:

#define USE_WEBSOCKET
#define WS_HOST "ws.vibemon.io"
#define WS_PORT 443
#define WS_PATH "/"
#define WS_USE_SSL true
#define WS_TOKEN "your-access-token"

For SSL, change the Partition Scheme to "Huge APP (3MB No OTA/1MB SPIFFS)".

Testing via serial:

# macOS
echo '{"state":"working","tool":"Bash","project":"my-project"}' > /dev/cu.usbmodem1101

# Linux (set baud rate first)
stty -F /dev/ttyACM0 115200
echo '{"state":"working","tool":"Bash","project":"my-project"}' > /dev/ttyACM0

API

WebSocket

wss://ws.vibemon.io?token=your-access-token

Message types:

// Status update
{
  "type": "status",
  "data": {
    "state": "working",
    "tool": "Bash",
    "project": "my-project",
    "model": "opus",
    "memory": 45,
    "character": "clawd",
    "usage5h": 36,
    "usageWeek": 37,
    "usage5hResetsIn": 154,
    "usageWeekResetsIn": 4381,
    "createdAt": "2026-01-01T00:00:00.000Z",
    "updatedAt": "2026-01-01T00:00:00.000Z"
  }
}

// Project deleted
{
  "type": "delete",
  "data": { "project": "my-project" }
}

HTTP API

curl -X POST https://vibemon.io/api/status \
  -H "Authorization: Bearer your-token" \
  -H "Content-Type: application/json" \
  -d '{
    "state": "working",
    "project": "my-project",
    "character": "clawd",
    "tool": "Bash",
    "model": "opus",
    "memory": 45
  }'
FieldTypeDescription
statestringstart, idle, thinking, planning, working, packing, notification, done, sleep, alert (required)
projectstringProject name (required)
characterstringvibemon, clawd, codex, kiro, claw, or daangni (required; an unrecognized value falls back to vibemon rather than being rejected). daangni is manual selection only (no tool maps to it)
toolstringTool name (Bash, Read, Edit, etc.) (optional)
modelstringModel name (opus, sonnet, etc.) (optional)
memorynumberContext window usage 0-100 (optional)
usage5h / usageWeeknumberPlan-usage percentage 0-100 (optional; Claude Code and Codex hooks only — see above)
usage5hResetsIn / usageWeekResetsInnumberMinutes until the usage window resets (optional; see above)
usageWeekModelnumberModel-scoped weekly plan-usage percentage 0-100, e.g. the Fable weekly limit (optional)
usageWeekModelResetsInnumberMinutes until the model-scoped weekly window resets (optional)
usageWeekModelLabelstringDisplay label of the scoped model, e.g. "Fable" (optional)
# Delete agent status
curl -X DELETE "https://vibemon.io/api/status?project=my-project" \
  -H "Authorization: Bearer your-token"

# Aggregated metrics
curl "https://vibemon.io/api/metrics?granularity=HOUR&range=24h" \
  -H "Authorization: Bearer your-token"

Token format: a-z, 0-9, _, -, 8-64 characters (e.g. my_token_123).

State Mapping

Claude Code

EventState
SessionStartstart
UserPromptSubmitthinking
PreToolUseworking
SubagentStartworking
PreCompactpacking
Notificationnotification
PermissionRequestnotification
SessionEnddone
Stopdone

Plan Mode: when Claude Code is in plan mode, thinking and working states automatically become planning.

Codex CLI

EventState
SessionStartstart
UserPromptSubmitthinking
PreToolUse (Bash only)working
SubagentStartworking
PermissionRequest (Bash only)notification
PostToolUse (Bash only)thinking
SubagentStopthinking
PreCompactpacking
PostCompactthinking
Stopdone

PreToolUse, PermissionRequest, and PostToolUse are registered with a Bash-only matcher (see docs/codex/hooks.json), so non-Bash tool calls (Read, Edit, etc.) don't trigger a state change.

Codex hooks are experimental, and Codex CLI's own hooks documentation currently lists Windows as unsupported.

Kiro IDE

EventState
agentSpawnstart
promptSubmit / userPromptSubmitthinking
fileCreated / fileEdited / fileDeletedworking
preToolUseworking
postToolUsethinking
agentStop / stopdone

OpenClaw

EventState
gateway_startstart
before_agent_startthinking
before_tool_callworking
after_tool_callthinking
message_sent / agent_enddone (3s delay)
session_end / gateway_stopdone