CLI Reference
This reference covers all Turnkey CLI commands.
tk - Buck2 Wrapper
tk is the primary Turnkey CLI. It wraps Buck2 with automatic dependency synchronization.
Overview
When using Buck2 with Nix-managed dependencies, certain files must be regenerated when source files change. tk solves this by automatically running sync operations before buck2 commands that read the build graph.
Quick Start
# Use tk just like buck2 - it syncs automatically
tk build //some:target # syncs first, then builds
tk test //some:target # syncs first, then tests
tk run //some:target # syncs first, then runs
# Explicit sync operations
tk sync # manually sync all stale files
tk check # check if files are stale (for CI)
# Skip sync when needed
tk --no-sync build //... # skip sync, run buck2 directly
Command Reference
tk build/run/test/... (Buck2 passthrough)
Most tk commands are passed through to Buck2. Commands that read the build graph automatically sync first:
Sync-first commands (sync before running):
build- Build targetsrun- Run a targettest- Run testsquery- Query the build graphcquery- Configured queryuquery- Unconfigured querytargets- List targetsaudit- Audit the buildbxl- Run BXL scripts
Pass-through commands (no sync):
clean- Clean build artifactskill- Kill Buck2 daemonkillall- Kill all Buck2 processesstatus- Show daemon statuslog- View build logsrage- Generate debug reporthelp- Show helpdocs- Open documentationinit- Initialize a project
Unknown commands default to syncing first (safe default).
tk sync
Explicitly synchronize all stale files, or only those of the named deps
rules (go, rust, pylock, python, javascript, solidity; the
rules are in .turnkey/sync.toml). Rules always run in sync.toml order,
so pylock runs before python, which reads what it writes.
tk sync # sync stale files
tk sync go # sync only go-deps.toml
tk sync --verbose # show what's being synced
tk sync --dry-run # show what would be synced without doing it
Exit codes:
0- Success (files synced or nothing to sync)1- Sync failed
tk check
Check if any files are stale without regenerating them. Useful for CI validation.
tk check # check staleness
tk check rust # check only rust-deps.toml
tk check --verbose # show detailed status
Exit codes:
0- All files up-to-date1- Files are stale (runtk syncto fix)
Example CI usage:
- name: Check files in sync
run: tk check
tk completion
Generate shell completion scripts.
tk completion bash # output bash completion script
tk completion zsh # output zsh completion script
tk completion fish # output fish completion script
Enable completions:
# Bash (add to ~/.bashrc)
eval "$(tk completion bash)"
# Zsh (add to ~/.zshrc)
eval "$(tk completion zsh)"
# Fish (run once)
tk completion fish > ~/.config/fish/completions/tk.fish
tk materialize
Bring deps cells in line with their cell indexes. The shell runs it on every load (direnv's use_turnkey and enterShell), so you rarely need to.
tk materialize /nix/store/…-rustdeps-index.json
It adds missing store links, rewrites alias packages whose target changed, removes what the index no longer names, roots the index under .turnkey/gcroots/, and records the deps file's hash. It fails, and changes nothing, if a store link points anywhere but its own name. A run with nothing to do touches no file.
tk rules
Manage rules.star files that define Buck2 build targets from source files. This command automatically detects imports from source files and updates the deps list in rules.star.
tk rules check # Check every rules.star file against its sources
tk rules sync # Update rules.star files with detected dependencies
tk rules help # Show help
Options:
| Flag | Description |
|---|---|
--all, -a | sync: process all files (skip staleness detection) |
--force, -f | Same as --all |
--verbose, -v | Show detailed output including skipped files |
--quiet, -q | Suppress output |
--dry-run, -n | Show what would be changed without writing |
Staleness Detection:
check always checks every rules.star, so it also catches a stale file that is already committed. sync by default only processes directories with uncommitted changes whose source files are newer than rules.star. Use --all or --force to sync all files.
Examples:
tk rules check # Check all rules.star files
tk rules check src/cmd/tk # Check one directory
tk rules sync # Update stale rules.star files
tk rules sync --all # Force update all files
tk rules sync src/cmd/tk # Sync specific directory
tk rules sync --dry-run # Preview changes without writing
Preserving Manual Dependencies:
If you have manual dependencies that shouldn't be auto-detected, use preservation markers in your rules.star:
# turnkey:preserve-start
"//some/manual:dep",
# turnkey:preserve-end
Dependencies within these markers are preserved during sync.
How tk runs it: tk rules, and the rules sync tk runs before a Buck2
command, call the rules-syncer library (src/rust/rules-syncer) linked
into tk, and print what it reports as above.
tk Flags
Flags must come before the subcommand:
| Flag | Description |
|---|---|
--no-sync | Skip sync and the cell-freshness check, run Buck2 directly |
--no-local | Skip local target overrides from .turnkey/local.toml |
--verbose, -v | Show what tk is doing |
--dry-run, -n | Show what would be synced without doing it |
--quiet, -q | Suppress non-error output |
--help, -h | Show help |
Examples:
tk --no-sync build //... # skip sync
tk --no-local run //target # skip local overrides
tk --verbose sync # verbose sync
tk -v -n sync # dry-run with verbose output
Configuration
Sync Configuration File
tk reads staleness rules from .turnkey/sync.toml. This file is automatically generated from your Nix configuration.
When you configure dependency files in your flake.nix:
goDepsFilegenerates a Go deps rulerustDepsFilegenerates a Rust deps rulepythonDepsFilegenerates a Python deps rule
Example generated sync.toml:
[[deps]]
name = "go"
sources = ["go.mod", "go.sum"]
target = "go-deps.toml"
generator = ["godeps-gen", "--go-mod", "go.mod", "--go-sum", "go.sum"]
[[deps]]
name = "rust"
sources = ["Cargo.toml", "Cargo.lock"]
target_sources = "manifests"
target = "rust-deps.toml"
generator = ["rustdeps-gen", "--cargo-lock", "Cargo.lock", "--platform", "linux-x86_64=x86_64-unknown-linux-gnu", "--platform", "macos-arm64=aarch64-apple-darwin"]
Each [[deps]] entry defines:
name- Human-readable name for this rulesources- Files that trigger regeneration when modifiedtarget- The generated filegenerator- Command to regenerate the targettarget_sources(optional) - A top-level key of the target whose array lists more sources, relative to the project root. They are files only the generator can find, such as a Cargo workspace's member manifests. A target without that key is stale.
Local Target Overrides
tk supports per-developer local overrides via .turnkey/local.toml. This file is not committed to git, allowing each developer to customize target arguments for their local environment.
Use cases:
- Different network addresses for local development
- Debug flags for specific targets
- Custom ports or configuration
Example .turnkey/local.toml:
# Override args for tk run
[run."//docs/user-manual"]
args = ["-n", "192.168.1.100"]
# Override args for tk build
[build."//src/cmd/server:server"]
args = ["--config=debug"]
# Pattern matching with "..."
[test."//src/..."]
args = ["--verbose", "--timeout=60s"]
How it works:
When you run a command that matches a configured target:
tk run //docs/user-manual
# Becomes: buck2 run //docs/user-manual -- -n 192.168.1.100
The args are injected after --, which passes them to the target binary.
Pattern matching:
Patterns ending with ... match any target with that prefix:
//src/...matches//src:foo,//src/pkg:bar,//src/cmd/tool:main//...matches any target
Disable for a single command:
tk --no-local run //docs/user-manual # skips local.toml
Verbose output:
tk --verbose run //docs/user-manual
# Output: tk: applying local override for run //docs/user-manual: [-n 192.168.1.100]
Shell Integration
buck2 alias to tk:
# In devenv shell, buck2 is aliased to tk
buck2 build //... # actually runs: tk build //...
Disable with:
TURNKEY_NO_ALIAS=1 buck2 build //... # uses raw buck2
tw - Native Tool Wrapper
tw wraps native language tools (go, cargo, uv) to keep dependency files in sync when using standard workflows.
The Problem
When you run go get github.com/foo/bar, Go updates go.mod and go.sum. But Buck2 needs go-deps.toml to know about the new dependency. Without auto-sync, you'd need to manually regenerate it.
The Solution
Turnkey transparently wraps go, cargo, and uv so that dependency sync happens automatically:
go get github.com/foo/bar # Just works - go-deps.toml is auto-updated
How It Works
User runs: go get github.com/foo/bar
│
▼
Shell wrapper (provides 'go' binary)
• Sets TURNKEY_REAL_GO to actual go binary path
• Calls: tw go get github.com/foo/bar
│
▼
tw (turnkey wrapper)
1. Loads .turnkey/sync.toml configuration
2. Finds the [[wrappers]] rule for 'go' (none: runs go untouched)
3. Checks if 'get' is a mutating subcommand → yes
4. Captures SHA256 hashes of go.mod, go.sum
5. Runs the real 'go get' command
6. Compares hashes - detects changes
7. Runs the rule's post-commands (go mod tidy)
8. Runs the go deps rule (godeps-gen → go-deps.toml), then any other
rule left stale
Supported Tools
turnkey writes one [[wrappers]] rule per language into
.turnkey/sync.toml, from its language records
(nix/buck2/languages.nix), for each enabled language that has deps rules:
| Tool | Mutating Commands | Watch Files | Deps Rule |
|---|---|---|---|
go | get, mod | go.mod, go.sum | go (go-deps.toml) |
cargo | add, remove, update | Cargo.toml, Cargo.lock | rust (rust-deps.toml) |
uv | add, remove, lock, sync | pyproject.toml, uv.lock | pylock (pylock.toml), then python (python-deps.toml) |
The file names are the configured ones (buck2.go.modFile and so on).
Without buck2.python.uvLockFile, uv watches only pyproject.toml and
runs the python rule.
Escape Hatches
Bypass for a Single Command
TURNKEY_NO_WRAP=1 go get github.com/foo/bar
This runs the real go directly, skipping tw entirely.
Disable Sync for a Command
tw --no-sync go get github.com/foo/bar
This runs through tw but skips the sync step even if files change.
Verbose Output
tw -v go get github.com/foo/bar
Shows what tw is doing:
tw: capturing state of [go.mod go.sum]
tw: detected changes in [go.mod go.sum], running sync
Syncing go-deps.toml...
Running: godeps-gen --go-mod go.mod --go-sum go.sum
Regenerated go-deps.toml
Non-Mutating Commands
Commands not in mutating_subcommands pass through without any overhead:
go build ./... # No snapshot, no sync check - just runs go build
go version # Direct passthrough
Deps generators
tk sync runs a deps generator for each enabled language (the [[deps]]
rules of .turnkey/sync.toml), so you rarely run one yourself. When you do,
every generator takes the same options:
| Option | Description |
|---|---|
-o, --output PATH | Output file (default: stdout) |
--no-prefetch | Skip prefetching the Nix hashes the deps cell fetches with (the file gets placeholder or missing hashes) |
--no-cache | Always fetch from the network, bypassing turnkey's prefetch cache |
Prefetching is on by default. jsdeps-gen takes only --output: the pnpm
lock's integrity hashes are the ones Nix fetches with.
godeps-gen
Generate go-deps.toml from go.mod and go.sum.
Usage
godeps-gen [OPTIONS]
Options
| Option | Description |
|---|---|
--go-mod PATH | Path to go.mod file (default: go.mod) |
--go-sum PATH | Path to go.sum file (default: go.sum) |
--indirect | Include indirect dependencies (default: true) |
Prefetching hashes the modules' proxy.golang.org zips, the source the godeps
cell fetches, in one nix-prefetch-cached --batch call: turnkey's prefetch
cache answers the modules it has seen, and the rest are fetched in parallel.
Examples
# Generate with prefetched hashes
godeps-gen -o go-deps.toml
# Use custom paths
godeps-gen --go-mod src/go.mod --go-sum src/go.sum -o go-deps.toml
# Quick check without fetching (placeholder hashes)
godeps-gen --no-prefetch
rustdeps-gen
Generate rust-deps.toml from Cargo.lock.
Usage
rustdeps-gen [OPTIONS]
Options
| Option | Description |
|---|---|
--cargo-lock PATH | Path to Cargo.lock file (default: Cargo.lock) |
--cargo-toml PATH | Path to the workspace's root Cargo.toml (default: next to Cargo.lock) |
Examples
# Generate from default Cargo.lock
rustdeps-gen -o rust-deps.toml
# Use custom path
rustdeps-gen --cargo-lock rust/Cargo.lock -o rust-deps.toml
pydeps-gen
Generate python-deps.toml from Python dependency files. Each distribution
is recorded as its pure (py3-none-any) wheel, hashed unpacked; a
distribution with only platform-specific wheels, or only an sdist, fails
with its name (see Python).
Usage
pydeps-gen [OPTIONS]
Options
| Option | Description |
|---|---|
--lock PATH | Path to pylock.toml (PEP 751 lock file) - RECOMMENDED |
--pyproject PATH | Path to pyproject.toml |
--requirements PATH | Path to requirements.txt |
--uv-lock PATH | uv.lock, whose dependency graph is recorded with the --lock packages |
--include-dev | Include dev dependencies |
Input Formats
| Format | Flag | Reproducibility | Notes |
|---|---|---|---|
| pylock.toml (PEP 751) | --lock | Best | Exact versions and URLs |
| pyproject.toml | --pyproject | Varies | Uses latest matching versions |
| requirements.txt | --requirements | Varies | Pin versions with == |
Recommended Workflow
# 1. Generate lock file from pyproject.toml
uv lock
# 2. Export to PEP 751 format
uv export --format pylock.toml -o pylock.toml
# 3. Generate python-deps.toml with Nix hashes
pydeps-gen --lock pylock.toml -o python-deps.toml
Examples
# From PEP 751 lock file (best for reproducibility)
pydeps-gen --lock pylock.toml -o python-deps.toml
# From pyproject.toml (resolves to latest matching versions)
pydeps-gen --pyproject pyproject.toml -o python-deps.toml
# From requirements.txt
pydeps-gen --requirements requirements.txt -o python-deps.toml
# Include dev dependencies
pydeps-gen --lock pylock.toml --include-dev -o python-deps.toml
Troubleshooting
"tk: .buckconfig not found"
tk looks for .buckconfig to find the project root. Make sure you're in a Buck2 project directory.
"tk: failed to load sync config"
The .turnkey/sync.toml file is missing or invalid. Ensure you're in a Turnkey project with proper configuration.
"tk: sync failed: generator command failed"
The generator command failed. Check that:
- The generator command is correct
- Required tools are in PATH
- Source files exist
Sync is slow
If sync takes a long time:
- Prefetched hashes are cached (
prefetch-cache.jsonunder$TURNKEY_CACHE_DIR, or elseturnkey/in the platform cache directory, e.g.~/.cache/turnkey/on Linux); check a generator isn't running with--no-cache - Check if generators are doing unnecessary work
Bypass tk
If you need to use raw Buck2:
# Option 1: --no-sync flag
tk --no-sync build //...
# Option 2: TURNKEY_NO_ALIAS environment variable
TURNKEY_NO_ALIAS=1 buck2 build //...