diff --git a/.gitignore b/.gitignore
index 12189ba90..44d509a1d 100644
--- a/.gitignore
+++ b/.gitignore
@@ -10,6 +10,7 @@ node_modules
*_diff.txt
*.png
!docs/**/*.png
+!assets/**/*.png
!apps/web/public/**/*.png
!apps/ios/**/*.png
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 0e4b6e35b..e90444f2b 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -1,115 +1,33 @@
# Contributing to ADE
-Pull requests are welcome from anyone. Only the maintainer (Arul) can merge or close PRs.
+Contributions are wanted and encouraged. If a pull request is high quality and
+useful, it gets merged. Only the maintainer (Arul) can merge or close PRs.
-## Development Setup
+Bug reports and feature requests count too, especially on Windows, which is
+still in beta and where the rough edges are easiest to find.
-Install dependencies for each app from the repo root:
+## Getting set up
```bash
-npm run setup
+npm run setup # first time only
+npm run dev # rebuild the CLI, refresh the dev runtime, launch desktop
```
-Start the normal desktop development flow from the repo root:
+Everything else lives in
+[docs/development/local-development.md](docs/development/local-development.md):
+the full dev command matrix, running a lane worktree in isolation, previewing
+the renderer without Electron, and packaging local Alpha and Beta builds.
-```bash
-npm run dev
-```
-
-This builds the ADE CLI, refreshes the shared dev runtime when needed, launches
-the Electron desktop app, and points desktop at that runtime. For renderer-only
-UI work, see [apps/desktop/README.md](apps/desktop/README.md).
-
-`npm run dev` also works from a lane checkout under `.ade/worktrees/`. To
-run a lane build **in isolation** — its own runtime + bridge sockets, without
-restarting your installed app's runtime — follow
-[Run a specific lane worktree](README.md#run-a-specific-lane-worktree) in the root
-README. Key rule: never aim `dev:desktop --socket` at a runtime you do not want
-`--auto` to shut down; use a fresh per-lane `/tmp/ade-runtime-.sock`.
-
-On Windows, `npm run dev` uses a per-user named pipe instead of a Unix socket
-path, and ADE runs on Windows 10/11 x64 only. Platform behaviour, the background
-brain contract, and the current gaps are documented in
-[docs/development/windows-support.md](docs/development/windows-support.md).
-
-## Before Submitting
-
-- Run the smallest relevant checks for your change first
-- Desktop: `npm --prefix apps/desktop run typecheck`, targeted Vitest files, and `npm --prefix apps/desktop run lint` when touching renderer or main-process code
-- ADE CLI: `npm --prefix apps/ade-cli run typecheck` and `npm --prefix apps/ade-cli run test` when touching CLI/runtime code
-- Docs: `node scripts/validate-docs.mjs` (fetch tags first if your clone is shallow)
-- TypeScript strict mode is enabled
-- Tests use Vitest
-
-## Signed releases
-
-### macOS
-
-For ADE's current release path, the correct Apple objects are:
-
-- `Developer ID Application` certificate for signing the `.app`
-- App Store Connect `Team Key` for notarization
-
-You do not need these for the current ADE flow:
-
-- `Developer ID Installer` certificate, because ADE ships `dmg` + `zip`, not `pkg`
-- A provisioning profile, unless the app later adds Apple advanced capabilities that require a Developer ID provisioning profile
-
-The tagged macOS release workflow expects these GitHub Actions secrets:
-
-- `CSC_LINK` — Developer ID Application certificate (`.p12`), typically base64-encoded
-- `CSC_KEY_PASSWORD` — password for the Developer ID Application certificate
-- `APPLE_API_KEY_P8` — raw contents of the App Store Connect Team API key (`AuthKey_*.p8`)
-- `APPLE_API_KEY_ID` — App Store Connect key ID
-- `APPLE_API_ISSUER` — App Store Connect issuer ID
-
-The release workflow builds ADE in three stages:
-
-1. `arm64` app bundle on `macos-latest`
-2. `x64` app bundle on `macos-15-intel`
-3. universal app merge, then signing, notarization, `dmg`/`zip` packaging, and GitHub release publish from the merged app
-
-Current Apple setup flow:
-
-1. On a Mac, create a CSR in Keychain Access using `Certificate Assistant > Request a Certificate from a Certificate Authority`, and save it to disk.
-2. In Apple Developer > Certificates, Identifiers & Profiles > Certificates, click `+`.
-3. Under `Software`, choose `Developer ID`, then choose `Developer ID Application`.
-4. Upload the CSR, download the `.cer`, and double-click it so it appears in Keychain Access under `login > My Certificates`.
-5. Export that certificate from Keychain Access as a `.p12` file with a password. This is the certificate material used by `CSC_LINK`.
-6. In App Store Connect > Users and Access > Integrations > Team Keys, generate a Team API key and download the `.p8` file. Note the key ID and issuer ID.
-
-To test a signed macOS build locally, export the matching environment variables expected by `electron-builder` and run:
-
-```bash
-cd apps/desktop
-export CSC_LINK=/absolute/path/to/DeveloperIDApplication.p12
-export CSC_KEY_PASSWORD=...
-export APPLE_API_KEY=/absolute/path/to/AuthKey_XXXXXXXXXX.p8
-export APPLE_API_KEY_ID=XXXXXXXXXX
-export APPLE_API_ISSUER=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
-npm run dist:mac:signed
-```
-
-To test the unsigned intermediate app bundle that the CI workflow produces per architecture, run:
-
-```bash
-cd apps/desktop
-npm run dist:mac:dir -- --arm64
-```
-
-### Windows
-
-Windows release publication is gated on the repository variable
-`ADE_WINDOWS_PUBLIC_RELEASE_ENABLED`. With it unset, the Windows jobs skip
-cleanly and the tagged release ships macOS assets only. The signed Windows path
-requires a pinned Authenticode identity, the same signer for the installer and
-`ADE.exe`, and a trusted RFC3161 timestamp. Pull-request preview builds are
-unsigned and are for internal testing only.
+## Before you submit
-The tagged release workflow should be run from a tag that points at `main`. Push the release tag only after the intended `main` commit is in place.
+Run the smallest checks that cover what you changed:
-## Code Style
+- Desktop: `npm --prefix apps/desktop run typecheck`, the relevant Vitest files,
+ and `npm --prefix apps/desktop run lint` for renderer or main process code
+- ADE CLI: `npm --prefix apps/ade-cli run typecheck` and
+ `npm --prefix apps/ade-cli run test`
+- Docs: `node scripts/validate-docs.mjs`
-- TypeScript with strict mode
-- Follow existing patterns in the codebase
-- Keep changes focused and minimal
+TypeScript runs in strict mode and tests are Vitest. The best style guide is the
+code already sitting around whatever you are changing, so keep changes focused
+and match what is there.
diff --git a/README.md b/README.md
index c8792b053..9fe535e07 100644
--- a/README.md
+++ b/README.md
@@ -37,455 +37,175 @@
-ADE runs **Claude Code, Codex, Cursor, Factory Droid, OpenCode** (more coming soon) in one workspace that you can open from any machine you own, the web, or a mobile app. The best part? It's completely free. Simply sign in to ADE on a machine you own, and you can magically run and manage agents from any other ADE client. In real time, every single ADE chat or CLI session (yes even your CLI sessions) sync to every other device. I can start a thread on my desktop at home, pick it up on my phone at the gym, and then finish it off from a website on my friends computer. It works like magic, and removes the friction of managing agents on the go.
+ADE runs **Claude Code, Codex, Cursor, Factory Droid, OpenCode** (more coming soon) in one workspace that you can open from any machine you own, the web, or a mobile app. The best part? It's completely free.
-Beyond agent management, ADE (like the name implies) offers a full development environment. This means git management, a full file editing/viewing experience like vscode, pr management, automations, gitcracken inspired history, and more. This one app is mean to replace every single modern dev tool.
+Simply sign in to ADE on a machine you own, and you can magically run and manage agents from any other ADE client. In real time, every single ADE chat or CLI session (yes even your CLI sessions) syncs to every other device.
+
+I can start a thread on my desktop at home, pick it up on my phone at the gym, and then finish it off from a website on my friend's computer. It works like magic, and removes the friction of managing agents on the go.
+
+Beyond agent management, ADE (like the name implies) offers a full development environment. This means git management, a full file editing/viewing experience like VS Code, PR management, automations, GitKraken inspired history, and more. This one app is meant to replace every single modern dev tool.
---
-
-
- |
-
-
-### Manage worktrees. In parallel.
-Every task gets its own git worktree. Describe it and ADE spins up the branch — edit, test, and commit side by side, with no stashing, no rebasing, no context switch.
+ |
+
- |
-
-
-
-|
+**Desktop app**
-### Every coding agent. One workspace.
-Claude Code, Codex, Cursor, Factory Droid, OpenCode — pick whichever model fits the task. All run against the same worktree, with live diffs and approval gates. Grid view tiles every run side by side.
+The full workspace. Every agent and every worktree, tiled side by side. macOS and Windows.
|
-
-
- |
-
+
+
- |
-
-
- |
-
+**Web client**
-### The whole IDE. In your terminal.
-`ade code` is ADE, terminal-native — the same worktrees, chats, and PRs in a fast TUI. Start in the shell, finish on the desktop or your phone.
+The same workspace in a browser at [app.ade-app.dev](https://app.ade-app.dev). Nothing to install — sign in and your machines are there.
|
-
-|
-
-### Open, review, and merge PRs.
-Every PR your agents open lands in ADE — diff, CI, comments, merge button. No GitHub tab. Auto-merge when green.
-
- |
-
-
- |
-
+
+
- |
-
-
- |
-
+**Terminal**
-### The conductor for your agents.
-An always-on CTO with context across every worktree. Pulls work from Linear, dispatches to the right worker, reports back when it's done.
+`ade code` is ADE, terminal-native — the same worktrees, chats, and PRs in a fast TUI. Works over SSH.
|
-
+
+
+
+
- |
-|
+**Mobile app**
-### Everything above. On your phone.
-Every worktree, every agent, every PR — synced to iOS. Start a task on the desktop, approve the diff from the train.
+Every worktree, agent, and PR synced to iOS. Start a task on the desktop, approve the diff from the train.
- |
-
-
-
-
|
-
-
-
-
-Plus files, terminals, git history, the workspace graph, multi-tasking, Linear sync, cron automations, computer-use proofs, and the `ade` CLI.
-
## Install
-One command installs the **brain** — ADE's always-on engine — and offers you everything else.
+Think of ADE as having 4 UI clients (web, desktop app, terminal, and mobile app) you can interact and work with. Each one of those clients needs to connect to a machine, and it does that via "ADE Brain". You can install the Brain to any machine, and magically it will then be accessible and controllable by any of those UI clients. The machine just needs to stay on, and suddenly you get a full agentic development experience from anywhere.
+
+This one command below installs the **ADE brain** — the always-on engine that every ADE UI client talks to. Running this command will also optionally ask you to install the desktop app, which is one of the four UI clients, and the most powerful one. This command will also ask you to sign up or login to your ADE account. Your ADE account is what lets you seamlessly connect to all other machines you use. Of course, you can use ADE without an account, you will just have to connect to your machines to an ADE Brain via LAN, Tailscale, or SSH.
-macOS and Linux:
+**macOS and Linux**
```bash
curl -fsSL https://ade-app.dev/install.sh | sh
```
-Windows (PowerShell):
+**Windows (PowerShell)**
```powershell
irm https://ade-app.dev/install.ps1 | iex
```
-The script downloads a self-contained runtime binary, verifies it against the published `SHA256SUMS`, and installs it under `~/.ade` with rollback on failure — no Node, npm, or other prerequisite. It then registers the brain as a per-user background service (launchd on macOS, a systemd user service on Linux, a startup entry on Windows — none of them need an elevated shell), offers to sign you in, and on macOS and Windows offers the desktop app too.
+**Want to install the full Desktop App + Brain via a dmg or exe file?** Download it from [**GitHub Releases**](https://github.com/arul28/ADE/releases/latest) or [ade-app.dev](https://ade-app.dev) instead. That gets you the same thing as the above command, but with the above command you can opt out of the desktop app and only install the Brain, these download files bundle it all together. Note - linux doesnt have a desktop app yet, only Brain.
-With no terminal attached — CI, a provisioning script — it skips the prompts and prints the follow-up commands instead. `ADE_INSTALL_NO_PROMPT=1` (`-NoPrompt` for PowerShell) opts out explicitly, and `ade connect` can always be run later.
+If you already have the Brain installed, and want the desktop app, you can safely run the command above, or install the appropriate download file.
-On macOS and Linux the script installs `ade` to `~/.ade/bin` and leaves your shell profile alone — add that directory to `PATH` yourself. The PowerShell installer updates your user `PATH` (skip with `-NoPath`). Other overrides: `ADE_VERSION` to pin a release tag, `ADE_INSTALL_DIR` for the binary destination, `ADE_HOME` for the state root.
+### Desktop app
-Then open ADE on any git repo and add a provider key (or subscription) in Settings. ADE runs in Guest Mode without an account.
+macOS and Windows. Install it with the one-liner above, or download it directly:
-### Connect a machine
+[**macOS · Apple Silicon**](https://ade-app.dev/download/mac-arm64) · [**macOS · Intel**](https://ade-app.dev/download/mac-x64) · [**Windows x64**](https://ade-app.dev/download/windows)
-`ade connect` is the one command that puts a machine on your ADE account. It signs you in, installs the login service, and waits for the machine to reach your account directory — after which you can reach it from ADE desktop, the [web client](https://app.ade-app.dev), and iOS. It is idempotent; re-running it on a connected machine just reports status.
+Requirements: macOS 13+, or Windows 10/11 **x64** (ARM64 unsupported)
-```bash
-ade connect # sign in, install the service, publish this machine
-ade connect --status --text # report the three steps, change nothing
-ade connect --headless # copy-paste device flow, for SSH sessions
-ade connect --no-login # service only; stay local/LAN-only
-```
+Windows is in **beta** and may be a little buggy, PR's and bug reports are encouraged to help grow this project. Due to lack of support and general Windows quirks, some features are not available that work on MacOS, nothing major though. Full detail: [docs/development/windows-support.md](docs/development/windows-support.md).
-`ade logout` undoes the account half; `ade runtime uninstall-service` removes the service.
+If you prefer Homebrew, this command installs the full desktop app + ADE Brain: `brew install --cask arul28/ade/ade`
+
+#### Linux
-### macOS
+There is no Linux desktop app yet, but it can run the Brain. You can install it on any x64 or arm64 box with the one-liner command from above, and that machine becomes reachable from any ADE UI Client.
-Download the desktop app for [**Apple Silicon**](https://ade-app.dev/download/mac-arm64) or [**Intel**](https://ade-app.dev/download/mac-x64), or use [Homebrew](https://brew.sh):
+Brain-only downloads, if you would rather not pipe a script: [`darwin-arm64`](https://ade-app.dev/download/brain/darwin-arm64) · [`darwin-x64`](https://ade-app.dev/download/brain/darwin-x64) · [`linux-arm64`](https://ade-app.dev/download/brain/linux-arm64) · [`linux-x64`](https://ade-app.dev/download/brain/linux-x64) · [`win32-x64`](https://ade-app.dev/download/brain/win32-x64).
+
+### Terminal
+
+Nothing extra to install — once you have the ADE Brain installed, either via the single command above or by installing the full desktop app, you can simply run:
```bash
-brew install --cask arul28/ade/ade
+ade code
```
-Both paths install the same signed + notarized app; ADE keeps itself current afterwards through its built-in auto-updater.
+On macOS and Linux this binary lives in `~/.ade/bin`, and the installer asks before adding it to your `PATH`. It writes `~/.ade/env` and drops one marked line into your shell profile, so say yes and new terminals just work. Say no and nothing gets touched, or set `ADE_INSTALL_NO_PATH=1` to skip the question. Windows updates your user `PATH` the same way, and `-NoPath` skips it. Other overrides: `ADE_VERSION` to pin a release tag, `ADE_INSTALL_DIR` for the binary destination, `ADE_HOME` for the state root.
-Requirements: macOS 13+, git on `PATH`.
+With no terminal attached — CI, a provisioning script — the installer skips every prompt and prints the follow-up commands instead. `ADE_INSTALL_NO_PROMPT=1` (`-NoPrompt` for PowerShell) opts out explicitly.
-### Linux
+### Web client
-Linux runs the **brain**, not a desktop app — and that is the point. Install it on any x64 or arm64 Linux box with the one-liner above, run `ade connect`, and drive that machine from ADE on your Mac, Windows PC, phone, or the web. Your build server becomes an ADE machine; agents, builds, and tests run on it, against its filesystem and its own credentials.
+Nothing to install. Sign in at [**app.ade-app.dev**](https://app.ade-app.dev) and every machine on your account is right there. Any machine running the ADE brain is reachable from it, from any browser.
-`ade code` gives you the full TUI over SSH on the box itself. There is no ADE desktop app for Linux.
+### Mobile app
-Brain-only downloads, if you'd rather not pipe a script: [`darwin-arm64`](https://ade-app.dev/download/brain/darwin-arm64) · [`darwin-x64`](https://ade-app.dev/download/brain/darwin-x64) · [`linux-arm64`](https://ade-app.dev/download/brain/linux-arm64) · [`linux-x64`](https://ade-app.dev/download/brain/linux-x64) · [`win32-x64`](https://ade-app.dev/download/brain/win32-x64).
+Available now on [**TestFlight**](https://testflight.apple.com/join/ZSdJGKPy). App Store coming soon; Android coming soon.
-### iOS
+Sign in on the phone to see your machines and connect automatically. If you choose not to create a free account, you can **Continue without an account** and pair to any machine via direct QR/link, Nearby LAN/Tailscale scan, or SSH pairing. To maintain and relay information between machines, ADE will prefer LAN, then Tailscale, and use its own relay service as fallback to maintain mobile to machine connections. Having an account makes the discovery of machines seamless, and will also allow you to connect off wifi or without Tailscale. But if you have access to internet, or have Tailscale setup on mobile and another machine, then ADE will automatically use that to maintain the connection, it's always a bit faster.
-Install ADE Mobile from [TestFlight](https://testflight.apple.com/join/ZSdJGKPy).
-Sign in on the phone and Mac to see your machines automatically and use ADE
-Relay away from home, or choose **Continue without an account** and pair from
-desktop **Connections > Mobile** / `ade brain pin generate`. Direct QR/link,
-Nearby, address/PIN, and SSH pairing work without an account; Tailscale keeps a
-direct pairing reachable when the devices are on different networks.
-### Windows
-Windows 10/11 **x64** is supported in beta. ARM64 is not supported.
+#### What `ade connect` does
-Download the [**signed Windows installer**](https://ade-app.dev/download/windows) or run the PowerShell one-liner above. It is a per-user NSIS installer — no administrator rights — and lays down the app, the `ade` CLI, `ade code`, and the background ADE brain under your user profile. The brain runs from a per-user startup entry, so it returns at the next sign-in without a service install or an elevated shell.
+It links a machine to your ADE account — nothing more. Once linked, that machine shows up in every ADe client once you are signed in.
-Beta means the gaps are written down rather than papered over, and the app says so on every launch. Everything below is a real limitation, not a rough edge.
+You rarely run it yourself. The install script runs it at the end, and the desktop app does the same job when you sign in. Reach for it when you declined the sign-in prompt, installed non-interactively, or want to check or repair an existing machine:
-What is not on Windows: native OS computer use (screenshot, video, GUI automation), the iOS Simulator drawer and Xcode Preview, and the Notch. App Control over CDP, the built-in browser, proof-file ingestion, phone pairing, and the local brain all work. Claude Code turns respawn the CLI instead of reattaching to a live background job, and the Claude Code sandbox is unavailable (permission modes still apply). A Windows machine can drive a remote macOS, Linux, or Windows runtime over SSH, and Windows 10 22H2 / Windows 11 x64 can itself be an SSH bootstrap target (OpenSSH Server and PowerShell 5.1+ required; WSL, ARM64, and Windows Server are out of scope).
+```bash
+ade connect # sign in, install the service, publish this machine
+ade connect --status --text # report the three steps, change nothing
+ade connect --headless # copy-paste device flow, for SSH sessions
+ade connect --no-login # service only; stay local/LAN-only
+```
-Requirements: Windows 10 or 11 x64, Git for Windows on `PATH`. Full detail: [docs/development/windows-support.md](docs/development/windows-support.md).
+`ade logout` undoes the account half; `ade runtime uninstall-service` removes the service.
## CLI
+The same `ade` binary that runs the Brain is your CLI too, so once the Brain is installed you already have it. A taste of what it does:
+
```bash
ade desktop
ade brain status --text
-ade brain start
-ade brain stop
-ade doctor --json
ade code
ade lanes create --name fix-checkout-flow
ade prs checks 168 --text
-ade tests run --suite unit --wait
ade actions list --text # discover every service action
```
-[CLI reference →](apps/ade-cli/README.md)
+[Full CLI reference →](apps/ade-cli/README.md)
## Architecture
-Local-first, on purpose. The center of ADE is the **brain** — the always-on, machine-owned ADE process for a channel. The brain owns the project catalog, sync websocket, and executor authority; desktop, `ade code`, the iOS app, and paired/SSH-attached desktop windows attach to it as clients. Runtime state lives under `.ade/` inside each project (SQLite db, worktree checkouts, proof artifacts, encrypted secrets) and machine-wide state lives under `~/.ade` or `~/.ade-`. When desktop is running, its Electron main process also hosts a **desktop bridge endpoint** at `~/.ade/sock/desktop-bridge.sock` (override: `ADE_DESKTOP_BRIDGE_SOCKET_PATH`) so the brain can proxy `ade browser …` calls into the Electron-only `WebContentsView` APIs it can't reach under `ELECTRON_RUN_AS_NODE=1`.
+ADE is local first. The Brain is the always on process that owns your project catalog, the sync websocket, and the authority to actually run things. Every UI client attaches to it, including the desktop app, `ade code`, mobile, and the web client. Project data stays in `.ade/` inside each repo, and machine wide state lives in `~/.ade`.
```text
-apps/ade-cli ADE brain + manual runtime entry points + `ade` CLI + `ade code` terminal client
-apps/desktop Electron client — multi-window, attaches to a local brain or SSH-bound runtime
-apps/ios SwiftUI controller that pairs with an ADE machine over WebSocket
-apps/web Public website and download surface
+apps/ade-cli ADE Brain, the `ade` CLI, and the `ade code` terminal client
+apps/desktop Electron desktop app
+apps/ios SwiftUI mobile app
+apps/web Website and download surface
docs/ Product and engineering docs
```
Deep reference: [ARCHITECTURE.md](docs/ARCHITECTURE.md).
-## Glossary
-
-| Term | Meaning |
-| --- | --- |
-| Brain | The always-on, machine-owned ADE process for one channel. It carries the sync websocket, project catalog, local RPC endpoint, and executor authority. |
-| Runtime | ADE execution machinery: processes/services that open DBs and run agents, PTYs, git, and orchestration. A runtime process can host the brain role, but "brain" is the authority/lifecycle term. |
-| Manual runtime | A foreground runtime process started explicitly with `ade runtime run --socket `. Sync is always off; use it for dev/test work instead of the automated stable/beta/alpha brain service. |
-| Machine | A physical computer with a per-channel ADE home and stable sync device identity. |
-| Channel | A release lane such as stable, beta, alpha, or dev. Each channel has its own ADE home. |
-| Client | A surface that attaches to the brain: desktop, `ade code`, ADE Mobile, or an SSH-bound desktop window. |
-| Project | A registered repo with one ADE database at `/.ade/ade.db`. |
-| Lane | A task worktree under `.ade/worktrees/` that shares the project database. |
-| Catalog | The machine-level project list served by the brain to clients and ADE Mobile. |
-
-### Brain vs. manual runtime
-
-This table describes the current code behavior.
-
-| Capability | Brain | Manual runtime |
-| --- | --- | --- |
-| Lifecycle | Always-on login service for an ADE channel; Desktop can install/repair it in packaged builds. | Foreground process started explicitly with `ade runtime run --socket `. |
-| Owner | Machine / ADE install. | User or developer who launched it. |
-| Sync | Yes. | No; `ade runtime run` forces sync off. |
-| Mobile websocket | Yes. | No. |
-| Phone pairing / PIN | Yes. | No. |
-| Mobile/machine catalog authority | Yes. | No; it may expose registry data to explicitly attached clients, but ADE Mobile ignores manual runtimes. |
-| Runs agents, PTYs, git, lanes, PR work | Yes. | Yes. |
-| Clients | Desktop, `ade code`, and ADE Mobile attach to it; SSH-bound desktop windows attach to the remote machine's ADE transport. | Only clients explicitly pointed at its endpoint attach to it. |
-| Survives client close | Yes, when service-owned. Desktop/TUI fallback spawns still exist for recovery and dev paths. | Only while that foreground process is still running. |
-
-### How to test changes from a lane
-
-| Change you made | What to run/test | Why |
-| --- | --- | --- |
-| iOS UI/client-only change | Build the iOS app from the lane and connect it to an existing ADE brain. | The phone is a client; UI-only work does not require a new brain. |
-| iOS sync protocol, project catalog, pairing, or remote-command change | Rebuild/restart the target brain from the lane, then build the iOS app from the same lane. | The phone and brain both need the new contract. |
-| Desktop renderer UI change | Run/build Desktop from the lane and let it attach to the channel brain. | Renderer code is client-side unless it depends on new brain APIs. |
-| Desktop main/preload/runtime-bridge change | Run/build Desktop from the lane; rebuild/restart the brain only if the runtime RPC contract or brain behavior changed. | Electron main is a client/bridge, but some handlers route through the brain. |
-| `ade code` / TUI UI change | Build/run `ade code` from the lane and attach to the existing brain. | The TUI is a client of the brain. |
-| TUI command that depends on new RPC or shared types | Rebuild/restart the brain from the lane, then run the lane's `ade code`. | Both sides of the RPC contract must match. |
-| Brain, sync, project catalog, pairing, agents, PTYs, lanes, PR workflows, or CLI runtime service change | Rebuild the ADE CLI/brain from the lane and restart the target brain before testing clients. | These live in the always-on process; existing installed brains keep running old code. |
-| Manual runtime behavior | Start `ade runtime run --socket ` from the lane and point a client at that endpoint. | Manual runtimes are standalone and sync is always off. |
-| Remote runtime / SSH transport change | Test with a remote target using the lane-built desktop/runtime artifacts. | SSH-bound windows talk to the remote ADE transport, not the local mobile brain. |
-| Docs or web-only change | Run the docs/web preview or static checks for that surface. | No ADE brain/client lifecycle is involved. |
-
## Develop
-First-time setup:
-
-```bash
-npm run setup
-```
-
-Daily desktop dev:
-
-```bash
-npm run dev
-```
-
-That aliases to `npm run dev:desktop`: it rebuilds `apps/ade-cli`, refreshes the shared dev runtime at `/tmp/ade-runtime-dev.sock` when needed, launches the Electron desktop app, and points desktop at that runtime. This is the normal desktop-dev flow.
-
-When these commands are run from an ADE lane worktree under `.ade/worktrees/`,
-they still run code from that lane checkout, but they open the primary checkout's
-project data by default. For example, running from
-`/path/to/ADE/.ade/worktrees/my-lane` opens `/path/to/ADE` as the ADE project
-and uses the lane path as the workspace root for `dev:code`.
-
-Dev command matrix:
-
-```bash
-npm run dev:desktop # refresh shared dev runtime, then launch desktop
-npm run dev:desktop:attach # desktop only; fail if dev runtime is not already running
-npm run dev:desktop:clean # desktop only; clear Vite cache before launch
-npm run dev:code:web # `ade code` in the browser (PTY + inspector WebSocket)
-npm run dev:code:attach # terminal TUI only; fail if dev runtime is not already running
-npm run dev:runtime # runtime only in the foreground
-npm run dev:all # start shared dev runtime, then run desktop/code attach commands in separate terminals
-npm run dev:stop # stop the dev runtime
-npm stop dev # same as dev:stop
-```
-
-Browser preview of the desktop renderer (UI work without Electron):
-
-```bash
-cd apps/desktop
-npm run dev:vite # mock-only: synthetic window.ade, fast shell
-ADE_PROJECT_ROOT=/path/to/project npm run dev:vite:live # mock + live runtime bridge (Linear, sync, lanes)
-```
-
-`dev:vite:live` starts the ADE dev runtime, a localhost HTTP bridge to the runtime endpoint, and Vite with a proxy so the browser can call real backend methods on top of the mock. Set `ADE_PROJECT_ROOT` to your primary project checkout (where `.ade/` and secrets live), especially when working from a lane worktree. Full details: [apps/desktop/README.md](apps/desktop/README.md).
-
-The dev commands intentionally use a temp endpoint and a separate Electron profile so they do not collide with the installed ADE app:
-
-```text
-/tmp/ade-runtime-dev.sock
-~/Library/Application Support/ade-desktop-dev
-```
-
-Override it when needed:
-
-```bash
-npm run dev:desktop -- --socket /tmp/my-ade-dev.sock
-npm run dev:code -- --socket /tmp/my-ade-dev.sock
-ADE_DEV_RUNTIME_SOCKET_PATH=/tmp/my-ade-dev.sock npm run dev:runtime
-ADE_DESKTOP_BRIDGE_SOCKET_PATH=/tmp/my-bridge.sock npm run dev:desktop
-```
-
-> [!WARNING]
-> Never point `--socket` at an ADE runtime you do not want restarted. In the default
-> `--auto` mode the wrapper **shuts down and recreates** whatever runtime is
-> already listening on that endpoint whenever its build hash does not match the
-> checkout you are launching — so aiming at the production `~/.ade/sock/ade.sock`
-> or another lane's live runtime will kill it (and any clients attached to it).
-> Point at a fresh per-lane endpoint (below), or use
-> `npm run dev:desktop:attach -- --socket ` to connect to an already-running
-> runtime — attach mode refuses on a build-hash mismatch instead of restarting.
-
-### Run a specific lane worktree
-
-To preview a lane's build without disturbing your installed ADE app or its
-runtime, run `dev:desktop` **from the lane checkout** on its own endpoints. Running
-from the worktree makes Vite serve that lane's code, while the wrapper
-auto-resolves project *data* to the primary checkout (as described above), so you
-see the lane's UI backed by your real lanes, PRs, and chats:
-
-```bash
-cd /path/to/ADE/.ade/worktrees/
-ADE_DESKTOP_BRIDGE_SOCKET_PATH=/tmp/ade-desktop-bridge-.sock \
- npm run dev:desktop -- --socket /tmp/ade-runtime-.sock
-```
-
-The per-lane `--socket` gives the lane build an isolated runtime (and sidesteps
-the warning above — nothing else is listening there); the per-lane bridge endpoint
-avoids colliding with the installed app's `~/.ade/sock/desktop-bridge.sock`. Set
-`ADE_PROJECT_ROOT=/path/to/other-project` only if you want a different project's
-data. A fresh worktree has no `node_modules` — symlink the root and `apps/desktop`
-`node_modules` from the primary checkout, or run `npm run setup` inside the
-worktree first.
-
-When launching that same flow through ADE App Control from a running Alpha/Beta
-ADE window, also clear the packaged-channel environment variables inherited from
-the host app (and use an absolute lane cwd). Otherwise the dev Electron app can
-reuse the Alpha/Beta profile and lose the single-instance lock instead of opening
-the lane build:
-
-```bash
-ade --socket app-control launch --force \
- --cwd "/path/to/ADE/.ade/worktrees/" \
- --command "sh -lc 'ADE_PACKAGE_CHANNEL= ADE_DESKTOP_APP_NAME= ADE_DESKTOP_BRIDGE_SOCKET_PATH=/tmp/ade-desktop-bridge-.sock npm run dev:desktop -- --socket /tmp/ade-runtime-.sock'" \
- --text
-```
-
-To test auto-runtime creation, use the default dev commands after stopping the dev runtime:
-
-```bash
-npm run dev:stop
-npm run dev:desktop # tests the desktop wrapper creating the dev runtime
-npm run dev:stop
-npm run dev:code # tests TUI wrapper creating the dev runtime
-```
-
-### Rebuild ADE Alpha or Beta locally
-
-Use these commands when you need a local packaged macOS channel build without
-waiting for the GitHub release workflow.
-
-```bash
-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-beta
-```
-
-`package:alpha` builds exactly the checkout you are in. `package:beta` is
-release-like: it fetches `origin/main`, fast-forwards the local `main` checkout
-when possible, and builds that checkout as `ADE Beta`. It does not create a
-packaging worktree.
-
-To smoke-test the Beta channel from a PR branch before it lands on `main`, pass
-the branch checkout explicitly:
-
-```bash
-node scripts/package-channel.mjs beta --repo "$PWD" --skip-install
-```
-
-Local channel outputs:
-
-```text
-apps/desktop/release-alpha/mac-arm64/ADE Alpha.app
-apps/desktop/release-alpha/ADE-Alpha-local.zip
-apps/desktop/release-beta/mac-arm64/ADE Beta.app
-apps/desktop/release-beta/ADE-Beta-local.zip
-```
-
-Install the build you want to test by replacing the matching app in
-`/Applications`:
-
-```bash
-rm -rf "/Applications/ADE Beta.app"
-ditto "apps/desktop/release-beta/mac-arm64/ADE Beta.app" "/Applications/ADE Beta.app"
-xattr -dr com.apple.quarantine "/Applications/ADE Beta.app" 2>/dev/null || true
-```
-
-Use `ADE Alpha.app` and `release-alpha` for Alpha. If the Dock already has an
-ADE Alpha/Beta icon, remove and re-pin it after installing from `/Applications`;
-Dock icons keep the exact bundle path they were pinned from, so an old icon can
-launch a stale `apps/desktop/release-*` build even after `/Applications` was
-updated.
-
-Replacing the app bundle does not replace a brain process that is already
-running for that channel. Before restarting the channel brain, close or finish
-any active ADE Desktop, ADE Code, agent, or mobile sessions that depend on it.
-Then restart and verify the channel brain through the CLI:
-
```bash
-ADE_PACKAGE_CHANNEL=beta ADE_HOME="$HOME/.ade-beta" ade brain status --text
-ADE_PACKAGE_CHANNEL=beta ADE_HOME="$HOME/.ade-beta" ade brain restart --text
-ADE_PACKAGE_CHANNEL=beta ADE_HOME="$HOME/.ade-beta" ade doctor --text
-ADE_PACKAGE_CHANNEL=beta ADE_HOME="$HOME/.ade-beta" ade sync status --text
+npm run setup # first time only
+npm run dev # rebuild the CLI, refresh the dev runtime, launch desktop
```
-For Alpha, use `ADE_PACKAGE_CHANNEL=alpha` and `ADE_HOME="$HOME/.ade-alpha"`.
-Do not kill ADE brain processes directly during normal testing; the channel
-brain owns the mobile sync websocket and may have desktop, terminal, or phone
-clients attached. If you intentionally leave an old incompatible brain running,
-the packaged desktop may preserve it and launch a private no-sync fallback
-runtime for the desktop window, which means the Mobile drawer will not be using
-that fallback's sync service.
-
-Launching a packaged channel build should install or repair that channel's
-always-on brain service. Official auto-updates also refresh this service on the
-first launch after an update, and the installed service is expected to report the
-same runtime build hash as the packaged desktop CLI:
-
-```bash
-launchctl print gui/$(id -u)/com.ade.runtime.beta
-ls -l ~/Library/LaunchAgents/com.ade.runtime.beta.plist ~/.ade-beta/sock/ade.sock
-```
-
-Set or rotate the channel's mobile pairing PIN from **Connections > Mobile**,
-or from the CLI against that channel home:
-
-```bash
-ADE_PACKAGE_CHANNEL=beta ADE_HOME="$HOME/.ade-beta" ade brain pin generate
-ADE_PACKAGE_CHANNEL=beta ADE_HOME="$HOME/.ade-beta" ade brain pin set 123456
-```
+`npm run dev` uses a temp endpoint and its own Electron profile, so it never touches your installed ADE app or the Brain it talks to.
-For Alpha, use `com.ade.runtime.alpha` and `~/.ade-alpha`. These builds do not
-replace the production `ADE.app`, production `ade`, or `~/.ade` runtime/state.
-Alpha and Beta also use separate Electron profile directories
-(`ade-desktop-alpha` / `ade-desktop-beta`) so browser storage and window state
-do not collide with dev or stable. Local channel packages include this Mac's
-runtime binary. Release builds still require the full cross-platform runtime
-artifact set used by remote runtime bootstrap.
+Everything else lives in [docs/development/local-development.md](docs/development/local-development.md): the full dev command matrix, running a specific lane worktree, previewing the renderer in a browser without Electron, packaging local Alpha and Beta builds, and a table of what to rebuild after a given change.
Validate with `npm --prefix apps/desktop run typecheck` and `npm run test:desktop:sharded` for the full desktop suite. The desktop test suite is large, so run the smallest relevant subset first.
diff --git a/apps/ade-cli/README.md b/apps/ade-cli/README.md
index 29064e304..553c2c1c3 100644
--- a/apps/ade-cli/README.md
+++ b/apps/ade-cli/README.md
@@ -65,12 +65,13 @@ Three ways to put `ade` on a machine:
- `ADE_RELEASE_REPO=owner/repo` — fetch from a fork.
- `ADE_HOME=/custom/.ade` — change the per-machine state root.
- `ADE_INSTALL_NO_PROMPT=1` — skip the interactive sign-in and desktop-app offers.
+ - `ADE_INSTALL_NO_PATH=1` — write `$ADE_HOME/env` but never touch a shell profile (the POSIX equivalent of `-NoPath`).
After a successful install both scripts run `ade tools ensure` so the pinned agent CLIs (Codex, Claude Code, OpenCode) are in the shared machine cache before the first agent run rather than as a surprise multi-hundred-megabyte download. That step is non-fatal — the brain retries it in the background on every `ade serve`. Then both scripts offer to run `ade connect`, which links the machine to your ADE account, and then offer the desktop app (macOS `.zip` via `ditto`, Windows NSIS installer via a silent per-user `/S` run). Both desktop downloads are verified against the base64 SHA-512 in the electron-updater manifest (`latest-mac.yml` / `latest.yml`) — the published `SHA256SUMS` covers only the standalone runtime assets. Prompts are read from `/dev/tty` on POSIX because `curl | sh` occupies stdin; when no terminal is attached (CI, automation) both scripts skip the interactive steps and print the follow-up commands instead. `install.ps1` also accepts `-NoPrompt`.
For an unpublished Windows proof bundle, run `install.ps1 -AssetDirectory ` (or set `ADE_RELEASE_ASSET_DIR`) to install the local checksum, executable, and native archive without creating a GitHub Release.
- The POSIX script downloads `ade-` to `$ADE_INSTALL_DIR/ade`; the PowerShell script downloads `ade-win32-x64.exe` to `$ADE_INSTALL_DIR\ade.exe`. Both verify the binary and matching `.native.tar.gz` against `SHA256SUMS`, extract native dependencies under `$ADE_HOME/runtime//`, run `ade --version`, and register the per-user login service. The PowerShell installer also adds the install directory to the current user's `PATH` unless `-NoPath` is passed; use `-NoService` to skip startup registration.
+ The POSIX script downloads `ade-` to `$ADE_INSTALL_DIR/ade`; the PowerShell script downloads `ade-win32-x64.exe` to `$ADE_INSTALL_DIR\ade.exe`. Both verify the binary and matching `.native.tar.gz` against `SHA256SUMS`, extract native dependencies under `$ADE_HOME/runtime//`, run `ade --version`, and register the per-user login service. Both put `ade` on `PATH`. The PowerShell installer adds the install directory to the current user's `PATH` (idempotently, then broadcasts `WM_SETTINGCHANGE`) and tells you to open a new terminal. The POSIX installer writes `$ADE_HOME/env` — a guarded `case ":${PATH}:" in ... esac` prepend that is safe to source repeatedly — and, with consent on a tty, appends one marker-commented block (`# >>> ade >>>` / `. "$HOME/.ade/env"` / `# <<< ade <<<`) to `~/.zshrc` (zsh; `~/.zprofile` only when no `~/.zshrc` exists), `~/.bash_profile` (bash on macOS) or `~/.bashrc` (bash on Linux). It greps for the marker first, so re-running the installer — which is also the update path — never duplicates the block. fish and unrecognized shells are never edited: the installer prints `fish_add_path ""` or the source line instead, as it does with no tty or with `ADE_INSTALL_NO_PATH=1`. After a profile edit the closing output tells you to run `. "$HOME/.ade/env"` or open a new terminal. Both accept `-NoPath` / `ADE_INSTALL_NO_PATH=1`; use `-NoService` to skip startup registration.
2. **Desktop bundle** — every packaged ADE.app ships the CLI. macOS path:
diff --git a/apps/ade-cli/scripts/install-runtime.ps1 b/apps/ade-cli/scripts/install-runtime.ps1
index 47dcfa94a..73bc05b28 100644
--- a/apps/ade-cli/scripts/install-runtime.ps1
+++ b/apps/ade-cli/scripts/install-runtime.ps1
@@ -6,7 +6,9 @@ param(
[string]$InstallDir = $env:ADE_INSTALL_DIR,
[string]$AdeHome = $env:ADE_HOME,
[switch]$NoService,
- [switch]$NoPath,
+ # `irm ... | iex` cannot take parameters, so the env var is the only way to
+ # opt out on the one-liner we actually promote.
+ [switch]$NoPath = ($env:ADE_INSTALL_NO_PATH -eq "1"),
[switch]$NoPrompt
)
@@ -376,6 +378,9 @@ try {
# ---------------------------------------------------------------------------
if ($installSucceeded) {
$adeCommand = if ($NoPath) { $destinationBinary } else { "ade" }
+ # Without a PATH entry the command is an absolute path, which is not
+ # runnable as-is once it contains a space. Call operator + quotes fixes it.
+ $adeInvocation = if ($NoPath) { "& `"$destinationBinary`"" } else { "ade" }
$interactive = Test-AdeInteractive
$onboardingTemp = Join-Path ([IO.Path]::GetTempPath()) ("ade-onboard-" + [Guid]::NewGuid().ToString("N"))
$onboardingPreviousEnvironment = @{
@@ -414,7 +419,7 @@ if ($installSucceeded) {
Write-Output "Next: run '$adeCommand connect' to link this machine to your ADE account."
} else {
Write-Output ""
- if (Read-AdeConfirmation "Sign in to link this machine to your ADE account?" $true) {
+ if (Read-AdeConfirmation "Sign in or create your ADE account to link this machine?" $true) {
try {
& $destinationBinary connect
if ($LASTEXITCODE -ne 0) {
@@ -477,7 +482,13 @@ if ($installSucceeded) {
}
Write-Output ""
- if (-not $NoPath) { Write-Output "Open a new terminal and run: $adeCommand connect --status --text" }
+ # With -NoPath nothing was added to the user PATH, so `ade` resolves only by
+ # full path and a new terminal buys the user nothing.
+ if (-not $NoPath) {
+ Write-Output "Open a new terminal and run: $adeInvocation connect --status --text"
+ } else {
+ Write-Output "Done. Try: $adeInvocation connect --status --text"
+ }
} finally {
foreach ($name in $onboardingPreviousEnvironment.Keys) {
[Environment]::SetEnvironmentVariable($name, $onboardingPreviousEnvironment[$name], "Process")
diff --git a/apps/ade-cli/scripts/install-runtime.sh b/apps/ade-cli/scripts/install-runtime.sh
index 4982f46af..a7fcf62c8 100644
--- a/apps/ade-cli/scripts/install-runtime.sh
+++ b/apps/ade-cli/scripts/install-runtime.sh
@@ -190,6 +190,204 @@ ask() {
esac
}
+# --- ade path setup (start) -------------------------------------------------
+# Everything between these markers is self-contained: it reads `dest_dir`,
+# `ade_home`, `interactive` and `ADE_INSTALL_NO_PATH`, and it is extracted
+# verbatim by the installer's PATH tests. Keep it free of install-specific
+# state so it stays testable in isolation.
+
+path_marker_begin='# >>> ade >>>'
+path_marker_end='# <<< ade <<<'
+# Set to 1 only when this run appended the block to a profile file, which is
+# what makes the *current* shell stale.
+path_profile_updated=0
+# Filled in by detect_profile.
+path_shell=""
+path_profile=""
+
+env_file_path() {
+ printf '%s\n' "$ade_home/env"
+}
+
+# The literal line written into the profile. Kept `$HOME`-relative so it stays
+# readable, and so it survives a home directory mounted at a different path.
+# `$HOME` can legitimately be unset here (ADE_HOME set, no home directory: some
+# CI images, docker RUN, systemd units), so it is read defensively -- an
+# unbound expansion would abort the whole installer under `set -u`.
+env_file_ref() {
+ env_ref_file="$(env_file_path)"
+ env_ref_home="${HOME:-}"
+ if [ -n "$env_ref_home" ]; then
+ case "$env_ref_file" in
+ "$env_ref_home"/*)
+ printf '. "$HOME/%s"\n' "${env_ref_file#"$env_ref_home"/}"
+ return 0
+ ;;
+ esac
+ fi
+ printf '. "%s"\n' "$env_ref_file"
+}
+
+# rustup/bun/uv shape: a tiny POSIX-sh file that prepends the install dir to
+# PATH, guarded so sourcing it twice (or in an already-configured shell) is a
+# no-op rather than a growing PATH.
+# Returns non-zero instead of letting `set -e` kill the run: by the time this
+# is called the runtime is already installed, so a stale root-owned $ADE_HOME/env
+# must cost the user a PATH hint, not the sign-in and agent-CLI steps below.
+write_env_file() {
+ env_file="$(env_file_path)"
+ mkdir -p "$(dirname "$env_file")" 2>/dev/null || return 1
+ cat >"$env_file" </dev/null || true
+ return 0
+}
+
+# Prefer $SHELL (the login shell, which is what the user's next terminal will
+# start), and only fall back to sniffing dotfiles when it is absent or exotic.
+detect_profile() {
+ path_shell=""
+ path_profile=""
+ path_home="${HOME:-}"
+ if [ -n "${SHELL:-}" ]; then
+ path_shell="$(basename "$SHELL")"
+ fi
+ # No home directory means no profile to sniff for or write to. Leave
+ # path_profile empty and let the caller fall back to printing the hint.
+ if [ -z "$path_home" ]; then
+ return 0
+ fi
+ case "$path_shell" in
+ zsh | bash | fish) ;;
+ *)
+ if [ -f "$HOME/.zshrc" ] || [ -f "$HOME/.zprofile" ]; then
+ path_shell="zsh"
+ elif [ -f "$HOME/.bashrc" ] || [ -f "$HOME/.bash_profile" ]; then
+ path_shell="bash"
+ fi
+ ;;
+ esac
+
+ case "$path_shell" in
+ zsh)
+ # Every interactive zsh reads .zshrc, while .zprofile is login-shell only.
+ # Writing to .zprofile would leave PATH missing in the non-login shells
+ # editors and multiplexers spawn, so always target .zshrc and create it
+ # when it is absent.
+ path_profile="$HOME/.zshrc"
+ ;;
+ bash)
+ # macOS Terminal starts login shells, which read .bash_profile and never
+ # .bashrc; Linux terminals are the other way round.
+ if [ "$(uname -s)" = "Darwin" ]; then
+ path_profile="$HOME/.bash_profile"
+ else
+ path_profile="$HOME/.bashrc"
+ fi
+ ;;
+ *)
+ # fish and anything unrecognized: we do not know the syntax or the file,
+ # so we print instructions instead of guessing at someone's config.
+ path_profile=""
+ ;;
+ esac
+}
+
+profile_has_block() {
+ [ -f "$1" ] || return 1
+ grep -Fq "$path_marker_begin" "$1"
+}
+
+append_profile_block() {
+ append_target="$1"
+ mkdir -p "$(dirname "$append_target")" 2>/dev/null || return 1
+ # One printf, so the whole block lands in a single append -- three separate
+ # writes let a concurrently running installer interleave into the middle of
+ # ours and produce a nested, unreadable pair of blocks. The leading newline
+ # also terminates a profile whose last line has no newline of its own.
+ printf '\n%s\n%s\n%s\n' \
+ "$path_marker_begin" "$(env_file_ref)" "$path_marker_end" \
+ >>"$append_target" || return 1
+ return 0
+}
+
+print_path_hint() {
+ if [ "$path_shell" = "fish" ]; then
+ printf 'To use `ade` in your own terminal, run:\n fish_add_path "%s"\n' "$dest_dir"
+ else
+ printf 'To use `ade` in your own terminal, add this line to your shell profile:\n %s\n' "$(env_file_ref)"
+ fi
+}
+
+# Writes the env file always; edits a profile file only with consent, on a tty,
+# and only once (re-running the installer is the update path).
+setup_path() {
+ # Without the env file there is nothing for a profile line to source, so a
+ # failed write ends PATH setup here rather than pointing a dotfile at a file
+ # that does not exist.
+ if ! write_env_file; then
+ printf 'ade install: could not write %s; skipping PATH setup.\n' "$(env_file_path)" >&2
+ printf 'To use `ade` in your own terminal, add %s to your PATH.\n' "$dest_dir"
+ return 0
+ fi
+ detect_profile
+
+ case ":$PATH:" in
+ *":$dest_dir:"*) path_on_path=1 ;;
+ *) path_on_path=0 ;;
+ esac
+
+ # Already managed by a previous install: the block sources the env file we
+ # just rewrote, so there is nothing to do and nothing to ask.
+ if [ -n "$path_profile" ] && profile_has_block "$path_profile"; then
+ return 0
+ fi
+
+ if [ "${ADE_INSTALL_NO_PATH:-}" = "1" ]; then
+ [ "$path_on_path" -eq 1 ] || print_path_hint
+ return 0
+ fi
+
+ # The directory is already on PATH by some other arrangement the user owns.
+ # Adding our own block would be redundant noise in their profile.
+ if [ "$path_on_path" -eq 1 ]; then
+ return 0
+ fi
+
+ if [ -z "$path_profile" ] || [ "$path_shell" = "fish" ]; then
+ print_path_hint
+ return 0
+ fi
+
+ # No terminal (CI, automation): never touch dotfiles unasked.
+ if [ "$interactive" -ne 1 ]; then
+ print_path_hint
+ return 0
+ fi
+
+ printf '\n'
+ if ! ask "Add ade to your PATH by updating $path_profile?" y; then
+ print_path_hint
+ return 0
+ fi
+
+ if append_profile_block "$path_profile"; then
+ path_profile_updated=1
+ printf 'Updated %s\n' "$path_profile"
+ else
+ printf 'ade install: could not update %s.\n' "$path_profile" >&2
+ print_path_hint
+ fi
+ return 0
+}
+# --- ade path setup (end) ---------------------------------------------------
+
choose_install_dir() {
if [ -n "$install_dir" ]; then
printf '%s\n' "$install_dir"
@@ -264,14 +462,12 @@ elif [ "$(uname -s)" = "Darwin" ]; then
fi
printf 'ADE runtime installed: %s\n' "$dest_dir/ade"
+# Commands we print must be runnable *now*, in this shell, so they name the
+# binary by path until the install dir is already on PATH here. A profile edit
+# made below only affects new shells.
case ":$PATH:" in
- *":$dest_dir:"*)
- ade_cmd="ade"
- ;;
- *)
- ade_cmd="$dest_dir/ade"
- printf 'Add %s to PATH to run ade from new shells.\n' "$dest_dir"
- ;;
+ *":$dest_dir:"*) ade_cmd="ade" ;;
+ *) ade_cmd="$dest_dir/ade" ;;
esac
# Under `curl | sh` the script's stdin is the download pipe, so every prompt --
@@ -304,7 +500,7 @@ offer_sign_in() {
fi
printf '\n'
- if ! ask 'Sign in to link this machine to your ADE account?' y; then
+ if ! ask 'Sign in or create your ADE account to link this machine?' y; then
printf 'Skipped. Run `%s connect` later to link this machine.\n' "$ade_cmd"
return 0
fi
@@ -414,8 +610,15 @@ offer_desktop_app() {
return 0
}
+# PATH first: the sign-in and agent-CLI steps below should run with a sane
+# environment, and the user should be asked about their shell profile before
+# they are asked about accounts and a 1 GB desktop download.
+setup_path
ensure_agent_tools
offer_sign_in
offer_desktop_app
printf '\nDone. Try: %s connect --status --text\n' "$ade_cmd"
+if [ "$path_profile_updated" -eq 1 ]; then
+ printf 'New terminals will find `ade` on PATH. To use it in this one, run: %s\n' "$(env_file_ref)"
+fi
diff --git a/apps/ade-cli/src/cli.ts b/apps/ade-cli/src/cli.ts
index af9065ccb..1e2fc7e90 100644
--- a/apps/ade-cli/src/cli.ts
+++ b/apps/ade-cli/src/cli.ts
@@ -20325,7 +20325,7 @@ async function runAccountLogin(
}
}
process.stderr.write(
- `\nSign in to ADE in your browser. If it did not open, visit:\n ${authorizeUrl}\n\nWaiting for sign-in…\n`,
+ `\nSign in or create your ADE account in your browser. If it did not open, visit:\n ${authorizeUrl}\n\nWaiting for sign-in…\n`,
);
const deadlineMs = deadlineFor(expiresAt);
while (true) {
diff --git a/apps/desktop/README.md b/apps/desktop/README.md
index e924f1de3..0d7854273 100644
--- a/apps/desktop/README.md
+++ b/apps/desktop/README.md
@@ -36,7 +36,7 @@ The mock stays the fallback for everything the bridge does not override. UI work
> This section covers the **browser preview** of the renderer. For the full
> Electron app, use `npm run dev` from the repo root — or, to run a specific lane
> build in isolation (its own runtime + bridge endpoints), see
-> [Run a specific lane worktree](../../README.md#run-a-specific-lane-worktree) in
+> [Run a specific lane worktree](../../docs/development/local-development.md#run-a-specific-lane-worktree) in
> the root README. Do not aim `dev:desktop --socket` at a runtime you do not want
> `--auto` to shut down (e.g. the production `~/.ade/sock/ade.sock`).
diff --git a/apps/desktop/src/renderer/components/app/SettingsPage.tsx b/apps/desktop/src/renderer/components/app/SettingsPage.tsx
index 9df313892..d3a5e4e6e 100644
--- a/apps/desktop/src/renderer/components/app/SettingsPage.tsx
+++ b/apps/desktop/src/renderer/components/app/SettingsPage.tsx
@@ -138,6 +138,11 @@ function TabContent({ tab }: { tab: SettingsTabId }) {
+
+
+
@@ -204,11 +209,6 @@ function TabContent({ tab }: { tab: SettingsTabId }) {
-
-
-
>
);
case "notifications":
diff --git a/apps/desktop/src/renderer/components/settings/AdeCliSection.tsx b/apps/desktop/src/renderer/components/settings/AdeCliSection.tsx
index 92a791032..b3cd69342 100644
--- a/apps/desktop/src/renderer/components/settings/AdeCliSection.tsx
+++ b/apps/desktop/src/renderer/components/settings/AdeCliSection.tsx
@@ -2,6 +2,16 @@ import { useCallback, useEffect, useState, type CSSProperties } from "react";
import { ArrowsClockwise, CheckCircle, TerminalWindow, Warning } from "@phosphor-icons/react";
import type { AdeCliStatus } from "../../../shared/types";
import { COLORS, MONO_FONT, SANS_FONT, cardStyle, inlineBadge, outlineButton, primaryButton } from "../lanes/laneDesignTokens";
+import { rendererPlatformAttribute } from "../../lib/platform";
+
+/**
+ * The terminal installer this card's button mirrors: it drops the same `ade`
+ * binary and, since it also manages shell PATH, is the answer for machines
+ * that never get the desktop app.
+ */
+const TERMINAL_INSTALL_COMMAND = rendererPlatformAttribute() === "win32"
+ ? "irm https://ade-app.dev/install.ps1 | iex"
+ : "curl -fsSL https://ade-app.dev/install.sh | sh";
type Props = {
compact?: boolean;
@@ -127,6 +137,14 @@ export function AdeCliSection({ compact = false, embedded = false }: Props) {
{!status?.installTargetDirOnPath && status?.installTargetPath ? (
{installTargetDir} is not on this shell PATH. Agents still get the bundled command; add that directory to your shell PATH for Terminal use.
+ {embedded ? null : (
+ <>
+ {" "}Installing here puts the same ade in your Terminal
+ that ADE uses everywhere else. On a machine without the desktop app,{" "}
+ {TERMINAL_INSTALL_COMMAND} installs it and sets up
+ PATH for you.
+ >
+ )}
) : null}
diff --git a/apps/desktop/src/renderer/components/settings/settingsManifest.ts b/apps/desktop/src/renderer/components/settings/settingsManifest.ts
index d57c7f638..a04fd62ef 100644
--- a/apps/desktop/src/renderer/components/settings/settingsManifest.ts
+++ b/apps/desktop/src/renderer/components/settings/settingsManifest.ts
@@ -62,16 +62,16 @@ export type SettingsTab = {
};
export const SETTINGS_TABS: readonly SettingsTab[] = [
- { id: "general", label: "General", description: "Project identity, launch behavior, updates, and privacy." },
+ { id: "general", label: "General", description: "Project identity, the ade command, launch behavior, updates, and privacy." },
{ id: "appearance", label: "Appearance", description: "How ADE looks and how the chat transcript reads." },
{ id: "agents", label: "Agents & Models", description: "Provider connections, model routing, and background helpers." },
{ id: "lanes-git", label: "Lanes & Git", description: "How lanes start, stay current, and tell you they fell behind." },
- { id: "integrations", label: "Integrations", description: "GitHub, Linear, and the ADE command line." },
+ { id: "integrations", label: "Integrations", description: "GitHub and Linear." },
{ id: "notifications", label: "Notifications & Sound", description: "What ADE interrupts you for, and how." },
{ id: "activity", label: "Activity", description: "What's running everywhere, and how ADE shows it." },
// Named "Secrets & Environment" while planning, on the assumption that
// `EnvironmentSection` held environment-variable mappings. It doesn't — it
- // was App version + ADE CLI, which now live in General and Integrations —
+ // was App version + ADE CLI, which now both live in General —
// and ADE has no env-mapping UI. Secrets *are* the env-style values here
// (they import straight from `.env`), so the tab is named for what it holds.
{ id: "secrets", label: "Secrets", description: "Encrypted key/value pairs for ADE agents, desktop, and the CLI." },
@@ -119,6 +119,16 @@ export const SETTINGS_ENTRIES: readonly SettingEntry[] = [
showScopeChip: true,
group: "Project",
},
+ {
+ id: "general.ade-cli",
+ label: "ADE command line",
+ keywords: ["cli", "terminal", "ade code", "install", "path", "shell"],
+ tab: "general",
+ anchor: "ade-cli",
+ scope: "machine",
+ web: "hidden",
+ group: "Command line",
+ },
{
id: "general.launch-prompt",
label: "Paste clipboard into new chats",
@@ -404,17 +414,6 @@ export const SETTINGS_ENTRIES: readonly SettingEntry[] = [
showScopeChip: true,
group: "Linear",
},
- {
- id: "integrations.ade-cli",
- label: "ADE command line",
- keywords: ["cli", "terminal", "ade code", "install", "path", "shell"],
- tab: "integrations",
- anchor: "ade-cli",
- scope: "machine",
- web: "hidden",
- group: "Command line",
- },
-
// ── Notifications & Sound ────────────────────────────────────────────────
{
id: "notifications.events",
diff --git a/apps/web/src/components/editorial/Lede.tsx b/apps/web/src/components/editorial/Lede.tsx
index 0e6812f01..9308a4c5d 100644
--- a/apps/web/src/components/editorial/Lede.tsx
+++ b/apps/web/src/components/editorial/Lede.tsx
@@ -136,7 +136,7 @@ export function Lede() {
onClick={() => openInstall("linux")}
className="inline-flex items-center gap-1 text-[color:var(--color-cream-muted)] underline decoration-[color:var(--color-hairline-strong)] underline-offset-4 transition-colors hover:text-[color:var(--color-cream)]"
>
- Linux · install the brain
+ Linux
·
{target.title}
-
- {target.subtitle}
-
+ {target.subtitle ? (
+
+ {target.subtitle}
+
+ ) : null}
-
- {target.terminal.blurb}
-
+ {target.terminal.blurb ? (
+
+ {target.terminal.blurb}
+
+ ) : null}
{target.downloads ? (
@@ -254,17 +258,21 @@ export function InstallDialog({
))}
-
- {target.downloads.blurb}
-
+ {target.downloads.blurb ? (
+
+ {target.downloads.blurb}
+
+ ) : null}
) : null}
-
- {target.footnote.text}
-
+ {target.footnote.text ? (
+
+ {target.footnote.text}
+
+ ) : null}
{target.footnote.command ? (
<>
diff --git a/apps/web/src/lib/installTargets.ts b/apps/web/src/lib/installTargets.ts
index a9d362c4a..d3ecca211 100644
--- a/apps/web/src/lib/installTargets.ts
+++ b/apps/web/src/lib/installTargets.ts
@@ -25,22 +25,22 @@ export type InstallTarget = {
platform: InstallPlatform;
/** Dialog heading. */
title: string;
- /** One line under the heading. */
- subtitle: string;
+ /** One line under the heading, when there is one. */
+ subtitle?: string;
terminal: {
heading: string;
command: string;
- blurb: string;
+ blurb?: string;
analyticsFeature: MarketingFeature;
};
/** Absent for Linux — there is no ADE desktop app for Linux. */
downloads?: {
heading: string;
- blurb: string;
+ blurb?: string;
options: InstallDownload[];
};
footnote: {
- text: string;
+ text?: string;
/** Copyable command rendered inside the footnote, when there is one. */
command?: string;
commandAnalyticsFeature?: MarketingFeature;
@@ -48,9 +48,6 @@ export type InstallTarget = {
openAnalyticsFeature: MarketingFeature;
};
-const TERMINAL_BLURB =
- "Installs the ADE brain — the headless engine — signs you in, and can optionally install the desktop app. All from the terminal.";
-
export const MAC_INSTALL_COMMAND = "curl -fsSL https://ade-app.dev/install.sh | sh";
export const WINDOWS_INSTALL_COMMAND = "irm https://ade-app.dev/install.ps1 | iex";
export const LINUX_INSTALL_COMMAND = MAC_INSTALL_COMMAND;
@@ -60,16 +57,13 @@ export const INSTALL_TARGETS: Readonly> =
mac: {
platform: "mac",
title: "Get ADE for Mac",
- subtitle: "Two ways in. Both end up in the same place.",
terminal: {
heading: "Install from your terminal",
command: MAC_INSTALL_COMMAND,
- blurb: TERMINAL_BLURB,
analyticsFeature: MARKETING_FEATURES.COPY_INSTALL_COMMAND_MAC,
},
downloads: {
- heading: "Download the app",
- blurb: "Signed and notarized. Updates itself from then on.",
+ heading: "or Download from file",
options: [
{
href: LINKS.downloadMacArm64,
@@ -95,16 +89,13 @@ export const INSTALL_TARGETS: Readonly> =
windows: {
platform: "windows",
title: "Get ADE for Windows",
- subtitle: "Two ways in. Both end up in the same place.",
terminal: {
heading: "Install from your terminal",
command: WINDOWS_INSTALL_COMMAND,
- blurb: TERMINAL_BLURB,
analyticsFeature: MARKETING_FEATURES.COPY_INSTALL_COMMAND_WINDOWS,
},
downloads: {
- heading: "Download the app",
- blurb: "Signed installer. Updates itself from then on.",
+ heading: "or Download from file",
options: [
{
href: LINKS.downloadWindows,
@@ -122,7 +113,6 @@ export const INSTALL_TARGETS: Readonly> =
linux: {
platform: "linux",
title: "Run the ADE brain on Linux",
- subtitle: "One command. No desktop app needed.",
terminal: {
heading: "Install from your terminal",
command: LINUX_INSTALL_COMMAND,
@@ -130,9 +120,7 @@ export const INSTALL_TARGETS: Readonly> =
"Run the brain on any Linux box and control it from ADE on your Mac, Windows, phone, or the web.",
analyticsFeature: MARKETING_FEATURES.COPY_INSTALL_COMMAND_LINUX,
},
- footnote: {
- text: "x64 and arm64. The brain is the whole engine — the desktop app is just one of its faces.",
- },
+ footnote: {},
openAnalyticsFeature: MARKETING_FEATURES.INSTALL_DIALOG_LINUX,
},
});
diff --git a/assets/readme/web-client.png b/assets/readme/web-client.png
new file mode 100644
index 000000000..3a181316d
Binary files /dev/null and b/assets/readme/web-client.png differ
diff --git a/docs/README.md b/docs/README.md
index b26e0ea70..e76b22033 100644
--- a/docs/README.md
+++ b/docs/README.md
@@ -12,6 +12,10 @@ The mental model up front: ADE has a **brain** — the always-on, machine-owned
4. [**features/**](./features/) — per-feature subfolders, each with a `README.md` + detail docs. Start with `remote-runtime/`, `ade-code/`, and `sync-and-multi-device/` for the brain+clients picture.
5. [**playbooks/**](./playbooks/) — operational workflows agents can follow directly.
+Running ADE from a checkout starts with
+[`development/local-development.md`](./development/local-development.md) for the
+glossary, the dev command matrix, lane worktrees, and local Alpha/Beta packaging.
+
Windows release engineering starts with
[`development/windows-release-proof.md`](./development/windows-release-proof.md)
for the exact-SHA evidence contract and
@@ -31,6 +35,7 @@ docs/
│ ├── ship-lane.md # autonomous PR-to-merge driver
│ └── windows-signed-release.md # signed Windows publication
├── development/
+│ ├── local-development.md # run, test, and package ADE locally
│ ├── windows-release-proof.md # exact-SHA evidence schema + validator
│ ├── windows-full-system-scenarios.json # external Windows acceptance inventory
│ ├── windows-source-provenance.json # #999 source/rebase/stack mapping
diff --git a/docs/development/local-development.md b/docs/development/local-development.md
new file mode 100644
index 000000000..5610c8101
--- /dev/null
+++ b/docs/development/local-development.md
@@ -0,0 +1,259 @@
+# Local development
+
+Everything you need to run, test, and package ADE from a checkout. The README
+keeps the short version; this is the full reference.
+
+## Glossary
+
+| Term | Meaning |
+| --- | --- |
+| Brain | The always-on, machine-owned ADE process for one channel. It carries the sync websocket, project catalog, local RPC endpoint, and executor authority. |
+| Runtime | ADE execution machinery: processes/services that open DBs and run agents, PTYs, git, and orchestration. A runtime process can host the brain role, but "brain" is the authority/lifecycle term. |
+| Manual runtime | A foreground runtime process started explicitly with `ade runtime run --socket `. Sync is always off; use it for dev/test work instead of the automated stable/beta/alpha brain service. |
+| Machine | A physical computer with a per-channel ADE home and stable sync device identity. |
+| Channel | A release lane such as stable, beta, alpha, or dev. Each channel has its own ADE home. |
+| Client | A surface that attaches to the brain: desktop, `ade code`, ADE Mobile, or an SSH-bound desktop window. |
+| Project | A registered repo with one ADE database at `/.ade/ade.db`. |
+| Lane | A task worktree under `.ade/worktrees/` that shares the project database. |
+| Catalog | The machine-level project list served by the brain to clients and ADE Mobile. |
+
+## Brain vs. manual runtime
+
+This table describes the current code behavior.
+
+| Capability | Brain | Manual runtime |
+| --- | --- | --- |
+| Lifecycle | Always-on login service for an ADE channel; Desktop can install/repair it in packaged builds. | Foreground process started explicitly with `ade runtime run --socket `. |
+| Owner | Machine / ADE install. | User or developer who launched it. |
+| Sync | Yes. | No; `ade runtime run` forces sync off. |
+| Mobile websocket | Yes. | No. |
+| Phone pairing / PIN | Yes. | No. |
+| Mobile/machine catalog authority | Yes. | No; it may expose registry data to explicitly attached clients, but ADE Mobile ignores manual runtimes. |
+| Runs agents, PTYs, git, lanes, PR work | Yes. | Yes. |
+| Clients | Desktop, `ade code`, and ADE Mobile attach to it; SSH-bound desktop windows attach to the remote machine's ADE transport. | Only clients explicitly pointed at its endpoint attach to it. |
+| Survives client close | Yes, when service-owned. Desktop/TUI fallback spawns still exist for recovery and dev paths. | Only while that foreground process is still running. |
+
+## What to rebuild after a change
+
+| Change you made | What to run/test | Why |
+| --- | --- | --- |
+| iOS UI/client-only change | Build the iOS app from the lane and connect it to an existing ADE brain. | The phone is a client; UI-only work does not require a new brain. |
+| iOS sync protocol, project catalog, pairing, or remote-command change | Rebuild/restart the target brain from the lane, then build the iOS app from the same lane. | The phone and brain both need the new contract. |
+| Desktop renderer UI change | Run/build Desktop from the lane and let it attach to the channel brain. | Renderer code is client-side unless it depends on new brain APIs. |
+| Desktop main/preload/runtime-bridge change | Run/build Desktop from the lane; rebuild/restart the brain only if the runtime RPC contract or brain behavior changed. | Electron main is a client/bridge, but some handlers route through the brain. |
+| `ade code` / TUI UI change | Build/run `ade code` from the lane and attach to the existing brain. | The TUI is a client of the brain. |
+| TUI command that depends on new RPC or shared types | Rebuild/restart the brain from the lane, then run the lane's `ade code`. | Both sides of the RPC contract must match. |
+| Brain, sync, project catalog, pairing, agents, PTYs, lanes, PR workflows, or CLI runtime service change | Rebuild the ADE CLI/brain from the lane and restart the target brain before testing clients. | These live in the always-on process; existing installed brains keep running old code. |
+| Manual runtime behavior | Start `ade runtime run --socket ` from the lane and point a client at that endpoint. | Manual runtimes are standalone and sync is always off. |
+| Remote runtime / SSH transport change | Test with a remote target using the lane-built desktop/runtime artifacts. | SSH-bound windows talk to the remote ADE transport, not the local mobile brain. |
+| Docs or web-only change | Run the docs/web preview or static checks for that surface. | No ADE brain/client lifecycle is involved. |
+
+## Running ADE locally
+
+First-time setup:
+
+```bash
+npm run setup
+```
+
+Daily desktop dev:
+
+```bash
+npm run dev
+```
+
+That aliases to `npm run dev:desktop`: it rebuilds `apps/ade-cli`, refreshes the shared dev runtime at `/tmp/ade-runtime-dev.sock` when needed, launches the Electron desktop app, and points desktop at that runtime. This is the normal desktop-dev flow.
+
+When these commands are run from an ADE lane worktree under `.ade/worktrees/`,
+they still run code from that lane checkout, but they open the primary checkout's
+project data by default. For example, running from
+`/path/to/ADE/.ade/worktrees/my-lane` opens `/path/to/ADE` as the ADE project
+and uses the lane path as the workspace root for `dev:code`.
+
+Dev command matrix:
+
+```bash
+npm run dev:desktop # refresh shared dev runtime, then launch desktop
+npm run dev:desktop:attach # desktop only; fail if dev runtime is not already running
+npm run dev:desktop:clean # desktop only; clear Vite cache before launch
+npm run dev:code:web # `ade code` in the browser (PTY + inspector WebSocket)
+npm run dev:code:attach # terminal TUI only; fail if dev runtime is not already running
+npm run dev:runtime # runtime only in the foreground
+npm run dev:all # start shared dev runtime, then run desktop/code attach commands in separate terminals
+npm run dev:stop # stop the dev runtime
+npm stop dev # same as dev:stop
+```
+
+Browser preview of the desktop renderer (UI work without Electron):
+
+```bash
+cd apps/desktop
+npm run dev:vite # mock-only: synthetic window.ade, fast shell
+ADE_PROJECT_ROOT=/path/to/project npm run dev:vite:live # mock + live runtime bridge (Linear, sync, lanes)
+```
+
+`dev:vite:live` starts the ADE dev runtime, a localhost HTTP bridge to the runtime endpoint, and Vite with a proxy so the browser can call real backend methods on top of the mock. Set `ADE_PROJECT_ROOT` to your primary project checkout (where `.ade/` and secrets live), especially when working from a lane worktree. Full details: [apps/desktop/README.md](../../apps/desktop/README.md).
+
+The dev commands intentionally use a temp endpoint and a separate Electron profile so they do not collide with the installed ADE app:
+
+```text
+/tmp/ade-runtime-dev.sock
+~/Library/Application Support/ade-desktop-dev
+```
+
+Override it when needed:
+
+```bash
+npm run dev:desktop -- --socket /tmp/my-ade-dev.sock
+npm run dev:code -- --socket /tmp/my-ade-dev.sock
+ADE_DEV_RUNTIME_SOCKET_PATH=/tmp/my-ade-dev.sock npm run dev:runtime
+ADE_DESKTOP_BRIDGE_SOCKET_PATH=/tmp/my-bridge.sock npm run dev:desktop
+```
+
+> [!WARNING]
+> Never point `--socket` at an ADE runtime you do not want restarted. In the default
+> `--auto` mode the wrapper **shuts down and recreates** whatever runtime is
+> already listening on that endpoint whenever its build hash does not match the
+> checkout you are launching — so aiming at the production `~/.ade/sock/ade.sock`
+> or another lane's live runtime will kill it (and any clients attached to it).
+> Point at a fresh per-lane endpoint (below), or use
+> `npm run dev:desktop:attach -- --socket ` to connect to an already-running
+> runtime — attach mode refuses on a build-hash mismatch instead of restarting.
+
+## Run a specific lane worktree
+
+To preview a lane's build without disturbing your installed ADE app or its
+runtime, run `dev:desktop` **from the lane checkout** on its own endpoints. Running
+from the worktree makes Vite serve that lane's code, while the wrapper
+auto-resolves project *data* to the primary checkout (as described above), so you
+see the lane's UI backed by your real lanes, PRs, and chats:
+
+```bash
+cd /path/to/ADE/.ade/worktrees/
+ADE_DESKTOP_BRIDGE_SOCKET_PATH=/tmp/ade-desktop-bridge-.sock \
+ npm run dev:desktop -- --socket /tmp/ade-runtime-.sock
+```
+
+The per-lane `--socket` gives the lane build an isolated runtime (and sidesteps
+the warning above — nothing else is listening there); the per-lane bridge endpoint
+avoids colliding with the installed app's `~/.ade/sock/desktop-bridge.sock`. Set
+`ADE_PROJECT_ROOT=/path/to/other-project` only if you want a different project's
+data. A fresh worktree has no `node_modules` — symlink the root and `apps/desktop`
+`node_modules` from the primary checkout, or run `npm run setup` inside the
+worktree first.
+
+When launching that same flow through ADE App Control from a running Alpha/Beta
+ADE window, also clear the packaged-channel environment variables inherited from
+the host app (and use an absolute lane cwd). Otherwise the dev Electron app can
+reuse the Alpha/Beta profile and lose the single-instance lock instead of opening
+the lane build:
+
+```bash
+ade --socket app-control launch --force \
+ --cwd "/path/to/ADE/.ade/worktrees/" \
+ --command "sh -lc 'ADE_PACKAGE_CHANNEL= ADE_DESKTOP_APP_NAME= ADE_DESKTOP_BRIDGE_SOCKET_PATH=/tmp/ade-desktop-bridge-.sock npm run dev:desktop -- --socket /tmp/ade-runtime-.sock'" \
+ --text
+```
+
+To test auto-runtime creation, use the default dev commands after stopping the dev runtime:
+
+```bash
+npm run dev:stop
+npm run dev:desktop # tests the desktop wrapper creating the dev runtime
+npm run dev:stop
+npm run dev:code # tests TUI wrapper creating the dev runtime
+```
+
+## Rebuild ADE Alpha or Beta locally
+
+Use these commands when you need a local packaged macOS channel build without
+waiting for the GitHub release workflow.
+
+```bash
+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-beta
+```
+
+`package:alpha` builds exactly the checkout you are in. `package:beta` is
+release-like: it fetches `origin/main`, fast-forwards the local `main` checkout
+when possible, and builds that checkout as `ADE Beta`. It does not create a
+packaging worktree.
+
+To smoke-test the Beta channel from a PR branch before it lands on `main`, pass
+the branch checkout explicitly:
+
+```bash
+node scripts/package-channel.mjs beta --repo "$PWD" --skip-install
+```
+
+Local channel outputs:
+
+```text
+apps/desktop/release-alpha/mac-arm64/ADE Alpha.app
+apps/desktop/release-alpha/ADE-Alpha-local.zip
+apps/desktop/release-beta/mac-arm64/ADE Beta.app
+apps/desktop/release-beta/ADE-Beta-local.zip
+```
+
+Install the build you want to test by replacing the matching app in
+`/Applications`:
+
+```bash
+rm -rf "/Applications/ADE Beta.app"
+ditto "apps/desktop/release-beta/mac-arm64/ADE Beta.app" "/Applications/ADE Beta.app"
+xattr -dr com.apple.quarantine "/Applications/ADE Beta.app" 2>/dev/null || true
+```
+
+Use `ADE Alpha.app` and `release-alpha` for Alpha. If the Dock already has an
+ADE Alpha/Beta icon, remove and re-pin it after installing from `/Applications`;
+Dock icons keep the exact bundle path they were pinned from, so an old icon can
+launch a stale `apps/desktop/release-*` build even after `/Applications` was
+updated.
+
+Replacing the app bundle does not replace a brain process that is already
+running for that channel. Before restarting the channel brain, close or finish
+any active ADE Desktop, ADE Code, agent, or mobile sessions that depend on it.
+Then restart and verify the channel brain through the CLI:
+
+```bash
+ADE_PACKAGE_CHANNEL=beta ADE_HOME="$HOME/.ade-beta" ade brain status --text
+ADE_PACKAGE_CHANNEL=beta ADE_HOME="$HOME/.ade-beta" ade brain restart --text
+ADE_PACKAGE_CHANNEL=beta ADE_HOME="$HOME/.ade-beta" ade doctor --text
+ADE_PACKAGE_CHANNEL=beta ADE_HOME="$HOME/.ade-beta" ade sync status --text
+```
+
+For Alpha, use `ADE_PACKAGE_CHANNEL=alpha` and `ADE_HOME="$HOME/.ade-alpha"`.
+Do not kill ADE brain processes directly during normal testing; the channel
+brain owns the mobile sync websocket and may have desktop, terminal, or phone
+clients attached. If you intentionally leave an old incompatible brain running,
+the packaged desktop may preserve it and launch a private no-sync fallback
+runtime for the desktop window, which means the Mobile drawer will not be using
+that fallback's sync service.
+
+Launching a packaged channel build should install or repair that channel's
+always-on brain service. Official auto-updates also refresh this service on the
+first launch after an update, and the installed service is expected to report the
+same runtime build hash as the packaged desktop CLI:
+
+```bash
+launchctl print gui/$(id -u)/com.ade.runtime.beta
+ls -l ~/Library/LaunchAgents/com.ade.runtime.beta.plist ~/.ade-beta/sock/ade.sock
+```
+
+Set or rotate the channel's mobile pairing PIN from **Connections > Mobile**,
+or from the CLI against that channel home:
+
+```bash
+ADE_PACKAGE_CHANNEL=beta ADE_HOME="$HOME/.ade-beta" ade brain pin generate
+ADE_PACKAGE_CHANNEL=beta ADE_HOME="$HOME/.ade-beta" ade brain pin set 123456
+```
+
+For Alpha, use `com.ade.runtime.alpha` and `~/.ade-alpha`. These builds do not
+replace the production `ADE.app`, production `ade`, or `~/.ade` runtime/state.
+Alpha and Beta also use separate Electron profile directories
+(`ade-desktop-alpha` / `ade-desktop-beta`) so browser storage and window state
+do not collide with dev or stable. Local channel packages include this Mac's
+runtime binary. Release builds still require the full cross-platform runtime
+artifact set used by remote runtime bootstrap.
+
+Validate with `npm --prefix apps/desktop run typecheck` and `npm run test:desktop:sharded` for the full desktop suite. The desktop test suite is large, so run the smallest relevant subset first.
diff --git a/docs/features/onboarding-and-settings/README.md b/docs/features/onboarding-and-settings/README.md
index 21f8a54d4..5c44d9524 100644
--- a/docs/features/onboarding-and-settings/README.md
+++ b/docs/features/onboarding-and-settings/README.md
@@ -375,7 +375,8 @@ Renderer — settings:
— toggles `prTranscriptGists.enabled` in project local config.
- `apps/desktop/src/renderer/components/settings/AboutSection.tsx`
— About (version, runtime); rendered in General.
- `AdeCliSection.tsx` is rendered in Integrations. The
+ `AdeCliSection.tsx` is rendered in General too, directly under Project —
+ CLI availability is app basics, not an integration. The
`EnvironmentSection.tsx` wrapper that used to pair them is gone.
- `apps/desktop/src/renderer/components/settings/settingsSectionUi.tsx`
— shared section headers (`SettingsSectionShell`) and toggle styling.
@@ -868,10 +869,19 @@ irm https://ade-app.dev/install.ps1 | iex # Windows PowerShell
The script downloads the static `ade-` binary plus its
native dependency archive, drops the binary in `$ADE_INSTALL_DIR` (or
-`~/.local/bin`), extracts native modules under `~/.ade/runtime//`,
+`~/.ade/bin`), extracts native modules under `~/.ade/runtime//`,
and best-effort registers the login service. No Node or npm is required —
the binary is a Node SEA.
+Both installers then put `ade` on `PATH` before anything else prompts:
+Windows updates the per-user `PATH` unless `-NoPath`; POSIX writes
+`~/.ade/env` and, with consent on a tty, appends one marker-commented
+`. "$HOME/.ade/env"` block to the shell profile (`~/.zshrc`,
+`~/.bash_profile` on macOS bash, `~/.bashrc` on Linux bash). The block is
+grep-guarded so re-running the installer never duplicates it; fish and
+unrecognized shells get printed instructions instead, as do
+non-interactive runs and `ADE_INSTALL_NO_PATH=1`.
+
On an interactive terminal the script then offers to run `ade connect`,
which signs the machine in, ensures the login service, and publishes the
machine to the account directory so desktop, web, and iOS can reach it.
@@ -902,11 +912,11 @@ changing rather than which service backs it:
| Tab | Section file | What lives here |
|---|---|---|
-| General | `ProjectSection.tsx`, `LaunchPromptSection.tsx`, `AutoUpdatesSection.tsx`, `ProductAnalyticsSection.tsx`, `AboutSection.tsx` | Project identity, launch behavior, updates, privacy, and version info. Legacy `?tab=workspace`, `?tab=project`, `?tab=context`, `?tab=onboarding`, `?tab=help`, and `?tab=tours` land here. |
+| General | `ProjectSection.tsx`, `AdeCliSection.tsx`, `LaunchPromptSection.tsx`, `AutoUpdatesSection.tsx`, `ProductAnalyticsSection.tsx`, `AboutSection.tsx` | Project identity, the `ade` command line (`#ade-cli`), launch behavior, updates, privacy, and version info. Legacy `?tab=workspace`, `?tab=project`, `?tab=context`, `?tab=onboarding`, `?tab=help`, and `?tab=tours` land here. |
| Appearance | `AppearanceSection.tsx` (renders `ChatAppearancePreview`) | Theme, chat typography and density, chat surface (tint, corners), chat details (copy-button position, message minimap, prompt-stash bookmark, live preview), and terminal text. Rebuilt on the primitives — the old version used `font-mono` for every prose line and four different control idioms. Persisted to `localStorage` under `ade.userPreferences.v1`. |
| Agents & Models | `ProvidersSection.tsx`, `OAuthConnectModal.tsx`, `AiFeaturesSection.tsx`, `BudgetCapEditor.tsx`, `DictationSection.tsx` | Provider connections, model routing, background helpers, spend cap, and voice input — merged because provider auth and per-task model routing are one mental model. **Coding Agents** cards (Claude Code, Codex CLI, Cursor, Droid) and **OpenCode — Universal Model Access**. Background helpers cover summaries, PR descriptions, commit messages, auto-naming, and scheduled-work recovery. Legacy `?tab=ai`, `?tab=providers`, `?tab=background-jobs`, and `?tab=automations` land here. |
| Lanes & Git | `LaneBehaviorSection.tsx`, `LaneTemplatesSection.tsx`, `PrChatTranscriptsSection.tsx` | How lanes start (`new lane base`), stay current (`auto-rebase`), and tell you they fell behind (`rebase suggestions` off/badge/banner + min-behind threshold), plus lane init recipes and PR transcript gists. Legacy `?tab=lane-templates` lands here. |
-| Integrations | `GitHubIntegrationSection.tsx`, `LinearIntegrationSection.tsx`, `AdeCliSection.tsx` | GitHub, Linear, and the `ade` command line — reinstated as its own tab. Legacy `?tab=integrations`, `?tab=github`, and `?tab=linear` land here, as does `?integration=github|linear|cli`. |
+| Integrations | `GitHubIntegrationSection.tsx`, `LinearIntegrationSection.tsx` | GitHub and Linear — reinstated as its own tab. Legacy `?tab=integrations`, `?tab=github`, and `?tab=linear` land here; `?integration=github|linear` too, while `?integration=cli` follows the `ade-cli` anchor to General. |
| Notifications & Sound | `NotificationsSection.tsx`, `AgentCompletionSoundSection.tsx` | Delivery for `AttentionPreferences`: per-event policy (off / ambient / notify) for agent and PR events, quiet hours, focus suppression, phone delivery and escalation, the agent completion sound, and the Lanes banner budget. The per-event matrix and quiet hours were fully modelled with balanced defaults but had **no UI at all** before this tab. |
| Activity | `ActivitySection.tsx`, `ActivitySettingsControls.tsx` | The surfaces Activity itself paints: the ADE notch (enabled, reveal mode, expanded panel), celebrations, Activity sounds, hide-previews, and the per-machine notification mute. `ActivitySettingsControls` is mounted here **and** by the gear inside the Activity popover and pane, so the two entry points cannot drift. Legacy `?tab=attention` plus the `#attention-notch`, `#celebrations`, `#attention-sounds`, and `#hide-previews` hashes land here. |
| Secrets | `SecretsSection.tsx` | Encrypted key/value pairs for agents, desktop, and the CLI, with `.env` import. Legacy `?tab=secret` lands here. |