Codeflow streams coding agent sessions (Claude Code, Codex, Gemini CLI, etc.) to Discord or Telegram in real-time. Use when invoking coding agents and wanting transparent, observable dev sessions — no black box. Parses Claude Code's stream-json output into clean formatted messages showing tool calls, file writes, bash commands, and results with zero AI token burn. Use when asked to "stream to Discord", "stream to Telegram", "relay agent output", or "make dev sessions visible".
Live-stream coding agent sessions to Discord or Telegram. Zero AI tokens burned.
Breaking note:
/codeflowCODEFLOW_*First-time setup: see references/setup.md for webhook creation, unbuffer install, bot token, and smoke test.
For hard thread-scoped enforcement (skill-owned /codeflow + plugin tool blocking), install the bundled enforcer plugin once on the host:
bash {baseDir}/scripts/codeflow enforcer install --restart
If the plugin is missing, /codeflow still works in soft mode. The button/no-button behavior must be owned by the deterministic router script, not by free-form assistant prose.
Run a local sanity check bundle (Python syntax compile + unit tests + bash -n):
bash {baseDir}/scripts/codeflow check
/codeflow command contract (session-scoped)When user invokes /codeflow, treat it as a control command first, then as a session-scoped declaration:
Important command-order rule:
/codeflow, /codeflow on, /codeflow status, /codeflow off, or callback_data: cfe:install is not a request for a coding task.NO_REPLY because the router already sent the user-facing control reply./codeflow command and an actual coding task, run the control router first, then continue with the remaining task text.Soft fallback contract:
/codeflow is always owned by the skill. That is the public entrypoint and the soft fallback path.before_prompt_build / before_tool_call blocking on top of the skill flow; it does not own /codeflow.session_status and read the current sessionKey for the active OpenClaw conversation.bash {baseDir}/scripts/codeflow control \
--session-key "<sessionKey>" \
--text "<raw user message text>"
/codeflow or /codeflow on|enable|activate/codeflow status/codeflow off|disable|deactivatecallback_data: cfe:install (or raw cfe:install)/codeflow message, always route it here first. Do not improvise a textual reply.codeflow guard activate|deactivate when neededcodeflow enforcer status --json --session-key <sessionKey>message.sendrecommendation.buttons when Telegram routing is availableInstall: / Restart: command text when buttons cannot be sentNO_REPLY.3 and stdout starts with NEED_LLM_ROUTE, parse the JSON payload on the next line ({message, buttons}) and send that yourself. If your current channel/tooling cannot attach buttons, keep the plain text exactly as provided.cfe:install, be explicit that gateway restart may interrupt or reset the current conversation/thread.Guard enforcement (hard constraint in script):
run, resume, review, parallel) are guard-protected by default. review and parallel must precheck the guard before they clone repos, create worktrees, or post start summaries.codeflow guard activate|deactivate|status|current) bypass the precheck.${XDG_STATE_HOME:-$HOME/.local/state}/codeflow/guard-audit.jsonl (stores commandHint only — redacted + truncated; never the full command)./codeflow in the same chat/topic.Default inference rules (do not ask unless ambiguous):
Codex session policy under /codeflow:
-w <workdir> when available (keyed by realpath(workdir)).Under /codeflow, avoid asking for workdir/platform/chat if derivable from context.
Launch with exec background:true. Background exec sessions survive agent turns. Exit notifications (e.g. notifyOnExit) are provided by the host runtime (OpenClaw), not by Codeflow itself.
exec background:true command:"cat <<'PROMPT' | {baseDir}/scripts/codeflow run -w ~/projects/myapp -- claude -p --dangerously-skip-permissions --output-format stream-json --verbose
Your task here
PROMPT"
Prompt-quoting tip (avoid shell escaping footguns):
codex exec (and codex exec resume) reads the prompt from stdin when PROMPT is - (or omitted in exec). For multi-line prompts or prompts containing shell metacharacters (e.g. backticks), prefer stdin + a quoted heredoc.claude -p also supports reading the prompt from stdin; prefer stdin for the same reasons.Note the session ID from the response — use it to monitor via process.
Public entrypoint (do not call _internal/ scripts directly):
bash {baseDir}/scripts/codeflow <command> [...]
Commands:
codeflow run [run-flags] -- <agent command> — start a relay sessioncodeflow guard activate|deactivate|status|current [run-flags] — manage/query the session-scoped guardcodeflow control --session-key <key> --text <raw_text> — deterministic /codeflow soft-mode control routercodeflow resume [run-flags] <relay_dir> — replay a previous session from stream.jsonlcodeflow review [...] <pr_url> — PR review modecodeflow parallel [...] <tasks_file> — parallel tasks modecodeflow bridge [...] — Discord gateway bridgecodeflow enforcer install|update|uninstall|status [--json] — manage/query the bundled OpenClaw enforcer plugincodeflow check — local checks (syntax + unit tests)codeflow smoke — config/prereq smoke testSee bash {baseDir}/scripts/codeflow --help for the canonical CLI.
codeflow run)| Flag | Description | Default |
|---|---|---|
-w <dir> | Working directory | Current dir |
-t <sec> | Timeout | 1800 |
-h <sec> | Hang threshold | 120 |
-n <name> | Agent display name | Auto-detected |
-P <platform> | discord, telegram, auto (inferred) | discord |
--thread | Post into a Discord thread | Off |
--tg-chat <id> | Telegram chat id (when -P telegram) | — |
--tg-thread <id> | Telegram thread/topic id (optional) | — |
--skip-reads | Hide Read tool events | Off |
--new-session | For Codex exec: force a new Codex session | auto policy |
--reuse-session | For Codex exec: require and reuse previous session | auto policy |
--prompt-stdin | Enforce prompt via stdin for supported headless agents (Codex exec / Claude -p) | Auto (OpenClaw → on) |
--prompt-argv | Allow legacy argv prompt for supported headless agents | Auto (non-OpenClaw → on) |
Prompt mode can also be set via env: CODEFLOW_PROMPT_MODE=auto|argv|stdin (default: auto).
Rate limiting note (Telegram hardening): parse-stream now routes all delivery through an in-process delivery governor with strict 429 handling:
next_allowed_at = now + retry_after + 1s (strictly follows Telegram retry_after; adds +1s to avoid immediately hitting 429 again)next_allowed_atfinal > event > statestate cards (thinking/cmd) are strict snapshot overwrite (latest-wins); during 429 windows updates are merged in memory and only the latest snapshot is applied once the window opensIf you still need to reduce output volume, use CODEFLOW_OUTPUT_MODE (see below) plus existing knobs (--skip-reads, CODEFLOW_SAFE_MODE=true, CODEFLOW_COMPACT).
For PR review, parallel tasks, Discord bridge, and Codex structured output: see references/advanced-modes.md.
exec responsecodeflow run posts the session header and streams events to the target channel automaticallyprocess log / process poll; stop via process killCompletion notifications are runtime-dependent (OpenClaw). Codeflow itself simply exits when the inner agent command exits.
Backup: Append this to the inner agent's prompt for an additional signal:
When completely finished, run: openclaw system event --text "Done: <brief summary>" --mode now
process poll sessionId:<id> # Check status
process log sessionId:<id> # View recent output
process kill sessionId:<id> # Stop session
If you stream relay output into a shared channel, enable:
CODEFLOW_SAFE_MODE=trueEffects:
Write)command_execution output, raw mode output)Control how verbose channel posts are via env:
CODEFLOW_OUTPUT_MODE=minimal|balanced|verbose (default: balanced)
minimal: only warning/error/finalbalanced: key progress + warning/error/finalverbose: near-full (debug; Telegram is more likely to hit 429)Telegram compact state cards (strict snapshot overwrite):
edit the same anchor (no extra posts)edit replaces the full snapshot (no accumulated log); during 429 windows updates are merged in memory (latest-wins) and only the latest snapshot is applied once the window opens…(truncated); otherwise we do not fold/hide contentWhen -P telegram is used, Codex sessions run in compact mode by default:
Next turn starts a fresh pair of rolling messages.
Override with env:
CODEFLOW_COMPACT=true|false (default auto, where Telegram => true)Telegram 429 / anchor stability (compact mode):
retry_after + 1s (not a fixed 10s); when the window opens, it flushes by priority (final > event > state)Telegram adapter memory guard (oversized message edit groups):
CODEFLOW_TELEGRAM_EDIT_GROUPS_MAX=<n>: max tracked groups (LRU, default 64; set 0 to disable tracking)CODEFLOW_TELEGRAM_TRACK_EDIT_GROUPS=true|false: enable/disable tracking (default true)Notes:
platforms/telegram.py edit(); compact mode uses edit_single and is unaffected.edit() cannot delete/overwrite prior tail messages for already-split posts.For codex exec ..., Codeflow enforces a session policy in code (not just docs):
auto: reuse previous Codex session for the same workdir when available/new under auto: force a new session for that run--new-session: force new session--reuse-session: require previous session and force resume (error if missing)Optional env overrides:
CODEFLOW_CODEX_SESSION_MODE=auto|new|reuse (default auto)CODEFLOW_CODEX_SESSION_MAP=/tmp/dev-relay-codex-sessions.json (session map path)| Agent | Output Mode | Status |
|---|---|---|
| Claude Code | stream-json | Full support |
| Codex | --json JSONL | Full support |
| Any CLI | Raw ANSI | Basic support |
/tmp/dev-relay-sessions/<PID>.json (auto-removed on end)/tmp/dev-relay.XXXXXX/stream.jsonl (7-day auto-cleanup)CODEFLOW_STREAM_LOG=full|redacted|off (default: full; if CODEFLOW_SAFE_MODE=true and CODEFLOW_STREAM_LOG is unset, default becomes redacted). off writes minimal metadata only (resume/debug is limited).stream.jsonl as codeflow.delivery.* (exceptions only; no message bodies/tokens/URLs)./tmp/dev-relay.XXXXXX/delivery-summary.json (single-file summary; includes rate-limit counts, drops, etc).${XDG_STATE_HOME:-$HOME/.local/state}/codeflow/guard.json (stores commandHint only — redacted + truncated)${XDG_STATE_HOME:-$HOME/.local/state}/codeflow/guard-audit.jsonl (JSONL)process submit sessionId:<id> data:"message"
{baseDir}/scripts/ (see codeflow/scripts/_internal/bin/lib.sh).curl; tokens/webhooks do not appear in child process argv (avoids ps leakage)./tmp/dev-relay-sessions/<PID>.json is written atomically (tmp + rename, best-effort fsync) and minimized (does not persist full command/context).