telegram-claude-mcp 2.0.3 → 2.0.4
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/bin/daemon-ctl.js +4 -3
- package/bin/setup.js +228 -157
- package/package.json +1 -1
package/bin/daemon-ctl.js
CHANGED
|
@@ -76,12 +76,13 @@ async function start() {
|
|
|
76
76
|
|
|
77
77
|
// Determine which script to run
|
|
78
78
|
let script = daemonScript;
|
|
79
|
-
let
|
|
79
|
+
let args = [script];
|
|
80
80
|
|
|
81
81
|
if (!existsSync(daemonScript)) {
|
|
82
82
|
if (existsSync(daemonSrc)) {
|
|
83
83
|
script = daemonSrc;
|
|
84
|
-
|
|
84
|
+
// Use node with tsx for TypeScript support
|
|
85
|
+
args = ['--import', 'tsx', script];
|
|
85
86
|
} else {
|
|
86
87
|
console.error('Error: Daemon script not found. Run `npm run build` first.');
|
|
87
88
|
process.exit(1);
|
|
@@ -90,7 +91,7 @@ async function start() {
|
|
|
90
91
|
|
|
91
92
|
console.log('Starting telegram-claude-daemon...');
|
|
92
93
|
|
|
93
|
-
const child = spawn(
|
|
94
|
+
const child = spawn('node', args, {
|
|
94
95
|
detached: true,
|
|
95
96
|
stdio: ['ignore', 'pipe', 'pipe'],
|
|
96
97
|
env: process.env,
|
package/bin/setup.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
|
-
* Setup script for telegram-claude-mcp hooks integration
|
|
4
|
+
* Setup script for telegram-claude-mcp hooks integration (v2 - Daemon Architecture)
|
|
5
5
|
*
|
|
6
6
|
* Usage: npx telegram-claude-mcp setup
|
|
7
7
|
*
|
|
@@ -22,202 +22,101 @@ const CLAUDE_DIR = join(homedir(), '.claude');
|
|
|
22
22
|
const HOOKS_DIR = join(CLAUDE_DIR, 'hooks');
|
|
23
23
|
const SETTINGS_FILE = join(CLAUDE_DIR, 'settings.json');
|
|
24
24
|
|
|
25
|
-
//
|
|
26
|
-
const
|
|
27
|
-
|
|
28
|
-
# Claude Code Permission Hook - forwards permission requests to Telegram
|
|
29
|
-
# Set SESSION_NAME env var to target a specific session (for multi-session setups)
|
|
30
|
-
#
|
|
31
|
-
|
|
32
|
-
SESSION_DIR="/tmp/telegram-claude-sessions"
|
|
33
|
-
|
|
34
|
-
find_session() {
|
|
35
|
-
if [ -n "$SESSION_NAME" ]; then
|
|
36
|
-
local specific_file="$SESSION_DIR/\${SESSION_NAME}.info"
|
|
37
|
-
if [ -f "$specific_file" ]; then
|
|
38
|
-
local pid
|
|
39
|
-
pid=$(jq -r '.pid // empty' "$specific_file" 2>/dev/null)
|
|
40
|
-
if [ -n "$pid" ] && kill -0 "$pid" 2>/dev/null; then
|
|
41
|
-
echo "$specific_file"
|
|
42
|
-
return
|
|
43
|
-
fi
|
|
44
|
-
fi
|
|
45
|
-
return
|
|
46
|
-
fi
|
|
25
|
+
// Default daemon configuration
|
|
26
|
+
const DAEMON_PORT = 3333;
|
|
27
|
+
const DAEMON_HOST = 'localhost';
|
|
47
28
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
[ -d "$SESSION_DIR" ] || return
|
|
51
|
-
|
|
52
|
-
for info_file in "$SESSION_DIR"/*.info; do
|
|
53
|
-
[ -e "$info_file" ] || continue
|
|
54
|
-
local pid
|
|
55
|
-
pid=$(jq -r '.pid // empty' "$info_file" 2>/dev/null)
|
|
56
|
-
if [ -n "$pid" ] && kill -0 "$pid" 2>/dev/null; then
|
|
57
|
-
local file_time
|
|
58
|
-
file_time=$(stat -f %m "$info_file" 2>/dev/null || stat -c %Y "$info_file" 2>/dev/null)
|
|
59
|
-
if [ "$file_time" -gt "$latest_time" ]; then
|
|
60
|
-
latest_time=$file_time
|
|
61
|
-
latest_file=$info_file
|
|
62
|
-
fi
|
|
63
|
-
fi
|
|
64
|
-
done
|
|
65
|
-
echo "$latest_file"
|
|
66
|
-
}
|
|
29
|
+
// Hook scripts content - v2 daemon architecture
|
|
30
|
+
// These hooks communicate with a single daemon process via HTTP
|
|
67
31
|
|
|
68
|
-
|
|
69
|
-
|
|
32
|
+
const PERMISSION_HOOK = `#!/bin/bash
|
|
33
|
+
# Permission hook for telegram-claude-daemon (v2)
|
|
34
|
+
# Sends permission requests to the daemon for Telegram approval
|
|
35
|
+
|
|
36
|
+
DAEMON_PORT="\${TELEGRAM_CLAUDE_PORT:-${DAEMON_PORT}}"
|
|
37
|
+
DAEMON_HOST="\${TELEGRAM_CLAUDE_HOST:-${DAEMON_HOST}}"
|
|
38
|
+
HOOK_URL="http://\${DAEMON_HOST}:\${DAEMON_PORT}/permission"
|
|
70
39
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
40
|
+
# Auto-detect project name from current working directory
|
|
41
|
+
PROJECT_NAME=$(basename "$(pwd)")
|
|
42
|
+
BASE_SESSION="\${SESSION_NAME:-default}"
|
|
43
|
+
FULL_SESSION_NAME="\${BASE_SESSION}:\${PROJECT_NAME}"
|
|
74
44
|
|
|
75
45
|
INPUT=$(cat)
|
|
76
|
-
TOOL_NAME=$(echo "$INPUT" | jq -r '.tool_name // empty')
|
|
77
|
-
TOOL_INPUT=$(echo "$INPUT" | jq -c '.tool_input // {}')
|
|
78
46
|
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
47
|
+
TOOL_NAME=$(echo "$INPUT" | jq -r '.tool_name // .toolName // .name // empty')
|
|
48
|
+
TOOL_INPUT=$(echo "$INPUT" | jq -c '.tool_input // .toolInput // .input // .arguments // {}')
|
|
49
|
+
|
|
50
|
+
[ -z "$TOOL_NAME" ] && exit 0
|
|
83
51
|
|
|
84
52
|
PAYLOAD=$(jq -n \\
|
|
85
53
|
--arg tool_name "$TOOL_NAME" \\
|
|
86
54
|
--argjson tool_input "$TOOL_INPUT" \\
|
|
87
|
-
|
|
55
|
+
--arg session_name "$FULL_SESSION_NAME" \\
|
|
56
|
+
'{tool_name: $tool_name, tool_input: $tool_input, session_name: $session_name}')
|
|
88
57
|
|
|
89
58
|
RESPONSE=$(curl -s -X POST "$HOOK_URL" \\
|
|
90
59
|
-H "Content-Type: application/json" \\
|
|
60
|
+
-H "X-Session-Name: $FULL_SESSION_NAME" \\
|
|
91
61
|
-d "$PAYLOAD" \\
|
|
92
|
-
--max-time 600)
|
|
62
|
+
--max-time 600 2>/dev/null)
|
|
93
63
|
|
|
94
|
-
[ $? -eq 0 ] && echo "$RESPONSE"
|
|
64
|
+
[ $? -eq 0 ] && [ -n "$RESPONSE" ] && echo "$RESPONSE"
|
|
95
65
|
`;
|
|
96
66
|
|
|
97
67
|
const STOP_HOOK = `#!/bin/bash
|
|
98
|
-
#
|
|
99
|
-
#
|
|
100
|
-
# Set SESSION_NAME env var to target a specific session (for multi-session setups)
|
|
101
|
-
#
|
|
102
|
-
|
|
103
|
-
SESSION_DIR="/tmp/telegram-claude-sessions"
|
|
104
|
-
|
|
105
|
-
find_session() {
|
|
106
|
-
if [ -n "$SESSION_NAME" ]; then
|
|
107
|
-
local specific_file="$SESSION_DIR/\${SESSION_NAME}.info"
|
|
108
|
-
if [ -f "$specific_file" ]; then
|
|
109
|
-
local pid
|
|
110
|
-
pid=$(jq -r '.pid // empty' "$specific_file" 2>/dev/null)
|
|
111
|
-
if [ -n "$pid" ] && kill -0 "$pid" 2>/dev/null; then
|
|
112
|
-
echo "$specific_file"
|
|
113
|
-
return
|
|
114
|
-
fi
|
|
115
|
-
fi
|
|
116
|
-
return
|
|
117
|
-
fi
|
|
68
|
+
# Stop hook for telegram-claude-daemon (v2)
|
|
69
|
+
# Sends stop notification and waits for user response
|
|
118
70
|
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
if [ -n "$pid" ] && kill -0 "$pid" 2>/dev/null; then
|
|
128
|
-
local file_time
|
|
129
|
-
file_time=$(stat -f %m "$info_file" 2>/dev/null || stat -c %Y "$info_file" 2>/dev/null)
|
|
130
|
-
if [ "$file_time" -gt "$latest_time" ]; then
|
|
131
|
-
latest_time=$file_time
|
|
132
|
-
latest_file=$info_file
|
|
133
|
-
fi
|
|
134
|
-
fi
|
|
135
|
-
done
|
|
136
|
-
echo "$latest_file"
|
|
137
|
-
}
|
|
71
|
+
DAEMON_PORT="\${TELEGRAM_CLAUDE_PORT:-${DAEMON_PORT}}"
|
|
72
|
+
DAEMON_HOST="\${TELEGRAM_CLAUDE_HOST:-${DAEMON_HOST}}"
|
|
73
|
+
HOOK_URL="http://\${DAEMON_HOST}:\${DAEMON_PORT}/stop"
|
|
74
|
+
|
|
75
|
+
# Auto-detect project name from current working directory
|
|
76
|
+
PROJECT_NAME=$(basename "$(pwd)")
|
|
77
|
+
BASE_SESSION="\${SESSION_NAME:-default}"
|
|
78
|
+
FULL_SESSION_NAME="\${BASE_SESSION}:\${PROJECT_NAME}"
|
|
138
79
|
|
|
139
80
|
INPUT=$(cat)
|
|
140
81
|
|
|
82
|
+
# Prevent recursion
|
|
141
83
|
STOP_HOOK_ACTIVE=$(echo "$INPUT" | jq -r '.stop_hook_active // false')
|
|
142
84
|
[ "$STOP_HOOK_ACTIVE" = "true" ] && exit 0
|
|
143
85
|
|
|
144
|
-
INFO_FILE=$(find_session)
|
|
145
|
-
[ -z "$INFO_FILE" ] || [ ! -f "$INFO_FILE" ] && exit 0
|
|
146
|
-
|
|
147
|
-
HOOK_PORT=$(jq -r '.port' "$INFO_FILE")
|
|
148
|
-
HOOK_HOST=$(jq -r '.host // "localhost"' "$INFO_FILE")
|
|
149
|
-
HOOK_URL="http://\${HOOK_HOST}:\${HOOK_PORT}/stop"
|
|
150
|
-
|
|
151
86
|
TRANSCRIPT_PATH=$(echo "$INPUT" | jq -r '.transcript_path // empty')
|
|
152
87
|
|
|
153
88
|
PAYLOAD=$(jq -n \\
|
|
154
89
|
--arg transcript_path "$TRANSCRIPT_PATH" \\
|
|
155
|
-
|
|
90
|
+
--arg session_name "$FULL_SESSION_NAME" \\
|
|
91
|
+
'{transcript_path: $transcript_path, session_name: $session_name}')
|
|
156
92
|
|
|
157
93
|
RESPONSE=$(curl -s -X POST "$HOOK_URL" \\
|
|
158
94
|
-H "Content-Type: application/json" \\
|
|
95
|
+
-H "X-Session-Name: $FULL_SESSION_NAME" \\
|
|
159
96
|
-d "$PAYLOAD" \\
|
|
160
|
-
--max-time 300)
|
|
97
|
+
--max-time 300 2>/dev/null)
|
|
161
98
|
|
|
162
|
-
if [ $? -eq 0 ]; then
|
|
99
|
+
if [ $? -eq 0 ] && [ -n "$RESPONSE" ]; then
|
|
163
100
|
DECISION=$(echo "$RESPONSE" | jq -r '.decision // empty')
|
|
164
|
-
|
|
165
|
-
if [ "$DECISION" = "block" ] && [ -n "$REASON" ]; then
|
|
101
|
+
if [ "$DECISION" = "block" ]; then
|
|
166
102
|
echo "$RESPONSE"
|
|
167
|
-
# Exit code 2 tells Claude Code to continue with the reason as instructions
|
|
168
103
|
exit 2
|
|
169
104
|
fi
|
|
170
105
|
fi
|
|
171
106
|
`;
|
|
172
107
|
|
|
173
108
|
const NOTIFY_HOOK = `#!/bin/bash
|
|
174
|
-
#
|
|
175
|
-
#
|
|
176
|
-
# Set SESSION_NAME env var to target a specific session (for multi-session setups)
|
|
177
|
-
#
|
|
178
|
-
|
|
179
|
-
SESSION_DIR="/tmp/telegram-claude-sessions"
|
|
180
|
-
|
|
181
|
-
find_session() {
|
|
182
|
-
if [ -n "$SESSION_NAME" ]; then
|
|
183
|
-
local specific_file="$SESSION_DIR/\${SESSION_NAME}.info"
|
|
184
|
-
if [ -f "$specific_file" ]; then
|
|
185
|
-
local pid
|
|
186
|
-
pid=$(jq -r '.pid // empty' "$specific_file" 2>/dev/null)
|
|
187
|
-
if [ -n "$pid" ] && kill -0 "$pid" 2>/dev/null; then
|
|
188
|
-
echo "$specific_file"
|
|
189
|
-
return
|
|
190
|
-
fi
|
|
191
|
-
fi
|
|
192
|
-
return
|
|
193
|
-
fi
|
|
109
|
+
# Notification hook for telegram-claude-daemon (v2)
|
|
110
|
+
# Sends notifications to the daemon (fire-and-forget)
|
|
194
111
|
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
for info_file in "$SESSION_DIR"/*.info; do
|
|
200
|
-
[ -e "$info_file" ] || continue
|
|
201
|
-
local pid
|
|
202
|
-
pid=$(jq -r '.pid // empty' "$info_file" 2>/dev/null)
|
|
203
|
-
if [ -n "$pid" ] && kill -0 "$pid" 2>/dev/null; then
|
|
204
|
-
local file_time
|
|
205
|
-
file_time=$(stat -f %m "$info_file" 2>/dev/null || stat -c %Y "$info_file" 2>/dev/null)
|
|
206
|
-
if [ "$file_time" -gt "$latest_time" ]; then
|
|
207
|
-
latest_time=$file_time
|
|
208
|
-
latest_file=$info_file
|
|
209
|
-
fi
|
|
210
|
-
fi
|
|
211
|
-
done
|
|
212
|
-
echo "$latest_file"
|
|
213
|
-
}
|
|
112
|
+
DAEMON_PORT="\${TELEGRAM_CLAUDE_PORT:-${DAEMON_PORT}}"
|
|
113
|
+
DAEMON_HOST="\${TELEGRAM_CLAUDE_HOST:-${DAEMON_HOST}}"
|
|
114
|
+
HOOK_URL="http://\${DAEMON_HOST}:\${DAEMON_PORT}/notify"
|
|
214
115
|
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
HOOK_HOST=$(jq -r '.host // "localhost"' "$INFO_FILE")
|
|
220
|
-
HOOK_URL="http://\${HOOK_HOST}:\${HOOK_PORT}/notify"
|
|
116
|
+
# Auto-detect project name from current working directory
|
|
117
|
+
PROJECT_NAME=$(basename "$(pwd)")
|
|
118
|
+
BASE_SESSION="\${SESSION_NAME:-default}"
|
|
119
|
+
FULL_SESSION_NAME="\${BASE_SESSION}:\${PROJECT_NAME}"
|
|
221
120
|
|
|
222
121
|
INPUT=$(cat)
|
|
223
122
|
MESSAGE=$(echo "$INPUT" | jq -r '.message // "Notification from Claude"')
|
|
@@ -225,12 +124,137 @@ MESSAGE=$(echo "$INPUT" | jq -r '.message // "Notification from Claude"')
|
|
|
225
124
|
PAYLOAD=$(jq -n \\
|
|
226
125
|
--arg type "notification" \\
|
|
227
126
|
--arg message "$MESSAGE" \\
|
|
228
|
-
|
|
127
|
+
--arg session_name "$FULL_SESSION_NAME" \\
|
|
128
|
+
'{type: $type, message: $message, session_name: $session_name}')
|
|
229
129
|
|
|
230
130
|
curl -s -X POST "$HOOK_URL" \\
|
|
231
131
|
-H "Content-Type: application/json" \\
|
|
132
|
+
-H "X-Session-Name: $FULL_SESSION_NAME" \\
|
|
232
133
|
-d "$PAYLOAD" \\
|
|
233
|
-
--max-time 10
|
|
134
|
+
--max-time 10 >/dev/null 2>&1
|
|
135
|
+
`;
|
|
136
|
+
|
|
137
|
+
// Progress tracking hooks - fire-and-forget for real-time progress updates
|
|
138
|
+
|
|
139
|
+
const PROGRESS_PRE_TOOL_HOOK = `#!/bin/bash
|
|
140
|
+
# PreToolUse progress hook for telegram-claude-daemon
|
|
141
|
+
# Sends tool start events to update progress display (fire-and-forget)
|
|
142
|
+
|
|
143
|
+
DAEMON_PORT="\${TELEGRAM_CLAUDE_PORT:-${DAEMON_PORT}}"
|
|
144
|
+
DAEMON_HOST="\${TELEGRAM_CLAUDE_HOST:-${DAEMON_HOST}}"
|
|
145
|
+
HOOK_URL="http://\${DAEMON_HOST}:\${DAEMON_PORT}/progress"
|
|
146
|
+
|
|
147
|
+
# Auto-detect project name from current working directory
|
|
148
|
+
PROJECT_NAME=$(basename "$(pwd)")
|
|
149
|
+
BASE_SESSION="\${SESSION_NAME:-default}"
|
|
150
|
+
FULL_SESSION_NAME="\${BASE_SESSION}:\${PROJECT_NAME}"
|
|
151
|
+
|
|
152
|
+
INPUT=$(cat)
|
|
153
|
+
|
|
154
|
+
TOOL_NAME=$(echo "$INPUT" | jq -r '.tool_name // .toolName // .name // empty')
|
|
155
|
+
TOOL_INPUT=$(echo "$INPUT" | jq -c '.tool_input // .toolInput // .input // .arguments // {}')
|
|
156
|
+
|
|
157
|
+
[ -z "$TOOL_NAME" ] && exit 0
|
|
158
|
+
|
|
159
|
+
# Skip progress for Telegram tools (would be recursive)
|
|
160
|
+
case "$TOOL_NAME" in
|
|
161
|
+
mcp__telegram__*|send_message|continue_chat|notify_user|end_chat)
|
|
162
|
+
exit 0
|
|
163
|
+
;;
|
|
164
|
+
esac
|
|
165
|
+
|
|
166
|
+
PAYLOAD=$(jq -n \\
|
|
167
|
+
--arg type "pre_tool" \\
|
|
168
|
+
--arg session_name "$FULL_SESSION_NAME" \\
|
|
169
|
+
--arg tool_name "$TOOL_NAME" \\
|
|
170
|
+
--argjson tool_input "$TOOL_INPUT" \\
|
|
171
|
+
--arg timestamp "$(date -u +%Y-%m-%dT%H:%M:%SZ)" \\
|
|
172
|
+
'{type: $type, session_name: $session_name, tool_name: $tool_name, tool_input: $tool_input, timestamp: $timestamp}')
|
|
173
|
+
|
|
174
|
+
# Fire and forget
|
|
175
|
+
(curl -s -X POST "$HOOK_URL" \\
|
|
176
|
+
-H "Content-Type: application/json" \\
|
|
177
|
+
-H "X-Session-Name: $FULL_SESSION_NAME" \\
|
|
178
|
+
-d "$PAYLOAD" \\
|
|
179
|
+
--max-time 2 2>/dev/null) &
|
|
180
|
+
|
|
181
|
+
exit 0
|
|
182
|
+
`;
|
|
183
|
+
|
|
184
|
+
const PROGRESS_POST_TOOL_HOOK = `#!/bin/bash
|
|
185
|
+
# PostToolUse progress hook for telegram-claude-daemon
|
|
186
|
+
# Sends tool completion events to update progress display (fire-and-forget)
|
|
187
|
+
|
|
188
|
+
DAEMON_PORT="\${TELEGRAM_CLAUDE_PORT:-${DAEMON_PORT}}"
|
|
189
|
+
DAEMON_HOST="\${TELEGRAM_CLAUDE_HOST:-${DAEMON_HOST}}"
|
|
190
|
+
HOOK_URL="http://\${DAEMON_HOST}:\${DAEMON_PORT}/progress"
|
|
191
|
+
|
|
192
|
+
# Auto-detect project name from current working directory
|
|
193
|
+
PROJECT_NAME=$(basename "$(pwd)")
|
|
194
|
+
BASE_SESSION="\${SESSION_NAME:-default}"
|
|
195
|
+
FULL_SESSION_NAME="\${BASE_SESSION}:\${PROJECT_NAME}"
|
|
196
|
+
|
|
197
|
+
INPUT=$(cat)
|
|
198
|
+
|
|
199
|
+
TOOL_NAME=$(echo "$INPUT" | jq -r '.tool_name // .toolName // .name // empty')
|
|
200
|
+
|
|
201
|
+
IS_ERROR=$(echo "$INPUT" | jq -r '.is_error // .isError // false')
|
|
202
|
+
[ "$IS_ERROR" = "true" ] && SUCCESS="false" || SUCCESS="true"
|
|
203
|
+
|
|
204
|
+
[ -z "$TOOL_NAME" ] && exit 0
|
|
205
|
+
|
|
206
|
+
# Skip progress for Telegram tools
|
|
207
|
+
case "$TOOL_NAME" in
|
|
208
|
+
mcp__telegram__*|send_message|continue_chat|notify_user|end_chat)
|
|
209
|
+
exit 0
|
|
210
|
+
;;
|
|
211
|
+
esac
|
|
212
|
+
|
|
213
|
+
PAYLOAD=$(jq -n \\
|
|
214
|
+
--arg type "post_tool" \\
|
|
215
|
+
--arg session_name "$FULL_SESSION_NAME" \\
|
|
216
|
+
--arg tool_name "$TOOL_NAME" \\
|
|
217
|
+
--argjson success "$SUCCESS" \\
|
|
218
|
+
--arg timestamp "$(date -u +%Y-%m-%dT%H:%M:%SZ)" \\
|
|
219
|
+
'{type: $type, session_name: $session_name, tool_name: $tool_name, success: $success, timestamp: $timestamp}')
|
|
220
|
+
|
|
221
|
+
# Fire and forget
|
|
222
|
+
(curl -s -X POST "$HOOK_URL" \\
|
|
223
|
+
-H "Content-Type: application/json" \\
|
|
224
|
+
-H "X-Session-Name: $FULL_SESSION_NAME" \\
|
|
225
|
+
-d "$PAYLOAD" \\
|
|
226
|
+
--max-time 2 2>/dev/null) &
|
|
227
|
+
|
|
228
|
+
exit 0
|
|
229
|
+
`;
|
|
230
|
+
|
|
231
|
+
const PROGRESS_STOP_HOOK = `#!/bin/bash
|
|
232
|
+
# Stop progress hook for telegram-claude-daemon
|
|
233
|
+
# Sends stop event to finalize progress display (fire-and-forget)
|
|
234
|
+
|
|
235
|
+
DAEMON_PORT="\${TELEGRAM_CLAUDE_PORT:-${DAEMON_PORT}}"
|
|
236
|
+
DAEMON_HOST="\${TELEGRAM_CLAUDE_HOST:-${DAEMON_HOST}}"
|
|
237
|
+
HOOK_URL="http://\${DAEMON_HOST}:\${DAEMON_PORT}/progress"
|
|
238
|
+
|
|
239
|
+
# Auto-detect project name from current working directory
|
|
240
|
+
PROJECT_NAME=$(basename "$(pwd)")
|
|
241
|
+
BASE_SESSION="\${SESSION_NAME:-default}"
|
|
242
|
+
FULL_SESSION_NAME="\${BASE_SESSION}:\${PROJECT_NAME}"
|
|
243
|
+
|
|
244
|
+
PAYLOAD=$(jq -n \\
|
|
245
|
+
--arg type "stop" \\
|
|
246
|
+
--arg session_name "$FULL_SESSION_NAME" \\
|
|
247
|
+
--arg timestamp "$(date -u +%Y-%m-%dT%H:%M:%SZ)" \\
|
|
248
|
+
'{type: $type, session_name: $session_name, timestamp: $timestamp}')
|
|
249
|
+
|
|
250
|
+
# Fire and forget
|
|
251
|
+
(curl -s -X POST "$HOOK_URL" \\
|
|
252
|
+
-H "Content-Type: application/json" \\
|
|
253
|
+
-H "X-Session-Name: $FULL_SESSION_NAME" \\
|
|
254
|
+
-d "$PAYLOAD" \\
|
|
255
|
+
--max-time 2 2>/dev/null) &
|
|
256
|
+
|
|
257
|
+
exit 0
|
|
234
258
|
`;
|
|
235
259
|
|
|
236
260
|
// Generate hooks configuration for Claude settings
|
|
@@ -238,6 +262,7 @@ curl -s -X POST "$HOOK_URL" \\
|
|
|
238
262
|
function generateHooksConfig(sessionName = 'default') {
|
|
239
263
|
const envPrefix = `SESSION_NAME=${sessionName}`;
|
|
240
264
|
return {
|
|
265
|
+
// Permission request handling
|
|
241
266
|
PermissionRequest: [
|
|
242
267
|
{
|
|
243
268
|
matcher: '*',
|
|
@@ -249,17 +274,23 @@ function generateHooksConfig(sessionName = 'default') {
|
|
|
249
274
|
]
|
|
250
275
|
}
|
|
251
276
|
],
|
|
277
|
+
// Stop hook with interactive continue option
|
|
252
278
|
Stop: [
|
|
253
279
|
{
|
|
254
280
|
matcher: '*',
|
|
255
281
|
hooks: [
|
|
256
282
|
{
|
|
257
283
|
type: 'command',
|
|
258
|
-
command: `${envPrefix} ~/.claude/hooks/stop-hook.sh`
|
|
284
|
+
command: `${envPrefix} ~/.claude/hooks/progress-stop-hook.sh` // Progress finalization
|
|
285
|
+
},
|
|
286
|
+
{
|
|
287
|
+
type: 'command',
|
|
288
|
+
command: `${envPrefix} ~/.claude/hooks/stop-hook.sh` // Interactive stop
|
|
259
289
|
}
|
|
260
290
|
]
|
|
261
291
|
}
|
|
262
292
|
],
|
|
293
|
+
// Notification forwarding
|
|
263
294
|
Notification: [
|
|
264
295
|
{
|
|
265
296
|
matcher: '*',
|
|
@@ -270,6 +301,30 @@ function generateHooksConfig(sessionName = 'default') {
|
|
|
270
301
|
}
|
|
271
302
|
]
|
|
272
303
|
}
|
|
304
|
+
],
|
|
305
|
+
// Progress tracking - PreToolUse
|
|
306
|
+
PreToolUse: [
|
|
307
|
+
{
|
|
308
|
+
matcher: '*',
|
|
309
|
+
hooks: [
|
|
310
|
+
{
|
|
311
|
+
type: 'command',
|
|
312
|
+
command: `${envPrefix} ~/.claude/hooks/progress-pre-tool-hook.sh`
|
|
313
|
+
}
|
|
314
|
+
]
|
|
315
|
+
}
|
|
316
|
+
],
|
|
317
|
+
// Progress tracking - PostToolUse
|
|
318
|
+
PostToolUse: [
|
|
319
|
+
{
|
|
320
|
+
matcher: '*',
|
|
321
|
+
hooks: [
|
|
322
|
+
{
|
|
323
|
+
type: 'command',
|
|
324
|
+
command: `${envPrefix} ~/.claude/hooks/progress-post-tool-hook.sh`
|
|
325
|
+
}
|
|
326
|
+
]
|
|
327
|
+
}
|
|
273
328
|
]
|
|
274
329
|
};
|
|
275
330
|
}
|
|
@@ -284,7 +339,7 @@ function setup() {
|
|
|
284
339
|
console.log('✓ Created ~/.claude/hooks/');
|
|
285
340
|
}
|
|
286
341
|
|
|
287
|
-
// Write hook scripts
|
|
342
|
+
// Write hook scripts - core hooks
|
|
288
343
|
const permissionHookPath = join(HOOKS_DIR, 'permission-hook.sh');
|
|
289
344
|
writeFileSync(permissionHookPath, PERMISSION_HOOK);
|
|
290
345
|
chmodSync(permissionHookPath, 0o755);
|
|
@@ -300,6 +355,22 @@ function setup() {
|
|
|
300
355
|
chmodSync(notifyHookPath, 0o755);
|
|
301
356
|
console.log('✓ Installed notify-hook.sh');
|
|
302
357
|
|
|
358
|
+
// Write hook scripts - progress tracking
|
|
359
|
+
const progressPreToolPath = join(HOOKS_DIR, 'progress-pre-tool-hook.sh');
|
|
360
|
+
writeFileSync(progressPreToolPath, PROGRESS_PRE_TOOL_HOOK);
|
|
361
|
+
chmodSync(progressPreToolPath, 0o755);
|
|
362
|
+
console.log('✓ Installed progress-pre-tool-hook.sh');
|
|
363
|
+
|
|
364
|
+
const progressPostToolPath = join(HOOKS_DIR, 'progress-post-tool-hook.sh');
|
|
365
|
+
writeFileSync(progressPostToolPath, PROGRESS_POST_TOOL_HOOK);
|
|
366
|
+
chmodSync(progressPostToolPath, 0o755);
|
|
367
|
+
console.log('✓ Installed progress-post-tool-hook.sh');
|
|
368
|
+
|
|
369
|
+
const progressStopPath = join(HOOKS_DIR, 'progress-stop-hook.sh');
|
|
370
|
+
writeFileSync(progressStopPath, PROGRESS_STOP_HOOK);
|
|
371
|
+
chmodSync(progressStopPath, 0o755);
|
|
372
|
+
console.log('✓ Installed progress-stop-hook.sh');
|
|
373
|
+
|
|
303
374
|
// Update Claude settings
|
|
304
375
|
let settings = {};
|
|
305
376
|
if (existsSync(SETTINGS_FILE)) {
|