apps/ade-cli owns the ade command, the per-machine ADE runtime daemon, and the terminal ade code client. The runtime daemon is the source of truth for lanes, chats, PR state, process state, sync, and proof artifacts on a machine. Desktop ADE, ade code, the iOS app, and SSH-attached desktops all attach to it.
The ade binary has three operating modes:
- Socket — the runtime daemon (
ade serve) listens on~/.ade/sock/ade.sock(POSIX) or\\.\pipe\ade-runtime(Windows). All other CLI commands and clients open that socket and speak ADE JSON-RPC. - Headless (
--headlessorade code --embedded) — the CLI builds an in-processAdeRuntimefor one project and answers the same JSON-RPC surface directly. Used for one-shot commands and as a fallback when no socket is available. ade rpc --stdio— attaches to the local runtime daemon and bridges its JSON-RPC over stdio. This is the transport the desktop's remote runtime feature spawns over SSH.
Default routing for typed commands: prefer the socket if reachable; auto-spawn ade serve in the background if the socket does not exist; fall back to headless for commands that don't need shared live state. Add --socket to require the daemon, or --headless to force in-process execution.
resolveMachineAdeLayout() (in src/services/projects/machineLayout.ts) is the single source for per-machine paths. Override the root with ADE_HOME.
| Path | Purpose |
|---|---|
~/.ade/ |
Per-machine ADE state root. |
~/.ade/sock/ade.sock |
Runtime daemon socket (POSIX). |
\\.\pipe\ade-runtime |
Runtime daemon named pipe (Windows). |
~/.ade/projects.json |
Project registry. |
~/.ade/secrets/ |
Machine credential store (credentials.safe.enc for desktop safeStorage, credentials.json.enc plus .machine-key for headless fallback storage, and per-store *.lock files). |
~/.ade/bin/ade |
Bundled static runtime binary (release installs / remote uploads). |
~/.ade/runtime/<platform-arch>/ |
Native node modules for that runtime binary. |
~/.ade/runtime/launchd.{out,err}.log |
Daemon stdout/stderr when running as a login service on macOS. |
Per-project state stays under <project>/.ade/ and is governed by projectConfigService (see docs/features/onboarding-and-settings/configuration-schema.md).
Channel builds use parallel state roots and binary names so Stable, Beta, and Alpha can coexist:
ADE.app -> ade -> ~/.ade -> ade-desktop
ADE Beta.app -> ade-beta -> ~/.ade-beta -> ade-desktop-beta
ADE Alpha.app -> ade-alpha -> ~/.ade-alpha -> ade-desktop-alpha
Source dev launches use the temp dev socket and ade-desktop-dev Electron profile instead of the installed app profile.
Three ways to put ade on a machine:
-
Standalone runtime install — single static binary plus its native dependency archive, fetched from a GitHub release. Suitable for headless macOS/Linux servers.
curl -fsSL https://github.com/arul28/ADE/releases/latest/download/install.sh | shEnvironment overrides accepted by
install.sh:ADE_VERSION=vX.Y.Z— install a specific release tag (defaultlatest).ADE_INSTALL_DIR=/usr/local/bin— destination directory for the binary.ADE_RELEASE_REPO=owner/repo— fetch from a fork.ADE_HOME=/custom/.ade— change the per-machine state root.
The script downloads
ade-<platform-arch>to$ADE_INSTALL_DIR/ade, extractsade-<platform-arch>.native.tar.gzto~/.ade/runtime/<platform-arch>/, runsade --versionto verify, and best-effort registers the per-user login service on macOS / systemd. -
Desktop bundle — every packaged ADE.app ships the CLI. macOS path:
/Applications/ADE.app/Contents/Resources/ade-cli/bin/ade
Add it to
PATHonce with the channel-specific helper:/Applications/ADE.app/Contents/Resources/ade-cli/install-path.sh
The
install-path.shwrapper exposesade(orade-beta/ade-alphafrom the matching.app). The wrapper runs the CLI under the packaged Electron runtime, so users do not need a separate Node install. The desktop General settings tab also exposes Install / Repair viaAdeCliSection(window.ade.adeCli.installForUser()). -
Source build — for repository development:
cd apps/ade-cli npm run build npm link # or: npm pack && npm install -g ./ade-cli-*.tgz
Requires Node.js 22 or newer (the headless runtime depends on
node:sqlite).
The runtime daemon runs as a per-user login service. The implementations live in src/serviceManager/.
| Platform | Backend | Service path |
|---|---|---|
| macOS | launchd LaunchAgent |
~/Library/LaunchAgents/com.ade.runtime.plist |
| Linux | systemctl --user |
~/.config/systemd/user/ade-runtime.service |
| Windows | schtasks.exe ONLOGON |
scheduled task ADE Runtime |
The default service label is com.ade.runtime; channel builds override it via ADE_PACKAGE_CHANNEL=alpha|beta (com.ade.runtime.alpha, com.ade.runtime.beta). ADE_RUNTIME_SERVICE_NAME overrides the label outright. macOS also writes ~/.ade/runtime/launchd.{out,err}.log.
Manage the service from the CLI:
ade serve --install-service # write the plist/unit/task and start it
ade serve --uninstall-service # stop and remove it
ade serve --service-status # JSON: { ok, installed, running, path, message }
# Aliases on the runtime command (same backend):
ade runtime install-service
ade runtime uninstall-service
ade runtime service-status --textresolveAdeServeCommand() builds the service command from the current ade binary path so the installed service launches the same ADE channel that ran the install.
ade serve runs the runtime in the foreground. Use it for development or when the system service is disabled.
ade serve
ade serve --socket ~/.ade/sock/ade.sock
ade serve --port 8787 # also accept JSON-RPC on 127.0.0.1:8787
ade serve --no-sync # disable phone-sync host for this runade runtime is the typed wrapper for daemon lifecycle commands:
ade runtime status --text # is the daemon up, which socket
ade runtime start # spawn it in the background if missing
ade runtime stop # graceful shutdown via JSON-RPC
ade runtime install-service # delegates to ade serve --install-serviceade runtime start is idempotent: it spawns ade serve detached and returns once the runtime answers ade/initialize. ade runtime stop calls the daemon's shutdown method.
The runtime daemon owns a per-machine project registry at ~/.ade/projects.json (ProjectRegistry in src/services/projects/projectRegistry.ts). A project record carries a stable projectId (project_<sha256(rootPath)[..24]>), root path, display name, addedAt, lastOpenedAt, and the resolved git origin URL.
Manage the registry through typed CLI commands:
ade projects list --text
ade projects add /path/to/project
ade projects remove project_abc123…
ade projects touch project_abc123…
ade init # adds the cwd as a project
ade init /path/to/project # adds an explicit path…or call the same JSON-RPC methods directly:
projects.list { } -> ProjectRecord[]
projects.add { rootPath } -> ProjectRecord
projects.remove { projectId } -> { removed }
projects.touch { projectId } -> ProjectRecord
Adding a project creates <rootPath>/.ade/ if needed but does not run any heavy onboarding. The first project-scoped JSON-RPC call lazily builds an AdeRuntime for that root via ProjectScopeRegistry.
The runtime exposes two layers of JSON-RPC methods (src/multiProjectRpcServer.ts):
Runtime-scoped — no projectId required:
ade/initialize ade/initialized ping shutdown exit
runtime/info machineInfo.get
projects.list projects.add projects.remove projects.touch
runtimeEvents.subscribe runtimeEvents.unsubscribe
sync.getStatus sync.refreshDiscovery
sync.listDevices sync.updateLocalDevice
sync.connectToBrain sync.disconnectFromBrain
sync.forgetDevice
sync.getTransferReadiness sync.transferBrainToLocal
sync.getPin sync.setPin sync.clearPin
sync.setActiveLanePresence
Project-scoped — every other request must carry params.projectId. ade/actions/call (and the legacy ADE action / tool catalog underneath it) is dispatched into the per-project ProjectScope returned by ProjectScopeRegistry.get(projectId).
ade/initialize advertises runtimeInfo.multiProject: true and capabilities.projects: true. Clients use that to switch between sending projectId per request (multi-project runtime) and the legacy per-process binding (embedded runtime). Sync is hosted by the daemon for the most-recently-opened registered project; ProjectScopeRegistry.ensureSyncHost re-elects a host when projects are added or removed.
src/services/credentials/credentialStore.ts owns the machine-scoped credential store under ~/.ade/secrets/:
- Desktop uses
ElectronSafeStorageCredentialStore, which encryptscredentials.safe.encwith ElectronsafeStorageand migrates legacy file-encrypted stores on first read. - Headless CLI fallback uses
EncryptedFileCredentialStore, which keepscredentials.json.encencrypted with AES-256-GCM and serializes read-modify-write access withcredentials.json.enc.lock. - Secret directories are created with mode
0700; credential blobs, lock files, and legacy machine keys are written with mode0600.
ade code launches the terminal-native ADE Work chat (Ink + React, in src/tuiClient/). Default behavior:
ade code # attach to the machine daemon, auto-spawn it if missing
ade code --embedded # force the in-process embedded runtime
ade code --print-state # smoke-test the connection and exit
ade --socket /path/to/ade.sock code # attach to a specific socket
ade --project-root /repo code # bind to a specific project rootBrowser mirror (dev): from the repo root, npm run dev:code:web runs one ade code in a single PTY and mirrors that TTY to the browser (xterm). Use Cursor’s browser tools against that page like any other local URL. This is not the same as running ade code in a terminal app and in the browser at once—that would be two separate processes.
See docs/features/ade-code/README.md for the full attach/embedded handshake, slash command catalog, and right-pane drawers.
ade rpc --stdio attaches to the local runtime daemon (auto-spawning it if needed) and bridges its JSON-RPC over stdio. The remote-runtime path on the desktop runs ade rpc --stdio over an SSH exec channel; see docs/features/remote-runtime/internal-architecture.md for the protocol shape and bootstrap sequence.
ade desktop opens the installed ADE app from the terminal. On macOS it runs open -a "ADE" (or ADE Beta / ADE Alpha based on ADE_PACKAGE_CHANNEL / ADE_DESKTOP_APP_NAME). The desktop attaches to the same machine runtime; if the daemon is not running, the desktop spawns and waits for it via LocalRuntimeConnectionPool.
ade desktop
ade runtime status --text
ade runtime start
ade runtime stop
ade auth status
ade doctor --json
ade projects list --text
ade init
ade lanes list --text
ade lanes create "fix-checkout-flow" --parent main
ade lanes create "lin-123" --linear-issue-json '{"id":"...","identifier":"LIN-123","title":"...","projectId":"...","projectSlug":"...","teamId":"...","teamKey":"...","stateId":"...","stateName":"Todo","stateType":"unstarted","priority":2,"priorityLabel":"high","labels":[],"assigneeId":null,"assigneeName":null,"createdAt":"...","updatedAt":"..."}'
ade lanes reparent lane-child --parent lane-parent --stack-base-branch main
ade lanes delete lane-id --force --delete-branch
ade lanes create-from-linear --issue-id ENG-431 --start-chat --provider codex --model <model>
ade lanes batch-create-from-linear --linear-issues-json '[{"id":"...","identifier":"ENG-431"},{"id":"...","identifier":"ENG-440"}]'
ade chat attach-linear-issue <session> --issue-id ENG-431
ade chat create --from-linear-issue ENG-431
ade linear attach --this-session --issue-id ENG-431 # attach to the current CLI session ($ADE_CHAT_SESSION_ID)
ade linear comment "Pushed a fix; CI running" # write back to the session's attached issue over the daemon bridge
ade linear set-state ENG-431 <state-id>
ade --role cto linear quick-view --text
ade --role cto linear search-issues --query "auth" --state-type started,unstarted --first 50
ade --role cto linear issue-comments --issue-id <linear-issue-uuid>
ade git commit --lane lane-id
ade git push --lane lane-id
ade git pull --lane lane-id --rebase
ade git undo --lane lane-id
ade git redo --lane lane-id
ade git tag abc123 --name v1.0.0 --lane lane-id
ade git reset abc123 --soft --lane lane-id
ade git is-reachable abc123 --lane lane-id
ade git branches --lane lane-id --text
ade git user-identity --lane lane-id --text
ade history list --lane lane-id --status succeeded --text
ade history show --id operation-id --text
ade history commits --lane lane-id --text
ade history export --lane lane-id --out history.json
ade diff patch --lane lane-id --path src/file.ts --text
ade prs create --lane lane-id --base main --title "Fix checkout flow" --text # prints GitHub + ADE PR URLs
ade prs create --lane lane-id --base main --close-linear-issue-on-merge
ade prs list-open --text
ade prs github-snapshot --include-external-closed
ade prs path-to-merge --pr pr-id --model gpt-5.5 --max-rounds 3 --no-auto-merge
ade prs path-to-merge --pr pr-id --model gpt-5.5 --conflict-strategy auto --force-finalize conditional
ade prs pipeline pr-id save --conflict-strategy rebase --no-early-merge-on-green
ade run defs --text
ade run start web --lane lane-id
ade shell start --lane lane-id -- npm test
ade shell start-cli codex --lane lane-id --permission-mode edit --message "fix failing tests"
ade shell start-cli --provider claude --lane lane-id --permission-mode default
ade chat create --lane lane-id --model gpt-5.5
ade code
ade code --embedded
ade tests run --lane lane-id --suite unit --wait
ade proof list --arg ownerKind=chat --arg ownerId=session-id
ade ios-sim devices --text
ade --socket ios-sim apps --text
ade --socket ios-sim launch --target target-id --text
ade --socket ios-sim preview-render --source apps/ios/ADE/Views/Home.swift --index 0 --text
ade --socket app-control launch --command "npm run dev" --text
ade --socket app-control focus --text
ade --socket app-control minimize --text
ade --socket browser open http://localhost:5173 --new-tab --text
ade --socket macos-vm status --lane lane-id --text
ade --socket macos-vm start --lane lane-id --create --no-display --text
ade --socket macos-vm storage --text
ade --socket macos-vm get-credentials --vm-name vm-name --text
ade --socket macos-vm display-session --lane lane-id --text
ade --socket macos-vm detach --lane lane-id --text
ade --socket update status --text
ade --socket update check --text
ade --socket update install --text
ade usage snapshot --text
ade usage refresh --text
ade usage budget get --text
ade usage budget set --from-file budget.json
ade usage budget check --provider claude --scope global
ade usage budget cumulative --scope global --text
ade actions list
ade actions run git.stageFile --arg laneId=lane-id --arg path=src/index.ts
ade actions run pty.resumeSession --arg sessionId=session-id
ade cursor cloud agents list --text
ade cursor cloud agents create --repo https://github.com/owner/repo --prompt "fix flaky test" --auto-pr
ade open ade://lane/<lane-uuid>
ade open --linear-issue ADE-123 --branch arul/ade-123-fix
ade link lane <lane-uuid>
ade link branch owner/repo my-branch --pr 42
ade link pr owner/repo 42 --ade
ade link linear-issue ADE-123 --branch arul/ade-123-fix
ade linear installUse typed commands first. They validate common arguments and provide stable JSON fields or readable text summaries. Use ade help <command> <subcommand> for exact flags, ade actions list --text to discover the full service-backed action catalog, and ade actions run <domain.action> only when there is no typed command for the workflow yet.
Output modes are explicit: --text for human-readable summaries, --json (default for piped output) for stable JSON, and --pretty for pretty-printed JSON.
--socket requires the daemon and fails fast when it is missing. Without --socket, the CLI auto-attaches when reachable and falls back to headless for commands that can run that way.
ADE CLI auth is local project access, not a separate cloud login. ade auth status verifies that the current terminal can initialize an ADE runtime for the project. Provider credentials, GitHub tokens, Linear tokens, and computer-use policy are read from ADE project settings and the existing secure stores.
ade doctor reports local-only readiness metadata by default:
- CLI version, Node/runtime version, project root, workspace root,
.adeinitialization, and config file presence. - Machine socket path, whether the socket exists, and whether this invocation is using
runtime-socket,desktop-socket, orheadlessmode. - RPC tool count, ADE action count, and action counts by domain.
- Git repository readiness and GitHub readiness signals from local remotes,
ghavailability, and token environment presence. - Linear readiness from the active project's
.ade/secretscredential store (linear.token.v1), a legacy project-scoped encrypted token file, or headless environment variables. - Provider/model readiness from local ADE config, API-key provider references, and provider CLI availability.
- Computer-use readiness from local platform capabilities.
- Packaged/PATH status for the
adebinary and concrete next actions.
Default doctor / auth checks do not call provider, GitHub, or Linear networks. They report presence and local readiness only, without printing secret values.
Agents starting an unfamiliar ADE session should begin with:
ade doctor --json
ade actions list --text…then prefer typed commands such as ade lanes list --text, ade files read <path> --text, ade prs checks <pr> --text, or ade tests runs --json. Use ade actions run … as the broad escape hatch.
The installed ade command is the production CLI. Repository development uses root npm scripts so the command always runs the CLI and desktop code from this checkout, not whichever ade happens to be first on PATH.
npm run setup
npm run dev:desktop
npm run dev:code
npm run dev:runtime
npm run dev:stopThe dev scripts are the same runtime daemon, just running from source against a temporary socket so a packaged ADE on the same machine is not affected:
/tmp/ade-runtime-dev.sock
From an ADE lane checkout under .ade/worktrees/, the dev scripts keep using
that lane's source code, but default the ADE project root back to the primary
checkout. npm run dev:code also passes the lane checkout as the workspace root
so initial lane selection matches ade code launched directly from the lane.
Full matrix:
npm run dev:desktop # desktop only; dev socket; desktop may auto-create runtime
npm run dev:desktop:attach # desktop only; fail unless dev runtime is already running
npm run dev:desktop:clean # desktop only; clear Vite cache before launch
npm run dev:code # terminal TUI only; starts dev runtime if missing
npm run dev:code:attach # terminal TUI only; fail unless dev runtime is already running
npm run dev:runtime # runtime only in the foreground
npm run dev:all # start shared dev runtime, then use attach commands in separate terminals
npm run dev:stop # stop the dev runtime
npm stop dev # same as dev:stopLocal packaged builds are separate from dev-mode scripts:
npm run package:alpha # current checkout -> ADE Alpha.app, ade-alpha, ~/.ade-alpha
npm run package:beta # origin/main -> ADE Beta.app, ade-beta, ~/.ade-betaUse these when you want a production-shaped local app without going through the GitHub release workflow. Alpha builds from the current checkout under apps/desktop/release-alpha; beta fetches origin/main, fast-forwards the local main checkout when possible, and writes artifacts under apps/desktop/release-beta. Use the dev scripts when you want Vite/Electron live reload, the temp dev socket, and the dev-only Electron profile. Local channel packages include the host runtime binary for the build machine. GitHub release builds use and validate the full cross-platform runtime artifact set.
The prs path-to-merge and prs pipeline save commands persist a partial PipelineSettings patch via issue_inventory.savePipelineSettings before launching the resolver. The Path to Merge orchestrator reads these from saved settings, so the same flags work either way:
| Flag | PipelineSettings field | Values |
|---|---|---|
--max-rounds <n> (alias --rounds) |
maxRounds |
positive integer |
--auto-merge / --no-auto-merge |
autoMerge |
boolean |
--merge-method <m> |
mergeMethod |
repo_default | merge | squash | rebase |
--conflict-strategy <s> |
conflictStrategy |
pause | rebase | merge | auto |
--force-finalize <m> |
forceFinalizeMode |
off | conditional | unconditional |
--force-finalize-require-no-ci / --force-finalize-allow-ci |
forceFinalizeRequireNoCiFailures |
boolean |
--early-merge-on-green / --no-early-merge-on-green |
earlyMergeOnGreen |
boolean |
To set fields without a dedicated flag (for example autoAgentSettings), call the action directly:
ade actions run issue_inventory.savePipelineSettings --args-list-json \
'["pr-1",{"autoAgentSettings":{"provider":"claude","model":"sonnet","reasoningEffort":"high","permissionMode":"guarded_edit","confidenceThreshold":0.7}}]'Automation rules are managed with ade automations <subcommand>. Run ade help automations for the full flag reference. The lane-mode flags layer on top of --from-file / --stdin / --text for create and update:
# Open a fresh lane for every new GitHub issue, naming it from the issue number + title.
ade automations example > rule.json
ade automations create --from-file rule.json \
--lane-mode create --lane-name-preset issue-num-title
# Reuse an existing lane instead.
ade automations create --from-file rule.json --lane-mode reuse --lane lane-42
# Custom template (only valid with --lane-name-preset custom).
ade automations create --from-file rule.json \
--lane-mode create --lane-name-preset custom \
--lane-name-template "{{trigger.issue.author}}/{{trigger.issue.title}}"
# Filter run history by status.
ade automations runs --rule rule-1 --status failed
ade automations run-show <runId> --textThe standalone create-lane action is deprecated. By default the CLI auto-migrates a rule whose first action is create-lane into execution.laneMode: "create" and carries the template forward. Pass --allow-legacy on create / update to opt out of the migration.