* feat(cli): add `wmill flow dev` subcommand with per-flow reverse proxy and launch.json Also generates .claude/launch.json for existing flow folders during `wmill init`. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * feat: responsive dev layout and hide splitter for single-pane views Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * fix: clamp flow graph height between minHeight and maxHeight Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * feat(cli): enhance app new with Claude Desktop integration and better defaults - Add .claude/launch.json to generated app scaffold for Claude Code preview support - Add "Open in Claude Desktop?" prompt that creates a CLI session and opens it in Claude Desktop Code mode via the claude://resume deep link - Improve default CSS template with body background, system fonts, and padding Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(cli): handle both .flow and __flow suffixes in wmill dev The flow detection in loadPaths only checked the configured suffix (dotted or non-dotted), so users with nonDottedPaths=true who had .flow folders (or vice versa) would see inline script edits treated as standalone script changes instead of flow changes. Now checks both suffix forms everywhere: type classification, folder path extraction, path stripping, and loadWmPath lookup. Also adds raw_app launch.json generation to init and sync pull. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * docs(cli): update generated skills with dev workflow and preview commands Update cli-commands, write-flow, and raw-app skills to document the new local dev workflow (wmill dev --path, --proxy-port, .claude/launch.json). Add wmill script preview and wmill flow preview to all script/flow skills so agents know how to test without deploying. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(cli): include path in dev URL and use open.default for browser - Append &path= to the printed/opened URL when --path is specified - Use open.default(url) instead of open.openApp for more reliable browser opening Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * feat(cli): add Claude CLI/Desktop detection hints in wmill flow new Show contextual instructions for previewing flows based on available tools. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * chore: regenerate auto-generated CLI skills for new dev flags Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * fix(cli): handle mixed flow suffixes in dev file watcher The ignore() function uses isFlowPath() which only checks the configured suffix (__flow or .flow), causing files in the other variant to be silently ignored. Bypass the ignore check for any file inside a flow folder and force flow type detection regardless of suffix configuration. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * feat(cli): drop default proxy in flow folders, open browser, add --no-browser Manual `wmill dev` in a flow folder should not implicitly enable the reverse proxy. Both proxy and legacy modes now open the browser; the new --no-browser flag opts out. Claude Code launch.json templates pass --no-browser so the IDE preview doesn't fight a system browser window. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * feat(cli): gate dev broadcasts by --path and push currentLastEdit on connect When --path (or auto-detected flow path) is set, drop file events for any other path so the dev page stays locked to the requested resource and currentLastEdit can never reflect an unrelated edit. The connection handler proactively pushes currentLastEdit so the page renders without waiting for the first file change. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(frontend): prefer WebSocket for flow round-trip when wmill dev is connected updateFlow used isInIframe priority, which routed Claude Code's iframe preview through postMessage (no listener) and silently dropped flow edits. Flip the priority: when the wmill dev WebSocket is open, use it (covers standalone tabs and Claude Code's preview); fall back to postMessage only when no WS is connected (the VS Code extension's iframe URL has no `local=true`, so it never opens one). Also stop assigning lastSent before a channel actually accepted the message, so a CONNECTING WS doesn't silently swallow the first change. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * feat(dev): url is source of truth for path; add workspace file picker Drops the server-side --path gate added in3c2d5155e1. The dev page now filters by its URL's ?path= and the CLI is a dumb broadcaster, which lets multiple tabs each watch different paths. When the URL has no ?path=, the page asks the CLI for a list of workspace items (flows, scripts, raw_apps) via a new {type:'listPaths'} WS message and renders a picker. Clicking a flow or script soft-updates the URL via history.pushState and loads it; raw_apps surface a hint to use `wmill app dev` since they don't render here. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * feat(dev): picker uses homepage tree view with summaries Replace the hand-rolled Button-list picker with a TreeView-style layout that mirrors the Windmill homepage: folder/user tree grouping via `groupItems`, item rows rendered through the shared `Row.svelte` (no actions, no favourites, no link — just the visual), a `SearchItems` fuzzy filter with the same search input styling and placeholder as the homepage, and `group-open:` chevron toggling on native <details>. The CLI's listWorkspacePaths now also reads summaries from each item's metadata (flow.yaml for flows, <script>.script.yaml for scripts) in parallel so the picker shows summaries as the primary row label, same as the homepage. Raw apps have no standard manifest so they show the path only. Additional polish: title shows "<workspace> (local)" instead of generic text, subtitle trimmed, item-wrapper owns the border-b so Row's internal last:border-b-0 doesn't zero it out, summary border gated on group-open: to avoid doubled lines when a folder is collapsed. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(cli): wmill dev --no-browser was a no-op Cliffy's `.option("--no-browser", ...)` creates an option named `browser` (boolean, default undefined) that becomes `false` when the flag is passed. The previous code checked `opts.noBrowser`, which Cliffy never populates, so the guard silently no-op'd and the browser always opened. Rename to `browser` and check `=== false` explicitly, matching the `wmill app dev --no-open` convention. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * feat(dev): picker warns when wmill dev server is unreachable Track WS state in Dev.svelte (connecting/open/closed) — 'closed' is set on either the WS error or close event. When closed, the picker replaces the toggle + search + tree with a warning Alert telling the user to run `wmill dev` from the workspace root. Toggle and search are hidden rather than rendered disabled because there's nothing to filter anyway. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * refactor(cli): rename wmill dev --no-browser to --no-open Match the pre-existing `wmill app dev --no-open` flag. Having `--no-browser` on one dev command and `--no-open` on the other was just an oversight from my earlier change. All three launch.json templates (init, flow new, sync pull) switch to `--no-open`. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * feat(cli): wmill init creates root .claude/launch.json for the picker Adds a workspace-root .claude/launch.json so Claude Code can launch `wmill dev` from the project root and land on the file picker (no --path → picker mode). Per-flow and per-raw_app launch.json files are already generated by the existing scans. Skipped (with a gray log) if the file already exists, so the user's customizations are preserved. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * feat(cli): add skipClaudeAssets wmill.yaml flag When `skipClaudeAssets: true` is set in wmill.yaml, all generators that previously wrote Claude-specific assets become no-ops: - writeAiGuidanceFiles skips CLAUDE.md and .claude/skills/ (AGENTS.md is still written — vendor-neutral) - wmill init skips the root .claude/launch.json + per-flow + per-raw_app launch.json scans - wmill sync pull skips the per-flow + per-raw_app launch.json scans - wmill flow new skips the new flow's .claude/launch.json - wmill app new skips the new raw_app's .claude/ folder + launch.json The flag is added to SyncOptions, DEFAULT_SYNC_OPTIONS, and the generated wmill.yaml template (commented out — opt-in). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * feat(cli): wmill init removes existing Claude assets when skipClaudeAssets is set Re-running `wmill init` with `skipClaudeAssets: true` now removes previously-generated Claude assets so the workspace state matches the config. Narrow scope, no confirmation: - per-flow / per-raw_app .claude/launch.json (each parent .claude/ collapsed if empty) - root .claude/launch.json - .claude/skills/ (wholly ours; safe to remove the subtree) - root .claude/ collapsed if empty - CLAUDE.md only if its content matches the default ("Instructions are in @AGENTS.md\n"); otherwise left in place with a note Each removal is logged in yellow under a single gray intro line that prints lazily on the first removal — a clean tree stays silent. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(cli): wmill workspace add browser open silently no-ops `open.openApp(open.apps.browser, { arguments: [url] })` resolves its Promise even when the OS-level launch does nothing, so the CLI prints "Opened browser for you" but no tab appears. Same pattern was already fixed in `dev.ts` by commit3272c29c2e— use `open.default(url)`, which delegates to the native URL opener (`open` on macOS, `xdg-open` on Linux, `start` on Windows) and actually rejects on failure. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(cli): wmill init workspace prompt no longer duplicates active profile name Cliffy's Select.prompt renders `default: X` as `(X)` next to the question header, which duplicates whichever workspace name the default points to. Drop `default` and instead reorder the list so the active profile is first (cursor-preselected by virtue of position) and append "— active" to its label so the indicator lives where it's contextually relevant. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * docs(skills): expand preview-vs-run guidance for write-flow + all write-script-* skills Both `wmill flow preview/run` and `wmill script preview/run` have the same intent split — preview hits the local file, run hits the deployed version, sync push deploys. The skills' "after writing" sections used to terse-list the commands and just say "do not run them yourself", which encouraged the wrong reflex of `sync push` + `run` to "test". Rewrite the section in both `system_prompts/base/flow-base.md` (drives write-flow) and the `script_cli_intro` block in `system_prompts/generate.py` (drives all write-script-<lang>) to: - explicitly list `preview` as the default for local iteration, - spell out the few cases when `run` or `sync push` are appropriate, - offer to test as a one-sentence next step (no multi-option menus), - mark `preview` as safe to run autonomously. Regenerate auto-generated/ + cli/src/guidance/skills.ts. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * refactor(cli): wmill dev — clearer mode names and accurate startup messaging - Rename `startLegacyServer` to `startDirectServer`. "Legacy" implied it was on the way out; the two modes (proxy vs direct WS) actually serve different topologies and both stay. Add comments above each section spelling out who they're for: proxy mode for embedders that require a localhost origin (Claude Code preview), direct mode for standalone browser tabs and the VS Code extension iframe. - Replace the stale "Dev server will automatically point to the last script edited locally" log line. Now print path-aware text: - with --path (or auto-detected): "Watching <path> — edits will live -reload in the dev page" - without: "Open the dev page and pick a flow or script to preview — edits will live-reload" plus a hint about --path Mirror the same in proxy mode after the listen callback. - Drop the redundant "Go to <url>" line when --no-open isn't passed (maybeOpenBrowser already prints "Opened browser at <url>"). - Rename "Server listening on port 3001" to "Dev WebSocket listening on ws://localhost:<port>/ws" so the line's purpose is obvious. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * refactor(cli): drop per-folder .claude/launch.json generation Stop creating `.claude/launch.json` inside every flow folder, raw_app folder, and at `wmill flow new`/`wmill app new` time. The workspace- root `.claude/launch.json` from `wmill init` stays — it's the picker entry point and the one place where the deterministic "click → preview" UX is high-value. Removed from: - `wmill init` — per-flow + per-raw_app scans - `wmill sync pull` — per-flow + per-raw_app scans (also drops the now-unused `node:fs` mkdirSync/writeFileSync import) - `wmill flow new` — bootstrap no longer scaffolds `.claude/` - `wmill app new` — same; also drops the `.claude/launch.json` lines from the post-create directory listing Skills already give the agent the right CLI commands, so per-folder launch.json was redundant context. Existing files in user projects keep working but won't be regenerated; `wmill init` with `skipClaudeAssets: true` cleans them up. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * feat(cli): wmill app new flags + tighten raw-app skill for AI agents `wmill app new` is interactive by default, which hangs forever when an AI agent tries to use it. Add flags so the wizard can be bypassed end-to-end: - `--summary <text>`, `--path <path>`, `--framework <react19|react18| svelte5|vue>` (required for non-interactive) - `--datatable <name>` (opt into the datatable wizard) - `--schema <name>` (creates schema with CREATE SCHEMA IF NOT EXISTS if it doesn't already exist; only valid with --datatable) - `--overwrite` (replace existing directory without prompting) - `--no-open-in-desktop` (suppress the Claude Desktop offer) Mode is auto-detected: providing any of --summary/--path/--framework puts the run into non-interactive mode where the datatable wizard, overwrite prompt, and Claude Desktop prompt all skip silently (or fail fast on conflict instead of waiting for stdin). Each provided flag is validated upfront with a clear error message. Skill side: rewrite `system_prompts/base/raw-app.md`'s "Creating a Raw App" section so the AI agent knows it should run the command itself with flags (not tell the user to run it interactively). Direct the agent to use `AskUserQuestion` with one bundled call to gather any missing summary/path/framework — refuse to invent values, refuse to default. Anti-patterns spelled out explicitly. AGENTS.md template (`cli/src/guidance/core.ts`) had a contradicting line ("MUST ask the user to run wmill app new in its terminal first") that was loaded eagerly into agent context and overrode the skill — replaced with the same agent-driven guidance, pointing to the raw-app skill for the full procedure. Regenerate auto-generated/ + cli/src/guidance/skills.ts. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * feat(cli): per-target preview launch.json + agent uses wmill flow new Refactor the agent's dev/preview workflow: - Drop root .claude/launch.json generation from `wmill init`. Sharing one generic entry across sessions caused preview collisions; agents now add per-target named entries (windmill: <wmill_path>) on demand. - New `preview` skill in system_prompts/base/preview.md. Branches on whether `mcp__Claude_Preview__*` MCP tools are available: with them, add a per-target launch.json entry pinning its own port + --proxy-port + --path + --no-open and invoke the MCP preview tool; without them, start `wmill dev --path <X> --no-open` directly and hand the URL the CLI prints to the user. Never touch launch.json in the direct case. - Agents must run `wmill flow new <path>` themselves to scaffold flows (folder + flow.yaml with the right suffix), parallel to the existing `wmill app new` rule. Missing path/summary trigger AskUserQuestion; no inventing values. - write-flow skill: 4-step Creating a Flow procedure that opens the visual preview *before* editing flow.yaml so the user watches the flow take shape via live reload. - `wmill flow new` always prints the `wmill dev --path <X>` preview hint; drop the Claude CLI/Desktop detection branches. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * docs(skills): open app preview before editing in raw-app skill Mirrors the flow skill's Step 3 — opening `wmill app dev` via the preview skill before touching App.tsx so the user watches the app take shape via live reload, instead of seeing the finished result at the end. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(dev): guard WS replaceFlow with lockChanges to prevent echo The postMessage handler at Dev.svelte:306-312 wraps replaceFlow with lockChanges = true (cleared 500 ms later) so the $effect on flowStore.val doesn't immediately re-serialize and re-send the freshly received payload. The WebSocket handler did not, so on the initial flow push (dev.ts:568-574 sends currentLastEdit on connect), the client would echo back to handleFlowRoundTrip, which runs the orphan-file scan. On content equality the write was a no-op, but the scan could still delete files the server did not list. Mirror the same lockChanges/timeout pattern in the WS replaceData handler. Apply to both flow and script paths for symmetry. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(cli): correct wmill dev description + gate broadcasts server-side Two related fixes: 1. The 'auto-pushes them to the remote workspace' wording in the wmill dev description was wrong — the command never deploys, it only broadcasts file changes over WS for live preview. Reworded to call this out explicitly and point at 'wmill sync push' for the deploy case. 2. Move the path filter out of the client (Dev.svelte:491-495) and into broadcastChanges. Earlier the filter was client-side with the comment 'server stays a dumb broadcaster' even though commit3c2d5155was titled 'gate dev broadcasts by --path'. Doing the compare server-side aligns the implementation with the commit narrative, cuts WS traffic when --path is set, and keeps the per-tab semantics for the picker (each picker tab still gets the full 'paths' listing on first connect). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(cli): drop dead launch.json cleanup, fix description, narrow orphan scope Three review fixes: 1. cleanupClaudeAssets removed both root and per-folder .claude/launch.json files that this CLI never generates anymore. Per the user's "feature hasn't been released yet" guidance, no migration is needed — drop the dead scan and the root rm. Also drop the now-unused nonDottedPaths argument (and its flowSuffix / rawAppSuffix locals). 2. The skipClaudeAssets description in template.ts listed .claude/launch.json among the assets it skips, but launch.json is no longer generated. Drop it from the description string. 3. The dev round-trip's orphan cleanup deleted any non-dot file in a flow folder that wasn't in extractedPaths — including README.md, fixtures, TODO.md, etc. Restrict the deletion to files whose extension is in a known inline-script set (.ts/.js/.py/.go/.sh/.sql/.ps1/.php/.rs/.java/.cs/.r/.graphql). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * refactor(cli, frontend): dedupe flow suffix helpers, use UI components Five small follow-ups from the PR review: 1. dev.ts already had stripFolderSuffix() but three callsites were reimplementing the same .flow/__flow if-else inline. Add an isFlowFolderName(name) helper next to it and replace the duplicates in startProxyServer's cwd check, the file-watcher localPath strip, and normalizeWmPath. 2. Dev.svelte:866 was a <div onclick> with two svelte-ignore comments for the missing a11y handlers. Replace with a real <button type="button"> — kills the warnings, no visual change. 3. Dev.svelte:1283 was a raw <input type="text"> for the module summary. Replace with the existing <TextInput> component (same one the picker search at :1010 uses), per frontend/CLAUDE.md. 4. Dev.svelte:197 typed relativePaths as any[]; tighten to the actual union (string | [number, string])[] — the python helper returns tuples, the typescript one returns strings. 5. app/new.ts:822 fired exec("open <deeplink>") with no callback, so an OS that refused the URL scheme silently failed and we still logged "Opened in Claude Desktop!". Move the success log inside an exec callback that surfaces the error and prints the deep link for manual opening. Plus a brief comment above parseWatchPath explaining its resync contract (initial load + popstate + explicit pickPath, no generic pushState listener). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(cli): regenerate auto-gen for dev description; drop apostrophe to satisfy parser generate.py:326 extracts .description() with the regex [^"\']+ which bails on either quote type. Commit ff3a8e4ebd's new description had an apostrophe inside double quotes ('wmill sync push'), so the parser saw no description at all and the auto-generated files dropped the line entirely — which is what check-freshness caught on origin/main. Quickest path to green CI: rephrase the description without the inner apostrophe, then regenerate. The generator's regex is the real bug but fixing it is out of scope here. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * feat(cli): seed app .claude/launch.json before opening Claude Desktop When the user accepts "Open in Claude Desktop?" in wmill app new, write a per-app .claude/launch.json (named "windmill: <appPath>") into the freshly-created app folder before the deep link fires. Entry runs 'wmill app dev --no-open --port ${PORT:-4001}' from the app folder (which is the cwd Claude Desktop opens with), so the user can hit play right away to launch the preview. Skip if .claude/launch.json already exists — never clobber user edits. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix toggles positions * fix(dev): gate picker mode on ?local= so VS Code iframe still renders content The VS Code extension iframe loads the dev page without ?path= and without ?local=true. After the picker rework, an empty watchPath flipped pickerMode on, so the page rendered the picker UI even though the extension was sending replaceScript / replaceFlow postMessages — leaving the user stuck on the picker forever. Picker mode only makes sense on the local dev page, where the wmill dev WebSocket can supply the workspace listing. Anywhere else (VS Code iframe, plain remote tab) the picker has no data source and no purpose. Add an isLocalDevPage check so the picker only shows when ?local=true is present. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * refactor(dev): mirror vscode extension's processFlowMessage round-trip Three changes that bring our wmill dev round-trip into lockstep with the windmill-vscode extension's processFlowMessage in src/extension.ts: 1. New cli/src/commands/dev/pathscript-restore.ts — verbatim port of the extension's src/utils/pathscript-restore.ts. Adds AI-agent tool walking that the previous local copy was missing (flows with PathScript-shaped tools weren't being preserved across round-trip). Header comment makes the cross-repo link explicit. 2. handleFlowRoundTrip rewritten to mirror processFlowMessage step- for-step: reads failure_module + preprocessor_module from the current flow.yaml, passes them to extractCurrentMapping, shares one pathAssigner across all extraction calls, extracts inline scripts from those special modules too, skips writing files whose content starts with !inline (treats as pointer directives), and only rewrites flow.yaml when the serialized YAML actually differs. 3. snapshotPathScripts / tagReplacedPathScripts callsites in loadPaths were passing the FlowFile wrapper instead of FlowFile.value — the helpers walk .modules / .failure_module / .preprocessor_module, which only exist on .value, so PathScript snapshots silently no-op'd on the file-watcher path. Pass .value at all four sites. Deliberate divergence from the extension: orphan-cleanup keeps the INLINE_SCRIPT_EXTS allow-list so README.md / fixtures aren't deleted. The extension's version still over-deletes; that's tracked separately. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * docs(skills): offer visual preview after create instead of auto-opening Both write-flow and raw-app skills used to instruct the agent to open the visual preview without asking right after wmill flow new / wmill app new, on the rationale that live reload is most useful when the page is already up. In practice this surprised users — opening the dev page has side effects (browser window pop, possibly a launch.json entry under MCP-preview Branch A) that warrant consent. Change Step 3 in both skills from "open it without asking" to "offer it as a one-sentence next step" — same pattern the same skills already use for programmatic wmill flow preview offers. Two then- necessary anti-patterns ("just open it", "open it before editing") are dropped along with the auto-open instruction. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(cli): probe both ip stacks before binding wmill dev proxy / app dev port Node's default listen() has platform-dependent dual-stack behaviour. If the requested port is already held on the IPv4 stack, listen() can silently fall back to binding IPv6-only ([::1]:N). The OS then routes new localhost connections to the older IPv4 listener, so the user opens http://localhost:N and sees a stale prior server with no signal that anything is wrong. Bit us in practice: a leftover wmill dev --proxy-port 4000 served traffic for a freshly-started wmill app dev --port 4000. New helper at cli/src/utils/port-probe.ts probes both 0.0.0.0 and :: before binding. On collision it walks upward to the next free port (up to +20) and logs a prominent warning naming the holder when lsof / ss can find it: Port 4000 is already in use (held by PID 91418 `bun`). Using port 4001 instead. Wired into: - wmill dev --proxy-port: the resolved port flows into both proxyServer.listen() and the &port=N parameter in the redirect URL, so they always match. Bind explicitly to 0.0.0.0. - wmill app dev --port: only when the user passed --port explicitly (the default getPort.default(...) path already handles fallback). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(dev): pass placeholder via TextInput inputProps not as top-level prop `<TextInput>`'s top-level Props don't include `placeholder` — native input attributes go through the `inputProps` field. The previous `<TextInput placeholder="Summary" .../>` failed `npm run check` with "Object literal may only specify known properties, and '\"placeholder\"' does not exist in type 'Props<\"input\">'.". Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * nit * fix(cli): sequential port probe + sync dev test regex with renamed log Two CI regressions on test-linux: 1. port-probe parallel race on Linux. isPortFreeOnBothStacks ran the IPv4 and IPv6 binds via Promise.all. On Linux the default is net.ipv6.bindv6only=0, so a bind(::, port) socket also takes the IPv4 stack on the same port. Concurrent v4 + v6 binds then race for v4 — one wins, the other gets EADDRINUSE on a port that is actually free. Walks 20 ports up, all fail the same way, throws, child exits. Tests that fetch http://localhost:port time out at 60s. Doesn't repro on macOS (bindv6only=1 by default — what I tested against). Probe sequentially so each bind fully releases before the next starts. 2. dev_server.test 1 regex out of sync. Commit018dc3861arenamed the startup log from "Server listening on port N" to "Dev WebSocket listening on ws://localhost:N/ws" but didn't update the test, which times out at 30s waiting for the old string. Update the regex to match the current log. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * Update system_prompts/auto-generated/skills/write-script-graphql/SKILL.md Co-authored-by: cubic-dev-ai[bot] <191113872+cubic-dev-ai[bot]@users.noreply.github.com> * fix(cli): address dev/app PR review — bugs 1-7 Per code review: 1. app new.ts — wrap claude --session-id exec in try/finally so the spinner setInterval is always cleared. On rejection control jumped to the outer catch and the spinner kept writing \r forever, garbling subsequent output. 2. app new.ts — make --overwrite actually wipe the dir before re-creating. Previously logged "Overwriting" but only skipped the prompt; leftover files from a different framework (e.g. App.tsx from a prior react18 install when re-scaffolding as svelte5) survived and produced a hybrid scaffold. 3. dev/dev.ts — anchor the flow-folder match on path segments. The substring checks (cpath.includes(".flow/") / "__flow/") also fired on names like notes_about__flow_design/readme.md. New isInsideFlowFolder + findFlowFolderPrefix split on "/" and check segment suffixes. Drops the now-unreachable script→flow fallback inside the else branch. 4. dev/dev.ts — direct mode also routes through resolveBindPort so it detects dual-stack collisions like the proxy mode does. Bare getPort only probes one stack, defeating the whole point of port-probe.ts. Also bind to BIND_HOST explicitly. Drops the unused getPort import. 5. dev/dev.ts — normalize opts.path once after mergeConfigWithConfigFile. broadcastChanges compared against a non-normalized opts.path, so --path f/foo/ or --path f/foo.flow silently dropped every broadcast. Also pulls normalizeWmPath to module scope (was a closure inside dev()). 6. dev/dev.ts — guard the initial-state ws.send with readyState === OPEN, matching the other branches' pattern. 7. dev/dev.ts — typo: "givena" → "given a". Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(cli): address dev/app PR review — items 8-10 8. dev/dev.ts — derive INLINE_SCRIPT_EXTS from exts so adding a new script language to script.ts auto-extends orphan cleanup. Previously .gql, .nu, .rb were missing — flows using those languages would leave orphaned inline files behind. Excludes .yml because user fixtures commonly use it in flow folders, and leaving a stale .playbook.yml inline script is preferable to deleting a fixture. Keeps .js for hand-written flows that aren't in the exts list. 9. app/new.ts — wrap Claude Desktop install probe + prompt in process.platform === "darwin". The probe (ls /Applications/Claude.app) and the open command both only work on macOS — the explicit guard makes the platform scope grep-able. 10. app/new.ts — switch the deep-link spawn from exec(`open ${shell- escaped url}`) to execFile("open", [deepLink]). sessionId is a UUID and absAppDir is URI-encoded today so the old form was safe, but execFile removes the shell entirely. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(cli,dev): cubic review — port-probe error semantics, pluralize spacing [2] cli/src/utils/port-probe.ts — distinguish IPv6-unsupported from port collision in isPortFree. Previously every error code returned false, including EAFNOSUPPORT / EADDRNOTAVAIL on the IPv6 probe when the host has no v6 stack at all (IPv4-only containers). resolveBindPort would then walk all 20 ports getting the same error and throw. Treat only EADDRINUSE / EACCES as "not free"; everything else as free. [13] cli/src/commands/app/dev.ts — only probe both stacks when binding to localhost. The dual-stack collision risk is specific to localhost (which resolves to 127.0.0.1 + ::1); for an explicit IPv4 host there's only one stack to worry about, so don't move the user's requested port over a phantom v6 collision. [14] frontend/src/lib/components/Dev.svelte — pluralize already inserts a space between quantity and word, so " item" produced "3 items". Drop the leading space in both call sites. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * docs(system_prompts): cubic review — preview args, skill scopes Source changes in base/ + generate.py, then regenerated auto-generated/ via python system_prompts/generate.py. Per cubic review: [4]+[7] generate.py — "pick plausible args from the `main` signature" was language-blind. SQL queries and Bash scripts use $1/$2 positional parameters, not a main(...) signature. Reword to call out both shapes explicitly so the wording survives across all 19 generated language skills (postgresql, bash, mysql, …) instead of just the ones that happen to have main(). [5] base/raw-app.md — the "CLI Commands" table said "Tell the user they can run these commands (do NOT run them yourself)" while the "Creating a Raw App" section above (added in this PR) tells the agent to run `wmill app new` itself. Carve `wmill app new` out of the table and add a one-line note pointing back to the create flow, so the guidance no longer self-contradicts. [10] base/preview.md — "These print a `Go to <url>` line on stdout" was wrong for `wmill app dev`, which prints "🚀 Dev server running at <url>". List both line shapes explicitly and suggest a loose http:// match for URL capture. [12] base/flow-base.md — "regenerate lock files for the flow you modified" misstated the default scope. `wmill generate-metadata` scans scripts, flows, and apps by default (see cli/src/commands/generate-metadata/generate-metadata.ts:71-73). Update wording to call out the default scope and how to narrow it. Also folds the cubic [1] graphql safety wording (originally a one-off edit on the auto-generated file ina895db7) back into generate.py itself, so it survives regeneration and applies to all language skills. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * docs(system_prompts): cubic round 2 — language-specific placeholder syntax Round 1 wording was too narrow: - "$1, $2 placeholders for SQL queries and Bash" was wrong for MySQL (`?`), Snowflake (`?`), MSSQL (`@P1`), BigQuery (`@name`), and PowerShell (which uses `param(...)`, not main()). - The preview-skill URL match said "first `http://...` token" — remote workspaces serve HTTPS, so the regex would miss them. Source-only fixes in generate.py and base/preview.md, then regenerated auto-generated/ via python system_prompts/generate.py. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(flow): track maxHeight in FlowGraphV2 height effect cubic [3]: updateHeight() reads both minHeight and maxHeight, but the $effect only tracked minHeight. Changing maxHeight alone (e.g. when a parent shrinks the cap during a layout transition) left height frozen at the previously clamped value. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * docs(system_prompts): tool-agnostic wording in base/ cubic [11]: system_prompts/README.md says these prompts must NOT contain tool usage instructions. Three base files violated this: - base/flow-base.md (4× AskUserQuestion). Worst offender — leaks into the frontend copilot via FLOW_BASE in prompts.ts (consumed by getFlowPrompt in frontend/src/lib/components/copilot/chat/flow/ core.ts:1287). Frontend has no AskUserQuestion tool, so the wording was both irrelevant and confusing there. - base/raw-app.md (5× AskUserQuestion + 1× mcp__Claude_Preview__). CLI-skill-only but covered by the same scope rule. - base/preview.md (5× mcp__Claude_Preview__). CLI-skill-only, same. Replaced with role descriptions: "ask the user (use a structured- question tool if your runtime has one)" and "a tool that can embed a localhost URL inside the IDE / chat surface". Kept one mention of mcp__Claude_Preview__ in preview.md as an illustrative example, since documentation of one runtime is fine — what's not fine is gating behaviour on a specific tool name. Source-only edits, then regenerated auto-generated/ via python system_prompts/generate.py. Verification: grep -r AskUserQuestion system_prompts/auto-generated/ now returns nothing. The remaining AskUserQuestion refs in cli/src/guidance/core.ts are hand-written CLI-only AGENTS.md content (not part of system_prompts), and Claude Code does have that tool, so those are correctly scoped. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(flow): drop .no-splitter CSS hack, use snippets to skip Splitpanes cubic [8]: the previous fix for "top pane is empty in aiagent / noEditor mode" was a CSS rule that hid `:global(.splitpanes__splitter)` inside .no-splitter. That cascaded into nested splitpanes too — the aiagent left/right tabs panel (line 1043), the debug-console editor split (line 877), and the doubly-nested debug panel (line 1472) all lost their resize handles. Refactor the layout instead. Extract top-pane and bottom-pane content as snippets, then conditionally render either: - just the bottom snippet (no Splitpanes wrapper) when the top pane would be empty (aiagent or noEditor), or - the original two-Pane Splitpanes layout otherwise. This removes the splitter at its root rather than hiding it, so nested splitters are unaffected. The bottom Pane's complex bind:size getter/setter (which returned 100 when aiagent) collapses to a simple binding now that the aiagent path no longer goes through the wrapping Pane at all. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * nit * fix(flow,preview): cubic round 3 — FlowPathViewer regression + preview skill rewrite [3149192182] FlowModuleComponent.svelte: my last refactor's "aiagent || noEditor" condition stripped the FlowPathViewer for noEditor + type === 'flow', because the top-pane snippet was no longer rendered. The flow-viewer pane is the only thing that *does* show in that mode, so it shouldn't have been collapsed. Tighten the condition to "aiagent || (noEditor && type !== 'flow')". [3149060930] system_prompts/base/preview.md: Branch A detection was too broad — "can embed or open a localhost URL" is strictly weaker than "can read .claude/launch.json and launch a configuration". Only the Claude Desktop / Code MCP integration does the latter; most embedders only do the former. Restructure preview.md around two orthogonal axes: 1. Mode (proxy vs direct) — driven by "does the embedder need a localhost URL?". Direct is the default; proxy is for embedders that sandbox cross-origin loads. 2. Who starts the server — you spawn `wmill dev` yourself, OR a launch.json-aware runtime (currently only the `mcp__Claude_Preview__*` MCP family) launches it on demand. The two compose into four common cases (regular browser tab, generic preview pane, localhost-only preview pane, Claude MCP), each with a clear instruction. The launch.json/MCP machinery is now scoped to a single section gated on actually having that tool in your tool list. Source-only edit in base/preview.md, then regenerated auto-generated/ via python system_prompts/generate.py. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * Fix dev step display * nit --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Co-authored-by: cubic-dev-ai[bot] <191113872+cubic-dev-ai[bot]@users.noreply.github.com>
31 KiB
Windmill CLI Commands
The Windmill CLI (wmill) provides commands for managing scripts, flows, apps, and other resources.
Global Options
--workspace <workspace:string>- Specify the target workspace. This overrides the default workspace.--debug --verbose- Show debug/verbose logs--show-diffs- Show diff informations when syncing (may show sensitive informations)--token <token:string>- Specify an API token. This will override any stored token.--base-url <baseUrl:string>- Specify the base URL of the API. If used, --token and --workspace are required and no local remote/workspace already set will be used.--config-dir <configDir:string>- Specify a custom config directory. Overrides WMILL_CONFIG_DIR environment variable and default ~/.config location.
Commands
app
app related commands
Options:
--json- Output as JSON (for piping to jq)
Subcommands:
app list- list all apps--json- Output as JSON (for piping to jq)
app get <path:string>- get an app's details--json- Output as JSON (for piping to jq)
app push <file_path:string> <remote_path:string>- push a local appapp dev [app_folder:string]- Start a development server for building apps with live reload and hot module replacement--port <port:number>- Port to run the dev server on (will find next available port if occupied)--host <host:string>- Host to bind the dev server to--entry <entry:string>- Entry point file (default: index.ts for Svelte/Vue, index.tsx otherwise)--no-open- Don't automatically open the browser
app lint [app_folder:string]- Lint a raw app folder to validate structure and buildability--fix- Attempt to fix common issues (not implemented yet)
app new- create a new raw app from a template--summary <summary:string>- App summary (short description). Skips the prompt when provided. Triggers non-interactive mode.--path <path:string>- App path (e.g., f/folder/my_app or u/username/my_app). Skips the prompt when provided. Triggers non-interactive mode.--framework <framework:string>- Framework template: react19 | react18 | svelte5 | vue. Skips the prompt when provided. Triggers non-interactive mode.--datatable <datatable:string>- Datatable to wire up. Without this flag in non-interactive mode, no datatable is configured.--schema <schema:string>- Schema to use with --datatable. Created (CREATE SCHEMA IF NOT EXISTS) if it doesn't already exist.--overwrite- Overwrite the target directory if it already exists, without prompting.--no-open-in-desktop- Do not prompt to open the new app in Claude Desktop.
app generate-agents [app_folder:string]- regenerate AGENTS.md and DATATABLES.md from remote workspaceapp set-permissioned-as <path:string> <email:string>- Set the on_behalf_of_email for an app (requires admin or wm_deployers group)
audit
View audit logs (requires admin)
Subcommands:
audit list- List audit log entriesaudit get <id:string>- Get a specific audit log entry--json- Output as JSON (for piping to jq)
config
Show all available wmill.yaml configuration options
Options:
--json- Output as JSON for programmatic consumption
Subcommands:
config migrate- Migrate wmill.yaml from gitBranches/environments to workspaces format
dependencies
workspace dependencies related commands
Alias: deps
Subcommands:
dependencies push <file_path:string>- Push workspace dependencies from a local file
dev
Watch local file changes and live-reload the dev page for preview. Does NOT deploy to the remote workspace — use wmill sync push for that.
Options:
--includes <pattern...:string>- Filter paths given a glob pattern or path--proxy-port <port:number>- Port for a localhost reverse proxy to the remote Windmill server--path <path:string>- Watch a specific windmill path (e.g., u/admin/my_script or f/my_flow)--no-open- Do not open the browser automatically
docs
Search Windmill documentation.
Arguments: <query:string>
Options:
--json- Output results as JSON.
flow
flow related commands
Options:
--show-archived- Enable archived flows in output--json- Output as JSON (for piping to jq)
Subcommands:
flow list- list all flows--show-archived- Enable archived flows in output--json- Output as JSON (for piping to jq)
flow get <path:string>- get a flow's details--json- Output as JSON (for piping to jq)
flow push <file_path:string> <remote_path:string>- push a local flow spec. This overrides any remote versions.--message <message:string>- Deployment message
flow run <path:string>- run a flow by path.-d --data <data:string>- Inputs specified as a JSON string or a file using @ or stdin using @-.-s --silent- Do not ouput anything other then the final output. Useful for scripting.
flow preview <flow_path:string>- preview a local flow without deploying it. Runs the flow definition from local files and uses local PathScripts by default.-d --data <data:string>- Inputs specified as a JSON string or a file using @ or stdin using @-.-s --silent- Do not output anything other then the final output. Useful for scripting.--remote- Use deployed workspace scripts for PathScript steps instead of local files.
flow new <flow_path:string>- create a new empty flow--summary <summary:string>- flow summary--description <description:string>- flow description
flow bootstrap <flow_path:string>- create a new empty flow (alias for new)--summary <summary:string>- flow summary--description <description:string>- flow description
flow history <path:string>- Show version history for a flow--json- Output as JSON (for piping to jq)
flow show-version <path:string> <version:string>- Show a specific version of a flow--json- Output as JSON (for piping to jq)
flow set-permissioned-as <path:string> <email:string>- Set the on_behalf_of_email for a flow (requires admin or wm_deployers group)
folder
folder related commands
Options:
--json- Output as JSON (for piping to jq)
Subcommands:
folder list- list all folders--json- Output as JSON (for piping to jq)
folder get <name:string>- get a folder's details--json- Output as JSON (for piping to jq)
folder new <name:string>- create a new folder locally--summary <summary:string>- folder summary
folder push <name:string>- push a local folder to the remote by name. This overrides any remote versions.folder add-missing- create default folder.meta.yaml for all subdirectories of f/ that are missing one-y, --yes- skip confirmation prompt
folder show-rules <name:string>- Show default_permissioned_as rules for a folder. Use --test-path to see which rule matches a given item path.--test-path <path:string>- Test which rule matches this item path (e.g. f/prod/jobs/my_script)--json- Output as JSON
generate-metadata
Generate metadata (locks, schemas) for all scripts, flows, and apps
Arguments: [folder:string]
Options:
--yes- Skip confirmation prompt--dry-run- Show what would be updated without making changes--lock-only- Re-generate only the lock files--schema-only- Re-generate only script schemas (skips flows and apps)--skip-scripts- Skip processing scripts--skip-flows- Skip processing flows--skip-apps- Skip processing apps--strict-folder-boundaries- Only update items inside the specified folder (requires folder argument)-i --includes <patterns:file[]>- Comma separated patterns to specify which files to include-e --excludes <patterns:file[]>- Comma separated patterns to specify which files to exclude
gitsync-settings
Manage git-sync settings between local wmill.yaml and Windmill backend
Subcommands:
gitsync-settings pull- Pull git-sync settings from Windmill backend to local wmill.yaml--repository <repo:string>- Specify repository path (e.g., u/user/repo)--default- Write settings to top-level defaults instead of overrides--replace- Replace existing settings (non-interactive mode)--override- Add branch-specific override (non-interactive mode)--diff- Show differences without applying changes--json-output- Output in JSON format--with-backend-settings <json:string>- Use provided JSON settings instead of querying backend (for testing)--yes- Skip interactive prompts and use default behavior--promotion <branch:string>- Use promotionOverrides from the specified branch instead of regular overrides
gitsync-settings push- Push git-sync settings from local wmill.yaml to Windmill backend--repository <repo:string>- Specify repository path (e.g., u/user/repo)--diff- Show what would be pushed without applying changes--json-output- Output in JSON format--with-backend-settings <json:string>- Use provided JSON settings instead of querying backend (for testing)--yes- Skip interactive prompts and use default behavior--promotion <branch:string>- Use promotionOverrides from the specified branch instead of regular overrides
group
Manage workspace groups
Options:
--json- Output as JSON (for piping to jq)
Subcommands:
group list- List all groups in the workspace--json- Output as JSON (for piping to jq)
group get <name:string>- Get group details and members--json- Output as JSON (for piping to jq)
group create <name:string>- Create a new group--summary <summary:string>- Group summary/description
group delete <name:string>- Delete a groupgroup add-user <name:string> <username:string>- Add a user to a groupgroup remove-user <name:string> <username:string>- Remove a user from a group
hub
Hub related commands. EXPERIMENTAL. INTERNAL USE ONLY.
Subcommands:
hub pull- pull any supported definitions. EXPERIMENTAL.
init
Bootstrap a windmill project with a wmill.yaml file
Options:
--use-default- Use default settings without checking backend--use-backend- Use backend git-sync settings if available--repository <repo:string>- Specify repository path (e.g., u/user/repo) when using backend settings--bind-profile- Automatically bind active workspace profile to current Git branch--no-bind-profile- Skip workspace profile binding prompt
instance
sync local with a remote instance or the opposite (push or pull)
Subcommands:
instance add [instance_name:string] [remote:string] [token:string]- Add a new instanceinstance remove <instance:string:instance>- Remove an instanceinstance switch <instance:string:instance>- Switch the current instanceinstance pull- Pull instance settings, users, configs, instance groups and overwrite local--yes- Pull without needing confirmation--dry-run- Perform a dry run without making changes--skip-users- Skip pulling users--skip-settings- Skip pulling settings--skip-configs- Skip pulling configs (worker groups)--skip-groups- Skip pulling instance groups--include-workspaces- Also pull workspaces--folder-per-instance- Create a folder per instance--instance <instance:string>- Name of the instance to pull from, override the active instance--prefix <prefix:string>- Prefix of the local workspaces to pull, used to create the folders when using --include-workspaces--prefix-settings- Store instance yamls inside prefixed folders when using --prefix and --folder-per-instance
instance push- Push instance settings, users, configs, group and overwrite remote--yes- Push without needing confirmation--dry-run- Perform a dry run without making changes--skip-users- Skip pushing users--skip-settings- Skip pushing settings--skip-configs- Skip pushing configs (worker groups)--skip-groups- Skip pushing instance groups--include-workspaces- Also push workspaces--folder-per-instance- Create a folder per instance--instance <instance:string>- Name of the instance to push to, override the active instance--prefix <prefix:string>- Prefix of the local workspaces folders to push--prefix-settings- Store instance yamls inside prefixed folders when using --prefix and --folder-per-instance
instance whoami- Display information about the currently logged-in userinstance get-config- Dump the current instance config (global settings + worker configs) as YAML-o, --output-file <file:string>- Write YAML to a file instead of stdout--show-secrets- Include sensitive fields (license key, JWT secret) without prompting--instance <instance:string>- Name of the instance, override the active instance
instance connect-slack--bot-token <bot_token:string>- Slack bot token (xoxb-...)--team-id <team_id:string>- Slack team id--team-name <team_name:string>- Slack team name--instance <instance:string>- Instance profile to connect against (defaults to the active instance)
job
Manage jobs (list, inspect, cancel)
Subcommands:
job list- List recent jobsjob get <id:string>- Get job details. For flows: shows step tree with sub-job IDs--json- Output as JSON (for piping to jq)
job result <id:string>- Get the result of a completed job (machine-friendly)job logs <id:string>- Get job logs. For flows: aggregates all step logsjob cancel <id:string>- Cancel a running or queued job--reason <reason:string>- Reason for cancellation
jobs
Pull completed and queued jobs from workspace
Arguments: [workspace:string]
Options:
-c, --completed-output <file:string>- Completed jobs output file (default: completed_jobs.json)-q, --queued-output <file:string>- Queued jobs output file (default: queued_jobs.json)--skip-worker-check- Skip checking for active workers before export
Subcommands:
jobs pulljobs push
lint
Validate Windmill flow, schedule, and trigger YAML files in a directory
Arguments: [directory:string]
Options:
--json- Output results in JSON format--fail-on-warn- Exit with code 1 when warnings are emitted--locks-required- Fail if scripts or flow inline scripts that need locks have no locks-w, --watch- Watch for file changes and re-lint automatically
queues
List all queues with their metrics
Arguments: [workspace:string] the optional workspace to filter by (default to all workspaces)
Options:
--instance [instance]- Name of the instance to push to, override the active instance--base-url [baseUrl]- If used with --token, will be used as the base url for the instance
resource
resource related commands
Options:
--json- Output as JSON (for piping to jq)
Subcommands:
resource list- list all resources--json- Output as JSON (for piping to jq)
resource get <path:string>- get a resource's details--json- Output as JSON (for piping to jq)
resource new <path:string>- create a new resource locallyresource push <file_path:string> <remote_path:string>- push a local resource spec. This overrides any remote versions.
resource-type
resource type related commands
Options:
--json- Output as JSON (for piping to jq)
Subcommands:
resource-type list- list all resource types--schema- Show schema in the output--json- Output as JSON (for piping to jq)
resource-type get <path:string>- get a resource type's details--json- Output as JSON (for piping to jq)
resource-type new <name:string>- create a new resource type locallyresource-type push <file_path:string> <name:string>- push a local resource spec. This overrides any remote versions.resource-type generate-namespace- Create a TypeScript definition file with the RT namespace generated from the resource types
schedule
schedule related commands
Options:
--json- Output as JSON (for piping to jq)
Subcommands:
schedule list- list all schedules--json- Output as JSON (for piping to jq)
schedule get <path:string>- get a schedule's details--json- Output as JSON (for piping to jq)
schedule new <path:string>- create a new schedule locallyschedule push <file_path:string> <remote_path:string>- push a local schedule spec. This overrides any remote versions.schedule enable <path:string>- Enable a scheduleschedule disable <path:string>- Disable a scheduleschedule set-permissioned-as <path:string> <email:string>- Set the email (run-as user) for a schedule (requires admin or wm_deployers group)
script
script related commands
Options:
--show-archived- Show archived scripts instead of active ones--json- Output as JSON (for piping to jq)
Subcommands:
script list- list all scripts--show-archived- Show archived scripts instead of active ones--json- Output as JSON (for piping to jq)
script push <path:file>- push a local script spec. This overrides any remote versions. Use the script file (.ts, .js, .py, .sh)--message <message:string>- Deployment message
script get <path:file>- get a script's details--json- Output as JSON (for piping to jq)
script show <path:file>- show a script's content (alias for get)script run <path:file>- run a script by path-d --data <data:file>- Inputs specified as a JSON string or a file using @ or stdin using @-.-s --silent- Do not output anything other then the final output. Useful for scripting.
script preview <path:file>- preview a local script without deploying it. Supports both regular and codebase scripts.-d --data <data:file>- Inputs specified as a JSON string or a file using @ or stdin using @-.-s --silent- Do not output anything other than the final output. Useful for scripting.
script new <path:file> <language:string>- create a new script--summary <summary:string>- script summary--description <description:string>- script description
script bootstrap <path:file> <language:string>- create a new script (alias for new)--summary <summary:string>- script summary--description <description:string>- script description
script set-permissioned-as <path:string> <email:string>- Set the on_behalf_of_email for a script (requires admin or wm_deployers group)script history <path:string>- show version history for a script--json- Output as JSON (for piping to jq)
sync
sync local with a remote workspaces or the opposite (push or pull)
Subcommands:
sync pull- Pull any remote changes and apply them locally.--yes- Pull without needing confirmation--dry-run- Show changes that would be pulled without actually pushing--plain-secrets- Pull secrets as plain text--json- Use JSON instead of YAML--skip-variables- Skip syncing variables (including secrets)--skip-secrets- Skip syncing only secrets variables--include-secrets- Include secrets in sync (overrides skipSecrets in wmill.yaml)--skip-resources- Skip syncing resources--skip-resource-types- Skip syncing resource types--skip-scripts- Skip syncing scripts--skip-flows- Skip syncing flows--skip-apps- Skip syncing apps--skip-folders- Skip syncing folders--skip-workspace-dependencies- Skip syncing workspace dependencies--skip-scripts-metadata- Skip syncing scripts metadata, focus solely on logic--include-schedules- Include syncing schedules--include-triggers- Include syncing triggers--include-users- Include syncing users--include-groups- Include syncing groups--include-settings- Include syncing workspace settings--include-key- Include workspace encryption key--skip-branch-validation- Skip git branch validation and prompts--json-output- Output results in JSON format-i --includes <patterns:file[]>- Comma separated patterns to specify which file to take into account (among files that are compatible with windmill). Patterns can include * (any string until '/') and ** (any string). Overrides wmill.yaml includes-e --excludes <patterns:file[]>- Comma separated patterns to specify which file to NOT take into account. Overrides wmill.yaml excludes--extra-includes <patterns:file[]>- Comma separated patterns to specify which file to take into account (among files that are compatible with windmill). Patterns can include * (any string until '/') and ** (any string). Useful to still take wmill.yaml into account and act as a second pattern to satisfy--repository <repo:string>- Specify repository path (e.g., u/user/repo) when multiple repositories exist--promotion <branch:string>- Use promotionOverrides from the specified branch instead of regular overrides--branch, --env <branch:string>- [Deprecated: use --workspace] Override the current git branch/environment
sync push- Push any local changes and apply them remotely.--yes- Push without needing confirmation--dry-run- Show changes that would be pushed without actually pushing--plain-secrets- Push secrets as plain text--json- Use JSON instead of YAML--skip-variables- Skip syncing variables (including secrets)--skip-secrets- Skip syncing only secrets variables--include-secrets- Include secrets in sync (overrides skipSecrets in wmill.yaml)--skip-resources- Skip syncing resources--skip-resource-types- Skip syncing resource types--skip-scripts- Skip syncing scripts--skip-flows- Skip syncing flows--skip-apps- Skip syncing apps--skip-folders- Skip syncing folders--skip-workspace-dependencies- Skip syncing workspace dependencies--skip-scripts-metadata- Skip syncing scripts metadata, focus solely on logic--include-schedules- Include syncing schedules--include-triggers- Include syncing triggers--include-users- Include syncing users--include-groups- Include syncing groups--include-settings- Include syncing workspace settings--include-key- Include workspace encryption key--skip-branch-validation- Skip git branch validation and prompts--json-output- Output results in JSON format-i --includes <patterns:file[]>- Comma separated patterns to specify which file to take into account (among files that are compatible with windmill). Patterns can include * (any string until '/') and ** (any string)-e --excludes <patterns:file[]>- Comma separated patterns to specify which file to NOT take into account.--extra-includes <patterns:file[]>- Comma separated patterns to specify which file to take into account (among files that are compatible with windmill). Patterns can include * (any string until '/') and ** (any string). Useful to still take wmill.yaml into account and act as a second pattern to satisfy--message <message:string>- Include a message that will be added to all scripts/flows/apps updated during this push--parallel <number>- Number of changes to process in parallel--repository <repo:string>- Specify repository path (e.g., u/user/repo) when multiple repositories exist--branch, --env <branch:string>- [Deprecated: use --workspace] Override the current git branch/environment--lint- Run lint validation before pushing--locks-required- Fail if scripts or flow inline scripts that need locks have no locks--auto-metadata- Automatically regenerate stale metadata (locks and schemas) before pushing--accept-overriding-permissioned-as-with-self- Accept that items with a different permissioned_as will be updated with your own user
token
Manage API tokens
Options:
--json- Output as JSON (for piping to jq)
Subcommands:
token list- List API tokens--json- Output as JSON (for piping to jq)
token create- Create a new API token--label <label:string>- Token label--expiration <expiration:string>- Token expiration (ISO 8601 timestamp)
token delete <token_prefix:string>- Delete a token by its prefix
trigger
trigger related commands
Options:
--json- Output as JSON (for piping to jq)
Subcommands:
trigger list- list all triggers--json- Output as JSON (for piping to jq)
trigger get <path:string>- get a trigger's details--json- Output as JSON (for piping to jq)--kind <kind:string>- Trigger kind (http, websocket, kafka, nats, postgres, mqtt, sqs, gcp, azure, email). Recommended for faster lookup
trigger new <path:string>- create a new trigger locally--kind <kind:string>- Trigger kind (required: http, websocket, kafka, nats, postgres, mqtt, sqs, gcp, azure, email)
trigger push <file_path:string> <remote_path:string>- push a local trigger spec. This overrides any remote versions.trigger set-permissioned-as <path:string> <email:string>- Set the email (run-as user) for a trigger (requires admin or wm_deployers group)--kind <kind:string>- Trigger kind (required: http, websocket, kafka, nats, postgres, mqtt, sqs, gcp, azure, email)
user
user related commands
Subcommands:
user add <email:string> [password:string]- Create a user--superadmin- Specify to make the new user superadmin.--company <company:string>- Specify to set the company of the new user.--name <name:string>- Specify to set the name of the new user.
user remove <email:string>- Delete a useruser create-token- Create a new API token for the authenticated user--email <email:string>- Specify credentials to use for authentication. This will not be stored. It will only be used to exchange for a token with the API server, which will not be stored either.--password <password:string>- Specify credentials to use for authentication. This will not be stored. It will only be used to exchange for a token with the API server, which will not be stored either.
variable
variable related commands
Options:
--json- Output as JSON (for piping to jq)
Subcommands:
variable list- list all variables--json- Output as JSON (for piping to jq)
variable get <path:string>- get a variable's details--json- Output as JSON (for piping to jq)
variable new <path:string>- create a new variable locallyvariable push <file_path:string> <remote_path:string>- Push a local variable spec. This overrides any remote versions.--plain-secrets- Push secrets as plain text
variable add <value:string> <remote_path:string>- Create a new variable on the remote. This will update the variable if it already exists.--plain-secrets- Push secrets as plain text--public- Legacy option, use --plain-secrets instead
version
Show version information
worker-groups
display worker groups, pull and push worker groups configs
Subcommands:
worker-groups pull- Pull worker groups (similar towmill instance pull --skip-users --skip-settings --skip-groups)--instance- Name of the instance to push to, override the active instance--base-url- Base url to be passed to the instance settings instead of the local one--yes- Pull without needing confirmation
worker-groups push- Push worker groups (similar towmill instance push --skip-users --skip-settings --skip-groups)--instance [instance]- Name of the instance to push to, override the active instance--base-url [baseUrl]- If used with --token, will be used as the base url for the instance--yes- Push without needing confirmation
workers
List all workers grouped by worker groups
Options:
--instance [instance]- Name of the instance to push to, override the active instance--base-url [baseUrl]- If used with --token, will be used as the base url for the instance
workspace
workspace related commands
Alias: profile
Subcommands:
workspace switch <workspace_name:string:workspace>- Switch to another workspaceworkspace add [workspace_name:string] [workspace_id:string] [remote:string]- Add a workspace-c --create- Create the workspace if it does not exist--create-workspace-name <workspace_name:string>- Specify the workspace name. Ignored if --create is not specified or the workspace already exists. Will default to the workspace id.--create-username <username:string>- Specify your own username in the newly created workspace. Ignored if --create is not specified, the workspace already exists or automatic username creation is enabled on the instance.
workspace remove <workspace_name:string>- Remove a workspaceworkspace whoami- Show the currently active userworkspace list- List local workspace profilesworkspace list-remote- List workspaces on the remote server that you have access toworkspace list-forks- List forked workspaces on the remote serverworkspace bind- Create or update a workspace entry in wmill.yaml from the active profile--workspace <name:string>- Workspace name (default: current branch or workspaceId)--branch <branch:string>- Git branch to associate (default: workspace name)
workspace unbind- Remove baseUrl and workspaceId from a workspace entry--workspace <name:string>- Workspace to unbind
workspace fork [workspace_name:string] [workspace_id:string]- Create a forked workspace--create-workspace-name <workspace_name:string>- Specify the workspace name. Ignored if --create is not specified or the workspace already exists. Will default to the workspace id.--color <color:string>- Workspace color (hex code, e.g. #ff0000)--datatable-behavior <behavior:string>- How to handle datatables: skip, schema_only, or schema_and_data (default: interactive prompt)-y --yes- Skip interactive prompts (defaults datatable behavior to 'skip')
workspace delete-fork <fork_name:string>- Delete a forked workspace and git branch-y --yes- Skip confirmation prompt
workspace merge- Compare and deploy changes between a fork and its parent workspace--direction <direction:string>- Deploy direction: to-parent or to-fork--all- Deploy all changed items including conflicts--skip-conflicts- Skip items modified in both workspaces--include <items:string>- Comma-separated kind:path items to include (e.g. script:f/test/main,flow:f/my/flow)--exclude <items:string>- Comma-separated kind:path items to exclude--preserve-on-behalf-of- Preserve original on_behalf_of/permissioned_as values-y --yes- Non-interactive mode (deploy without prompts)
workspace connect-slack- Non-interactively connect Slack to the active workspace using a pre-minted bot token (xoxb-...). Produces the same artifacts as the UI OAuth flow: workspace_settings fields, g/slack group, f/slack_bot folder, and the encrypted bot token variable + resource at f/slack_bot/bot_token.--bot-token <bot_token:string>- Slack bot token (xoxb-...)--team-id <team_id:string>- Slack team id--team-name <team_name:string>- Slack team name
workspace disconnect-slack