@fonz/tgcc 0.0.1 → 0.2.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.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 fnz
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,266 @@
1
+ # @fonz/tgcc
2
+
3
+ **Telegram ↔ Claude Code bridge** — run Claude Code sessions from Telegram with full streaming, inline editing, and multi-agent support.
4
+
5
+ ## What is TGCC?
6
+
7
+ TGCC bridges the [Claude Code CLI](https://docs.anthropic.com/en/docs/claude-code) to Telegram, turning any Telegram bot into a full Claude Code client.
8
+
9
+ - **Streaming output** — CC responses stream into a single Telegram message that updates in place (no message spam)
10
+ - **Multi-agent** — run one bot per project/repo, each with its own config, model, and permissions
11
+ - **Session management** — resume, switch, and list sessions with inline keyboard buttons
12
+ - **Permission relay** — CC permission prompts appear as inline buttons (Allow / Deny / Allow All)
13
+ - **Thinking display** — thinking content shown as expandable blockquotes (collapsible in Telegram)
14
+ - **Sub-agent threading** — sub-agent tool calls appear as threaded replies to the main message
15
+ - **Staleness detection** — detects when a session was modified externally (e.g. from VS Code) and reconnects
16
+ - **Usage stats** — per-turn token counts and cost shown as a subtle footer
17
+ - **CLI tool** — send messages from your terminal, manage agents and repos
18
+ - **HTML formatting** — code blocks with syntax highlighting, bold, italic, links
19
+
20
+ ## Architecture
21
+
22
+ ```
23
+ User ──► Telegram ──► TGCC ──► Claude Code CLI (stream-json)
24
+
25
+ config.json (agents, repos, permissions)
26
+ state.json (sessions, models, per-user overrides)
27
+ ```
28
+
29
+ TGCC runs as a persistent service. Each configured agent connects to its own Telegram bot. When a user sends a message, TGCC spawns (or reuses) a Claude Code process using the `stream-json` protocol, forwards the message, and streams the response back to Telegram with edit-in-place updates.
30
+
31
+ **Key design decisions:**
32
+ - **Config-driven** — everything in `~/.tgcc/config.json`, hot-reloaded on changes
33
+ - **Unix sockets** — CLI communicates with the running service via per-agent sockets in `/tmp/tgcc/ctl/`
34
+ - **MCP bridge** — CC can send files, images, and voice back to the user via a built-in MCP server
35
+ - **State-aware hang detection** — distinguishes between API waits, tool execution (checks child processes), and real hangs
36
+
37
+ ## Quick Start
38
+
39
+ ```bash
40
+ # Install
41
+ npm install -g @fonz/tgcc
42
+
43
+ # Create a Telegram bot via @BotFather, get the token
44
+
45
+ # Register an agent
46
+ tgcc agent add mybot --bot-token 123456:ABC-DEF --repo ~/myproject
47
+
48
+ # Start the service
49
+ tgcc
50
+
51
+ # Send your bot a message on Telegram!
52
+ ```
53
+
54
+ ## Configuration
55
+
56
+ Config lives at `~/.tgcc/config.json`. TGCC creates it automatically when you run `tgcc agent add`.
57
+
58
+ ```jsonc
59
+ {
60
+ "global": {
61
+ "ccBinaryPath": "claude", // Path to claude CLI binary
62
+ "logLevel": "info",
63
+ "mediaDir": "~/.tgcc/media",
64
+ "socketDir": "/tmp/tgcc",
65
+ "stateFile": "~/.tgcc/state.json"
66
+ },
67
+ "repos": {
68
+ "myproject": "/home/user/myproject",
69
+ "backend": "/home/user/backend"
70
+ },
71
+ "agents": {
72
+ "mybot": {
73
+ "botToken": "123456:ABC-DEF...",
74
+ "allowedUsers": ["123456789"], // Telegram user IDs
75
+ "defaults": {
76
+ "model": "claude-sonnet-4-20250514",
77
+ "repo": "myproject",
78
+ "permissionMode": "acceptEdits",
79
+ "maxTurns": 30,
80
+ "idleTimeoutMs": 600000,
81
+ "hangTimeoutMs": 300000
82
+ }
83
+ }
84
+ }
85
+ }
86
+ ```
87
+
88
+ ### Permission Modes
89
+
90
+ | Mode | Description |
91
+ |------|-------------|
92
+ | `dangerously-skip` | Skip all permission prompts (⚠️ use with care) |
93
+ | `acceptEdits` | Auto-accept file edits, prompt for everything else |
94
+ | `default` | CC's built-in permission flow — prompts appear as inline buttons in Telegram |
95
+ | `plan` | Plan-only mode, no tool execution |
96
+
97
+ ### Repo Registry
98
+
99
+ Repos are named shortcuts for project paths. Register them once, use everywhere:
100
+
101
+ ```bash
102
+ tgcc repo add myproject ~/code/myproject
103
+ tgcc repo add backend ~/code/backend
104
+ tgcc repo assign mybot myproject # Set as agent's default
105
+ ```
106
+
107
+ In Telegram, `/repo` shows an inline keyboard to switch repos on the fly.
108
+
109
+ ## Telegram Commands
110
+
111
+ | Command | Description |
112
+ |---------|-------------|
113
+ | `/start` | Welcome message, register bot commands |
114
+ | `/help` | List all commands |
115
+ | `/new` | Start a fresh session |
116
+ | `/sessions` | List recent sessions with Resume/Delete buttons |
117
+ | `/resume <id>` | Resume a session by ID |
118
+ | `/session` | Current session info |
119
+ | `/status` | Process state, model, repo, uptime, cost |
120
+ | `/cost` | Show session cost |
121
+ | `/model <name>` | Switch model (takes effect on next spawn) |
122
+ | `/permissions` | Set permission mode with inline buttons |
123
+ | `/repo` | Switch repo with inline buttons |
124
+ | `/cancel` | Abort current CC turn (sends SIGINT) |
125
+ | `/catchup` | Summarize external CC activity on the same repo |
126
+ | `/ping` | Liveness check |
127
+
128
+ ### Examples
129
+
130
+ **Start a conversation:**
131
+ > You: Fix the auth middleware to handle expired tokens
132
+
133
+ > Bot:
134
+ > <blockquote expandable>💭 Thinking<br>Looking at the auth middleware...</blockquote>
135
+ >
136
+ > I've updated `src/middleware/auth.ts` to handle expired tokens gracefully...
137
+ >
138
+ > *↩️ 1.2k in · 450 out · $0.0034*
139
+
140
+ **Permission prompt (when not using bypass mode):**
141
+ > 🔐 CC wants to use `Write`
142
+ > ```{"file_path": "src/auth.ts", ...}```
143
+ >
144
+ > `[✅ Allow]` `[❌ Deny]` `[✅ Allow All]`
145
+
146
+ **Switch repos:**
147
+ > `/repo`
148
+ >
149
+ > Current repo: `~/myproject`
150
+ >
151
+ > `[myproject]`
152
+ > `[backend]`
153
+ > `[➕ Add]` `[❓ Help]`
154
+
155
+ ## CLI Commands
156
+
157
+ The `tgcc` CLI communicates with the running service via Unix sockets.
158
+
159
+ ```bash
160
+ # Start the service (foreground)
161
+ tgcc
162
+
163
+ # Send a message to a running agent
164
+ tgcc message "fix the login bug" --agent mybot
165
+ tgcc msg "deploy to staging" --agent mybot --session abc123
166
+
167
+ # Check status
168
+ tgcc status
169
+ tgcc status --agent mybot
170
+
171
+ # Agent management
172
+ tgcc agent add mybot --bot-token <token> --repo ~/project
173
+ tgcc agent remove mybot
174
+ tgcc agent rename mybot newname
175
+ tgcc agent list
176
+ tgcc agent repo mybot backend # Set default repo
177
+
178
+ # Repo management
179
+ tgcc repo add myproject ~/code/myproject
180
+ tgcc repo remove myproject
181
+ tgcc repo assign mybot myproject
182
+ tgcc repo clear mybot
183
+ tgcc repo list
184
+
185
+ # Permissions
186
+ tgcc permissions set mybot dangerously-skip
187
+ ```
188
+
189
+ ## Features in Detail
190
+
191
+ ### Streaming with Edit-in-Place
192
+
193
+ CC output streams into a single Telegram message. As CC produces text, TGCC edits the same message with the accumulated content (throttled to ~1 edit/second to respect Telegram rate limits). When the message gets too long (~4000 chars), it splits into a new message.
194
+
195
+ ### HTML Formatting
196
+
197
+ All output uses Telegram's HTML parse mode:
198
+ - Code blocks → `<pre><code class="language-python">...</code></pre>`
199
+ - Inline code → `<code>...</code>`
200
+ - Bold, italic, strikethrough, links — all converted from CC's markdown
201
+
202
+ ### Thinking in Expandable Blockquotes
203
+
204
+ When CC thinks, the thinking content is captured and displayed as a collapsible blockquote:
205
+
206
+ ```html
207
+ <blockquote expandable>💭 Thinking
208
+ Analyzing the auth middleware pattern...
209
+ </blockquote>
210
+ ```
211
+
212
+ Users can tap to expand and see what CC was thinking.
213
+
214
+ ### Sub-Agent Activity
215
+
216
+ When CC spawns sub-agents (via `dispatch_agent`, `Task`, etc.), TGCC sends a threaded reply:
217
+
218
+ > 🔄 Sub-agent spawned: `dispatch_agent`
219
+ > *(updates with input preview, then ✅ on completion)*
220
+
221
+ ### Smart Hang Detection
222
+
223
+ TGCC tracks CC's activity state and checks for active child processes before declaring a hang:
224
+ - **Tool executing** with active children → extend timer
225
+ - **Waiting for API** → extend timer (API can be slow)
226
+ - **No activity** for hangTimeoutMs → truly hung, kill and notify
227
+
228
+ ### Session Staleness Detection
229
+
230
+ If you use the same CC session from both Telegram and VS Code, TGCC detects the modification when you next message from Telegram. It shows a summary of what happened externally and reconnects cleanly.
231
+
232
+ ## How It Works with Claude Code
233
+
234
+ TGCC uses CC's `stream-json` protocol:
235
+
236
+ 1. **Spawn** — `claude -p --input-format stream-json --output-format stream-json --verbose --include-partial-messages`
237
+ 2. **Initialize** — Send `control_request` with `subtype: "initialize"` (SDK handshake)
238
+ 3. **Messages** — Write JSON user messages to stdin, parse NDJSON events from stdout
239
+ 4. **Streaming** — `stream_event` wraps inner events: `message_start`, `content_block_start`, `content_block_delta` (text, thinking, image), `content_block_stop`, `message_stop`
240
+ 5. **Permissions** — CC sends `control_request` with `subtype: "can_use_tool"`, TGCC relays to Telegram and sends `control_response` back
241
+ 6. **Sessions** — `--resume <id>` reconnects to an existing session, same JSONL files as the VS Code extension
242
+ 7. **Results** — `result` event with cost, token usage, success/error status
243
+
244
+ Sessions are fully interoperable with the VS Code Claude Code extension — the same `~/.claude/projects/` JSONL files are used by both.
245
+
246
+ ## Project Structure
247
+
248
+ ```
249
+ src/
250
+ ├── cli.ts # CLI tool (tgcc command)
251
+ ├── bridge.ts # Core orchestrator (TG ↔ CC)
252
+ ├── cc-process.ts # CC process lifecycle management
253
+ ├── cc-protocol.ts # CC stream-json protocol types & parser
254
+ ├── streaming.ts # Stream accumulator (edit-in-place, splitting)
255
+ ├── telegram.ts # Telegram bot (grammY)
256
+ ├── config.ts # Config loading, validation, hot-reload
257
+ ├── session.ts # Session store, staleness, catchup
258
+ ├── ctl-server.ts # Unix socket server for CLI communication
259
+ ├── mcp-bridge.ts # MCP server for CC → TG file/image/voice
260
+ ├── mcp-server.ts # MCP tool definitions
261
+ └── index.ts # Entry point
262
+ ```
263
+
264
+ ## License
265
+
266
+ MIT
@@ -0,0 +1,32 @@
1
+ import { EventEmitter } from 'node:events';
2
+ import pino from 'pino';
3
+ import type { TgccConfig, ConfigDiff } from './config.js';
4
+ import { type CtlHandler, type CtlAckResponse, type CtlStatusResponse } from './ctl-server.js';
5
+ export declare class Bridge extends EventEmitter implements CtlHandler {
6
+ private config;
7
+ private agents;
8
+ private mcpServer;
9
+ private ctlServer;
10
+ private sessionStore;
11
+ private logger;
12
+ constructor(config: TgccConfig, logger?: pino.Logger);
13
+ start(): Promise<void>;
14
+ private startAgent;
15
+ handleConfigChange(newConfig: TgccConfig, diff: ConfigDiff): Promise<void>;
16
+ private stopAgent;
17
+ private handleTelegramMessage;
18
+ private sendToCC;
19
+ /** Check if the session JSONL was modified externally since we last tracked it. */
20
+ private checkSessionStaleness;
21
+ /** Update JSONL tracking from the current file state. */
22
+ private updateJsonlTracking;
23
+ private spawnCCProcess;
24
+ private handleStreamEvent;
25
+ private handleResult;
26
+ private handleSlashCommand;
27
+ private handleCallbackQuery;
28
+ handleCtlMessage(agentId: string, text: string, sessionId?: string): CtlAckResponse;
29
+ handleCtlStatus(agentId?: string): CtlStatusResponse;
30
+ private handleMcpToolRequest;
31
+ stop(): Promise<void>;
32
+ }