中文 | English
Unified session orchestrator for terminal-based AI coding tools.
CodeCLI wraps Claude Code, Cursor Agent, OpenAI Codex CLI, OpenCode, and Bash into one control layer. Run multiple AI sessions simultaneously and manage them from your local terminal, Telegram, WeChat, an HTTP bridge, or a LangChain tool — all sharing one command system.
When you run multiple AI CLIs at once, things get messy fast:
- Switching between terminal windows for different tools
- Can't see session status remotely
- A tool is stuck on a
y/nconfirmation and you're not at your desk - TUI output is noisy and hard to read in messaging apps
- No unified way to manage sessions across tools
CodeCLI solves this by putting all your AI sessions behind one command interface, accessible from anywhere.
| Tool | Command | Default Executable |
|---|---|---|
| Claude Code | /claude [cwd] |
claude |
| Cursor Agent | /cursor [cwd] |
agent |
| OpenAI Codex CLI | /codex [cwd] |
codex |
| OpenCode | /opencode [cwd] |
opencode |
| Bash | /bash [cwd] [cmd] |
bash |
CodeCLI does not install these tools — you install them yourself, and CodeCLI launches, manages, and orchestrates them.
| Channel | Command | Use Case |
|---|---|---|
| Local CLI | python main.py cli |
Direct terminal access |
| Telegram | python main.py telegram |
Remote control from your phone |
python main.py weixin |
Remote control via WeChat (QR login) | |
| HTTP | python main.py http |
API bridge for external systems |
| Embedded Tool / LangChain | create_tool_adapter() |
In-process agent integration |
All transports use the shared message router, with channel-specific capabilities and delivery behavior. python main.py tool is a lifecycle mode, not a standalone RPC service.
Requirements: Python 3.10+, at least one AI CLI tool installed.
# Clone the repository
git clone https://github.com/ax128/CodeCLI.git
cd CodeCLI
# Install dependencies
pip install -r requirements.lock # pinned for reproducible installs
# or: pip install -r requirements.txt
# Create runtime config (required)
cp config/codecli.example.json config/codecli.json
# Run locally
python main.py cli
# Start a Claude session
/claude /path/to/project
# Send a task
Review this project and list potential issues
# Check status
/status
# See output
/output- Create a bot with @BotFather and get your token
- Copy config:
cp config/codecli.example.json config/codecli.json - Pick a Telegram config name
<name>(e.g.agent) and setdefault_channel.chatto<name> - Set
channels.telegram.<name>.enabled=trueand configurebotToken - If
chatIdis empty, runpython main.py telegramand send any message to the bot within 120s — CodeCLI discovers and saves the chat ID - Optional: set
channels.telegram.<name>.bindToken(orbindTokenEnv) to require sending/bind <token>instead, so a third party can't bind the bot first during discovery - To enable
/file, setfileTransferPasswordto a strong non-placeholder value; an empty value disables file transfer
python main.py weixin
# First run: QR code is drawn in the terminal (install deps first: pip install -e .[weixin], or pip install Pillow qrcode), then scan with WeChat; credentials saved automatically
# Subsequent runs: connects directlyOr login separately: python -m cli_orchestrator.weixin_login
WeChat uses a compact mobile /help view by default. Use /help cursor for Cursor-specific advanced commands.
Note: the WeChat channel is text-only (no images/files/voice).
- In
config/codecli.json, setchannels.http.<name>.enabled=true - Configure either a strong inline
authTokenor anauthTokenEnvwhose environment variable is set; leaveauthTokenEnvempty when using the inline token - Run
python main.py http(usesdefault_channel.http) orpython main.py http:<name> - POST messages and poll events (default template uses
127.0.0.1:8787,/message,/events; auth viaX-CodeCLI-TokenorAuthorization: Bearer ...):
# Send one message
curl -sS -X POST "http://127.0.0.1:8787/message" \
-H "Content-Type: application/json" \
-H "X-CodeCLI-Token: <authToken>" \
-d '{"sender":"alice","text":"/status"}'
# Poll events (drains the queue for that sender)
curl -sS "http://127.0.0.1:8787/events?sender=alice" \
-H "X-CodeCLI-Token: <authToken>"Responses: POST /message returns {"ok": true, "accepted": true, "channel_key": "..."} and GET /events returns {"ok": true, "events": [{"text": "...", ...}]}.
Undelivered events are capped per sender by maxEventsPerSender (default 100; oldest are
dropped once the cap is hit). Set eventsStorePath to persist the queue across restarts;
leave it empty for memory-only events. Delivery is best effort: in-memory pending replies,
event queues, and ephemeral sender registrations are bounded and may be discarded after
expiry, capacity pressure, channel shutdown, or repeated transport failure.
./start.sh telegram # Run as background daemon with Telegram
./start.sh weixin # Run as background daemon with WeChat
./start.sh # Use default channel from configThe Bash/Linux script starts a new instance with nohup, writes its PID plus Linux
process-start token under logs/, and tails logs/codecli.out. It only stops the process
recorded by this checkout when /proc confirms its working directory, exact Python argv,
and stored start token. If identity cannot be verified, the script exits without deleting
the PID file or starting a duplicate. Press Ctrl+C to detach; CodeCLI keeps running.
Multi-session management — Run multiple AI sessions in parallel, switch between them with /use <id>, send to a specific session with /1 text.
Clean output delivery — TUI chrome, ANSI codes, spinners, and box-drawing characters are stripped. Telegram and WeChat remote chat channels deliver final results by default, while HTTP keeps explicit request/response acknowledgements.
Output mode control — result mode (default) delivers only completed output. stream mode delivers incremental updates. Automatic policy switches to stream for long-running tasks like test suites.
Key injection — /enter, /esc, /tab, /y, /n, /key ctrl+c — handle confirmations and navigate TUI tools remotely.
State detection — Automatically detects running, idle, completed, need_confirmation, error, stopped.
Cross-channel ownership — Sessions track which channel started them. Use /handoff and /claim to transfer ownership between channels.
Message verification gate — Optional password protection with configurable TTL and startup grace period.
LangChain integration — Use CodeCLI as a LangChain Tool or LangGraph ToolNode, with structured responses including status_hint, next_suggested_commands, and normalized session state.
The table below is a summary. See the complete command reference for aliases, Cursor advanced commands, channel differences, and error behavior.
| Category | Commands |
|---|---|
| Start | /claude, /cursor, /codex, /opencode, /bash, /start <cmd> |
| Send | direct text, /1 text, /send [id|#id] [--output result|stream] <text>, /send [id] -- <text> |
| Confirm | /y, /n, /enter, /esc, /tab, /key <name> |
| Monitor | /status, /output, /screen, /logs, /watch, /list, /all |
| Mode | /mode [id] [result|stream] |
| Manage | /use <id>, /stop, /stopall, /kill <id>, /restart |
| Paths | /cd, /pwd, /ls [--all] [path], /paths, /cat <file> |
| Ownership | /owner, /handoff, /claim |
| System | /ping, /info, /help, /reboot, /exit |
All commands with [id] default to the active session when omitted.
cp config/codecli.example.json config/codecli.jsonconfig/codecli.json is required for all modes. Telegram and HTTP channels will not start unless their enabled flag is set to true (HTTP also requires a strong non-placeholder token from either authToken or authTokenEnv).
Key sections in config/codecli.json:
| Section | Purpose |
|---|---|
default_channel |
Which channel starts by default |
channels.telegram |
Bot token, chat ID, file transfer password |
channels.weixin |
WeChat token, account ID (auto-filled by QR login) |
channels.http |
HTTP bridge bind address, port, auth token |
cli_path |
Override executable paths for each tool |
security.verification |
Optional message password gate |
output_policy.rules |
Custom automatic output mode rules |
worked_path |
Saved directory aliases (p1, p2, ...) |
See config/codecli.example.json for the full template.
LangChain is optional and not included in requirements.txt / requirements.lock. Install it before using create_langchain_tool(...) (e.g. pip install langchain pydantic).
from cli_orchestrator import create_langchain_tool
tool = create_langchain_tool(response_format="content")
result = tool.invoke({"message": "/claude /workspace/project"})
print(result["reply_text"])
print(result["active_session"])For LangGraph ToolNode, use response_format="content_and_artifact". See the
embedded Tool API contract for the complete structured response and
best-effort delivery limits.
- Command reference — commands, aliases, paths, and channel behavior
- Architecture — module ownership, data flow, and current limitations
- Embedded Tool API — Python/LangChain contract and response schema
- Audit prompt — repository-specific review checklist
Chat routes all channel messages into a shared command/session layer. SessionManager
owns lifecycle and ordered notifications. Provider adapters select either structured
headless subprocesses (Claude, Cursor, Codex, OpenCode) or PTY transport (primarily Bash
and custom commands). Channel capabilities and known compatibility leftovers are detailed
in the architecture document.
Install the optional development toolchain first:
pip install -e ".[dev]"Then run the checks appropriate to your change:
python -m compileall cli_orchestrator # Compile check
python -m pip check # Dependency check
python -m mypy cli_orchestrator # Static type check
python -m pytest # Test suiteThe full verification gate (used before a release) is:
python -m compileall -q cli_orchestrator
python -m mypy cli_orchestrator
python -m pytest --cov=cli_orchestrator --cov-report=term
pip-audit -r requirements.lock
python -m buildThe test suite under tests/ documents the intended behavior contract (commands, channels, process wrappers, security boundaries).
- Never commit
config/codecli.json— it may contain real tokens and passwords config/codecli.example.jsonis the safe template for version control- Treat
logs/andsessions/as local runtime data, not release artifacts - The message verification gate adds optional password protection for remote channels
- The embedded Tool API bypasses the message gate (designed for in-process programmatic access)
- Leave Telegram
fileTransferPasswordempty to disable/file; never useCHANGE_ME - Claude sessions launch with
--permission-mode acceptEditsplus an explicit--allowedToolsallowlist (Read,Write,Edit,Glob,Grep,Bash,WebFetch) rather thanbypassPermissions. Anything outside the allowlist still triggers a confirmation prompt, which CodeCLI surfaces asneed_confirmation. The list is defined once incli_orchestrator/tools/claude.py(CLAUDE_ALLOWED_TOOLS) and shared by the interactive launch, print mode and the session process — narrow it there if you want a stricter policy. - WeChat
tokencan be supplied out-of-band viachannels.weixin.<name>.tokenEnv(name of an environment variable), same asauthTokenEnvfor the HTTP channel
MIT License. See LICENSE.