back
loading skill details...
Project management guide for vx. Use when setting up a new project, configuring vx.toml, or managing project-level tool versions and scripts.
---
name: vx-project
description: "Project management guide for vx. Use when setting up a new project, configuring vx.toml, or managing project-level tool versions and scripts."
---
# VX Project Management Guide
> **Quick start**: Run `vx init` to create `vx.toml`, `vx setup` to install all tools, `vx dev` to enter the dev environment. For existing projects, just run `vx setup` after cloning.
## Project Setup
### Initialize a Project
```bash
vx init # Create vx.toml interactively
vx init --template node # Use a template
vx init --minimal # Create minimal vx.toml
```
### Project Detection
vx automatically detects project types and suggests tools:
```bash
vx analyze # Analyze project (detects languages, dependencies)
vx analyze --json # JSON output for AI parsing
```
**Detected ecosystems**: Node.js, Python, Rust, Go, Java, .NET, C/C++, Zig
**Detected frameworks**: React, Vue, Angular, Next.js, Nuxt, Svelte, Django, Flask, FastAPI, Tauri, Electron, React Native, NW.js, and more
**Detected package managers**: npm, yarn, pnpm, bun, pip, uv, cargo, go modules
The project analyzer reads indicator files like `package.json`, `pyproject.toml`, `Cargo.toml`, `go.mod`, etc. to suggest the right tools.
## vx.toml Configuration
### Basic Structure
```toml
# vx.toml - Project tool configuration
[tools]
# Version constraints
node = "22" # Major version (any 22.x.x)
go = "1.22" # Minor version (any 1.22.x)
uv = "latest" # Always use latest
rust = "1.80" # Specific version
# rust = "rustup-managed" defers to rustup instead
just = "*" # Any version
# Platform-specific tools
[tools.msvc]
version = "14.42"
os = ["windows"] # Only install on Windows
[tools.brew]
version = "latest"
os = ["macos", "linux"]
[scripts]
# Development scripts
dev = "npm run dev"
test = "cargo test"
lint = "npm run lint && cargo clippy"
build = "just build"
# CI/CD scripts
ci = "just ci"
release = "just release"
[hooks]
# Lifecycle hooks
pre_commit = ["vx run lint"]
post_setup = ["npm install", "cargo fetch"]
```
### Multi-Python Legacy Projects
For projects that need modern Python plus legacy Python 3.7 or 2.7, keep the
runtime requirements in `vx.toml` and the environment/test matrix in `justfile`:
```toml
[tools]
uv = "latest"
python = "3.12"
just = "latest"
[scripts]
test = "vx just test"
test-legacy = "vx just test-legacy"
```
```makefile
venv312:
vx uv venv .venv312 --python 3.12
vx uv pip install --python .venv312 -r requirements.txt
venv37:
vx uv venv .venv37 --python 3.7
vx uv pip install --python .venv37 -r requirements-py37.txt
venv27:
vx uv venv .venv27 --python 2.7
.venv27/bin/python -m pip install -r requirements-py27.txt
test312: venv312
.venv312/bin/python -m pytest
test37: venv37
.venv37/bin/python -m pytest
test27: venv27
.venv27/bin/python -m pytest
test-legacy: test37 test27
test: test312 test-legacy
```
Agents should use `vx uv venv ... --python 3.7` and
`vx uv venv ... --python 2.7`; vx resolves those versions to managed
interpreters. Python 2.7 is a legacy compatibility path using PyPy2.7 and
PyPA's Python 2.7 `virtualenv.pyz`, so flag CPython-only extension risks early.
### Multi-Version Runtime Test Matrices
When a project promises compatibility across several runtime lines, record the
baseline runtime in `vx.toml` and put every compatibility lane in `justfile`.
This keeps local developer testing, CI, and AI-agent verification aligned.
```toml
[tools]
node = "22"
python = "3.12"
uv = "latest"
just = "latest"
[scripts]
test = "vx just test"
test-matrix = "vx just test-matrix"
[ai]
skills_hash = "<recorded by vx ai setup --project>"
```
```makefile
test-node18:
vx node@18 npm test
test-node20:
vx node@20 npm test
test-node22:
vx node@22 npm test
venv37:
vx uv venv .venv37 --python 3.7
vx uv pip install --python .venv37 -r requirements-py37.txt
venv312:
vx uv venv .venv312 --python 3.12
vx uv pip install --python .venv312 -r requirements.txt
test-py37: venv37
.venv37/bin/python -m pytest tests/py37
test-py312: venv312
.venv312/bin/python -m pytest tests/py312
test-matrix: test-node18 test-node20 test-node22 test-py37 test-py312
test: test-matrix
```
Agents should use the matrix recipe (`vx just test-matrix` or
`vx run test-matrix`) when changing shared code. Add the smallest missing lane
when a bug report mentions an unsupported Python, Node.js, npm, pnpm, or yarn
version.
### Project AI Skills Hash
`vx ai setup` installs built-in vx skills globally by default. Use project scope
only when the repository wants local skill copies:
```bash
vx ai setup --project
vx ai check
vx ai setup --project --force
```
Project setup records `[ai].skills_hash` in `vx.toml`. `vx ai check` compares
that hash with the embedded skills hash and reminds developers to refresh stale
project skills.
Global scope is verified too: `vx ai setup` records the hash under
`~/.vx/ai-skills.toml`, and `vx ai check` reports drift for both scopes.
```bash
vx ai check # report global + project drift
vx ai check --fix # refresh stale vx-builtin copies, drop project duplicates
```
A repository that ships no skills of its own needs neither scope pinned — prefer
the global install and keep the repo free of skill copies. See the
**vx-repo-contract** skill for the repository layout rules.
### `[scripts]` vs `justfile`: pick one task surface
`vx.toml` declares *tool versions*. A task runner declares *how work runs*.
Keeping both means every task has two definitions, and the stale one wins
silently.
- If the repo has a `justfile`, put tasks there and keep `[scripts]` out of
`vx.toml`. A `[scripts]` entry that forwards to `just <recipe>` is a duplicate
source of truth: rename the recipe and the script keeps invoking the old
behaviour.
- Only use `[scripts]` when there is no `justfile`.
```bash
vx run test # only meaningful when [scripts] exists
vx just test # preferred when a justfile exists
```
**Check** — this must print nothing:
```bash
test -f justfile && vx rg -q '^\[scripts\]' vx.toml && echo "FAIL: [scripts] duplicates justfile"
```
### Version Constraints
| Constraint | Example | Meaning |
|------------|---------|---------|
| Exact | `"1.2.3"` | Only version 1.2.3 |
| Major | `"1"` | Any 1.x.x |
| Minor | `"1.2"` | Any 1.2.x |
| Latest | `"latest"` | Always latest |
| Any | `"*"` | Any available version |
| Range | `">=1.0.0 <2.0.0"` | Range constraint |
### Platform-Specific Tools
```toml
[tools]
# Cross-platform tools
node = "22"
uv = "latest"
# Windows-only
[tools.msvc]
version = "14.42"
os = ["windows"]
# macOS/Linux only
[tools.brew]
version = "latest"
os = ["macos", "linux"]
```
### Rust Version Resolution
Rust is the one ecosystem where vx is **not** the authority. If the repository already
names a toolchain, vx delegates to rustup: it installs nothing, keeps its store off
`PATH`, and never runs `rustup default` (which would change the default for every other
project on the machine).
| Priority | Signal | Behaviour |
|---|---|---|
| 1 | `rust-toolchain.toml` / `rust-toolchain` | Delegate to rustup (searched upward from the working directory) |
| 2 | `RUSTUP_TOOLCHAIN` set | Delegate to rustup |
| 3 | `rust = "rustup-managed"` in `vx.toml` | Delegate to rustup, explicitly |
| 4 | `rust = "<version>"` in `vx.toml` | vx installs that toolchain into its store |
- Both (1) and (2) present → (2) wins, matching rustup's own precedence.
- A numeric `vx.toml` pin that disagrees with the effective toolchain is warned about
on stderr and makes `vx check` exit non-zero. The toolchain file still wins; the
warning exists so a dead pin cannot go unnoticed.
- For a repository that already pins with rustup, write `rust = "rustup-managed"`
instead of omitting `rust` with an explanatory comment.
- An explicit `vx cargo@1.90.0` always goes through vx.
## Project Commands
### Setup & Sync
```bash
vx setup # Full project setup (sync + hooks)
vx sync # Install all tools from vx.toml
vx sync --clean # Remove unlisted tools
vx sync --check # Check without installing
```
### Running Scripts
```bash
vx run dev # Run development server
vx run test # Run tests
vx run build # Build project
vx run --list # List available scripts
```
### Lock File
```bash
vx lock # Generate vx.lock
vx lock --update # Update locked versions
vx lock --check # Verify lock file
```
The `vx.lock` file ensures reproducible builds:
```toml
# vx.lock - Auto-generated, do not edit
[tools]
node = { version = "22.0.0", checksum = "sha256:..." }
go = { version = "1.22.0", checksum = "sha256:..." }
```
## Environment Management
### Project Environment
```bash
vx dev # Enter project environment
vx env list # List environments
vx env activate # Print activation commands
eval $(vx env activate) # Activate in shell
```
### Environment Variables
Define in `vx.toml`:
```toml
[env]
NODE_ENV = "development"
DATABASE_URL = "postgresql://localhost:5432/dev"
API_KEY = { env = "API_KEY", required = true }
```
## Dependency Management
### Add/Remove Tools
```bash
vx add node@22 # Add tool to vx.toml
vx add go rust uv # Add multiple tools
vx remove node # Remove tool from vx.toml
```
### Check Constraints
```bash
vx check # Verify tool constraints
vx check --json # JSON output
vx check --fix # Auto-fix issues
```
## Multi-Package Projects
### Monorepo Support
For monorepos, create `vx.toml` in root:
```toml
# Root vx.toml
[tools]
node = "22"
pnpm = "latest"
[scripts]
install = "pnpm install"
build = "pnpm -r build"
test = "pnpm -r test"
```
### Workspace Packages
Individual packages can have their own `vx.toml`:
```toml
# packages/backend/vx.toml
[tools]
go = "1.22"
[scripts]
dev = "go run ./cmd/server"
```
## Best Practices
### 1. Version Pinning
Pin versions for CI/CD:
```toml
[tools]
node = "22.0.0" # Exact version for CI
```
### 2. Lock Files
Always commit `vx.lock`:
```bash
git add vx.lock
```
### 3. Scripts Organization
Group related scripts:
```toml
[scripts]
# Development
dev = "..."
watch = "..."
# Testing
test = "..."
test:watch = "..."
# Build
build = "..."
build:prod = "..."
```
### 4. Hooks for Quality
Use hooks for automated checks:
```toml
[hooks]
pre_commit = ["vx run lint", "vx run test"]
post_checkout = ["vx sync"]
```
## Project Templates
Create reusable templates:
```bash
# Create template from current project
vx template create my-template
# Use template
vx init --template my-template
```
### Template Structure
```
~/.vx/templates/my-template/
├── vx.toml
├── .gitignore
├── README.md
└── hooks/
└── post_setup.sh
```
---
## Delivery Surface — External Systems Are Evidence
GitHub PRs, CI runs, and dashboards are **supporting evidence**, not the delivery
surface. The conclusion has to land where the work is tracked.
- **Record the outcome once, completely** — one issue comment carrying status,
branch/commit/PR, what you validated, the blocker, and the next owner.
- **Verify terminal state, not intermediate state.** A green CI run or an open,
review-ready PR is not "shipped". Confirm merged / released / deployed, then
record *that*.
- **Keep public surfaces public-safe.** PR titles, bodies, and commit messages
carry technical content only — no internal issue IDs, routing history,
reviewer handoffs, local absolute paths, or internal hostnames.
- **Collect results in the foreground.** A queued or pending state is a handoff,
not a completion; never end a turn "standing by" for background work.
Concretely: the delivery surface is the team's issue or task tracker — the
issue comment plus its metadata. A PR comment alone delivers nothing.
don't have the plugin yet? install it then click "run inline in claude" again.