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, OpenClaw, or opencode.
The installer and Desktop App honor CLAUDE_CONFIG_DIR, CODEX_HOME, KIRO_HOME, and OPENCODE_CONFIG_DIR (default $XDG_CONFIG_HOME/opencode, falling back to ~/.config/opencode). Hook commands are rewritten to the resolved directory, and Kiro is detected through either kiro or kiro-cli.
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 / --opencode 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"
}
| Field | Description | Example |
|---|---|---|
debug | Enable debug logging | true |
cache_path | Cache file path for project metadata | ~/.vibemon/cache/projects.json |
auto_launch | Auto-launch Desktop App on session start | true |
http_urls | HTTP targets (Desktop App, ESP32 WiFi) | ["http://127.0.0.1:19280"] |
serial_port | ESP32 USB serial port (wildcard supported) | /dev/cu.usbmodem* |
vibemon_url | VibeMon cloud API URL | https://vibemon.io |
vibemon_token | VibeMon 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/usageWeekModelResetsIn) 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/usageWeekModel 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 and opencode hooks don't report usage, and OpenClaw reports context-window usage as memory instead.
Codex Configuration
Codex uses the same ~/.vibemon/config.json as Claude Code, Kiro, the OpenClaw plugin, and the opencode plugin. Hooks are enabled by default; the installer preserves an explicit [features].hooks = false in ~/.codex/config.toml. Merge codex/hooks.json into your existing ~/.codex/hooks.json (do not overwrite), then open /hooks and review/trust the new definitions.
Kiro Configuration
Kiro IDE 1.x and CLI 3.x discover the global v1 hook at ~/.kiro/hooks/vibemon.json. It applies to every local project without selecting a custom agent. The installer removes only VibeMon's legacy agent hooks and .kiro.hook files during migration.
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.
opencode Configuration
The opencode plugin reads transmission settings (http_urls, serial_port, vibemon_url, vibemon_token) from the same ~/.vibemon/config.json as the other tools. opencode has no Claude Code-style hooks, so the plugin at ~/.config/opencode/plugins/vibemon.js bridges opencode events to VibeMon's hook pipeline: it spawns the adapter at ~/.config/opencode/hooks/vibemon.py, which feeds vibemon_core.py. opencode auto-discovers plugins in ~/.config/opencode/plugins/ at startup, so no config registration is needed — install the plugin file and restart opencode.
The installer honors an OPENCODE_CONFIG_DIR override (default $XDG_CONFIG_HOME/opencode, falling back to ~/.config/opencode); on Windows it also pins the plugin's interpreter to the Python that ran the installer, since python3 isn't on PATH there.
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
}'
| Field | Type | Description |
|---|---|---|
state | string | start, idle, thinking, planning, working, packing, notification, done, sleep, alert (required) |
project | string | Project name (required) |
character | string | vibemon, clawd, codex, kiro, claw, opencode, or daangni (required; an unrecognized value falls back to vibemon rather than being rejected). daangni is manual selection only (no tool maps to it) |
tool | string | Tool name (Bash, Read, Edit, etc.) (optional) |
model | string | Model name (opus, sonnet, etc.) (optional) |
memory | number | Context window usage 0-100 (optional) |
usage5h / usageWeek | number | Plan-usage percentage 0-100 (optional; Claude Code and Codex hooks only — see above) |
usage5hResetsIn / usageWeekResetsIn | number | Minutes until the usage window resets (optional; see above) |
usageWeekModel | number | Model-scoped weekly plan-usage percentage 0-100, e.g. the Fable weekly limit (optional) |
usageWeekModelResetsIn | number | Minutes until the model-scoped weekly window resets (optional) |
usageWeekModelLabel | string | Display 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
State reporting is edge-driven: a new state start replaces the previous state. Completion hooks are retained only when no later start reliably restores the state (PostToolUse/PostCompact) or an explicit ending must be reported (Stop/SessionEnd). Claude and Codex subagent Agent calls already pass through the tool hooks, so separate subagent hooks are omitted.
Claude Code
| Event | State |
|---|---|
SessionStart | start |
UserPromptSubmit | thinking |
PreToolUse | working |
PostToolUse | thinking |
PostToolUseFailure | thinking |
PermissionDenied | thinking |
PreCompact | packing |
PostCompact | thinking |
Notification | notification |
PermissionRequest | notification |
SessionEnd | done |
Stop | done |
StopFailure | done |
Plan Mode: when Claude Code is in plan mode, thinking and working states automatically become planning.
Codex CLI
| Event | State |
|---|---|
SessionStart | start |
UserPromptSubmit | thinking |
PreToolUse | working |
PostToolUse | thinking |
PermissionRequest | notification |
PreCompact | packing |
PostCompact | thinking |
Stop | done |
Interrupt | done |
SessionEnd | done |
SessionStart covers startup, resume, and clear. Matcher-free PreToolUse/PostToolUse hooks observe every supported local tool call. Informational hooks run in the background; SessionEnd and Interrupt run synchronously with a three-second timeout.
After installation, open /hooks and review/trust new or changed VibeMon hook definitions.
Kiro IDE
| Event | State |
|---|---|
SessionStart | start |
UserPromptSubmit | thinking |
PreToolUse | working |
PostToolUse | thinking |
Stop | done |
OpenClaw
| Event | State |
|---|---|
gateway_start | start |
before_agent_run (fallback: before_agent_start) | thinking |
before_tool_call | working |
after_tool_call | thinking (working while another tool runs) |
before_compaction / after_compaction | packing / thinking |
subagent_spawned | working |
agent_end (success or failure) | done (3s delay, after all runs finish) |
message_sent | done fallback (only with no active run) |
session_end / gateway_stop | done |
opencode
| Event | State |
|---|---|
SessionStart | start |
UserPromptSubmit | thinking |
PreToolUse | working |
PostToolUse | thinking |
PermissionRequest | notification |
PreCompact | packing |
PostCompact | thinking |
Stop | done |
SessionEnd | done |
opencode has no Claude Code-style hooks, so the plugin bridges its events to these hook events:
session.created→ SessionStartchat.message→ UserPromptSubmittool.execute.before→ PreToolUsetool.execute.after→ PostToolUsepermission.asked(bus event; legacypermission.askhook) → PermissionRequestexperimental.session.compacting→ PreCompactsession.compacted→ PostCompactsession.status(busy/retry) → UserPromptSubmitsession.idle,session.status(idle), orsession.error→ Stopsession.deleted→ SessionEnd