macOS menu-bar mission-control for your Claude Code sessions — see, hear and reply to every agent so none sits idle waiting on you. Rust + Tauri, event-driven on native hooks, fully local.
Rust
6
269 commits
updated Oct 18, 2026
English · Русский
Mission control for your coding agents, right in the menu bar / tray.
Jarvis watches every Claude Code, Codex and Kimi session you run — and tells you, even out loud, the moment one needs you.
🖼 Screenshot and demo coming soon. What to capture and how to wire it in: docs/assets/README.md.
Running several coding agents at once turns you into the bottleneck. Sessions are spread across terminals, tabs and macOS Spaces, and you can't see at a glance which agent is blocked on a permission prompt, which finished half an hour ago and is idling while you think it's working, and which hit a rate limit. The agents' own notifications are per-session and terminal-scoped (they don't even fire in the VS Code extension) — there is no single pane showing the aggregate state. And a sleeping Mac silently freezes agent processes and severs in-flight API requests, killing long overnight runs; on Apple Silicon, closing the lid forces a sleep that plain caffeinate can't prevent.
Jarvis is that missing single pane. It sits in the menu bar, watches every interactive Claude Code, Codex and Kimi Code CLI session on your machine, and makes sure no agent ever waits on you silently:
/ command palette included.git worktree), a per-item fan-out over a list computed at runtime, and a join that merges them back. The graph exports to BPMN 2.0, is edited in Camunda Modeler and comes back without losses (docs/pipelines.md).caffeinate equivalent) plus a guarded closed-lid mode for overnight runs.TodoWrite progress (done / in-progress / queued) per session.Boundary: Jarvis is a monitor and a remote, not an orchestrator. It does not spawn agents and does not own the plan; it complements orchestrators (Claude Squad, Conductor, Crystal, native Agent Teams) and the sessions you already run.
The panel is one of three ways into the same fleet — not a client/server split.
Each program talks to the agents' own hooks, and the ones on one machine share
one data directory (~/.jarvis): a loop started in the terminal shows up in the
panel, and a bundle started in the panel is driven from the terminal. The phone
is deliberately narrower — sessions, chat and replies, nothing else.
| What it is | Where | |
|---|---|---|
| Jarvis | the panel: menu bar / tray, toasts over fullscreen, voice, dictation, usage | this repo |
| jarvis-cli | the same fleet from a terminal: live TUI, chat, loops, bundles — over SSH, inside tmux, no windows | arklual/jarvis-cli |
| jarvis-mobile | Android client for agents that run on other machines: see, read, reply | arklual/jarvis-mobile |
| jarvis-node | the thin node on the remote machine: buffers hook events, serves them over SSH | src-tauri/node, in this repo |
The panel and the CLI are two faces of one local state, so use whichever is in
front of you. The phone never touches ~/.jarvis — it talks to jarvis-node over
your own SSH, which is exactly what lets it work while the laptop is closed
(docs/remote.md). The node is also what the panel and the CLI
reach for when an agent runs on a VPS.
Jarvis is a power-user tool. It pays off once you cross the pain threshold of 3+ parallel agent sessions:
It's probably not for you if you run one session in one terminal (native notifications may be enough), or if you're looking for something to spawn and plan agents — pair Jarvis with an orchestrator for that.
Platforms. macOS and Linux. macOS is the primary target and the only one with prebuilt releases; on Linux you build from source — setup and the list of platform differences live in
docs/linux.md.
Download Jarvis_x.y.z_aarch64.dmg from the releases page.
Open the DMG, drag Jarvis into Applications, launch it.
First launch: the build is ad-hoc signed (no Apple Developer ID yet, not notarized), so macOS will warn about an unidentified developer. Right-click the app → Open → Open. If macOS claims the app is "damaged", clear the quarantine flag:
xattr -dr com.apple.quarantine /Applications/Jarvis.app
On first launch Jarvis offers to set up the Claude Code / Codex / Kimi integration and download voice models — click «Настроить» ("Set up"); progress is shown step by step in the window.
The app checks for updates itself (built-in updater). You can reinstall the integration anytime: menu bar → "Reinstall integration…".
# Debian / Ubuntu — see docs/linux.md for Fedora and Arch
sudo apt install tmux libwebkit2gtk-4.1-dev libgtk-3-dev libayatana-appindicator3-dev \
librsvg2-dev libasound2-dev libxdo-dev patchelf cmake
# Arch (the port's main bench: Arch + Sway)
# sudo pacman -S tmux webkit2gtk-4.1 gtk3 libayatana-appindicator librsvg \
# alsa-lib patchelf cmake wtype wl-clipboard playerctl
npm ci
npm run setup # hooks into ~/.claude/settings.json
npm run start:linux # build and launch
npm run bundle:linux # optional: .deb / .AppImage / .rpm
On Wayland (Sway, Hyprland, GNOME) global shortcuts belong to the compositor, not
to the app: bind them there — bindsym $mod+j exec jarvis --toggle — and add the
window rules from docs/sway/jarvis.conf, otherwise the
panel arrives as an ordinary tile. On X11 the panel hotkey is Ctrl+J: Super belongs to the desktop environment
(GNOME uses Super+1..4 for the dock). Full list of differences and of the
optional helpers (playerctl, wmctrl, xdotool) — docs/linux.md.
Requirements:
brew install tmux / apt install tmux);claude, codex, kimi) is found on the system.You need: Rust stable (rustup; the minimum version is the rust-version field in src-tauri/Cargo.toml), Node.js 20+, CMake, tmux. On Linux also the system libraries listed above.
npm ci
npm run setup # install hooks into ~/.claude/settings.json (backed up, idempotent)
npm start # build and launch the daemon + menu bar (◇ top-right) against the dev profile (~/.jarvis-dev)
(npm here is just a familiar runner: under the hood it's cargo build --release plus an ad-hoc codesign needed for microphone access.)
Then restart any active claude sessions — hooks are snapshotted at session start. Remove everything: npm run teardown. Check the status of each component: npm run status. Build a DMG locally (unsigned, host arch): npm run bundle. Run the prod profile: npm run start:prod.
Try it without Claude Code (the daemon must be running) — manually, the way a real hook does it:
echo '{"session_id":"t1","cwd":"'$PWD'"}' | ~/.jarvis/bin/jarvis-hook claude session-start
echo '{"session_id":"t1","cwd":"'$PWD'","prompt":"add tests"}' | ~/.jarvis/bin/jarvis-hook claude prompt
echo '{"session_id":"t1","cwd":"'$PWD'","message":"Claude needs your permission to use Bash"}' | ~/.jarvis/bin/jarvis-hook claude notification
echo '{"session_id":"t1","cwd":"'$PWD'"}' | ~/.jarvis/bin/jarvis-hook claude stop
Check the daemon is alive: curl -s --unix-socket ~/.jarvis/run.sock http://jarvis/
A registry of every interactive Claude Code / Codex / Kimi session: status (idle / working / waiting / finished / hit the limit), project, branch, model, activity. The menu-bar counter (◇ ⏸N ⚙M) is the cross-terminal summary; the hotkey panel is the detailed view.
No screen parsing — only structured events from the agents themselves, via their native hooks: SessionStart → idle, UserPromptSubmit → working, Notification → waiting (+a notification), Stop → done (+a notification), SessionEnd → the session disappears. A session can also be ended by hand — ⌘⇧⌫ or "Завершить сессию" in ⌘K: a live one loses its pane along with the agent, while a stuck one (the agent died, no SessionEnd arrived, the node is unreachable — the liveness sweep has nothing to judge by) is simply dropped from the list. The registry-reducer lives in the Rust daemon; state survives a daemon restart (~/.jarvis/state.json) and live tmux sessions are re-adopted. The pane's screen is read only by the interactive-prompt detector and the remote (a regex, not screenshots).
Jarvis's own toast notifications render over fullscreen; the panel (alwaysOnTop) does not steal focus. The global hotkey (⌘J on macOS, Ctrl+J on Linux) opens the panel centered on screen — Esc, a click outside, or the hotkey again closes it.
Click ◇ for the panel. Right-click for the menu (test notification, autostart, quit). The hotkey, panel position, notification toggles and autostart live in settings (⚙ in the panel header), stored in ~/.jarvis/settings.json. Notification content is composable from segments (branch · model · effort · tokens · duration) with a live preview; a separate toggle speaks notifications only when a Bluetooth headset is connected. Note: the global hotkey intercepts that combination across all apps (Chrome "downloads", VS Code "panel") — change it in settings if it gets in the way.
The interface is a sheet of paper: one opaque surface, one accent colour, and monochrome status dots (working — filled, finished — outline only, waiting — accent, rate-limited — dim, asleep — pale). The bottom strip carries a waveform and the line "say «jarvis» — I'm listening"; on the right, a subscription-limit meter with the window until reset.
Settings → Look: mode (overlay / window), theme (light / dark / system), paint (Coal · Clover · Raspberry, or your own — pick one hue and the rest is derived from it), and what the bottom strip shows — the subscription limit or the day's spend. Below that: row density, corner radius and interface scale (85–140%), with a reset button. In window mode Jarvis is an ordinary window: sessions on the left, the conversation on the right, the limit in the title bar; it resizes, minimises, does not vanish when you click away, and lives in the Dock (the icon appears on the next launch). Overlay is the familiar hotkey panel that floats above everything. Changes apply immediately across every window: panel, toasts, agent chat, onboarding. The typeface is Golos Text, bundled with the app so the UI renders identically with no network. The full visual language is documented in docs/superpowers/specs/2026-08-02-clover-redesign.md.
In a session's chat the Reply field inserts text straight into the session's terminal — even if the window is minimized or on another Space. Below it sits the remote: Model segments (Opus / Sonnet / Haiku) and Effort (auto / low / med / high / xhigh) send a slash command (/model sonnet, /effort high) into the live session. When an agent asks a multiple-choice question, the panel renders native pickers (multi-select included) instead of making you type numbers into a terminal.
npm run setup installs a PATH shim ~/.jarvis/shims/claude (pyenv pattern, a managed block in ~/.zshrc between jarvis markers). After exec zsh, every interactive claude launch is transparently wrapped in tmux on a separate server (-L jarvis, config ~/.jarvis/tmux.conf): no status bar, mouse scrolls, your personal tmux untouched. In iTerm2 — control mode (-CC), native tabs. Headless runs (-p, pipes, $TMUX) are not wrapped. Insertion: set-buffer → paste-buffer -p → send-keys Enter (multi-line prompts arrive as one chunk). The command palette (/ in the reply field) reaches the session's other slash commands.
claude --resume <session_id>).tmux -L jarvis attach -t <name>.If codex is found during setup, Jarvis wires it up too: hooks in ~/.codex/hooks.json and a codex shim using the same tmux mechanism. Interactive Codex sessions appear in the panel with a codex badge and get the same treatment — status, toasts, voice, chat, reply-into-session, and a resume command (codex resume <session_id>).
Model and reasoning are changed via Codex's own /model picker (there is no separate /effort). Headless codex exec does not fire hooks — such runs aren't monitored (by design, same as claude -p). On a fresh machine Codex may ask you to trust ~/.codex/hooks.json; Jarvis never injects the global --dangerously-bypass-hook-trust flag. Codex usage/cost numbers are estimates.
The third agent is Kimi Code CLI (kimi, Moonshot AI) — same deal: a kimi shim on the same tmux mechanism, a kimi badge in the panel, status, toasts, voice, chat, reply-into-session and a resume command (kimi -S <session_id>). Hooks live elsewhere, though: Kimi keeps them in [[hooks]] sections inside ~/.kimi-code/config.toml (the home is overridable via KIMI_CODE_HOME). That file is the user's shared config — providers, models, permission rules — so Jarvis edits it as a managed block between markers, takes a backup first and runs kimi doctor after writing; if the config doesn't pass the check, the backup is restored.
Eleven events. Kimi is the only one of the three that sends SessionHeartbeat (a pulse every 60 s → honest session liveness) while also having PermissionRequest (the "waiting for permission" status arrives as an event, with no screen reading) and SessionEnd (which Codex lacks). Models: K3 · K3-256k · K2.7 Coding · K2.7 Coding Highspeed; thinking effort (low / high / max) is set separately from the model, as in Claude Code — not inside the /model picker, as in Codex. Kimi fires hooks from kimi web and from the ACP mode for IDEs as well — the panel shows terminal CLI sessions only, because the rest have no tmux pane and can't be controlled; the same goes for kimi -p, which does fire hooks but runs outside tmux. Kimi doesn't store the session's git branch anywhere, so the branch comes from the working directory's .git/HEAD (the same fallback as for Codex). Kimi usage/cost numbers are estimates too — there are no official prices in the Kimi config.
An agent running on a VPS or an office workstation appears in the same list as your local ones — same statuses, toasts, voice, chat, reply-into-session and remote, with the node's name as a badge. A thin node (jarvis-node) lives on that machine: it receives the agents' hooks locally and buffers the events, so a closed laptop doesn't lose the night. Jarvis reaches it over your own SSH (ssh -N -L onto a unix socket) — the node opens no ports and Jarvis creates no new secrets. The same node is what jarvis-cli (jarvis -m vps …) and the Android client talk to, so a VPS agent is reachable from a terminal or a phone with nothing extra installed on it.
Set up straight from the panel: Settings → «Удалённые» → enter the ssh host → "Проверить машину" (shows what's there) → "Установить". Jarvis delivers the node itself, wires the agents' hooks and sets up autostart, showing each step as it goes. If ssh doesn't let it in yet, the panel offers the public key to add on that side. Same thing from a terminal — jarvis-setup remote add vps [email protected].
The remote machine needs SSH access, curl (used by the hooks, and to fetch the node) and tmux — the latter only for replying into a session and the remote control. Setup, what it looks like in the UI, diagnostics, security and boundaries: docs/remote.md.
Jarvis tracks token usage and estimated cost per session, model and project, and understands the rolling usage-limit window. When a session hits the limit, the panel and the voice line tell you when it resets — and, with auto-resume enabled, Jarvis picks the session back up the moment the window opens.
After a session event Jarvis briefly says out loud what happened, via a local TTS engine — currently in Russian. The text is assembled from structural signals by template; numbers are spelled out with correct grammatical agreement.
The engine is Silero (a local Python sidecar, FastAPI, model held in memory; binds to 127.0.0.1 only), managed by the daemon: started on demand, restarted on crash, stopped when idle and on exit. Speakers: aidar · baya · kseniya · xenia · eugene. Utterances don't overlap: the queue is serialized, "waiting" jumps ahead of "done", a pile-up of Stop lines is coalesced. Config lives in ~/.jarvis/settings.json:
"voice": { "engine": "silero", "mute": false,
"events": { "stop": true, "notification": true, "stopFailure": true } }
Menu bar: a "Mute" toggle and "Test voice". If the engine is unavailable the daemon runs as before, the agents are untouched, the reason is in the log.
⚠️ Voice license: the default Russian Silero voice v4_ru is non-commercial (CC BY-NC-SA 4.0). For commercial use see the License section.
Say the wake word and talk — no keyboard, no window switching:
Replies are half-duplex: Jarvis pauses listening while it speaks, and notifications wait while you're talking. Wake-word detection (openWakeWord) is included in release builds but off by default — enable it in the panel; the detector model is downloaded on demand (non-commercial license — see License). There is no speaker verification yet — anyone in the room can say the wake word.
Hold F8, speak, release — your speech is transcribed locally and inserted into the active session. The default engine is Qwen3-ASR via an MLX sidecar; Whisper (whisper.cpp, Metal) is built in as an alternative. Mixed Russian/English speech — including inline code terms — is handled.
The panel keeps a full dictation history: search, day grouping, stats, transcript enhancement, export, delete — and the last dictations keep their compressed audio, so you can re-transcribe them with a different engine. Against STT hallucinations on silence/noise there is a VAD gate (Silero, alpha toggle, off by default), a phrase blocklist, and tuned decoding parameters.
A sleeping Mac means frozen agent processes and severed API requests. Two pluggable power plugins (toggles in settings), UX modeled on Raycast Coffee and Amphetamine:
caffeinate): indefinitely, for a duration (15m…8h), while a process lives, or auto — while agents are working.pmset disablesleep): the Mac keeps running with the lid shut. On Apple Silicon this is exactly what plain caffeinate can't do.The assertion lives in the daemon process: a crash auto-releases it, so a "stuck" sleep block is impossible. Check: pmset -g assertions | grep -i jarvis.
Clamshell means root and thermal risk, so the plugin detects and suggests rather than silently sudo-ing: after an interrupted sleep it offers closed-display mode; with an external display it advises native clamshell (no root needed). The manual toggle is an honest admin dialog; for silent switching there's "Set up silent mode" (/etc/sudoers.d/jarvis-pmset, exactly two commands, validated via visudo -c). Fail-safes: the ~/.jarvis/clamshell.json marker, sleep restored on daemon start/exit, a battery guard (≤15% → restore sleep). A fanless MacBook Air throttles under a closed lid — the plugin warns about it.
Sessions running a multi-step plan (TodoWrite) show an "N/M tasks" ring → a "Tasks" slide-over: an aggregate, a progress bar, a list with statuses, and where possible the model and duration of the correlated subagent. The board is live: the next TodoWrite redraws it on its own.
The source of truth for tasks is the orchestrator inside the session. The TodoWrite list has no external "mark done" API — it can't be mutated from outside. Jarvis only reads the board (PostToolUse, last-write-wins) and displays it. A task action (go to / skip / restart) is an instruction to the orchestrator: it pre-fills the reply field with editable text that you review and send yourself. The board changes only when the next real TodoWrite arrives — so you see what the agent actually does, not what we asked for.
All local models — TTS voices, STT engines, the wake-word detector — are managed from a single "Models" section in settings: status, size on disk, download, delete, and hot-swapping the active STT engine without restarting the daemon. First-run onboarding offers a model checklist with unified download progress. Nothing is bundled with the app; weights are fetched from their upstream sources into ~/.jarvis/.
| Jarvis | Menu-bar monitors | Orchestrators | Claude Code (native) | |
|---|---|---|---|---|
| State detection | hooks (events) | process polling | spawn their own | itself |
| All sessions in one place | ✅ | ✅ | partial | ❌ |
| Reply into a session | ✅ (tmux) | ❌ | ✅ | — |
| Remote model/effort | ✅ | ❌ | partial | manual |
| Voice (TTS + assistant) | ✅ | ❌ | ❌ | ❌ |
| Usage & limit tracking | ✅ | partial | partial | per-session |
| Anti-sleep + clamshell | ✅ | ❌ | ❌ | ❌ |
| Task board | ✅ read-only | ❌ | ✅ owns it | ✅ TodoWrite |
| Spawns agents | ❌ (by design) | ❌ | ✅ | — |
| License | MIT | varies | varies | proprietary |
The closest in spirit are menu-bar monitors; their edge is zero setup. Jarvis answers with accuracy (hooks, not guessing), reply-into-session plus a remote, voice, and power management.
claude / codex / kimi (any terminal)
└─ native hooks (~/.claude/settings.json · ~/.codex/hooks.json · ~/.kimi-code/config.toml) ← installed by setup
└─ ~/.jarvis/bin/jarvis-hook ← fail-silent shim, 0.3 s curl
└─ unix socket ~/.jarvis/run.sock
└─ Rust daemon (Tauri) = session registry + effects
├─ toasts over fullscreen · hotkey panel (always-on-top)
├─ tmux reply & remote · TTS/STT sidecars · power plugins
└─ menu-bar counter: ⏸ waiting · ⚙ working
The frontend (panel ui/index.html + ui/renderer.js, toasts ui/toast.*) runs in the system WKWebView; the window.jarvis contract is implemented by a thin adapter ui/bridge.js over Tauri IPC. The main process is Rust (src-tauri/src/): a unix socket on axum, the registry-reducer with effects, tmux, transcripts, usage/history, voice, STT, wake-word, the power plugins. State, settings and stats live in ~/.jarvis/ in stable formats.
Principles: no screen reading (structured events only) · fail-silent hooks (they never break the agents) · everything local, no telemetry · remove everything with one command (npm run teardown). The increment-by-increment development history — specs, plans, mockups — is in docs/superpowers/.
brew install tmux / apt install tmux) — without it Jarvis still monitors and notifies, but can't reply into sessions or drive the remote. In iTerm2 sessions get native tabs (tmux control mode).v5_cis_base, Whisper/Qwen3, own or no wake-word) — see THIRD-PARTY-NOTICES.md.claude --resume <session_id> to make it controllable.Jarvis is a solo-maintained pre-1.0 project; this is a direction, not a promise. The best way to influence it is an issue.
Voice assistant → hands-free mission control
VoiceProcessingIO).Agent platform
jarvis-mcp MCP bridge are already in place.Engines & models
ort, or Silero via ort with native Russian stress handling).Codex parity — limit tracking with auto-resume and exact (not estimated) usage numbers for Codex sessions.
Distribution & polish
1.0: freeze the hook contract and the on-disk formats.Exploring — English UI and English spoken summaries (the interface and voice are currently Russian-first).
Explicitly out of scope: spawning/orchestrating agents (by design — see the boundary note), Windows, and the Mac App Store (its sandbox is incompatible with hooks and tmux). Linux is supported — see docs/linux.md; a few macOS-only capabilities (clamshell mode, the Accessibility focus snapshot) have no counterpart there and report themselves as unavailable.
A pre-1.0 MVP, developed in the open. Deliberate boundaries and known issues:
docs/linux.md); Claude Code (CLI), Codex (CLI) and Kimi Code CLI are supported — interactive sessions only (headless claude -p / codex exec don't fire hooks — not monitored; kimi -p does fire them, but such runs live outside tmux and can't be controlled); the remote and reply need tmux.claude update, compare against the current hooks docs and fix EVENTS/the format in src-tauri/src/bin/setup.rs.SessionEnd) leaves a session hanging — the panel's "Clear" button removes done/idle ones.npm start) includes the feature flag but the detector stays inert until enabled in the panel.jarvis-mcp) — the bridge exists, the UI isn't shipped. In progress.Pre-1.0 (SemVer 0.x): the hook contract and the on-disk formats (~/.jarvis/) may change. 0.MINOR bumps for features/breaking changes, 0.x.PATCH for fixes. 1.0 will freeze the hook contract and the state format. Updates never touch your data: everything user-owned lives in ~/.jarvis/, outside the app bundle — see docs/release/versioning-and-migration.md.
Issues and PRs are welcome! See CONTRIBUTING.md for how to build, test, and open a PR. By participating you agree to the Code of Conduct. Report vulnerabilities privately via SECURITY.md. Specs and per-increment plans live in docs/superpowers/.
Direct pushes to master are disabled — changes land only via Pull Request with green CI.
Jarvis's code is licensed under the MIT License © 2026 Sergey Chernyshev.
Model weights are downloaded, not part of the repo, and keep their own licenses — these restrict use of the corresponding feature independently of the MIT code:
| Artifact | Used for | License | Commercial |
|---|---|---|---|
Silero v4_ru | default voice | CC BY-NC-SA 4.0 | ❌ |
Silero v5_cis_base / _nostress | voice | MIT | ✅ |
openWakeWord hey_jarvis_v0.1 | default wake-word | CC BY-NC-SA 4.0 | ❌ |
whisper.cpp ggml-* | speech-to-text | MIT | ✅ |
Qwen3 (mlx-community) | speech-to-text | Apache-2.0 | ✅ |
⚠️ The default voice (
v4_ru) and wake-word (hey_jarvis) are non-commercial. For commercial use: voice → Silerov5_cis_base(MIT); wake-word → train your own or disable it; STT → Whisper or Qwen3. The full breakdown and third-party obligations are in THIRD-PARTY-NOTICES.md (including the BSD-3 attribution formediaremote-adapter).
Non-affiliation. Jarvis is an independent open-source project. It is not affiliated with, or endorsed by, Anthropic, PBC; "Claude" and "Claude Code" are trademarks of Anthropic, used nominatively (to describe compatibility). It is likewise not affiliated with OpenAI or with Moonshot AI; "Codex" and "Kimi" are referenced nominatively. Jarvis works on top of the agents' official hooks. The project is also unaffiliated with Marvel/Disney; any resemblance to the fictional "J.A.R.V.I.S." is unintentional.
macOS menu-bar mission-control for your Claude Code sessions — see, hear and reply to every agent so none sits idle waiting on you. Rust + Tauri, event-driven on native hooks, fully local.
Rust
6
269 commits
updated Oct 18, 2026
English · Русский
Mission control for your coding agents, right in the menu bar / tray.
Jarvis watches every Claude Code, Codex and Kimi session you run — and tells you, even out loud, the moment one needs you.
🖼 Screenshot and demo coming soon. What to capture and how to wire it in: docs/assets/README.md.
Running several coding agents at once turns you into the bottleneck. Sessions are spread across terminals, tabs and macOS Spaces, and you can't see at a glance which agent is blocked on a permission prompt, which finished half an hour ago and is idling while you think it's working, and which hit a rate limit. The agents' own notifications are per-session and terminal-scoped (they don't even fire in the VS Code extension) — there is no single pane showing the aggregate state. And a sleeping Mac silently freezes agent processes and severs in-flight API requests, killing long overnight runs; on Apple Silicon, closing the lid forces a sleep that plain caffeinate can't prevent.
Jarvis is that missing single pane. It sits in the menu bar, watches every interactive Claude Code, Codex and Kimi Code CLI session on your machine, and makes sure no agent ever waits on you silently:
/ command palette included.git worktree), a per-item fan-out over a list computed at runtime, and a join that merges them back. The graph exports to BPMN 2.0, is edited in Camunda Modeler and comes back without losses (docs/pipelines.md).caffeinate equivalent) plus a guarded closed-lid mode for overnight runs.TodoWrite progress (done / in-progress / queued) per session.Boundary: Jarvis is a monitor and a remote, not an orchestrator. It does not spawn agents and does not own the plan; it complements orchestrators (Claude Squad, Conductor, Crystal, native Agent Teams) and the sessions you already run.
The panel is one of three ways into the same fleet — not a client/server split.
Each program talks to the agents' own hooks, and the ones on one machine share
one data directory (~/.jarvis): a loop started in the terminal shows up in the
panel, and a bundle started in the panel is driven from the terminal. The phone
is deliberately narrower — sessions, chat and replies, nothing else.
| What it is | Where | |
|---|---|---|
| Jarvis | the panel: menu bar / tray, toasts over fullscreen, voice, dictation, usage | this repo |
| jarvis-cli | the same fleet from a terminal: live TUI, chat, loops, bundles — over SSH, inside tmux, no windows | arklual/jarvis-cli |
| jarvis-mobile | Android client for agents that run on other machines: see, read, reply | arklual/jarvis-mobile |
| jarvis-node | the thin node on the remote machine: buffers hook events, serves them over SSH | src-tauri/node, in this repo |
The panel and the CLI are two faces of one local state, so use whichever is in
front of you. The phone never touches ~/.jarvis — it talks to jarvis-node over
your own SSH, which is exactly what lets it work while the laptop is closed
(docs/remote.md). The node is also what the panel and the CLI
reach for when an agent runs on a VPS.
Jarvis is a power-user tool. It pays off once you cross the pain threshold of 3+ parallel agent sessions:
It's probably not for you if you run one session in one terminal (native notifications may be enough), or if you're looking for something to spawn and plan agents — pair Jarvis with an orchestrator for that.
Platforms. macOS and Linux. macOS is the primary target and the only one with prebuilt releases; on Linux you build from source — setup and the list of platform differences live in
docs/linux.md.
Download Jarvis_x.y.z_aarch64.dmg from the releases page.
Open the DMG, drag Jarvis into Applications, launch it.
First launch: the build is ad-hoc signed (no Apple Developer ID yet, not notarized), so macOS will warn about an unidentified developer. Right-click the app → Open → Open. If macOS claims the app is "damaged", clear the quarantine flag:
xattr -dr com.apple.quarantine /Applications/Jarvis.app
On first launch Jarvis offers to set up the Claude Code / Codex / Kimi integration and download voice models — click «Настроить» ("Set up"); progress is shown step by step in the window.
The app checks for updates itself (built-in updater). You can reinstall the integration anytime: menu bar → "Reinstall integration…".
# Debian / Ubuntu — see docs/linux.md for Fedora and Arch
sudo apt install tmux libwebkit2gtk-4.1-dev libgtk-3-dev libayatana-appindicator3-dev \
librsvg2-dev libasound2-dev libxdo-dev patchelf cmake
# Arch (the port's main bench: Arch + Sway)
# sudo pacman -S tmux webkit2gtk-4.1 gtk3 libayatana-appindicator librsvg \
# alsa-lib patchelf cmake wtype wl-clipboard playerctl
npm ci
npm run setup # hooks into ~/.claude/settings.json
npm run start:linux # build and launch
npm run bundle:linux # optional: .deb / .AppImage / .rpm
On Wayland (Sway, Hyprland, GNOME) global shortcuts belong to the compositor, not
to the app: bind them there — bindsym $mod+j exec jarvis --toggle — and add the
window rules from docs/sway/jarvis.conf, otherwise the
panel arrives as an ordinary tile. On X11 the panel hotkey is Ctrl+J: Super belongs to the desktop environment
(GNOME uses Super+1..4 for the dock). Full list of differences and of the
optional helpers (playerctl, wmctrl, xdotool) — docs/linux.md.
Requirements:
brew install tmux / apt install tmux);claude, codex, kimi) is found on the system.You need: Rust stable (rustup; the minimum version is the rust-version field in src-tauri/Cargo.toml), Node.js 20+, CMake, tmux. On Linux also the system libraries listed above.
npm ci
npm run setup # install hooks into ~/.claude/settings.json (backed up, idempotent)
npm start # build and launch the daemon + menu bar (◇ top-right) against the dev profile (~/.jarvis-dev)
(npm here is just a familiar runner: under the hood it's cargo build --release plus an ad-hoc codesign needed for microphone access.)
Then restart any active claude sessions — hooks are snapshotted at session start. Remove everything: npm run teardown. Check the status of each component: npm run status. Build a DMG locally (unsigned, host arch): npm run bundle. Run the prod profile: npm run start:prod.
Try it without Claude Code (the daemon must be running) — manually, the way a real hook does it:
echo '{"session_id":"t1","cwd":"'$PWD'"}' | ~/.jarvis/bin/jarvis-hook claude session-start
echo '{"session_id":"t1","cwd":"'$PWD'","prompt":"add tests"}' | ~/.jarvis/bin/jarvis-hook claude prompt
echo '{"session_id":"t1","cwd":"'$PWD'","message":"Claude needs your permission to use Bash"}' | ~/.jarvis/bin/jarvis-hook claude notification
echo '{"session_id":"t1","cwd":"'$PWD'"}' | ~/.jarvis/bin/jarvis-hook claude stop
Check the daemon is alive: curl -s --unix-socket ~/.jarvis/run.sock http://jarvis/
A registry of every interactive Claude Code / Codex / Kimi session: status (idle / working / waiting / finished / hit the limit), project, branch, model, activity. The menu-bar counter (◇ ⏸N ⚙M) is the cross-terminal summary; the hotkey panel is the detailed view.
No screen parsing — only structured events from the agents themselves, via their native hooks: SessionStart → idle, UserPromptSubmit → working, Notification → waiting (+a notification), Stop → done (+a notification), SessionEnd → the session disappears. A session can also be ended by hand — ⌘⇧⌫ or "Завершить сессию" in ⌘K: a live one loses its pane along with the agent, while a stuck one (the agent died, no SessionEnd arrived, the node is unreachable — the liveness sweep has nothing to judge by) is simply dropped from the list. The registry-reducer lives in the Rust daemon; state survives a daemon restart (~/.jarvis/state.json) and live tmux sessions are re-adopted. The pane's screen is read only by the interactive-prompt detector and the remote (a regex, not screenshots).
Jarvis's own toast notifications render over fullscreen; the panel (alwaysOnTop) does not steal focus. The global hotkey (⌘J on macOS, Ctrl+J on Linux) opens the panel centered on screen — Esc, a click outside, or the hotkey again closes it.
Click ◇ for the panel. Right-click for the menu (test notification, autostart, quit). The hotkey, panel position, notification toggles and autostart live in settings (⚙ in the panel header), stored in ~/.jarvis/settings.json. Notification content is composable from segments (branch · model · effort · tokens · duration) with a live preview; a separate toggle speaks notifications only when a Bluetooth headset is connected. Note: the global hotkey intercepts that combination across all apps (Chrome "downloads", VS Code "panel") — change it in settings if it gets in the way.
The interface is a sheet of paper: one opaque surface, one accent colour, and monochrome status dots (working — filled, finished — outline only, waiting — accent, rate-limited — dim, asleep — pale). The bottom strip carries a waveform and the line "say «jarvis» — I'm listening"; on the right, a subscription-limit meter with the window until reset.
Settings → Look: mode (overlay / window), theme (light / dark / system), paint (Coal · Clover · Raspberry, or your own — pick one hue and the rest is derived from it), and what the bottom strip shows — the subscription limit or the day's spend. Below that: row density, corner radius and interface scale (85–140%), with a reset button. In window mode Jarvis is an ordinary window: sessions on the left, the conversation on the right, the limit in the title bar; it resizes, minimises, does not vanish when you click away, and lives in the Dock (the icon appears on the next launch). Overlay is the familiar hotkey panel that floats above everything. Changes apply immediately across every window: panel, toasts, agent chat, onboarding. The typeface is Golos Text, bundled with the app so the UI renders identically with no network. The full visual language is documented in docs/superpowers/specs/2026-08-02-clover-redesign.md.
In a session's chat the Reply field inserts text straight into the session's terminal — even if the window is minimized or on another Space. Below it sits the remote: Model segments (Opus / Sonnet / Haiku) and Effort (auto / low / med / high / xhigh) send a slash command (/model sonnet, /effort high) into the live session. When an agent asks a multiple-choice question, the panel renders native pickers (multi-select included) instead of making you type numbers into a terminal.
npm run setup installs a PATH shim ~/.jarvis/shims/claude (pyenv pattern, a managed block in ~/.zshrc between jarvis markers). After exec zsh, every interactive claude launch is transparently wrapped in tmux on a separate server (-L jarvis, config ~/.jarvis/tmux.conf): no status bar, mouse scrolls, your personal tmux untouched. In iTerm2 — control mode (-CC), native tabs. Headless runs (-p, pipes, $TMUX) are not wrapped. Insertion: set-buffer → paste-buffer -p → send-keys Enter (multi-line prompts arrive as one chunk). The command palette (/ in the reply field) reaches the session's other slash commands.
claude --resume <session_id>).tmux -L jarvis attach -t <name>.If codex is found during setup, Jarvis wires it up too: hooks in ~/.codex/hooks.json and a codex shim using the same tmux mechanism. Interactive Codex sessions appear in the panel with a codex badge and get the same treatment — status, toasts, voice, chat, reply-into-session, and a resume command (codex resume <session_id>).
Model and reasoning are changed via Codex's own /model picker (there is no separate /effort). Headless codex exec does not fire hooks — such runs aren't monitored (by design, same as claude -p). On a fresh machine Codex may ask you to trust ~/.codex/hooks.json; Jarvis never injects the global --dangerously-bypass-hook-trust flag. Codex usage/cost numbers are estimates.
The third agent is Kimi Code CLI (kimi, Moonshot AI) — same deal: a kimi shim on the same tmux mechanism, a kimi badge in the panel, status, toasts, voice, chat, reply-into-session and a resume command (kimi -S <session_id>). Hooks live elsewhere, though: Kimi keeps them in [[hooks]] sections inside ~/.kimi-code/config.toml (the home is overridable via KIMI_CODE_HOME). That file is the user's shared config — providers, models, permission rules — so Jarvis edits it as a managed block between markers, takes a backup first and runs kimi doctor after writing; if the config doesn't pass the check, the backup is restored.
Eleven events. Kimi is the only one of the three that sends SessionHeartbeat (a pulse every 60 s → honest session liveness) while also having PermissionRequest (the "waiting for permission" status arrives as an event, with no screen reading) and SessionEnd (which Codex lacks). Models: K3 · K3-256k · K2.7 Coding · K2.7 Coding Highspeed; thinking effort (low / high / max) is set separately from the model, as in Claude Code — not inside the /model picker, as in Codex. Kimi fires hooks from kimi web and from the ACP mode for IDEs as well — the panel shows terminal CLI sessions only, because the rest have no tmux pane and can't be controlled; the same goes for kimi -p, which does fire hooks but runs outside tmux. Kimi doesn't store the session's git branch anywhere, so the branch comes from the working directory's .git/HEAD (the same fallback as for Codex). Kimi usage/cost numbers are estimates too — there are no official prices in the Kimi config.
An agent running on a VPS or an office workstation appears in the same list as your local ones — same statuses, toasts, voice, chat, reply-into-session and remote, with the node's name as a badge. A thin node (jarvis-node) lives on that machine: it receives the agents' hooks locally and buffers the events, so a closed laptop doesn't lose the night. Jarvis reaches it over your own SSH (ssh -N -L onto a unix socket) — the node opens no ports and Jarvis creates no new secrets. The same node is what jarvis-cli (jarvis -m vps …) and the Android client talk to, so a VPS agent is reachable from a terminal or a phone with nothing extra installed on it.
Set up straight from the panel: Settings → «Удалённые» → enter the ssh host → "Проверить машину" (shows what's there) → "Установить". Jarvis delivers the node itself, wires the agents' hooks and sets up autostart, showing each step as it goes. If ssh doesn't let it in yet, the panel offers the public key to add on that side. Same thing from a terminal — jarvis-setup remote add vps [email protected].
The remote machine needs SSH access, curl (used by the hooks, and to fetch the node) and tmux — the latter only for replying into a session and the remote control. Setup, what it looks like in the UI, diagnostics, security and boundaries: docs/remote.md.
Jarvis tracks token usage and estimated cost per session, model and project, and understands the rolling usage-limit window. When a session hits the limit, the panel and the voice line tell you when it resets — and, with auto-resume enabled, Jarvis picks the session back up the moment the window opens.
After a session event Jarvis briefly says out loud what happened, via a local TTS engine — currently in Russian. The text is assembled from structural signals by template; numbers are spelled out with correct grammatical agreement.
The engine is Silero (a local Python sidecar, FastAPI, model held in memory; binds to 127.0.0.1 only), managed by the daemon: started on demand, restarted on crash, stopped when idle and on exit. Speakers: aidar · baya · kseniya · xenia · eugene. Utterances don't overlap: the queue is serialized, "waiting" jumps ahead of "done", a pile-up of Stop lines is coalesced. Config lives in ~/.jarvis/settings.json:
"voice": { "engine": "silero", "mute": false,
"events": { "stop": true, "notification": true, "stopFailure": true } }
Menu bar: a "Mute" toggle and "Test voice". If the engine is unavailable the daemon runs as before, the agents are untouched, the reason is in the log.
⚠️ Voice license: the default Russian Silero voice v4_ru is non-commercial (CC BY-NC-SA 4.0). For commercial use see the License section.
Say the wake word and talk — no keyboard, no window switching:
Replies are half-duplex: Jarvis pauses listening while it speaks, and notifications wait while you're talking. Wake-word detection (openWakeWord) is included in release builds but off by default — enable it in the panel; the detector model is downloaded on demand (non-commercial license — see License). There is no speaker verification yet — anyone in the room can say the wake word.
Hold F8, speak, release — your speech is transcribed locally and inserted into the active session. The default engine is Qwen3-ASR via an MLX sidecar; Whisper (whisper.cpp, Metal) is built in as an alternative. Mixed Russian/English speech — including inline code terms — is handled.
The panel keeps a full dictation history: search, day grouping, stats, transcript enhancement, export, delete — and the last dictations keep their compressed audio, so you can re-transcribe them with a different engine. Against STT hallucinations on silence/noise there is a VAD gate (Silero, alpha toggle, off by default), a phrase blocklist, and tuned decoding parameters.
A sleeping Mac means frozen agent processes and severed API requests. Two pluggable power plugins (toggles in settings), UX modeled on Raycast Coffee and Amphetamine:
caffeinate): indefinitely, for a duration (15m…8h), while a process lives, or auto — while agents are working.pmset disablesleep): the Mac keeps running with the lid shut. On Apple Silicon this is exactly what plain caffeinate can't do.The assertion lives in the daemon process: a crash auto-releases it, so a "stuck" sleep block is impossible. Check: pmset -g assertions | grep -i jarvis.
Clamshell means root and thermal risk, so the plugin detects and suggests rather than silently sudo-ing: after an interrupted sleep it offers closed-display mode; with an external display it advises native clamshell (no root needed). The manual toggle is an honest admin dialog; for silent switching there's "Set up silent mode" (/etc/sudoers.d/jarvis-pmset, exactly two commands, validated via visudo -c). Fail-safes: the ~/.jarvis/clamshell.json marker, sleep restored on daemon start/exit, a battery guard (≤15% → restore sleep). A fanless MacBook Air throttles under a closed lid — the plugin warns about it.
Sessions running a multi-step plan (TodoWrite) show an "N/M tasks" ring → a "Tasks" slide-over: an aggregate, a progress bar, a list with statuses, and where possible the model and duration of the correlated subagent. The board is live: the next TodoWrite redraws it on its own.
The source of truth for tasks is the orchestrator inside the session. The TodoWrite list has no external "mark done" API — it can't be mutated from outside. Jarvis only reads the board (PostToolUse, last-write-wins) and displays it. A task action (go to / skip / restart) is an instruction to the orchestrator: it pre-fills the reply field with editable text that you review and send yourself. The board changes only when the next real TodoWrite arrives — so you see what the agent actually does, not what we asked for.
All local models — TTS voices, STT engines, the wake-word detector — are managed from a single "Models" section in settings: status, size on disk, download, delete, and hot-swapping the active STT engine without restarting the daemon. First-run onboarding offers a model checklist with unified download progress. Nothing is bundled with the app; weights are fetched from their upstream sources into ~/.jarvis/.
| Jarvis | Menu-bar monitors | Orchestrators | Claude Code (native) | |
|---|---|---|---|---|
| State detection | hooks (events) | process polling | spawn their own | itself |
| All sessions in one place | ✅ | ✅ | partial | ❌ |
| Reply into a session | ✅ (tmux) | ❌ | ✅ | — |
| Remote model/effort | ✅ | ❌ | partial | manual |
| Voice (TTS + assistant) | ✅ | ❌ | ❌ | ❌ |
| Usage & limit tracking | ✅ | partial | partial | per-session |
| Anti-sleep + clamshell | ✅ | ❌ | ❌ | ❌ |
| Task board | ✅ read-only | ❌ | ✅ owns it | ✅ TodoWrite |
| Spawns agents | ❌ (by design) | ❌ | ✅ | — |
| License | MIT | varies | varies | proprietary |
The closest in spirit are menu-bar monitors; their edge is zero setup. Jarvis answers with accuracy (hooks, not guessing), reply-into-session plus a remote, voice, and power management.
claude / codex / kimi (any terminal)
└─ native hooks (~/.claude/settings.json · ~/.codex/hooks.json · ~/.kimi-code/config.toml) ← installed by setup
└─ ~/.jarvis/bin/jarvis-hook ← fail-silent shim, 0.3 s curl
└─ unix socket ~/.jarvis/run.sock
└─ Rust daemon (Tauri) = session registry + effects
├─ toasts over fullscreen · hotkey panel (always-on-top)
├─ tmux reply & remote · TTS/STT sidecars · power plugins
└─ menu-bar counter: ⏸ waiting · ⚙ working
The frontend (panel ui/index.html + ui/renderer.js, toasts ui/toast.*) runs in the system WKWebView; the window.jarvis contract is implemented by a thin adapter ui/bridge.js over Tauri IPC. The main process is Rust (src-tauri/src/): a unix socket on axum, the registry-reducer with effects, tmux, transcripts, usage/history, voice, STT, wake-word, the power plugins. State, settings and stats live in ~/.jarvis/ in stable formats.
Principles: no screen reading (structured events only) · fail-silent hooks (they never break the agents) · everything local, no telemetry · remove everything with one command (npm run teardown). The increment-by-increment development history — specs, plans, mockups — is in docs/superpowers/.
brew install tmux / apt install tmux) — without it Jarvis still monitors and notifies, but can't reply into sessions or drive the remote. In iTerm2 sessions get native tabs (tmux control mode).v5_cis_base, Whisper/Qwen3, own or no wake-word) — see THIRD-PARTY-NOTICES.md.claude --resume <session_id> to make it controllable.Jarvis is a solo-maintained pre-1.0 project; this is a direction, not a promise. The best way to influence it is an issue.
Voice assistant → hands-free mission control
VoiceProcessingIO).Agent platform
jarvis-mcp MCP bridge are already in place.Engines & models
ort, or Silero via ort with native Russian stress handling).Codex parity — limit tracking with auto-resume and exact (not estimated) usage numbers for Codex sessions.
Distribution & polish
1.0: freeze the hook contract and the on-disk formats.Exploring — English UI and English spoken summaries (the interface and voice are currently Russian-first).
Explicitly out of scope: spawning/orchestrating agents (by design — see the boundary note), Windows, and the Mac App Store (its sandbox is incompatible with hooks and tmux). Linux is supported — see docs/linux.md; a few macOS-only capabilities (clamshell mode, the Accessibility focus snapshot) have no counterpart there and report themselves as unavailable.
A pre-1.0 MVP, developed in the open. Deliberate boundaries and known issues:
docs/linux.md); Claude Code (CLI), Codex (CLI) and Kimi Code CLI are supported — interactive sessions only (headless claude -p / codex exec don't fire hooks — not monitored; kimi -p does fire them, but such runs live outside tmux and can't be controlled); the remote and reply need tmux.claude update, compare against the current hooks docs and fix EVENTS/the format in src-tauri/src/bin/setup.rs.SessionEnd) leaves a session hanging — the panel's "Clear" button removes done/idle ones.npm start) includes the feature flag but the detector stays inert until enabled in the panel.jarvis-mcp) — the bridge exists, the UI isn't shipped. In progress.Pre-1.0 (SemVer 0.x): the hook contract and the on-disk formats (~/.jarvis/) may change. 0.MINOR bumps for features/breaking changes, 0.x.PATCH for fixes. 1.0 will freeze the hook contract and the state format. Updates never touch your data: everything user-owned lives in ~/.jarvis/, outside the app bundle — see docs/release/versioning-and-migration.md.
Issues and PRs are welcome! See CONTRIBUTING.md for how to build, test, and open a PR. By participating you agree to the Code of Conduct. Report vulnerabilities privately via SECURITY.md. Specs and per-increment plans live in docs/superpowers/.
Direct pushes to master are disabled — changes land only via Pull Request with green CI.
Jarvis's code is licensed under the MIT License © 2026 Sergey Chernyshev.
Model weights are downloaded, not part of the repo, and keep their own licenses — these restrict use of the corresponding feature independently of the MIT code:
| Artifact | Used for | License | Commercial |
|---|---|---|---|
Silero v4_ru | default voice | CC BY-NC-SA 4.0 | ❌ |
Silero v5_cis_base / _nostress | voice | MIT | ✅ |
openWakeWord hey_jarvis_v0.1 | default wake-word | CC BY-NC-SA 4.0 | ❌ |
whisper.cpp ggml-* | speech-to-text | MIT | ✅ |
Qwen3 (mlx-community) | speech-to-text | Apache-2.0 | ✅ |
⚠️ The default voice (
v4_ru) and wake-word (hey_jarvis) are non-commercial. For commercial use: voice → Silerov5_cis_base(MIT); wake-word → train your own or disable it; STT → Whisper or Qwen3. The full breakdown and third-party obligations are in THIRD-PARTY-NOTICES.md (including the BSD-3 attribution formediaremote-adapter).
Non-affiliation. Jarvis is an independent open-source project. It is not affiliated with, or endorsed by, Anthropic, PBC; "Claude" and "Claude Code" are trademarks of Anthropic, used nominatively (to describe compatibility). It is likewise not affiliated with OpenAI or with Moonshot AI; "Codex" and "Kimi" are referenced nominatively. Jarvis works on top of the agents' official hooks. The project is also unaffiliated with Marvel/Disney; any resemblance to the fictional "J.A.R.V.I.S." is unintentional.