telegram-claude-mcp 1.6.0 → 2.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.
@@ -0,0 +1,207 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * Daemon Control CLI
5
+ *
6
+ * Manage the telegram-claude-daemon:
7
+ * daemon-ctl start - Start the daemon
8
+ * daemon-ctl stop - Stop the daemon
9
+ * daemon-ctl restart - Restart the daemon
10
+ * daemon-ctl status - Check daemon status
11
+ * daemon-ctl logs - Show daemon logs (if running with pm2 or similar)
12
+ */
13
+
14
+ import { spawn, execSync } from 'child_process';
15
+ import { existsSync, readFileSync, unlinkSync } from 'fs';
16
+ import { fileURLToPath } from 'url';
17
+ import { dirname, join } from 'path';
18
+
19
+ const DAEMON_PID_FILE = '/tmp/telegram-claude-daemon.pid';
20
+ const DAEMON_SOCKET_PATH = '/tmp/telegram-claude-daemon.sock';
21
+ const DAEMON_HTTP_PORT = 3333;
22
+
23
+ const __filename = fileURLToPath(import.meta.url);
24
+ const __dirname = dirname(__filename);
25
+ const daemonScript = join(__dirname, '..', 'dist', 'daemon', 'index.js');
26
+ const daemonSrc = join(__dirname, '..', 'src', 'daemon', 'index.ts');
27
+
28
+ function getPid() {
29
+ if (existsSync(DAEMON_PID_FILE)) {
30
+ try {
31
+ const pid = parseInt(readFileSync(DAEMON_PID_FILE, 'utf-8').trim(), 10);
32
+ // Check if process is alive
33
+ try {
34
+ process.kill(pid, 0);
35
+ return pid;
36
+ } catch {
37
+ // Process not running, clean up stale files
38
+ cleanup();
39
+ return null;
40
+ }
41
+ } catch {
42
+ return null;
43
+ }
44
+ }
45
+ return null;
46
+ }
47
+
48
+ function cleanup() {
49
+ try {
50
+ if (existsSync(DAEMON_PID_FILE)) unlinkSync(DAEMON_PID_FILE);
51
+ if (existsSync(DAEMON_SOCKET_PATH)) unlinkSync(DAEMON_SOCKET_PATH);
52
+ } catch {}
53
+ }
54
+
55
+ async function getStatus() {
56
+ const pid = getPid();
57
+ if (!pid) {
58
+ return { running: false };
59
+ }
60
+
61
+ try {
62
+ const response = await fetch(`http://localhost:${DAEMON_HTTP_PORT}/status`);
63
+ const data = await response.json();
64
+ return { running: true, pid, ...data };
65
+ } catch {
66
+ return { running: true, pid, sessions: [] };
67
+ }
68
+ }
69
+
70
+ async function start() {
71
+ const pid = getPid();
72
+ if (pid) {
73
+ console.log(`Daemon already running (PID: ${pid})`);
74
+ return;
75
+ }
76
+
77
+ // Determine which script to run
78
+ let script = daemonScript;
79
+ let runner = 'node';
80
+
81
+ if (!existsSync(daemonScript)) {
82
+ if (existsSync(daemonSrc)) {
83
+ script = daemonSrc;
84
+ runner = 'bun';
85
+ } else {
86
+ console.error('Error: Daemon script not found. Run `npm run build` first.');
87
+ process.exit(1);
88
+ }
89
+ }
90
+
91
+ console.log('Starting telegram-claude-daemon...');
92
+
93
+ const child = spawn(runner, [script], {
94
+ detached: true,
95
+ stdio: ['ignore', 'pipe', 'pipe'],
96
+ env: process.env,
97
+ });
98
+
99
+ child.unref();
100
+
101
+ // Wait for daemon to start
102
+ await new Promise((resolve) => setTimeout(resolve, 1000));
103
+
104
+ const newPid = getPid();
105
+ if (newPid) {
106
+ console.log(`Daemon started (PID: ${newPid})`);
107
+ console.log(` Socket: ${DAEMON_SOCKET_PATH}`);
108
+ console.log(` HTTP: http://localhost:${DAEMON_HTTP_PORT}`);
109
+ } else {
110
+ console.error('Failed to start daemon');
111
+ process.exit(1);
112
+ }
113
+ }
114
+
115
+ function stop() {
116
+ const pid = getPid();
117
+ if (!pid) {
118
+ console.log('Daemon is not running');
119
+ return;
120
+ }
121
+
122
+ console.log(`Stopping daemon (PID: ${pid})...`);
123
+
124
+ try {
125
+ process.kill(pid, 'SIGTERM');
126
+
127
+ // Wait for process to exit
128
+ let attempts = 0;
129
+ while (attempts < 10) {
130
+ try {
131
+ process.kill(pid, 0);
132
+ execSync('sleep 0.5');
133
+ attempts++;
134
+ } catch {
135
+ break;
136
+ }
137
+ }
138
+
139
+ cleanup();
140
+ console.log('Daemon stopped');
141
+ } catch (err) {
142
+ console.error('Failed to stop daemon:', err.message);
143
+ cleanup();
144
+ }
145
+ }
146
+
147
+ async function status() {
148
+ const info = await getStatus();
149
+
150
+ if (!info.running) {
151
+ console.log('Daemon: not running');
152
+ return;
153
+ }
154
+
155
+ console.log('Daemon: running');
156
+ console.log(` PID: ${info.pid}`);
157
+ console.log(` Version: ${info.version || 'unknown'}`);
158
+ console.log(` Active Sessions: ${info.activeSessions || 0}`);
159
+
160
+ if (info.sessions && info.sessions.length > 0) {
161
+ console.log(' Sessions:');
162
+ for (const session of info.sessions) {
163
+ console.log(` - ${session.sessionName} (${session.sessionId})`);
164
+ console.log(` Connected: ${session.connectedAt}`);
165
+ console.log(` Last Activity: ${session.lastActivity}`);
166
+ }
167
+ }
168
+ }
169
+
170
+ async function main() {
171
+ const command = process.argv[2];
172
+
173
+ switch (command) {
174
+ case 'start':
175
+ await start();
176
+ break;
177
+
178
+ case 'stop':
179
+ stop();
180
+ break;
181
+
182
+ case 'restart':
183
+ stop();
184
+ await new Promise((r) => setTimeout(r, 500));
185
+ await start();
186
+ break;
187
+
188
+ case 'status':
189
+ await status();
190
+ break;
191
+
192
+ default:
193
+ console.log('Usage: daemon-ctl <command>');
194
+ console.log('');
195
+ console.log('Commands:');
196
+ console.log(' start - Start the daemon');
197
+ console.log(' stop - Stop the daemon');
198
+ console.log(' restart - Restart the daemon');
199
+ console.log(' status - Check daemon status');
200
+ process.exit(1);
201
+ }
202
+ }
203
+
204
+ main().catch((err) => {
205
+ console.error('Error:', err.message);
206
+ process.exit(1);
207
+ });
package/bin/daemon.js ADDED
@@ -0,0 +1,20 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * Telegram Claude Daemon launcher
5
+ *
6
+ * Usage: telegram-claude-daemon
7
+ *
8
+ * Required environment variables:
9
+ * TELEGRAM_BOT_TOKEN - Your Telegram bot token
10
+ * TELEGRAM_CHAT_ID - Your Telegram chat ID
11
+ */
12
+
13
+ import { register } from 'node:module';
14
+ import { pathToFileURL } from 'node:url';
15
+
16
+ // Register tsx loader for TypeScript support
17
+ register('tsx', pathToFileURL('./'));
18
+
19
+ // Import and run the daemon
20
+ import('../src/daemon/index.ts');
package/bin/proxy.js ADDED
@@ -0,0 +1,22 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * Telegram Claude MCP Proxy launcher
5
+ *
6
+ * Usage: telegram-claude-proxy
7
+ *
8
+ * This connects to a running telegram-claude-daemon via Unix socket.
9
+ * Make sure to start the daemon first: telegram-claude-ctl start
10
+ *
11
+ * Optional environment variables:
12
+ * SESSION_NAME - Session identifier (default: auto-detected from CWD)
13
+ */
14
+
15
+ import { register } from 'node:module';
16
+ import { pathToFileURL } from 'node:url';
17
+
18
+ // Register tsx loader for TypeScript support
19
+ register('tsx', pathToFileURL('./'));
20
+
21
+ // Import and run the proxy
22
+ import('../src/proxy/index.ts');
package/bin/setup.js CHANGED
@@ -97,19 +97,31 @@ RESPONSE=$(curl -s -X POST "$HOOK_URL" \\
97
97
  const STOP_HOOK = `#!/bin/bash
98
98
  #
99
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)
100
101
  #
101
102
 
102
103
  SESSION_DIR="/tmp/telegram-claude-sessions"
103
104
 
104
- find_active_session() {
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
118
+
105
119
  local latest_file=""
106
120
  local latest_time=0
107
-
108
121
  [ -d "$SESSION_DIR" ] || return
109
122
 
110
123
  for info_file in "$SESSION_DIR"/*.info; do
111
124
  [ -e "$info_file" ] || continue
112
-
113
125
  local pid
114
126
  pid=$(jq -r '.pid // empty' "$info_file" 2>/dev/null)
115
127
  if [ -n "$pid" ] && kill -0 "$pid" 2>/dev/null; then
@@ -121,22 +133,16 @@ find_active_session() {
121
133
  fi
122
134
  fi
123
135
  done
124
-
125
136
  echo "$latest_file"
126
137
  }
127
138
 
128
139
  INPUT=$(cat)
129
140
 
130
141
  STOP_HOOK_ACTIVE=$(echo "$INPUT" | jq -r '.stop_hook_active // false')
131
- if [ "$STOP_HOOK_ACTIVE" = "true" ]; then
132
- exit 0
133
- fi
134
-
135
- INFO_FILE=$(find_active_session)
142
+ [ "$STOP_HOOK_ACTIVE" = "true" ] && exit 0
136
143
 
137
- if [ -z "$INFO_FILE" ] || [ ! -f "$INFO_FILE" ]; then
138
- exit 0
139
- fi
144
+ INFO_FILE=$(find_session)
145
+ [ -z "$INFO_FILE" ] || [ ! -f "$INFO_FILE" ] && exit 0
140
146
 
141
147
  HOOK_PORT=$(jq -r '.port' "$INFO_FILE")
142
148
  HOOK_HOST=$(jq -r '.host // "localhost"' "$INFO_FILE")
@@ -156,29 +162,38 @@ RESPONSE=$(curl -s -X POST "$HOOK_URL" \\
156
162
  if [ $? -eq 0 ]; then
157
163
  DECISION=$(echo "$RESPONSE" | jq -r '.decision // empty')
158
164
  REASON=$(echo "$RESPONSE" | jq -r '.reason // empty')
159
-
160
- if [ "$DECISION" = "block" ] && [ -n "$REASON" ]; then
161
- echo "$RESPONSE"
162
- fi
165
+ [ "$DECISION" = "block" ] && [ -n "$REASON" ] && echo "$RESPONSE"
163
166
  fi
164
167
  `;
165
168
 
166
169
  const NOTIFY_HOOK = `#!/bin/bash
167
170
  #
168
171
  # Claude Code Notification Hook - sends notifications to Telegram
172
+ # Set SESSION_NAME env var to target a specific session (for multi-session setups)
169
173
  #
170
174
 
171
175
  SESSION_DIR="/tmp/telegram-claude-sessions"
172
176
 
173
- find_active_session() {
177
+ find_session() {
178
+ if [ -n "$SESSION_NAME" ]; then
179
+ local specific_file="$SESSION_DIR/\${SESSION_NAME}.info"
180
+ if [ -f "$specific_file" ]; then
181
+ local pid
182
+ pid=$(jq -r '.pid // empty' "$specific_file" 2>/dev/null)
183
+ if [ -n "$pid" ] && kill -0 "$pid" 2>/dev/null; then
184
+ echo "$specific_file"
185
+ return
186
+ fi
187
+ fi
188
+ return
189
+ fi
190
+
174
191
  local latest_file=""
175
192
  local latest_time=0
176
-
177
193
  [ -d "$SESSION_DIR" ] || return
178
194
 
179
195
  for info_file in "$SESSION_DIR"/*.info; do
180
196
  [ -e "$info_file" ] || continue
181
-
182
197
  local pid
183
198
  pid=$(jq -r '.pid // empty' "$info_file" 2>/dev/null)
184
199
  if [ -n "$pid" ] && kill -0 "$pid" 2>/dev/null; then
@@ -190,15 +205,11 @@ find_active_session() {
190
205
  fi
191
206
  fi
192
207
  done
193
-
194
208
  echo "$latest_file"
195
209
  }
196
210
 
197
- INFO_FILE=$(find_active_session)
198
-
199
- if [ -z "$INFO_FILE" ] || [ ! -f "$INFO_FILE" ]; then
200
- exit 0
201
- fi
211
+ INFO_FILE=$(find_session)
212
+ [ -z "$INFO_FILE" ] || [ ! -f "$INFO_FILE" ] && exit 0
202
213
 
203
214
  HOOK_PORT=$(jq -r '.port' "$INFO_FILE")
204
215
  HOOK_HOST=$(jq -r '.host // "localhost"' "$INFO_FILE")
@@ -218,42 +229,46 @@ curl -s -X POST "$HOOK_URL" \\
218
229
  --max-time 10 > /dev/null 2>&1
219
230
  `;
220
231
 
221
- // Hooks configuration for Claude settings
222
- const HOOKS_CONFIG = {
223
- PermissionRequest: [
224
- {
225
- matcher: '*',
226
- hooks: [
227
- {
228
- type: 'command',
229
- command: '~/.claude/hooks/permission-hook.sh'
230
- }
231
- ]
232
- }
233
- ],
234
- Stop: [
235
- {
236
- matcher: '*',
237
- hooks: [
238
- {
239
- type: 'command',
240
- command: '~/.claude/hooks/stop-hook.sh'
241
- }
242
- ]
243
- }
244
- ],
245
- Notification: [
246
- {
247
- matcher: '*',
248
- hooks: [
249
- {
250
- type: 'command',
251
- command: '~/.claude/hooks/notify-hook.sh'
252
- }
253
- ]
254
- }
255
- ]
256
- };
232
+ // Generate hooks configuration for Claude settings
233
+ // sessionName parameter enables multi-session support
234
+ function generateHooksConfig(sessionName = 'default') {
235
+ const envPrefix = `SESSION_NAME=${sessionName}`;
236
+ return {
237
+ PermissionRequest: [
238
+ {
239
+ matcher: '*',
240
+ hooks: [
241
+ {
242
+ type: 'command',
243
+ command: `${envPrefix} ~/.claude/hooks/permission-hook.sh`
244
+ }
245
+ ]
246
+ }
247
+ ],
248
+ Stop: [
249
+ {
250
+ matcher: '*',
251
+ hooks: [
252
+ {
253
+ type: 'command',
254
+ command: `${envPrefix} ~/.claude/hooks/stop-hook.sh`
255
+ }
256
+ ]
257
+ }
258
+ ],
259
+ Notification: [
260
+ {
261
+ matcher: '*',
262
+ hooks: [
263
+ {
264
+ type: 'command',
265
+ command: `${envPrefix} ~/.claude/hooks/notify-hook.sh`
266
+ }
267
+ ]
268
+ }
269
+ ]
270
+ };
271
+ }
257
272
 
258
273
  function setup() {
259
274
  console.log('\n📱 telegram-claude-mcp Setup\n');
@@ -291,8 +306,8 @@ function setup() {
291
306
  }
292
307
  }
293
308
 
294
- // Merge hooks config
295
- settings.hooks = { ...settings.hooks, ...HOOKS_CONFIG };
309
+ // Merge hooks config (default session)
310
+ settings.hooks = { ...settings.hooks, ...generateHooksConfig('default') };
296
311
 
297
312
  // Add MCP server config if not present
298
313
  if (!settings.mcpServers) {
@@ -337,6 +352,18 @@ function setup() {
337
352
  console.log('4. Restart Claude Code\n');
338
353
 
339
354
  console.log('─'.repeat(50));
355
+ console.log('\n📋 Multi-Session Setup (optional):\n');
356
+ console.log('If you have multiple Claude configs (e.g., ~/.claude and ~/.claude-personal),');
357
+ console.log('each needs its own SESSION_NAME to avoid message routing conflicts.\n');
358
+ console.log('For each additional config, update its settings.json:');
359
+ console.log(' 1. Change SESSION_NAME in mcpServers.telegram.env');
360
+ console.log(' 2. Update hook commands to use that SESSION_NAME:\n');
361
+ console.log(' "command": "SESSION_NAME=personal ~/.claude/hooks/permission-hook.sh"\n');
362
+ console.log('Example for ~/.claude-personal/settings.json:');
363
+ console.log(' SESSION_NAME: "personal" (in mcpServers.telegram.env)');
364
+ console.log(' Hook commands: "SESSION_NAME=personal ~/.claude/hooks/..."');
365
+
366
+ console.log('\n' + '─'.repeat(50));
340
367
  console.log('\n✨ Setup complete! Configure your bot token and chat ID to finish.\n');
341
368
  }
342
369
 
@@ -0,0 +1,32 @@
1
+ #!/bin/bash
2
+ # Notify hook for telegram-claude-daemon (v2)
3
+ # Uses single daemon port instead of session discovery
4
+
5
+ DAEMON_PORT="${TELEGRAM_CLAUDE_PORT:-3333}"
6
+ DAEMON_HOST="${TELEGRAM_CLAUDE_HOST:-localhost}"
7
+ HOOK_URL="http://${DAEMON_HOST}:${DAEMON_PORT}/notify"
8
+
9
+ # Read input from stdin
10
+ INPUT=$(cat)
11
+ MESSAGE=$(echo "$INPUT" | jq -r '.message // "Notification"')
12
+
13
+ # Build payload
14
+ if [ -n "$SESSION_NAME" ]; then
15
+ PAYLOAD=$(jq -n \
16
+ --arg type "notification" \
17
+ --arg message "$MESSAGE" \
18
+ --arg session_name "$SESSION_NAME" \
19
+ '{type: $type, message: $message, session_name: $session_name}')
20
+ else
21
+ PAYLOAD=$(jq -n \
22
+ --arg type "notification" \
23
+ --arg message "$MESSAGE" \
24
+ '{type: $type, message: $message}')
25
+ fi
26
+
27
+ # Send to daemon (non-blocking, 10 second timeout)
28
+ curl -s -X POST "$HOOK_URL" \
29
+ -H "Content-Type: application/json" \
30
+ -H "X-Session-Name: ${SESSION_NAME:-default}" \
31
+ -d "$PAYLOAD" \
32
+ --max-time 10 >/dev/null 2>&1
@@ -0,0 +1,43 @@
1
+ #!/bin/bash
2
+ # Permission hook for telegram-claude-daemon (v2)
3
+ # Uses single daemon port instead of session discovery
4
+
5
+ DAEMON_PORT="${TELEGRAM_CLAUDE_PORT:-3333}"
6
+ DAEMON_HOST="${TELEGRAM_CLAUDE_HOST:-localhost}"
7
+ HOOK_URL="http://${DAEMON_HOST}:${DAEMON_PORT}/permission"
8
+
9
+ # Read input from stdin
10
+ INPUT=$(cat)
11
+
12
+ # Extract tool name and input
13
+ TOOL_NAME=$(echo "$INPUT" | jq -r '.tool_name // .toolName // .name // empty')
14
+ TOOL_INPUT=$(echo "$INPUT" | jq -c '.tool_input // .toolInput // .input // .arguments // {}')
15
+
16
+ if [ -z "$TOOL_NAME" ]; then
17
+ exit 0
18
+ fi
19
+
20
+ # Build payload with session name if available
21
+ if [ -n "$SESSION_NAME" ]; then
22
+ PAYLOAD=$(jq -n \
23
+ --arg tool_name "$TOOL_NAME" \
24
+ --argjson tool_input "$TOOL_INPUT" \
25
+ --arg session_name "$SESSION_NAME" \
26
+ '{tool_name: $tool_name, tool_input: $tool_input, session_name: $session_name}')
27
+ else
28
+ PAYLOAD=$(jq -n \
29
+ --arg tool_name "$TOOL_NAME" \
30
+ --argjson tool_input "$TOOL_INPUT" \
31
+ '{tool_name: $tool_name, tool_input: $tool_input}')
32
+ fi
33
+
34
+ # Send to daemon (10 minute timeout)
35
+ RESPONSE=$(curl -s -X POST "$HOOK_URL" \
36
+ -H "Content-Type: application/json" \
37
+ -H "X-Session-Name: ${SESSION_NAME:-default}" \
38
+ -d "$PAYLOAD" \
39
+ --max-time 600 2>/dev/null)
40
+
41
+ if [ $? -eq 0 ] && [ -n "$RESPONSE" ]; then
42
+ echo "$RESPONSE"
43
+ fi
@@ -0,0 +1,45 @@
1
+ #!/bin/bash
2
+ # Stop hook for telegram-claude-daemon (v2)
3
+ # Uses single daemon port instead of session discovery
4
+
5
+ DAEMON_PORT="${TELEGRAM_CLAUDE_PORT:-3333}"
6
+ DAEMON_HOST="${TELEGRAM_CLAUDE_HOST:-localhost}"
7
+ HOOK_URL="http://${DAEMON_HOST}:${DAEMON_PORT}/stop"
8
+
9
+ # Read input from stdin
10
+ INPUT=$(cat)
11
+
12
+ # Check if stop hook is already active (prevent recursion)
13
+ STOP_HOOK_ACTIVE=$(echo "$INPUT" | jq -r '.stop_hook_active // false')
14
+ if [ "$STOP_HOOK_ACTIVE" = "true" ]; then
15
+ exit 0
16
+ fi
17
+
18
+ # Extract transcript path
19
+ TRANSCRIPT_PATH=$(echo "$INPUT" | jq -r '.transcript_path // empty')
20
+
21
+ # Build payload
22
+ if [ -n "$SESSION_NAME" ]; then
23
+ PAYLOAD=$(jq -n \
24
+ --arg transcript_path "$TRANSCRIPT_PATH" \
25
+ --arg session_name "$SESSION_NAME" \
26
+ '{transcript_path: $transcript_path, session_name: $session_name}')
27
+ else
28
+ PAYLOAD=$(jq -n \
29
+ --arg transcript_path "$TRANSCRIPT_PATH" \
30
+ '{transcript_path: $transcript_path}')
31
+ fi
32
+
33
+ # Send to daemon (5 minute timeout)
34
+ RESPONSE=$(curl -s -X POST "$HOOK_URL" \
35
+ -H "Content-Type: application/json" \
36
+ -H "X-Session-Name: ${SESSION_NAME:-default}" \
37
+ -d "$PAYLOAD" \
38
+ --max-time 300 2>/dev/null)
39
+
40
+ if [ $? -eq 0 ] && [ -n "$RESPONSE" ]; then
41
+ DECISION=$(echo "$RESPONSE" | jq -r '.decision // empty')
42
+ if [ "$DECISION" = "block" ]; then
43
+ echo "$RESPONSE"
44
+ fi
45
+ fi
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "telegram-claude-mcp",
3
- "version": "1.6.0",
3
+ "version": "2.0.1",
4
4
  "description": "MCP server that lets Claude message you on Telegram with hooks support",
5
5
  "author": "Geravant",
6
6
  "license": "MIT",
@@ -16,23 +16,34 @@
16
16
  "bot",
17
17
  "messaging",
18
18
  "hooks",
19
- "permissions"
19
+ "permissions",
20
+ "daemon"
20
21
  ],
21
22
  "type": "module",
22
23
  "main": "src/index.ts",
23
24
  "bin": {
24
25
  "telegram-claude-mcp": "./bin/run.js",
25
- "telegram-claude-setup": "./bin/setup.js"
26
+ "telegram-claude-setup": "./bin/setup.js",
27
+ "telegram-claude-daemon": "./bin/daemon.js",
28
+ "telegram-claude-proxy": "./bin/proxy.js",
29
+ "telegram-claude-ctl": "./bin/daemon-ctl.js"
26
30
  },
27
31
  "files": [
28
32
  "src",
29
33
  "bin",
30
34
  "hooks",
31
- "README.md"
35
+ "hooks-v2",
36
+ "README.md",
37
+ "ARCHITECTURE.md"
32
38
  ],
33
39
  "scripts": {
34
40
  "start": "node --import tsx src/index.ts",
35
- "dev": "node --watch --import tsx src/index.ts"
41
+ "dev": "node --watch --import tsx src/index.ts",
42
+ "daemon": "node --import tsx src/daemon/index.ts",
43
+ "proxy": "node --import tsx src/proxy/index.ts",
44
+ "daemon:start": "node bin/daemon-ctl.js start",
45
+ "daemon:stop": "node bin/daemon-ctl.js stop",
46
+ "daemon:status": "node bin/daemon-ctl.js status"
36
47
  },
37
48
  "dependencies": {
38
49
  "@modelcontextprotocol/sdk": "^1.0.4",