con

con-cli and surfaces

con-cli is the command-line handle for a running con app. It is installed with con on macOS, Windows, and Linux.

Most users do not need it. Use it when another program, script, test runner, or agent needs to inspect the visible terminal, create panes, drive pane-local surfaces, or ask con's built-in agent.

External agents should get a real terminal to work with, not a headless shell that behaves differently from what the user sees.

Check that con is reachable

Start con first, then run:

con-cli identify
con-cli tabs list
con-cli panes list

For scripts and agent adapters, prefer JSON:

con-cli --json identify
con-cli --json tree

con-cli asks the running app for state instead of guessing from outside the terminal.

Pane commands

Read terminal content:

con-cli --json panes read --tab 1 --pane-id 0 --lines 120

Create a visible split:

con-cli --json panes create --tab 1 --location right --command "htop"

Run a command visibly in a shell pane:

con-cli --json panes exec --tab 1 --pane-id 0 -- cargo test -q

Use pane_id for follow-up automation. Pane indexes can change when a user splits, closes, or rearranges panes.

Surface commands

A pane is a visible split region. A surface is a terminal session inside one pane. Every pane has one active surface, and a pane can host more surfaces when you want pane-local tabs.

This is the pattern most subagent tools want:

  1. Create the first worker as a visible split.
  2. Add later workers as surfaces inside that worker pane.
  3. Drive each worker by surface_id.
  4. Close each worker surface when it finishes.

Create the first worker pane:

con-cli --json surfaces split \
  --tab 1 \
  --pane-id 0 \
  --location right \
  --title worker-1 \
  --owner pi-interactive-subagents \
  --command "codex"

Create another worker inside the same pane:

con-cli --json surfaces create \
  --tab 1 \
  --pane-id <worker_pane_id> \
  --title worker-2 \
  --owner pi-interactive-subagents \
  --command "codex"

Wait before sending input:

con-cli --json surfaces wait-ready --surface-id <surface_id> --timeout 10

Drive the worker:

con-cli --json surfaces send-text --surface-id <surface_id> "explain this repo"
con-cli --json surfaces send-key --surface-id <surface_id> enter
con-cli --json surfaces read --surface-id <surface_id> --lines 120

Close it:

con-cli --json surfaces close --surface-id <surface_id>

If a worker pane was created only for owned surfaces, an orchestrator can ask con to close that pane when its last surface closes:

con-cli --json surfaces close \
  --surface-id <surface_id> \
  --close-empty-owned-pane

Ask the built-in agent from a script

agent ask uses the same in-app agent session you see in the side panel:

con-cli --json agent ask --tab 1 "Summarize what is happening in this tab"

That means the response appears in con, uses the tab's current context, and follows the same provider and approval settings as the UI.

Automation rules

The socket is local to the user session. It is not a remote API, and it does not change shell permissions. If a script can drive con-cli, it can drive the visible terminal you already opened.

Socket path

Release builds use the default socket for the installed channel. Debug builds use a separate debug socket so a local development build can run beside the installed app.

Set CON_SOCKET_PATH only when you intentionally run con on a custom endpoint:

CON_SOCKET_PATH=/tmp/con-alt.sock con
con-cli --socket /tmp/con-alt.sock identify