claude-tg 1.0.8 → 1.0.10

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/claude-tg.js CHANGED
@@ -140,6 +140,25 @@ daemon
140
140
  }
141
141
  });
142
142
 
143
+ const HOOKS_LOG_PATH = path.join(CONFIG_DIR, 'hooks.log');
144
+
145
+ program
146
+ .command('hooks-log')
147
+ .description('Show hook execution log (diagnose if hooks are firing)')
148
+ .action(() => {
149
+ if (!fs.existsSync(HOOKS_LOG_PATH)) {
150
+ console.log('No hooks log found. Hooks have not been called yet.');
151
+ console.log('Make sure Claude Code is running and try using a tool.');
152
+ return;
153
+ }
154
+ const content = fs.readFileSync(HOOKS_LOG_PATH, 'utf8');
155
+ const lines = content.trim().split('\n');
156
+ const last = lines.slice(-30);
157
+ console.log(`--- Last ${last.length} hook log entries ---\n`);
158
+ console.log(last.join('\n'));
159
+ console.log(`\nFull log: ${HOOKS_LOG_PATH}`);
160
+ });
161
+
143
162
  // --- Send commands (stateless, no daemon needed) ---
144
163
 
145
164
  function createTelegramClient() {
@@ -276,18 +295,27 @@ program
276
295
  const settings = JSON.parse(fs.readFileSync(settingsPath, 'utf8'));
277
296
  const hooks = settings.hooks || {};
278
297
 
279
- // Extract script path from hook command (handles optional env prefix)
298
+ // Extract script path and node binary from hook command
280
299
  function extractScriptPath(cmd) {
281
300
  const match = cmd.match(/node\s+(.+)$/);
282
301
  return match ? match[1].trim() : null;
283
302
  }
303
+ function extractNodeBin(cmd) {
304
+ const match = cmd.match(/^(?:\S+=\S+\s+)?(\S*node)\s/);
305
+ return match ? match[1] : 'node';
306
+ }
284
307
 
285
308
  const permHooks = hooks.PermissionRequest || [];
286
309
  const permCmd = permHooks[0]?.hooks?.[0]?.command || 'NOT FOUND';
287
310
  console.log(` PermissionRequest: ${permCmd}`);
288
311
  if (permCmd !== 'NOT FOUND') {
312
+ const nodeBin = extractNodeBin(permCmd);
289
313
  const scriptPath = extractScriptPath(permCmd);
290
- if (scriptPath && fs.existsSync(scriptPath)) {
314
+ if (nodeBin !== 'node' && !fs.existsSync(nodeBin)) {
315
+ console.log(` Node binary: NO — ${nodeBin}`);
316
+ console.log(' Run: claude-tg setup (to fix hook paths)');
317
+ ok = false;
318
+ } else if (scriptPath && fs.existsSync(scriptPath)) {
291
319
  console.log(' File exists: YES');
292
320
  } else {
293
321
  console.log(` File exists: NO — ${scriptPath || permCmd}`);
@@ -299,8 +327,13 @@ program
299
327
  const notifCmd = notifHooks[0]?.hooks?.[0]?.command || 'NOT FOUND';
300
328
  console.log(` Notification: ${notifCmd}`);
301
329
  if (notifCmd !== 'NOT FOUND') {
330
+ const nodeBin = extractNodeBin(notifCmd);
302
331
  const scriptPath = extractScriptPath(notifCmd);
303
- if (scriptPath && fs.existsSync(scriptPath)) {
332
+ if (nodeBin !== 'node' && !fs.existsSync(nodeBin)) {
333
+ console.log(` Node binary: NO — ${nodeBin}`);
334
+ console.log(' Run: claude-tg setup (to fix hook paths)');
335
+ ok = false;
336
+ } else if (scriptPath && fs.existsSync(scriptPath)) {
304
337
  console.log(' File exists: YES');
305
338
  } else {
306
339
  console.log(` File exists: NO — ${scriptPath || notifCmd}`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-tg",
3
- "version": "1.0.8",
3
+ "version": "1.0.10",
4
4
  "description": "Control Claude Code from Telegram — approve permissions, answer questions, reply to idle sessions, send files, all from your phone",
5
5
  "bin": {
6
6
  "claude-tg": "./bin/claude-tg.js"
@@ -4,10 +4,21 @@
4
4
  // Detects parent TTY, reads hook input from stdin, fires POST to daemon, exits.
5
5
 
6
6
  const http = require('http');
7
+ const fs = require('fs');
8
+ const path = require('path');
7
9
  const { execSync } = require('child_process');
8
10
 
9
11
  const DAEMON_PORT = parseInt(process.env.CLAUDE_TG_PORT || '7483', 10);
10
12
  const DAEMON_HOST = '127.0.0.1';
13
+ const HOOK_LOG = path.join(process.env.HOME, '.claude-telegram-bridge', 'hooks.log');
14
+
15
+ function hookLog(msg) {
16
+ try {
17
+ const dir = path.dirname(HOOK_LOG);
18
+ if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
19
+ fs.appendFileSync(HOOK_LOG, `[${new Date().toISOString()}] [notify] ${msg}\n`);
20
+ } catch {}
21
+ }
11
22
 
12
23
  function readStdin() {
13
24
  return new Promise((resolve, reject) => {
@@ -70,21 +81,27 @@ function postToDaemon(body) {
70
81
  }
71
82
 
72
83
  async function main() {
84
+ hookLog('Hook called');
73
85
  try {
74
86
  const input = await readStdin();
75
87
  const hookInput = input.hookInput || input;
76
88
  const ttyPath = findTty();
89
+ const notifType = hookInput.notification_type || hookInput.type || 'unknown';
90
+
91
+ hookLog(`type=${notifType} session=${hookInput.session_id} tty=${ttyPath} port=${DAEMON_PORT}`);
77
92
 
78
93
  await postToDaemon({
79
94
  session_id: hookInput.session_id,
80
95
  cwd: hookInput.cwd,
81
- notification_type: hookInput.notification_type || hookInput.type,
96
+ notification_type: notifType,
82
97
  message: hookInput.message,
83
98
  transcript_path: hookInput.transcript_path,
84
99
  tty_path: ttyPath,
85
100
  });
86
- } catch {
87
- // Daemon unreachable — silently ignore
101
+
102
+ hookLog(`Sent to daemon OK`);
103
+ } catch (err) {
104
+ hookLog(`ERROR: ${err.message || err}`);
88
105
  }
89
106
  process.exit(0);
90
107
  }
@@ -5,10 +5,21 @@
5
5
  // On error/timeout: exits 0 with no output → falls back to local dialog.
6
6
 
7
7
  const http = require('http');
8
+ const fs = require('fs');
9
+ const path = require('path');
8
10
  const { execSync } = require('child_process');
9
11
 
10
12
  const DAEMON_PORT = parseInt(process.env.CLAUDE_TG_PORT || '7483', 10);
11
13
  const DAEMON_HOST = '127.0.0.1';
14
+ const HOOK_LOG = path.join(process.env.HOME, '.claude-telegram-bridge', 'hooks.log');
15
+
16
+ function hookLog(msg) {
17
+ try {
18
+ const dir = path.dirname(HOOK_LOG);
19
+ if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
20
+ fs.appendFileSync(HOOK_LOG, `[${new Date().toISOString()}] [perm] ${msg}\n`);
21
+ } catch {}
22
+ }
12
23
 
13
24
  function findTty() {
14
25
  try {
@@ -71,11 +82,14 @@ function postToDaemon(path, body) {
71
82
  }
72
83
 
73
84
  async function main() {
85
+ hookLog('Hook called');
74
86
  try {
75
87
  const input = await readStdin();
76
-
77
88
  const hookInput = input.hookInput || input;
78
89
  const ttyPath = findTty();
90
+
91
+ hookLog(`tool=${hookInput.tool_name} session=${hookInput.session_id} tty=${ttyPath} port=${DAEMON_PORT}`);
92
+
79
93
  const result = await postToDaemon('/api/permission', {
80
94
  session_id: hookInput.session_id,
81
95
  cwd: hookInput.cwd,
@@ -87,10 +101,12 @@ async function main() {
87
101
  });
88
102
 
89
103
  if (!result || !result.decision) {
90
- // No decision — fall back to local dialog
104
+ hookLog('No decision from daemon falling back to local dialog');
91
105
  process.exit(0);
92
106
  }
93
107
 
108
+ hookLog(`Decision: ${JSON.stringify(result.decision)}`);
109
+
94
110
  const output = {
95
111
  hookSpecificOutput: {
96
112
  hookEventName: 'PermissionRequest',
@@ -100,8 +116,8 @@ async function main() {
100
116
  };
101
117
 
102
118
  process.stdout.write(JSON.stringify(output));
103
- } catch {
104
- // Daemon unreachable or error — fall back to local dialog
119
+ } catch (err) {
120
+ hookLog(`ERROR: ${err.message || err}`);
105
121
  process.exit(0);
106
122
  }
107
123
  }
package/src/setup.js CHANGED
@@ -26,13 +26,16 @@ function telegramApiCall(token, method) {
26
26
 
27
27
  function getHooksConfig(port) {
28
28
  const envPrefix = port !== 7483 ? `CLAUDE_TG_PORT=${port} ` : '';
29
+ // Use absolute path to the node binary that ran setup — ensures hooks work
30
+ // even if node isn't in PATH for non-interactive shells (nvm, fnm, etc.)
31
+ const nodeBin = process.execPath;
29
32
  return {
30
33
  PermissionRequest: [
31
34
  {
32
35
  hooks: [
33
36
  {
34
37
  type: 'command',
35
- command: `${envPrefix}node ${path.join(HOOKS_DIR, 'permission-request.js')}`,
38
+ command: `${envPrefix}${nodeBin} ${path.join(HOOKS_DIR, 'permission-request.js')}`,
36
39
  timeout: 1800,
37
40
  statusMessage: 'Waiting for Telegram approval...',
38
41
  },
@@ -41,11 +44,10 @@ function getHooksConfig(port) {
41
44
  ],
42
45
  Notification: [
43
46
  {
44
- matcher: 'idle_prompt|elicitation_dialog',
45
47
  hooks: [
46
48
  {
47
49
  type: 'command',
48
- command: `${envPrefix}node ${path.join(HOOKS_DIR, 'notification.js')}`,
50
+ command: `${envPrefix}${nodeBin} ${path.join(HOOKS_DIR, 'notification.js')}`,
49
51
  async: true,
50
52
  },
51
53
  ],