@vzn/vx 0.0.16 → 0.0.21

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (138) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +135 -124
  3. package/index.ts +10 -0
  4. package/launcher.mjs +10 -11
  5. package/package.json +11 -7
  6. package/src/bin.ts +14 -4
  7. package/src/cache/archive.ts +659 -0
  8. package/src/cache/cache.ts +769 -1101
  9. package/src/cache/chained-cache.ts +194 -0
  10. package/src/cache/config-evals.ts +89 -0
  11. package/src/cache/file-hashes.ts +262 -0
  12. package/src/cache/git-inputs.ts +637 -0
  13. package/src/cache/index.ts +24 -5
  14. package/src/cache/inputs.ts +373 -456
  15. package/src/cache/layer.ts +604 -0
  16. package/src/cache/layered-cache.ts +96 -43
  17. package/src/cache/output-index.ts +224 -0
  18. package/src/cache/policy.ts +84 -0
  19. package/src/cache/run-history.ts +147 -0
  20. package/src/cache/tar-stream.ts +358 -0
  21. package/src/cache/zstd.ts +160 -0
  22. package/src/cli/cache.ts +75 -21
  23. package/src/cli/completions.ts +104 -0
  24. package/src/cli/core-alias.ts +25 -0
  25. package/src/cli/help.ts +195 -128
  26. package/src/cli/index.ts +116 -46
  27. package/src/cli/info.ts +122 -63
  28. package/src/cli/init.ts +75 -0
  29. package/src/cli/last.ts +172 -0
  30. package/src/cli/lock.ts +2 -1
  31. package/src/cli/plan-format.ts +72 -6
  32. package/src/cli/plugin-commands.ts +103 -0
  33. package/src/cli/run.ts +245 -271
  34. package/src/cli/select.ts +332 -0
  35. package/src/cli/show.ts +144 -69
  36. package/src/cli/upgrade.ts +9 -7
  37. package/src/cli/watch.ts +858 -110
  38. package/src/cli/why.ts +223 -0
  39. package/src/cli/workspace-config.ts +126 -0
  40. package/src/config.ts +187 -150
  41. package/src/exec/env.ts +18 -1
  42. package/src/exec/executor.ts +224 -0
  43. package/src/exec/index.ts +24 -2
  44. package/src/exec/local-executor.ts +41 -0
  45. package/src/exec/runner.ts +166 -40
  46. package/src/exec/sandbox-binds.ts +182 -0
  47. package/src/exec/sandbox-paths.ts +50 -0
  48. package/src/exec/sandbox-runtime.ts +642 -260
  49. package/src/exec/sandbox-violations.ts +280 -0
  50. package/src/graph/index.ts +4 -10
  51. package/src/graph/scheduler.ts +232 -203
  52. package/src/graph/task-graph.ts +222 -8
  53. package/src/index.ts +146 -153
  54. package/src/orchestrator/admission.ts +137 -0
  55. package/src/orchestrator/deferred-outputs.ts +147 -0
  56. package/src/orchestrator/doctor.ts +255 -0
  57. package/src/orchestrator/download-policy.ts +171 -0
  58. package/src/orchestrator/events.ts +72 -8
  59. package/src/orchestrator/execute-task.ts +374 -549
  60. package/src/orchestrator/failure-mode.ts +202 -0
  61. package/src/orchestrator/framed-output.ts +126 -69
  62. package/src/orchestrator/history.ts +126 -59
  63. package/src/orchestrator/hit-restore.ts +221 -0
  64. package/src/orchestrator/index.ts +77 -93
  65. package/src/orchestrator/local-shortcircuit.ts +22 -0
  66. package/src/orchestrator/lockfile-claim.ts +281 -0
  67. package/src/orchestrator/logger.ts +208 -35
  68. package/src/orchestrator/metrics.ts +102 -1388
  69. package/src/orchestrator/miss-save.ts +169 -0
  70. package/src/orchestrator/options.ts +55 -34
  71. package/src/orchestrator/persistent.ts +79 -0
  72. package/src/orchestrator/placement.ts +182 -0
  73. package/src/orchestrator/plan.ts +144 -1
  74. package/src/orchestrator/plugin-host.ts +357 -120
  75. package/src/orchestrator/plugin.ts +298 -58
  76. package/src/orchestrator/prepare.ts +202 -106
  77. package/src/orchestrator/projects.ts +259 -0
  78. package/src/orchestrator/remote-prefetch.ts +12 -8
  79. package/src/orchestrator/run-artifacts.ts +63 -16
  80. package/src/orchestrator/run-context.ts +167 -29
  81. package/src/orchestrator/run-records.ts +149 -0
  82. package/src/orchestrator/run-report.ts +74 -47
  83. package/src/orchestrator/run.ts +534 -417
  84. package/src/orchestrator/sandbox-request.ts +243 -0
  85. package/src/orchestrator/save-lane.ts +47 -0
  86. package/src/orchestrator/signals.ts +111 -0
  87. package/src/orchestrator/stable-keys.ts +129 -31
  88. package/src/orchestrator/status-line.ts +49 -9
  89. package/src/orchestrator/summary.ts +90 -26
  90. package/src/orchestrator/tally.ts +78 -35
  91. package/src/orchestrator/task-hash.ts +111 -15
  92. package/src/orchestrator/task-log-buffer.ts +288 -0
  93. package/src/orchestrator/telemetry-host.ts +34 -6
  94. package/src/orchestrator/telemetry.ts +182 -36
  95. package/src/orchestrator/upstream.ts +72 -31
  96. package/src/util/cgroup.ts +160 -0
  97. package/src/util/edit-distance.ts +70 -0
  98. package/src/util/errors.ts +15 -1
  99. package/src/util/index.ts +15 -2
  100. package/src/util/num.ts +37 -0
  101. package/src/util/paths.ts +64 -0
  102. package/src/util/settle.ts +67 -0
  103. package/src/util/size.ts +5 -1
  104. package/src/util/tail.ts +66 -0
  105. package/src/util/timing.ts +63 -0
  106. package/src/util/verbs.ts +39 -0
  107. package/src/workspace/affected.ts +250 -23
  108. package/src/workspace/config-cache.ts +313 -0
  109. package/src/workspace/config-eval.ts +217 -0
  110. package/src/workspace/config-imports.ts +162 -0
  111. package/src/workspace/config-schema.ts +837 -0
  112. package/src/workspace/filter.ts +65 -10
  113. package/src/workspace/fingerprint.ts +53 -4
  114. package/src/workspace/index.ts +42 -3
  115. package/src/workspace/lockfile.ts +25 -9
  116. package/src/workspace/migrate-scripts.ts +134 -0
  117. package/src/workspace/migration.ts +314 -0
  118. package/src/workspace/nested-dirs.ts +21 -7
  119. package/src/workspace/package-graph.ts +114 -41
  120. package/src/workspace/project-loader.ts +224 -450
  121. package/src/workspace/workspace.ts +298 -72
  122. package/src/cache/cas-backend.ts +0 -97
  123. package/src/cache/digest.ts +0 -47
  124. package/src/cache/tar.ts +0 -343
  125. package/src/cli/backend.ts +0 -23
  126. package/src/cli/mcp-rpc.ts +0 -292
  127. package/src/cli/mcp.ts +0 -92
  128. package/src/cli/migrate-nx.ts +0 -450
  129. package/src/cli/migrate-turbo.ts +0 -364
  130. package/src/cli/migrate.ts +0 -219
  131. package/src/orchestrator/devframe-surface.ts +0 -74
  132. package/src/orchestrator/predict.ts +0 -106
  133. package/src/orchestrator/protocol.ts +0 -156
  134. package/src/orchestrator/resources.ts +0 -59
  135. package/src/orchestrator/run-state.ts +0 -145
  136. package/src/orchestrator/verify.ts +0 -164
  137. package/src/orchestrator/wire-render.ts +0 -92
  138. package/src/orchestrator/wire.ts +0 -212
package/LICENSE CHANGED
@@ -1,6 +1,6 @@
1
1
  MIT License
2
2
 
3
- Copyright (c) 2026 nxt contributors
3
+ Copyright (c) 2026 vx contributors
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
package/README.md CHANGED
@@ -3,11 +3,33 @@
3
3
  **The fastest way to build a monorepo.**
4
4
 
5
5
  vx runs your task graph, remembers every result, and never does the
6
- same work twice. Fully cached runs finish in milliseconds — 144 ms
7
- across 100 packages, 0.62 s across a 1090-package graph of 3,270
8
- tasks. Measured, reproducible, on hardware you own.
9
-
10
- One binary. No daemon. No Node. Nothing to babysit.
6
+ same work twice.
7
+
8
+ <!-- bench:start — generated by packages/vx-bench/update-site.ts from results.json; do not hand-edit -->
9
+
10
+ The runner adds seconds to a cold build where others add minutes. On a
11
+ 1,090-package graph of 3,270 tasks whose ideal schedule is 3m 38s,
12
+ vx finishes in 3m 46s (+0:08), Turborepo in 5m 13s (+1:35) and Nx in
13
+ 34m 44s (+31:06) — 8 ms of overhead per package against 88 ms and
14
+ 1,712 ms, so the graph can grow and the runner stays in seconds. The cold build
15
+ burns 35 s of CPU in vx, 73 s in Turborepo and 114 minutes in Nx; a
16
+ fully cached run replays the graph in 510 ms.
17
+ <!-- bench:end -->
18
+
19
+ On a real Turbo monorepo (`solidjs/solid`, its own `turbo.json`, no
20
+ config rewritten) vx replays a cached build in 51 ms where Turbo takes
21
+ 95, restores every output in 66 ms against 127, and builds cold in
22
+ 40.6 s against 45.5 s. Measured, reproducible, on hardware you own
23
+ ([benchmarks](packages/vx/docs/benchmarks.md)).
24
+
25
+ One binary. No daemon, no cloud, no account, no paywall. Sandboxed
26
+ tasks, a plugin at every stage, seconds of overhead at a thousand
27
+ packages.
28
+
29
+ vx does one thing — run and cache a task graph, correctly — and stops
30
+ there. Remote caches, remote execution, telemetry, AI agents, learned
31
+ scheduling and your own verbs are plugins on documented seams, not
32
+ features inside: what Nx would be if it weren't a product.
11
33
 
12
34
  📖 **[Documentation site →](https://vznjs.github.io/vx/)** — guides,
13
35
  architecture, caching, and the full CLI / config reference.
@@ -42,6 +64,7 @@ export default defineProject({
42
64
  ```
43
65
 
44
66
  ```sh
67
+ vx init # scaffold vx.workspace.ts + a vx.config.ts per package from scripts
45
68
  vx run build # cwd project + its workspace deps
46
69
  vx run test --all # every project that declares `test`
47
70
  vx run ci --affected # only what changed since origin/HEAD
@@ -73,16 +96,11 @@ skip extraction entirely when the tree already matches. In-process
73
96
  tar (no subprocess on the hot path). Atomic artifact publishes.
74
97
  Single-transaction metadata writes. Every optimization is recorded
75
98
  with the invariant that keeps it valid —
76
- [`docs/optimizations.md`](docs/optimizations.md) is the ledger, and
77
- [`bench/`](bench/) reproduces the numbers.
99
+ [`packages/vx/docs/optimizations.md`](packages/vx/docs/optimizations.md) is the ledger, and
100
+ [`packages/vx-bench/`](packages/vx-bench/) reproduces the numbers.
78
101
 
79
102
  ## Built for trust
80
103
 
81
- - **Provable cache correctness.** `vx run --verify` re-runs each
82
- cacheable task and byte-compares outputs (determinism);
83
- `--verify=inputs` sandboxes it against the declared inputs and fails
84
- loud naming any undeclared read (completeness). No other runner can
85
- prove a cache entry safe.
86
104
  - **Corruption can't go live.** A remote artifact is verified against
87
105
  its content digest and validated before it enters the store
88
106
  (zstd-bomb and oversize downloads refused); bad bytes degrade to a
@@ -92,7 +110,11 @@ with the invariant that keeps it valid —
92
110
  - **Readiness you can bound.** Persistent tasks gate downstream work
93
111
  on a `readyWhen` signal; `exec.timeout` bounds any task (with
94
112
  `--timeout` / workspace defaults), `exec.retries` + `--retry` absorb
95
- flakes — and retried-then-passed tasks are flagged flaky.
113
+ flakes.
114
+ - **Flaky tasks are found, not guessed.** The same inputs both passing
115
+ and failing is the definition, and vx holds every hash and outcome
116
+ locally: a run names them under its footer, `--summarize` types them
117
+ per task, `vx info` lists them. No service.
96
118
  - **Kernel-level sandboxing**, opt-in per task, that fails the build
97
119
  on violation instead of hiding it.
98
120
 
@@ -125,46 +147,75 @@ resolution that bridges packages without the task · multi-task runs
125
147
  with one shared graph · pnpm-style filters and `--affected` ·
126
148
  watch mode · `--dry` / `--graph` plans · persistent dev servers ·
127
149
  retries, timeouts, `--continue` modes · per-layer cache control
128
- (`--cache=local:r,remote:`) · `vx info`, `--summarize`, `--profile`
129
- Chrome traces, `--report` · `vx cache prune` with TTL and size caps ·
130
- `vx migrate` from turbo.json or an Nx graph.
131
-
132
- ## Extensible by design — the core is provider-neutral
133
-
134
- `vx` runs tasks and nothing else. A dashboard, a remote cache,
135
- distributed execution, and telemetry export all arrive through
136
- **plugins** declared in `vx.workspace.ts`, filling three seams the
137
- core exposes — **backend** (where a task runs), **cache** (a remote
138
- layer behind the local one), and **telemetry** (a read-only stream of
139
- the versioned `TelemetryRecord` / `RunSummaryRecord` contract). Core
140
- depends on none of them; the arrow only ever points plugin → core.
141
- First-party plugins ship the OpenTelemetry exporter
142
- ([`@vzn/vx-otel`](https://www.npmjs.com/package/@vzn/vx-otel), OTLP
143
- traces + metrics with zero OTel-SDK deps) and **vx Cloud** (below) —
144
- and the same seams are open to anyone.
150
+ (`--cache=local:r,remote:`) · `vx why` explains a re-run from the
151
+ persisted input fingerprints · `vx last` replays a recorded run ·
152
+ `vx info`,
153
+ `--summarize`, `--profile` Chrome traces, `--report` · `vx cache prune`
154
+ with TTL and size caps · `bunx @vzn/vx-migrate` from turbo.json or an Nx graph.
155
+
156
+ ## A pipeline with seams — plugins decide what happens
157
+
158
+ Core is the pipeline: discover projects, evaluate configs, build the
159
+ task graph, derive keys, schedule, execute, cache, observe. Plugins
160
+ declared in `vx.workspace.ts` hook each stage, Vite-style, on one
161
+ `VxPlugin` object:
162
+
163
+ | Stage | Hook | A plugin can… |
164
+ | --------- | -------------------------- | ----------------------------------------------------------- |
165
+ | workspace | `config(ws, ctx)` | edit the workspace config before it is used |
166
+ | project | `project(config, ctx)` | add, remove or rewrite a project's tasks (keyed like yours) |
167
+ | graph | `graph(nodes, ctx)` | add or drop edges, mark tasks requested |
168
+ | key | `key(task, ctx)` | fold extra material into the cache key (named in `vx why`) |
169
+ | schedule | `schedule(nodes, ctx)` | decide which ready task runs first |
170
+ | execute | `executor(ctx)` | decide WHERE a task's command runs (local, a REAPI worker) |
171
+ | store | `cache(ctx)` | decide where artifacts live (local, a shared remote) |
172
+ | observe | `telemetry(ctx)` / `setup` | receive every run record, or the raw event bus |
173
+ | cli | `commands` | add verbs to `vx` |
174
+
175
+ Core applies **no plugin by default** and names none. Running here and
176
+ caching here are its floor — the tail of every executor list and cache
177
+ chain — so a workspace with no `vx.workspace.ts` runs and caches, and a
178
+ plugin that declines a task hands it back to this machine. First-party
179
+ plugins:
180
+ [`@vzn/vx-reapi`](packages/vx-reapi) (Bazel Remote Execution API —
181
+ remote cache and remote execution against NativeLink, BuildBuddy,
182
+ Buildbarn or bazel-remote), [`@vzn/vx-otel`](packages/vx-otel)
183
+ (OpenTelemetry traces + metrics + logs, zero SDK deps),
184
+ [`@vzn/vx-github`](packages/vx-github) (Actions job summary + Checks
185
+ API), [`@vzn/vx-mcp`](packages/vx-mcp) (`vx mcp` — a read-only Model
186
+ Context Protocol server for AI coding agents, no SDK),
187
+ [`@vzn/vx-migrate`](packages/vx-migrate) (adoption in one package:
188
+ `turbo()` runs a Turbo repo under vx with nothing written, `bunx
189
+ @vzn/vx-migrate` writes configs from `turbo.json` or an Nx graph, and
190
+ `turboCache()` / `nxCache()` keep a remote cache speaking Turbo's
191
+ `/v8/artifacts` API or Nx's self-hosted spec — the wire is theirs, the
192
+ artifacts are vx's),
193
+ [`@vzn/vx-lockfile`](packages/vx-lockfile) (`pnpm()`, `bun()`, `npm()`,
194
+ `yarn()`: the lockfile keyed per project, so one install re-keys only the
195
+ projects it reaches, and `--affected` follows), and [`@vzn/vx-schedule-history`](packages/vx-schedule-history)
196
+ (order by learned critical path) and [`@vzn/vx-migrate`](packages/vx-migrate)
197
+ (`bunx @vzn/vx-migrate`: turbo.json or an Nx graph → vx.config.ts, through
198
+ core's migration seam). Core ships no plugin and reads no other runner's
199
+ format; nothing
200
+ distributed ships in this repo; the seams are how you build it.
145
201
 
146
202
  ## How it compares
147
203
 
148
- | | vx | Turborepo | Nx |
149
- | ------------------------- | --------------------------------------------------------------------- | ------------------------------ | ---------------- |
150
- | Fully cached, 100 pkgs¹ | **144 ms** | 279 ms | 583+ ms |
151
- | Config | TypeScript, evaluated into the cache key | JSON (static) | JSON (static) |
152
- | Output ownership | **Strict** — wiped before exec AND restore | Additive (stale files survive) | Additive |
153
- | Clean-tree hashing | **Zero reads** (git index OIDs) | git OIDs | re-hash / daemon |
154
- | Daemon required for speed | **No** | Optional | Yes |
155
- | Per-task sandbox | **Yes** — kernel-level, opt-in | No | No |
156
- | Provable cache safety | **Yes** — `--verify` (determinism) + `--verify=inputs` (completeness) | No | No |
157
- | MCP server for AI agents | **Yes** — `vx mcp` (stdio, reads your local cache) | No | No |
158
- | Plugin API | **Yes** — backend / cache / telemetry seams | No | Yes (TS-tied) |
159
- | Predictive scheduling | **Yes** (opt-in: `predictive: true`) | No | No |
160
- | OTel CI/CD spans | **Yes** — `otel()` plugin, zero OTel-SDK deps | No | Paid |
161
- | Self-hosted platform² | **Yes** — dashboard, distributed CI, remote cache; Docker compose | Vercel-only cache | Paid (Nx Cloud) |
162
- | Install | **Single binary** — npm or 1 curl line, no Node/Bun needed | npm + Node | npm + Node |
204
+ | | vx | Turborepo | Nx |
205
+ | ------------------------- | ---------------------------------------------------------- | ------------------------------ | ---------------- |
206
+ | Fully cached, 100 pkgs¹ | **144 ms** | 279 ms | 583+ ms |
207
+ | Config | TypeScript, evaluated into the cache key | JSON (static) | JSON (static) |
208
+ | Output ownership | **Strict** — wiped before exec AND restore | Additive (stale files survive) | Additive |
209
+ | Clean-tree hashing | **Zero reads** (git index OIDs) | git OIDs | re-hash / daemon |
210
+ | Daemon required for speed | **No** | Optional | Yes |
211
+ | Per-task sandbox | **Yes** — kernel-level, opt-in | No | No |
212
+ | Plugin API | **Yes** — executor / cache / telemetry seams | No | Yes (TS-tied) |
213
+ | OTel CI/CD spans | **Yes** — `otel()` plugin, zero OTel-SDK deps | No | Paid |
214
+ | Install | **Single binary** — npm or 1 curl line, no Node/Bun needed | npm + Node | npm + Node |
163
215
 
164
216
  ¹ Wall-clock, direct binaries, same machine and workspace — full
165
217
  methodology and more scenarios in
166
- [`docs/benchmarks.md`](docs/benchmarks.md). ² Optional, self-hosted,
167
- OSS — see [vx Cloud](#vx-cloud).
218
+ [`packages/vx/docs/benchmarks.md`](packages/vx/docs/benchmarks.md).
168
219
 
169
220
  ## Switching from another runner
170
221
 
@@ -212,51 +263,9 @@ Differences to know:
212
263
  - Persistent tasks: `persistent: { readyWhen: 'regex' }` (Turbo uses just `persistent: true`).
213
264
  - Remote caching is a plugin, not a built-in — connect one and every `vx run` reads through it.
214
265
 
215
- Side-by-side feature matrix + every known gap: [`docs/comparison.md`](./docs/comparison.md).
216
-
217
- ## vx Cloud
218
-
219
- An **optional, self-hosted CI platform** — the first-party service you
220
- run yourself. It is a fully independent app (accounts, roles,
221
- organizations, teams, multiple workspaces), with **Postgres** as the
222
- system of record and an **S3-compatible bucket** for artifacts; the
223
- controller keeps zero artifact bytes at rest. Core `vx` never depends
224
- on it — it connects to a deployment through the plugin seams above.
225
-
226
- What a connected platform adds:
227
-
228
- - **Dashboard.** Runs, flamegraphs, a live run cockpit with the task
229
- DAG, per-task logs + artifacts, cache-key diffs ("why did this
230
- re-run?"), and flaky-task detection with the concrete fix.
231
- - **Remote cache.** The vx-native `/v1/cache` wire, tenant-partitioned
232
- per org/workspace, with **trusted / untrusted tiers derived from the
233
- token** — a fork-PR artifact can never feed a trusted build — and an
234
- always-on integrity digest the client verifies.
235
- - **Distributed CI (DTE).** Agents form a session-keyed pool; a
236
- `vx run` fans tasks out across same-commit agents, outputs propagate
237
- through the shared artifact store, and a standing pool multiplexes
238
- concurrent runs with fair scheduling.
239
- - **MCP over the platform.** `POST /mcp` (Postgres-backed, behind the
240
- token) exposes the analytics as typed tools for AI agents — alongside
241
- the core `vx mcp` stdio server that reads your local cache.
242
-
243
- Deploy the stack (app + Postgres + object storage) with Docker
244
- Compose, then connect any workspace with one URL and a minted token:
245
-
246
- ```sh
247
- # Deploy — open the URL, register the first admin, mint a CI token under Admin → Tokens:
248
- VX_CLOUD_SECRET=$(openssl rand -hex 32) \
249
- docker compose -f packages/cloud/deploy/docker-compose.yml up
250
-
251
- # Connect a workspace (remote cache + analytics + distribution):
252
- vx-cloud connect https://ci.acme.dev --token vxc_…
253
- # …or set VX_CLOUD_URL + VX_CLOUD_TOKEN in the environment.
254
- ```
255
-
256
- Install the platform CLI with `npm i -g @vzn/vx-cloud` (a standalone
257
- binary, or the `ghcr.io/vznjs/vx-cloud` image). Full setup, RBAC,
258
- distributed CI, and the wire protocol live in the **vx Cloud** section
259
- of the [documentation site](https://vznjs.github.io/vx/).
266
+ Side-by-side feature matrix + every known gap: [`packages/vx/docs/comparison.md`](packages/vx/docs/comparison.md).
267
+ What a Turbo or Nx user relies on, spelled in vx and pinned by a test:
268
+ [`packages/vx/docs/parity.md`](packages/vx/docs/parity.md).
260
269
 
261
270
  ## Architecture (one paragraph)
262
271
 
@@ -275,58 +284,60 @@ versioned telemetry contract (`TelemetryRecord` / `RunSummaryRecord`)
275
284
  them. Core never imports a plugin; the arrow only points plugin → core.
276
285
  Every module has a docs page; every interface is a swappable seam.
277
286
 
278
- Read [`docs/architecture.md`](./docs/architecture.md) for the module
279
- map; the design record lives under [`docs/design/`](./docs/design/).
287
+ Read [`packages/vx/docs/architecture.md`](packages/vx/docs/architecture.md) for the module
288
+ map; the design record lives under [`packages/vx/docs/design/`](packages/vx/docs/design/).
280
289
 
281
290
  ## Documentation
282
291
 
283
- Full technical docs live under [`docs/`](./docs/) and on the
292
+ Full technical docs live under [`packages/vx/docs/`](packages/vx/docs/) and on the
284
293
  [documentation site](https://vznjs.github.io/vx/):
285
294
 
286
- - [`docs/architecture.md`](./docs/architecture.md) — module map + data flow
287
- - [`docs/schema.md`](./docs/schema.md) — every config field
288
- - [`docs/caching.md`](./docs/caching.md) — cache-key derivation + invalidation table
289
- - [`docs/execution.md`](./docs/execution.md) — `vx run` lifecycle
290
- - [`docs/cli.md`](./docs/cli.md) — every flag
291
- - [`docs/comparison.md`](./docs/comparison.md) — Turbo / Nx / vite-task feature matrix
292
- - [`docs/modules/`](./docs/modules/) — one reference page per source module
293
- - The **vx Cloud** section of the [docs site](https://vznjs.github.io/vx/) — deploy, RBAC, dashboard, distributed CI, wire protocol
295
+ - [`packages/vx/docs/architecture.md`](packages/vx/docs/architecture.md) — module map + data flow
296
+ - [`packages/vx/docs/schema.md`](packages/vx/docs/schema.md) — every config field
297
+ - [`packages/vx/docs/caching.md`](packages/vx/docs/caching.md) — cache-key derivation + invalidation table
298
+ - [`packages/vx/docs/execution.md`](packages/vx/docs/execution.md) — `vx run` lifecycle
299
+ - [`packages/vx/docs/cli.md`](packages/vx/docs/cli.md) — every flag
300
+ - [`packages/vx/docs/comparison.md`](packages/vx/docs/comparison.md) — Turbo / Nx / vite-task feature matrix
301
+ - [`packages/vx/docs/modules/`](packages/vx/docs/modules/) — one reference page per source module
294
302
 
295
- The full design record (including the platform-pivot notes) lives
296
- under [`docs/design/`](./docs/design/).
303
+ The design record lives under [`packages/vx/docs/design/`](packages/vx/docs/design/); the
304
+ maintainers' handoff is [`packages/vx/docs/STATUS.md`](packages/vx/docs/STATUS.md).
297
305
 
298
306
  ## Status
299
307
 
300
308
  **Pre-alpha.** The schema is settling; we bump `CACHE_VERSION` rather
301
- than maintain back-compat. **1,600+ tests (core + packages); CI green
302
- on every commit**; the project dogfoods itself (`vx run ci`).
303
- Published on npm: [`@vzn/vx`](https://www.npmjs.com/package/@vzn/vx)
304
- (a prebuilt standalone binary) and `@vzn/vx-cloud`.
309
+ than maintain back-compat. **~2,500 core tests plus the package suites; CI green on every commit**;
310
+ the project dogfoods itself (`vx run ci`). Published on npm:
311
+ [`@vzn/vx`](https://www.npmjs.com/package/@vzn/vx) (a prebuilt standalone
312
+ binary).
305
313
 
306
314
  Production readiness for the **core task runner**: the semantics are
307
- solid; it is dogfooded continuously. The main operational rough edge
308
- is Windows (unsupported).
309
-
310
- | Surface | Maturity | Notes |
311
- | ---------------------------------------------- | ------------------------------ | ----------------------------------------------------------------- |
312
- | Core task runner + caching | **production-ready** | dogfooded continuously; 1,200+ core tests, all green |
313
- | `vx run --verify` (provable cache correctness) | **shippable** | determinism + input-completeness proofs; CI-gate recipe in docs |
314
- | `vx mcp` | **shippable** | live cache.db tools over stdio |
315
- | Plugin API (backend / cache / telemetry) | **shippable** | crash-isolated; the cloud + OTel plugins are ordinary plugins |
316
- | Predictive scheduling | **shippable as opt-in** | gated on `predictive: true` + observed data |
317
- | OTel export (`@vzn/vx-otel`) | **shippable** | declare `otel()` in `vx.workspace.ts`; OTLP traces + metrics |
318
- | vx Cloud platform (self-hosted) | **shippable for self-hosting** | accounts/RBAC/orgs on Postgres, S3 artifacts, dashboard, DTE, MCP |
315
+ solid; it is dogfooded continuously. Linux and macOS, x64 and arm64;
316
+ Windows runs vx under WSL (POSIX shell is the API), with no native
317
+ build.
318
+
319
+ | Surface | Maturity | Notes |
320
+ | -------------------------------------------------- | -------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------- |
321
+ | Core task runner + caching | **production-ready** | dogfooded continuously; ~2,500 core tests + the package suites, green |
322
+ | Plugin pipeline (9 hooks, `commands` included) | **shippable** | crash-isolated, re-validated; the local executor + cache are the floor under every plugin |
323
+ | `vx init` / `@vzn/vx-migrate` (scripts; Turbo, Nx) | **shippable** | one config per package, TODOs where a source cannot say |
324
+ | REAPI remote cache + execution (`@vzn/vx-reapi`) | **shippable** | Bazel AC + CAS + Execute; NativeLink / BuildBuddy / Buildbarn / bazel-remote |
325
+ | OTel export (`@vzn/vx-otel`) | **shippable** | OTLP traces + metrics + logs, zero SDK deps |
326
+ | GitHub Actions (`@vzn/vx-github`) | **shippable** | job summary + Checks API run |
327
+ | MCP server (`@vzn/vx-mcp`) | **shippable** | `vx mcp` — read-only tools for AI agents, no SDK |
328
+ | Adoption (`@vzn/vx-migrate`) | **shippable** | `turbo()`: a `turbo.json` workspace runs with no `vx.config` written; `turboCache()` / `nxCache()`: any `/v8/artifacts` or Nx `/v1/cache` server; the migrate CLI |
329
+ | Lockfile keys (`@vzn/vx-lockfile`) | **shippable** | `pnpm()` `bun()` `npm()` `yarn()`: per-project dependency-closure keys; `--affected` follows |
319
330
 
320
331
  ## Development
321
332
 
322
333
  ```sh
323
334
  git clone https://github.com/vznjs/vx && cd vx
324
335
  bun install
325
- bun src/bin.ts run ci # format-check + lint + test
326
- bun src/bin.ts run build # cross-target binaries → dist/
336
+ bun packages/vx/src/bin.ts run ci --all # lint + test + docs build, every package
337
+ bun packages/vx/src/bin.ts run build --filter @vzn/vx # cross-target binaries → packages/vx/dist/
327
338
  ```
328
339
 
329
- vx is self-hosted: every dev task routes through `bun src/bin.ts run <task>` per the repo's own `vx.config.ts`. No `package.json` scripts; CI invokes vx directly.
340
+ vx is self-hosted: every dev task routes through `bun packages/vx/src/bin.ts run <task>` per each package's own `vx.config.ts`. No `package.json` scripts; CI invokes vx directly. Start with [`packages/vx/docs/STATUS.md`](packages/vx/docs/STATUS.md) — the living handoff — and `CLAUDE.md`.
330
341
 
331
342
  ## License
332
343
 
package/index.ts ADDED
@@ -0,0 +1,10 @@
1
+ // Root entry shim. Bun 1.4.0's `--compile` binaries resolve an on-disk
2
+ // package by directory convention (`<pkg>/index.ts`, `<pkg>/<subpath>/index.ts`)
3
+ // and ignore package.json `exports` / `main` (measured 2026-09-03: an
4
+ // entry of `./src/index.ts` resolved to the root `index.ts` regardless of the
5
+ // manifest, and a package with no root file was "not found"). Without this
6
+ // file every `import … from '@vzn/vx'` in a workspace config fails under the
7
+ // standalone binary. The exports map still wins everywhere else and points
8
+ // at the same module, so this is one extra hop for the binary and nothing
9
+ // for anyone else.
10
+ export * from './src/index.js'
package/launcher.mjs CHANGED
@@ -1,12 +1,12 @@
1
1
  #!/usr/bin/env node
2
2
  // npm launcher for a vx command — the entry a published package's `bin` points
3
3
  // at. It execs the prebuilt standalone binary shipped as a per-platform
4
- // optionalDependency, so end users get the command WITHOUT installing Bun. ONE
5
- // launcher serves both `@vzn/vx` and `@vzn/vx-cloud`: everything is derived from
6
- // this package's OWN name, read from the package.json sitting beside this file.
4
+ // optionalDependency, so end users get the command WITHOUT installing Bun.
5
+ // Everything is derived from this package's OWN name, read from the
6
+ // package.json sitting beside this file, so any package built by
7
+ // scripts/build-npm.ts can carry it:
7
8
  //
8
- // @vzn/vx → platform pkg @vzn/vx-<key>, binary `vx`
9
- // @vzn/vx-cloud → platform pkg @vzn/vx-cloud-<key>, binary `vx-cloud`
9
+ // @vzn/vx → platform pkg @vzn/vx-<key>, binary `vx`
10
10
  //
11
11
  // Resolution order:
12
12
  // 1. the matching <name>-<platform> optionalDependency's binary (the normal
@@ -26,11 +26,10 @@ const require = createRequire(import.meta.url)
26
26
  const here = dirname(fileURLToPath(import.meta.url))
27
27
  const pkg = require('./package.json')
28
28
 
29
- // Derive the platform-package prefix + binary basename from this package's name
30
- // so the same launcher works for @vzn/vx and @vzn/vx-cloud. `base` is the
31
- // unscoped name (vx / vx-cloud) — the command AND the binary filename inside the
32
- // platform package. `vxSourceEntry` (a package.json field) is the source-mode
33
- // entry: `src/bin.ts` for vx, `src/cli/bin.ts` for vx-cloud.
29
+ // Derive the platform-package prefix + binary basename from this package's
30
+ // name. `base` is the unscoped name — the command AND the binary filename
31
+ // inside the platform package. `vxSourceEntry` (a package.json field) is the
32
+ // source-mode entry, `src/bin.ts` by default.
34
33
  const name = pkg.name
35
34
  const base = name.replace(/^@[^/]+\//, '')
36
35
  const sourceEntry = pkg.vxSourceEntry ?? 'src/bin.ts'
@@ -86,7 +85,7 @@ process.stderr.write(
86
85
  `${base}: no prebuilt binary for ${key}.\n` +
87
86
  ` Supported platforms: ${supported}.\n` +
88
87
  ` If your platform should be supported, reinstall so npm fetches the\n` +
89
- ` matching ${name}-${key} optionalDependency, or install Bun (>=1.3) to\n` +
88
+ ` matching ${name}-${key} optionalDependency, or install Bun (>=1.4) to\n` +
90
89
  ` run ${base} from source.\n`,
91
90
  )
92
91
  process.exit(1)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vzn/vx",
3
- "version": "0.0.16",
3
+ "version": "0.0.21",
4
4
  "description": "An open, extensible monorepo task runner.",
5
5
  "type": "module",
6
6
  "exports": {
@@ -17,21 +17,25 @@
17
17
  "node": ">=18"
18
18
  },
19
19
  "optionalDependencies": {
20
- "@vzn/vx-linux-x64": "0.0.16",
21
- "@vzn/vx-linux-arm64": "0.0.16",
22
- "@vzn/vx-darwin-x64": "0.0.16",
23
- "@vzn/vx-darwin-arm64": "0.0.16"
20
+ "@vzn/vx-linux-x64": "0.0.21",
21
+ "@vzn/vx-linux-arm64": "0.0.21",
22
+ "@vzn/vx-darwin-x64": "0.0.21",
23
+ "@vzn/vx-darwin-arm64": "0.0.21"
24
24
  },
25
25
  "dependencies": {
26
- "@anthropic-ai/sandbox-runtime": "^0.0.56"
26
+ "@anthropic-ai/sandbox-runtime": "^0.0.75"
27
27
  },
28
28
  "files": [
29
+ "index.ts",
29
30
  "src",
30
31
  "launcher.mjs",
31
32
  "README.md",
32
33
  "LICENSE"
33
34
  ],
34
- "repository": "https://github.com/vznjs/vx",
35
+ "repository": {
36
+ "type": "git",
37
+ "url": "git+https://github.com/vznjs/vx.git"
38
+ },
35
39
  "homepage": "https://github.com/vznjs/vx#readme",
36
40
  "bugs": "https://github.com/vznjs/vx/issues",
37
41
  "license": "MIT",
package/src/bin.ts CHANGED
@@ -1,6 +1,12 @@
1
1
  #!/usr/bin/env bun
2
- import { run } from './cli/index.js'
3
- import { UserError } from './util/index.js'
2
+ import { registerCoreAlias, run } from './cli/index.js'
3
+ import { isUserError } from './util/index.js'
4
+
5
+ // Every `import … from '@vzn/vx'` this process evaluates — a plugin
6
+ // package, a workspace or project config — resolves to THIS core, not to
7
+ // a second copy from node_modules (core-alias.ts says why and what it
8
+ // costs). Registered before any verb runs; the façade loads on first use.
9
+ registerCoreAlias(() => import('./index.js') as Promise<Record<string, unknown>>)
4
10
 
5
11
  // Wrapped in an explicit async main so `bun build --compile` accepts
6
12
  // the file. The compile target doesn't allow top-level await.
@@ -12,8 +18,12 @@ async function main(): Promise<void> {
12
18
  // print the message only; the stack is noise the user can't act
13
19
  // on. Everything else gets the full stack so internal bugs are
14
20
  // debuggable.
15
- if (err instanceof UserError) {
16
- process.stderr.write(`vx: ${err.message}\n`)
21
+ if (isUserError(err)) {
22
+ // A message that already names the tool (`vx why: …`, thrown by a verb
23
+ // that wants its own name in the line) is printed as it is; prefixing
24
+ // it produced `vx: vx why: …` (walkthrough, 2026-09-04).
25
+ const m = err.message
26
+ process.stderr.write(m.startsWith('vx ') ? `${m}\n` : `vx: ${m}\n`)
17
27
  } else {
18
28
  const message = err instanceof Error ? (err.stack ?? err.message) : String(err)
19
29
  process.stderr.write(`vx: ${message}\n`)