Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

dx init

bazel run @rules_dx//:dx -- init
bazel run @rules_dx//:dx -- init --apply
bazel run @rules_dx//:dx -- init --apply my_module
tools/dx init

dx init

dx init [--apply] [module-name]

Adopts dx into an existing tree. Use dx new to start a new project. Checks by default; --apply scaffolds. Takes an optional module name, which defaults to my_project. The module name starts with [a-z0-9] and uses [a-z0-9._-] only. An invalid module name fails before any file is written. Writes each file only when it is missing. The exceptions are the marked block in AGENTS.md and stale launchers (see below). It writes .dx/version, .gitignore, .bazelignore, .envrc, .devcontainer/devcontainer.json, .vscode/settings.json, .vscode/extensions.json, .github/workflows/ci.yml, MODULE.bazel.snippet, the launcher (tools/dx and tools/dx.cmd), and .claude/settings.json with the dx worktree hooks. The hooks run tools/dx, which also runs in a Git Bash or MSYS2 shell on Windows. It also writes a marked block to AGENTS.md, creating the file when it is missing (see below). If .claude/settings.json already exists, it is left alone and the missing hook entries are printed to stderr. The CI caller pins the rules_dx reusable workflow at a reviewed commit and passes rules_dx_version matching the snippet. When the tree has .cu or .cuh files, the snippet also carries the pinned CUDA toolkit and host-compiler stanza and the opt-in cuda-eula license line (see dx new cuda). The .gitignore lists target/, /bazel-*, node_modules/, __pycache__/, *.pyc, .gradle/, /build/, /.dx/ide/ and /.vscode/.rules_rust_analyzer/. The .bazelignore lists .dx, so Bazel skips the IDE output bases. When a .bazelignore already exists it is left alone, and dx init --apply prints dx: .bazelignore lacks .dx; add it so Bazel skips the IDE output base to stderr if it does not list .dx. Editor settings point at the managed interpreter and tools with host-native names. For Rust they set up rust-analyzer with the rules_rust discover and flycheck targets. Both run with bazel run on separate output bases under .dx/ide/, and the file watcher and search exclude bazel-* and .dx/ide/. They configure language servers, not debuggers. See Supported Surface. Flags: --apply. Scopes: optional single module name.

Output: text only. Check mode looks for .dx/version, .devcontainer/devcontainer.json, .gitignore, .bazelignore, .envrc, .vscode/settings.json, .vscode/extensions.json, .github/workflows/ci.yml, MODULE.bazel.snippet, tools/dx and tools/dx.cmd. It does not inspect .claude/settings.json or AGENTS.md. It also flags a dx-written launcher whose version differs from .dx/version, and an empty or non-semver .dx/version. When all of them exist, no launcher is stale and the pin is valid, it prints init current: <module> to stdout. Otherwise it prints dx: init_failed: init drift: missing <paths>; stale <paths>; invalid .dx/version ("<pin>") to stderr (only the parts that apply), followed by the dx init <module> --apply command to run. An invalid pin is not fixed by apply: write the module version to .dx/version, which the message names. Apply prints wrote <path> to stdout for each file it creates or refreshes, and wrote AGENTS.md when the block changes. For each scaffold or launcher file that already exists and is kept, it prints dx: <path> (absent-only, left untouched) to stderr. --quiet hides the stdout lines. The stderr lines still print.

Exit codes: 0 check passed or apply succeeded, 2 usage error, including extra positionals or an invalid module name, 1 check found missing files or apply failed.

Agent commands block

dx init --apply writes this block between <!-- dx:begin --> and <!-- dx:end --> markers in AGENTS.md:

<!-- dx:begin -->
- Start: `tools/dx worktree add <branch>`. Finish: `tools/dx worktree release`.
- Fix first: `tools/dx fix --apply --affected`.
- Then verify: `tools/dx test --affected`.
- Read the `--output=json` result.
<!-- dx:end -->

An existing file gets the block appended. A block that is out of date is replaced. Text outside the markers is never touched, and CRLF files keep CRLF. A begin marker without an end marker is an error and the file is left as is. dx status reports the agents check as warn when the block is missing or out of date, or a hook is missing from .claude/settings.json.

Launcher

tools/dx (sh) and tools/dx.cmd run dx without Bazel. They read .dx/version, download the matching standalone binary and its .sha256 file from the GitHub release into ~/.cache/dx/bin/<version>/ (%USERPROFILE%\.cache\dx\bin\<version>\ on Windows), check the binary against that sha256, and run it. A cached binary is only run. No Bazel server starts.

tools/dx version
tools/dx check //...

Binaries exist for Linux x86_64 and arm64, macOS arm64, and Windows x86_64 and arm64. The download launcher exits 1 and prints the bazel run @rules_dx//:dx -- ... command to use instead when the download fails or the platform has no binary. A binary that does not match its sha256 is deleted and never run.

When .dx/version differs from the launcher's version, the launcher exits 1 and prints:

dx: tools/dx is pinned to dx <v> but .dx/version is '<pin>'; refresh it: bazel run @rules_dx//:dx -- init --apply

dx init --apply writes a missing launcher and rewrites one whose VERSION line differs from .dx/version. With no .dx/version it uses the running dx version. dx treats a launcher as its own when it starts with the template's first three lines (#!/bin/sh, set -eu, VERSION="<v>", or @echo off, setlocal, set "VERSION=<v>"). Any other launcher is yours and is never touched. A dx launcher is rewritten when stale, so hand edits to it are lost. When a launcher would change and .dx/version is not semver, init fails with init_failed before writing any file. dx rollback --apply refreshes existing launchers the same way.

Git hooks

dx does not install or manage Git hooks. For a commit or push hook, write a plain hook or use a hook tool, and have it run:

bazel run @rules_dx//:dx -- check //...

Enforce the same check in CI. See GitHub CI.