Repository navigation
Description
Summary
vp run resolves a lot before it executes anything: which packages the selection
picks, each package's task, the commands after compound splitting, the dependsOn
edges and their order, and whether a task would replay from cache. None of it can be
asked for. A --plan flag would print that resolution and exit, in text or JSON, the
way pip install --dry-run --report -, make -n, turbo run --dry=json and
bazel aquery do for their runs.
Motivation
Anything built around a task runner eventually needs to know what a run would do:
- a pre-tool hook for coding agents that decides whether a shell command is about to
start something expensive (a type checker, a browser suite) before letting it run; - CI that picks jobs from the task graph instead of from path globs;
- a repository doctor or lint rule that checks task wiring ("does this
dependsOn
reach the task it means to?", "which packages drop out of-rfor this task?"); - a person or an agent asking "what does
vp run -r testactually run, in what order?"
Today the only route is to load every vite.config.* yourself and re-implement the
selection rules, the dependsOn forms (task, pkg#task, { task, from }), compound
splitting and ordering, then keep that copy in step with Vite+. The existing surfaces
do not cover it: -v and --last-details describe a run after it ran, the interactive
selector is for a person choosing one task, and loadConfigFromFile resolves one
config with no selection, no edges and no cache status.
Prior art
pip install --dry-run --report -: resolve, print the JSON report, install nothing.make -n/--dry-run: print the commands that would run.just --dry-run,gradle --dry-run: list the recipes / tasks that would execute.turbo run <task> --dry/--dry=json: per task,taskId,package,command,
hash,inputs,outputs,dependencies,dependents, cache status.bazel aquery: the actions (commands) a build would execute.nx run-many -t <task> --graph=stdout: the task graph the command would execute.
Proposal
vp run --plan [--json] <the usual selection: -r | -t | -w | --filter … | [pkg#]task> [--ignore-depends-on]Same resolution as a real run, then print and exit 0; execute nothing, write no cache.
Text output: one line per task in execution order, package task: command, with
← dep markers for edges. JSON output:
{
"selection": { "packages": ["@acme/app", "@acme/core"], "task": "build" },
"tasks": [
{
"id": "@acme/core#build",
"package": "@acme/core",
"task": "build",
"commands": ["tsdown", "node scripts/emit-types.mjs"],
"dependsOn": [],
"cache": "replay"
},
{
"id": "@acme/app#build",
"package": "@acme/app",
"task": "build",
"commands": ["vp build"],
"dependsOn": [{ "id": "@acme/core#build", "kind": "topological" }],
"cache": "execute"
}
]
}commandsare the sub-tasks after compound splitting (&&or array form), since
those are what Vite Task runs and caches.dependsOnentries carrykind:explicit(adependsOnentry) ortopological
(package-graph order), because the two are what a wiring check needs to tell apart.cacheisreplay,executeordisabledwhen the plan already knows it; if input
hashing is too costly to do just to answer--plan,unknownis fine and an opt-in
--plan=hashcan come later.
Non-goals: not a visualisation (#1182 asks for that; --plan --json is an input it
could consume), and not a change to --last-details.
How we hit it
In a 128-package workspace we wrote an agent hook that needed one bit: does this
vp run … start a TypeScript compiler? Answering it faithfully meant loading all 128
configs (about 1.8 s per call) and carrying ~500 lines that re-implemented selection
and dependsOn. We replaced that with a hard-coded list of task names, which is exactly
what --plan would retire; a lint rule in the same repo reads run.tasks out of the
config AST for the same reason. Happy to test a preview against that workspace.
Related
- Dependency graph visualization for monorepos (like nx graph) #1182 (dependency graph visualisation) and Consolidated output for recursive commands (like nx run-many) #1167 (consolidated recursive output)
need the same resolved list;--planis its static, machine-readable form. - Print hint when config load exceeds 500ms #1231 (hint when config load exceeds 500 ms) is the same gap from the timing side.
dependsOnobject form cannot reach a task behind a package that lacks it; the query can vite-task#738 (dependsOnobject form cannot reach a task behind a
package that lacks it) is the kind of wiring fact a plan with labelled edges shows
directly.
Metadata
Metadata
Assignees
Labels
Type
Fields
Priority
Start date
Target date
Effort
Activity
chore(deps): update rust crates (#1780)
jasonkuhrt commentedon Nov 4, 2026
This is now covered by the dry-run RFC in voidzero-dev/vite-task#933. Our notes on the CI case are in voidzero-dev/vite-task#933 (comment).
docs(rfc): JSON task graph, and dry-run plans with cache status (#933)