claude-telegram-mirror 0.1.0

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.
Files changed (99) hide show
  1. package/README.md +331 -0
  2. package/dist/bot/commands.d.ts +41 -0
  3. package/dist/bot/commands.d.ts.map +1 -0
  4. package/dist/bot/commands.js +231 -0
  5. package/dist/bot/commands.js.map +1 -0
  6. package/dist/bot/formatting.d.ts +62 -0
  7. package/dist/bot/formatting.d.ts.map +1 -0
  8. package/dist/bot/formatting.js +295 -0
  9. package/dist/bot/formatting.js.map +1 -0
  10. package/dist/bot/telegram.d.ts +93 -0
  11. package/dist/bot/telegram.d.ts.map +1 -0
  12. package/dist/bot/telegram.js +378 -0
  13. package/dist/bot/telegram.js.map +1 -0
  14. package/dist/bot/types.d.ts +28 -0
  15. package/dist/bot/types.d.ts.map +1 -0
  16. package/dist/bot/types.js +5 -0
  17. package/dist/bot/types.js.map +1 -0
  18. package/dist/bridge/daemon.d.ts +93 -0
  19. package/dist/bridge/daemon.d.ts.map +1 -0
  20. package/dist/bridge/daemon.js +626 -0
  21. package/dist/bridge/daemon.js.map +1 -0
  22. package/dist/bridge/index.d.ts +10 -0
  23. package/dist/bridge/index.d.ts.map +1 -0
  24. package/dist/bridge/index.js +9 -0
  25. package/dist/bridge/index.js.map +1 -0
  26. package/dist/bridge/injector.d.ts +97 -0
  27. package/dist/bridge/injector.d.ts.map +1 -0
  28. package/dist/bridge/injector.js +289 -0
  29. package/dist/bridge/injector.js.map +1 -0
  30. package/dist/bridge/session.d.ts +108 -0
  31. package/dist/bridge/session.d.ts.map +1 -0
  32. package/dist/bridge/session.js +381 -0
  33. package/dist/bridge/session.js.map +1 -0
  34. package/dist/bridge/socket.d.ts +97 -0
  35. package/dist/bridge/socket.d.ts.map +1 -0
  36. package/dist/bridge/socket.js +436 -0
  37. package/dist/bridge/socket.js.map +1 -0
  38. package/dist/bridge/types.d.ts +38 -0
  39. package/dist/bridge/types.d.ts.map +1 -0
  40. package/dist/bridge/types.js +5 -0
  41. package/dist/bridge/types.js.map +1 -0
  42. package/dist/cli.d.ts +7 -0
  43. package/dist/cli.d.ts.map +1 -0
  44. package/dist/cli.js +332 -0
  45. package/dist/cli.js.map +1 -0
  46. package/dist/hooks/handler.d.ts +94 -0
  47. package/dist/hooks/handler.d.ts.map +1 -0
  48. package/dist/hooks/handler.js +431 -0
  49. package/dist/hooks/handler.js.map +1 -0
  50. package/dist/hooks/index.d.ts +8 -0
  51. package/dist/hooks/index.d.ts.map +1 -0
  52. package/dist/hooks/index.js +7 -0
  53. package/dist/hooks/index.js.map +1 -0
  54. package/dist/hooks/installer.d.ts +46 -0
  55. package/dist/hooks/installer.d.ts.map +1 -0
  56. package/dist/hooks/installer.js +317 -0
  57. package/dist/hooks/installer.js.map +1 -0
  58. package/dist/hooks/types.d.ts +88 -0
  59. package/dist/hooks/types.d.ts.map +1 -0
  60. package/dist/hooks/types.js +6 -0
  61. package/dist/hooks/types.js.map +1 -0
  62. package/dist/index.d.ts +19 -0
  63. package/dist/index.d.ts.map +1 -0
  64. package/dist/index.js +20 -0
  65. package/dist/index.js.map +1 -0
  66. package/dist/service/doctor.d.ts +10 -0
  67. package/dist/service/doctor.d.ts.map +1 -0
  68. package/dist/service/doctor.js +424 -0
  69. package/dist/service/doctor.js.map +1 -0
  70. package/dist/service/manager.d.ts +48 -0
  71. package/dist/service/manager.d.ts.map +1 -0
  72. package/dist/service/manager.js +584 -0
  73. package/dist/service/manager.js.map +1 -0
  74. package/dist/service/setup.d.ts +10 -0
  75. package/dist/service/setup.d.ts.map +1 -0
  76. package/dist/service/setup.js +266 -0
  77. package/dist/service/setup.js.map +1 -0
  78. package/dist/utils/chunker.d.ts +24 -0
  79. package/dist/utils/chunker.d.ts.map +1 -0
  80. package/dist/utils/chunker.js +123 -0
  81. package/dist/utils/chunker.js.map +1 -0
  82. package/dist/utils/config.d.ts +48 -0
  83. package/dist/utils/config.d.ts.map +1 -0
  84. package/dist/utils/config.js +154 -0
  85. package/dist/utils/config.js.map +1 -0
  86. package/dist/utils/logger.d.ts +7 -0
  87. package/dist/utils/logger.d.ts.map +1 -0
  88. package/dist/utils/logger.js +28 -0
  89. package/dist/utils/logger.js.map +1 -0
  90. package/package.json +88 -0
  91. package/postinstall.cjs +76 -0
  92. package/scripts/claude-wrapper.sh +122 -0
  93. package/scripts/doctor.sh +433 -0
  94. package/scripts/get-chat-id.sh +64 -0
  95. package/scripts/global-hooks.sh +39 -0
  96. package/scripts/install.sh +831 -0
  97. package/scripts/start-daemon.sh +49 -0
  98. package/scripts/telegram-hook.sh +449 -0
  99. package/scripts/uninstall.sh +261 -0
package/README.md ADDED
@@ -0,0 +1,331 @@
1
+ # Claude Code Telegram Mirror
2
+
3
+ Bidirectional communication between Claude Code CLI and Telegram. Control your Claude Code sessions from your phone.
4
+
5
+ **Supported platforms:** Linux, macOS
6
+
7
+ ## Quick Install
8
+
9
+ ```bash
10
+ curl -fsSL https://raw.githubusercontent.com/robertelee78/claude-telegram-mirror/master/scripts/install.sh | bash
11
+ ```
12
+
13
+ Or download and review first:
14
+
15
+ ```bash
16
+ curl -fsSL https://raw.githubusercontent.com/robertelee78/claude-telegram-mirror/master/scripts/install.sh -o install.sh
17
+ less install.sh # Review the script
18
+ bash install.sh
19
+ ```
20
+
21
+ The interactive installer will guide you through:
22
+ 1. Checking prerequisites (node, npm, git, tmux, jq, nc)
23
+ 2. Creating a Telegram bot via @BotFather
24
+ 3. Setting up a supergroup with Topics
25
+ 4. Verifying bot permissions
26
+ 5. Installing hooks and the system service
27
+
28
+ ### Other Scripts
29
+
30
+ ```bash
31
+ # Diagnose issues
32
+ ~/.local/share/claude-telegram-mirror/scripts/doctor.sh
33
+
34
+ # Uninstall completely
35
+ ~/.local/share/claude-telegram-mirror/scripts/uninstall.sh
36
+ ```
37
+
38
+ ## Manual Setup (for developers)
39
+
40
+ <details>
41
+ <summary>Click to expand manual installation steps</summary>
42
+
43
+ For developers who want to work on the source code:
44
+
45
+ ```bash
46
+ # 1. Clone and build
47
+ git clone https://github.com/robertelee78/claude-telegram-mirror.git
48
+ cd claude-telegram-mirror && npm install && npm run build
49
+
50
+ # 2. Create a Telegram bot via @BotFather, get the token
51
+
52
+ # 3. Create a supergroup with Topics enabled, add your bot as admin
53
+
54
+ # 4. Get your chat ID
55
+ ./scripts/get-chat-id.sh YOUR_BOT_TOKEN
56
+
57
+ # 5. Configure environment
58
+ cat > ~/.telegram-env << 'EOF'
59
+ export TELEGRAM_BOT_TOKEN="your-token-here"
60
+ export TELEGRAM_CHAT_ID="-100your-chat-id"
61
+ export TELEGRAM_MIRROR=true
62
+ EOF
63
+
64
+ # 6. Install hooks
65
+ node dist/cli.js install-hooks # Global install
66
+ # OR for projects with custom .claude/settings.json:
67
+ cd /path/to/project && node dist/cli.js install-hooks --project
68
+
69
+ # 7. Start daemon (choose one)
70
+ node dist/cli.js start # Foreground (for testing)
71
+ node dist/cli.js service install && \
72
+ node dist/cli.js service start # As system service (recommended)
73
+ ```
74
+
75
+ **Note:** When using the Quick Install method, use `ctm` instead of `node dist/cli.js`.
76
+
77
+ </details>
78
+
79
+ ## Features
80
+
81
+ - **CLI → Telegram**: Mirror Claude's responses, tool usage, and notifications
82
+ - **Telegram → CLI**: Send prompts from Telegram directly to Claude Code
83
+ - **Session Threading**: Each Claude session gets its own Forum Topic
84
+ - **Multi-System Support**: Run separate daemons on multiple machines
85
+ - **Compaction Notifications**: Get notified when Claude summarizes context
86
+
87
+ ## Architecture
88
+
89
+ ```
90
+ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
91
+ │ Claude Code │────▶│ Bridge Daemon │────▶│ Telegram │
92
+ │ CLI │◀────│ │◀────│ Bot │
93
+ └─────────────────┘ └─────────────────┘ └─────────────────┘
94
+ │ │
95
+ │ hooks │ Unix socket
96
+ ▼ ▼
97
+ ┌─────────────────┐ ┌─────────────────┐
98
+ │ telegram-hook │────▶│ Socket Server │
99
+ │ (bash) │ │ │
100
+ └─────────────────┘ └─────────────────┘
101
+ ```
102
+
103
+ **Flow:**
104
+ 1. Claude Code hooks capture events (prompts, responses, tool use)
105
+ 2. Hook script sends JSON to bridge daemon via Unix socket
106
+ 3. Bridge forwards messages to Telegram Forum Topic
107
+ 4. Telegram replies are injected into CLI via `tmux send-keys`
108
+
109
+ ## Multi-System Architecture
110
+
111
+ When running Claude Code on multiple machines, each system needs its own bot to avoid Telegram API conflicts (error 409: only one polling connection per bot token is allowed).
112
+
113
+ **The model:**
114
+ - **One daemon per host** - Each machine runs its own bridge daemon
115
+ - **One bot per daemon** - Each daemon uses a unique Telegram bot
116
+ - **Multiple sessions per host** - One daemon handles all Claude sessions on that machine
117
+ - **Shared supergroup** - All bots post to the same Telegram supergroup
118
+
119
+ ### Setup for Multiple Systems
120
+
121
+ 1. **Create one bot per system** via [@BotFather](https://t.me/botfather)
122
+ - System A: `@claude_mirror_system_a_bot`
123
+ - System B: `@claude_mirror_system_b_bot`
124
+
125
+ 2. **Add all bots to the same supergroup** with admin permissions
126
+
127
+ 3. **Configure each system** with its own bot token:
128
+ ```bash
129
+ # On System A (~/.telegram-env)
130
+ export TELEGRAM_BOT_TOKEN="token-for-system-a-bot"
131
+ export TELEGRAM_CHAT_ID="-100shared-group-id"
132
+
133
+ # On System B (~/.telegram-env)
134
+ export TELEGRAM_BOT_TOKEN="token-for-system-b-bot"
135
+ export TELEGRAM_CHAT_ID="-100shared-group-id" # Same group!
136
+ ```
137
+
138
+ 4. **Each daemon creates topics for its sessions** - Messages route correctly because each daemon only processes topics it created.
139
+
140
+ ## Prerequisites
141
+
142
+ - Node.js 18+
143
+ - Claude Code CLI
144
+ - tmux (for bidirectional communication)
145
+ - jq (JSON processing)
146
+ - nc (netcat, for socket communication)
147
+ - Telegram account
148
+
149
+ ## Telegram Setup
150
+
151
+ ### 1. Create a Bot
152
+
153
+ 1. Message [@BotFather](https://t.me/botfather) → `/newbot`
154
+ 2. Choose name and username (must end in `bot`)
155
+ 3. Save the API token
156
+
157
+ ### 2. Create Supergroup with Topics
158
+
159
+ 1. Create a new group in Telegram
160
+ 2. Add your bot to the group
161
+ 3. Group Settings → Enable **Topics**
162
+
163
+ ### 3. Make Bot an Admin
164
+
165
+ 1. Group Settings → Administrators → Add your bot
166
+ 2. Enable: **Manage Topics**, **Post Messages**
167
+
168
+ ### 4. Get Chat ID
169
+
170
+ 1. Send any message in the group
171
+ 2. Run the helper script:
172
+ ```bash
173
+ ./scripts/get-chat-id.sh YOUR_BOT_TOKEN
174
+ ```
175
+ Or manually: `https://api.telegram.org/botYOUR_TOKEN/getUpdates`
176
+ 3. Copy the chat ID (supergroups start with `-100`)
177
+
178
+ ### 5. Disable Privacy Mode
179
+
180
+ 1. [@BotFather](https://t.me/botfather) → `/mybots` → Select bot
181
+ 2. Bot Settings → Group Privacy → **Turn off**
182
+
183
+ ## Configuration
184
+
185
+ ### Environment Variables
186
+
187
+ Create `~/.telegram-env`:
188
+
189
+ ```bash
190
+ export TELEGRAM_BOT_TOKEN="123456789:ABCdefGHIjklMNOpqrsTUVwxyz"
191
+ export TELEGRAM_CHAT_ID="-1001234567890"
192
+ export TELEGRAM_MIRROR=true
193
+ # Optional:
194
+ # export TELEGRAM_MIRROR_VERBOSE=true
195
+ # export TELEGRAM_BRIDGE_SOCKET=~/.config/claude-telegram-mirror/bridge.sock
196
+ ```
197
+
198
+ Source in your shell profile (`~/.bashrc` or `~/.zshrc`):
199
+
200
+ ```bash
201
+ [[ -f ~/.telegram-env ]] && source ~/.telegram-env
202
+ ```
203
+
204
+ ### Test Connection
205
+
206
+ ```bash
207
+ ctm config --test
208
+ # ✅ Bot connected: @your_bot_username
209
+ # ✅ Test message sent to chat
210
+ ```
211
+
212
+ ## Usage
213
+
214
+ ### Start the Bridge
215
+
216
+ ```bash
217
+ # Foreground (for testing)
218
+ ctm start
219
+
220
+ # As system service (recommended for production)
221
+ ctm service install # Install systemd/launchd service
222
+ ctm service start # Start the service
223
+ ```
224
+
225
+ ### Run Claude in tmux
226
+
227
+ ```bash
228
+ tmux new -s claude
229
+ claude
230
+ # Bridge auto-detects tmux session
231
+ ```
232
+
233
+ ### CLI Commands
234
+
235
+ ```bash
236
+ # Daemon control
237
+ ctm start # Start daemon in foreground
238
+ ctm status # Show status
239
+ ctm config --test # Test connection
240
+
241
+ # Hook management
242
+ ctm install-hooks # Install global hooks
243
+ ctm install-hooks -p # Install to current project's .claude/
244
+ ctm uninstall-hooks # Remove hooks
245
+ ctm hooks # Show hook status
246
+
247
+ # Service management (systemd on Linux, launchd on macOS)
248
+ ctm service install # Install as system service
249
+ ctm service uninstall # Remove system service
250
+ ctm service start # Start service
251
+ ctm service stop # Stop service
252
+ ctm service restart # Restart service
253
+ ctm service status # Show service status
254
+ ```
255
+
256
+ **Note:** The `ctm` command is installed to `~/.local/bin/` by the installer. If you cloned the repo manually, use `node dist/cli.js` instead.
257
+
258
+ ## Project-Level Hooks
259
+
260
+ If your project has `.claude/settings.json` with custom hooks, global hooks are ignored. Install hooks to the project:
261
+
262
+ ```bash
263
+ cd /path/to/your/project
264
+ ctm install-hooks --project
265
+ # or shorthand:
266
+ ctm install-hooks -p
267
+ ```
268
+
269
+ The installer will prompt you to set up project-level hooks during installation. You can also add them later to any project.
270
+
271
+ ## How Messages Flow
272
+
273
+ | Direction | Event | Display |
274
+ |-----------|-------|---------|
275
+ | CLI → Telegram | User types | 👤 User (cli): ... |
276
+ | CLI → Telegram | Tool starts | 🔧 Running: Bash |
277
+ | CLI → Telegram | Claude responds | 🤖 Claude: ... |
278
+ | CLI → Telegram | Session starts | New Forum Topic created |
279
+ | CLI → Telegram | Context compacting | ⏳ Notification sent |
280
+ | Telegram → CLI | User sends message | Injected via tmux |
281
+
282
+ ## Technical Details
283
+
284
+ - **Session storage**: SQLite at `~/.config/claude-telegram-mirror/sessions.db`
285
+ - **Socket path**: `~/.config/claude-telegram-mirror/bridge.sock`
286
+ - **Response extraction**: Reads Claude's transcript `.jsonl` on Stop event
287
+ - **Deduplication**: Telegram-originated messages tracked to prevent echo
288
+ - **Topic routing**: Each daemon only processes topics it created (multi-bot safe)
289
+ - **Compaction alerts**: PreCompact hook sends notification before context summarization
290
+
291
+ ## Troubleshooting
292
+
293
+ **Hooks not firing?**
294
+ - Check if project has local `.claude/settings.json` overriding globals
295
+ - Run `ctm install-hooks -p` from project directory
296
+ - Restart Claude Code after installing hooks
297
+
298
+ **409 Conflict error?**
299
+ - Only one polling connection per bot token is allowed
300
+ - If running multiple systems, each needs its own bot (see Multi-System Architecture)
301
+ - Kill duplicate daemons: `pkill -f "claude-telegram-mirror"`
302
+
303
+ **Bridge not receiving events?**
304
+ - Check socket: `ls -la ~/.config/claude-telegram-mirror/bridge.sock`
305
+ - Enable debug: `export TELEGRAM_HOOK_DEBUG=1` then retry
306
+ - Check debug log: `cat ~/.config/claude-telegram-mirror/hook-debug.log`
307
+
308
+ **tmux injection not working?**
309
+ - Verify tmux session: `tmux list-sessions`
310
+ - Check daemon logs for "Session tmux target stored"
311
+
312
+ **Messages going to wrong topic?**
313
+ - Clear session DB: `rm ~/.config/claude-telegram-mirror/sessions.db`
314
+
315
+ **Service not starting (Linux)?**
316
+ - Check status: `systemctl --user status claude-telegram-mirror`
317
+ - View logs: `journalctl --user -u claude-telegram-mirror -f`
318
+ - Enable linger: `loginctl enable-linger $USER`
319
+
320
+ **Service not starting (macOS)?**
321
+ - Check status: `launchctl list | grep claude`
322
+ - View logs: `cat ~/Library/Logs/claude-telegram-mirror.*.log`
323
+ - Check permissions: Ensure Terminal has Accessibility access
324
+
325
+ ## License
326
+
327
+ MIT
328
+
329
+ ## Credits
330
+
331
+ Built as part of the claude-mobile project for remote Claude Code interaction.
@@ -0,0 +1,41 @@
1
+ /**
2
+ * Bot Command Handlers
3
+ * Implements all Telegram bot commands
4
+ */
5
+ import { Bot, Context, SessionFlavor, InlineKeyboard } from 'grammy';
6
+ interface SessionData {
7
+ attachedSessionId: string | null;
8
+ muted: boolean;
9
+ lastActivity: number;
10
+ }
11
+ type BotContext = Context & SessionFlavor<SessionData>;
12
+ export interface BridgeCallbacks {
13
+ getActiveSessions(): Promise<Array<{
14
+ id: string;
15
+ startedAt: Date;
16
+ projectDir?: string;
17
+ }>>;
18
+ abortSession(sessionId: string): Promise<boolean>;
19
+ sendToSession(sessionId: string, text: string): Promise<boolean>;
20
+ }
21
+ /**
22
+ * Register all bot commands
23
+ */
24
+ export declare function registerCommands(bot: Bot<BotContext>, bridge?: BridgeCallbacks): void;
25
+ /**
26
+ * Create approval keyboard
27
+ */
28
+ export declare function createApprovalKeyboard(approvalId: string): InlineKeyboard;
29
+ /**
30
+ * Register approval handlers
31
+ */
32
+ export declare function registerApprovalHandlers(bot: Bot<BotContext>, onApproval: (approvalId: string, action: 'approve' | 'reject' | 'abort') => Promise<void>): void;
33
+ /**
34
+ * Register tool details handler
35
+ */
36
+ export declare function registerToolDetailsHandler(bot: Bot<BotContext>, getToolDetails: (toolUseId: string) => {
37
+ tool: string;
38
+ input: unknown;
39
+ } | undefined): void;
40
+ export default registerCommands;
41
+ //# sourceMappingURL=commands.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"commands.d.ts","sourceRoot":"","sources":["../../src/bot/commands.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,GAAG,EAAE,OAAO,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,QAAQ,CAAC;AASrE,UAAU,WAAW;IACnB,iBAAiB,EAAE,MAAM,GAAG,IAAI,CAAC;IACjC,KAAK,EAAE,OAAO,CAAC;IACf,YAAY,EAAE,MAAM,CAAC;CACtB;AAED,KAAK,UAAU,GAAG,OAAO,GAAG,aAAa,CAAC,WAAW,CAAC,CAAC;AAGvD,MAAM,WAAW,eAAe;IAC9B,iBAAiB,IAAI,OAAO,CAAC,KAAK,CAAC;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,IAAI,CAAC;QAAC,UAAU,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC,CAAC;IAC1F,YAAY,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IAClD,aAAa,CAAC,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;CAClE;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAC9B,GAAG,EAAE,GAAG,CAAC,UAAU,CAAC,EACpB,MAAM,CAAC,EAAE,eAAe,GACvB,IAAI,CA2NN;AAED;;GAEG;AACH,wBAAgB,sBAAsB,CAAC,UAAU,EAAE,MAAM,GAAG,cAAc,CAMzE;AAED;;GAEG;AACH,wBAAgB,wBAAwB,CACtC,GAAG,EAAE,GAAG,CAAC,UAAU,CAAC,EACpB,UAAU,EAAE,CAAC,UAAU,EAAE,MAAM,EAAE,MAAM,EAAE,SAAS,GAAG,QAAQ,GAAG,OAAO,KAAK,OAAO,CAAC,IAAI,CAAC,GACxF,IAAI,CA4BN;AAED;;GAEG;AACH,wBAAgB,0BAA0B,CACxC,GAAG,EAAE,GAAG,CAAC,UAAU,CAAC,EACpB,cAAc,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,OAAO,CAAA;CAAE,GAAG,SAAS,GAClF,IAAI,CA+BN;AAED,eAAe,gBAAgB,CAAC"}
@@ -0,0 +1,231 @@
1
+ /**
2
+ * Bot Command Handlers
3
+ * Implements all Telegram bot commands
4
+ */
5
+ import { InlineKeyboard } from 'grammy';
6
+ import { formatHelp, formatStatus, formatToolDetails } from './formatting.js';
7
+ import logger from '../utils/logger.js';
8
+ /**
9
+ * Register all bot commands
10
+ */
11
+ export function registerCommands(bot, bridge) {
12
+ // /start - Welcome message
13
+ bot.command('start', async (ctx) => {
14
+ await ctx.reply('👋 *Claude Code Mirror Bot*\n\n' +
15
+ 'I mirror your Claude Code sessions to Telegram, allowing you to:\n' +
16
+ '• Monitor agent progress from your phone\n' +
17
+ '• Send responses and commands remotely\n' +
18
+ '• Approve/reject actions via buttons\n\n' +
19
+ 'Use /help to see all available commands.', { parse_mode: 'Markdown' });
20
+ logger.info('User started bot');
21
+ });
22
+ // /help - Show commands
23
+ bot.command('help', async (ctx) => {
24
+ await ctx.reply(formatHelp(), { parse_mode: 'Markdown' });
25
+ });
26
+ // /status - Current session status
27
+ bot.command('status', async (ctx) => {
28
+ const session = ctx.session;
29
+ const hasSession = !!session?.attachedSessionId;
30
+ await ctx.reply(formatStatus(hasSession, session?.attachedSessionId || undefined, session?.muted), { parse_mode: 'Markdown' });
31
+ });
32
+ // /sessions - List active sessions
33
+ bot.command('sessions', async (ctx) => {
34
+ if (!bridge) {
35
+ await ctx.reply('⚠️ Bridge not connected. No session info available.');
36
+ return;
37
+ }
38
+ try {
39
+ const sessions = await bridge.getActiveSessions();
40
+ if (sessions.length === 0) {
41
+ await ctx.reply('📭 No active sessions.');
42
+ return;
43
+ }
44
+ let message = '📋 *Active Sessions:*\n\n';
45
+ sessions.forEach((s, idx) => {
46
+ const age = Math.floor((Date.now() - s.startedAt.getTime()) / 60000);
47
+ message += `${idx + 1}. \`${s.id}\`\n`;
48
+ message += ` Started: ${age}m ago\n`;
49
+ if (s.projectDir) {
50
+ message += ` Project: \`${s.projectDir}\`\n`;
51
+ }
52
+ message += '\n';
53
+ });
54
+ message += '_Use /attach <id> to attach to a session_';
55
+ await ctx.reply(message, { parse_mode: 'Markdown' });
56
+ }
57
+ catch (error) {
58
+ logger.error('Failed to get sessions', { error });
59
+ await ctx.reply('❌ Failed to fetch sessions.');
60
+ }
61
+ });
62
+ // /attach <id> - Attach to session
63
+ bot.command('attach', async (ctx) => {
64
+ const sessionId = ctx.match?.trim();
65
+ if (!sessionId) {
66
+ await ctx.reply('⚠️ Please provide a session ID.\n\n' +
67
+ 'Usage: `/attach <session-id>`\n\n' +
68
+ 'Use /sessions to see available sessions.', { parse_mode: 'Markdown' });
69
+ return;
70
+ }
71
+ ctx.session.attachedSessionId = sessionId;
72
+ ctx.session.muted = false;
73
+ await ctx.reply(`✅ Attached to session \`${sessionId}\`\n\n` +
74
+ 'You will now receive updates from this session.\n' +
75
+ 'Reply with text to send input.', { parse_mode: 'Markdown' });
76
+ logger.info('Attached to session', { sessionId });
77
+ });
78
+ // /detach - Detach from session
79
+ bot.command('detach', async (ctx) => {
80
+ const currentSession = ctx.session.attachedSessionId;
81
+ if (!currentSession) {
82
+ await ctx.reply('ℹ️ You are not attached to any session.');
83
+ return;
84
+ }
85
+ ctx.session.attachedSessionId = null;
86
+ await ctx.reply(`🔌 Detached from session \`${currentSession}\`\n\n` +
87
+ 'You will no longer receive updates.', { parse_mode: 'Markdown' });
88
+ logger.info('Detached from session', { sessionId: currentSession });
89
+ });
90
+ // /mute - Mute notifications
91
+ bot.command('mute', async (ctx) => {
92
+ if (ctx.session.muted) {
93
+ await ctx.reply('🔇 Notifications already muted.');
94
+ return;
95
+ }
96
+ ctx.session.muted = true;
97
+ await ctx.reply('🔇 Notifications muted.\n\n' +
98
+ 'Use /unmute to resume.', { parse_mode: 'Markdown' });
99
+ logger.info('Notifications muted');
100
+ });
101
+ // /unmute - Resume notifications
102
+ bot.command('unmute', async (ctx) => {
103
+ if (!ctx.session.muted) {
104
+ await ctx.reply('🔔 Notifications already active.');
105
+ return;
106
+ }
107
+ ctx.session.muted = false;
108
+ await ctx.reply('🔔 Notifications resumed.');
109
+ logger.info('Notifications unmuted');
110
+ });
111
+ // /abort - Abort current session
112
+ bot.command('abort', async (ctx) => {
113
+ const sessionId = ctx.session.attachedSessionId;
114
+ if (!sessionId) {
115
+ await ctx.reply('⚠️ No session attached. Use /attach first.');
116
+ return;
117
+ }
118
+ // Show confirmation
119
+ const keyboard = new InlineKeyboard()
120
+ .text('🛑 Yes, abort', `confirm_abort:${sessionId}`)
121
+ .text('❌ Cancel', 'cancel_abort');
122
+ await ctx.reply(`⚠️ *Abort Session?*\n\n` +
123
+ `This will terminate session \`${sessionId}\`.\n\n` +
124
+ 'Are you sure?', {
125
+ parse_mode: 'Markdown',
126
+ reply_markup: keyboard
127
+ });
128
+ });
129
+ // Handle abort confirmation
130
+ bot.callbackQuery(/^confirm_abort:(.+)$/, async (ctx) => {
131
+ const sessionId = ctx.match[1];
132
+ if (bridge) {
133
+ try {
134
+ const success = await bridge.abortSession(sessionId);
135
+ if (success) {
136
+ ctx.session.attachedSessionId = null;
137
+ await ctx.editMessageText(`🛑 Session \`${sessionId}\` aborted.`, { parse_mode: 'Markdown' });
138
+ }
139
+ else {
140
+ await ctx.editMessageText('❌ Failed to abort session.');
141
+ }
142
+ }
143
+ catch (error) {
144
+ logger.error('Failed to abort session', { sessionId, error });
145
+ await ctx.editMessageText('❌ Error aborting session.');
146
+ }
147
+ }
148
+ else {
149
+ ctx.session.attachedSessionId = null;
150
+ await ctx.editMessageText(`🛑 Detached from session \`${sessionId}\`.\n\n` +
151
+ '_(Bridge not connected - session may still be running)_', { parse_mode: 'Markdown' });
152
+ }
153
+ await ctx.answerCallbackQuery();
154
+ });
155
+ // Handle abort cancellation
156
+ bot.callbackQuery('cancel_abort', async (ctx) => {
157
+ await ctx.editMessageText('✅ Abort cancelled.');
158
+ await ctx.answerCallbackQuery();
159
+ });
160
+ // /ping - Simple health check
161
+ bot.command('ping', async (ctx) => {
162
+ const start = Date.now();
163
+ const msg = await ctx.reply('🏓 Pong!');
164
+ const latency = Date.now() - start;
165
+ await ctx.api.editMessageText(ctx.chat.id, msg.message_id, `🏓 Pong! _${latency}ms_`, { parse_mode: 'Markdown' });
166
+ });
167
+ }
168
+ /**
169
+ * Create approval keyboard
170
+ */
171
+ export function createApprovalKeyboard(approvalId) {
172
+ return new InlineKeyboard()
173
+ .text('✅ Approve', `approve:${approvalId}`)
174
+ .text('❌ Reject', `reject:${approvalId}`)
175
+ .row()
176
+ .text('🛑 Abort Session', `abort:${approvalId}`);
177
+ }
178
+ /**
179
+ * Register approval handlers
180
+ */
181
+ export function registerApprovalHandlers(bot, onApproval) {
182
+ bot.callbackQuery(/^(approve|reject|abort):(.+)$/, async (ctx) => {
183
+ const action = ctx.match[1];
184
+ const approvalId = ctx.match[2];
185
+ try {
186
+ await onApproval(approvalId, action);
187
+ // Update message to show decision
188
+ const actionText = {
189
+ approve: '✅ Approved',
190
+ reject: '❌ Rejected',
191
+ abort: '🛑 Session Aborted'
192
+ }[action];
193
+ const originalText = ctx.callbackQuery.message?.text || '';
194
+ await ctx.editMessageText(`${originalText}\n\n*Decision:* ${actionText}`, { parse_mode: 'Markdown' });
195
+ await ctx.answerCallbackQuery({ text: `${actionText}!` });
196
+ }
197
+ catch (error) {
198
+ logger.error('Failed to process approval', { approvalId, action, error });
199
+ await ctx.answerCallbackQuery({ text: 'Error processing response' });
200
+ }
201
+ });
202
+ }
203
+ /**
204
+ * Register tool details handler
205
+ */
206
+ export function registerToolDetailsHandler(bot, getToolDetails) {
207
+ bot.callbackQuery(/^tooldetails:(.+)$/, async (ctx) => {
208
+ const toolUseId = ctx.match[1];
209
+ try {
210
+ const details = getToolDetails(toolUseId);
211
+ if (!details) {
212
+ await ctx.answerCallbackQuery({ text: 'Details expired (5 min cache)', show_alert: true });
213
+ return;
214
+ }
215
+ // Format the tool details nicely for mobile
216
+ const formattedDetails = formatToolDetails(details.tool, details.input);
217
+ // Reply with full details (don't edit original - keep it clean)
218
+ await ctx.reply(formattedDetails, {
219
+ parse_mode: 'Markdown',
220
+ reply_parameters: { message_id: ctx.callbackQuery.message?.message_id || 0 }
221
+ });
222
+ await ctx.answerCallbackQuery();
223
+ }
224
+ catch (error) {
225
+ logger.error('Failed to show tool details', { toolUseId, error });
226
+ await ctx.answerCallbackQuery({ text: 'Error loading details' });
227
+ }
228
+ });
229
+ }
230
+ export default registerCommands;
231
+ //# sourceMappingURL=commands.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"commands.js","sourceRoot":"","sources":["../../src/bot/commands.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,62 @@
1
+ /**
2
+ * Message Formatting Utilities
3
+ * Formats Claude Code output for Telegram display
4
+ */
5
+ /**
6
+ * Strip ANSI escape codes from text
7
+ */
8
+ export declare function stripAnsi(text: string): string;
9
+ /**
10
+ * Escape special characters for MarkdownV2
11
+ * Note: Don't escape inside code blocks
12
+ */
13
+ export declare function escapeMarkdownV2(text: string): string;
14
+ /**
15
+ * Format agent response for Telegram
16
+ */
17
+ export declare function formatAgentResponse(content: string): string;
18
+ /**
19
+ * Format tool execution for Telegram
20
+ */
21
+ export declare function formatToolExecution(tool: string, input: unknown, output: unknown, verbose?: boolean): string;
22
+ /**
23
+ * Format approval request for Telegram
24
+ */
25
+ export declare function formatApprovalRequest(prompt: string): string;
26
+ /**
27
+ * Format error message for Telegram
28
+ */
29
+ export declare function formatError(error: Error | string): string;
30
+ /**
31
+ * Format session start notification
32
+ */
33
+ export declare function formatSessionStart(sessionId: string, projectDir?: string, hostname?: string): string;
34
+ /**
35
+ * Format session end notification
36
+ */
37
+ export declare function formatSessionEnd(sessionId: string, duration?: number): string;
38
+ /**
39
+ * Format status message
40
+ */
41
+ export declare function formatStatus(isActive: boolean, sessionId?: string, muted?: boolean): string;
42
+ /**
43
+ * Format help message
44
+ */
45
+ export declare function formatHelp(): string;
46
+ /**
47
+ * Format a message and chunk if necessary
48
+ */
49
+ export declare function formatAndChunk(content: string, maxLength?: number): string[];
50
+ /**
51
+ * Detect code language from content (best effort)
52
+ */
53
+ export declare function detectLanguage(content: string): string;
54
+ /**
55
+ * Wrap content in code block with language detection
56
+ */
57
+ export declare function wrapInCodeBlock(content: string, language?: string): string;
58
+ /**
59
+ * Format tool details for mobile-friendly Telegram display
60
+ */
61
+ export declare function formatToolDetails(tool: string, input: unknown): string;
62
+ //# sourceMappingURL=formatting.d.ts.map