English | 日本語
An app that collects events from Claude Code global Hooks and lets you monitor session states across multiple projects via a menubar (tray) and a dashboard. It also plays nicely with tmux — view and interact with your panes right from the dashboard!
- macOS (12+)
- Linux (untested; may work)
- Windows (untested; pls someone helps us 🙇🏻♂️)
- Claude Code
- Node.js v23+
- Go to Releases
- Download the appropriate file for your platform:
- macOS:
.dmgfile - Linux:
.debor.AppImagefile - Windows:
.msior.exefile
- macOS:
- Install and launch the app
Note
On macOS, you may see "App is damaged and can't be opened" error. See Troubleshooting for the workaround.
# Clone the repository
git clone https://github.com/joe-re/eyes-on-claude-code.git
cd eyes-on-claude-code
# Install dependencies
pnpm install
# Run in development mode
pnpm tauri dev
# Or build for production
pnpm tauri buildOn the first launch, the app shows instructions for installing the hooks configuration.
Follow the instructions in the Setup modal and apply the generated config to ~/.claude/settings.json.
Note
The JSON shown in the Setup modal is generated by reading your existing settings and producing a JSON that only replaces the hooks (old eocc hooks are replaced, while other hooks/settings are preserved).
- The dashboard shows a list of Sessions.
Monitoring: 0 sessions in a Waiting state (monitoring)N waiting: N sessions inWaitingPermissionorWaitingInput- The dot on the top right also switches to a warning color and blinks when there are waiting sessions
There are four session states, and they are reflected in the list/tray/menu display as well.
| State | Meaning | Display (emoji) |
|---|---|---|
Active |
Claude is working / normal state | 🟢 |
WaitingPermission |
Claude Code is waiting for permission approval | 🔐 |
WaitingInput |
Waiting for input (idle, etc.) | ⏳ |
Completed |
Response completed | ✅ |
- Click a card to expand it and see the Git status.
Click Diff to launch difit and review the diff. (Thanks the awesome product for difit dev team! 🙇🏻♂️)
unstaged: diff in the working treestaged: diff in the indexcommit: diff of the latest commit (HEAD~1..HEAD)branch: comparison against the default branch (auto-detected)
Note
Diff display internally runs npx difit inside the target repository, so Node.js (npx) is required.
If the target is not a Git repository, or if there is no diff, it cannot be opened.
If Claude Code is running inside a tmux pane, the session card displays a Terminal button that opens a viewer window showing the pane contents in real-time.
- Real-time updates: Pane content refreshes every 500ms
- Keyboard input: Send keystrokes (including Ctrl combinations and special keys) directly to the pane
- IME support: Japanese input method composition is supported
- ANSI colors: Terminal colors are rendered correctly
Note
tmux integration requires Claude Code to be running inside a tmux session. The TMUX_PANE environment variable is automatically captured by the hook script.
- Supports toggling Always on Top.
- You can change Opacity depending on focus state.
- When the difit window is focused, the dashboard is treated as "inactive".
- When Sound is ON, sound effects play on state changes.
- Waiting (Permission/Input): attention sound
- Completed: completion sound
When enabled (default), the dashboard automatically shrinks to a compact view when it loses focus:
- Compact display: Shows only the title bar with session status counts (🟢:1 🔐:2 ⏳:1 ✅:3)
- Auto-restore: Returns to full size when the window regains focus
- Position memory: Remembers the original window size and position
You can toggle this feature from the menubar: Window → Minimum Mode.
- The dashboard switches opacity between active/inactive.
- Active: when the dashboard has focus (default: 100%)
- Inactive: when it does not have focus (default: 100%)
- You can change opacity from the menubar: Window → Opacity.
- Monitor multiple sessions
- Status display: Active / WaitingPermission / WaitingInput / Completed
- Visualize the number of waiting sessions
- Show Git info: branch name, unstaged/staged presence, latest commit
- Diff view: open diffs in a separate window via difit (
npx difit) - tmux integration: view and interact with tmux pane contents in real-time
- Notification sounds: notify waiting / completed with sounds (Sound ON/OFF available)
- Rust (
rustup) - Node.js (including
npx) - pnpm
pnpm installpnpm tauri devHooks / logs
~/.local/bin/
└── eocc-hook # Hook script (symlink created by the app)
~/.claude/
└── settings.json # Global Hooks settings
~/.eocc/
└── logs/
└── events.jsonl # Event queue (cleared after processing by the app)
App data
~/Library/Application Support/tech.joe-re.eocc/ # macOS
├── settings.json # App settings
└── runtime_state.json # Session state (restored on app restart)
~/Library/Logs/tech.joe-re.eocc/ # macOS
└── *.log # App logs
The hook script appends events to events.jsonl.
| event | Usage | Reflected into session state |
|---|---|---|
session_start (startup/resume) |
Start a session | Registered as Active |
session_end |
End a session | Remove the session |
notification (permission_prompt) |
Waiting for approval | WaitingPermission |
notification (idle_prompt) |
Waiting for input | WaitingInput |
stop |
Response completed | Completed |
post_tool_use |
After a tool is executed | Active |
user_prompt_submit |
Prompt submitted | Active |
sequenceDiagram
participant CC as Claude Code
participant Hook as eocc-hook
participant Queue as events.jsonl
participant App as EOCC App
participant Log as App Log
CC->>Hook: Hook call (stdin: event data)
Hook->>Queue: Append event (JSON line)
loop File watch
App->>Queue: Change detected
App->>App: events.jsonl → events.processing.*.jsonl (atomic rename)
App->>App: Recreate an empty events.jsonl
App->>App: Process processing file line by line
App->>Log: Write event into logs
App->>App: Update session state
App->>App: Delete processing file
App->>App: Save to runtime_state.json
end
pnpm tauri buildArtifacts (macOS example):
src-tauri/target/release/bundle/macos/Eyes on Claude Code.appsrc-tauri/target/release/bundle/dmg/Eyes on Claude Code_1.0.0_aarch64.dmg
Releases are automated via GitHub Actions. To create a new release:
- Update version in
src-tauri/tauri.conf.jsonandpackage.json - Commit the version bump
- Create and push a version tag:
git tag v1.0.1 git push origin v1.0.1
- GitHub Actions will automatically build for all platforms and create a draft release
- Review the release notes on GitHub and publish
| Platform | Architecture | Artifact |
|---|---|---|
| macOS | Apple Silicon (aarch64) | .dmg, .app |
| Linux | x64 | .deb, .AppImage |
| Windows | x64 | .msi, .exe |
For local builds targeting a specific platform:
# macOS (Apple Silicon)
pnpm tauri buildTo regenerate icons from the source PNG:
./scripts/generate-icons.sh
# or
pnpm tauri icon src-tauri/icons/icon.pngThis error occurs because the app is not signed/notarized with an Apple Developer certificate. To bypass this warning, run:
xattr -cr "/Applications/Eyes on Claude Code.app"Or if opening from the mounted DMG:
xattr -cr "/Volumes/Eyes on Claude Code/Eyes on Claude Code.app"Note
Apple Developer certificate signing is planned for a future release.
- Open
~/.claude/settings.jsonand confirm the generated content from the Setup modal was applied correctly - Run
/hooksin Claude Code to confirm hooks are loaded - Confirm
~/.local/bin/eocc-hookexists and is executablels -la ~/.local/bin/eocc-hook
- Confirm Node.js is available
command -v node
- Check the app logs
ls -la ~/Library/Logs/tech.joe-re.eocc/cat ~/Library/Logs/tech.joe-re.eocc/*.log | grep -i error
- Check whether
events.jsonlis created/updatedls -la ~/.eocc/logs/
- Check whether the hook is being called (run
/hooksin Claude Code)
- Confirm the target is a Git repository (without
.git, it will error) - If there is no diff, you’ll see “No diff content to display” and it can’t be opened
- Confirm Node.js (
npx) is availablecommand -v node && command -v npx
- Depending on your environment, the network may be required for
npxto fetchdifit
- Confirm Sound is ON in the menu
- Audio may be blocked by browser/OS restrictions (after changing settings, trigger another state change and test again)
- Confirm Claude Code is running inside a tmux session
echo $TMUX_PANEshould show the pane ID (e.g.,%0)
- Check if the hook is capturing the TMUX_PANE variable
- Look for
tmux_panein event logs:grep tmux_pane ~/.eocc/logs/events.jsonl
- Look for
- Confirm tmux is installed and accessible
command -v tmux
- Confirm the pane still exists
tmux list-panes -a
This project is licensed under the MIT License. See LICENSE.
For third-party license notes and how to generate a dependency license report, see THIRD_PARTY_NOTICES.md.
- Local-only: the app processes hook events locally and does not implement telemetry/analytics.
- What is stored:
~/.eocc/logs/events.jsonl: event queue written by the hook (consumed/cleared by the app)- App logs: raw event JSON lines are also written to the app log (rotated by
tauri-plugin-log)
- Note: events may include fields like
messagedepending on Claude Code hook payloads. Treat this data as potentially sensitive.





