Vx Best Practices

MCP Tools

Best practices for using vx effectively. Use when following recommended patterns for tool management, project setup, and team workflows with vx.

Install

openclaw skills install vx-best-practices

VX Best Practices

Golden rule: Always prefix tool commands with vx in vx-managed projects. Use vx.toml for project-level tool versions, commit vx.lock for reproducibility, prefer templates over custom code when creating providers, and prefer structured or compact output before reading large logs.

General Principles

1. Always Use vx Prefix

In vx-managed projects, always prefix tool commands with vx:

# ✅ Correct
vx npm install
vx cargo build
vx just test

# ❌ Wrong (might use system tools)
npm install
cargo build
just test

2. Prefer Project-Level Configuration

Use vx.toml to ensure consistency across team members:

# ✅ Correct - defined in vx.toml
vx sync

# ❌ Wrong - manual installation
vx install node@22

3. Commit Lock Files

Always commit vx.lock to ensure reproducible builds:

vx git add vx.lock
vx git commit -m "chore: update dependencies"

4. Keep Agent Work Small and Observable

When an AI agent uses vx, optimize for correctness, speed, judgment, and token efficiency:

  • Read enough surrounding code to understand the local pattern, then stop exploring.
  • Prefer targeted searches, focused file sections, scoped diffs, selected JSON fields, and capped logs.
  • Make the smallest maintainable change that solves the request.
  • Reuse existing project helpers before creating new abstractions.
  • Avoid single-use wrappers, speculative architecture, and unrelated cleanup.
  • Validate according to risk: focused tests for narrow changes, broader checks for shared behavior.
  • Preserve evidence from the actual command, CI job, or runtime surface when debugging.

For large or unknown output, scope first and filter through vx-managed tools:

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:

  1. Ask for semantic data: --json, selected fields, --jq, --fields, --toon.
  2. Search the raw source with caps: vx rg -n -m 80 ..., vx jq, vx yq.
  3. Use vx --compact <tool> ... for broad subprocess output that still needs context.
  4. Read full raw output only after the smaller views fail to explain the issue.

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.

Project Setup

Initial Setup

# 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

Team Onboarding

New team members only need to run:

# Clone repository
git clone <repo>
cd <repo>

# One command setup
vx setup

CI/CD Configuration

Use the vx GitHub Action:

# .github/workflows/ci.yml
- uses: loonghao/vx@main
  with:
    setup: 'true'
    cache: 'true'

Version Management

Version Selection Strategy

ScenarioConstraintExample
DevelopmentMajor versionnode = "22"
CI/CDExact versionnode = "22.0.0"
LibraryRangenode = ">=18 <23"
Latest features"latest"uv = "latest"

Avoid Over-Specification

# ✅ 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

LTS for Production

# Use LTS versions for stability
[tools]
node = "lts"        # Auto-updates to latest LTS

Scripts Organization

Naming Conventions

[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"

Script Dependencies

# Scripts can depend on specific tools
[scripts]
lint = "eslint . && cargo clippy"  # Uses node and rust
build = "go build ./..."           # Uses go

Environment Variables

Project-Level Defaults

[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" }

Secrets Management

Never commit secrets. Use environment variables:

# .env (add to .gitignore)
DATABASE_URL=postgresql://...
API_KEY=secret123

# Reference in vx.toml
[env]
DATABASE_URL = { env = "DATABASE_URL" }

Performance Optimization

Cache Configuration

[cache]
# Enable aggressive caching
enabled = true
ttl = 86400  # 24 hours

# Cache location
dir = "~/.vx/cache"

Pre-install Common Tools

# In project setup, pre-install tools
vx sync --parallel

Use Offline Mode

# When network is unreliable
vx sync --offline

Cross-Platform Considerations

Platform-Specific Tools

[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"]

Cross-Platform Scripts

# 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

Security Best Practices

Verify Checksums

vx automatically verifies checksums. For additional security:

# Verify installation
vx install node@22 --verify

Minimal Permissions

# Install as regular user, not root
# ❌ sudo vx install node

# vx manages user-level installations
vx install node

Audit Dependencies

# Audit installed tools
vx audit

# Check for vulnerabilities
vx npm audit

Team Workflows

Adding New Tools

# 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"

Updating Tools

# 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

Removing Tools

# 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

Monitoring & Maintenance

Regular Checks

# Weekly: Check for updates
vx outdated

# Monthly: Clean cache
vx cache clean

# After issues: Run diagnostics
vx doctor

Health Check Script

[scripts]
doctor = "vx doctor"
check = "vx check"
audit = "vx npm audit && cargo audit"

Anti-Patterns to Avoid

1. Manual Tool Installation

# ❌ Don't manually install tools in vx projects
npm install -g node  # Conflicts with vx

# ✅ Let vx manage tools
vx npm install

2. Ignoring Lock Files

# ❌ Don't ignore vx.lock
git rm --cached vx.lock

# ✅ Always commit lock files
git add vx.lock

3. Global vx.toml

# ❌ Don't rely on global configuration
~/.vx/vx.toml

# ✅ Use project-level configuration
./vx.toml

4. Hardcoded Paths

# ❌ Don't hardcode absolute paths
[env]
TOOL_PATH = "/Users/alice/.vx/tools/node"

# ✅ Use vx variables
[env]
TOOL_PATH = "${VX_ROOT}/tools/node"

Migration Guide

From nvm/fnm → vx

# 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

From pyenv → vx

# 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

Provider Development Best Practices

Choose the Right Template

Decision tree for new providers:

  • Tool releases on GitHub with Rust target triple? → github_rust_provider (most common)
  • Tool releases on GitHub with Go goreleaser? → github_go_provider
  • Single binary download (no archive)? → github_binary_provider
  • System package manager only? → system_provider
  • Custom download source? → Hand-write download_url function

For most tools, use a template instead of writing custom functions:

# ✅ 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...

Understanding the ctx Object

All provider.star functions receive a ctx object:

FieldExample valueDescription
ctx.platform.os"windows", "macos", "linux"Current OS
ctx.platform.arch"x64", "arm64"CPU architecture
ctx.install_dir~/.vx/store/node/22.0.0Install path
ctx.store_dir~/.vx/storeGlobal store root
ctx.cache_dir~/.vx/cacheCache directory

Provider Naming

# ✅ 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

Platform Constraints

Return None from download_url for unsupported platforms:

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)

Bundled Runtimes

Use bundled_runtime_def for tools shipped inside another:

runtimes = [
    runtime_def("node"),                     # Primary runtime
    bundled_runtime_def("npm", "node"),       # npm comes with node
    bundled_runtime_def("npx", "node"),       # npx comes with node
]

vx Development Best Practices

Quick Development Cycle

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

Code Organization Rules

  1. Layer dependencies go downward only — Never import from higher layers
  2. Tests in tests/ directories — Never inline #[cfg(test)]
  3. Use rstest for parameterized tests
  4. Use tracing for logging, never println! or eprintln!
  5. Use correct terminology — Runtime, Provider, provider.star

AI Agent Documentation Ecosystem

vx maintains a comprehensive set of AI agent configuration files for 15+ agents:

FilePurposeAudience
AGENTS.mdPrimary AI agent entry point — rules, architecture, quick referenceAll AI coding agents (official standard)
CLAUDE.mdClaude Code specific instructions with @-import supportClaude Code
llms.txtConcise LLM-friendly project index (llmstxt.org protocol)LLMs discovering the project
llms-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 rulesTrae
skills/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.