Metadata-Version: 2.4
Name: claudehub-monitor
Version: 0.1.5
Summary: Desktop and LAN web dashboard for Claude Code hook events
Author: Pandiyaraj Karuppasamy
License-Expression: MIT
License-File: LICENSE
Keywords: claude,claude-code,dashboard,monitor,pyside6
Classifier: Development Status :: 4 - Beta
Classifier: Environment :: Win32 (MS Windows)
Classifier: Environment :: X11 Applications :: Qt
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Software Development :: User Interfaces
Requires-Python: >=3.11
Requires-Dist: anthropic>=0.40
Requires-Dist: pyside6>=6.6
Provides-Extra: hooks
Requires-Dist: claudehub-hooks>=0.1.0; extra == 'hooks'
Description-Content-Type: text/markdown

<!--
Author: Pandiyaraj Karuppasamy
-->

# Claudehub Monitor

A desktop **and** LAN web dashboard that shows what Claude Code is doing in real
time — across every repo and machine on your network.

`claudehub-monitor` is the central collector: Claude Code hooks POST lifecycle
events to it over HTTP, and it surfaces them as an animated desktop card, system
sounds, a scrolling history, a usage quota toast, and a live web page you can
open from your phone.

```
  Claude Code (any repo / any machine)
        │  hook fires  →  claudehub-hooks  →  HTTP POST /event
        ▼
  claudehub-monitor  ──►  desktop flash card + toasts + sounds + history (SQLite)
        │
        └─►  web dashboard  ◄── browser on the LAN (SSE live updates)
```

## Quick start

1. **Start the monitor** (once per machine):

```bash
pip install claudehub-monitor
claudehub          # short alias
claudehub-monitor  # full name (same command)
```

2. **Wire hooks into each repo** you use with Claude Code:

```bash
pip install claudehub-hooks
claudehub-hooks install
claudehub-hooks install --url http://192.168.1.50:7070/event   # remote monitor
```

3. Open Claude Code in that repo — events flow to the monitor automatically.

Test hook connectivity:

```bash
python .claude/hooks/claude_hook.py --test
```

## Install

**Monitor** (`claudehub-monitor`):

```bash
pip install claudehub-monitor
claudehub          # short alias
claudehub-monitor  # full name (same command)
```

**Hooks** (`claudehub-hooks` — separate package, stdlib only):

```bash
pip install claudehub-hooks
claudehub-hooks install
```

## Components

| Package / path | Role |
|----------------|------|
| `claudehub-monitor` (`claudehub/monitor.py`) | Monitor: HTTP server, desktop UI, web dashboard, SQLite store. |
| `claudehub-hooks` | Pip installer that writes `.claude/hooks/claude_hook.py` + `settings.json` entries. |
| `claudehub/dashboard.html` | Web dashboard page (served at `/`). |
| `monitor_config.json` | Example monitor config (copy to `~/.claude/monitor_config.json`). |
| `.claude/hooks/monitor_config.json` | Per-repo hook transport config (written by `claudehub-hooks install`). |
| `~/.claude/claude_events.db` | SQLite history + per-session status (auto-created). |

## Hook events

`claudehub-hooks install` registers these Claude Code hooks:

| Event | When it fires | Monitor behavior |
|-------|---------------|------------------|
| `SessionStart` | Session begins | Card shows “Session Started”; history entry |
| `UserPromptSubmit` | User sends a message | **Thinking** — ring animation, persistent toast |
| `PostToolUse` | Tool used (matcher: `Edit\|Write\|NotebookEdit\|MultiEdit\|Read`) | **Reading** (Read) or **Editing** (others); brief flash → back to thinking |
| `Stop` | Turn completes | **Done** — green tick, ding, clears session |
| `StopFailure` | API / runtime error | **Error** — red card, pong sound |
| `Notification` | Claude needs input | **Waiting** — persistent ask toast |
| `PermissionRequest` | Allow/deny dialog shown | **Waiting** — permission toast + orange card |
| `PermissionGranted` | User approved a tool | Resumes **Thinking**; history shows “Allowed ✓” |
| `PermissionDenied` | User denied a tool | Resumes **Thinking**; history shows “Denied ✗” |
| `PreToolUse` | Tool about to run | Resolves pending permission → **Thinking** (implicit allow for sub-tools) |

Hook self-test (`claude_hook.py --test`) sends `__test__` — shown on the card as a purple “Test” flash (not recorded in history).

### Event payload (from hooks)

Hooks POST JSON to `POST /event`:

```jsonc
{
  "event": "PostToolUse",
  "datetime": "<ISO-8601 UTC>",
  "session_id": "<str>",
  "host": "<hostname>",
  "cwd": "<working dir>",
  "files": ["<path>"],           // PostToolUse
  "prompt": "<first 500 chars>", // UserPromptSubmit
  "tool_name": "<tool>",         // PostToolUse / Permission* / PreToolUse
  "command": "<cmd>",            // Permission* / PreToolUse
  "notification_type": "<type>", // Notification
  "error_type": "<type>",        // StopFailure
  "error_message": "<msg>"       // StopFailure
}
```

## Status model

Each surfaced status maps to colour, icon, and animation on the desktop:

| Status | Trigger (hook event) | Colour | Desktop animation |
|--------|----------------------|--------|-------------------|
| Thinking | `UserPromptSubmit` | blue | rotating arc ring |
| Reading | `PostToolUse` (Read) | teal | sweeping scan bar |
| Editing | `PostToolUse` (Edit/Write/…) | blue | rippling bars |
| Waiting | `PermissionRequest` / `Notification` | amber | persistent ask toast |
| Allowed | `PermissionGranted` / `PreToolUse` (after approve) | green | ding; back to thinking |
| Denied | `PermissionDenied` | red | thud; back to thinking |
| Done | `Stop` | green | ✓ tick + ding |
| Error | `StopFailure` | red | pong |

Read/Edit briefly flash, then the card returns to **Thinking** until the turn
ends (`Stop`) — mirroring how Claude actually works. The desktop card shows the
status **name**; the history table and web feed show the matching **icon**.

## Usage toast

When Claude Code OAuth credentials exist (`~/.claude/.credentials.json`), a fixed
top-right toast shows subscription quota:

- **5-hour session** and **7-day weekly** utilization (%)
- Reset times for each window
- Refreshed every 5 minutes (one minimal Haiku probe call per refresh)

If credentials are missing, the toast is skipped.

## Run it

```bat
claudehub
claudehub-monitor
python -m claudehub
```

The app starts **hidden in the system tray** by default (the HTTP server and web
dashboard run regardless). Double-click the tray icon to open the window;
"Minimize to Tray" or closing the window hides it again; the tray menu's **Quit**
exits. Only one instance runs per machine.

## Web dashboard (view on the go)

With the monitor running, open from any device on the LAN:

```
http://<monitor-ip>:7070/
```

It shows a live grid of active sessions, the last prompt, and a recent-activity
feed — pushed over **Server-Sent Events** (auto-reconnecting), with a JSON
snapshot for the initial load.

Open TCP 7070 on the monitor machine's firewall:

```powershell
New-NetFirewallRule -DisplayName "Claude Monitor" -Direction Inbound `
  -Protocol TCP -LocalPort 7070 -Action Allow
```

## HTTP API

| Method / path | Purpose |
|---------------|---------|
| `POST /event` | Ingest one hook event (from `claudehub-hooks` dispatcher). |
| `GET /` | The web dashboard. |
| `GET /api/status` | Active sessions snapshot + last prompt (JSON). |
| `GET /api/events` | Recent events feed (JSON). |
| `GET /events/stream` | Live event stream (SSE). |
| `GET /health` | `{"status":"ok"}`. |

## Configuration

### Monitor (`claudehub-monitor`)

Copy `monitor_config.json` to `~/.claude/monitor_config.json`, or set
`CLAUDE_MONITOR_CONFIG` to point at a file:

```jsonc
{
  "transport": { "http": { "enabled": true, "url": "http://127.0.0.1:7070/event", "timeout_s": 2.0 } },
  "monitor":   { "http_host": "0.0.0.0", "http_port": 7070, "history_max": 50, "start_hidden": true },
  "logging":   { "level": "INFO", "file": "", "max_bytes": 1048576, "backup_count": 5 },
  "sounds":    { "enabled": true }
}
```

Monitor env vars (override the file): `CLAUDE_MONITOR_CONFIG`, `CLAUDE_MONITOR_PORT`,
`CLAUDE_MONITOR_HOST`, `CLAUDE_MONITOR_SOUNDS`, `CLAUDE_MONITOR_LOG_LEVEL`,
`CLAUDE_MONITOR_LOG_FILE`.

Logs and the SQLite database default to `~/.claude/`.

### Hooks (`claudehub-hooks`)

Per-repo transport config lives at `.claude/hooks/monitor_config.json` (created
by `claudehub-hooks install`). Point hooks at a monitor on another machine:

```json
{
  "transport": {
    "http": { "url": "http://192.168.1.50:7070/event" }
  }
}
```

Hook env vars (override the file):

| Variable | Default | Meaning |
|----------|---------|---------|
| `CLAUDE_HUB_URL` | `http://127.0.0.1:7070/event` | Monitor endpoint |
| `CLAUDE_HUB_TIMEOUT` | `2.0` | HTTP timeout (seconds) |
| `CLAUDE_HUB_HTTP_ENABLED` | `true` | Set to `false` to silence all hooks |
| `CLAUDE_HUB_CONFIG` | — | Path to a `monitor_config.json` override |
| `CLAUDE_HOOK_LOG_LEVEL` | `INFO` | Dispatcher log level |
| `CLAUDE_HOOK_LOG_FILE` | `~/.claude/claude_hook.log` | Dispatcher log path |

## Multi-instance & reopen recovery

The monitor tracks status **per session** in SQLite, so:

- Several Claude instances (local or remote) can report at once — the card shows
  the latest, the history/web distinguish by host + session.
- If you close and reopen the monitor mid-turn, it restores the in-progress
  state from `session_status`; a 2-second watchdog idles a session that goes
  silent.

## Requirements

- Python 3.11+ (monitor); Python 3.10+ (hooks)
- `claudehub-monitor`: PySide6 and anthropic (installed automatically)
- `claudehub-hooks`: stdlib only
- Windows for sounds (`winsound`); silently disabled elsewhere.
