Installing the Leed CLI

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. The CLI itself is an ordinary package on public npm, @leed-ai/cli, so you can also install it by hand.

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.

RequirementMinimum versionIf missingFatal?
curlany“curl is required but not found.”Yes
Git2.31.0“git v2.31.0 or later is required but not found.”, or “git v… is below the minimum v2.31.0.”, followed by how to install or upgrade it on your platformYes
Bun1.1.0Offers to run Bun’s own installer for youYes, if you decline or no terminal is attached

Git’s minimum is set by the CLI: the repository token reaches git through config environment variables that older git ignores. 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] or Update bun? [Y/n]. Answering no stops the install with the command to run yourself; with no terminal attached it does not ask at all, and stops the same way. A Bun that is installed but not yet on this terminal’s PATH is found and used rather than installed twice. 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

Supported natively. When leed setup signs you in, it 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 everything else on this page is identical to native Linux. Where WSL exposes no display, open the printed sign-in URL in your Windows browser.

On every platform the sign-in code and URL are printed first, and opening a browser is only an attempt. On a platform the CLI does not recognize, over SSH, or on a Linux box with no display, it does not try — 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.

The Developer Setup dialog, showing the install command, the prerequisites list and the After Installation section
curl -fsSL https://app.leed.ai/install | bash

Run 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 your site can be set up. That is a permission, not a plan: developer access is a resource override an administrator grants to a team member, and without it leed setup stops when it fetches your site’s developer credentials — the CMS answers HTTP 403, reported as permission_denied. See billing and developer access.

What the installer does

Three steps, then a hand-off. The installer itself only installs — Bun if needed, the CLI, and shell completion. Signing in and setting up your site are the job of leed setup, which the installer runs last for the environment it was served from: production, for https://app.leed.ai/install. Every step checks before it acts, which is why the same command behaves differently on your second machine than on your first.

StepWhat it asksWhat it writesIf it fails
1. Check prerequisitesWhether to install or update BunBun, if you agreeFatal — git older than 2.31, or Bun missing and not installed
2. Install the CLINothing@leed-ai/cli from public npm as the global leed binary; the old @leed/site-management package is removed; a PATH line for Bun’s bin directory in your shell’s startup fileFatal if leed is still not on your PATH, or is still the old package
3. Shell completionNothingYour shell’s completion hookNever fatal — a warning naming leed completion install
4. Hand off to leed setupWhich saved login to use; where to set your site up (default ~/leed)~/.config/leed/auth.json, your site’s checkout, ~/.config/leed/sites.jsonReported by leed setup

Step 2 keeps a leed already on your PATH when it is 6.18.0 or later — the first release with leed setup — and is not the old @leed/site-management package. Anything else is replaced with bun install -g @leed-ai/cli, and the old package is then removed so it can never take the leed command back. Afterwards the installer makes sure a new terminal finds leed: it adds one marked line putting Bun’s bin directory on PATH to your shell’s startup file — ~/.bashrc, ~/.zshrc or fish’s config.fish — and, for bash, to the login file an SSH session or macOS Terminal reads, unless one is already there.

Step 4 runs leed setup --env <environment>, which prints the environment and the CMS it will talk to, then signs you in. When this machine already holds logins for that environment, it asks which one to use — or to sign in again — 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: it prints the code and the approval URL, opens your browser where the machine has a display, and waits for you to approve. The company is chosen on the approval page. The flow is described in full on CLI authentication.

It then keeps a site already set up for that company, or asks for a folder — ~/leed by default, with a leading ~ expanded — and runs leed site init there. It installs Font Awesome Free into the site and sets up shell completion, and at a terminal it offers, once each, to write a VS Code workspace and to install the Leed plugin for Claude Code. The plugin offer needs the claude command on your PATH; if you use Claude Code only in an IDE or in the Code tab of the Claude desktop app, setup prints the two /plugin commands to run there instead. A no to either offer is remembered in ~/.config/leed/preferences.json and never asked again. Without a terminal — CI, or a pipe with nothing attached — the installer stops after step 3 and prints the leed setup command to run instead.

leed site init is what clones your repository into <folder>/<your-domain-slug>/raw-content, installs the project tooling and records the site in ~/.config/leed/sites.json.

flowchart TD
    start([Installer starts]) --> git{git 2.31 or later?}
    git -->|no| gitFatal[Fatal — install or upgrade git]
    git -->|yes| bun{bun 1.1.0 or later?}
    bun -->|missing or older| tty{Terminal attached?}
    tty -->|no| bunFatal[Fatal — install bun yourself, then re-run]
    tty -->|yes| askBun{Install or update bun?}
    askBun -->|declined| bunFatal2[Fatal — bun is required]
    askBun -->|accepted| leed
    bun -->|yes| leed{"leed 6.18.0 or later on PATH,<br/>and not the old package?"}
    leed -->|yes| keep[Keep it]
    leed -->|no| install[bun install -g @leed-ai/cli]
    keep --> completion[Set up shell completion]
    install --> completion
    completion --> path[Put Bun's bin directory on PATH for new terminals]
    path --> handoff{Terminal attached?}
    handoff -->|yes| setup([leed setup --env environment])
    handoff -->|no| print([Print the leed setup command and exit 0])

Every branch on that diagram is a step somebody has watched the installer skip and wondered about. The version check is the one that surprises people most: a leed that already meets the minimum is kept, so re-running the installer does not upgrade it — leed self-update does that — unless you name a version, as below. The environment is stamped into the script by the CMS that served it and passed to leed setup explicitly, so the CLI always talks to the system the script came from.

Moving from @leed/site-management

Before the CLI moved to public npm it was published privately as @leed/site-management, installed through a registry token in ~/.bunfig.toml. That package no longer receives updates, and leed self-update run on it does not move you across. Re-run the one-line installer once: it installs @leed-ai/cli, removes the old package, and leaves the leed command and every subcommand as they were. By hand, the same move is:

bun remove -g @leed/site-management && npm i -g @leed-ai/cli

If your ~/.bunfig.toml still has an "@leed" line under [install.scopes], it was only there for the old package and can be deleted; the CLI no longer reads or writes that file. A leed.config.json the old CLI left beside your checkout is imported into ~/.config/leed/sites.json the first time the new one meets it — see files and environment.

Pinning a version

Pass a version as the script’s first argument. It is installed even over a leed that already meets the minimum, and a version below 6.18.0 is refused before anything is installed:

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 environmentTag installed
productionrelease
stagingstaging
devstaging
anything elserelease

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 --version

If the command is not found, the terminal you ran the installer from started before the installer put Bun’s bin directory on PATH. Open a new terminal, or run the line the installer printed:

export PATH="$HOME/.bun/bin:$PATH"

Then move into the checkout leed setup created and build it. Its next steps print the exact path, <folder>/<your-domain-slug>/raw-content: the site’s folder is named after your domain with dots turned into hyphens, so pithy.sh becomes pithy-sh. Every leed site command works from that checkout or any folder inside it.

cd ~/leed/<your-domain-slug>/raw-content
leed site build --serve

That 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 skips prerequisites that are already satisfied, keeps a CLI that already meets the minimum, and hands off to leed setup, which asks which saved login to reuse rather than signing you in again, keeps a site that is already set up rather than cloning it twice, and does not repeat an offer you declined. Nothing in your site repository is touched. To see what a run would still do without changing anything, run leed setup --env production --check.

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 or a sandbox site, CI is unset, --json was not passed, and neither of the internal --customer / --location flags was given, the CLI consults a 24-hour throttle recorded in a .lastcheck.json file beside itself. If the throttle has elapsed it reads the dist-tag for the environment of the site you are in — release for production, staging for staging and dev, and release outside any site — compares it with the running version, and, when the remote one is newer, runs bun install -g @leed-ai/cli@<tag> on the spot. Updates only move forward, so a staging install is never downgraded by running in a production site.

To force the check past the throttle:

leed self-update

When 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 in it. At that point the CLI compares the huskyVersion recorded for the site in ~/.config/leed/sites.json against its own version and, when they differ, reinstalls the git hooks. That is the only managed set left: the CLI no longer rewrites VS Code files, which only leed site vscode writes, and no longer installs Claude Code skills, which now come from the Leed plugin for Claude. The first run after upgrading from an older CLI removes the skill copies that CLI put in .claude/skills/, once, with a one-line notice.

This is why hook 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 install

Restart your terminal to pick it up. To remove it:

leed completion uninstall

The package runs no install scripts, so a manual npm i -g does not set this up. The installer and leed setup both run it for you, and it does nothing when completion is already installed. The completion command is hidden from leed --help, but it is supported and documented here.

What setup leaves in your repository

Step 4 leaves more than a git checkout behind. Inside raw-content you get husky git hooks that enforce the commit and push workflow, the project’s build dependencies, and Font Awesome Free under src/static/webfonts when no Font Awesome fonts were there already. A VS Code workspace is written only when you accept leed setup’s offer or run leed site vscode, and the CLI writes no Claude Code skills into the site.

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 leed setup created shows up alongside your browser sessions, badged CLI, in sessions, connected accounts and API tokens.

ESC