claude-code-watch 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 phiat (https://github.com/phiat/claude-esp)
4
+ Copyright (c) 2026 shuxuecode
5
+
6
+ Permission is hereby granted, free of charge, to any person obtaining a copy
7
+ of this software and associated documentation files (the "Software"), to deal
8
+ in the Software without restriction, including without limitation the rights
9
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10
+ copies of the Software, and to permit persons to whom the Software is
11
+ furnished to do so, subject to the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be included in all
14
+ copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,110 @@
1
+ # claude-watch
2
+
3
+ > Stream Claude Code's hidden output (thinking, tool calls, subagents) to a web browser in real-time.
4
+
5
+ Claude Code writes detailed JSONL logs under `~/.claude/projects/` as it works — including thinking blocks, tool inputs/outputs, subagent activity, and token usage. `claude-watch` tails those logs and streams everything to a local web dashboard, so you can see exactly what Claude Code is doing under the hood.
6
+
7
+ ![](https://img.shields.io/badge/node-%3E%3D18.0.0-brightgreen)
8
+
9
+ ## Features
10
+
11
+ - **Real-time streaming** — thinking, tool calls, tool results, and text responses appear as they happen
12
+ - **Multi-session** — watch all active Claude Code sessions simultaneously in a tree view
13
+ - **Subagent tracking** — see subagent activity nested under their parent session
14
+ - **Token & cost visibility** — tracks input/output/cache tokens per agent, with context window utilization
15
+ - **Filter controls** — toggle thinking, tool input, tool output, and text visibility independently
16
+ - **Auto-discovery** — automatically picks up new sessions as they start (toggleable)
17
+
18
+ ## Quick Start
19
+
20
+ ```bash
21
+ npx claude-watch
22
+ ```
23
+
24
+ This starts the dashboard at `http://localhost:23000` and opens it in your browser.
25
+
26
+ It will auto-discover active Claude Code sessions from `~/.claude/projects/` and start streaming immediately.
27
+
28
+ ## Installation
29
+
30
+ ```bash
31
+ npm install -g claude-watch
32
+ ```
33
+
34
+ Then run:
35
+
36
+ ```bash
37
+ claude-watch
38
+ ```
39
+
40
+ ## Usage
41
+
42
+ ```
43
+ claude-watch [OPTIONS]
44
+
45
+ OPTIONS:
46
+ -p, --port <port> HTTP port (default: 23000)
47
+ -h, --host <host> Bind host (default: 127.0.0.1)
48
+ -s <ID> Watch a specific session by ID
49
+ -n Start from newest (skip history, live only)
50
+ -l [N] List recent sessions (default 10) and exit
51
+ -a [N] List active sessions (default all) and exit
52
+ -w <dur> Active window duration (default 5m, e.g. 30s, 2m, 10m)
53
+ -m <N> Max sessions to show in tree (default 0=unlimited)
54
+ -c <dur> Auto-collapse sessions inactive for this duration (e.g. 2m)
55
+ -D Debug: show raw type:subtype for every JSONL line we'd drop
56
+ --poll <ms> Polling interval in milliseconds (default: 500)
57
+ -v Show version
58
+ --help Show this help
59
+ ```
60
+
61
+ ### Examples
62
+
63
+ ```bash
64
+ # List recent sessions
65
+ claude-watch -l
66
+
67
+ # List active sessions from last 10 minutes
68
+ claude-watch -a -w 10m
69
+
70
+ # Watch a specific session
71
+ claude-watch -s abc123-def456
72
+
73
+ # Live-only mode (don't replay history)
74
+ claude-watch -n
75
+
76
+ # Custom port and host
77
+ claude-watch -p 8080 -h 0.0.0.0
78
+
79
+ # Limit tree to 5 most recent sessions, auto-collapse after 2m of inactivity
80
+ claude-watch -m 5 -c 2m
81
+
82
+ # Debug mode: see every unknown JSONL line type
83
+ claude-watch -D
84
+ ```
85
+
86
+ ## How It Works
87
+
88
+ `claude-watch` monitors the Claude Code project directory (`~/.claude/projects/`) for JSONL log files. Each Claude Code session writes structured JSON lines containing:
89
+
90
+ - `assistant` messages — thinking blocks, text responses, and tool use requests
91
+ - `user` messages — tool results and user prompts
92
+ - `system` messages — turn duration markers, compaction boundaries
93
+ - `attachment` messages — hook outputs and diagnostics
94
+ - Agent metadata — session titles, subagent type info
95
+
96
+ The watcher tails these files (via chokidar fsnotify events, with polling fallback), parses each line into structured stream items, and pushes them to the browser over WebSocket. The browser renders them in a terminal-style dashboard with filtering, tree navigation, and token tracking.
97
+
98
+ ## Environment
99
+
100
+ | Variable | Description |
101
+ |----------|-------------|
102
+ | `CLAUDE_HOME` | Override Claude config directory (default: `~/.claude`) |
103
+
104
+ ## License
105
+
106
+ MIT
107
+
108
+ ## Acknowledgments
109
+
110
+ This project was inspired by and developed based on [claude-esp](https://github.com/phiat/claude-esp) by phiat.
@@ -0,0 +1,30 @@
1
+ # claude-watch
2
+
3
+ claude-watch — 一个 Claude Code 的实时 Web 监控仪表盘。
4
+
5
+ ## 核心作用
6
+
7
+ Claude Code 在运行时会将详细的 JSONL 日志写入 `~/.claude/projects/` 目录,包括思考内容、工具调用、子代理活动、token 使用量等。这些信息在 Claude Code 的正常界面中并不全部可见。`claude-watch` 的作用就是**读取这些隐藏日志,实时流式传输到本地 Web 仪表盘**,让你能看到 Claude Code 的"幕后"工作细节。
8
+
9
+ ## 架构组成
10
+
11
+ 项目由三个核心模块构成:
12
+
13
+ 1. **`src/parser/parser.js`** — JSONL 日志解析器。将 Claude Code 的 JSONL 行解析为结构化的流项目(thinking、tool_input、tool_output、text、turn_marker、hook_output 等),并提取 token 使用量和模型信息。
14
+
15
+ 2. **`src/watcher/watcher.js`** — 文件监视器。使用 chokidar 监听 `~/.claude/projects/` 下的 JSONL 文件变化(带轮询 fallback),管理多会话和子代理的发现与跟踪,增量读取文件新增内容并触发解析。
16
+
17
+ 3. **`src/server/server.js`** — HTTP + WebSocket 服务器。提供静态页面服务、REST API(会话列表、状态、上下文信息)和 WebSocket 实时推送,将解析后的内容广播到浏览器客户端。启动时自动打开浏览器。
18
+
19
+ ## 主要功能
20
+
21
+ - **实时流式传输** — 思考过程、工具调用/结果、文本响应实时呈现
22
+ - **多会话监视** — 同时查看所有活跃的 Claude Code 会话
23
+ - **子代理追踪** — 在父会话下嵌套显示子代理活动
24
+ - **Token/成本追踪** — 每个代理的输入/输出/缓存 token 及上下文窗口利用率
25
+ - **过滤控制** — 独立切换 thinking、工具输入/输出、文本的可见性
26
+ - **自动发现** — 新会话启动时自动纳入监控
27
+
28
+ ## 致谢
29
+
30
+ 本项目基于 [phiat](https://github.com/phiat) 的 [claude-esp](https://github.com/phiat/claude-esp) 项目提供思路并开发。
@@ -0,0 +1,187 @@
1
+ #!/usr/bin/env node
2
+
3
+ 'use strict';
4
+
5
+ const { startServer } = require('../src/server/server');
6
+ const { listSessions, listActiveSessions } = require('../src/watcher/watcher');
7
+
8
+ const VERSION = '0.0.1';
9
+
10
+ function printHelp() {
11
+ console.log(`claude-watch v${VERSION}
12
+
13
+ Stream Claude Code's hidden output (thinking, tool calls, subagents)
14
+ to a web browser.
15
+
16
+ USAGE:
17
+ claude-watch [OPTIONS]
18
+
19
+ OPTIONS:
20
+ -p, --port <port> HTTP port (default: 23000)
21
+ -h, --host <host> Bind host (default: 127.0.0.1)
22
+ -s <ID> Watch a specific session by ID
23
+ -n Start from newest (skip history, live only)
24
+ -l [N] List recent sessions (default 10) and exit
25
+ -a [N] List active sessions (default all) and exit
26
+ -w <dur> Active window duration (default 5m, e.g. 30s, 2m, 10m)
27
+ -m <N> Max sessions to show in tree (default 0=unlimited)
28
+ -c <dur> Auto-collapse sessions inactive for this duration (e.g. 2m)
29
+ -D Debug: show raw type:subtype for every JSONL line we'd drop
30
+ --poll <ms> Polling interval in milliseconds (default: 500)
31
+ -v Show version
32
+ --help Show this help
33
+
34
+ ENVIRONMENT:
35
+ CLAUDE_HOME Override Claude config directory (default: ~/.claude)
36
+ `);
37
+ }
38
+
39
+ function parseDuration(s) {
40
+ const match = s.match(/^(\d+)(ms|s|m|h)$/);
41
+ if (!match) throw new Error(`Invalid duration: ${s}`);
42
+ const val = parseInt(match[1], 10);
43
+ switch (match[2]) {
44
+ case 'ms': return val;
45
+ case 's': return val * 1000;
46
+ case 'm': return val * 60 * 1000;
47
+ case 'h': return val * 3600 * 1000;
48
+ default: throw new Error(`Invalid duration unit: ${match[2]}`);
49
+ }
50
+ }
51
+
52
+ async function main() {
53
+ const args = process.argv.slice(2);
54
+
55
+ const options = {
56
+ port: 23000,
57
+ host: '127.0.0.1',
58
+ sessionID: '',
59
+ skipHistory: false,
60
+ pollMs: 500,
61
+ activeWindow: 5 * 60 * 1000,
62
+ maxSessions: 0,
63
+ collapseAfter: 0,
64
+ debugAll: false,
65
+ };
66
+
67
+ // First pass: collect all option values
68
+ for (let i = 0; i < args.length; i++) {
69
+ switch (args[i]) {
70
+ case '-s':
71
+ options.sessionID = args[++i] || '';
72
+ break;
73
+ case '-n':
74
+ options.skipHistory = true;
75
+ break;
76
+ case '-p':
77
+ case '--port':
78
+ if (i + 1 >= args.length || args[i + 1].startsWith('-')) {
79
+ console.error(`Error: ${args[i]} requires a port number`);
80
+ process.exit(1);
81
+ }
82
+ const pv = parseInt(args[++i], 10);
83
+ if (isNaN(pv)) {
84
+ console.error(`Error: ${args[i - 1]} requires a numeric port, got '${args[i]}'`);
85
+ process.exit(1);
86
+ }
87
+ options.port = pv;
88
+ break;
89
+ case '-h':
90
+ case '--host':
91
+ if (i + 1 >= args.length || args[i + 1].startsWith('-')) {
92
+ console.error(`Error: ${args[i]} requires a host address`);
93
+ process.exit(1);
94
+ }
95
+ options.host = args[++i];
96
+ break;
97
+ case '-w':
98
+ try {
99
+ options.activeWindow = parseDuration(args[++i] || '5m');
100
+ } catch {
101
+ options.activeWindow = 5 * 60 * 1000;
102
+ }
103
+ break;
104
+ case '-c':
105
+ try {
106
+ options.collapseAfter = parseDuration(args[++i] || '5m');
107
+ } catch {
108
+ options.collapseAfter = 5 * 60 * 1000;
109
+ }
110
+ break;
111
+ case '-m':
112
+ options.maxSessions = parseInt(args[++i], 10) || 0;
113
+ break;
114
+ case '-D':
115
+ options.debugAll = true;
116
+ break;
117
+ case '--poll':
118
+ options.pollMs = parseInt(args[++i], 10) || 500;
119
+ break;
120
+ default:
121
+ break;
122
+ }
123
+ }
124
+
125
+ // Second pass: execute action flags with fully resolved options
126
+ for (let i = 0; i < args.length; i++) {
127
+ switch (args[i]) {
128
+ case '-l': {
129
+ const v = parseInt(args[i + 1]);
130
+ const limit = !isNaN(v) ? v : 10;
131
+ if (!isNaN(v)) i++;
132
+ const sessions = await listSessions(limit);
133
+ if (sessions.length === 0) {
134
+ console.log('No sessions found.');
135
+ } else {
136
+ const now = Date.now();
137
+ for (const s of sessions) {
138
+ const age = Math.round((now - new Date(s.modified).getTime()) / 1000);
139
+ const ageStr = age < 60 ? `${age}s ago` : age < 3600 ? `${Math.floor(age / 60)}m ago` : `${Math.floor(age / 3600)}h ago`;
140
+ const active = s.isActive ? '●' : '○';
141
+ const id = s.id.length > 40 ? s.id.slice(0, 37) + '...' : s.id;
142
+ console.log(`${active} ${id} ${s.projectPath || '?'} ${ageStr}`);
143
+ }
144
+ }
145
+ return;
146
+ }
147
+ case '-a': {
148
+ const v = parseInt(args[i + 1]);
149
+ const limit = !isNaN(v) ? v : 0;
150
+ if (!isNaN(v)) i++;
151
+ const sessions = await listActiveSessions(options.activeWindow);
152
+ const result = limit > 0 ? sessions.slice(0, limit) : sessions;
153
+ if (result.length === 0) {
154
+ console.log('No active sessions found.');
155
+ } else {
156
+ const now = Date.now();
157
+ for (const s of result) {
158
+ const age = Math.round((now - new Date(s.modified).getTime()) / 1000);
159
+ const ageStr = age < 60 ? `${age}s ago` : age < 3600 ? `${Math.floor(age / 60)}m ago` : `${Math.floor(age / 3600)}h ago`;
160
+ const id = s.id.length > 40 ? s.id.slice(0, 37) + '...' : s.id;
161
+ console.log(`● ${id} ${s.projectPath || '?'} ${ageStr}`);
162
+ }
163
+ }
164
+ return;
165
+ }
166
+ case '-v':
167
+ console.log(`claude-watch v${VERSION}`);
168
+ return;
169
+ case '--help':
170
+ printHelp();
171
+ return;
172
+ default:
173
+ if (args[i].startsWith('-')) {
174
+ console.error(`Unknown option: ${args[i]}`);
175
+ printHelp();
176
+ process.exit(1);
177
+ }
178
+ }
179
+ }
180
+
181
+ startServer(options);
182
+ }
183
+
184
+ main().catch(err => {
185
+ console.error(`Fatal error: ${err.message}`);
186
+ process.exit(1);
187
+ });
package/package.json ADDED
@@ -0,0 +1,38 @@
1
+ {
2
+ "name": "claude-code-watch",
3
+ "version": "0.0.1",
4
+ "description": "Web-based real-time monitor for Claude Code.",
5
+ "main": "./src/server/server.js",
6
+ "bin": {
7
+ "claude-watch": "./bin/claude-watch.js"
8
+ },
9
+ "scripts": {
10
+ "start": "node bin/claude-watch.js",
11
+ "dev": "node --watch bin/claude-watch.js",
12
+ "test": "node --test tests/all.test.js tests/watcher.test.js tests/server.test.js"
13
+ },
14
+ "files": [
15
+ "bin",
16
+ "src",
17
+ "public"
18
+ ],
19
+ "keywords": [
20
+ "claude",
21
+ "claude-code",
22
+ "watcher",
23
+ "web",
24
+ "dashboard"
25
+ ],
26
+ "license": "MIT",
27
+ "repository": {
28
+ "type": "git",
29
+ "url": "https://github.com/shuxuecode/claude-watch"
30
+ },
31
+ "engines": {
32
+ "node": ">=18.0.0"
33
+ },
34
+ "dependencies": {
35
+ "chokidar": "^4.0.3",
36
+ "ws": "^8.20.0"
37
+ }
38
+ }