Coding-agent hooks
Hooks convert coding-agent events into local status updates and authenticated Web summaries. The Desktop App displays local activity and selected cloud accounts. Provider credentials remain on the source machine.
Supported Platforms
| Platform | Character | Description |
|---|---|---|
| Claude Code | clawd | Anthropic's CLI for Claude |
| Codex CLI | codex | OpenAI's CLI for Codex |
| Kiro IDE | kiro | Amazon's AI coding assistant |
| OpenClaw | claw | Open source AI gateway |
| opencode | vibemon | Sends opencode; the current registry renders the default character |
Installation
Follow the installation guide for Desktop, headless, and Windows instructions, scoped write tokens, integrity checks, repair, and uninstall. This reference covers configuration and event mappings after installation.
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; POSIX only, ignored on Windows) | "/dev/cu.usbmodem*" |
vibemon_url |
VibeMon cloud API URL | https://vibemon.io |
vibemon_token |
Generated write token (from /account) |
Claude Code's statusline reads a separate ~/.vibemon/statusline.json for display toggles (e.g. show_cost, show_git, show_model, show_tokens) — 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 a recent local session snapshot. This fallback reads at most the newest 1 MiB of up to eight recent logs, preserves the observation timestamp, and does not renew stale usage by rewriting the cache. 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 provider/account-specific 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 in Codex and review/trust the new definitions. Codex skips new or changed non-managed hooks until their current definition is trusted.
Kiro IDE 1.x and CLI 3.x load VibeMon from the global v1 hook file at ~/.kiro/hooks/vibemon.json, so no custom agent needs to be selected. During upgrades, the installer removes only VibeMon's legacy hooks from ~/.kiro/agents/default.json and its old .kiro.hook files; neighboring user hooks are preserved.
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. --status works with every
monitor target. --lock, --unlock, and --lock-mode are attempted
against every configured HTTP target first (a Desktop app that doesn't
expose the endpoint is skipped) and then over serial; --reboot targets
the ESP32 only and skips the Desktop app:
# 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
On Windows PowerShell, run the same commands as
python "$env:USERPROFILE\.claude\hooks\vibemon.py" --status — python3 and
~ in argument position don't resolve there.
Apps
Desktop App
Electron app with system tray for macOS, Windows, Linux. See Installation above to install.
Read tokens are configured in the desktop Monitoring window; hook write tokens use the hook configuration.
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
ESP32 firmware is outside this overhaul. Local serial integration is unchanged. See the firmware repository for hardware setup. The retired cloud WebSocket endpoint is unavailable.
API
Hooks deliver coding activity to the Status API. Cloud calls require a generated write token. The Monitoring API defines account identity, resource summaries, selected-source reads, and trends. Local Desktop endpoints use a separate HTTP API.
State Mapping
State reporting is edge-driven: a new state start replaces the previous state. Completion hooks are retained only where no later start event reliably restores the state (PostToolUse/PostCompact) or an explicit turn/session ending must be reported (Stop/SessionEnd). Claude and Codex subagent Agent calls already pass through the matcher-free tool hooks, so separate subagent hooks would only duplicate those transitions.
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. PreToolUse, PostToolUse, and PermissionRequest are registered without a matcher, so every supported local tool call (Bash, apply_patch/Edit/Write, MCP tools, and other local function tools) is observed. Informational hooks run in the background, while SessionEnd and Interrupt run synchronously with Codex's three-second maximum timeout. done indicates that activity ended, including cancellation or failure; it does not assert task success.
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 (success or failure) | 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 |
OpenClaw reads the active model and context usage from llm_output, resets them per run, and tracks parallel runs and tools. USB writes use the shared Python transport and file lock; each HTTP target serializes updates, coalesces pending states, and has a 2.5-second request deadline.
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
Resumed OpenCode sessions use the project instance directory and resolved chat model. Known child sessions do not publish independent status because the parent task tool represents them. Each adapter process has a 10-second deadline so a stalled child cannot stop the queue.
Related Projects
- VibeMon Web - Cloud dashboard and API
- vibemon-app - Desktop App
- Public App source - Hooks, collectors, registries, renderers, and character images