Skip to content

Commit bd40216

Browse files
committed
Fix history display and add workspace filtering
- Add JSON tags to database.Edit struct to fix timestamp/content display - Fix inverted navigation in history view (down was going up) - Add workspace filtering for history queries via daemon - Add GetEditsByWorkspace() database function with session join - Create HOOKS.md documentation for hook installation - Add component modules for history, context, plan, prompts, ralph views
1 parent b7fc883 commit bd40216

File tree

23 files changed

+3708
-118
lines changed

23 files changed

+3708
-118
lines changed

.claude/hooks/PostToolUse

Lines changed: 61 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
#!/bin/bash
22
# claude-follow-tui PostToolUse hook
3-
# Sends tool edits to the TUI for real-time display
3+
# Sends tool edits to both the TUI and daemon for real-time display and persistence
44

55
# Get the current directory and resolve to absolute path
66
CWD="$(cd "$(pwd)" && pwd)"
@@ -21,10 +21,65 @@ HASH="$(echo -n "$CWD" | sha256sum | cut -c1-12)"
2121
# Get username
2222
USER="${USER:-unknown}"
2323

24-
# Socket path
25-
SOCKET_PATH="/tmp/claude-follow-tui-${USER}-${HASH}.sock"
24+
# Socket paths
25+
TUI_SOCKET="/tmp/claude-mon-${USER}-${HASH}.sock"
26+
DAEMON_SOCKET="/tmp/claude-mon-daemon.sock"
2627

27-
# Send to TUI if socket exists
28-
if [[ -S "$SOCKET_PATH" ]]; then
29-
echo "$TOOL_INPUT" | nc -U "$SOCKET_PATH" &
28+
# Send to TUI if socket exists (raw TOOL_INPUT)
29+
if [[ -S "$TUI_SOCKET" ]]; then
30+
echo "$TOOL_INPUT" | nc -U "$TUI_SOCKET" &
31+
fi
32+
33+
# Send to daemon if socket exists (formatted payload)
34+
if [[ -S "$DAEMON_SOCKET" ]] && command -v jq &>/dev/null; then
35+
# Parse tool input
36+
TOOL_NAME="${TOOL_NAME:-unknown}"
37+
FILE_PATH=$(echo "$TOOL_INPUT" | jq -r '.file_path // .path // empty' 2>/dev/null)
38+
OLD_STRING=$(echo "$TOOL_INPUT" | jq -r '.old_string // empty' 2>/dev/null | head -c 10000)
39+
NEW_STRING=$(echo "$TOOL_INPUT" | jq -r '.new_string // .content // empty' 2>/dev/null | head -c 10000)
40+
41+
if [[ -n "$FILE_PATH" ]]; then
42+
# Get git info
43+
BRANCH=""
44+
COMMIT_SHA=""
45+
if git rev-parse --git-dir &>/dev/null; then
46+
BRANCH=$(git branch --show-current 2>/dev/null || echo "")
47+
COMMIT_SHA=$(git rev-parse HEAD 2>/dev/null || echo "")
48+
fi
49+
50+
# Calculate line count
51+
LINE_COUNT=0
52+
if [[ -n "$NEW_STRING" ]]; then
53+
LINE_COUNT=$(echo "$NEW_STRING" | wc -l | tr -d ' ')
54+
fi
55+
56+
# Create daemon payload
57+
PAYLOAD=$(jq -n \
58+
--arg type "edit" \
59+
--arg workspace "$CWD" \
60+
--arg workspace_name "$(basename "$CWD")" \
61+
--arg branch "$BRANCH" \
62+
--arg commit_sha "$COMMIT_SHA" \
63+
--arg tool_name "$TOOL_NAME" \
64+
--arg file_path "$FILE_PATH" \
65+
--arg old_string "$OLD_STRING" \
66+
--arg new_string "$NEW_STRING" \
67+
--argjson line_num 0 \
68+
--argjson line_count "$LINE_COUNT" \
69+
'{
70+
type: $type,
71+
workspace: $workspace,
72+
workspace_name: $workspace_name,
73+
branch: $branch,
74+
commit_sha: $commit_sha,
75+
tool_name: $tool_name,
76+
file_path: $file_path,
77+
old_string: $old_string,
78+
new_string: $new_string,
79+
line_num: $line_num,
80+
line_count: $line_count
81+
}')
82+
83+
echo "$PAYLOAD" | nc -U "$DAEMON_SOCKET" &
84+
fi
3085
fi

HOOKS.md

Lines changed: 189 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,189 @@
1+
# Claude Code Hooks Setup
2+
3+
This guide explains how to install the hooks that send Claude's edits to claude-mon for real-time tracking and persistent history.
4+
5+
## Prerequisites
6+
7+
- `jq` - JSON processor (required for daemon communication)
8+
- `nc` (netcat) - For Unix socket communication
9+
- `sha256sum` or equivalent - For workspace hashing
10+
11+
```bash
12+
# macOS
13+
brew install jq netcat
14+
15+
# Ubuntu/Debian
16+
sudo apt install jq netcat-openbsd
17+
```
18+
19+
## Quick Install
20+
21+
### Option 1: Copy to your project
22+
23+
Copy the hook to your project's `.claude/hooks/` directory:
24+
25+
```bash
26+
# From the claude-mon repo
27+
cp .claude/hooks/PostToolUse /path/to/your/project/.claude/hooks/
28+
chmod +x /path/to/your/project/.claude/hooks/PostToolUse
29+
```
30+
31+
### Option 2: Symlink (recommended for multiple projects)
32+
33+
Create a symlink from your project to a central location:
34+
35+
```bash
36+
# Install hook to a central location
37+
mkdir -p ~/.local/share/claude-mon/hooks
38+
cp .claude/hooks/PostToolUse ~/.local/share/claude-mon/hooks/
39+
chmod +x ~/.local/share/claude-mon/hooks/PostToolUse
40+
41+
# In each project, symlink to it
42+
mkdir -p /path/to/your/project/.claude/hooks
43+
ln -s ~/.local/share/claude-mon/hooks/PostToolUse /path/to/your/project/.claude/hooks/PostToolUse
44+
```
45+
46+
### Option 3: Global hook (all projects)
47+
48+
Install as a global Claude Code hook:
49+
50+
```bash
51+
mkdir -p ~/.claude/hooks
52+
cp .claude/hooks/PostToolUse ~/.claude/hooks/
53+
chmod +x ~/.claude/hooks/PostToolUse
54+
```
55+
56+
## What the Hook Does
57+
58+
The `PostToolUse` hook runs after every Claude tool call (Edit, Write, etc.) and:
59+
60+
1. **Sends to TUI** - Raw tool input to the TUI socket for real-time display
61+
2. **Sends to Daemon** - Formatted payload to the daemon for persistent storage
62+
63+
### Data Captured
64+
65+
| Field | Source | Description |
66+
|-------|--------|-------------|
67+
| `workspace` | `$PWD` | Current working directory |
68+
| `workspace_name` | `basename $PWD` | Project name |
69+
| `branch` | `git branch` | Current git branch |
70+
| `commit_sha` | `git rev-parse HEAD` | Current commit |
71+
| `tool_name` | `$TOOL_NAME` | Edit, Write, etc. |
72+
| `file_path` | Tool input | File being modified |
73+
| `old_string` | Tool input | Original content (max 10KB) |
74+
| `new_string` | Tool input | New content (max 10KB) |
75+
76+
## Verifying Installation
77+
78+
### 1. Check the hook is executable
79+
80+
```bash
81+
ls -la .claude/hooks/PostToolUse
82+
# Should show: -rwxr-xr-x ... PostToolUse
83+
```
84+
85+
### 2. Start the daemon
86+
87+
```bash
88+
claude-mon daemon start
89+
```
90+
91+
### 3. Verify daemon is listening
92+
93+
```bash
94+
ls -la /tmp/claude-mon-daemon.sock
95+
# Should show: srwxr-xr-x ... claude-mon-daemon.sock
96+
```
97+
98+
### 4. Test the hook manually
99+
100+
```bash
101+
export TOOL_NAME="Edit"
102+
export TOOL_INPUT='{"file_path": "/tmp/test.go", "old_string": "old", "new_string": "new"}'
103+
.claude/hooks/PostToolUse
104+
105+
# Check database
106+
sqlite3 ~/.claude-mon/claude-mon.db "SELECT * FROM edits ORDER BY id DESC LIMIT 1;"
107+
```
108+
109+
### 5. Make an edit with Claude
110+
111+
Ask Claude to make a small edit, then verify:
112+
113+
```bash
114+
sqlite3 ~/.claude-mon/claude-mon.db "SELECT timestamp, tool_name, file_path FROM edits ORDER BY id DESC LIMIT 5;"
115+
```
116+
117+
## Troubleshooting
118+
119+
### Hook not running
120+
121+
Verify Claude Code is configured to use project hooks:
122+
123+
```bash
124+
# Check Claude settings
125+
cat ~/.config/claude/settings.json
126+
```
127+
128+
Should include hooks enabled or not explicitly disabled.
129+
130+
### Daemon not receiving data
131+
132+
1. Check daemon is running: `claude-mon daemon status`
133+
2. Check socket exists: `ls -la /tmp/claude-mon-daemon.sock`
134+
3. Test socket manually:
135+
```bash
136+
echo '{"type":"edit","workspace":"/tmp/test","workspace_name":"test","tool_name":"Edit","file_path":"/tmp/test.go","old_string":"a","new_string":"b","line_num":0,"line_count":1}' | nc -U /tmp/claude-mon-daemon.sock
137+
```
138+
139+
### jq not found
140+
141+
The hook silently skips daemon communication if `jq` is not installed:
142+
143+
```bash
144+
which jq || echo "jq not installed!"
145+
```
146+
147+
### TUI not receiving updates
148+
149+
The TUI socket is workspace-specific. Check the socket exists:
150+
151+
```bash
152+
# Get the expected socket path
153+
HASH=$(echo -n "$(pwd)" | sha256sum | cut -c1-12)
154+
ls -la "/tmp/claude-mon-${USER}-${HASH}.sock"
155+
```
156+
157+
## Socket Paths
158+
159+
| Socket | Purpose | Path |
160+
|--------|---------|------|
161+
| Daemon | Persistent storage | `/tmp/claude-mon-daemon.sock` |
162+
| TUI | Real-time display | `/tmp/claude-mon-${USER}-${HASH}.sock` |
163+
164+
The TUI socket is unique per workspace (hashed from the directory path).
165+
166+
## Content Limits
167+
168+
To prevent huge payloads:
169+
- `old_string`: Truncated to 10KB
170+
- `new_string`: Truncated to 10KB
171+
172+
For larger edits, only the first 10KB is stored. The full file diff can still be viewed in the TUI by reading the actual file.
173+
174+
## Multiple Projects
175+
176+
Each project can have its own hook, or share a global hook. The workspace path is automatically detected, so the same hook works across all projects.
177+
178+
## Uninstalling
179+
180+
```bash
181+
# Remove project hook
182+
rm .claude/hooks/PostToolUse
183+
184+
# Remove global hook
185+
rm ~/.claude/hooks/PostToolUse
186+
187+
# Stop daemon
188+
claude-mon daemon stop
189+
```

go.mod

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ require (
4242
github.com/muesli/cancelreader v0.2.2 // indirect
4343
github.com/muesli/reflow v0.3.0 // indirect
4444
github.com/rivo/uniseg v0.4.7 // indirect
45+
github.com/sahilm/fuzzy v0.1.1 // indirect
4546
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect
4647
github.com/yuin/goldmark v1.7.8 // indirect
4748
github.com/yuin/goldmark-emoji v1.0.5 // indirect

go.sum

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,8 @@ github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
5757
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
5858
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
5959
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
60+
github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
61+
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
6062
github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY=
6163
github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
6264
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
@@ -84,6 +86,8 @@ github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJ
8486
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
8587
github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
8688
github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
89+
github.com/sahilm/fuzzy v0.1.1 h1:ceu5RHF8DGgoi+/dR5PsECjCDH1BE3Fnmpo7aVXOdRA=
90+
github.com/sahilm/fuzzy v0.1.1/go.mod h1:VFvziUEIMCrT6A6tw2RFIXPXXmzXbOsSHF0DOI8ZK9Y=
8791
github.com/sergi/go-diff v1.4.0 h1:n/SP9D5ad1fORl+llWyN+D6qoUETXNZARKjyY2/KVCw=
8892
github.com/sergi/go-diff v1.4.0/go.mod h1:A0bzQcvG0E7Rwjx0REVgAGH58e96+X0MeOfepqsbeW4=
8993
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=

0 commit comments

Comments
 (0)