@inetafrica/open-claudia 1.10.0 → 1.11.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/CHANGELOG.md CHANGED
@@ -1,5 +1,12 @@
1
1
  # Changelog
2
2
 
3
+ ## v1.11.0
4
+ - Backend-aware plan mode: /plan passes `--mode plan` to Cursor Agent, `--permission-mode plan` to Claude
5
+ - New /ask command: read-only Q&A mode (Cursor Agent only, `--mode ask`)
6
+ - /effort and /budget now warn when on Cursor backend (unsupported flags)
7
+ - Worktree flag (`--worktree`) wired into Cursor Agent args
8
+ - buildCursorArgs now forwards plan/ask/worktree settings
9
+
3
10
  ## v1.10.0
4
11
  - Cursor Agent backend: switch between Claude Code and Cursor Agent CLI
5
12
  - New commands: /cursor, /claude, /backend with inline keyboard
package/README.md CHANGED
@@ -1,41 +1,69 @@
1
1
  # Open Claudia
2
2
 
3
- Your always-on AI coding assistant — Claude Code via Telegram.
3
+ Your always-on AI coding assistant — Claude Code and Cursor Agent via Telegram.
4
4
 
5
- Send text, voice notes, screenshots, and files from your phone. Claude Code works on your projects and reports back.
5
+ Send text, voice notes, screenshots, and files from your phone. Your chosen AI agent works on your projects and reports back.
6
6
 
7
7
  ## Features
8
8
 
9
+ - **Multi-backend** — switch between Claude Code and Cursor Agent on the fly (`/cursor`, `/claude`)
9
10
  - **Multi-project sessions** — switch between workspace projects
10
11
  - **Per-project conversation history** — auto-resumes last conversation, switch with `/sessions`
12
+ - **Separate session persistence** — Claude and Cursor each maintain their own conversation state
11
13
  - **Voice notes** — speak instructions, transcribed locally via Whisper
12
14
  - **Screenshots & images** — send UI mockups, errors, or code screenshots
13
- - **File sharing** — send PDFs, code files, documents — saved and read by Claude
15
+ - **File sharing** — send PDFs, code files, documents — saved and read by the agent
14
16
  - **Reply context** — reply to any message (including files) for follow-up
15
- - **Streaming output** — see Claude working in real-time
17
+ - **Streaming output** — see the agent working in real-time
18
+ - **Agent mode** — non-blocking side conversations while heavy tasks run in the background
16
19
  - **Cron jobs** — scheduled tasks (standups, git digests, health checks)
17
20
  - **Encrypted vault** — store API keys and credentials securely
18
21
  - **Customizable soul** — define your assistant's personality and knowledge
19
- - **Model switching** — toggle between Opus, Sonnet, Haiku
20
- - **Plan mode, effort levels, budgets** — full Claude Code control from Telegram
22
+ - **Model switching** — toggle between models on either backend
23
+ - **Plan mode, effort levels, budgets** — full control from Telegram
21
24
  - **Auto-updates** — checks for new versions every 5 minutes, upgrade with `/upgrade`
22
25
  - **Multi-user auth** — authorize additional users with code verification
23
26
  - **Cross-platform** — works on macOS, Linux, and Windows
24
27
 
25
28
  ## Prerequisites
26
29
 
27
- - [Claude Code CLI](https://docs.anthropic.com/en/docs/claude-code) installed and authenticated
28
30
  - [Node.js](https://nodejs.org/) 18+
29
31
  - A Telegram bot token (from [@BotFather](https://t.me/BotFather))
32
+ - At least one authenticated CLI backend on the host machine (see below)
30
33
  - (Optional) [whisper.cpp](https://github.com/ggerganov/whisper.cpp) + ffmpeg for voice notes
31
34
 
32
- ## Install
35
+ ## Quick Start
36
+
37
+ ### 1. Install and authenticate the CLI backends
38
+
39
+ You need **at least one** of these authenticated on the machine where Open Claudia will run.
40
+
41
+ **Claude Code** (required):
42
+
43
+ ```bash
44
+ npm install -g @anthropic-ai/claude-code
45
+ claude # Opens browser to log in
46
+ claude --version # Verify it works
47
+ ```
48
+
49
+ **Cursor Agent** (optional — enables `/cursor` backend):
50
+
51
+ ```bash
52
+ # Install from Cursor IDE: Settings > General > Agent CLI
53
+ # Or download from https://docs.cursor.com/agent
54
+ agent login # Opens browser to authenticate
55
+ agent status # Verify: should show your email and plan
56
+ ```
57
+
58
+ > **Important**: Both CLIs store authentication locally on the machine. Open Claudia doesn't handle auth itself — it shells out to whichever CLI you've authenticated. If you see auth errors in Telegram, SSH into the machine and re-authenticate the relevant CLI.
59
+
60
+ ### 2. Install Open Claudia
33
61
 
34
62
  ```bash
35
63
  npm install -g @inetafrica/open-claudia
36
64
  ```
37
65
 
38
- ## Setup
66
+ ### 3. Run setup
39
67
 
40
68
  ```bash
41
69
  open-claudia setup
@@ -43,7 +71,7 @@ open-claudia setup
43
71
 
44
72
  The setup wizard will:
45
73
 
46
- 1. Detect Claude CLI, ffmpeg, and whisper on your system
74
+ 1. Detect Claude CLI, Cursor Agent CLI, ffmpeg, and whisper on your system
47
75
  2. Verify Claude is authenticated
48
76
  3. Ask for your Telegram bot token and verify it
49
77
  4. Generate a verification code — send it to your bot to prove your identity
@@ -69,6 +97,16 @@ If installed as a background service, the bot starts automatically on login and
69
97
 
70
98
  ## Telegram Commands
71
99
 
100
+ ### Backend Switching
101
+
102
+ | Command | Description |
103
+ |---------|-------------|
104
+ | `/cursor` | Switch to Cursor Agent backend |
105
+ | `/claude` | Switch to Claude Code backend |
106
+ | `/backend` | Show current backend with picker |
107
+
108
+ Each backend keeps its own persistent session. Switching doesn't lose your place — you can go back and forth freely.
109
+
72
110
  ### Session Management
73
111
 
74
112
  | Command | Description |
@@ -85,13 +123,15 @@ When you select a project, the last conversation is automatically resumed. Tap "
85
123
 
86
124
  | Command | Description |
87
125
  |---------|-------------|
88
- | `/model` | Switch model (opus / sonnet / haiku) |
126
+ | `/model` | Switch model (opus / sonnet / haiku for Claude; any model flag for Cursor) |
89
127
  | `/effort` | Set effort level (low / medium / high / max) |
90
- | `/budget` | Set max spend for next task (e.g. `/budget 0.50`) |
91
- | `/plan` | Toggle plan mode |
128
+ | `/budget` | Set max spend for next task (e.g. `/budget 0.50`) — Claude only |
129
+ | `/plan` | Toggle plan mode — `--permission-mode plan` (Claude) / `--mode plan` (Cursor) |
130
+ | `/ask` | Toggle ask mode — read-only Q&A, no edits (Cursor Agent only) |
92
131
  | `/compact` | Summarize conversation context |
93
- | `/worktree` | Toggle isolated git branch |
94
- | `/status` | Show current session and settings |
132
+ | `/worktree` | Toggle isolated git branch — Claude only |
133
+ | `/mode` | Switch between direct and agent bot modes |
134
+ | `/status` | Show current session, backend, and settings |
95
135
 
96
136
  ### Automation
97
137
 
@@ -111,11 +151,28 @@ When you select a project, the last conversation is automatically resumed. Tap "
111
151
  | `/stop` | Cancel a running task |
112
152
  | `/help` | Show all commands |
113
153
 
154
+ ## Backend Comparison
155
+
156
+ | | Claude Code | Cursor Agent |
157
+ |---|---|---|
158
+ | Binary | `claude` | `agent` |
159
+ | Session flag | `--resume <id>` | `--resume <id>` |
160
+ | Auth | `claude auth` | `agent login` |
161
+ | Plan mode | Yes (`--permission-mode plan`) | Yes (`--mode plan`) |
162
+ | Ask mode | No | Yes (`--mode ask`) |
163
+ | Budget control | Yes (`--max-budget-usd`) | No |
164
+ | Effort levels | Yes | No |
165
+ | Worktree | Yes (`--worktree`) | Yes (`--worktree`) |
166
+ | Model switching | Yes | Yes |
167
+ | Dangerously skip permissions | Yes | Yes (`--trust`) |
168
+
169
+ Both backends output `stream-json` which Open Claudia parses for real-time progress updates.
170
+
114
171
  ## Sending Files
115
172
 
116
- Send any file to the bot — PDFs, code files, documents, images. Files are saved to `~/.open-claudia/files/` with their original names. Claude reads the file and responds based on content.
173
+ Send any file to the bot — PDFs, code files, documents, images. Files are saved to `~/.open-claudia/files/` with their original names. The agent reads the file and responds based on content.
117
174
 
118
- Add a caption to your file to give Claude specific instructions:
175
+ Add a caption to your file to give the agent specific instructions:
119
176
  - Send a PDF with caption "summarize the key findings"
120
177
  - Send a code file with caption "find bugs in this"
121
178
  - Send a screenshot with caption "implement this design"
@@ -152,11 +209,14 @@ This shows authorized chats, pending requests, and lets you approve/deny or add
152
209
  ## How It Works
153
210
 
154
211
  ```
155
- Phone (Telegram) --> Bot (Node.js) --> Claude Code CLI --> Your codebase
212
+ Phone (Telegram) --> Bot (Node.js) --> Claude Code CLI --> Your codebase
213
+ --> Cursor Agent CLI -->
156
214
  <-- <-- <--
157
215
  ```
158
216
 
159
- The bot spawns `claude -p` for each message, streaming output back to Telegram. It maintains conversation context via `--resume` and passes a system prompt that gives Claude awareness of the Telegram environment, its configuration files, and the ability to send files/images directly.
217
+ The bot spawns the active backend CLI in headless mode (`--print` / `-p`) for each message, streaming `stream-json` output back to Telegram. It maintains conversation context via `--resume` and passes a system prompt that gives the agent awareness of the Telegram environment, configuration files, and the ability to send files/images directly.
218
+
219
+ Use `/cursor` or `/claude` to switch which CLI handles your messages. Each maintains its own session ID, so switching doesn't lose context on either side.
160
220
 
161
221
  ## Configuration Files
162
222
 
@@ -164,17 +224,30 @@ All stored in `~/.open-claudia/`:
164
224
 
165
225
  | File | Purpose |
166
226
  |------|---------|
167
- | `.env` | Telegram token, workspace path, binary paths |
227
+ | `.env` | Telegram token, workspace path, binary paths (`CLAUDE_PATH`, `CURSOR_PATH`) |
168
228
  | `auth.json` | Authorized users and pending requests |
169
229
  | `vault.enc` | Encrypted credential store |
170
230
  | `soul.md` | Assistant identity and personality (editable via `/soul`) |
171
231
  | `crons.json` | Scheduled tasks |
172
232
  | `sessions.json` | Per-project conversation history |
173
- | `state.json` | Current session state (survives restarts) |
233
+ | `state.json` | Current session state including active backend (survives restarts) |
174
234
  | `bot.log` | Bot logs |
175
235
  | `files/` | Files received from Telegram |
176
236
  | `media/` | Temporary media (voice notes, photos) |
177
237
 
238
+ ### Environment Variables (.env)
239
+
240
+ | Variable | Required | Description |
241
+ |----------|----------|-------------|
242
+ | `TELEGRAM_BOT_TOKEN` | Yes | Bot token from BotFather |
243
+ | `TELEGRAM_CHAT_ID` | Yes | Comma-separated authorized chat IDs |
244
+ | `WORKSPACE` | Yes | Path to your projects directory |
245
+ | `CLAUDE_PATH` | Yes | Path to Claude Code CLI binary |
246
+ | `CURSOR_PATH` | No | Path to Cursor Agent CLI binary (auto-detected if in PATH) |
247
+ | `WHISPER_CLI` | No | Path to whisper.cpp binary |
248
+ | `WHISPER_MODEL` | No | Whisper model to use |
249
+ | `FFMPEG` | No | Path to ffmpeg binary |
250
+
178
251
  ## Background Service
179
252
 
180
253
  ### macOS (launchd)
@@ -188,6 +261,8 @@ launchctl load ~/Library/LaunchAgents/com.open-claudia.plist
188
261
  launchctl unload ~/Library/LaunchAgents/com.open-claudia.plist
189
262
  ```
190
263
 
264
+ **Important**: If `agent` is installed in a non-standard location (e.g. `~/.local/bin`), make sure that path is included in the launchd plist's `PATH` environment variable. Otherwise the bot won't detect it at startup.
265
+
191
266
  ### Linux (systemd)
192
267
 
193
268
  ```bash
@@ -202,7 +277,7 @@ sudo systemctl status claude-telegram-bot
202
277
 
203
278
  The bot checks npm for new versions every 5 minutes. When an update is available, you get a Telegram notification:
204
279
 
205
- > "Hey! A new version is available (v1.3.0). You're on v1.2.9."
280
+ > "Hey! A new version is available (v1.10.0). You're on v1.9.2."
206
281
 
207
282
  Send `/upgrade` to update. The bot will:
208
283
  1. Download and install the new version
@@ -232,7 +307,7 @@ Store sensitive credentials encrypted:
232
307
  /vault lock # Lock vault
233
308
  ```
234
309
 
235
- Claude can read vault credentials when unlocked — useful for deployment scripts and API calls.
310
+ The agent can read vault credentials when unlocked — useful for deployment scripts and API calls.
236
311
 
237
312
  ## License
238
313
 
package/bot-agent.js CHANGED
@@ -178,6 +178,7 @@ bot.setMyCommands([
178
178
  { command: "effort", description: "Set effort level" },
179
179
  { command: "budget", description: "Set max spend for next task" },
180
180
  { command: "plan", description: "Toggle plan mode" },
181
+ { command: "ask", description: "Toggle ask mode (Cursor only)" },
181
182
  { command: "sessions", description: "List conversations for this project" },
182
183
  { command: "compact", description: "Summarize conversation context" },
183
184
  { command: "continue", description: "Resume last conversation" },
@@ -696,6 +697,9 @@ function buildCursorArgs(prompt, opts = {}) {
696
697
  if (opts.continueSession) args.push("--continue");
697
698
  else if (cursorSessionId && !opts.fresh) args.push("--resume", cursorSessionId);
698
699
  if (settings.model) args.push("--model", settings.model);
700
+ if (settings.permissionMode === "plan") args.push("--mode", "plan");
701
+ else if (settings.permissionMode === "ask") args.push("--mode", "ask");
702
+ if (settings.worktree) args.push("--worktree");
699
703
  args.push(prompt);
700
704
  return args;
701
705
  }
@@ -1110,32 +1114,62 @@ bot.onText(/\/sessions$/, (msg) => {
1110
1114
 
1111
1115
  bot.onText(/\/model$/, (msg) => {
1112
1116
  if (!isAuthorized(msg)) return;
1113
- send(`Model: ${settings.model || "default"}`, { keyboard: { inline_keyboard: [
1117
+ const keyboard = settings.backend === "cursor" ? [
1118
+ [{ text: "Composer 2", callback_data: "m:composer-2" }, { text: "Composer 2 Fast", callback_data: "m:composer-2-fast" }],
1119
+ [{ text: "Opus 4.6 Thinking", callback_data: "m:claude-4.6-opus-high-thinking" }, { text: "Sonnet 4.6", callback_data: "m:claude-4.6-sonnet-medium" }],
1120
+ [{ text: "GPT-5.4", callback_data: "m:gpt-5.4-medium" }, { text: "GPT-5.4 High", callback_data: "m:gpt-5.4-high" }],
1121
+ [{ text: "Auto", callback_data: "m:auto" }, { text: "Default", callback_data: "m:default" }],
1122
+ ] : [
1114
1123
  [{ text: "Opus", callback_data: "m:opus" }, { text: "Sonnet", callback_data: "m:sonnet" }, { text: "Haiku", callback_data: "m:haiku" }],
1115
1124
  [{ text: "Default", callback_data: "m:default" }],
1116
- ] } });
1125
+ ];
1126
+ const label = settings.backend === "cursor" ? "Cursor Agent" : "Claude Code";
1127
+ send(`${label} model: ${settings.model || "default"}\n\nOr type /model <name> for any model.`, { keyboard: { inline_keyboard: keyboard } });
1117
1128
  });
1118
1129
  bot.onText(/\/model (.+)/, (msg, match) => { if (!isAuthorized(msg)) return; settings.model = match[1].trim().toLowerCase(); if (settings.model === "default") settings.model = null; send(`Model: ${settings.model || "default"}`); });
1119
1130
 
1120
1131
  bot.onText(/\/effort$/, (msg) => {
1121
1132
  if (!isAuthorized(msg)) return;
1133
+ if (settings.backend === "cursor") return send("Effort levels are not supported on Cursor Agent.\nSwitch to Claude with /claude to use this.");
1122
1134
  send(`Effort: ${settings.effort || "default"}`, { keyboard: { inline_keyboard: [
1123
1135
  [{ text: "Low", callback_data: "e:low" }, { text: "Med", callback_data: "e:medium" }, { text: "High", callback_data: "e:high" }, { text: "Max", callback_data: "e:max" }],
1124
1136
  [{ text: "Default", callback_data: "e:default" }],
1125
1137
  ] } });
1126
1138
  });
1127
- bot.onText(/\/effort (.+)/, (msg, match) => { if (!isAuthorized(msg)) return; const e = match[1].trim().toLowerCase(); settings.effort = ["low","medium","high","max"].includes(e) ? e : null; send(`Effort: ${settings.effort || "default"}`); });
1139
+ bot.onText(/\/effort (.+)/, (msg, match) => {
1140
+ if (!isAuthorized(msg)) return;
1141
+ if (settings.backend === "cursor") return send("Effort levels are not supported on Cursor Agent.");
1142
+ const e = match[1].trim().toLowerCase(); settings.effort = ["low","medium","high","max"].includes(e) ? e : null; send(`Effort: ${settings.effort || "default"}`);
1143
+ });
1128
1144
 
1129
1145
  bot.onText(/\/budget$/, (msg) => {
1130
1146
  if (!isAuthorized(msg)) return;
1147
+ if (settings.backend === "cursor") return send("Budget limits are not supported on Cursor Agent.\nSwitch to Claude with /claude to use this.");
1131
1148
  send(`Budget: ${settings.budget ? "$" + settings.budget : "none"}`, { keyboard: { inline_keyboard: [
1132
1149
  [{ text: "$1", callback_data: "b:1" }, { text: "$5", callback_data: "b:5" }, { text: "$10", callback_data: "b:10" }, { text: "$25", callback_data: "b:25" }],
1133
1150
  [{ text: "No limit", callback_data: "b:none" }],
1134
1151
  ] } });
1135
1152
  });
1136
- bot.onText(/\/budget (.+)/, (msg, match) => { if (!isAuthorized(msg)) return; const v = parseFloat(match[1].replace("$","")); settings.budget = v > 0 ? v : null; send(`Budget: ${settings.budget ? "$"+settings.budget : "none"}`); });
1153
+ bot.onText(/\/budget (.+)/, (msg, match) => {
1154
+ if (!isAuthorized(msg)) return;
1155
+ if (settings.backend === "cursor") return send("Budget limits are not supported on Cursor Agent.");
1156
+ const v = parseFloat(match[1].replace("$","")); settings.budget = v > 0 ? v : null; send(`Budget: ${settings.budget ? "$"+settings.budget : "none"}`);
1157
+ });
1137
1158
 
1138
- bot.onText(/\/plan$/, (msg) => { if (!isAuthorized(msg)) return; const p = settings.permissionMode === "plan"; settings.permissionMode = p ? null : "plan"; send(p ? "Plan mode off." : "Plan mode on."); });
1159
+ bot.onText(/\/plan$/, (msg) => {
1160
+ if (!isAuthorized(msg)) return;
1161
+ const p = settings.permissionMode === "plan";
1162
+ settings.permissionMode = p ? null : "plan";
1163
+ const label = settings.backend === "cursor" ? "read-only planning, no edits" : "plan permission mode";
1164
+ send(p ? "Plan mode off." : `Plan mode on (${label}).`);
1165
+ });
1166
+ bot.onText(/\/ask$/, (msg) => {
1167
+ if (!isAuthorized(msg)) return;
1168
+ if (settings.backend !== "cursor") return send("Ask mode is only available on Cursor Agent.\nUse /cursor to switch.");
1169
+ const a = settings.permissionMode === "ask";
1170
+ settings.permissionMode = a ? null : "ask";
1171
+ send(a ? "Ask mode off." : "Ask mode on (read-only Q&A, no edits).");
1172
+ });
1139
1173
  bot.onText(/\/compact/, async (msg) => { if (!isAuthorized(msg)) return; if (!requireSession(msg)) return; if (!getActiveSessionId()) return send("No conversation."); await runClaude("Summarize: key decisions, code state, next steps.", currentSession.dir, msg.message_id); });
1140
1174
  bot.onText(/\/continue$/, async (msg) => { if (!isAuthorized(msg)) return; if (!requireSession(msg)) return; await runClaude("continue where we left off", currentSession.dir, msg.message_id, { continueSession: true }); });
1141
1175
  bot.onText(/\/worktree$/, (msg) => { if (!isAuthorized(msg)) return; settings.worktree = !settings.worktree; send(settings.worktree ? "Worktree on." : "Worktree off."); });
package/bot.js CHANGED
@@ -236,6 +236,7 @@ bot.setMyCommands([
236
236
  { command: "effort", description: "Set effort level" },
237
237
  { command: "budget", description: "Set max spend for next task" },
238
238
  { command: "plan", description: "Toggle plan mode" },
239
+ { command: "ask", description: "Toggle ask mode (Cursor only)" },
239
240
  { command: "sessions", description: "List conversations for this project" },
240
241
  { command: "compact", description: "Summarize conversation context" },
241
242
  { command: "continue", description: "Resume last conversation" },
@@ -757,6 +758,9 @@ function buildCursorArgs(prompt, opts = {}) {
757
758
  if (opts.continueSession) args.push("--continue");
758
759
  else if (cursorSessionId && !opts.fresh) args.push("--resume", cursorSessionId);
759
760
  if (settings.model) args.push("--model", settings.model);
761
+ if (settings.permissionMode === "plan") args.push("--mode", "plan");
762
+ else if (settings.permissionMode === "ask") args.push("--mode", "ask");
763
+ if (settings.worktree) args.push("--worktree");
760
764
  args.push(prompt);
761
765
  return args;
762
766
  }
@@ -1153,32 +1157,62 @@ bot.onText(/\/sessions$/, (msg) => {
1153
1157
 
1154
1158
  bot.onText(/\/model$/, (msg) => {
1155
1159
  if (!isAuthorized(msg)) return;
1156
- send(`Model: ${settings.model || "default"}`, { keyboard: { inline_keyboard: [
1160
+ const keyboard = settings.backend === "cursor" ? [
1161
+ [{ text: "Composer 2", callback_data: "m:composer-2" }, { text: "Composer 2 Fast", callback_data: "m:composer-2-fast" }],
1162
+ [{ text: "Opus 4.6 Thinking", callback_data: "m:claude-4.6-opus-high-thinking" }, { text: "Sonnet 4.6", callback_data: "m:claude-4.6-sonnet-medium" }],
1163
+ [{ text: "GPT-5.4", callback_data: "m:gpt-5.4-medium" }, { text: "GPT-5.4 High", callback_data: "m:gpt-5.4-high" }],
1164
+ [{ text: "Auto", callback_data: "m:auto" }, { text: "Default", callback_data: "m:default" }],
1165
+ ] : [
1157
1166
  [{ text: "Opus", callback_data: "m:opus" }, { text: "Sonnet", callback_data: "m:sonnet" }, { text: "Haiku", callback_data: "m:haiku" }],
1158
1167
  [{ text: "Default", callback_data: "m:default" }],
1159
- ] } });
1168
+ ];
1169
+ const label = settings.backend === "cursor" ? "Cursor Agent" : "Claude Code";
1170
+ send(`${label} model: ${settings.model || "default"}\n\nOr type /model <name> for any model.`, { keyboard: { inline_keyboard: keyboard } });
1160
1171
  });
1161
1172
  bot.onText(/\/model (.+)/, (msg, match) => { if (!isAuthorized(msg)) return; settings.model = match[1].trim().toLowerCase(); if (settings.model === "default") settings.model = null; send(`Model: ${settings.model || "default"}`); });
1162
1173
 
1163
1174
  bot.onText(/\/effort$/, (msg) => {
1164
1175
  if (!isAuthorized(msg)) return;
1176
+ if (settings.backend === "cursor") return send("Effort levels are not supported on Cursor Agent.\nSwitch to Claude with /claude to use this.");
1165
1177
  send(`Effort: ${settings.effort || "default"}`, { keyboard: { inline_keyboard: [
1166
1178
  [{ text: "Low", callback_data: "e:low" }, { text: "Med", callback_data: "e:medium" }, { text: "High", callback_data: "e:high" }, { text: "Max", callback_data: "e:max" }],
1167
1179
  [{ text: "Default", callback_data: "e:default" }],
1168
1180
  ] } });
1169
1181
  });
1170
- bot.onText(/\/effort (.+)/, (msg, match) => { if (!isAuthorized(msg)) return; const e = match[1].trim().toLowerCase(); settings.effort = ["low","medium","high","max"].includes(e) ? e : null; send(`Effort: ${settings.effort || "default"}`); });
1182
+ bot.onText(/\/effort (.+)/, (msg, match) => {
1183
+ if (!isAuthorized(msg)) return;
1184
+ if (settings.backend === "cursor") return send("Effort levels are not supported on Cursor Agent.");
1185
+ const e = match[1].trim().toLowerCase(); settings.effort = ["low","medium","high","max"].includes(e) ? e : null; send(`Effort: ${settings.effort || "default"}`);
1186
+ });
1171
1187
 
1172
1188
  bot.onText(/\/budget$/, (msg) => {
1173
1189
  if (!isAuthorized(msg)) return;
1190
+ if (settings.backend === "cursor") return send("Budget limits are not supported on Cursor Agent.\nSwitch to Claude with /claude to use this.");
1174
1191
  send(`Budget: ${settings.budget ? "$" + settings.budget : "none"}`, { keyboard: { inline_keyboard: [
1175
1192
  [{ text: "$1", callback_data: "b:1" }, { text: "$5", callback_data: "b:5" }, { text: "$10", callback_data: "b:10" }, { text: "$25", callback_data: "b:25" }],
1176
1193
  [{ text: "No limit", callback_data: "b:none" }],
1177
1194
  ] } });
1178
1195
  });
1179
- bot.onText(/\/budget (.+)/, (msg, match) => { if (!isAuthorized(msg)) return; const v = parseFloat(match[1].replace("$","")); settings.budget = v > 0 ? v : null; send(`Budget: ${settings.budget ? "$"+settings.budget : "none"}`); });
1196
+ bot.onText(/\/budget (.+)/, (msg, match) => {
1197
+ if (!isAuthorized(msg)) return;
1198
+ if (settings.backend === "cursor") return send("Budget limits are not supported on Cursor Agent.");
1199
+ const v = parseFloat(match[1].replace("$","")); settings.budget = v > 0 ? v : null; send(`Budget: ${settings.budget ? "$"+settings.budget : "none"}`);
1200
+ });
1180
1201
 
1181
- bot.onText(/\/plan$/, (msg) => { if (!isAuthorized(msg)) return; const p = settings.permissionMode === "plan"; settings.permissionMode = p ? null : "plan"; send(p ? "Plan mode off." : "Plan mode on."); });
1202
+ bot.onText(/\/plan$/, (msg) => {
1203
+ if (!isAuthorized(msg)) return;
1204
+ const p = settings.permissionMode === "plan";
1205
+ settings.permissionMode = p ? null : "plan";
1206
+ const label = settings.backend === "cursor" ? "read-only planning, no edits" : "plan permission mode";
1207
+ send(p ? "Plan mode off." : `Plan mode on (${label}).`);
1208
+ });
1209
+ bot.onText(/\/ask$/, (msg) => {
1210
+ if (!isAuthorized(msg)) return;
1211
+ if (settings.backend !== "cursor") return send("Ask mode is only available on Cursor Agent.\nUse /cursor to switch.");
1212
+ const a = settings.permissionMode === "ask";
1213
+ settings.permissionMode = a ? null : "ask";
1214
+ send(a ? "Ask mode off." : "Ask mode on (read-only Q&A, no edits).");
1215
+ });
1182
1216
  bot.onText(/\/compact/, async (msg) => { if (!isAuthorized(msg)) return; if (!requireSession(msg)) return; if (!getActiveSessionId()) return send("No conversation."); await runClaude("Summarize: key decisions, code state, next steps.", currentSession.dir, msg.message_id); });
1183
1217
  bot.onText(/\/continue$/, async (msg) => { if (!isAuthorized(msg)) return; if (!requireSession(msg)) return; await runClaude("continue where we left off", currentSession.dir, msg.message_id, { continueSession: true }); });
1184
1218
  bot.onText(/\/worktree$/, (msg) => { if (!isAuthorized(msg)) return; settings.worktree = !settings.worktree; send(settings.worktree ? "Worktree on." : "Worktree off."); });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@inetafrica/open-claudia",
3
- "version": "1.10.0",
3
+ "version": "1.11.0",
4
4
  "description": "Your always-on AI coding assistant — Claude Code via Telegram",
5
5
  "main": "bot.js",
6
6
  "bin": {