@gonzih/cc-tg 0.2.18 → 0.2.20

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/dist/bot.d.ts CHANGED
@@ -38,10 +38,12 @@ export declare class CcTgBot {
38
38
  /** Kill cc-agent PIDs with SIGTERM. Returns the list of killed PIDs. */
39
39
  private killCcAgent;
40
40
  private handleReloadMcp;
41
+ private handleMcpStatus;
41
42
  private handleMcpVersion;
42
43
  private handleClearNpxCache;
43
44
  private handleRestart;
44
45
  private handleGetFile;
46
+ private callCcAgentTool;
45
47
  private killSession;
46
48
  stop(): void;
47
49
  }
package/dist/bot.js CHANGED
@@ -6,7 +6,7 @@ import TelegramBot from "node-telegram-bot-api";
6
6
  import { existsSync, createWriteStream, mkdirSync, statSync, readdirSync, readFileSync, writeFileSync } from "fs";
7
7
  import { resolve, basename, join } from "path";
8
8
  import os from "os";
9
- import { execSync } from "child_process";
9
+ import { execSync, spawn } from "child_process";
10
10
  import https from "https";
11
11
  import http from "http";
12
12
  import { ClaudeProcess, extractText } from "./claude.js";
@@ -20,6 +20,7 @@ const BOT_COMMANDS = [
20
20
  { command: "help", description: "Show all available commands" },
21
21
  { command: "cron", description: "Manage cron jobs — add/list/edit/remove/clear" },
22
22
  { command: "reload_mcp", description: "Restart the cc-agent MCP server process" },
23
+ { command: "mcp_status", description: "Check MCP server connection status" },
23
24
  { command: "mcp_version", description: "Show cc-agent npm version and npx cache info" },
24
25
  { command: "clear_npx_cache", description: "Clear npx cache and restart MCP to pick up latest version" },
25
26
  { command: "restart", description: "Restart the bot process in-place" },
@@ -65,6 +66,28 @@ function formatCronCostFooter(usage) {
65
66
  const cost = computeCostUsd(usage);
66
67
  return `\nšŸ’° Cron cost: $${cost.toFixed(4)} (${formatTokens(usage.inputTokens)} in / ${formatTokens(usage.outputTokens)} out tokens)`;
67
68
  }
69
+ function formatAgentCostSummary(text) {
70
+ try {
71
+ const data = JSON.parse(text);
72
+ const totalCost = (data.total_cost_usd ?? data.total_cost ?? 0);
73
+ const totalJobs = (data.total_jobs ?? data.job_count ?? 0);
74
+ const byRepo = (data.by_repo ?? []);
75
+ const lines = [
76
+ "šŸ¤– Agent jobs (all time)",
77
+ `Total: $${totalCost.toFixed(2)} across ${totalJobs} jobs`,
78
+ ];
79
+ for (const entry of byRepo) {
80
+ const repo = (entry.repo ?? entry.repository ?? "unknown");
81
+ const cost = (entry.cost_usd ?? entry.cost ?? 0);
82
+ const jobs = (entry.job_count ?? entry.jobs ?? 0);
83
+ lines.push(` ${repo}: $${cost.toFixed(2)} (${jobs} jobs)`);
84
+ }
85
+ return lines.join("\n");
86
+ }
87
+ catch {
88
+ return `šŸ¤– Agent jobs (all time)\n${text}`;
89
+ }
90
+ }
68
91
  class CostStore {
69
92
  costs = new Map();
70
93
  storePath;
@@ -214,6 +237,11 @@ export class CcTgBot {
214
237
  await this.handleReloadMcp(chatId);
215
238
  return;
216
239
  }
240
+ // /mcp_status — run `claude mcp list` and show connection status
241
+ if (text === "/mcp_status") {
242
+ await this.handleMcpStatus(chatId);
243
+ return;
244
+ }
217
245
  // /mcp_version — show published npm version and cached npx entries
218
246
  if (text === "/mcp_version") {
219
247
  await this.handleMcpVersion(chatId);
@@ -237,7 +265,17 @@ export class CcTgBot {
237
265
  // /cost — show session token usage and cost
238
266
  if (text === "/cost") {
239
267
  const cost = this.costStore.get(chatId);
240
- await this.bot.sendMessage(chatId, formatCostReport(cost));
268
+ let reply = formatCostReport(cost);
269
+ try {
270
+ const rawSummary = await this.callCcAgentTool("cost_summary");
271
+ if (rawSummary) {
272
+ reply += "\n\n" + formatAgentCostSummary(rawSummary);
273
+ }
274
+ }
275
+ catch (err) {
276
+ console.error("[cost] cc-agent cost_summary failed:", err.message);
277
+ }
278
+ await this.bot.sendMessage(chatId, reply);
241
279
  return;
242
280
  }
243
281
  const session = this.getOrCreateSession(chatId);
@@ -809,12 +847,30 @@ export class CcTgBot {
809
847
  return pids;
810
848
  }
811
849
  async handleReloadMcp(chatId) {
850
+ await this.bot.sendMessage(chatId, "Clearing npx cache and reloading MCP...");
851
+ try {
852
+ const home = process.env.HOME ?? "~";
853
+ execSync(`rm -rf "${home}/.npm/_npx/"`, { encoding: "utf8", shell: "/bin/sh" });
854
+ console.log("[mcp] cleared ~/.npm/_npx/");
855
+ }
856
+ catch (err) {
857
+ await this.bot.sendMessage(chatId, `Warning: failed to clear npx cache: ${err.message}`);
858
+ }
812
859
  const pids = this.killCcAgent();
813
860
  if (pids.length === 0) {
814
- await this.bot.sendMessage(chatId, "No cc-agent process found. MCP will start fresh on the next agent call.");
861
+ await this.bot.sendMessage(chatId, "NPX cache cleared. No cc-agent process found — MCP will start fresh on the next agent call.");
815
862
  return;
816
863
  }
817
- await this.bot.sendMessage(chatId, `Sent SIGTERM to cc-agent (pid${pids.length > 1 ? "s" : ""}: ${pids.join(", ")}).\nMCP restarted. New process will load on next agent call.`);
864
+ await this.bot.sendMessage(chatId, `NPX cache cleared. Sent SIGTERM to cc-agent (pid${pids.length > 1 ? "s" : ""}: ${pids.join(", ")}).\nMCP restarted. New process will load on next agent call.`);
865
+ }
866
+ async handleMcpStatus(chatId) {
867
+ try {
868
+ const output = execSync("claude mcp list", { encoding: "utf8", shell: "/bin/sh" }).trim();
869
+ await this.bot.sendMessage(chatId, `MCP server status:\n\n${output || "(no output)"}`);
870
+ }
871
+ catch (err) {
872
+ await this.bot.sendMessage(chatId, `Failed to run claude mcp list: ${err.message}`);
873
+ }
818
874
  }
819
875
  async handleMcpVersion(chatId) {
820
876
  let npmVersion = "unknown";
@@ -890,6 +946,77 @@ export class CcTgBot {
890
946
  }
891
947
  await this.bot.sendDocument(chatId, filePath);
892
948
  }
949
+ callCcAgentTool(toolName, args = {}) {
950
+ return new Promise((resolve) => {
951
+ let settled = false;
952
+ const done = (val) => {
953
+ if (!settled) {
954
+ settled = true;
955
+ resolve(val);
956
+ }
957
+ };
958
+ let proc;
959
+ try {
960
+ proc = spawn("npx", ["-y", "@gonzih/cc-agent@latest"], {
961
+ env: { ...process.env },
962
+ stdio: ["pipe", "pipe", "pipe"],
963
+ });
964
+ }
965
+ catch (err) {
966
+ console.error("[mcp] failed to spawn cc-agent:", err.message);
967
+ done(null);
968
+ return;
969
+ }
970
+ const timeout = setTimeout(() => {
971
+ console.warn("[mcp] cc-agent tool call timed out");
972
+ proc.kill();
973
+ done(null);
974
+ }, 30_000);
975
+ let buffer = "";
976
+ const sendMsg = (msg) => { proc.stdin.write(JSON.stringify(msg) + "\n"); };
977
+ sendMsg({
978
+ jsonrpc: "2.0", id: 1, method: "initialize",
979
+ params: { protocolVersion: "2024-11-05", capabilities: {}, clientInfo: { name: "cc-tg", version: "1.0.0" } },
980
+ });
981
+ proc.stdout.on("data", (chunk) => {
982
+ buffer += chunk.toString();
983
+ const lines = buffer.split("\n");
984
+ buffer = lines.pop() ?? "";
985
+ for (const line of lines) {
986
+ if (!line.trim())
987
+ continue;
988
+ try {
989
+ const msg = JSON.parse(line);
990
+ if (msg.id === 1 && "result" in msg) {
991
+ sendMsg({ jsonrpc: "2.0", method: "notifications/initialized" });
992
+ sendMsg({ jsonrpc: "2.0", id: 2, method: "tools/call", params: { name: toolName, arguments: args } });
993
+ }
994
+ else if (msg.id === 2) {
995
+ clearTimeout(timeout);
996
+ if (msg.error) {
997
+ console.error("[mcp] cost_summary error:", JSON.stringify(msg.error));
998
+ proc.kill();
999
+ done(null);
1000
+ return;
1001
+ }
1002
+ const result = msg.result;
1003
+ const content = result?.content;
1004
+ const text = (content ?? []).filter((b) => b.type === "text").map((b) => b.text).join("");
1005
+ proc.kill();
1006
+ done(text || null);
1007
+ }
1008
+ }
1009
+ catch { /* ignore non-JSON lines */ }
1010
+ }
1011
+ });
1012
+ proc.on("error", (err) => {
1013
+ console.error("[mcp] cc-agent spawn error:", err.message);
1014
+ clearTimeout(timeout);
1015
+ done(null);
1016
+ });
1017
+ proc.on("exit", () => { clearTimeout(timeout); done(null); });
1018
+ });
1019
+ }
893
1020
  killSession(chatId, keepCrons = true) {
894
1021
  const session = this.sessions.get(chatId);
895
1022
  if (session) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gonzih/cc-tg",
3
- "version": "0.2.18",
3
+ "version": "0.2.20",
4
4
  "description": "Claude Code Telegram bot — chat with Claude Code via Telegram",
5
5
  "type": "module",
6
6
  "bin": {