@thxmxx/telegram-mcp 1.0.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/README.md +88 -0
- package/package.json +16 -0
- package/src/cli.js +27 -0
- package/src/index.js +179 -0
- package/src/setup.js +164 -0
package/README.md
ADDED
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
# @thxmxx/telegram-mcp
|
|
2
|
+
|
|
3
|
+
Telegram bridge for Claude Code. While Claude runs tasks on your machine, it notifies you, asks questions and shows option buttons — on your phone **and** on the terminal simultaneously. Whichever you answer first wins.
|
|
4
|
+
|
|
5
|
+
## Install (one-time, global)
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npx @thxmxx/telegram-mcp init
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
The wizard asks for your Telegram bot token and user ID, registers the MCP server globally in Claude Code, and installs the `/use-telegram` slash command. You never need to run this again.
|
|
12
|
+
|
|
13
|
+
## Usage
|
|
14
|
+
|
|
15
|
+
In any Claude Code session, activate Telegram with the slash command:
|
|
16
|
+
|
|
17
|
+
```
|
|
18
|
+
/use-telegram
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
Or just mention it naturally in your prompt:
|
|
22
|
+
|
|
23
|
+
```
|
|
24
|
+
Refactor the auth module and notify me on Telegram when done.
|
|
25
|
+
Deploy to staging — ask me on Telegram if anything is unclear.
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
### Modes
|
|
29
|
+
|
|
30
|
+
```
|
|
31
|
+
/use-telegram Full mode — notify + ask + choose
|
|
32
|
+
/use-telegram notify Notifications only, no questions
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
### Combining with other slash commands
|
|
36
|
+
|
|
37
|
+
Slash commands are independent and composable:
|
|
38
|
+
|
|
39
|
+
```
|
|
40
|
+
/deploy staging
|
|
41
|
+
/use-telegram notify
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
## How it works
|
|
45
|
+
|
|
46
|
+
```
|
|
47
|
+
Claude Code runs a task
|
|
48
|
+
↓ calls telegram_choose("Which DB?", ["PostgreSQL", "MySQL", "SQLite"])
|
|
49
|
+
You get buttons on Telegram AND a numbered list on the terminal
|
|
50
|
+
↓ you tap PostgreSQL on your phone (or type 1 in the terminal)
|
|
51
|
+
Claude receives "PostgreSQL" and continues
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
Every message is tagged with an auto-generated instance label like `[backend#a3f2]` or `[frontend#9c11]` — so when you have multiple Claude Code sessions open you always know which one is talking.
|
|
55
|
+
|
|
56
|
+
If you answer from the terminal, Telegram confirms it:
|
|
57
|
+
```
|
|
58
|
+
[backend#a3f2] ✅ PostgreSQL (via terminal)
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
## Tools Claude gains
|
|
62
|
+
|
|
63
|
+
| Tool | Description |
|
|
64
|
+
|---|---|
|
|
65
|
+
| `telegram_notify` | Send a progress update. No reply needed. |
|
|
66
|
+
| `telegram_ask` | Ask a free-form question. Waits for reply. |
|
|
67
|
+
| `telegram_choose` | Show option buttons. Waits for a tap. |
|
|
68
|
+
|
|
69
|
+
## Requirements
|
|
70
|
+
|
|
71
|
+
- Node.js 18+
|
|
72
|
+
- [Claude Code](https://docs.claude.ai/claude-code) installed and logged in
|
|
73
|
+
- A Telegram bot token — get one free from [@BotFather](https://t.me/botfather)
|
|
74
|
+
- Your Telegram user ID — message [@userinfobot](https://t.me/userinfobot)
|
|
75
|
+
|
|
76
|
+
## Permissions
|
|
77
|
+
|
|
78
|
+
On first use, Claude Code will ask you to approve the three tools this MCP server registers (`telegram_notify`, `telegram_ask`, `telegram_choose`). This is standard Claude Code behaviour — you can review exactly what is being granted before accepting.
|
|
79
|
+
|
|
80
|
+
## Security
|
|
81
|
+
|
|
82
|
+
- The MCP server only accepts responses from your configured Telegram user ID
|
|
83
|
+
- Credentials are stored in `~/.claude.json` by Claude Code — never in this repo
|
|
84
|
+
- If your token is ever exposed, revoke it immediately via @BotFather `/revoke` then re-run `init`
|
|
85
|
+
|
|
86
|
+
## License
|
|
87
|
+
|
|
88
|
+
MIT
|
package/package.json
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@thxmxx/telegram-mcp",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Telegram bridge for Claude Code — notify, ask and choose via your phone",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"telegram-mcp": "./src/cli.js"
|
|
8
|
+
},
|
|
9
|
+
"dependencies": {
|
|
10
|
+
"@modelcontextprotocol/sdk": "^1.0.0",
|
|
11
|
+
"node-telegram-bot-api": "^0.66.0",
|
|
12
|
+
"dotenv": "^16.0.0"
|
|
13
|
+
},
|
|
14
|
+
"engines": { "node": ">=18" },
|
|
15
|
+
"license": "MIT"
|
|
16
|
+
}
|
package/src/cli.js
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* @thxmxx/telegram-mcp
|
|
4
|
+
*
|
|
5
|
+
* npx @thxmxx/telegram-mcp init — one-time global setup
|
|
6
|
+
* npx @thxmxx/telegram-mcp start — start MCP server manually (for testing)
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
const cmd = process.argv[2];
|
|
10
|
+
|
|
11
|
+
if (cmd === "init") {
|
|
12
|
+
await import("./setup.js");
|
|
13
|
+
} else if (cmd === "start") {
|
|
14
|
+
await import("./index.js");
|
|
15
|
+
} else {
|
|
16
|
+
console.log(`
|
|
17
|
+
@thxmxx/telegram-mcp
|
|
18
|
+
|
|
19
|
+
npx @thxmxx/telegram-mcp init One-time setup — registers MCP in Claude Code globally
|
|
20
|
+
npx @thxmxx/telegram-mcp start Start MCP server manually (for testing)
|
|
21
|
+
|
|
22
|
+
After init, use in any Claude Code session:
|
|
23
|
+
/use-telegram Full mode (notify + ask + choose)
|
|
24
|
+
/use-telegram notify Notifications only
|
|
25
|
+
`);
|
|
26
|
+
process.exit(0);
|
|
27
|
+
}
|
package/src/index.js
ADDED
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* @thxmxx/telegram-mcp — MCP server
|
|
4
|
+
*
|
|
5
|
+
* Tools:
|
|
6
|
+
* telegram_notify — send a message (fire and forget)
|
|
7
|
+
* telegram_ask — ask a question, wait for text reply
|
|
8
|
+
* telegram_choose — show buttons, wait for a tap
|
|
9
|
+
*
|
|
10
|
+
* Instance label is auto-generated from cwd + ppid.
|
|
11
|
+
* No per-project configuration needed.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
15
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
16
|
+
import TelegramBot from "node-telegram-bot-api";
|
|
17
|
+
import { readFileSync, existsSync } from "node:fs";
|
|
18
|
+
import { join, dirname } from "node:path";
|
|
19
|
+
import { fileURLToPath } from "node:url";
|
|
20
|
+
import { z } from "zod";
|
|
21
|
+
import { createInterface } from "node:readline/promises";
|
|
22
|
+
import { stdin as input, stdout as output, env, exit } from "node:process";
|
|
23
|
+
import { randomBytes } from "node:crypto";
|
|
24
|
+
|
|
25
|
+
// ── Config ────────────────────────────────────────────────────────────────────
|
|
26
|
+
|
|
27
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
28
|
+
const envPath = join(__dirname, "../.env");
|
|
29
|
+
|
|
30
|
+
if (existsSync(envPath)) {
|
|
31
|
+
for (const line of readFileSync(envPath, "utf8").split("\n")) {
|
|
32
|
+
const [k, ...v] = line.split("=");
|
|
33
|
+
if (k && v.length && !env[k.trim()]) {
|
|
34
|
+
env[k.trim()] = v.join("=").trim();
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const TOKEN = env.TELEGRAM_BOT_TOKEN;
|
|
40
|
+
const CHAT_ID = env.TELEGRAM_CHAT_ID;
|
|
41
|
+
|
|
42
|
+
if (!TOKEN || !CHAT_ID) {
|
|
43
|
+
process.stderr.write(
|
|
44
|
+
"[telegram-mcp] Missing TELEGRAM_BOT_TOKEN or TELEGRAM_CHAT_ID.\n" +
|
|
45
|
+
" Run: npx @thxmxx/telegram-mcp init\n"
|
|
46
|
+
);
|
|
47
|
+
exit(1);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// ── Auto instance label: folder#shortid ───────────────────────────────────────
|
|
51
|
+
|
|
52
|
+
const folder = process.cwd().split("/").pop() || "claude";
|
|
53
|
+
const shortId = randomBytes(2).toString("hex"); // e.g. "a3f2"
|
|
54
|
+
const INSTANCE = `${folder}#${shortId}`;
|
|
55
|
+
const HDR = `\`[${INSTANCE}]\``;
|
|
56
|
+
|
|
57
|
+
// ── Telegram client ───────────────────────────────────────────────────────────
|
|
58
|
+
|
|
59
|
+
const bot = new TelegramBot(TOKEN, { polling: true });
|
|
60
|
+
|
|
61
|
+
function waitForReply(timeoutMs = 300_000) {
|
|
62
|
+
return new Promise((resolve, reject) => {
|
|
63
|
+
const timer = setTimeout(() => {
|
|
64
|
+
bot.removeListener("message", handler);
|
|
65
|
+
reject(new Error("Timed out (5 min)"));
|
|
66
|
+
}, timeoutMs);
|
|
67
|
+
|
|
68
|
+
function handler(msg) {
|
|
69
|
+
if (String(msg.chat.id) !== String(CHAT_ID)) return;
|
|
70
|
+
if (msg.text?.startsWith("/")) return;
|
|
71
|
+
clearTimeout(timer);
|
|
72
|
+
bot.removeListener("message", handler);
|
|
73
|
+
resolve({ source: "telegram", value: msg.text || "" });
|
|
74
|
+
}
|
|
75
|
+
bot.on("message", handler);
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function waitForCallback(timeoutMs = 300_000) {
|
|
80
|
+
return new Promise((resolve, reject) => {
|
|
81
|
+
const timer = setTimeout(() => {
|
|
82
|
+
bot.removeListener("callback_query", handler);
|
|
83
|
+
reject(new Error("Timed out (5 min)"));
|
|
84
|
+
}, timeoutMs);
|
|
85
|
+
|
|
86
|
+
function handler(query) {
|
|
87
|
+
if (String(query.from.id) !== String(CHAT_ID)) return;
|
|
88
|
+
clearTimeout(timer);
|
|
89
|
+
bot.removeListener("callback_query", handler);
|
|
90
|
+
bot.answerCallbackQuery(query.id);
|
|
91
|
+
resolve({ source: "telegram", value: query.data || "" });
|
|
92
|
+
}
|
|
93
|
+
bot.on("callback_query", handler);
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
function terminalPrompt(question) {
|
|
98
|
+
return new Promise((resolve) => {
|
|
99
|
+
const rl = createInterface({ input, output, terminal: true });
|
|
100
|
+
process.stderr.write(`\n[${INSTANCE}] ${question}\n> `);
|
|
101
|
+
rl.once("line", (line) => { rl.close(); resolve(line.trim()); });
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
function raceReply(question) {
|
|
106
|
+
return Promise.race([
|
|
107
|
+
waitForReply(),
|
|
108
|
+
terminalPrompt(question).then((v) => ({ source: "terminal", value: v })),
|
|
109
|
+
]);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
function raceCallback(question, options) {
|
|
113
|
+
const numbered = options.map((o, i) => ` ${i + 1}. ${o}`).join("\n");
|
|
114
|
+
return Promise.race([
|
|
115
|
+
waitForCallback(),
|
|
116
|
+
terminalPrompt(`${question}\n${numbered}\nChoose (1-${options.length})`).then((v) => {
|
|
117
|
+
const idx = parseInt(v, 10) - 1;
|
|
118
|
+
return { source: "terminal", value: options[idx] ?? v };
|
|
119
|
+
}),
|
|
120
|
+
]);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// ── MCP server ────────────────────────────────────────────────────────────────
|
|
124
|
+
|
|
125
|
+
const server = new McpServer({ name: "telegram-mcp", version: "1.0.0" });
|
|
126
|
+
|
|
127
|
+
server.tool(
|
|
128
|
+
"telegram_notify",
|
|
129
|
+
"Send a Telegram notification to the user. Use for progress updates and task completions. Does NOT wait for a reply.",
|
|
130
|
+
{ message: z.string().describe("The message to send") },
|
|
131
|
+
async ({ message }) => {
|
|
132
|
+
await bot.sendMessage(CHAT_ID, `${HDR} ${message}`, { parse_mode: "Markdown" });
|
|
133
|
+
process.stderr.write(`[${INSTANCE}] notify: ${message}\n`);
|
|
134
|
+
return { content: [{ type: "text", text: "Sent." }] };
|
|
135
|
+
}
|
|
136
|
+
);
|
|
137
|
+
|
|
138
|
+
server.tool(
|
|
139
|
+
"telegram_ask",
|
|
140
|
+
"Ask the user a free-form question via Telegram and wait for their reply. The same question is shown on the terminal — whoever answers first wins.",
|
|
141
|
+
{ question: z.string().describe("The question to ask") },
|
|
142
|
+
async ({ question }) => {
|
|
143
|
+
await bot.sendMessage(CHAT_ID, `${HDR} ❓ ${question}`, { parse_mode: "Markdown" });
|
|
144
|
+
const { source, value } = await raceReply(question);
|
|
145
|
+
if (source === "terminal") {
|
|
146
|
+
await bot.sendMessage(CHAT_ID, `${HDR} ✅ Answered from terminal: *${value}*`, { parse_mode: "Markdown" });
|
|
147
|
+
}
|
|
148
|
+
process.stderr.write(`[${INSTANCE}] ask (${source}): ${value}\n`);
|
|
149
|
+
return { content: [{ type: "text", text: value }] };
|
|
150
|
+
}
|
|
151
|
+
);
|
|
152
|
+
|
|
153
|
+
server.tool(
|
|
154
|
+
"telegram_choose",
|
|
155
|
+
"Ask the user to pick one option. Shows inline buttons on Telegram and a numbered list on the terminal. Whoever responds first wins.",
|
|
156
|
+
{
|
|
157
|
+
question: z.string().describe("The question or prompt"),
|
|
158
|
+
options: z.array(z.string()).min(2).max(10).describe("Options to present (2–10)"),
|
|
159
|
+
},
|
|
160
|
+
async ({ question, options }) => {
|
|
161
|
+
const keyboard = {
|
|
162
|
+
inline_keyboard: options.map((opt) => [{ text: opt, callback_data: opt }]),
|
|
163
|
+
};
|
|
164
|
+
await bot.sendMessage(CHAT_ID, `${HDR} 🔘 ${question}`, {
|
|
165
|
+
parse_mode: "Markdown",
|
|
166
|
+
reply_markup: keyboard,
|
|
167
|
+
});
|
|
168
|
+
const { source, value } = await raceCallback(question, options);
|
|
169
|
+
await bot.sendMessage(CHAT_ID, `${HDR} ✅ *${value}* _(via ${source})_`, { parse_mode: "Markdown" });
|
|
170
|
+
process.stderr.write(`[${INSTANCE}] choose (${source}): ${value}\n`);
|
|
171
|
+
return { content: [{ type: "text", text: value }] };
|
|
172
|
+
}
|
|
173
|
+
);
|
|
174
|
+
|
|
175
|
+
// ── Start ─────────────────────────────────────────────────────────────────────
|
|
176
|
+
|
|
177
|
+
const transport = new StdioServerTransport();
|
|
178
|
+
await server.connect(transport);
|
|
179
|
+
process.stderr.write(`[telegram-mcp] Ready — instance: ${INSTANCE}\n`);
|
package/src/setup.js
ADDED
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* npx @thxmxx/telegram-mcp init
|
|
4
|
+
*
|
|
5
|
+
* One-time global setup:
|
|
6
|
+
* 1. Asks for bot token and chat ID
|
|
7
|
+
* 2. Registers the MCP server globally in Claude Code (~/.claude.json)
|
|
8
|
+
* 3. Installs the /use-telegram slash command in ~/.claude/commands/
|
|
9
|
+
*
|
|
10
|
+
* Instance names are auto-generated per session — no need to configure them.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import { createInterface } from "node:readline/promises";
|
|
14
|
+
import { stdin as input, stdout as output, exit } from "node:process";
|
|
15
|
+
import { execFileSync } from "node:child_process";
|
|
16
|
+
import { mkdirSync, writeFileSync, existsSync } from "node:fs";
|
|
17
|
+
import { join } from "node:path";
|
|
18
|
+
import { homedir } from "node:os";
|
|
19
|
+
|
|
20
|
+
const rl = createInterface({ input, output });
|
|
21
|
+
const ask = (q) => rl.question(q);
|
|
22
|
+
|
|
23
|
+
const BOLD = "\x1b[1m";
|
|
24
|
+
const GREEN = "\x1b[32m";
|
|
25
|
+
const CYAN = "\x1b[36m";
|
|
26
|
+
const DIM = "\x1b[2m";
|
|
27
|
+
const RESET = "\x1b[0m";
|
|
28
|
+
|
|
29
|
+
const ok = (msg) => console.log(`${GREEN}✔${RESET} ${msg}`);
|
|
30
|
+
const die = (msg) => { console.error(`\x1b[31m✘${RESET} ${msg}`); rl.close(); exit(1); };
|
|
31
|
+
|
|
32
|
+
// ── Pre-flight ────────────────────────────────────────────────────────────────
|
|
33
|
+
|
|
34
|
+
console.log(`\n${BOLD}@thxmxx/telegram-mcp — one-time setup${RESET}\n`);
|
|
35
|
+
|
|
36
|
+
const [major] = process.versions.node.split(".").map(Number);
|
|
37
|
+
if (major < 18) die(`Node.js 18+ required (you have ${process.versions.node})`);
|
|
38
|
+
ok(`Node.js ${process.versions.node}`);
|
|
39
|
+
|
|
40
|
+
try {
|
|
41
|
+
const ver = execFileSync("claude", ["--version"], { encoding: "utf8" }).trim();
|
|
42
|
+
ok(`Claude Code: ${ver}`);
|
|
43
|
+
} catch {
|
|
44
|
+
die("`claude` not found. Install: https://docs.claude.ai/claude-code");
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// ── Step 1: Bot token ─────────────────────────────────────────────────────────
|
|
48
|
+
|
|
49
|
+
console.log(`
|
|
50
|
+
${BOLD}Step 1 — Bot token${RESET}
|
|
51
|
+
${DIM}Open Telegram → @BotFather → /newbot${RESET}
|
|
52
|
+
`);
|
|
53
|
+
const token = (await ask(" Bot token: ")).trim();
|
|
54
|
+
if (!token) die("Token is required.");
|
|
55
|
+
|
|
56
|
+
// ── Step 2: Chat ID ───────────────────────────────────────────────────────────
|
|
57
|
+
|
|
58
|
+
console.log(`
|
|
59
|
+
${BOLD}Step 2 — Your Telegram user ID${RESET}
|
|
60
|
+
${DIM}Message @userinfobot on Telegram to get your numeric ID.${RESET}
|
|
61
|
+
`);
|
|
62
|
+
const chatId = (await ask(" Your Telegram user ID: ")).trim();
|
|
63
|
+
if (!chatId) die("User ID is required.");
|
|
64
|
+
|
|
65
|
+
rl.close();
|
|
66
|
+
|
|
67
|
+
// ── Register MCP globally in Claude Code ─────────────────────────────────────
|
|
68
|
+
|
|
69
|
+
console.log(`\n${BOLD}Registering MCP server globally…${RESET}`);
|
|
70
|
+
|
|
71
|
+
const serverPath = new URL("./index.js", import.meta.url).pathname;
|
|
72
|
+
|
|
73
|
+
try {
|
|
74
|
+
execFileSync("claude", [
|
|
75
|
+
"mcp", "add", "--scope", "user", // global, persists across all projects
|
|
76
|
+
"telegram-mcp",
|
|
77
|
+
"-e", `TELEGRAM_BOT_TOKEN=${token}`,
|
|
78
|
+
"-e", `TELEGRAM_CHAT_ID=${chatId}`,
|
|
79
|
+
"--", "node", serverPath,
|
|
80
|
+
], { stdio: "inherit" });
|
|
81
|
+
} catch {
|
|
82
|
+
// Fallback: older Claude Code versions without --scope flag
|
|
83
|
+
try {
|
|
84
|
+
execFileSync("claude", [
|
|
85
|
+
"mcp", "add",
|
|
86
|
+
"telegram-mcp",
|
|
87
|
+
"-e", `TELEGRAM_BOT_TOKEN=${token}`,
|
|
88
|
+
"-e", `TELEGRAM_CHAT_ID=${chatId}`,
|
|
89
|
+
"--", "node", serverPath,
|
|
90
|
+
], { stdio: "inherit" });
|
|
91
|
+
} catch {
|
|
92
|
+
die(
|
|
93
|
+
"`claude mcp add` failed.\n" +
|
|
94
|
+
" Update Claude Code: https://docs.claude.ai/claude-code\n\n" +
|
|
95
|
+
" Manual config:\n" +
|
|
96
|
+
` TELEGRAM_BOT_TOKEN=${token}\n` +
|
|
97
|
+
` TELEGRAM_CHAT_ID=${chatId}\n` +
|
|
98
|
+
` command: node ${serverPath}`
|
|
99
|
+
);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
ok("MCP server registered globally.");
|
|
104
|
+
|
|
105
|
+
// ── Install /use-telegram slash command ───────────────────────────────────────
|
|
106
|
+
|
|
107
|
+
const commandsDir = join(homedir(), ".claude", "commands");
|
|
108
|
+
mkdirSync(commandsDir, { recursive: true });
|
|
109
|
+
|
|
110
|
+
const commandPath = join(commandsDir, "use-telegram.md");
|
|
111
|
+
writeFileSync(commandPath, `# use-telegram
|
|
112
|
+
|
|
113
|
+
Activate Telegram integration for this Claude Code session.
|
|
114
|
+
|
|
115
|
+
## What this does
|
|
116
|
+
|
|
117
|
+
Enables the telegram_notify, telegram_ask and telegram_choose tools so you
|
|
118
|
+
can be notified and asked questions on your phone while Claude works.
|
|
119
|
+
|
|
120
|
+
The instance name is derived automatically from the current folder and
|
|
121
|
+
session ID, so messages on Telegram always show which session is talking.
|
|
122
|
+
|
|
123
|
+
## Modes
|
|
124
|
+
|
|
125
|
+
- \`/use-telegram\` — full interactive mode (notify + ask + choose)
|
|
126
|
+
- \`/use-telegram notify\` — notifications only, no questions
|
|
127
|
+
|
|
128
|
+
## Behaviour
|
|
129
|
+
|
|
130
|
+
When this command is active:
|
|
131
|
+
- Use telegram_notify for progress updates and completed tasks
|
|
132
|
+
- Use telegram_ask when you need a free-form answer from the user
|
|
133
|
+
- Use telegram_choose when the user must pick from known options
|
|
134
|
+
- Always prefix messages with the auto-generated instance label
|
|
135
|
+
- Mirror every question to the terminal too — whoever answers first wins
|
|
136
|
+
- After any long-running task, send a completion summary via telegram_notify
|
|
137
|
+
|
|
138
|
+
## Instance naming
|
|
139
|
+
|
|
140
|
+
Generate the instance label as: \`{folder-name}#{short-session-id}\`
|
|
141
|
+
Example: \`backend#a3f2\`, \`frontend#9c11\`
|
|
142
|
+
|
|
143
|
+
Use the same label for the entire session.
|
|
144
|
+
`);
|
|
145
|
+
|
|
146
|
+
ok(`Slash command installed → ${commandPath}`);
|
|
147
|
+
|
|
148
|
+
// ── Done ──────────────────────────────────────────────────────────────────────
|
|
149
|
+
|
|
150
|
+
console.log(`
|
|
151
|
+
${GREEN}${BOLD}All done!${RESET}
|
|
152
|
+
|
|
153
|
+
Restart Claude Code. From now on, in any session:
|
|
154
|
+
|
|
155
|
+
${BOLD}/use-telegram${RESET} full mode (notify + ask + choose)
|
|
156
|
+
${BOLD}/use-telegram notify${RESET} notifications only
|
|
157
|
+
|
|
158
|
+
Or just tell Claude in the prompt:
|
|
159
|
+
${DIM}"refactor this and notify me on telegram when done"${RESET}
|
|
160
|
+
${DIM}"deploy to staging, ask me via telegram if anything is unclear"${RESET}
|
|
161
|
+
|
|
162
|
+
Each session auto-identifies itself as ${BOLD}[folder#id]${RESET} in Telegram.
|
|
163
|
+
${DIM}No need to run init again unless you change your bot token.${RESET}
|
|
164
|
+
`);
|