cd /news/developer-tools/show-hn-control-claude-code-codex-pi… Β· home β€Ί topics β€Ί developer-tools β€Ί article
[ARTICLE Β· art-96386] src=github.com β†— pub= topic=developer-tools verified=true sentiment=Β· neutral

Show HN: Control Claude Code, Codex, Pi and Gemini CLI from Telegram

Developer Younggi Choi released cliclaw, a daemon that lets users control Claude Code, Codex, Pi, and Gemini CLI from Telegram, with per-chat agent sessions, a safety gate, and auto-detection of corporate TLS interceptors. The tool, installed via Bun or npm as @younggichoi/cliclaw, supports switching between agents, image attachments, and launchd auto-start on macOS.

read7 min views1 publishedAug 14, 2026
Show HN: Control Claude Code, Codex, Pi and Gemini CLI from Telegram
Image: source

English | ν•œκ΅­μ–΄

A single daemon that lets you drive four local coding CLIs (Claude Code Β· Codex Β· Pi Β· Gemini) from Telegram, switching between them per chat.

It keeps an independent per-agent session for every chat, and ships a confirm gate for dangerous commands, response streaming, image-attachment handling, and auto-detection of corporate TLS interceptors (Zscaler, etc.).

Install

only the CLIs you wantβ€” missing agents are automatically dropped from the active list.

The chat UX (user messages, errors,

/help

) is currently Korean-first; the bot itself works fine either way.

  • macOS (Apple Silicon / Intel) Bun1.x β€”curl -fsSL https://bun.sh/install | bash

At least one of the coding CLIs installed and logged in (the bot merely spawns authenticated child processes)- Claude Code: npm install -g @anthropic-ai/claude-code

  • Codex: npm install -g @openai/codex

  • Pi: npm install -g @earendil-works/pi-coding-agent

  • Gemini: npm install -g @google/gemini-cli

  • Claude Code:

bun add -g @younggichoi/cliclaw

npm install -g @younggichoi/cliclaw

The package publishes under a scoped name (

@younggichoi/cliclaw

), but the installed command is simply.cliclaw

cliclaw init

Five steps, guided:

Welcome to cliclaw setup.

Step 1/5 β€” Telegram bot token
  Get one from @BotFather (/newbot) on Telegram.
  Bot token: 1234:ABC...
  βœ“ @yourbotname (id=...) verified

Step 2/5 β€” Detect installed coding agents
  βœ“ claude  2.1.139 (Claude Code)         @ ~/.nvm/.../bin/claude
  βœ“ codex   1.0.0                          @ /opt/homebrew/bin/codex
  βœ“ pi      unknown                        @ /opt/homebrew/bin/pi
  βœ“ gemini  0.42.0                         @ ~/.nvm/.../bin/gemini
  Default agent? [claude] (claude/codex/pi/gemini): claude

Step 3/5 β€” Authorize your Telegram account
  Open Telegram and send any message to @yourbotname now.
  Waiting up to 5 minutes... press Ctrl-C to abort.
  βœ“ Received from user_id=123456789
  Authorize this Telegram user? [Y/n] y

Step 4/5 β€” Corporate TLS interceptor (optional)
  $NODE_EXTRA_CA_CERTS: /path/to/Zscaler.pem
  Apply this CA certificate to the bot's LaunchAgent environment? [Y/n] y

Step 5/5 β€” Auto-start at login (launchd)
  Install LaunchAgent so the bot starts automatically on login? [Y/n] y
  βœ“ Installed ~/Library/LaunchAgents/com.alice.cliclaw.plist
  βœ“ Bot started.

All set.
  Logs:  tail -f ~/.cliclaw/logs/bot.log
  Test:  send /status in Telegram.

That's it. The bot restarts automatically after screen lock or a reboot.

Command What it does
cliclaw init
Interactive setup (token, agent detection, telegram-id capture, CA, launchd)
cliclaw start
Run the bot in the foreground (for testing)
cliclaw install-launchd
Install the LaunchAgent (auto-applies launchd.extraEnv from config.json)
cliclaw uninstall-launchd
Remove the LaunchAgent
cliclaw doctor
Check paths, agents, and plist status
cliclaw help
Help
Command What it does
/claude /codex /pi /gemini
Switch this chat's active agent (uninstalled agents are hidden)
/status
Per-agent session status + any job in progress
/health
Bot system status (uptime, memory, log sizes, chat/job counts)
/stop
Cancel this chat's running job (SIGTERM β†’ SIGKILL after 5s)
/reset
Discard only the current active agent's session
/reset all
Discard every agent session in this chat
/safety
Show safety-mode status β€” toggle with /safety on / /safety off
/start /help
Help
Any other text / photo Sent as a prompt to the active agent (photos are downloaded and their path is prepended to the prompt)

Switching agents keeps the old session intact β€” come back and continue. Sending a new prompt to a chat with a running job is rejected (use /stop

or wait).

All state is isolated under ~/.cliclaw/

:

~/.cliclaw/
β”œβ”€β”€ config.json              # mode 600; token, allowlist, launchd extraEnv
β”œβ”€β”€ safety.json              # persisted /safety on|off state
β”œβ”€β”€ sessions.json            # per-chat active-agent metadata
β”œβ”€β”€ sessions/                # per-chat codex / pi / gemini directories
β”œβ”€β”€ workspace/               # the agents' shared cwd (sandbox)
β”‚   β”œβ”€β”€ .claude/settings.json # dangerous-command hook + deny rules when safety is ON
β”‚   └── uploads/<chatId>/    # Telegram photo downloads
β”œβ”€β”€ logs/
β”‚   β”œβ”€β”€ bot.log              # token auto-masking applied
β”‚   β”œβ”€β”€ bot.err              # launchd stderr
β”‚   └── audit.jsonl          # audit log (decisions, safety state)
└── .sock/                   # confirm-gate IPC

The state directory can be moved with the CLICLAW_HOME

env var:

CLICLAW_HOME=~/my-bot cliclaw init

No absolute paths in config.json

. At startup, agents are discovered in three passes:

~/.local/bin

,~/.claude/local

,/usr/local/bin

,/opt/homebrew/bin

bin/<cmd>

of the newest node under$NVM_DIR

or~/.nvm

command -v <cmd>

in a login shell (PATH with.zshrc

loaded)

Undetected agents are gracefully skipped. The bot runs fine with any subset of the four.

ON (default):

  • Dangerous Bash commands ( rm -rf

,git push --force

, DROP,kubectl delete

, AWSdelete-*

,sudo

,curl|sh

, ssh prd-*, …) are re-confirmed via a Telegram inline keyboard[βœ… Allow] [❌ Deny]

β€” no response means auto-deny. - Claude's Read tool denies sensitive files: ~/.ssh/**

,~/.aws/**

,~/.gnupg/**

,~/.netrc

,~/.npmrc

,**/.env*

,**/*.pem

,**/id_rsa*

,**/id_ed25519*

,./secrets/**

. - Add your own regexes via confirmGate.extraPatterns

.

OFF: if your environment already has an external guard (pre-bash-guard

, EDR, …) and the bot's confirm prompts feel redundant, turn it off with one line in Telegram. Deny rules are disabled together. Every IPC request is still recorded in logs/audit.jsonl

as decision: allow, reason: safety_off

.

The state persists in $CLICLAW_HOME/safety.json

across restarts.

Consumes text_delta

from --include-partial-messages

and live-updates via editMessageText

, debounced at 1.5s. Past 3800 chars it rolls over to a new message.

Telegram photos/image documents are downloaded to workspace/uploads/<chatId>/<msgId>.<ext>

and the path is prepended to the prompt.

Claude: runs with--permission-mode bypassPermissions

β€” dangerous Bash is caught by the confirm gate, and sensitive files by safety-mode deny rules.Codex:sandbox=workspace-write

by default. Never usedanger-full-access

.Pi: default mode.** Gemini**:approvalMode=auto_edit

by default (edits auto-approved, destructive commands prompt). More autonomousyolo

or more conservativedefault

/plan

available.

Where Zscaler / Forticlient / Cisco Umbrella intercepts HTTPS, Node cannot trust Telegram's certificate and the bot cannot run. Step 4 of cliclaw init

auto-detects $NODE_EXTRA_CA_CERTS

or launchctl getenv NODE_EXTRA_CA_CERTS

, asks you, and persists it into launchd.extraEnv

in config.json

. Every later install-launchd

bakes it into the plist automatically.

Everything written to logs/bot.log

/ bot.err

is pre-redacted:

  • Telegram bot tokens ( \d{8,}:[A-Za-z0-9_-]{30,}

) - npm tokens ( npm_…

) - GitHub PATs ( gh[pousr]_…

) - exact matches of the live config.token

Defends against Time Machine backups, EDR, and shoulder surfing alike.

All user-facing messages, errors, and /help

are in Korean (English copy PRs welcome).

Answering "Yes" at step 5 of cliclaw init

:

  • Creates ~/Library/LaunchAgents/com.<username>.cliclaw.plist

(corporate CA baked in) - Loads and starts it immediately via launchctl bootstrap gui/$UID <plist>

  • Auto-restarts on login / boot / crash from then on
  • stdout β†’ ~/.cliclaw/logs/bot.log

, stderr β†’bot.err

Manual management:

launchctl kill SIGTERM gui/$UID/com.<username>.cliclaw

cliclaw uninstall-launchd

cliclaw install-launchd

The bot token = a remote shell into every installed agent. If it leaks,/revoke

at BotFather immediately.- An empty allowedUserIds

rejects all messages (fail-closed). - Keep config.json

at mode600

(init sets it automatically). - Never set confirmGate.enabled: false

or switch the codex sandbox todanger-full-access

. - Opting Gemini's approvalMode

intoyolo

auto-approves every tool β€” use it with full understanding. - When in doubt, /safety on

re-activates the deny rules instantly.

To install without the cliclaw init

flow:

git clone https://github.com/choiyounggi/cliclaw.git
cd cliclaw
bun install
mkdir -p ~/.cliclaw
cp config.example.json ~/.cliclaw/config.json
chmod 600 ~/.cliclaw/config.json
bun run bot.ts
bun run test
  • Dangerous patterns are regex-based β€” 100% classification is impossible; you own the policy.
  • No body-text streaming for Codex / Pi / Gemini (no structured events, or not integrated).
  • Gemini's dangerous commands rely solely on its own approvalMode

(bash-confirm IPC not integrated). - The hook holds the IPC while waiting for the user's decision.

  • No voice/file attachments (photos only).
  • Concurrent messages in the same chat are rejected ( /stop

or wait). - macOS only.

Per-version changes live on GitHub Releases.

Publishing a new version:

npm version patch          # or minor / major

git push --follow-tags

Then in the GitHub web UI: "Draft a new release" β†’ pick the tag β†’ Publish release. .github/workflows/publish.yml

runs automatically through npm publish --access public

. The workflow first verifies the release tag matches the package.json

version and fails without publishing on a mismatch.

One-time prerequisite: repo Settings β†’ Secrets and variables β†’ Actions β†’ register NPM_TOKEN with an npm token capable of 2FA bypass.

https://www.npmjs.com/settings/younggichoi/tokens/new- Issue a Granular Access Token or a Classic Automation token (with 2FA bypass) - Add the npm_…

token as the GitHub Actions secretNPM_TOKEN

Hardening option: switch to npm Trusted Publishing (OIDC) and no token is needed at all.

) - In .github/workflows/publish.yml

, addpermissions: id-token: write

, removeNODE_AUTH_TOKEN

, add the--provenance

flag - Delete the old NPM_TOKEN secret

MIT. See LICENSE

.

── more in #developer-tools 4 stories Β· sorted by recency
── more on @younggi choi 3 stories trending now
sponsored brought to you by zahid.host 4,200+ EU-deployed projects
reading about agents? ship yours in a single git push.

Run your AI side-project on zahid.host

EU-based hosting, git-push deploys, automatic HTTPS, no cold starts. Free tier with a custom domain β€” perfect for shipping the agent you just read about.

$git push zahid main
β†’ Live at https://your-agent.zahid.host βœ“
Get free account β†’ Pricing
from €0/mo Β· no card required
LIVE [news/show-hn-control-clau…] indexed:0 read:7min 2026-08-14 Β· β€”