leed site init turns a downloaded leed.config.json into a working project: it clones your content repository, installs the tooling that keeps the workflow enforceable, and writes a finalized configuration file next to the checkout. The installer runs it for you as its last step, so most people never type it. You run it yourself on a second machine, or after downloading a fresh leed.config.json from Developer Setup because the old project folder is gone or its repository token has expired.
Synopsis and preconditions
leed site initThe command takes no options of its own. Everything it needs comes from the configuration file and from your stored session.
| Precondition | What it means | If it is missing |
|---|---|---|
A reachable leed.config.json | The file the CMS gave you, in the current directory or its parent | “Could not locate leed.config.json. Run this application from the directory where your repo exists.” |
initialized is false in that file | The file has not already been consumed by an init | “Site configuration already complete! Try running a build leed site build” |
A stored session for the file’s environment | Credentials for production, staging or dev, whichever the file names | “Not authenticated.”, with both login commands printed under it |
| Developer access on your account | The credentials endpoint is gated on the developer override, not on your plan | The developer-credentials fetch is refused and init stops |
| A global git identity | user.name and user.email set with git config --global | Init finishes anyway and prints the two commands to run — but leed site commit refuses until you run them |
The configuration file is looked for in exactly two places: the directory you are standing in, and its parent. Nothing deeper up the tree is searched, which is why the installer leaves you in ~/leed with the file directly beside you.
What it does, in order
- Creates the project folder. A directory named after your domain, slugified —
acme.combecomesacme-com— inside the directory holdingleed.config.json. - Loads your credentials for the environment the configuration names. Without them it stops at “Not authenticated.” and prints both
leed auth loginforms as the hint. - Selects your organization, when the configuration carries a
companyId. This is best-effort: a failure here is ignored, because the session may already have the right organization selected. - Fetches developer credentials from the CMS. This is the call that mints a fresh GitLab token for your content repository. A 401 is “Session expired or invalid.”; a 422 is “No active organization. Download a fresh leed.config.json from the CMS.” — the signal that the file you are holding is stale rather than that your session is.
- Clones the repository, or re-points the one already on disk at a remote URL carrying the token just issued.
- Checks your git identity. A missing
user.nameoruser.emailis reported, not fatal. - Installs the project tooling — dependencies, git hooks, VS Code files and the Claude Code skills.
- Writes the finalized configuration into the new project folder and deletes the file you started from.
When it finishes it prints where to go next:
Initialization is complete, cd into your new project directory and try running:
leed site buildThe repository it clones
The content repository lives at gitlab.com/leed-ai/customers/<environment>/<your-domain-slug>/raw-content, and init checks it out on the staging branch. That default matters more than it looks: leed site commit and leed site push accept no other branch, so a fresh checkout is already on the only branch that can ship.
The origin URL init writes embeds the GitLab token it was just issued, in the form https://access-token:<token>@gitlab.com/…. It is stored in plain text in raw-content/.git/config.
If raw-content/ is already on disk, init does not re-clone. It refreshes the origin URL with the freshly issued token and leaves your working tree untouched — which is exactly why re-running init is the supported repair for a repository token that has stopped authenticating.
A clone that fails does not abort the run. Init reports what git said, finishes configuring the project, and leaves you with a site directory whose content repository is not yet usable — so the error is worth reading rather than scrolling past.
What it installs into the repository
| Path | What it is | Overwrites an existing file? |
|---|---|---|
raw-content/package.json | A minimal @leed/site manifest | No — copied only when the repository has none |
raw-content/node_modules/, bun.lock | The four devDependencies below | Managed by bun install |
raw-content/.husky/ | Three git hooks plus the scripts they call | Yes, every file, on every version change |
raw-content/.vscode/tasks.json | Twelve leed tasks | Yes |
<project>/<domain-slug>.code-workspace | A VS Code workspace pointing at raw-content | Yes |
raw-content/.claude/skills/ | Three Claude Code skills | Yes — and skills removed in a newer release are deleted |
<project>/<domain-slug>/leed.config.json | The finalized configuration | Written fresh; the source file is deleted |
Git hooks
Init installs pre-commit, pre-push and commit-msg under .husky/, along with the three scripts they run. pre-commit and pre-push both check for git config --get leed.invoked, a flag only the CLI sets, and commit-msg checks that the message carries the structured payload the CLI writes. A bare git commit or git push fails with exit 18 and a message naming the leed command to use instead.
Those hooks are the reason validate, commit and push is a requirement rather than a recommendation. Everything else about git — git status, git log, git diff, git pull --rebase — keeps working normally.
When .husky/ does not yet exist, init runs bunx husky init first to register the hooks path with git, then copies its own files over the top.
Dependencies
Four packages are installed as devDependencies with bun install --silent --dev:
tailwindcss
@tailwindcss/forms
@tailwindcss/typography
huskyThe exact versions are pinned by the CLI release you have installed, so they move when the CLI does. package.json itself is copied only when the repository does not already have one — if you have added scripts or dependencies of your own, init installs alongside them and never replaces the file.
VS Code
A <domain-slug>.code-workspace file is written beside the checkout, in the project folder rather than inside the repository. It opens raw-content directly, because that is where git, .claude/ and every file you are allowed to edit live. .vscode/tasks.json goes inside the repository, so it travels with the workspace.
The twelve VS Code tasks
| Task label | What it runs |
|---|---|
| Run Webserver | leed site build -d --serve |
| Build | leed site build |
| Build (debug) | leed site build -d |
| Site Validation | leed site validate |
| Site Validation - Reset (Dry-Run) | leed site validate -r --dry-run |
| Site Validation - Reset | leed site validate -r |
| Commit Changes | leed site commit -m <prompted message> |
| Push to Staging | leed site push |
| Pull Latest from Staging | git pull |
| Login | leed auth login --browser |
| Logout | leed auth logout |
| Auth Status | leed auth status |
Commit Changes prompts for the message, defaulting to update site content. Note that Run Webserver passes -d: it is a debug build, which is faster to iterate on but is deliberately not recorded as your pre-commit build. Run Build before committing, or read why on leed site build.
Site Validation - Reset discards your changes to every protected file and now asks before it does. From a VS Code task there is a terminal to answer on, so the prompt appears normally.
Claude Code skills
Three skills are installed under .claude/skills/, so an agent working in the repository knows the toolchain without being taught it each time:
site-builder— templates, layouts, partials, CSS, JavaScript and custom pages, with the Handlebars helpers, Tailwind v4, Alpine.js and Leed conventions the build expects.site-preview— the local dev-server lifecycle: build, serve, restart, stop.site-publish— the validate → build → commit → push workflow, including what to do when validation refuses or a rebase conflicts.
The configuration file moves
This is the step that surprises people. Init does not edit leed.config.json in place. It writes a new, finalized copy inside the project folder it created — carrying initialized: true, the resolved location, and version markers for the husky, VS Code and skill files it just installed — and then deletes the original.
Before:
~/leed/
└── leed.config.jsonAfter:
~/leed/
└── acme-com/
├── leed.config.json ← the finalized copy
├── acme-com.code-workspace
└── raw-content/
├── .claude/skills/{site-builder,site-preview,site-publish}/
├── .husky/{pre-commit,pre-push,commit-msg,…}
├── .vscode/tasks.json
├── package.json
├── node_modules/
└── src/Every field in the finalized file is described on leed.config.json, files and environment.
Running it twice
Once initialized is true, init refuses:
Site configuration already complete! Try running a build `leed site build`Nothing is touched. That refusal is checked before anything else runs, so it costs nothing and cannot half-apply.
To genuinely re-initialize — a new machine, a lost project folder, an expired repository token — download a fresh leed.config.json from Developer Setup, put it in the directory you want the project created under, and run leed site init there. If raw-content/ still exists at that location, your working tree survives and only the remote URL is refreshed.
Keeping the installed files current
Init records the CLI version it installed the managed files with, in three places: huskyVersion, vscodeVersion and the skills’ own skillsVersion. Every later leed site command compares those against the version of the CLI running, and reinstalls whichever set has fallen behind before it does its own work — then rewrites leed.config.json with the new markers.
That sync is the answer to “why did those files come back”. After the CLI updates itself, the next leed site build you run replaces your .husky/ hooks, your .vscode/tasks.json and your .claude/skills/ with the current versions. Skills that a newer release removed are deleted rather than left orphaned. The files are Leed’s, they are on the read-only list, and edits to them do not survive — editable and read-only files draws the line.
Init is step six of the installer, and the folder it produces is described top to bottom in your site repository and what Leed writes into your repo. With the site cloned, the loop you will actually live in is local development, and every other command you can run in this repository is indexed at the CLI command reference. If a step failed with a message this page did not explain, it is cataloged in CLI troubleshooting and exit codes.