leed is the command-line tool you develop your site with: it clones your site repository, builds and serves it locally, validates what you changed, and pushes it to your preview site. One command installs it, signs you in, and leaves you with a working checkout — and that installer is the only supported way to get it.
The CLI talks about pages, deployments, previews and page types exactly as the CMS does, so if any of those words are new, read core concepts first.
Before you start
The installer checks three things before it touches anything on disk.
| Requirement | Minimum version | If missing | Fatal? |
|---|---|---|---|
curl | any | “curl is required but not found.” | Yes |
| Git | any | “git is required but not found. Install from https://git-scm.com/” | Yes |
| Bun | 1.1.0 | Offers to run curl -fsSL https://bun.sh/install | bash for you | Yes, if you decline |
Bun is the one worth understanding. If Bun is absent, or older than the minimum, the installer asks before doing anything — Install bun? [Y/n]. Answering no stops the install: “bun v1.1.0 or later is required.” There is no alternative runtime. The minimum is a constant inside the installer and will move over time, so trust the number the installer prints over the number written here.
Platform support
- Linux
- Mac
- Windows
Supported natively. When you press O at the sign-in step, the installer opens your browser with xdg-open — but only when an X or Wayland display is present (DISPLAY or WAYLAND_DISPLAY is set).
Windows Subsystem for Linux is supported here too, not as a separate platform. The installer detects WSL from the kernel version string, and uses wslview instead of xdg-open so the sign-in page opens in your Windows browser. Everything else on this page is identical to native Linux.
Supported natively. When you press O at the sign-in step, the installer opens your browser with open. Both Apple silicon and Intel work; Bun ships builds for each and the installer picks the right one.
Native Windows and PowerShell are not supported. The installer is a bash script, and nothing in the CLI’s install path has a PowerShell equivalent.
Install WSL, open your Linux shell, and follow the Linux tab. Your site repository then lives inside the WSL filesystem, where the build tooling expects it.
On every platform the installer prints the sign-in code and URL first, and opening a browser is only an offer. On a platform it does not recognize, over SSH, or on a Linux box with no display, pressing O simply reports that no browser could be opened — open the printed URL yourself, on any machine where you are signed in to the CMS.
Get your install command
Open the profile menu at the bottom of the CMS rail and choose Developer Setup. The dialog builds the command from the origin you are signed in to and gives you a copy button.
curl -fsSL https://app.leed.ai/install | bashRun it in your terminal. Do not open https://app.leed.ai/install in a browser — that URL is content-negotiated and only returns the installer script to curl and wget.
Your account also needs developer access before the installer can finish. That is a permission, not a plan: developer access is a resource override an administrator grants to a team member, and without it step four fails with “… is not authorized to download configuration (HTTP 403). The account needs developer access to that company.” See billing and developer access.
What the installer does
Six steps, in this order. Each one either asks you something or writes something, and most of them can be skipped on a re-run — which is why the same command behaves differently on your second machine than on your first.
| Step | What it asks | What it writes | If it fails |
|---|---|---|---|
| 1. Check prerequisites | Whether to install or update Bun | Bun, if you agree | Fatal — Git or Bun missing |
| 2. Authenticate | Which saved login to use, when any is stored; then approve a code in your browser | ~/.config/leed/auth.json | Fatal after five minutes without approval |
| 3. Choose a directory | Where to create your project (default ~/leed) | The directory, if it does not exist | Fatal if the directory is not writable |
| 4. Download configuration | Nothing | <project>/leed.config.json | Fatal — 401 means the sign-in was not accepted, 403 no developer access, 422 a sign-in bound to no company; anything else is a server error |
| 5. Install the package | Nothing | ~/.bunfig.toml, then the global leed binary | Fatal if leed is still not on your PATH afterwards |
| 6. Initialize the site | Nothing | Your site folder and its raw-content git checkout | Reports whatever leed site init reports |
Step 2 starts with a question whenever this machine already holds a login: the installer lists each saved login by email and domain, offers Another company as followed by the email for companies that user belongs to but has no login for here — a company that has been deleted is not offered — and A different user (sign in) — with no default, so you always choose which account the install is for. Signing in is the standard device-code flow the CLI uses everywhere: the installer prints the code and the approval URL, then gives you 60 seconds to press O to open it in your browser (any other key continues without one), and waits for you to approve. If the installer requested no company, the approval page shows a company picker. The flow is described in full on CLI authentication.
Step 3 expands a leading ~, so ~/projects/leed works. A path that is not absolute is not used silently: the installer shows what it would resolve to and asks before using it. If the directory already contains a leed.config.json, you are asked “Overwrite existing configuration?” — declining keeps the file you have and the installer carries on.
Step 4 downloads your company’s configuration and the registry credentials in one request. A 401 means the sign-in was not accepted; a 403 names the account and company and means the account lacks developer access to it; a 422 means the sign-in is bound to no company because the account belongs to several and none was chosen — re-run and pick one. Any other status is reported as a server-side problem, with the server’s own response printed underneath, so you are never sent to chase a permission that is fine. Every installer request has a connect and total timeout and is retried a bounded number of times on a timeout, 429 or 5xx — except requests to this configuration endpoint (this download, and the saved-login check in step 2), which are never retried after a timeout, because the timed-out request may still have minted your tokens.
Step 6 runs leed site init, which clones your repository and installs the project tooling.
flowchart TD
start([Installer starts]) --> git{git on PATH?}
git -->|no| gitFatal[Fatal — install Git from git-scm.com]
git -->|yes| bun{bun 1.1.0 or later?}
bun -->|missing or older| askBun{Install or update bun?}
askBun -->|declined| bunFatal[Fatal — bun 1.1.0 or later is required]
bun -->|yes| creds{Any login saved on this machine?}
askBun -->|accepted| creds
creds -->|none| device[Sign in with the device code]
creds -->|yes| choose{Which account is this install for?}
choose -->|a different user| device
choose -->|another company as this user| device
choose -->|a saved login| probe{Probe the configuration endpoint}
probe -->|200| reuse[Reuse the stored session]
probe -->|401| device
probe -->|any other status| reuse
device --> dir{Directory already holds leed.config.json?}
reuse --> dir
dir -->|yes| overwrite{Overwrite it?}
dir -->|no| config[Write leed.config.json]
overwrite -->|yes| config
overwrite -->|no| keep[Keep the existing configuration]
config --> installed{leed 4.27.0 or later already installed?}
keep --> installed
installed -->|yes| skip[Skip the package install]
installed -->|no| install[bun install -g --trust the CLI package]
skip --> init([leed site init])
install --> init
Every branch on that diagram is a step somebody has watched the installer skip and wondered about. The probe is the one that surprises people most: when the configuration endpoint answers with anything other than 200 or 401 — a 502, say — the installer keeps your existing credentials rather than forcing you through a pointless re-login, and lets step four surface the real error. A 401 re-signs you in to the company you just chose, not whichever company your browser last had open.
What lands in ~/.bunfig.toml
Step 5 writes the @leed scope into Bun’s global configuration so bun install -g can reach the private registry:
[install.scopes]
"@leed" = { url = "https://gitlab.com/api/v4/projects/<project>/packages/npm/", token = "<your registry token>" }There are four cases, checked in this order:
- An existing
"@leed"line that references an environment variable ($VARor${VAR}) is left alone, and the installer prints “@leed registry in ~/.bunfig.toml uses an environment variable — leaving it alone”. That is a deliberate courtesy to developers managing their own token, not an error. - No
~/.bunfig.tomlat all — a fresh file with an[install.scopes]section is written. - The exact same line is already there — nothing happens.
- Otherwise the stale
"@leed"line is removed and the fresh one inserted under[install.scopes], adding that section if the file did not have one.
That file holds a real credential. It is covered, with the other two, in leed.config.json, files and environment.
Pinning a version
Pass a version as the script’s first argument:
curl -fsSL https://app.leed.ai/install | bash -s "1.2.3"Without an argument, the installer takes the npm dist-tag that matches the CMS environment serving it:
| CMS environment | Tag installed |
|---|---|
production | release |
staging | staging |
dev | staging |
| anything else | release |
In practice that means https://app.leed.ai/install installs release, which is what you want unless you are deliberately testing against staging.
Verify the install
leed --versionIf that prints nothing, the Bun global bin directory is not on your PATH. The installer detects this and tells you what to add to your shell profile:
export PATH="$HOME/.bun/bin:$PATH"Then open the site folder the installer created and build it. The installer’s Next steps line prints the exact path: the folder is named after your domain with dots turned into hyphens, so pithy.sh becomes pithy-sh.
cd ~/leed/<your-domain-slug>
leed site build --serveThat is the loop you will live in from here: local development.
Re-running the installer
Running the same curl command again is safe, and it is the standard fix for a setup that has drifted. It reuses valid credentials rather than signing you in again, skips prerequisites that are already satisfied, warns before overwriting a leed.config.json, replaces a stale registry line in ~/.bunfig.toml, and upgrades or no-ops the package install. Nothing in your site repository is touched.
If a step does fail, every message the installer can print is cataloged in CLI troubleshooting and exit codes.
Automatic updates
The CLI does not merely check for updates — it installs them, without asking.
After any command finishes, and provided you are not in a development checkout, CI is unset, and neither of the internal --customer / --location flags was passed, the CLI consults a 24-hour throttle recorded in a .lastcheck.json file beside itself. If the throttle has elapsed it reads the release dist-tag from the registry, compares it with the running version, and — when the remote one is newer — runs bun install -g @leed/site-management@release on the spot.
To force the check past the throttle:
leed self-updateWhen there is nothing to do it prints “Already up-to-date, no updates required.”
Managed-file drift
An upgrade changes the CLI, not your repository — until the next time you run a leed site command. At that point the CLI compares the stored huskyVersion, vscodeVersion and skills version in your leed.config.json against its own version, and re-installs whichever set is behind: the git hooks, the VS Code workspace files, the Claude Code skills. Then it rewrites leed.config.json with the new version markers.
This is why files you deleted reappear after an update, and why the first leed site command after an upgrade does a little more than you asked for.
Shell completion
Tab completion is available for bash, zsh and fish. The shell is detected from $SHELL:
leed completion installRestart your terminal to pick it up. To remove it:
leed completion uninstallThe package’s own post-install step also makes a best-effort attempt at this for every shell whose rc file already exists, and fails silently if it cannot — so completion may already be working before you run anything. The completion command is hidden from leed --help, but it is supported and documented here.
What init installs into your repository
Step 6 leaves more than a git checkout behind. Inside raw-content you get husky git hooks that enforce the commit and push workflow, a VS Code workspace with tasks for the common commands, and five Claude Code skills — site-builder, site-preview, site-publish, site-assets and site-settings.
The full inventory of what lands where — and which of those files you are allowed to edit — is in leed site init and in your site repository.
Every command the installer puts on your path, with its flags and its global options, is indexed at CLI command reference. And the session the installer created shows up alongside your browser sessions, badged Installer, in sessions, connected accounts and API tokens.