opencode-telegram-bot 1.0.6 â 1.0.8
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 +17 -7
- package/dist/app.d.ts +4 -0
- package/dist/index.js +228 -18
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -2,6 +2,10 @@
|
|
|
2
2
|
|
|
3
3
|
A Telegram bot that forwards messages to an [OpenCode](https://opencode.ai) agent and returns the responses. Each chat gets a persistent session, so the agent remembers conversation context across messages.
|
|
4
4
|
|
|
5
|
+
## đĨ Demo
|
|
6
|
+
|
|
7
|
+
https://github.com/user-attachments/assets/e071f536-7036-4e9c-a5fb-c77414626825
|
|
8
|
+
|
|
5
9
|
## Prerequisites
|
|
6
10
|
|
|
7
11
|
- [Node.js](https://nodejs.org/) 18+
|
|
@@ -111,12 +115,16 @@ These are handled directly by the Telegram bot:
|
|
|
111
115
|
| `/usage` | Show token and cost usage for this session |
|
|
112
116
|
| `/help` | Show available commands |
|
|
113
117
|
|
|
118
|
+
The bot also registers these commands in Telegram's command menu (the `/` button), plus any OpenCode server commands discovered at startup (excluding hidden ones like `/init` and `/review`).
|
|
119
|
+
|
|
114
120
|
### Verbose Mode
|
|
115
121
|
|
|
116
122
|
By default, the bot only shows the assistant's final text response. Use `/verbose` to toggle verbose mode (or `/verbose on|off` to set it explicitly), which also displays:
|
|
117
123
|
|
|
118
124
|
- **Thinking/reasoning** -- shown as plain text with a đ§ prefix, truncated to 500 characters
|
|
119
|
-
- **Tool calls** -- shown as a compact one-line summary (e.g.
|
|
125
|
+
- **Tool calls** -- shown as a compact one-line summary (e.g. `âī¸ read -- src/app.ts`)
|
|
126
|
+
- **Delegations** -- shown when work is delegated to a subagent (e.g. `đ§Š Delegated: Find relevant docs`)
|
|
127
|
+
- **Subagent details** -- reasoning/tool calls from subagents when available, plus a short `âšī¸ Subagent responded` notice
|
|
120
128
|
|
|
121
129
|
Verbose mode is per-chat and persists across bot restarts. Use `/verbose` again to turn it off.
|
|
122
130
|
|
|
@@ -128,6 +136,11 @@ Example with verbose mode on:
|
|
|
128
136
|
âī¸ grep -- pattern: "authenticate" in src/
|
|
129
137
|
âī¸ read -- src/auth/handler.ts
|
|
130
138
|
|
|
139
|
+
đ§Š Delegated: Search auth docs
|
|
140
|
+
đ§ Thinking (agent: subagent): Scanning docs for auth references...
|
|
141
|
+
âī¸ read -- README.md (agent: subagent)
|
|
142
|
+
âšī¸ Subagent responded
|
|
143
|
+
|
|
131
144
|
Here's what I found in the auth module...
|
|
132
145
|
```
|
|
133
146
|
|
|
@@ -217,17 +230,14 @@ Bot: Your sessions:
|
|
|
217
230
|
Current session: Fix the broken tests
|
|
218
231
|
Tap a session to switch or delete.
|
|
219
232
|
|
|
220
|
-
You: /switch def6
|
|
221
|
-
Bot: Switched to session: Auth refactoring (def67890)
|
|
222
|
-
|
|
223
233
|
You: What were we working on?
|
|
224
234
|
Bot: [agent responds with context from the auth refactoring session]
|
|
225
235
|
|
|
226
|
-
You:
|
|
227
|
-
Bot:
|
|
236
|
+
You: [tap "Auth refactoring"]
|
|
237
|
+
Bot: Switched to session: Auth refactoring
|
|
228
238
|
```
|
|
229
239
|
|
|
230
|
-
The `/sessions` list only shows sessions created by the Telegram bot, not sessions from other OpenCode clients (like the TUI). You cannot delete the currently active session -- use `/new`
|
|
240
|
+
The `/sessions` list only shows sessions created by the Telegram bot, not sessions from other OpenCode clients (like the TUI). You cannot delete the currently active session -- use `/new` first.
|
|
231
241
|
|
|
232
242
|
## Session Persistence
|
|
233
243
|
|
package/dist/app.d.ts
CHANGED
|
@@ -49,6 +49,10 @@ interface TelegramBot {
|
|
|
49
49
|
getFileLink: (fileId: string) => Promise<{
|
|
50
50
|
toString(): string;
|
|
51
51
|
}>;
|
|
52
|
+
setMyCommands: (commands: Array<{
|
|
53
|
+
command: string;
|
|
54
|
+
description: string;
|
|
55
|
+
}>) => Promise<unknown>;
|
|
52
56
|
};
|
|
53
57
|
}
|
|
54
58
|
export declare function startTelegram(options: StartOptions): Promise<TelegramBot>;
|
package/dist/index.js
CHANGED
|
@@ -18589,6 +18589,9 @@ async function startTelegram(options) {
|
|
|
18589
18589
|
const client = options.client || client_createOpencodeClient({ baseUrl: url });
|
|
18590
18590
|
// Verify connection to the OpenCode server and fetch available commands
|
|
18591
18591
|
const opencodeCommands = new Set();
|
|
18592
|
+
const opencodeCommandMenu = [];
|
|
18593
|
+
const hiddenOpenCodeCommands = new Set(["init", "review", "reviews"]);
|
|
18594
|
+
const isHiddenOpenCodeCommand = (name) => hiddenOpenCodeCommands.has(name.toLowerCase());
|
|
18592
18595
|
let projectDirectory = "";
|
|
18593
18596
|
try {
|
|
18594
18597
|
const sessions = await client.session.list();
|
|
@@ -18606,7 +18609,16 @@ async function startTelegram(options) {
|
|
|
18606
18609
|
const cmds = await client.command.list();
|
|
18607
18610
|
if (cmds.data) {
|
|
18608
18611
|
for (const cmd of cmds.data) {
|
|
18609
|
-
|
|
18612
|
+
const name = cmd.name;
|
|
18613
|
+
if (!name)
|
|
18614
|
+
continue;
|
|
18615
|
+
opencodeCommands.add(name);
|
|
18616
|
+
if (!isHiddenOpenCodeCommand(name)) {
|
|
18617
|
+
opencodeCommandMenu.push({
|
|
18618
|
+
command: name,
|
|
18619
|
+
description: cmd.description || "OpenCode command",
|
|
18620
|
+
});
|
|
18621
|
+
}
|
|
18610
18622
|
}
|
|
18611
18623
|
console.log(`[Telegram] Available OpenCode commands: ${[...opencodeCommands].join(", ")}`);
|
|
18612
18624
|
}
|
|
@@ -18618,6 +18630,17 @@ async function startTelegram(options) {
|
|
|
18618
18630
|
const telegramCommands = new Set([
|
|
18619
18631
|
"start", "help", "new", "sessions", "switch", "title", "delete", "export", "verbose", "model", "usage",
|
|
18620
18632
|
]);
|
|
18633
|
+
const telegramCommandMenu = [
|
|
18634
|
+
{ command: "new", description: "Start a new conversation" },
|
|
18635
|
+
{ command: "sessions", description: "List your sessions" },
|
|
18636
|
+
{ command: "title", description: "Rename a session (/title <text>)" },
|
|
18637
|
+
{ command: "export", description: "Export session (/export full for details)" },
|
|
18638
|
+
{ command: "verbose", description: "Toggle verbose mode" },
|
|
18639
|
+
{ command: "model", description: "Search models (/model <keyword>)" },
|
|
18640
|
+
{ command: "usage", description: "Show token and cost usage" },
|
|
18641
|
+
{ command: "help", description: "Show available commands" },
|
|
18642
|
+
];
|
|
18643
|
+
const getVisibleOpenCodeCommands = () => [...opencodeCommands].filter((command) => !isHiddenOpenCodeCommand(command));
|
|
18621
18644
|
// Map of chatId -> sessionId for the active session per chat
|
|
18622
18645
|
const chatSessions = new Map();
|
|
18623
18646
|
// Set of all session IDs ever created/used by this bot (for filtering)
|
|
@@ -18951,6 +18974,63 @@ async function startTelegram(options) {
|
|
|
18951
18974
|
let processingMsgDeleted = false;
|
|
18952
18975
|
const sentToolIds = new Set();
|
|
18953
18976
|
const sentReasoningIds = new Set();
|
|
18977
|
+
const sentSubtaskIds = new Set();
|
|
18978
|
+
let sawDelegation = false;
|
|
18979
|
+
let sentSubagentDetails = false;
|
|
18980
|
+
let sentSubagentResponseNotice = false;
|
|
18981
|
+
function extractTaskId(outputText) {
|
|
18982
|
+
const match = outputText.match(/task[_-]?id:\s*([\w-]+)/i);
|
|
18983
|
+
return match ? match[1] : null;
|
|
18984
|
+
}
|
|
18985
|
+
async function emitSubagentMessages(taskSessionId) {
|
|
18986
|
+
try {
|
|
18987
|
+
const messagesResult = await client.session.messages({
|
|
18988
|
+
path: { id: taskSessionId },
|
|
18989
|
+
});
|
|
18990
|
+
if (messagesResult.error || !messagesResult.data) {
|
|
18991
|
+
console.warn(`[Telegram] Failed to fetch subagent messages for ${taskSessionId}: ${JSON.stringify(messagesResult.error)}`);
|
|
18992
|
+
return;
|
|
18993
|
+
}
|
|
18994
|
+
for (const msg of messagesResult.data) {
|
|
18995
|
+
const info = msg.info;
|
|
18996
|
+
if (info?.role !== "assistant")
|
|
18997
|
+
continue;
|
|
18998
|
+
const parts = msg.parts || [];
|
|
18999
|
+
for (const part of parts) {
|
|
19000
|
+
if (part.type === "reasoning") {
|
|
19001
|
+
const text = part.text;
|
|
19002
|
+
if (!text)
|
|
19003
|
+
continue;
|
|
19004
|
+
const truncated = truncate(text.replace(/\n/g, " "), 500);
|
|
19005
|
+
await sendTelegramMessage(chatId, `\u{1F9E0} Thinking (agent: subagent): ${truncated}`, true);
|
|
19006
|
+
sentSubagentDetails = true;
|
|
19007
|
+
}
|
|
19008
|
+
if (part.type === "tool") {
|
|
19009
|
+
const tool = part.tool;
|
|
19010
|
+
const state = part.state;
|
|
19011
|
+
if (!state)
|
|
19012
|
+
continue;
|
|
19013
|
+
if (state.status !== "completed" && state.status !== "error")
|
|
19014
|
+
continue;
|
|
19015
|
+
const summary = summarizeTool(tool, state.input);
|
|
19016
|
+
let line = `\u{2699}\u{FE0F} ${summary} (agent: subagent)`;
|
|
19017
|
+
if (state.status === "error") {
|
|
19018
|
+
line += ` \u{274C}`;
|
|
19019
|
+
}
|
|
19020
|
+
await sendTelegramMessage(chatId, line, true);
|
|
19021
|
+
sentSubagentDetails = true;
|
|
19022
|
+
}
|
|
19023
|
+
}
|
|
19024
|
+
}
|
|
19025
|
+
if (!sentSubagentResponseNotice) {
|
|
19026
|
+
await sendTelegramMessage(chatId, "âšī¸ Subagent responded", true);
|
|
19027
|
+
sentSubagentResponseNotice = true;
|
|
19028
|
+
}
|
|
19029
|
+
}
|
|
19030
|
+
catch (err) {
|
|
19031
|
+
console.warn("[Telegram] Error fetching subagent messages:", err);
|
|
19032
|
+
}
|
|
19033
|
+
}
|
|
18954
19034
|
// Buffer the latest reasoning text -- we send it when a non-reasoning part arrives
|
|
18955
19035
|
// so we get the complete thinking block rather than a partial one
|
|
18956
19036
|
let pendingReasoning = null;
|
|
@@ -18976,7 +19056,10 @@ async function startTelegram(options) {
|
|
|
18976
19056
|
sentReasoningIds.add(pendingReasoning.id);
|
|
18977
19057
|
await deleteProcessingMsg();
|
|
18978
19058
|
const truncated = truncate(pendingReasoning.text.replace(/\n/g, " "), 500);
|
|
18979
|
-
|
|
19059
|
+
const agentLabel = pendingReasoning.agent
|
|
19060
|
+
? ` (agent: ${pendingReasoning.agent})`
|
|
19061
|
+
: "";
|
|
19062
|
+
await sendTelegramMessage(chatId, `\u{1F9E0} Thinking${agentLabel}: ${truncated}`, true);
|
|
18980
19063
|
}
|
|
18981
19064
|
pendingReasoning = null;
|
|
18982
19065
|
}
|
|
@@ -18985,14 +19068,26 @@ async function startTelegram(options) {
|
|
|
18985
19068
|
const ev = event;
|
|
18986
19069
|
if (ev.type === "message.part.updated" && ev.properties) {
|
|
18987
19070
|
const part = ev.properties.part;
|
|
18988
|
-
|
|
18989
|
-
|
|
19071
|
+
const partSessionId = part.sessionID;
|
|
19072
|
+
const parentSessionId = part.parentSessionID ||
|
|
19073
|
+
part.parentSessionId;
|
|
19074
|
+
const rootSessionId = part.rootSessionID ||
|
|
19075
|
+
part.rootSessionId;
|
|
19076
|
+
const isPrimarySession = partSessionId === sessionId;
|
|
19077
|
+
const isChildSession = parentSessionId === sessionId || rootSessionId === sessionId;
|
|
19078
|
+
// Only process events for our session (or subagent sessions that link back)
|
|
19079
|
+
if (!isPrimarySession && !isChildSession)
|
|
18990
19080
|
continue;
|
|
19081
|
+
if (!isPrimarySession && isChildSession && verbose) {
|
|
19082
|
+
// Subagent events are allowed to stream in verbose mode
|
|
19083
|
+
}
|
|
18991
19084
|
const partText = part.text;
|
|
18992
19085
|
if (part.type === "reasoning" && part.id) {
|
|
18993
19086
|
// Buffer reasoning -- keep updating with the latest full text
|
|
18994
19087
|
if (partText) {
|
|
18995
|
-
|
|
19088
|
+
const agent = part.agent ||
|
|
19089
|
+
(!isPrimarySession ? "subagent" : undefined);
|
|
19090
|
+
pendingReasoning = { id: part.id, text: partText, agent };
|
|
18996
19091
|
}
|
|
18997
19092
|
}
|
|
18998
19093
|
else if (part.type === "tool" && part.id) {
|
|
@@ -19007,12 +19102,74 @@ async function startTelegram(options) {
|
|
|
19007
19102
|
sentToolIds.add(part.id);
|
|
19008
19103
|
await deleteProcessingMsg();
|
|
19009
19104
|
const tool = part.tool;
|
|
19010
|
-
const
|
|
19011
|
-
|
|
19012
|
-
if (
|
|
19013
|
-
|
|
19105
|
+
const agent = part.agent ||
|
|
19106
|
+
(!isPrimarySession ? "subagent" : undefined);
|
|
19107
|
+
if (tool === "task") {
|
|
19108
|
+
const input = state.input;
|
|
19109
|
+
const description = input?.description || input?.prompt || summarizeTool(tool, state.input);
|
|
19110
|
+
let line = `\u{1F9E9} Delegated`;
|
|
19111
|
+
if (description) {
|
|
19112
|
+
line += `: ${truncate(description, 120)}`;
|
|
19113
|
+
}
|
|
19114
|
+
if (agent) {
|
|
19115
|
+
line += ` (agent: ${agent})`;
|
|
19116
|
+
}
|
|
19117
|
+
if (state.status === "error") {
|
|
19118
|
+
line += ` \u{274C}`;
|
|
19119
|
+
}
|
|
19120
|
+
await sendTelegramMessage(chatId, line, true);
|
|
19121
|
+
sawDelegation = true;
|
|
19122
|
+
if (state.output && verbose) {
|
|
19123
|
+
const rawOutput = typeof state.output === "string"
|
|
19124
|
+
? state.output
|
|
19125
|
+
: JSON.stringify(state.output, null, 2);
|
|
19126
|
+
const outputText = rawOutput.trim();
|
|
19127
|
+
if (outputText) {
|
|
19128
|
+
const taskSessionId = extractTaskId(outputText);
|
|
19129
|
+
if (taskSessionId) {
|
|
19130
|
+
await emitSubagentMessages(taskSessionId);
|
|
19131
|
+
}
|
|
19132
|
+
if (!sentSubagentDetails && !sentSubagentResponseNotice) {
|
|
19133
|
+
await sendTelegramMessage(chatId, "âšī¸ Subagent responded (no thought/tool details available)", true);
|
|
19134
|
+
sentSubagentResponseNotice = true;
|
|
19135
|
+
}
|
|
19136
|
+
}
|
|
19137
|
+
}
|
|
19138
|
+
}
|
|
19139
|
+
else {
|
|
19140
|
+
const summary = summarizeTool(tool, state.input);
|
|
19141
|
+
let line = `\u{2699}\u{FE0F} ${summary}`;
|
|
19142
|
+
if (agent) {
|
|
19143
|
+
line += ` (agent: ${agent})`;
|
|
19144
|
+
}
|
|
19145
|
+
if (state.status === "error") {
|
|
19146
|
+
line += ` \u{274C}`;
|
|
19147
|
+
}
|
|
19148
|
+
await sendTelegramMessage(chatId, line, true);
|
|
19149
|
+
}
|
|
19150
|
+
}
|
|
19151
|
+
}
|
|
19152
|
+
}
|
|
19153
|
+
else if (part.type === "subtask") {
|
|
19154
|
+
await flushReasoning();
|
|
19155
|
+
if (verbose) {
|
|
19156
|
+
const id = part.id;
|
|
19157
|
+
if (!id || !sentSubtaskIds.has(id)) {
|
|
19158
|
+
if (id)
|
|
19159
|
+
sentSubtaskIds.add(id);
|
|
19160
|
+
await deleteProcessingMsg();
|
|
19161
|
+
const description = part.description;
|
|
19162
|
+
const agent = part.agent ||
|
|
19163
|
+
(!isPrimarySession ? "subagent" : undefined);
|
|
19164
|
+
let line = "\u{1F9E9} Delegated";
|
|
19165
|
+
if (description) {
|
|
19166
|
+
line += `: ${truncate(description, 120)}`;
|
|
19167
|
+
}
|
|
19168
|
+
if (agent) {
|
|
19169
|
+
line += ` (agent: ${agent})`;
|
|
19014
19170
|
}
|
|
19015
19171
|
await sendTelegramMessage(chatId, line, true);
|
|
19172
|
+
sawDelegation = true;
|
|
19016
19173
|
}
|
|
19017
19174
|
}
|
|
19018
19175
|
}
|
|
@@ -19053,6 +19210,35 @@ async function startTelegram(options) {
|
|
|
19053
19210
|
if (!finalText) {
|
|
19054
19211
|
finalText = "The agent returned an empty response.";
|
|
19055
19212
|
}
|
|
19213
|
+
if (verbose && sawDelegation) {
|
|
19214
|
+
const lines = finalText.split("\n");
|
|
19215
|
+
let inTaskResult = false;
|
|
19216
|
+
const filtered = lines.filter((line) => {
|
|
19217
|
+
const trimmed = line.trim();
|
|
19218
|
+
if (/^<task_result>$/i.test(trimmed)) {
|
|
19219
|
+
inTaskResult = true;
|
|
19220
|
+
return false;
|
|
19221
|
+
}
|
|
19222
|
+
if (/^<\/task_result>$/i.test(trimmed)) {
|
|
19223
|
+
inTaskResult = false;
|
|
19224
|
+
return false;
|
|
19225
|
+
}
|
|
19226
|
+
if (inTaskResult)
|
|
19227
|
+
return false;
|
|
19228
|
+
if (/^Subagent response:/i.test(trimmed))
|
|
19229
|
+
return false;
|
|
19230
|
+
if (/^Subagent (ran|returned|reported)/i.test(trimmed))
|
|
19231
|
+
return false;
|
|
19232
|
+
if (/^Ran the subagent/i.test(trimmed))
|
|
19233
|
+
return false;
|
|
19234
|
+
return true;
|
|
19235
|
+
});
|
|
19236
|
+
const cleaned = filtered.join("\n").trim();
|
|
19237
|
+
if (!cleaned) {
|
|
19238
|
+
return;
|
|
19239
|
+
}
|
|
19240
|
+
finalText = cleaned;
|
|
19241
|
+
}
|
|
19056
19242
|
const chunks = splitMessage(finalText, 4096);
|
|
19057
19243
|
for (const chunk of chunks) {
|
|
19058
19244
|
await sendTelegramMessage(chatId, chunk);
|
|
@@ -19119,6 +19305,25 @@ async function startTelegram(options) {
|
|
|
19119
19305
|
const bot = options.botFactory
|
|
19120
19306
|
? options.botFactory(token)
|
|
19121
19307
|
: new lib.Telegraf(token);
|
|
19308
|
+
async function registerCommandMenu() {
|
|
19309
|
+
const combined = [...telegramCommandMenu, ...opencodeCommandMenu];
|
|
19310
|
+
const seen = new Set();
|
|
19311
|
+
const commands = [];
|
|
19312
|
+
for (const entry of combined) {
|
|
19313
|
+
if (!entry.command || seen.has(entry.command))
|
|
19314
|
+
continue;
|
|
19315
|
+
seen.add(entry.command);
|
|
19316
|
+
commands.push(entry);
|
|
19317
|
+
}
|
|
19318
|
+
if (commands.length === 0)
|
|
19319
|
+
return;
|
|
19320
|
+
try {
|
|
19321
|
+
await bot.telegram.setMyCommands(commands);
|
|
19322
|
+
}
|
|
19323
|
+
catch (err) {
|
|
19324
|
+
console.warn("[Telegram] Failed to register command menu:", err);
|
|
19325
|
+
}
|
|
19326
|
+
}
|
|
19122
19327
|
// Middleware to check if the user is authorized
|
|
19123
19328
|
bot.use((ctx, next) => {
|
|
19124
19329
|
if (!authorizedUserId) {
|
|
@@ -19134,24 +19339,26 @@ async function startTelegram(options) {
|
|
|
19134
19339
|
return;
|
|
19135
19340
|
}
|
|
19136
19341
|
});
|
|
19342
|
+
await registerCommandMenu();
|
|
19137
19343
|
// Handle /start command
|
|
19138
19344
|
bot.start((ctx) => {
|
|
19139
19345
|
let msg = "Hello! I'm your OpenCode bot. Send me a message and I'll forward it to the OpenCode agent.\n\n" +
|
|
19140
19346
|
"Bot commands:\n" +
|
|
19141
19347
|
"/new - Start a new conversation\n" +
|
|
19142
|
-
"/sessions - List your sessions\n" +
|
|
19348
|
+
"/sessions - List your sessions (buttons)\n" +
|
|
19143
19349
|
"/title <text> - Rename the current session\n" +
|
|
19144
19350
|
"/export - Export the current session as a markdown file\n" +
|
|
19145
19351
|
"/export full - Export with all details (thinking, costs, steps)\n" +
|
|
19146
19352
|
"/verbose - Toggle verbose mode (show thinking and tool calls)\n" +
|
|
19147
19353
|
"/verbose on|off - Set verbose mode explicitly\n" +
|
|
19148
|
-
"/model -
|
|
19354
|
+
"/model <keyword> - Search available models\n" +
|
|
19149
19355
|
"/usage - Show token and cost usage for this session\n" +
|
|
19150
19356
|
"/help - Show this help message\n";
|
|
19151
|
-
|
|
19357
|
+
const visibleOpenCodeCommands = getVisibleOpenCodeCommands();
|
|
19358
|
+
if (visibleOpenCodeCommands.length > 0) {
|
|
19152
19359
|
msg +=
|
|
19153
19360
|
"\nOpenCode commands are also available:\n" +
|
|
19154
|
-
|
|
19361
|
+
visibleOpenCodeCommands.map((c) => `/${c}`).join(", ");
|
|
19155
19362
|
}
|
|
19156
19363
|
ctx.reply(msg);
|
|
19157
19364
|
});
|
|
@@ -19160,19 +19367,20 @@ async function startTelegram(options) {
|
|
|
19160
19367
|
let msg = "Send me any message and I'll process it using OpenCode.\n\n" +
|
|
19161
19368
|
"Bot commands:\n" +
|
|
19162
19369
|
"/new - Start a new conversation\n" +
|
|
19163
|
-
"/sessions - List your sessions\n" +
|
|
19370
|
+
"/sessions - List your sessions (buttons)\n" +
|
|
19164
19371
|
"/title <text> - Rename the current session\n" +
|
|
19165
19372
|
"/export - Export the current session as a markdown file\n" +
|
|
19166
19373
|
"/export full - Export with all details (thinking, costs, steps)\n" +
|
|
19167
19374
|
"/verbose - Toggle verbose mode (show thinking and tool calls)\n" +
|
|
19168
19375
|
"/verbose on|off - Set verbose mode explicitly\n" +
|
|
19169
|
-
"/model -
|
|
19376
|
+
"/model <keyword> - Search available models\n" +
|
|
19170
19377
|
"/usage - Show token and cost usage for this session\n" +
|
|
19171
19378
|
"/help - Show this help message\n";
|
|
19172
|
-
|
|
19379
|
+
const visibleOpenCodeCommands = getVisibleOpenCodeCommands();
|
|
19380
|
+
if (visibleOpenCodeCommands.length > 0) {
|
|
19173
19381
|
msg +=
|
|
19174
19382
|
"\nOpenCode commands:\n" +
|
|
19175
|
-
|
|
19383
|
+
visibleOpenCodeCommands.map((c) => `/${c}`).join(", ");
|
|
19176
19384
|
}
|
|
19177
19385
|
ctx.reply(msg);
|
|
19178
19386
|
});
|
|
@@ -19842,7 +20050,9 @@ async function startTelegram(options) {
|
|
|
19842
20050
|
}
|
|
19843
20051
|
// Check if it's a known OpenCode command
|
|
19844
20052
|
if (!opencodeCommands.has(commandName)) {
|
|
19845
|
-
const available =
|
|
20053
|
+
const available = getVisibleOpenCodeCommands()
|
|
20054
|
+
.map((c) => `/${c}`)
|
|
20055
|
+
.join(", ");
|
|
19846
20056
|
await ctx.reply(`Unknown command: /${commandName}\n\n` +
|
|
19847
20057
|
`Available OpenCode commands: ${available || "none"}\n` +
|
|
19848
20058
|
`Bot commands: /new, /help`);
|