Golden rule: Always prefix tool commands with
vxin vx-managed projects. Usevx.tomlfor project-level tool versions, commitvx.lockfor reproducibility, prefer templates over custom code when creating providers, and prefer structured or compact output before reading large logs.
vx Prefix#In vx-managed projects, always prefix tool commands with vx:
bash
# ✅ Correct
vx npm install
vx cargo build
vx just test
# ❌ Wrong (might use system tools)
npm install
cargo build
just test
Use vx.toml to ensure consistency across team members:
bash
# ✅ Correct - defined in vx.toml
vx sync
# ❌ Wrong - manual installation
vx install node@22
Always commit vx.lock to ensure reproducible builds:
bash
vx git add vx.lock
vx git commit -m "chore: update dependencies"
When an AI agent uses vx, optimize for correctness, speed, judgment, and token efficiency:
For large or unknown output, scope first and filter through vx-managed tools:
bash
vx rg -n -m 20 "SearchTerm" src
vx git diff --stat
vx git diff --name-only origin/main...HEAD
vx gh pr view 123 --json title,state,files
vx gh run view 456 --json status,conclusion,jobs --jq '.jobs[] | {name,conclusion}'
vx gh run view 456 --log | vx rg -n -m 50 "error|failed|panic|Traceback|FAILED"
vx --compact gh run view 456 --log
Use this priority order for token-heavy surfaces:
--json, selected fields, --jq, --fields, --toon.vx rg -n -m 80 ..., vx jq, vx yq.vx --compact <tool> ... for broad subprocess output that still needs context.The compact filter follows RTK-style principles: preserve high-signal lines,
strip presentation noise, collapse repetition, cap volume, and measure savings
with vx metrics tokens --json when comparing approaches. Do not make
transparent forwarding commands ( vx git, vx gh, vx cargo, vx npm) compact
by default; agents should opt in explicitly with --compact so scripts and
humans keep the native tool contract.
bash
# 1. Initialize project
vx init
# 2. Add required tools
vx add node@22
vx add go
vx add just
# 3. Generate lock file
vx lock
# 4. Commit configuration
vx git add vx.toml vx.lock
New team members only need to run:
bash
# Clone repository
git clone <repo>
cd <repo>
# One command setup
vx setup
Use the vx GitHub Action:
yaml
# .github/workflows/ci.yml
- uses: vx-org/vx@main
with:
setup: 'true'
cache: 'true'
ScenarioConstraintExampleDevelopmentMajor versionnode = "22"CI/CDExact versionnode = "22.0.0"LibraryRangenode = ">=18 <23"Latest features"latest"``uv = "latest"
toml
# ✅ Good - flexible for patch updates
[tools]
node = "22"
go = "1.22"
# ❌ Bad - too rigid for development
[tools]
node = "22.0.0" # Blocks security patches
go = "1.22.0" # Requires update for each patch
toml
# Use LTS versions for stability
[tools]
node = "lts" # Auto-updates to latest LTS
toml
[scripts]
# Standard scripts (run with: vx run <name>)
dev = "npm run dev"
test = "npm run test"
build = "npm run build"
lint = "npm run lint"
# Colon-separated variants (run with: vx run test:watch)
test:watch = "npm run test -- --watch"
test:coverage = "npm run test -- --coverage"
build:prod = "npm run build -- --mode production"
# CI-specific scripts
ci = "just ci"
ci:test = "cargo test --all-features"
toml
# Scripts can depend on specific tools
[scripts]
lint = "eslint . && cargo clippy" # Uses node and rust
build = "go build ./..." # Uses go
toml
[env]
NODE_ENV = "development"
DEBUG = "app:*"
# Required variables (vx will warn if missing)
API_KEY = { env = "API_KEY", required = true }
# Default values
PORT = { default = "3000" }
Never commit secrets. Use environment variables:
bash
# .env (add to .gitignore)
DATABASE_URL=postgresql://...
API_KEY=secret123
# Reference in vx.toml
[env]
DATABASE_URL = { env = "DATABASE_URL" }
toml
[cache]
# Enable aggressive caching
enabled = true
ttl = 86400 # 24 hours
# Cache location
dir = "~/.vx/cache"
bash
# In project setup, pre-install tools
vx sync --parallel
bash
# When network is unreliable
vx sync --offline
toml
[tools]
# Cross-platform tools first
node = "22"
uv = "latest"
# Platform-specific
[tools.msvc]
version = "14.42"
os = ["windows"]
[tools.brew]
version = "latest"
os = ["macos", "linux"]
bash
# Use tools that work everywhere
[scripts]
build = "just build" # just is cross-platform
test = "cargo test" # cargo is cross-platform
# Avoid platform-specific commands
# ❌ build = "make build" # Unix only
vx automatically verifies checksums. For additional security:
bash
# Verify installation
vx install node@22 --verify
bash
# Install as regular user, not root
# ❌ sudo vx install node
# vx manages user-level installations
vx install node
bash
# Audit installed tools
vx audit
# Check for vulnerabilities
vx npm audit
bash
# 1. Add to vx.toml
vx add python@3.12
# 2. Update lock file
vx lock
# 3. Commit changes
git add vx.toml vx.lock
git commit -m "feat: add python 3.12"
bash
# 1. Check for updates
vx outdated
# 2. Update specific tool
vx update node
# 3. Update all tools
vx update --all
# 4. Commit lock file changes
git add vx.lock
bash
# 1. Remove from vx.toml
vx remove tool-name
# 2. Clean up installation
vx sync --clean
# 3. Commit changes
git add vx.toml vx.lock
bash
# Weekly: Check for updates
vx outdated
# Monthly: Clean cache
vx cache clean
# After issues: Run diagnostics
vx doctor
toml
[scripts]
doctor = "vx doctor"
check = "vx check"
audit = "vx npm audit && cargo audit"
bash
# ❌ Don't manually install tools in vx projects
npm install -g node # Conflicts with vx
# ✅ Let vx manage tools
vx npm install
bash
# ❌ Don't ignore vx.lock
git rm --cached vx.lock
# ✅ Always commit lock files
git add vx.lock
bash
# ❌ Don't rely on global configuration
~/.vx/vx.toml
# ✅ Use project-level configuration
./vx.toml
toml
# ❌ Don't hardcode absolute paths
[env]
TOOL_PATH = "/Users/alice/.vx/tools/node"
# ✅ Use vx variables
[env]
TOOL_PATH = "${VX_ROOT}/tools/node"
bash
# 1. Export current versions
node --version > .nvmrc
# 2. Create vx.toml
vx init
# 3. Add node version
vx add node@$(cat .nvmrc | tr -d 'v')
# 4. Remove nvm
rm -rf ~/.nvm
bash
# 1. Check current Python version
python --version
# 2. Create vx.toml
vx init
# 3. Add uv (recommended Python manager)
vx add uv
# 4. Remove pyenv
rm -rf ~/.pyenv
Decision tree for new providers:
github_rust_provider (most common)github_go_providergithub_binary_providersystem_providerdownload_url functionFor most tools, use a template instead of writing custom functions:
starlark
# ✅ Good — Use template (10 lines)
_p = github_rust_provider("owner", "tool",
asset = "tool-{vversion}-{triple}.{ext}")
# ❌ Avoid — Custom download_url when template works
def download_url(ctx, version):
# 30+ lines of custom code...
All provider.star functions receive a ctx object:
FieldExample valueDescriptionctx.platform.os``"windows", "macos", "linux"Current OSctx.platform.arch``"x64", "arm64"CPU architecturectx.install_dir``~/.vx/store/node/22.0.0Install pathctx.store_dir``~/.vx/storeGlobal store rootctx.cache_dir``~/.vx/cacheCache directory
starlark
# ✅ Correct terminology
name = "ripgrep" # Provider name
runtimes = [runtime_def("rg")] # Runtime name (what user types)
# Common pattern: provider name = project name, runtime name = binary name
# ripgrep provider → rg runtime
# rust provider → cargo, rustc, rustup runtimes
Return None from download_url for unsupported platforms:
starlark
def download_url(ctx, version):
platform = platform_map(ctx, _PLATFORMS)
if not platform:
return None # Not supported on this platform
return "https://example.com/v{}/tool-{}.tar.gz".format(version, platform)
Use bundled_runtime_def for tools shipped inside another:
starlark
runtimes = [
runtime_def("node"), # Primary runtime
bundled_runtime_def("npm", "node"), # npm comes with node
bundled_runtime_def("npx", "node"), # npx comes with node
]
bash
vx just quick # format → lint → test → build
vx cargo check -p vx-cli # Fast type-checking for one crate
vx cargo test -p vx-starlark # Test one crate
tests/ directories — Never inline #[cfg(test)]rstest for parameterized teststracing for logging, never println! or eprintln!vx maintains a comprehensive set of AI agent configuration files for 15+ agents:
FilePurposeAudienceAGENTS.mdPrimary AI agent entry point — rules, architecture, quick referenceAll AI coding agents (official standard)CLAUDE.mdClaude Code specific instructions with @-import supportClaude Codellms.txtConcise LLM-friendly project index (llmstxt.org protocol)LLMs discovering the projectllms-full.txtDetailed LLM documentation with full examplesLLMs needing deep context.github/copilot-instructions.mdGitHub Copilot-specific instructionsGitHub Copilot.cursor/rules/*.mdcModern Cursor IDE rules with YAML frontmatter (4 files)Cursor AI (new format).cursorrulesCursor IDE agent rules (legacy format, still supported)Cursor AI (legacy).clinerulesCline/Roo agent rulesCline.windsurfrulesWindsurf AI IDE rulesWindsurf.kiro/steering/*.mdKiro AI IDE steering documentsKiro.trae/rules/*.mdTrae AI IDE project rulesTraeskills/Distributable skill files for 15+ AI agentsClawHub, vx ai setup
Best practice: When making changes that affect AI agent behavior (terminology, architecture, commands), update AGENTS.md first — it is the single source of truth. Other files derive from it.
GitHub PRs, CI runs, and dashboards are supporting evidence, not the delivery surface. The conclusion has to land where the work is tracked.
Concretely: the delivery surface is the team's issue or task tracker — the issue comment plus its metadata. A PR comment alone delivers nothing.