ADE CLI control plane
SkillProductivityUse this skill when you need to hand work to another ADE agent, wait for or check whether delegated work has finished, read another agent's chat transcript, report status or raise a question on the Work row, schedule a wake-up for later, or spawn a child lane — and generally whenever you need ADE's own state (lanes, chats, actions, proof, secrets, runtime/socket status) through the `ade` CLI.
Available today. Use it from your connected AI after setup.
No other account needed.
Connect ahel once, and every AI you use reads what you have installed.
Then ask your AI: use the ADE CLI control plane skill
What this skill tells your AI
The instructions your AI receives, as published by arul28/ade in apps/desktop/resources/agent-skills/ade-cli-control-plane/SKILL.md and read by ahel’s review.
Core rule
Use normal shell commands for local repo edits, tests, and Git inspection. Use ade when you need ADE state or ADE-owned services: lanes, chats, PR metadata, proof/artifacts, managed terminals, App Control, iOS Simulator, browser, settings, project secrets, usage, updates, or service actions.
Do not route ordinary repo commands through ADE chat-attached terminals. Those
terminals exist so ADE Work chats can expose long-running process logs or let a
user inspect/control a chat-owned shell. In a tracked CLI session, run normal
shell commands through the CLI's own shell/tooling; use ade terminal ... only
to inspect or control an existing ADE-owned terminal session.
First checks
- Run
ade doctor --textwhen the ADE environment is unclear. - Run
ade help <command>orade help <command> <subcommand>before guessing flags. - Prefer
--textfor human-readable output and JSON output when scripting. - Use
ade actions list --textorade actions list --domain <domain> --textas the escape hatch for service methods without a typed command.
Project secrets
ADE project secrets are encrypted, project-scoped, and shared by ADE Desktop, the CLI, runtime-backed actions, lanes, and agents on the same machine. They live under the active project root, not inside an individual lane worktree.
Use them only when the user names a secret or clearly asks you to use a stored
credential. List output is metadata-only; get prints the value, so avoid
echoing it into logs or chat unless the user explicitly asks.
ade secrets list --text
ade secrets get STRIPE_API_KEY --text
ade secrets set STRIPE_API_KEY --value sk_...
printf %s "$TOKEN" | ade secrets set TOKEN --stdin
ade secrets set TOKEN --value-file token.txt
ade secrets delete STRIPE_API_KEY
ade actions list --domain project_secret --text
Socket mode
Use --socket when the CLI and ADE desktop drawer must share live state. This matters for App Control, iOS Simulator, Preview Lab, browser tabs, terminal logs, context selection, and proof drawer updates. The ade-app-control, ade-ios-simulator, and ade-browser skills all assume it.
Owning a drawer surface
ADE injects ADE_LANE_ID and ADE_CHAT_SESSION_ID into every agent it launches, and the drawer services (App Control, iOS Simulator, browser) carry them so the Work tools pane attributes what you drive to your lane rather than to the visible chat. When you attach to something already running instead of starting it yourself, run that surface's claim subcommand first — ade --socket app-control claim, ade --socket ios-sim claim, ade --socket browser claim — or Work will keep showing the previous owner.
Runtime daemon vs. desktop bridge
Most domains (lane, git, chat, app_control, ios_simulator, etc.) run inside the runtime daemon at ~/.ade/sock/ade.sock and work whether or not the desktop is open.
A small set of domains require the desktop bridge because the underlying service needs real Electron APIs. Today that is just built_in_browser; the ade-browser skill documents that hop, its socket path, and the exact failure text when no desktop is running. A bridge-backed domain with no desktop also surfaces as Domain unavailable — report the blocker and continue with the rest of the control plane, which is unaffected.
Linear issues attached to your session
The ade-linear skill is the home for working an attached Linear issue: the env vars ADE injects, the read/write commands, and attach/detach. You never need a Linear token — ade linear ... routes over the daemon to the desktop runtime, which holds the credentials. Two attachment commands live outside that skill's ade linear surface:
ade chat attach-linear-issue <session> --issue-id ENG-431
ade lanes link-linear-issue <lane> --linear-issue-json '{...}'
Sync and pairing
ade sync web [--open]— print + copy the web-client pairing link and code (pair a browser to this machine).
Start work from an issue:
ade new chat --mode chat --lane <lane> --provider codex --model <m> --type subagent --prompt "Work this issue"
ade lanes create-from-linear --issue-id ENG-431 --start-chat --provider codex --model <m> --type subagent
ade chat create --from-linear-issue ENG-431 --type subagent # compatibility path: chat with the issue attached + kickoff
Chat vs. CLI sessions
Use ade new chat as the canonical launch command. It mirrors the desktop New
Chat mode toggle:
ade new chat --mode chat --lane <lane> --provider codex --model openai/gpt-5.6-sol --type subagent --reasoning-effort xhigh --permissions full-auto --no-fast --prompt "Fix the issue"
ade new chat --mode cli --lane <lane> --provider codex --model openai/gpt-5.6-sol --type subagent --reasoning-effort xhigh --permissions full-auto --no-fast --prompt "Fix the issue"
ade new chat --mode chat --lane auto --lane-name fix-issue --type subagent --prompt "Fix the issue"
--mode chat creates a persistent ADE Work chat. --mode cli starts a tracked
provider CLI terminal. Both accept lane, provider, model, reasoning effort,
permission mode, fast/no-fast, and prompt flags. Use --lane auto or
--auto-create-lane when the desktop UI would use the auto-create lane row.
Spawning agents
ade new chat --mode chat --provider <p> --type <subagent|peer> --prompt "..."
spawns a tracked ADE agent and automatically links it to the current chat
through ADE_CHAT_SESSION_ID. The type is required for every parented agent
spawn; omitting it is a hard error whose message includes the decision rule.
There is no silent none type.
The type controls what happens to the parent when the child finishes: a
subagent turn wakes or steers the parent, a peer turn only leaves a quiet
completion note. --no-parent creates an unparented top-level session.
For persistent Work chats, a subagent turn reports back with its child turn id
and latest assistant summary whenever the parent still owns the child's
mission — including turns the child's own scheduled wakeups start, so a long
self-paced mission still reports when it actually finishes. ADE steers an active
parent or wakes an idle parent. Once a human messages the child directly,
ownership moves to the human and completions become quiet notes until the parent
dispatches again. The persisted parent link and
turn metadata survive brain restarts; a delivery failure retries and then
becomes a visible warning in the child. Every child receives
ADE_PARENT_CHAT_SESSION_ID and direct-report guidance as a recovery path.
Tracked provider CLI sessions also require subagent or peer when parented
and receive the same lineage environment. Their process boundary is still
checked with ade chat wait; use --mode chat when you need automatic
turn-completion wakeups and summaries.
When the new work must carry the current lane's unmerged commits, follow the
child-lane rule in the ade-lanes-git skill and use
ade lanes child --lane <current> --name <n> instead of a fresh lane.
Transcript reads are silent and available for any project-backed chat in any
registered project on this machine. Personal/no-project chats remain on the
separate --personal surface and are not included. Reads are bounded by
default; inspect a recent window, then page older content deliberately:
ade chat read <session> --limit 20 --max-chars 8000 --text
ade chat read <session> --page --cursor <nextCursor> --limit 20 --max-chars 8000 --text
The first read returns truncated when more content exists. A paged read
returns nextCursor; repeat only while you actually need older context. Do not
dump an entire long transcript into the model context. --max-chars is a hard
response ceiling; a single oversized entry is visibly clipped and the response
sets truncated. This machine-wide route is read-only; it does not broaden
cross-project message or mutation routing.
Within the active project, use ade chat show <session> --text before messaging
a chat you do not own:
- If you just need to hand context/directive/status to another chat, prefer
ade chat message <session> --kind auto --text .... ADE inspects the target: active turns are steered, idle chats are woken with a new turn, and the result reports the route (sendMessage,steer, orinterrupt-replace) plus whether a steer was queued. - If
statusisactiveand it is not waiting for user input, useade chat steer <session> --text .... This routes through the provider's active-turn path: Codex receivesturn/steer, Claude stages a steer message, and Cursor/Droid/OpenCode queue the message for the next safe boundary. - If the chat is idle/dormant, use
ade chat send <session> --text ...to start the next turn. The CLI also checks the session summary and will steer instead of sending when the target is already active, but prefer the explicit verb when your intent is to steer. - If you need to wait for a subagent before reading final output, use
ade chat wait <session> --for idle --timeout-ms <ms>(also supportsactive,awaiting-input, andterminal). - If you need to stop or redirect a running chat, use
ade chat message <session> --kind interrupt-replace --text ...or, when you need manual control,ade chat interrupt <session>first, thenade chat send ...with the new instruction. Do not send a second normal turn into an active chat and hope the provider interprets it as steering.
Checking whether delegated / backgrounded work finished
Prefer harness-tracked delegation and wait on the tracked handle — do not background a raw CLI and then guess at its state:
Native subagents vs ADE child chats
Use the provider's native subagent tool first when the current chat exposes one
and the result belongs in the same provider SDK thread. This is especially
important for Claude: a Claude chat should use its native Agent/Task tool
(and its native model override for another Anthropic-family model) instead of
creating a second ADE chat in the same lane with --type subagent merely to
run Opus, Sonnet, or another Claude model.
Use an ADE child chat when the work needs an independent durable transcript,
its own wake/schedule, cross-provider execution, separate permissions, or a
user-visible tracked lifecycle. Do not treat --type subagent as the generic
replacement for a provider-native child; it adds a separate ADE session and
should be intentional.
- ADE Work-chat subagents: spawn with
--type subagent; every completed turn wakes or steers the parent while the parent owns the mission. Read the bounded transcript after that signal when you need more detail. You never poll a transcript in a loop. - Tracked provider CLI subagents: wait with
ade chat wait <session> --for idle|terminal --timeout-ms <ms>or require an explicit direct report through$ADE_PARENT_CHAT_SESSION_ID. - A background provider CLI (e.g.
codex exec): run it detached with its own log and stdin closed, capturing the PID immediately, e.g.codex exec "…" </dev/null >"$LOG" 2>&1 & CODEX_PID=$!(closing stdin is required — without itcodexblocks forever on "Reading additional input from stdin…"). The authoritative completion signal is the waited PID: runwait "$CODEX_PID"and read its exit status as the outcome. Log end-of-run markers, a new session file under~/.codex/sessions/<date>/, andpgrepare supporting evidence only — not a deterministic done signal. Do keep the session-file existence check as a liveness diagnostic for wedge detection: no session file after ~2 min means the process wedged (kill and relaunch). Never check completion with a barepgrep <name>— it matches your own shell and sibling processes (the self-match trap); if you must usepgrepat all, match the full command line and exclude yourself (pgrep -f "codex exec" | grep -v $$), never the bare program name.
Session lifecycle: snooze, wake (settling is not yours)
You cannot settle or unsettle a session. ade chat settle, ade chat unsettle,
ade session settle, and ade session unsettle were removed: whether work is
actually finished is a subjective judgment, and a chat that settles itself
disappears from the user's active list on your say-so. A row leaves the active
list only when the user settles it in ADE, or when its PR merges and the
deterministic autoSettleLaneSessionsOnPrMerge policy files the lane's
sessions. Running a removed command fails with that explanation.
What to do instead when you finish: say so in your final message, and use
ade chat note "<one-line status>" to leave a durable status line on the Work
row. If you are blocked, ade chat ask "<question>" raises the row's hand.
What note and ask do to the Work row
They are two separate signals on the row the user is looking at:
ade chat notesets the row's status line. It shows a durable one-line summary of the current state; it does not change the row's phase. Length guidance and the hard display bound live inapps/desktop/src/shared/sessionStatusNote.ts(a note past that bound is truncated with an ellipsis, so put the decisive state first). Good:CI green; awaiting Codex reviewBad:Working,Still looking,Blocked, orDone.ade chat askraises the row's hand, moving it to Needs you. Because a note alone never changes the phase, an idle row with only a note can read as Done — when you are genuinely blocked on the user, callnotefor the context and thenaskfor the exact question.- The next accepted user message clears the hand-raise and the row returns
to Working while the reply is handled. If the reply does not unblock you,
leave an updated note and
askagain.
Snooze is the lifecycle verb you do own. The typed family takes the session id
as a positional, also accepts --session, and falls back to
ADE_CHAT_SESSION_ID when you omit it.
ade session show <id> --text # lifecycle state incl. wake reason
ade session snooze <id> --for 1h # also 30m, 4h, 1d (cap 30d)
ade session snooze <id> --until <iso> # mutually exclusive with --for
ade session snooze <id> --until-asked # no deadline; only a hand-raise returns it
ade session wake <id> [--reason <reason>]
ade session clear-woke <id>
Semantics that hold on every surface:
- Snooze is a visibility overlay, not a lifecycle state. It never changes a
session's canonical phase; it only files the row in a quiet tier. Timer expiry
is derived by comparing
snoozedUntilto now — nothing schedules a wakeup. This is why snooze is safe for you and settling is not: snooze quiets a row you are waiting on without claiming the work is done. - A snoozed session hand-raises early when it needs approval or input, when
it hits an error newer than the snooze, or when a running turn completes.
The row then carries a woke marker plus the reason (
needs approval,errored,turn finished,snooze ended). - Nothing derives a settle. A clean process exit means the CLI ended, not
that the work is done — such a row is
ended, neversettled.
Generic action-domain equivalents you can call: session.snoozeSession,
session.snoozeSessions, session.wakeSession, session.wakeSessions,
session.clearWokeMarker. The settle writers (session.settleSession,
session.unsettleSession, session.settleSessions,
session.unsettleSessions, session.setSettleOverride) are CTO-only and will
refuse your calls.
Lane branch drift
A lane's worktree HEAD can drift from the branch ADE recorded (someone runs
git checkout inside it). While drifted, PR matching is paused, because a PR
created from that lane would target the wrong branch.
ade lane drift [--lane <id>] --text
ade lane drift resolve --switch-back # restore the recorded branch
ade lane drift resolve --keep-head # adopt the live branch instead
--switch-back refuses on a dirty worktree rather than risking work. --keep-head
re-points the lane's branch and renames the lane only when its name was literally
advertising the old branch. Actions: lane.getBranchDrift, lane.resolveBranchDrift.
Scheduled work
Persistent ADE chats and tracked provider CLI sessions can schedule their own
durable wakeups. Use the typed ade chat scheduled-work create|list|cancel
commands or the generic chat.createScheduledWork, chat.listScheduledWork,
and chat.cancelScheduledWork actions. Pause or resume that session with
ade chat schedules <session> --pause|--resume or
chat.setScheduledWorkPaused. Omit the pause/resume flag, or call
chat.getScheduledWorkState, to inspect pause state, the next wake, and active
jobs for either a chat or tracked provider CLI session.
Omitting the target in an ADE-bound agent defaults create/list to
ADE_CHAT_SESSION_ID; an agent cannot schedule another session, and an
ordinary untracked shell fails instead of creating orphaned work. Chat delivery
starts a new turn at the next safe turn boundary. Tracked provider CLI delivery
waits for the provider's visible composer boundary, or resumes the same ended
CLI session before sending the prompt. Both survive brain restarts, and
recurring schedules expire seven days after creation. Users can pause chat jobs
in Chat Info or all scheduled work in Settings; CLI-owned jobs remain
manageable through these commands and the Settings recovery list.
Prefer a relative one-shot whenever the intent is "wake me in N minutes":
--in 12m in the typed CLI or delaySeconds: 720 in the action. This avoids
timezone conversion entirely. Absolute one-shots use --at / runAt and must
include Z or an explicit UTC offset. Five-field cron is interpreted in the
ADE brain machine's local timezone, not UTC unless that machine itself uses UTC.
The create result prints the computed next run in both local and ISO form;
verify that time before ending the turn.
ade chat scheduled-work create --in 12m --prompt "Check CI and report" --text
ade actions run chat.createScheduledWork --input-json '{"delaySeconds":720,"prompt":"Check CI and report"}' --text
ade chat scheduled-work create --at "2026-07-23T01:05:00-04:00" --prompt "Check CI and report" --text
ade chat scheduled-work create --cron "9,29,49 * * * *" --prompt "Check CI and report" --text
ade chat scheduled-work list --all --text
ade chat schedules "$ADE_CHAT_SESSION_ID" --pause --text
Compatibility commands still exist, but do not teach them as the first choice:
ade chat create --lane <lane> --provider codex --model <m> --type subagent --prompt "Fix" # persistent Work chat
ade shell start-cli codex --lane <lane> --model <m> --prompt "Fix" # tracked provider CLI terminal
ade agent spawn is the older CLI-session launcher and rejects
--reasoning-effort; avoid it for new flows. Common reasoning tiers include
minimal, low, medium, high, xhigh, max, and ultracode; confirm
model-specific support with ade actions run chat.modelCatalog --json.
Fallback path
If command -v ade fails:
- Try
${ADE_CLI_PATH:-}if set. - Try
${ADE_CLI_BIN_DIR:-}/adeif set. - In an ADE source checkout, after confirming it exists, use
node apps/ade-cli/dist/cli.cjs ....
The normal reason to skip ADE CLI is that it is truly unreachable after these fallbacks.
Signals
- GitHub stars
- 104
- Forks
- 12
- Last commit
- Sep 2026
Advanced
- Catalog kind
- skill
- Gateway key
ade-cli-control-plane- Source
- github.com/arul28/ade