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 CHANGED
@@ -76,12 +76,13 @@ async function start() {
76
76
 
77
77
  // Determine which script to run
78
78
  let script = daemonScript;
79
- let runner = 'node';
79
+ let args = [script];
80
80
 
81
81
  if (!existsSync(daemonScript)) {
82
82
  if (existsSync(daemonSrc)) {
83
83
  script = daemonSrc;
84
- runner = 'bun';
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(runner, [script], {
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
- // Hook scripts content - with SESSION_NAME support for multi-session setups
26
- const PERMISSION_HOOK = `#!/bin/bash
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
- local latest_file=""
49
- local latest_time=0
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
- INFO_FILE=$(find_session)
69
- [ -z "$INFO_FILE" ] || [ ! -f "$INFO_FILE" ] && exit 0
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
- HOOK_PORT=$(jq -r '.port' "$INFO_FILE")
72
- HOOK_HOST=$(jq -r '.host // "localhost"' "$INFO_FILE")
73
- HOOK_URL="http://\${HOOK_HOST}:\${HOOK_PORT}/permission"
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
- if [ -z "$TOOL_NAME" ]; then
80
- TOOL_NAME=$(echo "$INPUT" | jq -r '.toolName // .name // empty')
81
- TOOL_INPUT=$(echo "$INPUT" | jq -c '.toolInput // .input // .arguments // {}')
82
- fi
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
- '{tool_name: $tool_name, tool_input: $tool_input}')
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
- # Claude Code Interactive Stop Hook - sends stop notification and waits for reply
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
- local latest_file=""
120
- local latest_time=0
121
- [ -d "$SESSION_DIR" ] || return
122
-
123
- for info_file in "$SESSION_DIR"/*.info; do
124
- [ -e "$info_file" ] || continue
125
- local pid
126
- pid=$(jq -r '.pid // empty' "$info_file" 2>/dev/null)
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
- '{transcript_path: $transcript_path}')
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
- REASON=$(echo "$RESPONSE" | jq -r '.reason // empty')
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
- # Claude Code Notification Hook - sends notifications to Telegram
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
- local latest_file=""
196
- local latest_time=0
197
- [ -d "$SESSION_DIR" ] || return
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
- INFO_FILE=$(find_session)
216
- [ -z "$INFO_FILE" ] || [ ! -f "$INFO_FILE" ] && exit 0
217
-
218
- HOOK_PORT=$(jq -r '.port' "$INFO_FILE")
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
- '{type: $type, message: $message}')
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 > /dev/null 2>&1
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)) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "telegram-claude-mcp",
3
- "version": "2.0.3",
3
+ "version": "2.0.4",
4
4
  "description": "MCP server that lets Claude message you on Telegram with hooks support",
5
5
  "author": "Geravant",
6
6
  "license": "MIT",