One command per task
A task is a shell command with edges and a cache block; nothing boots in between.

There is no executor layer to learn and no process per task to pay for. A task is the command you would type, plus what it depends on and what it reads and writes.
Whatever your tools are, they run exactly as they do in your terminal.
// config
// packages/api/vx.config.ts
import { defineProject } from '@vzn/vx/config'
export default defineProject({
tasks: {
build: {
exec: { command: 'tsc -b' },
dependsOn: ['^build'],
description: 'Type-check and emit',
cache: { inputs: { files: ['src/**'] }, outputs: { files: ['dist/**'] } },
},
},
})More in Config

Rules that catch slow configs
Checks for configs that would make runs slow or wrong, on by default, each one optional.

A config that hangs, named
A config whose evaluation runs away fails after a deadline, naming the file.

No runs inside runs
A vx run inside a task of the same workspace is refused, with the reason.

Config with autocomplete
defineProject gives you completion and errors while you type.

Presets are just functions
Share task setups across projects with a TypeScript function, no new concept.

Config errors that point
A bad config fails at load with the file, the line and the key, no stack trace.