Implicit Package Execution
Execute globally installed packages or run packages on-demand without explicit installation, similar to npx and uvx but with cross-language support.
Overview
The implicit package execution feature allows you to run packages directly using a unified syntax. Unlike npx (Node.js only) or uvx (Python only), vx supports multiple ecosystems with a consistent interface.
Key Benefits:
- 🚀 One-Command Execution: Run packages without prior installation
- 🌍 Cross-Language: Works with npm, pip, cargo, go, and more
- 📦 Auto-Install: Automatically installs packages on first run
- 🔒 Isolated: Each package is installed in its own isolated environment
- 🎯 Version Control: Specify exact versions for reproducibility
Syntax
vx <ecosystem>[@runtime_version]:<package>[@version][::executable] [args...]Syntax Components
| Component | Description | Example |
|---|---|---|
ecosystem | Package ecosystem (npm, pip, cargo, go, etc.) | npm, pip |
@runtime_version | (Optional) Runtime version to use | @20, @3.11 |
package | Package name | typescript, ruff |
@version | (Optional) Package version | @5.3, @0.3 |
::executable | (Optional) Executable name if different from package | ::tsc, ::rg |
Basic Usage
Running Installed Tools
Once a package is installed via vx global install, you can run it directly:
# Run installed tool by executable name
vx tsc --version
vx black --check .
vx rg "pattern" ./srcExplicit Package Syntax
Use the full syntax when the package name differs from the executable:
# Package name ≠ executable name
vx npm:typescript::tsc --version # typescript package, tsc executable
vx pip:httpie::http GET example.com # httpie package, http executable
vx cargo:ripgrep::rg "pattern" # ripgrep package, rg executableAuto-Detection and Installation
If a package is not installed, vx automatically downloads and installs it:
# First run - automatically installs typescript
vx npm:typescript --version
# First run - automatically installs ruff
vx pip:ruff check .
# The package is cached for subsequent runsSupported Ecosystems
| Ecosystem | Aliases | Runtime | Description | Example |
|---|---|---|---|---|
npm | node, npx | Node.js | npm packages | npm:typescript |
bun | bunx | Bun | Bun packages | bun:typescript |
yarn | - | Node.js | Yarn packages | yarn:typescript |
pnpm | - | Node.js | pnpm packages | pnpm:typescript |
dlx | - | Node.js | pnpm dlx oneshot runner (like npx) | dlx:create-react-app |
pip | python, pypi | Python | pip packages | pip:black |
uv | - | Python (via uv) | uv packages | uv:ruff |
uvx | - | Python (via uvx) | uvx oneshot runner | uvx:ruff |
pipx | - | Python (via pipx) | pipx oneshot runner | pipx:cowsay |
deno | - | Deno | npm/JSR packages via deno run | deno:cowsay |
dotnet-tool | dotnet | .NET | .NET tools via dotnet tool install | dotnet-tool:dotnet-script |
jbang | java | Java | Java tools via jbang | jbang:picocli |
cargo | rust, crates | Rust | Rust crates | cargo:ripgrep |
go | golang | Go | Go packages | go:golangci-lint |
gem | ruby, rubygems | Ruby | Ruby gems | gem:rails |
choco | chocolatey | Windows | Chocolatey packages | choco:git |
Common Use Cases
TypeScript/Node.js
# Compile TypeScript (auto-installs if needed)
vx npm:typescript::tsc --version
# Run ESLint
vx npm:eslint .
# Create React App with specific Node version
vx npm@18:create-react-app my-app
# Run scoped package (@biomejs/biome)
vx npm:@biomejs/biome::biome check .
# Run TypeScript with specific version
vx npm:typescript@5.3::tsc --versionPython
# Format code with black
vx pip:black .
# Lint with ruff (specific version)
vx pip:ruff@0.3 check .
# Run pytest
vx pip:pytest -v
# Use specific Python version
vx pip@3.11:black .
# Using uv (faster)
vx uv:ruff check .
# Using uvx (isolated, ephemeral)
vx uvx:ruff check .
vx uvx:black .
vx uvx:pyinstaller::pyinstaller --version
# Using pipx (isolated, ephemeral)
vx pipx:cowsay Hello World
vx pipx:httpie::http GET example.com
# HTTP client
vx pip:httpie::http GET example.comDeno
# Run npm package via deno
vx deno:cowsay Hello
# Run JSR package via deno
vx deno:@std/cli --help
# Run with specific deno version
vx deno@2:cowsay Hello.NET Tools
# Run dotnet-script
vx dotnet-tool:dotnet-script script.csx
# Run dotnet-format
vx dotnet-tool:dotnet-format --verify-no-changes
# Run dotnet-ef (Entity Framework)
vx dotnet-tool:dotnet-ef migrations list
# Using alias
vx dotnet:dotnet-script script.csxJava (JBang)
# Run a JBang tool
vx jbang:picocli --help
# Run with GAV coordinate
vx jbang:info.picocli:picocli-codegen --help
# Using alias
vx java:picocli --helppnpm dlx
# Run via pnpm dlx (like npx but for pnpm)
vx dlx:create-react-app my-app
vx dlx:vite --version
vx dlx:@biomejs/biome::biome check .Rust
# Search with ripgrep
vx cargo:ripgrep::rg "pattern" ./src
# Find files with fd
vx cargo:fd-find::fd ".rs$" .
# Syntax highlighting with bat
vx cargo:bat::bat src/main.rsGo
# Run linter
vx go:golangci-lint run
# Run language server
vx go:gopls versionThe :: Separator Explained
Many packages provide executables with different names than the package itself. The :: separator lets you specify the exact executable:
| Package | Executable | Full Command | Shorthand (if installed) |
|---|---|---|---|
typescript | tsc | vx npm:typescript::tsc | vx tsc |
typescript | tsserver | vx npm:typescript::tsserver | vx tsserver |
httpie | http | vx pip:httpie::http | vx http |
ripgrep | rg | vx cargo:ripgrep::rg | vx rg |
fd-find | fd | vx cargo:fd-find::fd | vx fd |
bat | bat | vx cargo:bat::bat | vx bat |
When to Use ::
Use :: when:
- Package name differs from executable name (e.g.,
typescript→tsc) - Package has multiple executables (e.g.,
typescripthastscandtsserver) - You want to be explicit about which executable to run
Skip :: when:
- Package name equals executable name (e.g.,
eslint,ruff) - Running via shorthand after installation
Version Specifications
Package Versions
# Latest version (default)
vx npm:typescript --version
# Specific version
vx npm:typescript@5.3 --version
# Version range
vx npm:typescript@^5.0 --versionRuntime Versions
# Use specific Node.js version
vx npm@20:typescript::tsc --version
vx npm@18:eslint .
# Use specific Python version
vx pip@3.11:black .
vx pip@3.12:ruff check .
# Use latest runtime (default)
vx npm:typescript --versionCombined Specification
# Full specification: ecosystem@runtime:package@version::executable
vx npm@20:typescript@5.3::tsc --version
# │ │ │ │ │ │
# │ │ │ │ │ └── executable
# │ │ │ │ └───── package version
# │ │ │ └───────── package name
# │ │ └──────────────────── runtime version
# │ └─────────────────────── runtime
# └──────────────────────────── ecosystemScoped npm Packages
For npm packages with scopes (@org/package):
# Biome (JavaScript toolchain)
vx npm:@biomejs/biome::biome check .
# OpenAI Codex
vx npm:@openai/codex::codex
# TypeScript Go implementation
vx npm:@aspect-build/tsgo::tsgo check .Comparison with Existing Tools
vx vs npx
| Scenario | npx | vx |
|---|---|---|
| Basic execution | npx eslint | vx npm:eslint or vx eslint (installed) |
| Different executable | npx -p typescript tsc | vx npm:typescript::tsc |
| Specific version | npx eslint@8 | vx npm:eslint@8 |
| Runtime version | ❌ Not supported | vx npm@20:eslint |
| Other ecosystems | ❌ Not supported | ✅ pip, cargo, go, etc. |
vx vs uvx
| Scenario | uvx | vx |
|---|---|---|
| Basic execution | uvx ruff | vx pip:ruff or vx ruff (installed) |
| Different executable | uvx --from httpie http | vx pip:httpie::http |
| Specific version | uvx ruff@0.3 | vx pip:ruff@0.3 |
| Runtime version | uvx --python 3.11 ruff | vx pip@3.11:ruff |
| Other ecosystems | ❌ Not supported | ✅ npm, cargo, go, etc. |
Project-Level Configuration
For projects, you can declare commonly used packages in vx.toml:
[tools.global]
typescript = "5.3"
eslint = "8"
black = "24.1"
ruff = "0.3"Then use them directly:
vx sync # Install all declared global tools
vx tsc --version # Uses project's typescript version
vx eslint .
vx black .Environment Variables
| Variable | Description |
|---|---|
VX_AUTO_INSTALL | Enable/disable auto-install (default: true) |
VX_GLOBAL_CACHE | Override global packages cache directory |
Troubleshooting
"Package not found"
# Ensure correct ecosystem
vx npm:my-package # For npm packages
vx pip:my-package # For Python packages
# Check if package exists
vx global list"Runtime not installed"
# Install required runtime first
vx install node # For npm packages
vx install python # For pip packages
vx install rustup # For cargo packages (managed by rustup)Command Conflicts
If a command conflicts with a runtime name:
# Use explicit syntax
vx npm:node # Run 'node' package, not node runtime
# Or use global command
vx global install npm:node
vx node # Now runs the packageBest Practices
1. Pin Versions for Reproducibility
# Good: Specific version
vx npm:typescript@5.3.3 --version
# Less predictable: Latest version
vx npm:typescript --version2. Use Explicit Syntax in Scripts
# In CI/CD or shared scripts, be explicit
vx npm:typescript@5.3::tsc --project tsconfig.json3. Prefer vx global install for Frequently Used Tools
# Install once, use many times
vx global install npm:typescript@5.3
# Then use shorthand
vx tsc --version4. Use vx dev for Project Isolation
# Enter project environment
vx dev
# All tools are available without prefix
tsc --version
black .
ruff check .Implementation Details
vx-shim Crate
The vx-shim crate implements the RFC 0027 parsing and execution logic:
// Parse RFC 0027 syntax
let request = PackageRequest::parse("npm@20:typescript@5.3::tsc")?;
// request.ecosystem = "npm"
// request.package = "typescript"
// request.version = Some("5.3")
// request.executable = Some("tsc")
// request.runtime_spec = Some(RuntimeSpec { runtime: "node", version: "20" })Architecture:
┌─────────────────────────────────────────────────────────────────────────┐
│ vx-shim Architecture │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ PackageRequest │ │
│ │ ├── parse(input: &str) -> Result<Self> │ │
│ │ ├── is_package_request(input: &str) -> bool │ │
│ │ └── executable_name() -> &str │ │
│ └─────────────────────────────────────────────────────────────────┘ │
│ │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ ShimExecutor │ │
│ │ ├── execute_request(req, args) -> Result<ExitCode> │ │
│ │ ├── find_package(ecosystem, package) -> Option<GlobalPackage> │ │
│ │ └── resolve_executable(package, exe_name) -> PathBuf │ │
│ └─────────────────────────────────────────────────────────────────┘ │
│ │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ Execution Flow │ │
│ │ 1. Parse request (ecosystem:package@version::executable) │ │
│ │ 2. Check if package is installed in PackageRegistry │ │
│ │ 3. If not installed: return PackageNotInstalled error │ │
│ │ 4. If installed: resolve executable path │ │
│ │ 5. Execute with runtime in PATH │ │
│ └─────────────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────────┘Auto-Install Mechanism
When a package is not found, the CLI triggers automatic installation:
// In vx-cli/src/lib.rs
async fn execute_package_request(ctx, spec, args) {
match executor.execute_request(&pkg_request, args).await {
Ok(exit_code) => Ok(()),
Err(ShimError::PackageNotInstalled { ecosystem, package }) => {
// Auto-install the package
auto_install_package(ctx, &pkg_request).await?;
// Retry execution
executor.execute_request(&pkg_request, args).await
}
}
}This provides a seamless uvx/npx-like experience:
- First run: Auto-installs and executes
- Subsequent runs: Executes from cache
Supported Syntax Patterns
| Pattern | Example | Description |
|---|---|---|
| Simple | npm:typescript | Package name = executable |
| With version | npm:typescript@5.3 | Specific package version |
| Different executable | npm:typescript::tsc | Explicit executable name |
| Full syntax | npm@20:typescript@5.3::tsc | Runtime + package version + executable |
| Scoped npm | npm:@biomejs/biome::biome | Scoped package with executable |
| Runtime version | pip@3.11:black | Specific runtime version |
Parser Implementation
The parser handles edge cases like scoped npm packages:
// Scoped packages: @org/package@version
if part.starts_with('@') {
// Handle @types/node or @types/node@1.0
if let Some(slash_pos) = part.find('/') {
// Parse scope and package name
// Handle version after package name
}
}Shell Execution Syntax
vx supports launching shells with a specific runtime's environment attached to the current terminal:
vx <runtime>[::shell_name]This is equivalent to opening a shell with the runtime's tools in PATH, attached to the current terminal (not a new window).
Examples
# Open git-bash in current terminal (Windows)
vx git::git-bash
# Open PowerShell with node environment
vx node::powershell
# Open cmd with go environment
vx go::cmd
# Open bash with python environment
vx python::bash
# Open default shell with uv environment
vx uv::bashSupported Shells
| Shell | Platform | Notes |
|---|---|---|
git-bash | Windows | Git Bash (MINGW64), attaches to current terminal |
bash | Unix/Windows | Bash shell |
zsh | macOS/Linux | Z shell |
fish | Unix | Fish shell |
powershell | Windows/Unix | PowerShell |
cmd | Windows | Command Prompt |
sh | Unix | POSIX shell |
Behavior
- Default: Shells run in the current terminal (no new window)
- Windows git-bash: Uses
bin/bash.exedirectly (like VSCode), notgit-bash.exe(MinTTY launcher). Runs with--login -ifor a proper interactive login shell. - All shells: Inherit the runtime's PATH and environment variables
See Also
vx global- Manage global packagesvx install- Install runtime versions- RFC 0027: Implicit Package Execution
- RFC 0025: Cross-Language Package Isolation