feat(channel): forward Claude Code permission prompts to Telegram as interactive buttons#76
feat(channel): forward Claude Code permission prompts to Telegram as interactive buttons#76letrii wants to merge 5 commits intocodeaholicguy:mainfrom
Conversation
… Telegram Claude Code injects structured XML tags (command-name, command-message, local-command-stdout, system-reminder, etc.) into the conversation that are intended for the UI layer, not for end users. Without filtering, these tags appear verbatim in Telegram messages. Add stripSystemTags() to remove known system tags from agent output before forwarding to Telegram.
Claude Code responses use Markdown formatting (bold, italic, code blocks, inline code) which Telegram displays as raw text by default. Add markdownToHtml() converter in TelegramAdapter that transforms standard Markdown to Telegram HTML, and send messages with parse_mode: 'HTML'. Code blocks are extracted first to prevent formatting replacements inside them.
…interactive buttons When Claude Code requires user approval (bash commands, file operations, etc.), the bridge detects the permission prompt from the terminal output and sends it to Telegram as an inline keyboard with Yes/No buttons. Tapping a button forwards the corresponding keystroke back to the agent terminal. Changes: - TtyWriter: add captureOutput() to read terminal content via tmux/iTerm2/Terminal.app - TelegramAdapter: add sendKeyboard() and onCallbackQuery() for inline keyboard support - ChannelAdapter: extend interface with sendKeyboard() and onCallbackQuery() - channel: add startPermissionPolling() with prompt detection and callback handler
|
@letrii Thanks for the contribution! The permission forwarding to Telegram is a really useful addition for remote control. I have a suggestion for an alternative detection approach that might be more robust. As I understand, the current approach is terminal screen-scraping. The PR captures terminal output via tmux capture-pane / iTerm2 contents of session and uses regex patterns (/Allow\s+.+?/i, /›\s*(Yes|No)/, etc.) to detect permission prompts. This works but has some concerns:
Let's learn more deeply about the JSONL session file. Claude Code writes structured entries to {
"type": "assistant",
"message": {
"content": [{ "type": "tool_use", "name": "Bash", "input": { "command": "npm install" } }],
"stop_reason": "tool_use"
}
}And for AskUserQuestion: {
"type": "assistant",
"message": {
"content": [{ "type": "tool_use", "name": "AskUserQuestion", "input": { "questions": [...] } }],
"stop_reason": "tool_use"
}
}So the detection logic becomes: if the last entry is Benefits:
The ideal implementation would be:
What do you think? Happy to discuss further! |
|
When doing these changes, please also try |
Problem
When Claude Code requires user approval (bash commands, file operations, etc.),
the agent blocks and waits for terminal input. Users controlling agents remotely
via the Telegram bridge had no way to approve or reject these prompts without
being at their terminal.
Solution
The bridge now detects permission prompts from the terminal output and forwards
them to Telegram as inline keyboard messages with Yes/No buttons. Tapping a
button sends the corresponding keystroke back to the agent terminal. After
responding, the keyboard message updates to show the confirmation result.
Changes
captureOutput()to read terminal content via tmux, iTerm2, and Terminal.appsendKeyboard(),resolveKeyboard(), andonCallbackQuery()for inline keyboard supportstartPermissionPolling()with prompt detection, keyboard tracking, and callback handlerDemo
Notes
fix/strip-system-tags-from-telegram-messagesagent open