@selucas12/cheesy 2.0.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.
Files changed (292) hide show
  1. package/.env.example +22 -0
  2. package/CHANGELOG.md +268 -0
  3. package/LICENSE +21 -0
  4. package/README.md +394 -0
  5. package/ccgram.service +24 -0
  6. package/config/channels.json +58 -0
  7. package/config/default.json +27 -0
  8. package/config/defaults/config.json +16 -0
  9. package/config/defaults/i18n.json +32 -0
  10. package/config/email-template.json +31 -0
  11. package/config/test-with-subagent.json +16 -0
  12. package/config/user.json +27 -0
  13. package/dist/claude-hook-notify.d.ts +7 -0
  14. package/dist/claude-hook-notify.d.ts.map +1 -0
  15. package/dist/claude-hook-notify.js +154 -0
  16. package/dist/claude-hook-notify.js.map +1 -0
  17. package/dist/claude-remote.d.ts +50 -0
  18. package/dist/claude-remote.d.ts.map +1 -0
  19. package/dist/claude-remote.js +927 -0
  20. package/dist/claude-remote.js.map +1 -0
  21. package/dist/elicitation-notify.d.ts +20 -0
  22. package/dist/elicitation-notify.d.ts.map +1 -0
  23. package/dist/elicitation-notify.js +241 -0
  24. package/dist/elicitation-notify.js.map +1 -0
  25. package/dist/enhanced-hook-notify.d.ts +23 -0
  26. package/dist/enhanced-hook-notify.d.ts.map +1 -0
  27. package/dist/enhanced-hook-notify.js +402 -0
  28. package/dist/enhanced-hook-notify.js.map +1 -0
  29. package/dist/permission-denied-notify.d.ts +11 -0
  30. package/dist/permission-denied-notify.d.ts.map +1 -0
  31. package/dist/permission-denied-notify.js +193 -0
  32. package/dist/permission-denied-notify.js.map +1 -0
  33. package/dist/permission-hook.d.ts +15 -0
  34. package/dist/permission-hook.d.ts.map +1 -0
  35. package/dist/permission-hook.js +386 -0
  36. package/dist/permission-hook.js.map +1 -0
  37. package/dist/pre-compact-notify.d.ts +13 -0
  38. package/dist/pre-compact-notify.d.ts.map +1 -0
  39. package/dist/pre-compact-notify.js +197 -0
  40. package/dist/pre-compact-notify.js.map +1 -0
  41. package/dist/prompt-bridge.d.ts +50 -0
  42. package/dist/prompt-bridge.d.ts.map +1 -0
  43. package/dist/prompt-bridge.js +173 -0
  44. package/dist/prompt-bridge.js.map +1 -0
  45. package/dist/question-notify.d.ts +17 -0
  46. package/dist/question-notify.d.ts.map +1 -0
  47. package/dist/question-notify.js +356 -0
  48. package/dist/question-notify.js.map +1 -0
  49. package/dist/setup.d.ts +10 -0
  50. package/dist/setup.d.ts.map +1 -0
  51. package/dist/setup.js +649 -0
  52. package/dist/setup.js.map +1 -0
  53. package/dist/smart-monitor.d.ts +7 -0
  54. package/dist/smart-monitor.d.ts.map +1 -0
  55. package/dist/smart-monitor.js +256 -0
  56. package/dist/smart-monitor.js.map +1 -0
  57. package/dist/src/automation/clipboard-automation.d.ts +35 -0
  58. package/dist/src/automation/clipboard-automation.d.ts.map +1 -0
  59. package/dist/src/automation/clipboard-automation.js +242 -0
  60. package/dist/src/automation/clipboard-automation.js.map +1 -0
  61. package/dist/src/channels/base/channel.d.ts +60 -0
  62. package/dist/src/channels/base/channel.d.ts.map +1 -0
  63. package/dist/src/channels/base/channel.js +96 -0
  64. package/dist/src/channels/base/channel.js.map +1 -0
  65. package/dist/src/channels/email/smtp.d.ts +74 -0
  66. package/dist/src/channels/email/smtp.d.ts.map +1 -0
  67. package/dist/src/channels/email/smtp.js +605 -0
  68. package/dist/src/channels/email/smtp.js.map +1 -0
  69. package/dist/src/channels/line/line.d.ts +36 -0
  70. package/dist/src/channels/line/line.d.ts.map +1 -0
  71. package/dist/src/channels/line/line.js +180 -0
  72. package/dist/src/channels/line/line.js.map +1 -0
  73. package/dist/src/channels/line/webhook.d.ts +55 -0
  74. package/dist/src/channels/line/webhook.d.ts.map +1 -0
  75. package/dist/src/channels/line/webhook.js +191 -0
  76. package/dist/src/channels/line/webhook.js.map +1 -0
  77. package/dist/src/channels/local/desktop.d.ts +30 -0
  78. package/dist/src/channels/local/desktop.d.ts.map +1 -0
  79. package/dist/src/channels/local/desktop.js +161 -0
  80. package/dist/src/channels/local/desktop.js.map +1 -0
  81. package/dist/src/channels/telegram/telegram.d.ts +43 -0
  82. package/dist/src/channels/telegram/telegram.d.ts.map +1 -0
  83. package/dist/src/channels/telegram/telegram.js +223 -0
  84. package/dist/src/channels/telegram/telegram.js.map +1 -0
  85. package/dist/src/channels/telegram/webhook.d.ts +75 -0
  86. package/dist/src/channels/telegram/webhook.d.ts.map +1 -0
  87. package/dist/src/channels/telegram/webhook.js +278 -0
  88. package/dist/src/channels/telegram/webhook.js.map +1 -0
  89. package/dist/src/cli.d.ts +9 -0
  90. package/dist/src/cli.d.ts.map +1 -0
  91. package/dist/src/cli.js +99 -0
  92. package/dist/src/cli.js.map +1 -0
  93. package/dist/src/commands/hooks.d.ts +10 -0
  94. package/dist/src/commands/hooks.d.ts.map +1 -0
  95. package/dist/src/commands/hooks.js +50 -0
  96. package/dist/src/commands/hooks.js.map +1 -0
  97. package/dist/src/commands/init.d.ts +20 -0
  98. package/dist/src/commands/init.d.ts.map +1 -0
  99. package/dist/src/commands/init.js +173 -0
  100. package/dist/src/commands/init.js.map +1 -0
  101. package/dist/src/commands/license.d.ts +15 -0
  102. package/dist/src/commands/license.d.ts.map +1 -0
  103. package/dist/src/commands/license.js +107 -0
  104. package/dist/src/commands/license.js.map +1 -0
  105. package/dist/src/commands/start.d.ts +17 -0
  106. package/dist/src/commands/start.d.ts.map +1 -0
  107. package/dist/src/commands/start.js +150 -0
  108. package/dist/src/commands/start.js.map +1 -0
  109. package/dist/src/commands/status.d.ts +8 -0
  110. package/dist/src/commands/status.d.ts.map +1 -0
  111. package/dist/src/commands/status.js +95 -0
  112. package/dist/src/commands/status.js.map +1 -0
  113. package/dist/src/commands/stop.d.ts +8 -0
  114. package/dist/src/commands/stop.d.ts.map +1 -0
  115. package/dist/src/commands/stop.js +64 -0
  116. package/dist/src/commands/stop.js.map +1 -0
  117. package/dist/src/config-manager.d.ts +16 -0
  118. package/dist/src/config-manager.d.ts.map +1 -0
  119. package/dist/src/config-manager.js +152 -0
  120. package/dist/src/config-manager.js.map +1 -0
  121. package/dist/src/core/config.d.ts +28 -0
  122. package/dist/src/core/config.d.ts.map +1 -0
  123. package/dist/src/core/config.js +248 -0
  124. package/dist/src/core/config.js.map +1 -0
  125. package/dist/src/core/logger.d.ts +19 -0
  126. package/dist/src/core/logger.d.ts.map +1 -0
  127. package/dist/src/core/logger.js +47 -0
  128. package/dist/src/core/logger.js.map +1 -0
  129. package/dist/src/core/notifier.d.ts +45 -0
  130. package/dist/src/core/notifier.d.ts.map +1 -0
  131. package/dist/src/core/notifier.js +189 -0
  132. package/dist/src/core/notifier.js.map +1 -0
  133. package/dist/src/lib/license-validator.d.ts +120 -0
  134. package/dist/src/lib/license-validator.d.ts.map +1 -0
  135. package/dist/src/lib/license-validator.js +294 -0
  136. package/dist/src/lib/license-validator.js.map +1 -0
  137. package/dist/src/lib/preflight.d.ts +28 -0
  138. package/dist/src/lib/preflight.d.ts.map +1 -0
  139. package/dist/src/lib/preflight.js +90 -0
  140. package/dist/src/lib/preflight.js.map +1 -0
  141. package/dist/src/relay/claude-command-bridge.d.ts +57 -0
  142. package/dist/src/relay/claude-command-bridge.d.ts.map +1 -0
  143. package/dist/src/relay/claude-command-bridge.js +188 -0
  144. package/dist/src/relay/claude-command-bridge.js.map +1 -0
  145. package/dist/src/relay/email-listener.d.ts +65 -0
  146. package/dist/src/relay/email-listener.d.ts.map +1 -0
  147. package/dist/src/relay/email-listener.js +460 -0
  148. package/dist/src/relay/email-listener.js.map +1 -0
  149. package/dist/src/relay/relay-pty.d.ts +21 -0
  150. package/dist/src/relay/relay-pty.d.ts.map +1 -0
  151. package/dist/src/relay/relay-pty.js +696 -0
  152. package/dist/src/relay/relay-pty.js.map +1 -0
  153. package/dist/src/relay/smart-injector.d.ts +30 -0
  154. package/dist/src/relay/smart-injector.d.ts.map +1 -0
  155. package/dist/src/relay/smart-injector.js +233 -0
  156. package/dist/src/relay/smart-injector.js.map +1 -0
  157. package/dist/src/relay/tmux-injector.d.ts +46 -0
  158. package/dist/src/relay/tmux-injector.d.ts.map +1 -0
  159. package/dist/src/relay/tmux-injector.js +413 -0
  160. package/dist/src/relay/tmux-injector.js.map +1 -0
  161. package/dist/src/tools/config-manager.d.ts +33 -0
  162. package/dist/src/tools/config-manager.d.ts.map +1 -0
  163. package/dist/src/tools/config-manager.js +448 -0
  164. package/dist/src/tools/config-manager.js.map +1 -0
  165. package/dist/src/tools/installer.d.ts +38 -0
  166. package/dist/src/tools/installer.d.ts.map +1 -0
  167. package/dist/src/tools/installer.js +222 -0
  168. package/dist/src/tools/installer.js.map +1 -0
  169. package/dist/src/types/callbacks.d.ts +53 -0
  170. package/dist/src/types/callbacks.d.ts.map +1 -0
  171. package/dist/src/types/callbacks.js +7 -0
  172. package/dist/src/types/callbacks.js.map +1 -0
  173. package/dist/src/types/config.d.ts +56 -0
  174. package/dist/src/types/config.d.ts.map +1 -0
  175. package/dist/src/types/config.js +6 -0
  176. package/dist/src/types/config.js.map +1 -0
  177. package/dist/src/types/hooks.d.ts +47 -0
  178. package/dist/src/types/hooks.d.ts.map +1 -0
  179. package/dist/src/types/hooks.js +6 -0
  180. package/dist/src/types/hooks.js.map +1 -0
  181. package/dist/src/types/index.d.ts +7 -0
  182. package/dist/src/types/index.d.ts.map +1 -0
  183. package/dist/src/types/index.js +23 -0
  184. package/dist/src/types/index.js.map +1 -0
  185. package/dist/src/types/ipc.d.ts +43 -0
  186. package/dist/src/types/ipc.d.ts.map +1 -0
  187. package/dist/src/types/ipc.js +7 -0
  188. package/dist/src/types/ipc.js.map +1 -0
  189. package/dist/src/types/session.d.ts +87 -0
  190. package/dist/src/types/session.d.ts.map +1 -0
  191. package/dist/src/types/session.js +9 -0
  192. package/dist/src/types/session.js.map +1 -0
  193. package/dist/src/types/telegram.d.ts +58 -0
  194. package/dist/src/types/telegram.d.ts.map +1 -0
  195. package/dist/src/types/telegram.js +6 -0
  196. package/dist/src/types/telegram.js.map +1 -0
  197. package/dist/src/utils/active-check.d.ts +20 -0
  198. package/dist/src/utils/active-check.d.ts.map +1 -0
  199. package/dist/src/utils/active-check.js +42 -0
  200. package/dist/src/utils/active-check.js.map +1 -0
  201. package/dist/src/utils/callback-parser.d.ts +23 -0
  202. package/dist/src/utils/callback-parser.d.ts.map +1 -0
  203. package/dist/src/utils/callback-parser.js +85 -0
  204. package/dist/src/utils/callback-parser.js.map +1 -0
  205. package/dist/src/utils/controller-injector.d.ts +21 -0
  206. package/dist/src/utils/controller-injector.d.ts.map +1 -0
  207. package/dist/src/utils/controller-injector.js +108 -0
  208. package/dist/src/utils/controller-injector.js.map +1 -0
  209. package/dist/src/utils/conversation-tracker.d.ts +32 -0
  210. package/dist/src/utils/conversation-tracker.d.ts.map +1 -0
  211. package/dist/src/utils/conversation-tracker.js +119 -0
  212. package/dist/src/utils/conversation-tracker.js.map +1 -0
  213. package/dist/src/utils/deep-link.d.ts +22 -0
  214. package/dist/src/utils/deep-link.d.ts.map +1 -0
  215. package/dist/src/utils/deep-link.js +43 -0
  216. package/dist/src/utils/deep-link.js.map +1 -0
  217. package/dist/src/utils/ghostty-session-manager.d.ts +81 -0
  218. package/dist/src/utils/ghostty-session-manager.d.ts.map +1 -0
  219. package/dist/src/utils/ghostty-session-manager.js +370 -0
  220. package/dist/src/utils/ghostty-session-manager.js.map +1 -0
  221. package/dist/src/utils/hook-definitions.d.ts +25 -0
  222. package/dist/src/utils/hook-definitions.d.ts.map +1 -0
  223. package/dist/src/utils/hook-definitions.js +36 -0
  224. package/dist/src/utils/hook-definitions.js.map +1 -0
  225. package/dist/src/utils/http-request.d.ts +25 -0
  226. package/dist/src/utils/http-request.d.ts.map +1 -0
  227. package/dist/src/utils/http-request.js +66 -0
  228. package/dist/src/utils/http-request.js.map +1 -0
  229. package/dist/src/utils/optional-require.d.ts +13 -0
  230. package/dist/src/utils/optional-require.d.ts.map +1 -0
  231. package/dist/src/utils/optional-require.js +37 -0
  232. package/dist/src/utils/optional-require.js.map +1 -0
  233. package/dist/src/utils/paths.d.ts +13 -0
  234. package/dist/src/utils/paths.d.ts.map +1 -0
  235. package/dist/src/utils/paths.js +30 -0
  236. package/dist/src/utils/paths.js.map +1 -0
  237. package/dist/src/utils/pty-session-manager.d.ts +43 -0
  238. package/dist/src/utils/pty-session-manager.d.ts.map +1 -0
  239. package/dist/src/utils/pty-session-manager.js +183 -0
  240. package/dist/src/utils/pty-session-manager.js.map +1 -0
  241. package/dist/src/utils/subagent-tracker.d.ts +64 -0
  242. package/dist/src/utils/subagent-tracker.d.ts.map +1 -0
  243. package/dist/src/utils/subagent-tracker.js +191 -0
  244. package/dist/src/utils/subagent-tracker.js.map +1 -0
  245. package/dist/src/utils/tmux-monitor.d.ts +102 -0
  246. package/dist/src/utils/tmux-monitor.d.ts.map +1 -0
  247. package/dist/src/utils/tmux-monitor.js +642 -0
  248. package/dist/src/utils/tmux-monitor.js.map +1 -0
  249. package/dist/src/utils/trace-capture.d.ts +42 -0
  250. package/dist/src/utils/trace-capture.d.ts.map +1 -0
  251. package/dist/src/utils/trace-capture.js +102 -0
  252. package/dist/src/utils/trace-capture.js.map +1 -0
  253. package/dist/src/utils/transcript-reader.d.ts +57 -0
  254. package/dist/src/utils/transcript-reader.d.ts.map +1 -0
  255. package/dist/src/utils/transcript-reader.js +229 -0
  256. package/dist/src/utils/transcript-reader.js.map +1 -0
  257. package/dist/start-all-webhooks.d.ts +7 -0
  258. package/dist/start-all-webhooks.d.ts.map +1 -0
  259. package/dist/start-all-webhooks.js +98 -0
  260. package/dist/start-all-webhooks.js.map +1 -0
  261. package/dist/start-line-webhook.d.ts +7 -0
  262. package/dist/start-line-webhook.d.ts.map +1 -0
  263. package/dist/start-line-webhook.js +59 -0
  264. package/dist/start-line-webhook.js.map +1 -0
  265. package/dist/start-relay-pty.d.ts +7 -0
  266. package/dist/start-relay-pty.d.ts.map +1 -0
  267. package/dist/start-relay-pty.js +173 -0
  268. package/dist/start-relay-pty.js.map +1 -0
  269. package/dist/start-telegram-webhook.d.ts +7 -0
  270. package/dist/start-telegram-webhook.d.ts.map +1 -0
  271. package/dist/start-telegram-webhook.js +80 -0
  272. package/dist/start-telegram-webhook.js.map +1 -0
  273. package/dist/user-prompt-hook.d.ts +13 -0
  274. package/dist/user-prompt-hook.d.ts.map +1 -0
  275. package/dist/user-prompt-hook.js +45 -0
  276. package/dist/user-prompt-hook.js.map +1 -0
  277. package/dist/workspace-router.d.ts +114 -0
  278. package/dist/workspace-router.d.ts.map +1 -0
  279. package/dist/workspace-router.js +572 -0
  280. package/dist/workspace-router.js.map +1 -0
  281. package/dist/workspace-telegram-bot.d.ts +3 -0
  282. package/dist/workspace-telegram-bot.d.ts.map +1 -0
  283. package/dist/workspace-telegram-bot.js +1847 -0
  284. package/dist/workspace-telegram-bot.js.map +1 -0
  285. package/package.json +85 -0
  286. package/src/types/callbacks.ts +73 -0
  287. package/src/types/config.ts +63 -0
  288. package/src/types/hooks.ts +50 -0
  289. package/src/types/index.ts +6 -0
  290. package/src/types/ipc.ts +55 -0
  291. package/src/types/session.ts +91 -0
  292. package/src/types/telegram.ts +66 -0
package/README.md ADDED
@@ -0,0 +1,394 @@
1
+ <div align="center">
2
+
3
+ # Cheesyboy
4
+
5
+ **Control Claude Code from Telegram — approve permissions, answer questions, resume sessions, and manage AI coding agents from your phone.**
6
+
7
+ [![CI](https://github.com/selucas12/cheesy/actions/workflows/ci.yml/badge.svg)](https://github.com/selucas12/cheesy/actions/workflows/ci.yml)
8
+ [![npm version](https://img.shields.io/npm/v/@selucas12/cheesy)](https://www.npmjs.com/package/@selucas12/cheesy)
9
+ [![Node.js](https://img.shields.io/badge/node-%3E%3D18.0.0-brightgreen)](https://nodejs.org)
10
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
11
+
12
+ </div>
13
+
14
+ ---
15
+
16
+ Cheesyboy is a self-hosted Telegram bot that bridges Claude Code to your phone. When Claude needs a permission, has a question, or finishes a task — you get a Telegram message with inline buttons to respond. Resume past conversations, start new sessions, and manage multiple AI coding agents — all without being at your keyboard.
17
+
18
+ > Forked from [ccgram](https://github.com/jsayubi/ccgram) by JS Ayubi. See LICENSE for original copyright.
19
+
20
+ ```
21
+ Claude Code → Cheesyboy hooks → Telegram bot → 📱 your phone
22
+ ↑ ↓
23
+ └─ updatedInput / tmux / Ghostty / PTY ─┘
24
+ ```
25
+
26
+ ## Features
27
+
28
+ - **Universal terminal support** — Works with tmux, Ghostty, bare zsh, screen — anything. Question answers flow back to Claude Code directly via the `updatedInput` hook output, no keystroke injection needed.
29
+ - **Permission approvals** — Allow, Deny, Always, or Defer with a single tap
30
+ - **Question answering** — Single-select, multi-select, and free-text questions answered via Telegram buttons
31
+ - **Rich `/status`** — Model, version, git branch, session ID, context window % (auto-detects 1M mode), rate limit + reset time, last activity, last assistant message — all from Claude Code's transcript
32
+ - **MCP elicitation forwarding** — Schema-aware MCP server input requests routed to Telegram, one prompt per form field
33
+ - **Smart notifications** — Task completions, errors, compaction, session lifecycle, subagent activity — silent at your terminal, instant when you're away
34
+ - **Remote command routing** — Send any command to any Claude session from Telegram
35
+ - **Session management** — List, switch, interrupt; resume past conversations with `/resume`
36
+ - **Project launcher** — Start Claude in any project directory with `/new myproject`
37
+ - **Deep links** — `/link <prompt>` generates `claude-cli://open?q=...` URLs
38
+ - **Smart routing** — Prefix matching, default workspace, reply-to routing
39
+ - **Ghostty support** — Auto-detected on macOS via `TERM_PROGRAM=ghostty`; full keystroke injection via AppleScript
40
+ - **tmux optional** — Falls back to a headless PTY session (`node-pty`) when tmux is unavailable
41
+ - **One-command setup** — Interactive wizard installs hooks, generates service file, starts bot
42
+
43
+ ## Requirements
44
+
45
+ - [Node.js](https://nodejs.org) 18+
46
+ - A Telegram bot token (from [@BotFather](https://t.me/BotFather))
47
+ - Your Telegram chat ID (from [@userinfobot](https://t.me/userinfobot))
48
+ - A terminal — any of:
49
+ - [tmux](https://github.com/tmux/tmux/wiki) (recommended; cross-platform)
50
+ - [Ghostty](https://ghostty.org) (auto-detected on macOS via `TERM_PROGRAM`)
51
+ - bare zsh / screen / anything else (PTY fallback via `node-pty`)
52
+
53
+ ## Quick Start
54
+
55
+ ```bash
56
+ npx @selucas12/cheesy init
57
+ ```
58
+
59
+ The setup wizard will:
60
+ 1. Ask for your bot token and chat ID
61
+ 2. Install the bot to `~/.ccgram/`
62
+ 3. Merge the required hooks into `~/.claude/settings.json`
63
+ 4. Generate and start a background service (launchd on macOS, systemd on Linux)
64
+
65
+ > The install path remains `~/.ccgram/` so users upgrading from ccgram don't have to migrate.
66
+
67
+ Then open Telegram and message your bot — Claude Code will now notify you remotely.
68
+
69
+ ## How It Works
70
+
71
+ Cheesyboy integrates with [Claude Code hooks](https://docs.anthropic.com/en/docs/claude-code/hooks) — shell scripts that Claude Code calls at key moments. Hooks send Telegram messages and, depending on the event, return your response to Claude Code directly via stdout (`updatedInput` for questions, `decision` for permissions) or inject keystrokes into the active session (tmux, Ghostty, or PTY).
72
+
73
+ ### Hooks installed
74
+
75
+ | Hook | Event | What it does |
76
+ |------|-------|-------------|
77
+ | `permission-hook.js` | `PermissionRequest` | Sends a permission dialog with Yes / Yes-stop-asking / No / Explain buttons. Blocks Claude until you respond. |
78
+ | `question-notify.js` | `PreToolUse` (AskUserQuestion) | Sends Claude's question with selectable options. Returns answer directly via `updatedInput` — works with any terminal. |
79
+ | `enhanced-hook-notify.js completed` | `Stop` | Notifies you when Claude finishes a task, including the last response text. |
80
+ | `enhanced-hook-notify.js waiting` | `Notification` | Notifies you when Claude is waiting for input. |
81
+ | `user-prompt-hook.js` | `UserPromptSubmit` | Tracks terminal activity so notifications are suppressed when you're actively working. |
82
+ | `enhanced-hook-notify.js session-start` | `SessionStart` | Notifies you when a new Claude session starts. |
83
+ | `enhanced-hook-notify.js session-end` | `SessionEnd` | Notifies you when a Claude session ends. |
84
+ | `enhanced-hook-notify.js subagent-done` | `SubagentStop` | Notifies you when a subagent task completes. |
85
+ | `permission-denied-notify.js` | `PermissionDenied` | Notifies when auto mode blocks a tool. Retry button lets you override. |
86
+ | `enhanced-hook-notify.js stop-failure` | `StopFailure` | Notifies you on API errors (rate limits, network issues). |
87
+ | `enhanced-hook-notify.js post-compact` | `PostCompact` | Notifies when context compaction completes, with token savings. |
88
+ | `pre-compact-notify.js` | `PreCompact` | Warns before compaction starts. Block button lets you preserve context. |
89
+ | `elicitation-notify.js` | `Elicitation` | Forwards MCP server input requests to Telegram. Reply with your answer. |
90
+ | `enhanced-hook-notify.js task-created` | `TaskCreated` | Notifies when Claude creates a new task. |
91
+ | `enhanced-hook-notify.js cwd-changed` | `CwdChanged` | Notifies when Claude changes working directory. |
92
+ | `enhanced-hook-notify.js instructions-loaded` | `InstructionsLoaded` | Notifies when CLAUDE.md or rules are loaded. |
93
+
94
+ > **Smart suppression** — all notifications (including permissions) are automatically silenced when you've sent a message to Claude within the last 2 minutes. The moment you step away, Telegram takes over. Telegram-injected commands always get their response back to Telegram regardless.
95
+
96
+ ### Permission flow
97
+
98
+ ```
99
+ Claude requests permission
100
+ → hook generates promptId, writes pending file
101
+ → Telegram message with inline buttons sent to your phone
102
+ → you tap Allow / Deny
103
+ → bot writes response file
104
+ → hook reads response, returns decision to Claude
105
+ → Claude continues
106
+ ```
107
+
108
+ ### Question flow
109
+
110
+ ```
111
+ Claude asks a question (AskUserQuestion)
112
+ → question-notify sends options to Telegram
113
+ → you tap an option
114
+ → bot writes response file with your selection
115
+ → hook returns answer via updatedInput to Claude
116
+ → Claude receives answer directly (works with any terminal!)
117
+ ```
118
+
119
+ ## Bot Commands
120
+
121
+ ### Session management
122
+
123
+ | Command | Description |
124
+ |---------|-------------|
125
+ | `/sessions` | List all active Claude sessions with status and age |
126
+ | `/use <workspace>` | Set default workspace — plain text messages route there |
127
+ | `/use` | Show current default workspace |
128
+ | `/use clear` | Clear the default workspace |
129
+
130
+ ### Workspace control
131
+
132
+ | Command | Description |
133
+ |---------|-------------|
134
+ | `/<workspace> <command>` | Send a command to a specific Claude session |
135
+ | `/status [workspace]` | Rich status: model, version, branch, session ID, context %, rate limit, last activity, last message (and recent pane output for tmux/PTY) |
136
+ | `/stop [workspace]` | Send Ctrl+C to interrupt the running prompt |
137
+ | `/compact [workspace]` | Run `/compact` and wait for it to complete |
138
+ | `/effort [workspace] low\|medium\|high` | Set Claude's thinking effort level |
139
+ | `/model [workspace] <model>` | Switch Claude model (sonnet, opus, haiku) |
140
+ | `/link <prompt>` | Generate a deep link to open Claude Code with your prompt |
141
+
142
+ ### Project launcher
143
+
144
+ | Command | Description |
145
+ |---------|-------------|
146
+ | `/new` | Show recent projects as buttons |
147
+ | `/new myproject` | Start Claude in `~/projects/myproject` (or wherever it's found) |
148
+
149
+ The `/new` command searches your configured `PROJECT_DIRS`, finds exact or prefix-matched directories, creates a tmux session (or PTY session if tmux is unavailable), starts Claude, and sets it as the default workspace.
150
+
151
+ ### Resume past conversations
152
+
153
+ | Command | Description |
154
+ |---------|-------------|
155
+ | `/resume` | Show projects with past Claude sessions |
156
+ | `/resume myproject` | Jump straight to session picker for that project |
157
+
158
+ The `/resume` command reads directly from Claude Code's session storage (`~/.claude/projects/`), giving you access to your full conversation history — not just sessions started through the bot.
159
+
160
+ Each session shows a snippet of the first message for easy identification. Sessions are sorted by last activity.
161
+
162
+ **Active session protection:**
163
+ - If a session appears to be running in a terminal (JSONL file modified within 5 min), you get a warning before resuming to prevent dual-instance conflicts
164
+ - If a PTY session is running, you're warned that it will be terminated (PTY sessions can't be reattached)
165
+ - tmux sessions switch seamlessly — the bot injects `/exit` + `claude --resume` inline, keeping your terminal connected
166
+
167
+ ### Smart routing
168
+
169
+ **Prefix matching** — workspace names can be abbreviated. `/ass hello` routes to `assistant` if it's unique. Ambiguous prefixes show a list to choose from.
170
+
171
+ **Reply-to routing** — reply to any bot notification (permission, question, or status message) to route your reply to that workspace.
172
+
173
+ **Default workspace** — after `/use myproject`, plain text messages route there automatically.
174
+
175
+ ## Configuration
176
+
177
+ Cheesyboy is configured via `~/.ccgram/.env`. Run `cheesy init` (or `ccgram init` — both work) to generate it interactively, or edit it manually:
178
+
179
+ ```bash
180
+ # Required
181
+ TELEGRAM_ENABLED=true
182
+ TELEGRAM_BOT_TOKEN=your_bot_token_here
183
+ TELEGRAM_CHAT_ID=your_chat_id_here
184
+
185
+ # Project directories to scan (for /new command and session listing)
186
+ PROJECT_DIRS=~/projects,~/tools
187
+
188
+ # Suppress notifications when you're actively at the terminal
189
+ # Default: 120 seconds (2 minutes). Set to 0 to always notify.
190
+ ACTIVE_THRESHOLD_SECONDS=120
191
+ ```
192
+
193
+ ### Advanced options
194
+
195
+ ```bash
196
+ # Allow only specific Telegram user IDs (comma-separated)
197
+ TELEGRAM_WHITELIST=123456789,987654321
198
+
199
+ # Use webhooks instead of long-polling (requires public URL)
200
+ TELEGRAM_WEBHOOK_URL=https://example.com/webhook
201
+ TELEGRAM_WEBHOOK_PORT=3001
202
+
203
+ # Force IPv4 for Telegram API (useful on some VPS providers)
204
+ TELEGRAM_FORCE_IPV4=false
205
+
206
+ # Tmux keystroke injection mode
207
+ INJECTION_MODE=tmux # tmux (default) or pty
208
+
209
+ # Custom session map path
210
+ SESSION_MAP_PATH=~/.ccgram/src/data/session-map.json
211
+
212
+ # Logging
213
+ LOG_LEVEL=info # debug, info, warn, error
214
+ ```
215
+
216
+ ## Service Management
217
+
218
+ `cheesy init` generates and starts a background service automatically.
219
+
220
+ ### macOS (launchd)
221
+
222
+ ```bash
223
+ # Restart
224
+ launchctl kickstart -k gui/$(id -u)/com.ccgram
225
+
226
+ # Stop / Start
227
+ launchctl stop com.ccgram
228
+ launchctl start com.ccgram
229
+
230
+ # Logs
231
+ tail -f ~/.ccgram/logs/bot-stdout.log
232
+ tail -f ~/.ccgram/logs/bot-stderr.log
233
+ ```
234
+
235
+ ### Linux (systemd)
236
+
237
+ ```bash
238
+ sudo systemctl status ccgram
239
+ sudo systemctl restart ccgram
240
+ journalctl -u ccgram -f
241
+ ```
242
+
243
+ ## Installation Details
244
+
245
+ `cheesy init` installs the bot to `~/.ccgram/` — a persistent directory that survives `npx` cleanup and system updates. The hooks in `~/.claude/settings.json` always point to this location. (The path stays `~/.ccgram/` for backward compatibility with users upgrading from upstream ccgram.)
246
+
247
+ ```
248
+ ~/.ccgram/
249
+ ├── dist/ # Compiled JavaScript (hook scripts + bot)
250
+ ├── config/ # Default config templates
251
+ ├── src/data/
252
+ │ ├── session-map.json # Workspace → tmux session mapping
253
+ │ ├── default-workspace.json
254
+ │ ├── project-history.json # Recent projects for /new
255
+ │ └── message-workspace-map.json # reply-to routing (24h TTL)
256
+ ├── logs/
257
+ │ ├── bot-stdout.log
258
+ │ └── bot-stderr.log
259
+ └── .env # Your configuration
260
+ ```
261
+
262
+ ## Development
263
+
264
+ ```bash
265
+ git clone https://github.com/selucas12/cheesy
266
+ cd cheesy
267
+ npm install
268
+ cp .env.example .env # Add your bot token and chat ID
269
+ npm run build
270
+ node dist/workspace-telegram-bot.js
271
+ ```
272
+
273
+ ```bash
274
+ npm run build # Compile TypeScript → dist/
275
+ npm run build:watch # Watch mode
276
+ npm test # Run 120 tests (vitest)
277
+ ```
278
+
279
+ **Note:** Claude Code hooks run from `~/.ccgram/dist/`, not the repo's `dist/`. After changing hook scripts during development, sync them:
280
+
281
+ ```bash
282
+ cp -r dist/ ~/.ccgram/dist/
283
+ ```
284
+
285
+ End users don't need this — `cheesy init` handles it automatically.
286
+
287
+ ### Architecture
288
+
289
+ ```
290
+ src/
291
+ ├── utils/
292
+ │ ├── active-check.ts # Detect terminal activity; suppress notifications when present
293
+ │ ├── pty-session-manager.ts # Headless PTY backend via node-pty (tmux fallback)
294
+ │ ├── ghostty-session-manager.ts # Ghostty AppleScript backend (macOS, auto-detected)
295
+ │ ├── transcript-reader.ts # Read Claude Code session JSONL for /status (model, context %, last message)
296
+ │ ├── deep-link.ts # Generate claude-cli://open?q=... URLs for /link
297
+ │ ├── callback-parser.ts # Parse Telegram callback_data strings
298
+ │ ├── http-request.ts # Lightweight HTTPS wrapper (no axios)
299
+ │ ├── optional-require.ts # Graceful optional dependency loading
300
+ │ └── paths.ts # PROJECT_ROOT + CCGRAM_HOME constants
301
+ ├── types/ # TypeScript interfaces
302
+ └── data/ # Runtime data (session map, history)
303
+
304
+ workspace-telegram-bot.ts # Main bot (long-polling, routing, callbacks, /status, /link, /effort, /model)
305
+ workspace-router.ts # Session map, prefix matching, default workspace, rate limit storage
306
+ prompt-bridge.ts # File-based IPC via /tmp/claude-prompts/
307
+ permission-hook.ts # Blocking permission approval hook
308
+ permission-denied-notify.ts # PermissionDenied hook with retry button
309
+ question-notify.ts # Blocking AskUserQuestion hook (returns updatedInput to Claude Code)
310
+ enhanced-hook-notify.ts # Status notifications (Stop, Notification, SessionStart/End, SubagentStop, StopFailure, PostCompact, TaskCreated, CwdChanged, InstructionsLoaded)
311
+ pre-compact-notify.ts # PreCompact hook with block button
312
+ elicitation-notify.ts # MCP elicitation hook (schema-aware, per-field)
313
+ user-prompt-hook.ts # UserPromptSubmit hook — writes terminal activity timestamp
314
+ setup.ts # Interactive setup wizard
315
+ cli.ts # cheesy CLI entry point
316
+ ```
317
+
318
+ ### Tests
319
+
320
+ ```
321
+ test/
322
+ ├── prompt-bridge.test.js # 15 tests — IPC write/read/update/clean/expiry
323
+ ├── workspace-router.test.js # 43 tests — session map, prefix matching, defaults, reply-to, history, rate limits
324
+ ├── callback-parser.test.js # 27 tests — all callback_data formats (perm, opt, new, rp, rs, rc, perm-denied, pre-compact)
325
+ ├── active-check.test.js # 8 tests — terminal activity detection, thresholds
326
+ ├── deep-link.test.js # 11 tests — claude-cli:// URL generation, encoding, character limits
327
+ └── ghostty-session-manager.test.js # 16 tests — Ghostty AppleScript session management (mocked)
328
+ ```
329
+
330
+ Tests use isolated temp directories and run with `npm test` (vitest, no configuration needed).
331
+
332
+ ### Dependencies
333
+
334
+ **Core:** Only `dotenv` is required. The bot runs on Node.js built-ins.
335
+
336
+ **Optional** (graceful degradation if missing):
337
+ - `express` — webhook servers
338
+ - `node-pty` — PTY relay mode
339
+ - `nodemailer`, `node-imap`, `mailparser` — email relay
340
+ - `pino`, `pino-pretty` — structured logging (falls back to console)
341
+
342
+ ## FAQ
343
+
344
+ **Do I need a public server?**
345
+ No. Cheesyboy uses Telegram's long-polling API — it works behind NAT, on a laptop, or anywhere with outbound HTTPS.
346
+
347
+ **What if I'm already at my terminal?**
348
+ All notifications — including permission requests — are suppressed automatically when you've sent a message to Claude within the last 2 minutes. The threshold is configurable via `ACTIVE_THRESHOLD_SECONDS`. Step away for more than 2 minutes and Telegram instantly takes over.
349
+
350
+ **Can I use it with multiple projects at once?**
351
+ Yes. Each Claude session maps to a named tmux or PTY session. Use `/sessions` to see all active sessions, or `/use <workspace>` to set a default for plain text routing.
352
+
353
+ **Can I resume a conversation I started in the terminal?**
354
+ Yes. `/resume` reads from Claude Code's own session storage, so it sees every conversation — not just ones started through the bot. If the session is still running in your terminal, you'll get a warning before resuming to prevent conflicts.
355
+
356
+ **Do I need tmux?**
357
+ No. Cheesyboy supports three injection backends and picks the right one automatically:
358
+ - **tmux** (default when running) — cross-platform, recommended
359
+ - **Ghostty** — auto-detected on macOS via `TERM_PROGRAM=ghostty`; uses AppleScript for keystroke injection and tab focus
360
+ - **PTY** (`node-pty`) — headless fallback when neither tmux nor Ghostty is available
361
+
362
+ For question answering specifically (the `AskUserQuestion` hook), Cheesyboy returns answers to Claude Code directly via the `updatedInput` hook output — so it works on **any** terminal, including bare zsh, screen, or anything else, without keystroke injection at all.
363
+
364
+ To force a specific backend:
365
+ ```bash
366
+ # in ~/.ccgram/.env
367
+ INJECTION_MODE=pty # or tmux
368
+ ```
369
+
370
+ Then restart the bot: `launchctl kickstart -k gui/$(id -u)/com.ccgram` (macOS) or `sudo systemctl restart ccgram` (Linux).
371
+
372
+ To use PTY mode, install the optional dependency: `npm install node-pty` inside `~/.ccgram/`.
373
+
374
+ Full remote control — permission approvals, question answering, `/new`, `/stop` — works identically in all modes.
375
+
376
+ **Is my bot token stored securely?**
377
+ The token is stored in `~/.ccgram/.env`, readable only by your user. It's never logged or transmitted beyond Telegram's API.
378
+
379
+ **What's the 64-byte callback limit?**
380
+ Telegram limits inline button callback data to 64 bytes. Cheesyboy uses a compact `type:promptId:action` format to stay within this limit.
381
+
382
+ ## License
383
+
384
+ MIT — see [LICENSE](LICENSE).
385
+
386
+ ---
387
+
388
+ <div align="center">
389
+
390
+ Built for developers who run Claude Code unattended — approve permissions, resume conversations, and manage AI coding agents from anywhere.
391
+
392
+ [Report a bug](https://github.com/selucas12/cheesy/issues) · [Request a feature](https://github.com/selucas12/cheesy/issues)
393
+
394
+ </div>
package/ccgram.service ADDED
@@ -0,0 +1,24 @@
1
+ # Cheesyboy systemd unit file (formerly ccgram.service — filename kept for upgrade compatibility)
2
+ # Copy to /etc/systemd/system/ccgram.service and edit paths, then:
3
+ # sudo systemctl daemon-reload
4
+ # sudo systemctl enable ccgram
5
+ # sudo systemctl start ccgram
6
+
7
+ [Unit]
8
+ Description=Cheesyboy - Claude Code Telegram Bot
9
+ After=network.target
10
+
11
+ [Service]
12
+ Type=simple
13
+ User=YOUR_USERNAME
14
+ WorkingDirectory=/home/YOUR_USERNAME/tools/ccgram
15
+ ExecStart=/usr/bin/node dist/workspace-telegram-bot.js
16
+ Restart=always
17
+ RestartSec=5
18
+ Environment=NODE_ENV=production
19
+
20
+ StandardOutput=append:/home/YOUR_USERNAME/tools/ccgram/logs/bot-stdout.log
21
+ StandardError=append:/home/YOUR_USERNAME/tools/ccgram/logs/bot-stderr.log
22
+
23
+ [Install]
24
+ WantedBy=multi-user.target
@@ -0,0 +1,58 @@
1
+ {
2
+ "desktop": {
3
+ "type": "local",
4
+ "enabled": true,
5
+ "config": {}
6
+ },
7
+ "email": {
8
+ "type": "email",
9
+ "enabled": true
10
+ },
11
+ "discord": {
12
+ "type": "chat",
13
+ "enabled": false,
14
+ "config": {
15
+ "webhook": "",
16
+ "username": "Cheesyboy",
17
+ "avatar": null
18
+ }
19
+ },
20
+ "telegram": {
21
+ "type": "chat",
22
+ "enabled": true,
23
+ "config": {
24
+ "botToken": "",
25
+ "chatId": "",
26
+ "groupId": "",
27
+ "whitelist": [],
28
+ "forceIPv4": false
29
+ }
30
+ },
31
+ "whatsapp": {
32
+ "type": "chat",
33
+ "enabled": false,
34
+ "config": {
35
+ "webhook": "",
36
+ "apiKey": ""
37
+ }
38
+ },
39
+ "feishu": {
40
+ "type": "chat",
41
+ "enabled": false,
42
+ "config": {
43
+ "webhook": "",
44
+ "secret": ""
45
+ }
46
+ },
47
+ "line": {
48
+ "type": "chat",
49
+ "enabled": true,
50
+ "config": {
51
+ "channelAccessToken": "",
52
+ "channelSecret": "",
53
+ "userId": "",
54
+ "groupId": "",
55
+ "whitelist": []
56
+ }
57
+ }
58
+ }
@@ -0,0 +1,27 @@
1
+ {
2
+ "language": "en",
3
+ "sound": {
4
+ "completed": "Glass",
5
+ "waiting": "Tink"
6
+ },
7
+ "enabled": true,
8
+ "timeout": 5,
9
+ "customMessages": {
10
+ "completed": null,
11
+ "waiting": null
12
+ },
13
+ "channels": {
14
+ "desktop": {
15
+ "enabled": true,
16
+ "priority": 1
17
+ }
18
+ },
19
+ "relay": {
20
+ "enabled": false,
21
+ "port": 3000,
22
+ "auth": {
23
+ "enabled": false,
24
+ "token": null
25
+ }
26
+ }
27
+ }
@@ -0,0 +1,16 @@
1
+ {
2
+ "language": "en",
3
+ "sound": {
4
+ "completed": "Submarine",
5
+ "waiting": "Hero"
6
+ },
7
+ "enabled": true,
8
+ "enableSubagentNotifications": false,
9
+ "showSubagentActivitiesInEmail": false,
10
+ "subagentActivityDetail": "medium",
11
+ "timeout": 5,
12
+ "customMessages": {
13
+ "completed": null,
14
+ "waiting": null
15
+ }
16
+ }
@@ -0,0 +1,32 @@
1
+ {
2
+ "zh-CN": {
3
+ "completed": {
4
+ "title": "Claude Code - Task Completed",
5
+ "message": "[{project}] Task completed, Claude is waiting for next instruction"
6
+ },
7
+ "waiting": {
8
+ "title": "Claude Code - Waiting for Input",
9
+ "message": "[{project}] Claude needs your further guidance"
10
+ }
11
+ },
12
+ "en": {
13
+ "completed": {
14
+ "title": "Claude Code - Task Completed",
15
+ "message": "[{project}] Task completed, Claude is waiting for next instruction"
16
+ },
17
+ "waiting": {
18
+ "title": "Claude Code - Waiting for Input",
19
+ "message": "[{project}] Claude needs your further guidance"
20
+ }
21
+ },
22
+ "ja": {
23
+ "completed": {
24
+ "title": "Claude Code - Task Completed",
25
+ "message": "[{project}] Task completed, Claude is waiting for next instruction"
26
+ },
27
+ "waiting": {
28
+ "title": "Claude Code - Waiting for Input",
29
+ "message": "[{project}] Claude needs your further guidance"
30
+ }
31
+ }
32
+ }
@@ -0,0 +1,31 @@
1
+ {
2
+ "email": {
3
+ "type": "email",
4
+ "enabled": true,
5
+ "config": {
6
+ "smtp": {
7
+ "host": "smtp.gmail.com",
8
+ "port": 587,
9
+ "secure": false,
10
+ "auth": {
11
+ "user": "your-email@gmail.com",
12
+ "pass": "your-app-password"
13
+ }
14
+ },
15
+ "imap": {
16
+ "host": "imap.gmail.com",
17
+ "port": 993,
18
+ "secure": true,
19
+ "auth": {
20
+ "user": "your-email@gmail.com",
21
+ "pass": "your-app-password"
22
+ }
23
+ },
24
+ "from": "Cheesyboy <your-email@gmail.com>",
25
+ "to": "your-email@gmail.com",
26
+ "template": {
27
+ "checkInterval": 30
28
+ }
29
+ }
30
+ }
31
+ }
@@ -0,0 +1,16 @@
1
+ {
2
+ "language": "en",
3
+ "sound": {
4
+ "completed": "Submarine",
5
+ "waiting": "Hero"
6
+ },
7
+ "enabled": true,
8
+ "enableSubagentNotifications": false,
9
+ "showSubagentActivitiesInEmail": true,
10
+ "subagentActivityDetail": "medium",
11
+ "timeout": 5,
12
+ "customMessages": {
13
+ "completed": null,
14
+ "waiting": null
15
+ }
16
+ }
@@ -0,0 +1,27 @@
1
+ {
2
+ "language": "en",
3
+ "sound": {
4
+ "completed": "Submarine",
5
+ "waiting": "Hero"
6
+ },
7
+ "enabled": true,
8
+ "timeout": 5,
9
+ "customMessages": {
10
+ "completed": null,
11
+ "waiting": null
12
+ },
13
+ "channels": {
14
+ "desktop": {
15
+ "enabled": true,
16
+ "priority": 1
17
+ }
18
+ },
19
+ "relay": {
20
+ "enabled": false,
21
+ "port": 3000,
22
+ "auth": {
23
+ "enabled": false,
24
+ "token": null
25
+ }
26
+ }
27
+ }
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Claude Hook Notification Script
4
+ * Called by Claude Code hooks to send Telegram notifications
5
+ */
6
+ export {};
7
+ //# sourceMappingURL=claude-hook-notify.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"claude-hook-notify.d.ts","sourceRoot":"","sources":["../claude-hook-notify.ts"],"names":[],"mappings":";AAEA;;;GAGG"}