claude-telegram-bot 0.2.4 → 0.2.6

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
@@ -92,7 +92,7 @@ claude-telegram-bot ~/botconfigs/myproj/config.json
92
92
  | `projectDir` | Claude가 작업할 폴더의 절대경로 |
93
93
  | `claudeBin` | `which claude` 결과 (절대경로 권장) |
94
94
  | `permissionMode` | `plan`(읽기·계획만) / `acceptEdits`(편집 자동 승인) / `bypassPermissions`(쉘 포함 전부 자동) |
95
- | `model` | 비우면 기본 모델. `opus`, `sonnet` |
95
+ | `model` | 비우면 기본 모델. `opus`, `sonnet` 등. 런타임에 `/model`로 전환 가능(state에 저장) |
96
96
  | `lang` | (선택) UI 언어. 비우면 사용자별 자동 판별(기본 영어, 텔레그램이 한국어면 한국어). `"en"`/`"ko"`로 고정 가능 |
97
97
  | `name` | (선택) `/help`에 표시되는 봇 이름. 여러 봇 구분용 |
98
98
  | `persona` | (선택) 역할 시스템 프롬프트. 페르소나 봇 정의용 |
@@ -110,7 +110,7 @@ claude-telegram-bot ~/botconfigs/myproj/config.json
110
110
  - `테스트 돌려보고 통과하면 커밋하고 push 해줘`
111
111
  - `api.ts 에 에러 핸들링 추가해줘`
112
112
 
113
- 명령어: `/new`(맥락 초기화) · `/cron`(예약 작업 보기·추가·삭제) · `/restart`(문법 검사 후 재시작) · `/status`(봇 상태·버전) · `/id`(채팅 ID 확인) · `/help`(도움말)
113
+ 명령어: `/new`(맥락 초기화) · `/cron`(예약 작업 보기·추가·삭제) · `/restart`(문법 검사 후 재시작) · `/status`(봇 상태·버전) · `/model`(모델 보기·전환) · `/id`(채팅 ID 확인) · `/help`(도움말)
114
114
 
115
115
  > **`/restart`** 는 먼저 `bot.mjs` 에 `node --check` 를 돌려 **문법 오류가 있으면 재시작을 취소**합니다(잘못된 수정이 봇을 크래시 루프에 빠뜨리는 것 방지). 통과하면 프로세스를 종료하고, 다시 띄우는 건 프로세스 관리자에게 맡깁니다. [launchd 설정](#상시-실행-launchd)(`KeepAlive`)이면 바로 동작하고, 관리자 없이 `node bot.mjs` 로만 돌리면 그냥 멈춥니다. 재시작 후 대화 세션은 `state.json` 의 ID로 이어집니다.
116
116
 
package/README.md CHANGED
@@ -154,7 +154,7 @@ auth layer.)
154
154
  - `run the solver tests and commit + push if they pass`
155
155
  - `add an edge case to solve-2nd-floor-edges.ts`
156
156
 
157
- Commands: `/new` (reset context / new session) · `/cron` (list / add / remove scheduled tasks) · `/restart` (syntax-check & restart the bot) · `/status` (bot status & version) · `/id` (show chat ID) · `/help`.
157
+ Commands: `/new` (reset context / new session) · `/cron` (list / add / remove scheduled tasks) · `/restart` (syntax-check & restart the bot) · `/status` (bot status & version) · `/model` (view / switch the model) · `/id` (show chat ID) · `/help`.
158
158
 
159
159
  > **`/restart`** runs `node --check` on `bot.mjs` first and **aborts the restart if it has a syntax
160
160
  > error** (so a bad edit can't crash-loop the bot), then exits — relying on a process supervisor
@@ -184,7 +184,7 @@ Edit `config.json`:
184
184
  | `projectDir` | Absolute path to the working folder Claude runs in |
185
185
  | `claudeBin` | Output of `which claude` (absolute path recommended) |
186
186
  | `permissionMode` | `plan` / `acceptEdits` / `bypassPermissions` — see [Security](#security) |
187
- | `model` | Empty = default. Or `opus` / `sonnet`, etc. |
187
+ | `model` | Empty = default. Or `opus` / `sonnet`, etc. Override at runtime with `/model` (persists in state). |
188
188
  | `lang` | (optional) UI language. Empty = auto-detect per user (English default, Korean for Korean Telegram clients). Force with `"en"` / `"ko"`. |
189
189
  | `name` | (optional) Bot name shown in `/help` — handy for telling multiple bots apart |
190
190
  | `persona` | (optional) Role system prompt — defines a persona (developer/planner/…). See below |
package/bot.mjs CHANGED
@@ -129,6 +129,7 @@ const STR = {
129
129
  "• /cron — list tasks · /cron add <natural language> to add · /cron rm <id> to remove\n" +
130
130
  "• /restart — restart the bot (after a syntax check)\n" +
131
131
  "• /status — bot status & version\n" +
132
+ "• /model — view / switch the model\n" +
132
133
  "• /id — show this chat ID\n" +
133
134
  `\nWorking dir: ${cfg.projectDir}\nPermission mode: ${cfg.permissionMode}`,
134
135
  newSession: "🆕 Started a new conversation (previous context cleared).",
@@ -168,6 +169,12 @@ const STR = {
168
169
  `• Scheduled jobs: ${i.jobs}\n` +
169
170
  `• Project: ${i.projectDir}\n` +
170
171
  `• Permission: ${i.permissionMode}`,
172
+ modelStatus: (cur, list) =>
173
+ `🧠 Model: ${cur}\n` +
174
+ `Switch: ${list.map((x) => `/model ${x}`).join(" · ")} (or a full model id)\n` +
175
+ `/model default — clear the override`,
176
+ modelSet: (m) => `🧠 Model set to: ${m}`,
177
+ modelReset: (def) => `🧠 Model reset to default (${def}).`,
171
178
  },
172
179
  ko: {
173
180
  help: () =>
@@ -177,6 +184,7 @@ const STR = {
177
184
  "• /cron — 예약 작업 보기 · /cron add <자연어>로 추가 · /cron rm <번호>로 삭제\n" +
178
185
  "• /restart — 봇 재시작 (문법 검사 후 안전하게)\n" +
179
186
  "• /status — 봇 상태·버전 보기\n" +
187
+ "• /model — 모델 보기·전환\n" +
180
188
  "• /id — 이 채팅 ID 확인\n" +
181
189
  `\n작업 폴더: ${cfg.projectDir}\n권한 모드: ${cfg.permissionMode}`,
182
190
  newSession: "🆕 새 대화를 시작합니다 (이전 맥락 초기화).",
@@ -215,6 +223,12 @@ const STR = {
215
223
  `• 예약 작업: ${i.jobs}개\n` +
216
224
  `• 작업 폴더: ${i.projectDir}\n` +
217
225
  `• 권한 모드: ${i.permissionMode}`,
226
+ modelStatus: (cur, list) =>
227
+ `🧠 현재 모델: ${cur}\n` +
228
+ `전환: ${list.map((x) => `/model ${x}`).join(" · ")} (또는 전체 모델 ID)\n` +
229
+ `/model default — 오버라이드 해제`,
230
+ modelSet: (m) => `🧠 모델을 ${m} (으)로 설정했습니다.`,
231
+ modelReset: (def) => `🧠 모델을 기본값(${def})으로 되돌렸습니다.`,
218
232
  },
219
233
  };
220
234
  const t = (l, key, ...a) => {
@@ -222,6 +236,9 @@ const t = (l, key, ...a) => {
222
236
  return typeof v === "function" ? v(...a) : v;
223
237
  };
224
238
 
239
+ // /model 에서 보여줄 추천 별칭(claude CLI 가 별칭·전체 모델 ID 모두 허용).
240
+ const MODEL_SUGGESTIONS = ["opus", "sonnet", "haiku"];
241
+
225
242
  // /(슬래시) 자동완성 메뉴용 명령 목록 (언어별). setMyCommands 로 등록.
226
243
  const COMMANDS = {
227
244
  en: [
@@ -229,6 +246,7 @@ const COMMANDS = {
229
246
  { command: "cron", description: "List / add / remove scheduled tasks" },
230
247
  { command: "restart", description: "Restart the bot (after syntax check)" },
231
248
  { command: "status", description: "Bot status / version" },
249
+ { command: "model", description: "View / switch the model" },
232
250
  { command: "id", description: "Show this chat ID" },
233
251
  { command: "help", description: "Help" },
234
252
  ],
@@ -237,6 +255,7 @@ const COMMANDS = {
237
255
  { command: "cron", description: "예약 작업 보기·추가·삭제" },
238
256
  { command: "restart", description: "봇 재시작 (문법 검사 후)" },
239
257
  { command: "status", description: "봇 상태·버전 보기" },
258
+ { command: "model", description: "모델 보기·전환" },
240
259
  { command: "id", description: "이 채팅 ID 확인" },
241
260
  { command: "help", description: "도움말" },
242
261
  ],
@@ -260,7 +279,7 @@ function saveState(s) {
260
279
  }
261
280
  }
262
281
  migrateData(); // 루트 직하 → .claude-bot/ 1회 이주(있으면) 후 state 로드
263
- let state = loadState(); // { sessionId?, cron?: [{ id, cron, prompt, label? }], restartNotify? }
282
+ let state = loadState(); // { sessionId?, cron?: [{ id, cron, prompt, label? }], restartNotify?, model? }
264
283
 
265
284
  // ── 텔레그램 헬퍼 ─────────────────────────────────────────────────────────
266
285
  async function tg(method, body) {
@@ -360,7 +379,7 @@ async function send(chatId, text) {
360
379
  }
361
380
 
362
381
  // ── Claude 실행 ───────────────────────────────────────────────────────────
363
- function runClaude(prompt, sessionId) {
382
+ function runClaude(prompt, sessionId, opts = {}) {
364
383
  return new Promise((resolve) => {
365
384
  const args = [
366
385
  "-p",
@@ -370,13 +389,18 @@ function runClaude(prompt, sessionId) {
370
389
  "--permission-mode",
371
390
  cfg.permissionMode || "acceptEdits",
372
391
  ];
392
+ const model = state.model || cfg.model; // /model 로 바꾸면 state.model 우선
373
393
  const brevity =
374
394
  cfg.appendSystemPrompt ??
375
395
  "This reply is delivered over Telegram. Be concise — short paragraphs and lists, no filler intro/summary, avoid large tables. Reply in the user's language.";
376
- // 페르소나(cfg.persona) + 간결 지침을 함께 주입 → 멀티 (역할별) 운영용
377
- const appendSys = [cfg.persona, brevity].filter(Boolean).join("\n\n");
396
+ // opts.modelHint: 현재 모델을 주입 → 답변 끝에 상위 모델 권유 제안(판단은 Claude 본인)
397
+ const modelHint = opts.modelHint
398
+ ? `Current model: ${model || "claude (default)"}. Model tiers (low→high): haiku → sonnet → opus. 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\`) for a stronger answer. Omit the suggestion for simple questions.`
399
+ : null;
400
+ // 페르소나(cfg.persona) + 간결 지침 + 모델 힌트를 함께 주입 → 멀티 봇(역할별) 운영용
401
+ const appendSys = [cfg.persona, brevity, modelHint].filter(Boolean).join("\n\n");
378
402
  if (appendSys) args.push("--append-system-prompt", appendSys);
379
- if (cfg.model) args.push("--model", cfg.model);
403
+ if (model) args.push("--model", model);
380
404
  if (sessionId) args.push("--resume", sessionId);
381
405
 
382
406
  const child = spawn(cfg.claudeBin || "claude", args, {
@@ -684,7 +708,7 @@ async function handle(msg) {
684
708
  t(l, "status", {
685
709
  version: VERSION,
686
710
  name: cfg.name || "claude-telegram-bot",
687
- model: cfg.model || (l === "ko" ? "(기본값)" : "(default)"),
711
+ model: state.model || cfg.model || (l === "ko" ? "(기본값)" : "(default)"),
688
712
  hasSession: Boolean(state.sessionId),
689
713
  jobs: schedule.length,
690
714
  projectDir: cfg.projectDir,
@@ -693,6 +717,24 @@ async function handle(msg) {
693
717
  );
694
718
  return;
695
719
  }
720
+ if (text === "/model" || text.startsWith("/model ")) {
721
+ const arg = text.slice(6).trim();
722
+ if (!arg) {
723
+ const cur = state.model || cfg.model || (l === "ko" ? "(기본값)" : "(default)");
724
+ await send(chatId, t(l, "modelStatus", cur, MODEL_SUGGESTIONS));
725
+ return;
726
+ }
727
+ if (arg === "default" || arg === "reset") {
728
+ state.model = undefined;
729
+ saveState(state);
730
+ await send(chatId, t(l, "modelReset", cfg.model || (l === "ko" ? "기본값" : "default")));
731
+ return;
732
+ }
733
+ state.model = arg;
734
+ saveState(state);
735
+ await send(chatId, t(l, "modelSet", arg));
736
+ return;
737
+ }
696
738
  if (text === "/cron" || text.startsWith("/cron ")) {
697
739
  await handleCron(chatId, text.slice(5).trim(), l);
698
740
  return;
@@ -750,7 +792,7 @@ async function handle(msg) {
750
792
  await send(chatId, t(l, "attachFail", e.message));
751
793
  }
752
794
  }
753
- const res = await runClaude(prompt, state.sessionId);
795
+ const res = await runClaude(prompt, state.sessionId, { modelHint: true });
754
796
  if (res.sessionId) {
755
797
  state.sessionId = res.sessionId;
756
798
  saveState(state);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-telegram-bot",
3
- "version": "0.2.4",
3
+ "version": "0.2.6",
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": {