claude-telegram-bot 0.3.0 → 0.3.2

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.ko.md CHANGED
@@ -68,11 +68,10 @@ OpenClaw처럼 웹 UI까지 갖춘 구성을 써봤다면, 이 프로젝트는
68
68
  **npx로 바로 실행**
69
69
 
70
70
  ```sh
71
- npx claude-telegram-bot init # 현재 폴더에 config.json 생성
72
- npx claude-telegram-bot init mybot.json # 파일명 직접 지정도 가능
71
+ npx claude-telegram-bot init # 현재 폴더에 mybot.json 생성
72
+ npx claude-telegram-bot init myapp.json # 파일명 직접 지정도 가능
73
73
  # 설정 편집 (token, projectDir 등)
74
- npx claude-telegram-bot # config.json 으로 실행
75
- npx claude-telegram-bot mybot.json # 또는 경로를 직접 전달
74
+ npx claude-telegram-bot # mybot.json 으로 실행 (없으면 config.json 폴백)
76
75
  ```
77
76
 
78
77
  **전역 설치 (상시 가동에 권장)**
@@ -80,8 +79,8 @@ npx claude-telegram-bot mybot.json # 또는 경로를 직접 전달
80
79
  ```sh
81
80
  npm i -g claude-telegram-bot
82
81
 
83
- claude-telegram-bot init ~/botconfigs/myproj # config.json 생성
84
- claude-telegram-bot init ~/botconfigs/myproj/mybot.json # 또는 파일명 지정
82
+ claude-telegram-bot init ~/botconfigs/myproj # mybot.json 생성
83
+ claude-telegram-bot init ~/botconfigs/myproj/myapp.json # 또는 파일명 지정
85
84
  # 설정 편집
86
85
  claude-telegram-bot ~/botconfigs/myproj/mybot.json
87
86
  ```
@@ -90,7 +89,7 @@ claude-telegram-bot ~/botconfigs/myproj/mybot.json
90
89
 
91
90
  ## 설정
92
91
 
93
- `config.json`의 키는 다음과 같습니다.
92
+ `mybot.json`(또는 사용 중인 config 파일)의 키는 다음과 같습니다.
94
93
 
95
94
  | 키 | 설명 |
96
95
  |---|---|
package/README.md CHANGED
@@ -118,11 +118,10 @@ Prerequisites: **Node 18+** and the **`claude` CLI installed and authenticated**
118
118
  **Option A — npx (no install)**
119
119
 
120
120
  ```sh
121
- npx claude-telegram-bot init # writes ./config.json
122
- npx claude-telegram-bot init mybot.json # or pick your own filename
121
+ npx claude-telegram-bot init # writes ./mybot.json
122
+ npx claude-telegram-bot init myapp.json # or pick your own filename
123
123
  # edit the config (token, projectDir, …)
124
- npx claude-telegram-bot # runs ./config.json
125
- npx claude-telegram-bot mybot.json # or pass the path directly
124
+ npx claude-telegram-bot # runs ./mybot.json (falls back to config.json)
126
125
  ```
127
126
 
128
127
  **Option B — global install (recommended for an always-on daemon)**
@@ -130,8 +129,8 @@ npx claude-telegram-bot mybot.json # or pass the path directly
130
129
  ```sh
131
130
  npm i -g claude-telegram-bot
132
131
 
133
- claude-telegram-bot init ~/botconfigs/myproj # writes ~/botconfigs/myproj/config.json
134
- claude-telegram-bot init ~/botconfigs/myproj/mybot.json # or a custom filename
132
+ claude-telegram-bot init ~/botconfigs/myproj # writes ~/botconfigs/myproj/mybot.json
133
+ claude-telegram-bot init ~/botconfigs/myproj/myapp.json # or a custom filename
135
134
  # edit the config (token, projectDir, …)
136
135
  claude-telegram-bot ~/botconfigs/myproj/mybot.json
137
136
  ```
@@ -152,7 +151,7 @@ Run several projects/personas by making one config file each and passing its pat
152
151
  leave `allowedChatId` empty for now.
153
152
 
154
153
  **2) Find your chatId and lock the bot to it** — Start the bot (`claude-telegram-bot …`), send it any
155
- message in Telegram; it replies with this chat's `chatId`. Put that number into `config.json`
154
+ message in Telegram; it replies with this chat's `chatId`. Put that number into `mybot.json`
156
155
  `allowedChatId` and restart. Now only you can use it. (See [Security](#security) — this is your only
157
156
  auth layer.)
158
157
 
@@ -175,18 +174,18 @@ Commands: `/new` (reset context / new session) · `/stop` (stop current task; `-
175
174
 
176
175
  **4) Keep it always on (optional)** — see [Always-on with launchd](#always-on-with-launchd-macos).
177
176
 
178
- > **From source** (for hacking on the bot): clone the repo, `cp config.example.json config.json`,
179
- > then `node bot.mjs [config.json]`. Same behavior as the CLI.
177
+ > **From source** (for hacking on the bot): clone the repo, `cp config.example.json mybot.json`,
178
+ > then `node bot.mjs [mybot.json]`. Same behavior as the CLI.
180
179
 
181
180
  ---
182
181
 
183
182
  ## Configuration
184
183
 
185
184
  ```sh
186
- cp config.example.json config.json
185
+ cp config.example.json mybot.json
187
186
  ```
188
187
 
189
- Edit `config.json`:
188
+ Edit `mybot.json`:
190
189
 
191
190
  | Key | Description |
192
191
  |---|---|
@@ -273,7 +272,7 @@ Config-defined jobs still require a restart to change; only chat-added jobs are
273
272
 
274
273
  The code is project-agnostic: make **one config file per project** and run several at once.
275
274
 
276
- - Run: `node bot.mjs /absolute/path/to/project.config.json` (no arg → `./config.json`)
275
+ - Run: `node bot.mjs /absolute/path/to/project.config.json` (no arg → `./mybot.json`, fallback `./config.json`)
277
276
  - `state.json` and `attachments/` live in the **config file's folder**, so projects don't mix.
278
277
  - **Note**: Telegram allows only one poller per token → each project needs its **own BotFather
279
278
  token**.
@@ -299,7 +298,7 @@ One codebase, **a separate config file per role**.
299
298
  shell-using bot (`bypassPermissions`) to **just one** to avoid concurrent-edit conflicts. For
300
299
  read/plan-only, use `plan`.
301
300
  - **Session isolation**: the `state` filename is derived from the config name
302
- (`config.json` → `state.json`, `dev.config.json` → `dev.config.state.json`), so multiple configs
301
+ (`mybot.json` → `mybot.state.json`, `dev.config.json` → `dev.config.state.json`), so multiple configs
303
302
  in one folder don't share context.
304
303
  - **One token per bot**: each bot needs its own BotFather token (`allowedChatId` can be the same).
305
304
 
package/bot.mjs CHANGED
@@ -58,7 +58,7 @@ Requires: the claude CLI installed and authenticated on the host.`);
58
58
  }
59
59
  if (a === "init") {
60
60
  const arg = process.argv[3];
61
- const target = arg?.endsWith(".json") ? resolve(arg) : join(arg || process.cwd(), "config.json");
61
+ const target = arg?.endsWith(".json") ? resolve(arg) : join(arg || process.cwd(), "mybot.json");
62
62
  if (existsSync(target)) {
63
63
  console.error(`Already exists: ${target}`);
64
64
  process.exit(1);
@@ -71,8 +71,9 @@ Requires: the claude CLI installed and authenticated on the host.`);
71
71
 
72
72
  // Config path via arg or BOT_CONFIG env so one shared codebase can drive many
73
73
  // projects; state + attachments live next to that config, keeping projects
74
- // isolated. Falls back to ./config.json for the single-project setup.
75
- const CONFIG_PATH = process.argv[2] || process.env.BOT_CONFIG || join(HERE, "config.json");
74
+ // isolated. Defaults to mybot.json, falls back to config.json for existing setups.
75
+ const _defaultCfg = existsSync(join(HERE, "mybot.json")) ? join(HERE, "mybot.json") : join(HERE, "config.json");
76
+ const CONFIG_PATH = process.argv[2] || process.env.BOT_CONFIG || _defaultCfg;
76
77
  const DATA_DIR = dirname(CONFIG_PATH);
77
78
  // 데이터(state·attachments)는 config 폴더 아래 숨김 폴더 .claude-bot/ 에 모은다.
78
79
  // state 파일명은 config 이름에서 파생 → 여러 페르소나 config 가 한 .claude-bot/ 를 공유해도 안 섞임
@@ -82,6 +83,7 @@ const stateFile = stateBase === "config" ? "state.json" : `${stateBase}.state.js
82
83
  const BOT_DIR = join(DATA_DIR, ".claude-bot");
83
84
  const STATE_PATH = join(BOT_DIR, stateFile);
84
85
  const ATTACH_DIR = join(BOT_DIR, "attachments");
86
+ const MEMORY_PATH = join(BOT_DIR, "memory.md"); // /new 로 초기화해도 유지되는 퍼시스턴트 메모리
85
87
  const LEGACY_STATE_PATH = join(DATA_DIR, stateFile); // 구버전(루트 직하) 호환
86
88
  const LEGACY_ATTACH_DIR = join(DATA_DIR, "attachments");
87
89
 
@@ -129,6 +131,8 @@ const STR = {
129
131
  "• /new — reset conversation context (new session)\n" +
130
132
  "• /stop — stop the current task · /stop --reset to also roll back the session\n" +
131
133
  "• /cron — list tasks · /cron add <natural language> to add · /cron rm <id> to remove\n" +
134
+ "• /remember <text> — save to persistent memory (survives /new)\n" +
135
+ "• /memory — view memory · /memory clear to wipe\n" +
132
136
  "• /restart — restart the bot (after a syntax check)\n" +
133
137
  "• /status — bot status & version\n" +
134
138
  "• /model — view / switch the model\n" +
@@ -182,6 +186,12 @@ const STR = {
182
186
  `/model default — clear the override`,
183
187
  modelSet: (m) => `🧠 Model set to: ${m}`,
184
188
  modelReset: (def) => `🧠 Model reset to default (${def}).`,
189
+ memoryEmpty: "No memory yet. Use `/remember <text>` to add.",
190
+ memoryShow: (m) => `💾 Memory:\n\`\`\`\n${m}\n\`\`\``,
191
+ memoryCleared: "🧹 Memory cleared.",
192
+ remembered: "💾 Saved to memory.",
193
+ rememberUsage: "Usage: /remember <text to remember>",
194
+ memoryUsage: "Usage: /memory · /memory clear",
185
195
  },
186
196
  ko: {
187
197
  help: () =>
@@ -190,6 +200,8 @@ const STR = {
190
200
  "• /new — 대화 맥락 초기화 (새 세션)\n" +
191
201
  "• /stop — 진행 중인 작업 중단 · /stop --reset 으로 세션도 되돌리기\n" +
192
202
  "• /cron — 예약 작업 보기 · /cron add <자연어>로 추가 · /cron rm <번호>로 삭제\n" +
203
+ "• /remember <내용> — 퍼시스턴트 메모리에 저장 (/new 로 초기화해도 유지)\n" +
204
+ "• /memory — 메모리 보기 · /memory clear 로 삭제\n" +
193
205
  "• /restart — 봇 재시작 (문법 검사 후 안전하게)\n" +
194
206
  "• /status — 봇 상태·버전 보기\n" +
195
207
  "• /model — 모델 보기·전환\n" +
@@ -242,6 +254,12 @@ const STR = {
242
254
  `/model default — 오버라이드 해제`,
243
255
  modelSet: (m) => `🧠 모델을 ${m} (으)로 설정했습니다.`,
244
256
  modelReset: (def) => `🧠 모델을 기본값(${def})으로 되돌렸습니다.`,
257
+ memoryEmpty: "저장된 메모리가 없습니다. `/remember <내용>`으로 추가하세요.",
258
+ memoryShow: (m) => `💾 메모리:\n\`\`\`\n${m}\n\`\`\``,
259
+ memoryCleared: "🧹 메모리를 삭제했습니다.",
260
+ remembered: "💾 메모리에 저장했습니다.",
261
+ rememberUsage: "사용법: /remember <기억할 내용>",
262
+ memoryUsage: "사용법: /memory · /memory clear",
245
263
  },
246
264
  };
247
265
  const t = (l, key, ...a) => {
@@ -257,6 +275,8 @@ const COMMANDS = {
257
275
  en: [
258
276
  { command: "new", description: "Reset context (new session)" },
259
277
  { command: "stop", description: "Stop the current task (--reset to roll back session)" },
278
+ { command: "remember", description: "Save to persistent memory (survives /new)" },
279
+ { command: "memory", description: "View or clear persistent memory" },
260
280
  { command: "cron", description: "List / add / remove scheduled tasks" },
261
281
  { command: "restart", description: "Restart the bot (after syntax check)" },
262
282
  { command: "status", description: "Bot status / version" },
@@ -267,6 +287,8 @@ const COMMANDS = {
267
287
  ko: [
268
288
  { command: "new", description: "대화 맥락 초기화 (새 세션)" },
269
289
  { command: "stop", description: "작업 중단 (--reset 으로 세션 되돌리기)" },
290
+ { command: "remember", description: "퍼시스턴트 메모리에 저장 (/new 후에도 유지)" },
291
+ { command: "memory", description: "메모리 보기·삭제" },
270
292
  { command: "cron", description: "예약 작업 보기·추가·삭제" },
271
293
  { command: "restart", description: "봇 재시작 (문법 검사 후)" },
272
294
  { command: "status", description: "봇 상태·버전 보기" },
@@ -293,6 +315,15 @@ function checkLocalLock() {
293
315
  }
294
316
  }
295
317
 
318
+ // ── 퍼시스턴트 메모리 ─────────────────────────────────────────────────────
319
+ // /new 로 세션을 초기화해도 유지되는 메모리. runClaude 시 시스템 프롬프트에 주입.
320
+ function loadMemory() {
321
+ try { return readFileSync(MEMORY_PATH, "utf8").trim(); } catch { return ""; }
322
+ }
323
+ function saveMemory(content) {
324
+ writeFileSync(MEMORY_PATH, content);
325
+ }
326
+
296
327
  // ── 상태 (세션 이어가기용) ────────────────────────────────────────────────
297
328
  function loadState() {
298
329
  // 새 경로(.claude-bot/) 우선, 없으면 구버전 루트 경로로 폴백(이주 실패 시 안전망).
@@ -429,8 +460,11 @@ function runClaude(prompt, sessionId, opts = {}) {
429
460
  const modelHint = opts.modelHint
430
461
  ? `Current model: ${model || "claude (default)"}. Model tiers (low→high): haiku → sonnet → opus → fable. If this question seems to require more capability than the current model, append one short line at the very end of your reply: 💡 \`/model sonnet\` (or \`/model opus\`, \`/model fable\`) for a stronger answer. Omit the suggestion for simple questions.`
431
462
  : null;
432
- // 페르소나(cfg.persona) + 간결 지침 + 모델 힌트를 함께 주입 → 멀티 봇(역할별) 운영용
433
- const appendSys = [cfg.persona, brevity, modelHint].filter(Boolean).join("\n\n");
463
+ // opts.injectMemory: 퍼시스턴트 메모리를 시스템 프롬프트에 주입 (/new 초기화해도 유지)
464
+ const mem = opts.injectMemory ? loadMemory() : "";
465
+ const memoryBlock = mem ? `## Persistent memory (survives /new)\n${mem}` : null;
466
+ // 페르소나(cfg.persona) + 간결 지침 + 모델 힌트 + 메모리를 함께 주입
467
+ const appendSys = [cfg.persona, brevity, modelHint, memoryBlock].filter(Boolean).join("\n\n");
434
468
  if (appendSys) args.push("--append-system-prompt", appendSys);
435
469
  if (model) args.push("--model", model);
436
470
  if (sessionId) args.push("--resume", sessionId);
@@ -820,6 +854,25 @@ async function handle(msg) {
820
854
  await send(chatId, t(l, reset ? "stopReset" : "stopOk"));
821
855
  return;
822
856
  }
857
+ if (text.startsWith("/remember ")) {
858
+ const content = text.slice(10).trim();
859
+ if (!content) { await send(chatId, t(l, "rememberUsage")); return; }
860
+ const existing = loadMemory();
861
+ saveMemory(existing ? `${existing}\n- ${content}` : `- ${content}`);
862
+ await send(chatId, t(l, "remembered"));
863
+ return;
864
+ }
865
+ if (text === "/memory" || text.startsWith("/memory ")) {
866
+ const arg = text.slice(7).trim();
867
+ if (arg === "clear") {
868
+ saveMemory("");
869
+ await send(chatId, t(l, "memoryCleared"));
870
+ return;
871
+ }
872
+ const mem = loadMemory();
873
+ await send(chatId, mem ? t(l, "memoryShow", mem) : t(l, "memoryEmpty"));
874
+ return;
875
+ }
823
876
 
824
877
  if (busy) {
825
878
  msgQueue.push({ msg, receivedAt: Date.now() });
@@ -854,7 +907,7 @@ async function handle(msg) {
854
907
  }
855
908
  }
856
909
  prevSessionId = state.sessionId; // /stop --reset 복원 대상 저장
857
- const res = await runClaude(prompt, state.sessionId, { modelHint: true, trackChild: true });
910
+ const res = await runClaude(prompt, state.sessionId, { modelHint: true, trackChild: true, injectMemory: true });
858
911
  if (res.sessionId) {
859
912
  state.sessionId = res.sessionId;
860
913
  saveState(state);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-telegram-bot",
3
- "version": "0.3.0",
3
+ "version": "0.3.2",
4
4
  "description": "Drive Claude Code from Telegram — messages run headless `claude -p` in a project dir and replies come back to the chat. Zero dependencies.",
5
5
  "type": "module",
6
6
  "bin": {