Files
windmill/cli
Ruben Fiszel e3f43033ca fix(cli): macro-library parity in --local pipeline graph + read-only run --dry-run (#9942)
* fix(cli): surface macro libraries in --local pipeline graph + make run --dry-run read-only

* fix(cli): resolve workspace-wide macro libraries in --local graph (shared libs outside the pipeline folder)

* fix(cli): macro-lib consumers + //-prefix parity in --local pipeline graph

Address Codex review P1s: (1) macro libraries that consume another library's
macros now produce lib->lib edges (any folder DuckDB script is a consumer, not
just // pipeline members) so an upstream provider node no longer disappears;
(2) parseMacroAnnotations accepts //, --, and # prefixes like the backend, so a
.duckdb.sql library headed with // macros is detected locally. Both edge
endpoints are forced into the node set. Verified byte-for-byte against deployed.

* fix(cli): exclude non-pipeline macro-consumer nodes from --local run selection

Address Codex P1: buildMacroEdges surfaces macro-consumer nodes (a DuckDB script
calling a macro but not marked // pipeline) for lineage display. Those have no
local file, so pipeline run --local must not treat them as manual roots — a
dry-run listed them and a real run failed resolving local content. Exclude any
--local graph node absent from localScripts (the previewable set) from starts and
selection, alongside the existing macro-library exclusion.

* fix(cli): reject display-only macro consumers in explicit --from (post-merge with #9945)

The mid-DAG --from feature (#9945, now on main) admits any autorun-able script
via validFromStarts/fromEligible, which was filtered only by macroLibPaths. A
non-// pipeline macro-consumer helper (a --local display node) therefore passed
--from eligibility and produced an empty plan. Filter fromEligible by the broader
notRunnablePaths too, and reject such a --from with a clear message instead of a
silent empty plan.

* chore(cli): remove NUL edge-key separator + refresh stale macro comments

Address Codex P2 nits: (1) the macro edge map packed (lib, consumer) into a
string with a literal NUL separator, which made localGraph.ts read as a binary
file to grep/rg — replace with a nested lib->consumer Map (no separator); (2)
comments claiming macro nodes/edges are 'deployed graph only' contradicted this
PR's local derivation — describe the code as it is.

* fix(cli): tag unused // pipeline + // macros libraries so --local run excludes them

Address Codex P1: the deployed builder sets 'macros' on any node whose path
provides macros (edge or not), so a // pipeline + // macros script with no
consumers is still recognized as definition-only. Local enrichment only tagged
edge providers, leaving an unused pipeline macro library as a bare runnable that
pipeline run --local would schedule as a manual root. Also tag any library whose
path is already a runnable; unused non-pipeline libraries stay suppressed.

* fix(pipelines): `// macros` takes precedence over `// pipeline` (a library is never a member)

A macro library is definition-only — its macros are injected into consumers and
running it is a no-op — so marking it `// pipeline` is meaningless and only
produced a confusing state (an unused pipeline macro library appearing as a
manual root). Make `// macros` win: parse_pipeline_annotations forces in_pipeline
false when macros is set. Mirrored in all three parsers that must agree — the Rust
canonical parser (drives deploy membership), the frontend TS parser (live graph),
and the CLI local graph (pinned wasm still reports in_pipeline, so precedence is
applied when skipping members). Shared parity fixture + unit tests on each side.

* docs(cli): trim narrative comment blocks to non-obvious constraints

Address Codex P2: duckdbMacros.ts opened with a ~19-line narrative block whose
parity rationale belongs in the PR description; reduce to the two real constraints
(keep in lockstep with duckdb_macros.rs; dynamic-SQL calls need // use). Per the
AGENTS.md comment policy.

* fix(cli): model macro libraries as pipeline members, matching the deployed graph

Reverts the parser-precedence approach (b398b69): the backend deliberately marks
EVERY macro library auto_kind='pipeline' (scripts.rs:1474, macro_lib_defs), so a
macro library IS a graph member — the // pipeline marker is redundant, not
authoritative. Precedence was a no-op on deploy while diverging the CLI/frontend.

Instead mirror reality in the CLI local graph: an in-folder // macros library is a
member node (in_pipeline=true, with signatures) whether used or not; its // use is
processed (it's a member) so a library that reaches another only via dynamic SQL
still gets the via_use lib->lib edge (fixes the missing-edge case); an out-of-folder
library referenced by an in-folder consumer is a non-member provider node. Macro
libraries stay excluded from runs (via macros) and from the previewable scripts set.

Verified byte-for-byte (incl. in_pipeline) against the deployed graph: unused
in-folder lib, lexical lib->lib chain, // use dynamic-SQL lib->lib, out-of-folder
shared lib.
2026-07-06 01:36:42 +02:00
..
2022-11-01 15:53:28 +01:00
2023-02-03 19:49:46 +01:00
2026-02-23 09:09:16 +00:00
2025-12-26 20:32:42 +00:00

Windmill CLI

A simple CLI allowing interactions with windmill from the command line.

You can find more information in Windmill Docs

Installation

Install the wmill CLI tool using npm install -g windmill-cli.

Update to the latest version using wmill upgrade.

Workspaces

To get started run wmill workspace add or use the instructions from the workspace settings.

Running Flows & Scripts

Run a script or flow using wmill flow/script run u/username/path/to/script and pass any inputs using --data + Inputs specified as a JSON string or a file using @ <filename> or stdin using @-.

Curl-style syntax using -d @- for stdin or -d @<filename> is also supported.

Flow Steps and Logs will be streamed during execution automatically.

CLI input example

Pushing Resources, Scripts & More

The CLI can push specifications to a windmill instance. See the examples/ folder for formats.

Switch to a different workspace

wmill workspace switch <workspace_name>

Sync a workspace

Pull

wmill sync pull

Push

wmill sync push

We recommend using the --yaml option to use yaml instead of json as the encoding format. Yaml will be made the default soon.

Pushing individual files

You can push individual resources using wmill <type> push <file_name> \<remote_name\>. This does not require a special folder layout or file name, as this is given at runtime.

Listing

All commands support listing by just not providing a subcommand, ie wmill script will result in a list of scripts. Some allow additional options, learn about this by specifying --help.

User Management

You can add & remove users via wmill user add/remove, and list them using wmill user

Pulling

You can pull the entire workspace using wmill pull

Completion

The CLI comes with completions out of the box via wmill completions <shell>. (Via cliffy)

Bash

To enable bash completions add the following line to your ~/.bashrc:

source <(wmill completions bash)

Fish

To enable fish completions add the following line to your ~/.config/fish/config.fish:

source (wmill completions fish | psub)

Zsh

To enable zsh completions add the following line to your ~/.zshrc:

source <(wmill completions zsh)

Development

AI Guidance Variants

wmill init can now materialize alternate AI guidance bundles without changing the generated defaults in the repo, but this is exposed as internal env-var overrides rather than public CLI flags.

Examples:

WMILL_INIT_AI_SKILLS_SOURCE=/path/to/custom/skills wmill init --use-default
WMILL_INIT_AI_SKILLS_SOURCE=/path/to/custom/skills WMILL_INIT_AI_AGENTS_SOURCE=/path/to/AGENTS.md wmill init --use-default
WMILL_INIT_AI_SKILLS_SOURCE=/path/to/custom/skills WMILL_INIT_AI_CLAUDE_SOURCE=/path/to/CLAUDE.md wmill init --use-default

This is the same guidance-writing path used by the benchmark CLI under ai_evals/, so the benchmark harness and wmill init now generate the same project guidance shape:

  • AGENTS.md
  • CLAUDE.md
  • .agents/skills/*
  • .claude/skills/*

Testing with a local windmill-yaml-validator

To test local changes to the validator before publishing, use npm link:

# In windmill-yaml-validator/
npm run build
npm link

# In cli/
npm link windmill-yaml-validator

Running Tests

Prerequisites:

  • PostgreSQL running locally (default: postgres://postgres:changeme@localhost:5432)
  • Rust toolchain installed

Run tests locally (full features):

bun test test/

Run tests in CI mode (minimal features, skips EE tests):

CI_MINIMAL_FEATURES=true bun test test/
Variable Description
CI_MINIMAL_FEATURES Set to true to skip EE-dependent tests
DATABASE_URL PostgreSQL connection string
EE_LICENSE_KEY Enterprise license key for EE features