Skip to main content
Put a tools/bazel shell wrapper in your workspace and Bazelisk runs it on every bazel invocation. The wrapper sends each command to the right tool: aspect for the verbs aspect wraps (build, test, lint, format, gazelle, buildifier, delivery, any custom .axl task), and vanilla bazel for everything else (query, info, clean, …). Your arguments are forwarded verbatim. The wrapper never inspects or rewrites a flag; it only picks which binary to run. aspect then forwards each flag it doesn’t define to Bazel in the slot you typed it in: after the verb as a command option, before it as a startup option. A flag the task defines itself stays with the task. See flag passthrough.
Requires Bazelisk. The tools/bazel hook is a Bazelisk feature; the real bazel binary doesn’t look for it. It works when the bazel on your team’s PATH is Bazelisk.
bazel run stays on vanilla bazel by default: aspect run exists, but its semantics don’t match bazel run closely enough to replace it transparently yet. Reach for aspect run directly when you want it, or add run to the verb list below once you’ve validated it for your workflows. The Aspect CLI can drop the wrapper in for you. From anywhere in your workspace:
This downloads the wrapper and its reference doc from the latest release tag (never main), writes tools/bazel (executable) and tools/bazel.md, and prints optional snippets for your shell’s rc file that toggle the escape hatches below. Re-running is idempotent: an up-to-date copy is left alone, and a newer release is offered as an upgrade.
Requires Aspect CLI v2026.39.10 or newer. On an older release, install the wrapper by hand instead, as shown next.
Or drop it in by hand:
Developers who prefer raw bazel for a shell session can set ASPECT_WRAPPER_SKIP=1 to bypass routing entirely.

Customize the verb routing

Two lists at the top of tools/bazel drive every routing decision. Edit them in your repo’s copy:
  • ASPECT_VERBS: the verbs routed to aspect. A verb in neither list is treated as a custom .axl task and also routes to aspect, so what listing one really buys is the fallback to vanilla bazel when aspect isn’t installed. That only makes sense for verbs Bazel also has.
  • BAZEL_VERBS: the closed set of Bazel commands. A verb here that is not in ASPECT_VERBS goes to vanilla bazel untouched (query, info, clean, mod, coverage, …). Update it only when Bazel adds a command.
Two common edits:
  • Send build / test to vanilla bazel instead. Remove them from ASPECT_VERBS and they fall through to the real bazel untouched. Keep the aspect-only verbs (lint, format, delivery, gazelle) listed so bazel lint and friends still reach aspect.
  • Add run, or your own aspect commands. Once aspect run suits your workflows, add run to ASPECT_VERBS so bazel run routes through it.
The full reference (routing rules, every escape hatch, the trace output, and how the wrapper avoids recursing back into aspect) lives next to the script: tools/bazel.md.