Chat-channel ↔ ACP agent gateway with scheduler.
cli-gateway runs as a standalone service and lets you talk to ACP-compatible coding agents (Codex/Claude/Gemini, via ACP adapters) from:
It uses one ACP stdio agent process per conversation binding to avoid cross-talk and support concurrency.
It implements ACP stdio transport (JSON-RPC 2.0 over newline-delimited JSON) and supports the Client-side tool surface:
session/update streamingsession/request_permissionfs/read_text_file, fs/write_text_fileterminal/*ACP refs:
Requirements:
Node.js >= 18
Install
Option A (global):
npm i -g cli-gateway
Option B (no global install):
npx -y cli-gateway
On first run, if config is missing, cli-gateway opens an interactive setup wizard and writes:
~/.cli-gateway/config.jsonYou can edit that file any time to update tokens / agent command / defaults. See skills.md.
If installed globally:
cli-gateway
If using npx, use the same command each time:
npx -y cli-gateway
npm i
npm run dev
For crash protection in long-running deployments, run-guard.sh now runs as a background daemon with nohup, supports lifecycle commands, and automatically updates/builds on startup.
Start guard (default app command: node dist/main.js):
npm run start:guard
Restart/stop/status/logs:
bash scripts/run-guard.sh request-restart
bash scripts/run-guard.sh stop
bash scripts/run-guard.sh status
bash scripts/run-guard.sh logs
Custom command is supported:
bash scripts/run-guard.sh start -- npm run dev
bash scripts/run-guard.sh request-restart -- npm run dev
start/request-restart automatically runs:
npm i
npm run build
Then guard keeps restarting the app on abnormal exit with exponential backoff.
Before each launch attempt, guard also checks gateway.lock under CLI_GATEWAY_HOME (or ~/.cli-gateway), terminates the lock PID if still alive, and removes stale lock files.
Sandbox-friendly restart bridge:
scripts/restart-watcher.sh on the host (outside sandbox). It watches .run-guard/restart.request and calls run-guard.sh restart.bash scripts/run-guard.sh request-restart
nohup bash scripts/restart-watcher.sh >> .run-guard/restart-watcher.log 2>&1 &
Useful env vars:
RESTART_BASE_DELAY_SECONDS (default 2)RESTART_MAX_DELAY_SECONDS (default 30)RESTART_MAX_ATTEMPTS (default 0, unlimited)RESTART_ON_EXIT_0 (default 0)STOP_TIMEOUT_SECONDS (default 20)SKIP_UPDATE=1 to skip npm i + npm run buildGUARD_STATE_DIR to override pid/log directory (default ./.run-guard)RESTART_REQUEST_SOURCE payload source for request-restart (default manual)RESTART_REQUEST_COOLDOWN_SECONDS watcher debounce window (default 10)Feishu currently runs in webhook event-subscription mode:
http(s)://<host>:<feishuListenPort>/feishu/eventsfeishuAppId, feishuAppSecret, feishuVerificationToken, feishuListenPort/help show available commands/new start a fresh ACP session for this conversation/allow <n> select a pending permission option by index (fallback)/deny reject a pending permission request (fallback)/whitelist list|add|del|clear manage per-conversation permission whitelist by tool_kind (optional prefix scope)/cron help|list|add|del|enable|disable manage scheduled prompts/last show last run output for this session/replay [runId] replay stored session/update output for a run (best-effort)/ui verbose|summary set UI verbosity for this conversation/cli show|codex|claude show/switch ACP CLI preset for this conversation@zed-industries/claude-code-acp; make sure Claude auth is available (for example ANTHROPIC_API_KEY or Claude /login)./workspace show|~|~/...|/abs/path show/set per-conversation workspace root (alias: /ws)/help also includes ACP available_commands_update entries as cli-inline commands (best-effort)Telegram note:
- Chat-scoped command menu is synced best-effort from cli-inline commands. Commands with - are mapped to _ in Telegram UI.
Discord note:
- Built-in commands are available as slash commands (/help, /ui, /cli, /workspace, /new, /last, /replay, /allow, /deny, /whitelist, /cron).
- Slash commands are synced at startup (global + per-guild best-effort). Global command propagation may take time on Discord side.
- ACP cli-inline dynamic commands are not yet exposed as Discord slash commands.
- Inbound message processing uses reaction acks (🤔 while running, then 🕊 on success or 😢 on error), aligned with Telegram behavior.
- On fresh ACP sessions, the channel topic/description is injected as a global context block before the user prompt.
/workspace).tool_kind values per conversation via /whitelist add <tool_kind> (read|edit|delete|move|search|execute|think|fetch|switch_mode|other)./whitelist add read /abs/path/prefix (path kinds) or /whitelist add execute npm run (argument prefix). Non-matching calls still require approval.session/request_permission, the gateway synthesizes an interactive permission prompt and blocks the tool call until approved/denied.👍 allow, 👎 deny; ✅/❌ still accepted); /allow//deny remain as fallback.allow_always / reject_always) per conversation.summary (default): quieter.verbose: show structured messages for tool execution + plan/task updates.Set per conversation: /ui verbose|summary.
Tool-call UI is lifecycle-based (started/running/completed) and updates by tool-call id when supported by the channel sink.
Agent text is streamed by editing one message while output is text-only; when a tool call starts, the next agent text segment resumes in a new message.
/workspace) and run history follow the same binding scope./new starts a fresh ACP session but keeps conversation-scoped preferences (UI mode, workspace root, CLI preset, permission policies).ACP sessions are process-local; if the gateway restarts (or an idle runtime is GC'ed), the new ACP session would otherwise start "blank".
To reduce this, cli-gateway can replay recent conversation runs from the DB into the first prompt of a fresh ACP session:
contextReplayEnabled, contextReplayRuns, contextReplayMaxCharsThis repository is in active build-out; expect breaking changes.
$ claude mcp add cli-gateway \
-- python -m otcore.mcp_server <graph>