Telegram-native · Zero dependencies

yeci-notify

A remote control for your AI coding agents

yeci-notify pushes approvals and task updates from Claude Code, Codex, and Gemini CLI straight to your phone.

$ curl -fsSL https://notify.yeci.org/install.sh | bash

Installs a single binary to ~/.local/bin. Nothing runs and nothing is configured until you activate it.

Core Features

Notify

Get pinged the instant an agent needs a permission or finishes a task — foreground, tmux, nohup, or over SSH.

Approve

The terminal dialog, mirrored one-to-one: Allow, Deny, Deny with a note — plus the agent's own "always allow / don't ask again" options rendered as buttons.

Answer questions

When Claude asks a multiple-choice question, the options arrive as buttons. Tap one, or reply with your own answer — the "something else" path, remotely.

Terminal & phone in parallel

Dialogs pop in the terminal instantly as always, while Telegram gets a full mirror with buttons. Handle it on either side — the other side is safely invalidated. No priority, no timeouts, no guessing where you are.

Remote steer

Inject instructions into a live session from your phone — reply to any session notification, or use /send. Needs tmux; degrades gracefully to notify-only.

Multi-agent

Claude Code, Codex, and Gemini CLI unified behind one adapter layer, with defensive parsing across versions.

Single binary

One static CGO-free executable, no runtime dependencies. Present but absent until you run init — no processes, no config changes.

Secure by design

Outbound long-polling only — no public port or webhook. A single allowlisted Telegram ID; the token never touches logs or messages.

Agents & Platforms

Claude Code Codex Gemini CLI Telegram Linux macOS amd64 · arm64 systemd launchd tmux

Install

The install script detects your OS and architecture and drops one static binary into ~/.local/bin. Linux and macOS, amd64 and arm64.

# One-line install
curl -fsSL https://notify.yeci.org/install.sh | bash

# Custom install directory
YECI_BIN_DIR=/usr/local/bin curl -fsSL https://notify.yeci.org/install.sh | bash

Four static CGO_ENABLED=0 binaries per release, verified against checksums.txt.

Quick start

yeci-notify currently delivers notifications and interactive approvals through a Telegram bot only. Additional channels are on the roadmap.

One command activates everything. It is fully idempotent — re-run it any time to update.

yeci-notify init
  1. 1 Validate your Telegram Bot Token (from @BotFather) and capture your telegram_id (from @GotIDBot).
  2. 2 Detect installed agents and wire up their hooks — deep-merged, never overwriting your existing config.
  3. 3 Pick a launcher: systemd / launchd / shell lazy-start / manual, based on your environment.
  4. 4 Send a test notification to confirm the loop.

yeci-notify remove returns everything to factory state and restores every file it touched.

Telegram Bot Commands

/lsList sessions and each one's most recent event
/send <text>Inject text into the only active tmux session (lists candidates when there are several)
/send <session> <text>Inject text into a specific tmux pane
/pendingList pending approvals and re-render their buttons
/mute · /unmuteTemporarily silence notifications (approvals unaffected)
/statusRuntime status summary

Replying to a message works too: reply to an approval = deny with a note · reply to a question = your own answer · reply to a session notification = inject into that session.

Parallel by design

The terminal is the single source of truth: hooks never block it, and every dialog is mirrored to Telegram in full. Buttons and replies act on the terminal via tmux key injection — after verifying on screen that the dialog is still there.

Terminal acts first

You answer the dialog locally

The Telegram message flips to "handled in the terminal"; a late button tap is safely rejected instead of double-acting.

Phone acts first

You tap a button or reply in Telegram

The choice is injected into the terminal dialog — visible and auditable in the session. The agent carries on immediately.

Neither yet

You are busy elsewhere

No timeouts: the buttons stay valid as long as the dialog is open in the terminal. Answer whenever you get to it.

Replying to a session that is already running on fresh input? The bot holds your message and asks before sending, so both ends never answer twice. Remote actions require tmux; without it, mirrors still arrive and you decide in the terminal.

Capabilities

The notification mainline (every row without a requirement) holds unconditionally, for every user and every way of running an agent. Remote decisions inject keys through tmux — the deliberate trade for a terminal that stays the single source of truth.

Feature Requires Without it
Done / idle / error notifications (reply text in full, auto-split if long)Always available
Approval / question mirror notification (full command & option text)Always available
Approval remote decision (allow / deny-with-note / "always allow" options)tmuxMirror still sent; buttons say "handle in the terminal"
Questions (AskUserQuestion) answered remotely (single / multi / custom)tmuxMirror still sent; answer in the terminal
/send / reply session injectiontmux"Session not injectable"
/ls session listtmuxLists sessions seen in the event stream

Security

config.toml is forced to 0600. The token supports ${ENV_VAR} expansion, so it need never hit disk.

The token never appears in hook configs, logs, Telegram messages, or any distributed artifact.

A single Telegram ID allowlist is a hard check — non-matching updates are dropped and never answered.

Outbound long-polling only. No public port, webhook, or HTTPS certificate required.

Dangerous commands render a reject-only prompt by default — high-risk actions keep the friction of returning to the terminal.

Your bot token is a remote for your dev machine. Guard it accordingly; after remove, revoke it via @BotFather.