Skip to main content
AXL — the Aspect Extension Language — is Starlark, and its runtime ships inside the Aspect CLI. So the first thing to install is the CLI. The runtime is written in Rust. Under the hood AXL is powered by the Rust Starlark interpreter from the Buck2 team, which means that unlike the Starlark in Bazel, AXL has types, and constructs Bazel’s dialect doesn’t have — record() and enum() among them. You’ll see all three as the course goes on.

Install

See How to install the Aspect CLI for every option, or run:
The last line is a sudo mv into /usr/local/bin, so you need admin rights on the machine.
In a workshop, pin the version on the install line. With no argument the script asks api.github.com which release latest points at, and anonymous GitHub API calls are rate-limited to 60 per hour per IP. A roomful of people behind one NAT exhausts that, and the script hard-exits rather than guessing.
A concrete version makes the asset URL deterministic, so the API lookup is skipped entirely. Setting GITHUB_TOKEN works too, but pinning is one fewer thing to brief.
On macOS, Homebrew works too:

What you just installed

It says launcher, not CLI. What landed on your PATH is a small program whose only job is to fetch and run the right Aspect CLI for whatever repository you happen to be standing in. Two commands that differ only by a -- report two different programs: The launcher version and the CLI version do not have to match. Stand in two repositories with different pins — .aspect/version.axl, which the next section is about — and they come apart:
If your machine has never fetched 2026.41.28, the pin-b lines arrive behind a downloading aspect cli version v2026.41.28 / downloaded … pair — the shape shown in the next section. That is the launcher doing its job, once per version. One launcher, two CLIs, and the launcher line never moved. That is the design: the launcher is to the Aspect CLI what Bazelisk is to Bazel, so every developer on your team runs the same CLI version without anyone coordinating, because the repository decides.
The launcher itself rarely changes, and its own version almost never matters — the version that matters is the one it fetches for you. To upgrade it, run the install line again or brew upgrade aspect. The Homebrew formula is generally updated only when the launcher changes, so it can report an older version than curl installs.

Pinning a version

A repository pins its CLI version in .aspect/version.axl. You can also put one in your home directory, at ~/.aspect/version.axl, as a personal default. When both exist, the repository wins — a repository has real commitments to a version (its config.axl, its tasks, its CI), while the home pin is just your fallback for repositories that haven’t expressed an opinion. Unpinned, the launcher fetches the latest release. Run this somewhere with no pin:
That download happens once per version, then it’s cached.

A little housekeeping

Before the first exercise, here is something useful the CLI will do for you right now. Deleting a git worktree leaves its Bazel output base behind. Nothing in Bazel collects it, so every branch you have ever checked out and thrown away is probably still on this laptop — and if you run coding agents, which make a worktree per task, there will be more of them than you expect. This is read-only and takes a second:
Reclaim disk and memory from Bazel goes on from there: aspect gc to remove what nothing is using, and aspect worktree to stop the orphans accumulating in the first place. Worth running before you create one more output base, which the next section is about to do. Those three are AXL tasks, like the ones you are about to write. None of them changes how a build works — they improve the work around it, and they do it in the same Starlark you already use for Bazel. Next: a project consisting of exactly one file.