claudehq 1.0.0

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/README.md ADDED
@@ -0,0 +1,88 @@
1
+ # Claude HQ
2
+
3
+ A real-time command center for all your Claude Code sessions.
4
+
5
+ ## Features
6
+
7
+ - **Session Discovery**: Automatically discovers all running Claude Code sessions
8
+ - **Real-Time Activity**: Live stream of tool usage, prompts, and responses
9
+ - **Task Management**: View tasks organized by status (Pending, In Progress, Completed)
10
+ - **Linear-Inspired UI**: Clean, modern interface with dark/light themes
11
+ - **Zero Config**: One command setup with automatic hook installation
12
+
13
+ ## Quick Start
14
+
15
+ ```bash
16
+ # Install hooks (one time)
17
+ npx claudehq setup
18
+
19
+ # Start the dashboard
20
+ npx claudehq
21
+ ```
22
+
23
+ Open **http://localhost:3456** - done!
24
+
25
+ ## Requirements
26
+
27
+ - **Node.js** 18+
28
+ - **jq** - `brew install jq` (macOS) or `apt install jq` (Linux)
29
+
30
+ ## Commands
31
+
32
+ ```bash
33
+ npx claudehq # Start server
34
+ npx claudehq setup # Install Claude Code hooks
35
+ npx claudehq status # Check installation
36
+ npx claudehq uninstall # Remove hooks
37
+ ```
38
+
39
+ Or install globally:
40
+
41
+ ```bash
42
+ npm i -g claudehq
43
+ chq setup
44
+ chq
45
+ ```
46
+
47
+ ## How It Works
48
+
49
+ ```
50
+ Claude Code ──▶ Hook Script ──▶ Events File
51
+ │ │
52
+ ▼ ▼
53
+ ┌─────────────────────────┐
54
+ │ Claude HQ Server │
55
+ └───────────┬─────────────┘
56
+ │ SSE
57
+
58
+ ┌─────────────────────────┐
59
+ │ Web Dashboard │
60
+ │ • Sessions & Status │
61
+ │ • Task Board │
62
+ │ • Live Activity Feed │
63
+ └─────────────────────────┘
64
+ ```
65
+
66
+ ## Configuration
67
+
68
+ | Variable | Default | Description |
69
+ |----------|---------|-------------|
70
+ | `PORT` | `3456` | Server port |
71
+ | `TASKS_BOARD_DATA_DIR` | `~/.claude/tasks-board` | Data directory |
72
+
73
+ ## Troubleshooting
74
+
75
+ ```bash
76
+ # Check status
77
+ npx claudehq status
78
+
79
+ # Test hook manually
80
+ echo '{"hook_event_name":"PreToolUse","session_id":"test"}' | ~/.claude/hooks/tasks-board-hook.sh
81
+
82
+ # View events
83
+ tail ~/.claude/tasks-board/events.jsonl
84
+ ```
85
+
86
+ ## License
87
+
88
+ MIT
package/bin/cli.js ADDED
@@ -0,0 +1,56 @@
1
+ #!/usr/bin/env node
2
+
3
+ const { program } = require('commander');
4
+ const path = require('path');
5
+ const { version } = require('../package.json');
6
+
7
+ program
8
+ .name('claudehq')
9
+ .description('Claude HQ - A beautiful command center for Claude Code')
10
+ .version(version);
11
+
12
+ program
13
+ .command('start')
14
+ .description('Start the tasks board server')
15
+ .option('-p, --port <port>', 'Port to run on', '3456')
16
+ .option('--no-open', 'Don\'t open browser automatically')
17
+ .action((options) => {
18
+ process.env.PORT = options.port;
19
+ process.env.NO_OPEN = options.open ? '' : '1';
20
+ require('../lib/server.js');
21
+ });
22
+
23
+ program
24
+ .command('setup')
25
+ .description('Install Claude Code hooks (run this first!)')
26
+ .option('--force', 'Overwrite existing hook script')
27
+ .action(async (options) => {
28
+ const setup = require('../scripts/setup.js');
29
+ await setup.install(options);
30
+ });
31
+
32
+ program
33
+ .command('uninstall')
34
+ .description('Remove Claude Code hooks')
35
+ .action(async () => {
36
+ const setup = require('../scripts/setup.js');
37
+ await setup.uninstall();
38
+ });
39
+
40
+ program
41
+ .command('status')
42
+ .description('Check if hooks are installed and server is running')
43
+ .action(async () => {
44
+ const setup = require('../scripts/setup.js');
45
+ await setup.status();
46
+ });
47
+
48
+ // Default command is start
49
+ program
50
+ .action(() => {
51
+ // If no command specified, run start
52
+ process.env.PORT = process.env.PORT || '3456';
53
+ require('../lib/server.js');
54
+ });
55
+
56
+ program.parse();
@@ -0,0 +1,58 @@
1
+ {
2
+ "hooks": {
3
+ "PreToolUse": [
4
+ {
5
+ "matcher": "",
6
+ "hooks": [
7
+ {
8
+ "type": "command",
9
+ "command": "~/.claude/hooks/tasks-board-hook.sh"
10
+ }
11
+ ]
12
+ }
13
+ ],
14
+ "PostToolUse": [
15
+ {
16
+ "matcher": "",
17
+ "hooks": [
18
+ {
19
+ "type": "command",
20
+ "command": "~/.claude/hooks/tasks-board-hook.sh"
21
+ }
22
+ ]
23
+ }
24
+ ],
25
+ "Stop": [
26
+ {
27
+ "matcher": "",
28
+ "hooks": [
29
+ {
30
+ "type": "command",
31
+ "command": "~/.claude/hooks/tasks-board-hook.sh"
32
+ }
33
+ ]
34
+ }
35
+ ],
36
+ "UserPromptSubmit": [
37
+ {
38
+ "hooks": [
39
+ {
40
+ "type": "command",
41
+ "command": "~/.claude/hooks/tasks-board-hook.sh"
42
+ }
43
+ ]
44
+ }
45
+ ],
46
+ "SubagentStop": [
47
+ {
48
+ "matcher": "",
49
+ "hooks": [
50
+ {
51
+ "type": "command",
52
+ "command": "~/.claude/hooks/tasks-board-hook.sh"
53
+ }
54
+ ]
55
+ }
56
+ ]
57
+ }
58
+ }
@@ -0,0 +1,166 @@
1
+ #!/bin/bash
2
+ # tasks-board-hook.sh - Captures Claude Code events for Tasks Board
3
+ #
4
+ # Installation:
5
+ # 1. Copy this file to ~/.claude/hooks/tasks-board-hook.sh
6
+ # 2. Make it executable: chmod +x ~/.claude/hooks/tasks-board-hook.sh
7
+ # 3. Add hooks to ~/.claude/settings.json (see README)
8
+ #
9
+ # Environment variables:
10
+ # - TASKS_BOARD_DATA_DIR: Directory for events.jsonl (default: ~/.claude/tasks-board)
11
+ # - TASKS_BOARD_SERVER: Server URL for instant delivery (default: http://localhost:3456)
12
+ # - TASKS_BOARD_NOTIFY: Enable HTTP notification (default: true)
13
+
14
+ set -e
15
+
16
+ # Configuration
17
+ DATA_DIR="${TASKS_BOARD_DATA_DIR:-$HOME/.claude/tasks-board}"
18
+ EVENTS_FILE="${DATA_DIR}/events.jsonl"
19
+ SERVER_URL="${TASKS_BOARD_SERVER:-http://localhost:3456}"
20
+ ENABLE_NOTIFY="${TASKS_BOARD_NOTIFY:-true}"
21
+
22
+ # Ensure data directory exists
23
+ mkdir -p "$(dirname "$EVENTS_FILE")"
24
+
25
+ # PATH setup for tools (jq, curl)
26
+ KNOWN_PATHS=("/opt/homebrew/bin" "/usr/local/bin" "$HOME/.local/bin" "/usr/bin" "/bin")
27
+ for dir in "${KNOWN_PATHS[@]}"; do
28
+ [ -d "$dir" ] && export PATH="$dir:$PATH"
29
+ done
30
+
31
+ # Find jq (required)
32
+ JQ=$(command -v jq 2>/dev/null) || {
33
+ echo "tasks-board-hook: ERROR - jq not found" >&2
34
+ exit 1
35
+ }
36
+
37
+ # Find curl (optional)
38
+ CURL=$(command -v curl 2>/dev/null) || CURL=""
39
+
40
+ # Read input from stdin
41
+ input=$(cat)
42
+
43
+ # Extract common fields
44
+ hook_event_name=$("$JQ" -r '.hook_event_name // "unknown"' <<< "$input")
45
+ session_id=$("$JQ" -r '.session_id // "unknown"' <<< "$input")
46
+ cwd=$("$JQ" -r '.cwd // ""' <<< "$input")
47
+
48
+ # Generate timestamp (cross-platform)
49
+ if [[ "$OSTYPE" == "darwin"* ]]; then
50
+ timestamp=$(perl -MTime::HiRes=time -e 'printf "%.0f", time * 1000' 2>/dev/null || echo $(($(date +%s) * 1000)))
51
+ else
52
+ timestamp=$(($(date +%s) * 1000 + 10#$(date +%N | cut -c1-3)))
53
+ fi
54
+
55
+ event_id="${session_id}-${timestamp}-${RANDOM}"
56
+
57
+ # Map event names to types
58
+ case "$hook_event_name" in
59
+ PreToolUse) event_type="pre_tool_use" ;;
60
+ PostToolUse) event_type="post_tool_use" ;;
61
+ Stop) event_type="stop" ;;
62
+ SubagentStop) event_type="subagent_stop" ;;
63
+ SessionStart) event_type="session_start" ;;
64
+ SessionEnd) event_type="session_end" ;;
65
+ UserPromptSubmit) event_type="user_prompt_submit" ;;
66
+ Notification) event_type="notification" ;;
67
+ PreCompact) event_type="pre_compact" ;;
68
+ *) event_type="unknown" ;;
69
+ esac
70
+
71
+ # Build event JSON based on type
72
+ case "$event_type" in
73
+ pre_tool_use)
74
+ tool_name=$("$JQ" -r '.tool_name // ""' <<< "$input")
75
+ tool_input=$("$JQ" -c '.tool_input // {}' <<< "$input")
76
+ tool_use_id=$("$JQ" -r '.tool_use_id // ""' <<< "$input")
77
+ event=$("$JQ" -n -c \
78
+ --arg id "$event_id" \
79
+ --argjson ts "$timestamp" \
80
+ --arg type "$event_type" \
81
+ --arg sid "$session_id" \
82
+ --arg cwd "$cwd" \
83
+ --arg tool "$tool_name" \
84
+ --argjson input "$tool_input" \
85
+ --arg tuid "$tool_use_id" \
86
+ '{id:$id,timestamp:$ts,type:$type,sessionId:$sid,cwd:$cwd,tool:$tool,toolInput:$input,toolUseId:$tuid}')
87
+ ;;
88
+ post_tool_use)
89
+ tool_name=$("$JQ" -r '.tool_name // ""' <<< "$input")
90
+ tool_input=$("$JQ" -c '.tool_input // {}' <<< "$input")
91
+ tool_use_id=$("$JQ" -r '.tool_use_id // ""' <<< "$input")
92
+ # Truncate tool response to avoid huge events
93
+ tool_response=$("$JQ" -c 'if .tool_response | type == "string" and (. | length) > 500 then .tool_response[:500] + "..." else .tool_response // null end' <<< "$input")
94
+ event=$("$JQ" -n -c \
95
+ --arg id "$event_id" \
96
+ --argjson ts "$timestamp" \
97
+ --arg type "$event_type" \
98
+ --arg sid "$session_id" \
99
+ --arg cwd "$cwd" \
100
+ --arg tool "$tool_name" \
101
+ --argjson input "$tool_input" \
102
+ --argjson response "$tool_response" \
103
+ --arg tuid "$tool_use_id" \
104
+ --argjson success true \
105
+ '{id:$id,timestamp:$ts,type:$type,sessionId:$sid,cwd:$cwd,tool:$tool,toolInput:$input,toolResponse:$response,toolUseId:$tuid,success:$success}')
106
+ ;;
107
+ stop)
108
+ # Truncate response text
109
+ response=$("$JQ" -r 'if .response | type == "string" and (. | length) > 1000 then .response[:1000] + "..." else .response // "" end' <<< "$input")
110
+ event=$("$JQ" -n -c \
111
+ --arg id "$event_id" \
112
+ --argjson ts "$timestamp" \
113
+ --arg type "$event_type" \
114
+ --arg sid "$session_id" \
115
+ --arg cwd "$cwd" \
116
+ --arg response "$response" \
117
+ '{id:$id,timestamp:$ts,type:$type,sessionId:$sid,cwd:$cwd,response:$response}')
118
+ ;;
119
+ user_prompt_submit)
120
+ # Truncate prompt text
121
+ prompt=$("$JQ" -r 'if .prompt | type == "string" and (. | length) > 500 then .prompt[:500] + "..." else .prompt // "" end' <<< "$input")
122
+ event=$("$JQ" -n -c \
123
+ --arg id "$event_id" \
124
+ --argjson ts "$timestamp" \
125
+ --arg type "$event_type" \
126
+ --arg sid "$session_id" \
127
+ --arg cwd "$cwd" \
128
+ --arg prompt "$prompt" \
129
+ '{id:$id,timestamp:$ts,type:$type,sessionId:$sid,cwd:$cwd,prompt:$prompt}')
130
+ ;;
131
+ subagent_stop)
132
+ agent_id=$("$JQ" -r '.agent_id // ""' <<< "$input")
133
+ event=$("$JQ" -n -c \
134
+ --arg id "$event_id" \
135
+ --argjson ts "$timestamp" \
136
+ --arg type "$event_type" \
137
+ --arg sid "$session_id" \
138
+ --arg cwd "$cwd" \
139
+ --arg aid "$agent_id" \
140
+ '{id:$id,timestamp:$ts,type:$type,sessionId:$sid,cwd:$cwd,agentId:$aid}')
141
+ ;;
142
+ *)
143
+ event=$("$JQ" -n -c \
144
+ --arg id "$event_id" \
145
+ --argjson ts "$timestamp" \
146
+ --arg type "$event_type" \
147
+ --arg sid "$session_id" \
148
+ --arg cwd "$cwd" \
149
+ '{id:$id,timestamp:$ts,type:$type,sessionId:$sid,cwd:$cwd}')
150
+ ;;
151
+ esac
152
+
153
+ # Append to JSONL file
154
+ echo "$event" >> "$EVENTS_FILE"
155
+
156
+ # Notify server (fire and forget)
157
+ if [ "$ENABLE_NOTIFY" = "true" ] && [ -n "$CURL" ]; then
158
+ "$CURL" -s -X POST "${SERVER_URL}/api/claude-events" \
159
+ -H "Content-Type: application/json" \
160
+ -d "$event" \
161
+ --connect-timeout 1 \
162
+ --max-time 2 \
163
+ >/dev/null 2>&1 &
164
+ fi
165
+
166
+ exit 0