VibeMon normalizes multiple agent ecosystems into one display model. The rendering layer is shared, but the integration path is not.
| Agent | Integration path | Best signal source | Observability quality | Important limitation |
|---|---|---|---|---|
| Claude Code | Native hooks | Session, turn, and tool hooks | High | None significant for basic monitoring |
| Codex | Native hooks and non-interactive JSON output | Interactive lifecycle and local tool hooks, codex exec --json for automation |
High | Hosted tools such as WebSearch do not pass through local tool hooks |
| Kiro | Native hooks | Prompt, tool, and stop hooks | High | Fewer lifecycle events than Claude Code |
| OpenClaw | Plugin bridge | Plugin SDK hooks | Medium to high | Internal hooks are not enough by themselves for full tool-loop visibility |
start, thinking, working, notification, packing, and done.PreToolUse and PostToolUse hooks observe shell commands, apply_patch, MCP tools, and other local function tools; codex exec --json remains useful for CI or batch jobs.PreToolUse and PostToolUse, plus namespaced MCP tool names.| Character | Color | Description | Auto-selected for |
|---|---|---|---|
vibemon |
Purple | Robot with antenna, default character | Any bridge without its own character |
clawd |
Orange | Four-legged friend | Claude Code |
codex |
Navy | Cloud character with light eyes on a dark screen | Codex CLI |
kiro |
White | Ghost character | Kiro |
claw |
Red | Antenna character | OpenClaw |
daangni |
Peach/teal | Round face, fluffy top | Manual only (Character Lock) |
In 2D (default) all characters use image-based rendering (128x128 PNG). Images load remote-first from static.vibemon.io, with copies bundled in src/assets/characters/ as the offline fallback. Character is auto-selected by bridge, not by the core display runtime. You can also force one with Character Lock.
Characters are defined in a single registry canonically hosted in vibemon-static (served at static.vibemon.io/data/characters.json, with src/shared/data/characters.json bundled as the fallback and kept in sync via npm run check:registry): display name, accent color (the eye/accent overlay drawn on the sprite — white for VibeMon, distinct from the “Color” appearance above), image file, eye/effect coordinates (in canvas pixels on the 128x128 sprite, adjustable at 1px), and the theme palette the 3D engine paints. The character window, tray icon (downscaled from the same PNG), menus, and validation all derive from it — adding a character is one PNG plus one registry entry in vibemon-static, no code change here.
Settings → Character → Render Mode switches the character window between the two engines; the choice persists and the open window reloads into it.
2d (default): the pixel-art sprite described above3d: a procedurally rendered pet (three.js) — no images are used. The rig is the same for every character; each one is tinted by its registry theme (body/belly/accent/eye/blush/flame), so Character Lock and per-project switching behave identically to 2D.Both engines are vendored from vibemon-static (src/engine/); three.js ships locally in src/vendor/ because the renderer CSP forbids runtime CDN imports.
Forces the character window to always show one character, ignoring whatever character each project’s status reports.
auto (default): each project shows its own characterPOST /character-lockauto doesn’t retroactively fix the open window — it picks up each project’s real character again on its next status updateThe state drives the character’s eyes/effects on the sprite, the speech
bubble’s background color, and the tray icon’s background color. States
are defined in a single registry (src/shared/data/states.json): bubble
color/text, focus and loading behavior, and eye/effect type all live in
one entry per state.
| State | Color | Eyes | Bubble text | Trigger |
|---|---|---|---|---|
start |
Cyan | ■ ■ + ✦ | Hello! | Session begins |
idle |
Green | ■ ■ | Ready | Waiting for input |
thinking |
Purple | ▀ ▀ + 💭 | Thinking | User submits prompt |
planning |
Teal | ▀ ▀ + 💭 | Planning | Plan mode active |
working |
Blue | 👓 (glasses) | (tool-based) | Tool executing |
packing |
Gray | ▀ ▀ + 💭 | Packing | Context compacting |
notification |
Yellow | ● ● + ? | Input? | User input needed |
done |
Green | > < | Done! | Tool completed |
sleep |
Navy | ─ ─ + Z | Zzz… | 5min inactivity |
alert |
Red | ■ ■ + ! | Alert | Critical error/failure |
The working state’s speech bubble shows fixed text based on the active tool:
| Tool | Text |
|---|---|
| Bash | Running |
| Read | Reading |
| Edit | Editing |
| Write | Writing |
| Grep / WebSearch | Searching |
| Glob | Scanning |
| WebFetch | Fetching |
| Task | Tasking |
| Default | Working |
| From State | Timeout | To State |
|---|---|---|
start, done |
1 minute | idle |
planning, thinking, working, packing, notification, alert |
5 minutes | idle |
idle |
5 minutes | sleep |
After 10 minutes in sleep state, the window automatically closes. It reappears on the next status update.
The app shows exactly one character window plus its following speech bubble:
alert/notification from another project switch immediately; once the focused project settles, the most recently updated project takes over.| Mode | Description |
|---|---|
all |
The window stays on top regardless of state - Default |
active-only |
Only active states (thinking, planning, working, packing, notification, alert) stay on top |
disabled |
The window never stays on top |
When active-only is selected:
Change via system tray menu: Always on Top → Select mode
When running Claude Code in multiple terminal tabs, clicking the character window automatically switches to the corresponding terminal tab.
Supported Terminals:
Requirements:
A small, transparent, click-through window that displays selected info fields (status, project name, model, memory, 5h usage, weekly usage, model-scoped weekly usage — e.g. “Fable 12% · 4d11h”) next to the character. Each plan-usage row shows its own reset countdown when available. Positioned automatically so it never overlaps the character window and stays on-screen, with an animated slide when it needs to move.
A dedicated settings window (tray menu → Settings…) with four tabs in a sidebar:
vibemon_token into ~/.vibemon/config.json), plus Config (HTTP URLs, Serial Port, VibeMon URL, Debug Logging, Auto-launch Desktop App) read and written directly by the app — no python installer neededChanges apply immediately through the same code paths as the tray menu, and the window re-syncs when refocused so tray-made changes are reflected.
The account token is never returned to the settings renderer after it is saved; the UI only receives whether a token is configured. WebSocket authentication sends the token both as a connection URL query parameter (required by the deployed relay, which authorizes at the HTTP upgrade) and in a protocol auth message after connecting.
Grouped to mirror the Settings window’s tab order (VibeMon / Collector / AI Tools / About):
The character is rendered by a bundled engine (src/engine/vibemon-engine.js): a 128x128 canvas drawing the character PNG, state-driven pixel-art eyes/effects, and the floating animation, over a fully transparent background. Character images load remote-first from static.vibemon.io, falling back to the bundled copies in src/assets/characters/ — rendering works fully offline.
Hook installation verifies the downloaded installer against the installer SHA-256 published in the same origin’s manifest.json (fetched fresh at install time), so install.py updates ship with a vibemon-docs deploy alone — no app release needed. install.py then re-checks every file it downloads against that same manifest before writing it, so install.py and manifest.json have to go out in the same deploy. Custom installer deployments can pin a specific hash via VIBEMON_INSTALLER_SHA256 together with VIBEMON_DOCS_URL; the pin takes precedence over the manifest. VIBEMON_DOCS_URL only redirects where install.py itself is fetched from — the installer always pulls the files it installs, and the manifest it checks them against, from docs.vibemon.io.
Installs run unattended but not force-approved: the app passes a platform flag and never --yes. VibeMon’s own hook scripts are upgraded in place (so Reinstall still repairs drift), while settings you own — most visibly an existing Claude Code statusLine — are left as they are. Run install.py yourself with --yes to have those replaced too. When a run fails, the installer’s own reason (a failed integrity check, a file it couldn’t write) is shown with the exit code instead of the bare code.
Detection and hook paths honor CLAUDE_CONFIG_DIR, CODEX_HOME, and KIRO_HOME. Kiro is detected through either kiro or kiro-cli.
npm run build:mac # macOS (DMG, ZIP)
npm run build:win # Windows (NSIS, Portable)
npm run build:linux # Linux (AppImage, DEB)
npm run build:all # All platforms