@inetafrica/open-claudia 1.2.8 → 1.3.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 CHANGED
@@ -7,92 +7,232 @@ Send text, voice notes, screenshots, and files from your phone. Claude Code work
7
7
  ## Features
8
8
 
9
9
  - **Multi-project sessions** — switch between workspace projects
10
+ - **Per-project conversation history** — auto-resumes last conversation, switch with `/sessions`
10
11
  - **Voice notes** — speak instructions, transcribed locally via Whisper
11
- - **Screenshots** — send UI mockups, errors, or code screenshots
12
- - **File sharing** — send and receive files directly in Telegram
12
+ - **Screenshots & images** — send UI mockups, errors, or code screenshots
13
+ - **File sharing** — send PDFs, code files, documents saved and read by Claude
14
+ - **Reply context** — reply to any message (including files) for follow-up
13
15
  - **Streaming output** — see Claude working in real-time
14
16
  - **Cron jobs** — scheduled tasks (standups, git digests, health checks)
15
17
  - **Encrypted vault** — store API keys and credentials securely
16
18
  - **Customizable soul** — define your assistant's personality and knowledge
17
19
  - **Model switching** — toggle between Opus, Sonnet, Haiku
18
20
  - **Plan mode, effort levels, budgets** — full Claude Code control from Telegram
21
+ - **Auto-updates** — checks for new versions every 5 minutes, upgrade with `/upgrade`
22
+ - **Multi-user auth** — authorize additional users with code verification
23
+ - **Cross-platform** — works on macOS, Linux, and Windows
19
24
 
20
25
  ## Prerequisites
21
26
 
22
27
  - [Claude Code CLI](https://docs.anthropic.com/en/docs/claude-code) installed and authenticated
23
28
  - [Node.js](https://nodejs.org/) 18+
24
29
  - A Telegram bot token (from [@BotFather](https://t.me/BotFather))
25
- - (Optional) whisper-cpp + ffmpeg for voice notes
30
+ - (Optional) [whisper.cpp](https://github.com/ggerganov/whisper.cpp) + ffmpeg for voice notes
26
31
 
27
32
  ## Install
28
33
 
29
34
  ```bash
30
- # From npm package
31
- npm install -g open-claudia
32
-
33
- # Or from source
34
- git clone https://git.coders.africa/agents/open-claudia.git
35
- cd open-claudia
36
- npm install
35
+ npm install -g @inetafrica/open-claudia
37
36
  ```
38
37
 
39
38
  ## Setup
40
39
 
41
40
  ```bash
42
41
  open-claudia setup
43
- # or: node setup.js
44
42
  ```
45
43
 
46
44
  The setup wizard will:
47
- 1. Detect Claude CLI and dependencies
48
- 2. Ask for your Telegram bot token and verify it
49
- 3. Auto-detect your chat ID
50
- 4. Set a vault password for credential encryption
51
- 5. Optionally install as a background service (macOS launchd / Linux systemd)
45
+
46
+ 1. Detect Claude CLI, ffmpeg, and whisper on your system
47
+ 2. Verify Claude is authenticated
48
+ 3. Ask for your Telegram bot token and verify it
49
+ 4. Generate a verification code send it to your bot to prove your identity
50
+ 5. Set your workspace path (default: `~/.open-claudia/Workspace`)
51
+ 6. Create an encrypted vault for credentials
52
+ 7. Optionally install as a background service (macOS launchd / Linux systemd)
53
+
54
+ If setup is interrupted, running it again resumes from the last completed step.
55
+
56
+ All configuration is stored in `~/.open-claudia/` — survives npm upgrades.
52
57
 
53
58
  ## Run
54
59
 
55
60
  ```bash
56
61
  open-claudia start # Start the bot
57
62
  open-claudia stop # Stop the bot
58
- open-claudia status # Check if running
63
+ open-claudia status # Check if running (shows PID)
59
64
  open-claudia logs # View recent logs
65
+ open-claudia auth # Manage chat authorizations
60
66
  ```
61
67
 
68
+ If installed as a background service, the bot starts automatically on login and restarts on crash.
69
+
62
70
  ## Telegram Commands
63
71
 
72
+ ### Session Management
73
+
64
74
  | Command | Description |
65
75
  |---------|-------------|
66
76
  | `/session` | Pick a project to work on |
77
+ | `/sessions` | List past conversations for current project |
67
78
  | `/projects` | Browse all workspace projects |
68
- | `/model` | Switch model (opus/sonnet/haiku) |
69
- | `/effort` | Set effort level |
70
- | `/budget` | Set max spend per task |
79
+ | `/continue` | Resume last conversation explicitly |
80
+ | `/end` | End current session |
81
+
82
+ When you select a project, the last conversation is automatically resumed. Tap "New conversation" to start fresh.
83
+
84
+ ### Settings
85
+
86
+ | Command | Description |
87
+ |---------|-------------|
88
+ | `/model` | Switch model (opus / sonnet / haiku) |
89
+ | `/effort` | Set effort level (low / medium / high / max) |
90
+ | `/budget` | Set max spend for next task (e.g. `/budget 0.50`) |
71
91
  | `/plan` | Toggle plan mode |
72
- | `/continue` | Resume last conversation |
92
+ | `/compact` | Summarize conversation context |
93
+ | `/worktree` | Toggle isolated git branch |
94
+ | `/status` | Show current session and settings |
95
+
96
+ ### Automation
97
+
98
+ | Command | Description |
99
+ |---------|-------------|
73
100
  | `/cron` | Manage scheduled tasks |
74
- | `/vault` | Manage credentials (password-protected) |
75
- | `/soul` | View/edit assistant identity |
76
- | `/stop` | Cancel running task |
77
- | `/end` | End current session |
101
+ | `/vault` | Manage encrypted credentials (password required) |
102
+ | `/soul` | View/edit assistant identity and personality |
103
+
104
+ ### System
105
+
106
+ | Command | Description |
107
+ |---------|-------------|
108
+ | `/version` | Show current running version |
109
+ | `/upgrade` | Upgrade to latest version and restart |
110
+ | `/restart` | Restart the bot |
111
+ | `/stop` | Cancel a running task |
112
+ | `/help` | Show all commands |
113
+
114
+ ## Sending Files
115
+
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.
117
+
118
+ Add a caption to your file to give Claude specific instructions:
119
+ - Send a PDF with caption "summarize the key findings"
120
+ - Send a code file with caption "find bugs in this"
121
+ - Send a screenshot with caption "implement this design"
122
+
123
+ ## Voice Notes
124
+
125
+ Requires [whisper.cpp](https://github.com/ggerganov/whisper.cpp) and ffmpeg:
126
+
127
+ ```bash
128
+ # macOS
129
+ brew install whisper-cpp ffmpeg
130
+
131
+ # Linux (Ubuntu/Debian)
132
+ sudo apt install ffmpeg
133
+ # Build whisper.cpp from source: https://github.com/ggerganov/whisper.cpp
134
+ ```
135
+
136
+ Voice notes are transcribed locally — nothing sent to external services.
137
+
138
+ ## Multi-User Authorization
139
+
140
+ The setup owner is automatically authorized. To add more users:
141
+
142
+ **From Telegram** (unauthorized users):
143
+ - Send `/auth` to the bot — the owner gets notified
144
+
145
+ **From the terminal** (owner):
146
+ ```bash
147
+ open-claudia auth
148
+ ```
149
+
150
+ This shows authorized chats, pending requests, and lets you approve/deny or add new users with code verification.
78
151
 
79
152
  ## How It Works
80
153
 
81
154
  ```
82
- Phone (Telegram) Bot (Node.js) Claude Code CLI Your codebase
83
-
155
+ Phone (Telegram) --> Bot (Node.js) --> Claude Code CLI --> Your codebase
156
+ <-- <-- <--
84
157
  ```
85
158
 
86
- 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 own configuration files, and the ability to send files/images directly.
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.
87
160
 
88
161
  ## Configuration Files
89
162
 
163
+ All stored in `~/.open-claudia/`:
164
+
90
165
  | File | Purpose |
91
166
  |------|---------|
92
- | `.env` | Telegram token, paths, settings (created by setup) |
93
- | `soul.md` | Assistant identity and personality (editable) |
94
- | `crons.json` | Scheduled tasks |
167
+ | `.env` | Telegram token, workspace path, binary paths |
168
+ | `auth.json` | Authorized users and pending requests |
95
169
  | `vault.enc` | Encrypted credential store |
170
+ | `soul.md` | Assistant identity and personality (editable via `/soul`) |
171
+ | `crons.json` | Scheduled tasks |
172
+ | `sessions.json` | Per-project conversation history |
173
+ | `state.json` | Current session state (survives restarts) |
174
+ | `bot.log` | Bot logs |
175
+ | `files/` | Files received from Telegram |
176
+ | `media/` | Temporary media (voice notes, photos) |
177
+
178
+ ## Background Service
179
+
180
+ ### macOS (launchd)
181
+
182
+ Set up during `open-claudia setup`, or manually:
183
+
184
+ ```bash
185
+ # The setup wizard creates ~/Library/LaunchAgents/com.open-claudia.plist
186
+ # To manage:
187
+ launchctl load ~/Library/LaunchAgents/com.open-claudia.plist
188
+ launchctl unload ~/Library/LaunchAgents/com.open-claudia.plist
189
+ ```
190
+
191
+ ### Linux (systemd)
192
+
193
+ ```bash
194
+ # The setup wizard creates /etc/systemd/system/claude-telegram-bot.service
195
+ # To manage:
196
+ sudo systemctl enable claude-telegram-bot
197
+ sudo systemctl start claude-telegram-bot
198
+ sudo systemctl status claude-telegram-bot
199
+ ```
200
+
201
+ ## Auto-Updates
202
+
203
+ The bot checks npm for new versions every 5 minutes. When an update is available, you get a Telegram notification:
204
+
205
+ > "Hey! A new version is available (v1.3.0). You're on v1.2.9."
206
+
207
+ Send `/upgrade` to update. The bot will:
208
+ 1. Download and install the new version
209
+ 2. Go offline briefly
210
+ 3. Restart and notify you it's back
211
+
212
+ ## Cron Jobs
213
+
214
+ Schedule recurring tasks:
215
+
216
+ ```
217
+ /cron add "0 9 * * 1-5" myproject "Morning standup: summarize git changes since yesterday"
218
+ /cron add "0 18 * * *" myproject "Git digest: what changed today?"
219
+ /cron add "*/30 * * * *" myproject "Health check: verify the API is responding"
220
+ ```
221
+
222
+ Presets available via `/cron` menu.
223
+
224
+ ## Vault
225
+
226
+ Store sensitive credentials encrypted:
227
+
228
+ ```
229
+ /vault # Unlock vault (prompts for password)
230
+ /vault set AWS_KEY xxx # Store a credential
231
+ /vault remove AWS_KEY # Remove a credential
232
+ /vault lock # Lock vault
233
+ ```
234
+
235
+ Claude can read vault credentials when unlocked — useful for deployment scripts and API calls.
96
236
 
97
237
  ## License
98
238
 
package/bin/cli.js CHANGED
@@ -1,6 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  const { execSync } = require("child_process");
4
+ const fs = require("fs");
4
5
  const path = require("path");
5
6
 
6
7
  const args = process.argv.slice(2);
@@ -8,6 +9,30 @@ const command = args[0] || "help";
8
9
 
9
10
  const botDir = path.join(__dirname, "..");
10
11
 
12
+ function findBotProcesses() {
13
+ try {
14
+ if (process.platform === "win32") {
15
+ const out = execSync('tasklist /FI "IMAGENAME eq node.exe" /FO CSV /NH', { encoding: "utf-8" });
16
+ // Check if any node process has bot.js in its command line
17
+ const wmic = execSync('wmic process where "name=\'node.exe\'" get ProcessId,CommandLine /FORMAT:CSV 2>nul', { encoding: "utf-8" });
18
+ const pids = [];
19
+ for (const line of wmic.split("\n")) {
20
+ if (line.includes("bot.js") && line.includes("open-claudia")) {
21
+ const parts = line.trim().split(",");
22
+ const pid = parts[parts.length - 1];
23
+ if (pid) pids.push(pid.trim());
24
+ }
25
+ }
26
+ return pids;
27
+ } else {
28
+ const out = execSync('ps -eo pid,command | grep "bot.js" | grep "open-claudia" | grep -v grep', { encoding: "utf-8" });
29
+ return out.trim().split("\n").map((l) => l.trim().split(/\s+/)[0]).filter(Boolean);
30
+ }
31
+ } catch (e) {
32
+ return [];
33
+ }
34
+ }
35
+
11
36
  switch (command) {
12
37
  case "setup":
13
38
  require(path.join(botDir, "setup.js"));
@@ -24,33 +49,37 @@ switch (command) {
24
49
  require(path.join(botDir, "setup.js"));
25
50
  break;
26
51
 
27
- case "stop":
28
- try {
29
- execSync('pkill -f "node.*bot.js"', { stdio: "inherit" });
30
- console.log("Stopped.");
31
- } catch (e) {
52
+ case "stop": {
53
+ const pids = findBotProcesses();
54
+ if (pids.length === 0) {
32
55
  console.log("Not running.");
56
+ } else {
57
+ for (const pid of pids) {
58
+ try { process.kill(parseInt(pid, 10), "SIGTERM"); } catch (e) {}
59
+ }
60
+ console.log("Stopped.");
33
61
  }
34
62
  break;
63
+ }
35
64
 
36
- case "logs":
65
+ case "logs": {
37
66
  const configDir = require(path.join(botDir, "config-dir"));
38
67
  const logFile = path.join(configDir, "bot.log");
39
68
  try {
40
- execSync(`tail -50 "${logFile}"`, { stdio: "inherit" });
69
+ const content = fs.readFileSync(logFile, "utf-8");
70
+ const lines = content.split("\n");
71
+ console.log(lines.slice(-50).join("\n"));
41
72
  } catch (e) {
42
73
  console.log("No logs found.");
43
74
  }
44
75
  break;
76
+ }
45
77
 
46
- case "status":
47
- try {
48
- execSync('pgrep -f "node.*bot.js"', { stdio: "pipe" });
49
- console.log("Running.");
50
- } catch (e) {
51
- console.log("Not running.");
52
- }
78
+ case "status": {
79
+ const pids = findBotProcesses();
80
+ console.log(pids.length > 0 ? `Running (PID: ${pids.join(", ")}).` : "Not running.");
53
81
  break;
82
+ }
54
83
 
55
84
  default:
56
85
  console.log(`
package/bot.js CHANGED
@@ -61,8 +61,11 @@ const FULL_PATH = [
61
61
  path.dirname(process.execPath),
62
62
  FFMPEG ? path.dirname(FFMPEG) : null,
63
63
  WHISPER_CLI ? path.dirname(WHISPER_CLI) : null,
64
- "/opt/homebrew/bin", "/usr/local/bin", "/usr/bin", "/bin", "/usr/sbin", "/sbin",
65
- ].filter(Boolean).join(":");
64
+ ...(process.platform === "win32"
65
+ ? [process.env.APPDATA, process.env.LOCALAPPDATA].filter(Boolean).map((p) => path.join(p, "npm"))
66
+ : ["/opt/homebrew/bin", "/usr/local/bin", "/usr/bin", "/bin", "/usr/sbin", "/sbin"]
67
+ ),
68
+ ].filter(Boolean).join(path.delimiter);
66
69
 
67
70
  const bot = new TelegramBot(TOKEN, {
68
71
  polling: {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@inetafrica/open-claudia",
3
- "version": "1.2.8",
3
+ "version": "1.3.0",
4
4
  "description": "Your always-on AI coding assistant — Claude Code via Telegram",
5
5
  "main": "bot.js",
6
6
  "bin": {
package/setup.js CHANGED
@@ -138,20 +138,17 @@ function detectPlatform() {
138
138
  return "other";
139
139
  }
140
140
 
141
- function findClaude() {
141
+ function findBinary(name) {
142
+ const cmd = process.platform === "win32" ? `where ${name} 2>nul` : `which ${name} 2>/dev/null`;
142
143
  try {
143
- const p = execSync("which claude 2>/dev/null || where claude 2>nul", { encoding: "utf-8" }).trim();
144
+ const p = execSync(cmd, { encoding: "utf-8" }).trim().split("\n")[0];
144
145
  return p || null;
145
146
  } catch (e) { return null; }
146
147
  }
147
148
 
148
- function findWhisper() {
149
- try { return execSync("which whisper-cli 2>/dev/null", { encoding: "utf-8" }).trim() || null; } catch (e) { return null; }
150
- }
151
-
152
- function findFfmpeg() {
153
- try { return execSync("which ffmpeg 2>/dev/null", { encoding: "utf-8" }).trim() || null; } catch (e) { return null; }
154
- }
149
+ function findClaude() { return findBinary("claude"); }
150
+ function findWhisper() { return findBinary("whisper-cli"); }
151
+ function findFfmpeg() { return findBinary("ffmpeg"); }
155
152
 
156
153
  function findWhisperModel() {
157
154
  const candidates = [