Skip to content
vxvx
GitHubBlueskydev.toRSS
Releases

Fewer surprises when you switch

vx init maps a root package.json's repo-wide scripts, a bare --affected takes its base from affectedBase, and Turbo and Nx commands, flags and cache switches now land where you expect.

vx 0.0.325: Fewer surprises when you switch

vx 0.0.243 to 0.0.325 all shipped on 2026-10-01. The theme is adoption: moving from Turbo or Nx to vx has fewer surprises, from the first vx init to the CI job that must not write the shared cache.

In this release

vx run lint used to find no project in many repos, because the root’s lint, test and format scripts were never mapped. vx init now turns a root script that checks the whole repo into a root task. A script that runs the members (pnpm -r, --filter, --cwd, --prefix, vp run, a runner, or vx itself) stays unmapped, and init names it. A run scoped to a project that lacks a task others declare now says so and names --all.

Terminal window
acme (the workspace root): its scripts that check the whole repo are its tasks; left out as running the members (`pnpm -r`, `--filter`, a runner): build

PRs #1994, #1996, #2004, #1971

A bare --affected compared with origin/HEAD, so a git-flow repo diffed against the wrong branch. affectedBase in vx.workspace.ts now names the ref, and --affected=<base> still wins. nx() and turbo() set it from Nx’s defaultBase and NX_BASE, and from Turbo’s TURBO_SCM_BASE.

vx.workspace.ts
import { defineWorkspace } from '@vzn/vx/config'
export default defineWorkspace({ affectedBase: 'origin/develop' })

PR #1960. Docs: Workspace config.

Turbo spells a root task //#lint. vx read // as a project name and refused it in vx run, vx show, vx why and in dependsOn. All four now take it as the root project’s task, from any directory.

Terminal window
vx run //#lint

PRs #1953, #1956, #1957

Turbo’s and Nx’s run flags and verbs used to stop at “unknown flag” or “unknown command”. Now -F is --filter, three Nx options are aliases, and the rest (--env-mode, --global-deps, nx graph, nx show projects and more) say what does it in vx. A flag cut short (--dry) or --json on a verb that takes --format json gets a hint too.

Terminal window
$ vx run build --env-mode=strict
vx run: --env-mode (turbo): vx passes only the variables a task declares (strict): list the rest in `exec.env.passThrough` (see `vx run --help`)
$ vx graph
`nx graph` is `vx run <task> --graph[=<file>.dot]` here: the task graph as Graphviz DOT
$ vx info --json
vx info: unknown flag: --json (did you mean --format json?) (see `vx info --help`)

PRs #1948, #1949, #1950, #1951, #1955, #1964, #1936

A CI keeps an untrusted pull request from writing the shared cache with Turbo’s TURBO_CACHE and TURBO_REMOTE_CACHE_READ_ONLY, or Nx’s NX_SKIP_REMOTE_CACHE. vx ignored them, so such a job wrote what main would restore. turboCache() and nxCache() now honour each one. A switch can only narrow the run’s --cache policy, never widen it.

Terminal window
TURBO_REMOTE_CACHE_READ_ONLY=1 vx run build --all

PR #1941. Docs: Remote cache.

turboCache() reads all of Turbo’s cache config

Section titled “turboCache() reads all of Turbo’s cache config”

turboCache() declined on Vercel and in a turbo linked repo. It now reads VERCEL_ARTIFACTS_TOKEN and _OWNER, and .turbo/config.json. It honours Turbo’s timeouts, signs only where turbo.json turns signing on, and sends teamId only in Vercel’s team_ form. vx init declares it in a linked repo, and says when nx.json uses Nx Cloud, which vx cannot share.

vx.workspace.ts
import { defineWorkspace } from '@vzn/vx/config'
import { turbo, turboCache } from '@vzn/vx-migrate'
export default defineWorkspace({ plugins: [turboCache(), turbo()] })

PRs #1942, #1939, #1940, #1946, #1962

nx() now caps the local cache at maxCacheSize (or NX_MAX_CACHE_SIZE), and lets NX_PARALLEL win over nx.json’s parallel, as Nx 23 does. nxCache() keeps reads on when a read-only token’s upload is refused. bunx @vzn/vx-migrate --from nx exports the graph itself when none is on disk.

Terminal window
NX_PARALLEL=2 vx run build --all

PRs #1945, #1944, #1947, #1977

turbo() left a task uncached when its outputs glob might reach a source file. It now proves more globs safe: **/dist/** with no tracked dist directory, a pattern like dist-*/** that matches no tracked top-level entry, and top-level artifacts like *.xml. vercel/ai’s 69 builds and tldraw’s 35 now cache. nx() shares the same rules.

Terminal window
vx run build --all

PRs #1974, #1986, #1969, #1980

Every vx an npm user runs starts in a small Node launcher. It is now CommonJS, not ESM: vx --version went from 65.5 ms to 57.0 ms. On Node 22.15 and newer the launcher replaces itself with the binary instead of spawning it: from 54.2 ms to 45.3 ms. The binary itself answers --version before loading the dispatcher, from 20.1 ms to 16.4 ms.

Terminal window
npx vx --version
vx --version from npm
before65.5 ms
after57 ms
On Node, spawning vx
before54.2 ms
after45.3 ms
The binary
before20.1 ms
after16.4 ms

PRs #1979, #1981, #1961

bun.lock records a patch by path, so editing the patch leaves the lockfile byte-identical while bun install applies the new one. Without the bun() plugin, vx replayed the old outputs. Core now folds each patch bun.lock names into the key, and --affected widens on its edit.

Terminal window
# after editing patches/left-pad@1.3.0.patch and running bun install
vx run build --all

PR #1904. Docs: Lockfiles.

  • vx init and vx-migrate write import type { ProjectConfig } from '@vzn/vx/config' (#2013).
  • @vzn/vx-migrate adopts Turbo and Nx only: lage(), wireit(), workspaceScripts() and --from lage|wireit|scripts are gone (#2009).
  • moon support is removed: moon(), mapMoonWorkspace and --from moon (#2008).
  • vx info --format json and vx mcp’s getWorkspaceInfo gain sandbox.untraced (#2000).
  • vx init maps a root’s repo-wide scripts to root tasks (#1994).
  • vx init drops the readyWhen note on a persistent task nothing depends on (#1987).
  • turbo() caches outputs it proved safe, and the plugin API’s tracked set changed (#1986, #1974).
  • The npm launcher is CommonJS (#1979).
  • A bare --affected reads affectedBase first (#1960).
  • Turbo task tags map to nothing (#1934).

See Upgrading.

Terminal window
npm install -D @vzn/vx@latest

A standalone binary updates itself with vx upgrade.

Turborepo, Nx and other product names are trademarks of their owners. vx is not affiliated with or endorsed by them.