Skip to main content

Informations de source

Dépôt
windmill-labs/windmill
Dernière activité de la source
5 octobre 2026 à 14:23
Langue détectée de SKILL.md
anglais
Étoiles
18 107
Forks
1 111

Options d'installation

Le prompt qui vérifie d'abord la source est sélectionné par défaut. Vous pouvez passer à une commande directe ou télécharger une copie locale.

Vérifiez les fichiers source

Lisez SKILL.md et les fichiers associés affichés par SkillsMP avant de décider de l'installer.

Affichage de SKILL.md

SKILL.md
Instructions source · Aperçu en lecture seule
name
write-flow
description
MUST use when creating flows.
# Windmill Flow CLI Guide ## Creating a Flow **You — the AI agent — scaffold the flow yourself by running `wmill flow new <path>` with the right flags. Do NOT hand-create the folder + `flow.yaml`, and do NOT tell the user to "run `wmill flow new` and follow the prompts".** `wmill flow new` creates the folder with the correct suffix (`__flow` or `.flow` depending on the workspace's `nonDottedPaths` setting), writes a minimal `flow.yaml` shell, and prints Claude-specific next-step hints. Scaffolding by hand skips all of that and often picks the wrong suffix. ### Step 1 — Gather path + summary by asking the user You need two things: 1. **path** — the windmill path, e.g. `f/folder/my_flow` or `u/username/my_flow`. 2. **summary** — a short description of the flow. If the user's request didn't supply both, ask for both in a single round-trip. Use whichever interactive question facility your runtime provides — a structured multi-choice tool if available, otherwise plain chat — and provide one or two example values for each (with an "Other" / free-form fallback). Do not guess paths or summaries. ### Step 2 — Run the command yourself ```bash wmill flow new f/folder/my_flow --summary "Short description" ``` Add `--description "..."` when the user provided a longer explanation worth preserving separately from the summary. ### Step 3 — Fill in `flow.yaml` Open the generated `flow.yaml` (under the folder the command just created) and replace the empty `value.modules` + `schema` with the real flow definition. For rawscript modules, use `!inline path/to/script.ts` for the content key. Inline script files should NOT include `.inline_script.` in their names (e.g. use `a.ts`, not `a.inline_script.ts`). Once the flow has real content, **offer** to open the visual preview as a one-sentence next step (e.g. "Want me to open the visual preview?"). Don't auto-open — opening the dev page has side effects (browser window, possibly a `launch.json` entry) and the user should consent. ### Anti-patterns to avoid - ❌ Hand-creating the `__flow` folder + `flow.yaml` instead of running `wmill flow new`. You'll miss the suffix-setting resolution, the default shape, and the Claude hints. - ❌ Telling the user to "run `wmill flow new <path>`" — you can and should run it yourself. - ❌ Inventing a path/summary instead of asking the user. ## CLI Commands — running, previewing, deploying After writing, act on the user's intent instead of just listing commands. Run `wmill flow preview` yourself when it fits (see "After writing — offer to run, don't wait passively" below). `wmill generate-metadata` regenerates local lock/hash files (not a deploy) but re-resolves deps — offer it and run on agreement, unless the project's `AGENTS.md` opts into running metadata automatically. Only *name* `wmill sync push` (the deploy) so the user can approve it. The options: - `wmill flow preview <flow_path>` — **default when iterating on a local flow.** Runs the local `flow.yaml` against local inline scripts without deploying. Add `--remote` to use deployed workspace scripts for PathScript steps instead of local files. Add `--step <step_id>` to run only one module in isolation (see "Single-step vs whole-flow preview" below). - `wmill flow run <path>` — runs the flow **already deployed** in the workspace. Use only when the user explicitly wants to test the deployed version, not local edits. - `wmill generate-metadata` — regenerate stale local `.lock` files for the flow and its inline scripts and refresh their content hashes in `wmill-lock.yaml`. Writes local files only (not a deploy). Run it after editing inline scripts whose imports or arguments changed, so `wmill-lock.yaml` doesn't drift and add noise to git-sync/CI. By default it scans **scripts, flows, and apps** across the workspace but only regenerates stale ones; pass the flow's folder as an argument (or run from that subdirectory) to limit the scope to the flow you edited. Note a flow (or script) that imports a changed shared script is pulled in too — run `wmill generate-metadata --dry-run` to see exactly what is stale and why (`content changed` vs `depends on <path>`) before applying. - Deploy local changes to the workspace — via `git push` or `wmill sync push` depending on how the repo is wired (see the **Deploying** section in `AGENTS.wmill.md`). Only suggest/run a deploy when the user explicitly asks to deploy/publish/push — not when they say "run", "try", or "test". ### Preview vs run — choose by intent, not habit If the user says "run the flow", "try it", "test it", "does it work" while there are **local edits to a `flow.yaml`**, use `flow preview`. Do NOT push the flow to then `flow run` it — pushing is a deploy, and deploying just to test overwrites the workspace version with untested changes. Only use `flow run` when: - The user explicitly says "run the deployed version" / "run what's on the server". - There is no local `flow.yaml` being edited (you're just invoking an existing flow). Only use `sync push` when: - The user explicitly asks to deploy, publish, push, or ship. - The preview has already validated the change and the user wants it in the workspace. ### Single-step vs whole-flow preview Use `flow preview <flow_path> --step <step_id>` when the user is iterating on one module and the flow's upstream steps aren't part of what they're trying to validate. It runs only that step's runnable (rawscript: the inline script; script: the PathScript, locally if available; flow: the subflow by path) and is much faster than running the whole flow when previous steps are slow or expensive. The step id is resolved by walking nested branchone/branchall/forloopflow/whileloopflow modules and includes the special `preprocessor` and `failure` modules. Use `flow preview <flow_path>` (no `--step`) when steps depend on each other's outputs, when the user is validating the overall control flow, or when `--step` doesn't apply (branchone, branchall, forloopflow, whileloopflow, identity, and AI agent steps cannot themselves be tested in isolation — for branchone/branchall/forloopflow/whileloopflow, the *contained* steps can, by passing the inner step's id). ### After writing — offer to run, don't wait passively This is about **programmatic execution** (`wmill flow preview -d '<args>'`), which actually runs the flow and has side effects. Visual preview (the `preview` skill) is offered separately — see "Visual preview" below. If the user hasn't already told you to run/test the flow, offer it as a one-sentence next step (e.g. "Want me to run `wmill flow preview` with sample args?"). Do not present a multi-option menu. If the user already asked to test/run/try the flow in their original request, skip the offer and just execute `wmill flow preview <path> -d '<args>'` directly — pick plausible args from the flow's input schema. An input typed as a resource (`format: resource-<type>` in the schema) takes the bare string `"$res:<path>"` as its whole value — `-d '{"db": "$res:f/databases/postgres_prod"}'`, not `{"db": {"$res": "..."}}` and not a plain path. Same for a variable, with `"$var:<path>"`. See the `resources` skill. `wmill flow preview` is safe to run yourself (it does not deploy). `wmill generate-metadata` does not deploy either (it only writes local lock/hash files) but re-resolves deps — offer it and run on agreement, unless the project's `AGENTS.md` opts into automatic metadata. After running it, check the regenerated `.lock` diff and tell the user which inline-script dependency versions changed, so they can catch an unwanted bump before deploying. Only `wmill sync push` deploys; run it only when the user explicitly asks. ### Visual preview To open the flow visually in the dev page (graph + live reload), use the `preview` skill. Always **offer** it as a one-sentence next step (e.g. "Want me to open the visual preview?") rather than opening it automatically — opening the dev page has side effects (browser window, possibly a `launch.json` entry under MCP-preview branches) the user should consent to. If the user already asked to see/preview/visualize the flow in their original request, skip the offer and just invoke the skill. # Windmill Flow Building Guide ## OpenFlow Schema The OpenFlow schema (openflow.openapi.yaml) is the source of truth for flow structure. Refer to OPENFLOW_SCHEMA for the complete type definitions. ## Reserved Module IDs - `failure` - Reserved for failure handler module - `preprocessor` - Reserved for preprocessor module - `Input` - Reserved for flow input reference ## Hard Structural Rules These are strict Windmill schema rules. Follow them exactly. - `value.modules` is only for normal sequential steps - `value.preprocessor_module` and `value.failure_module` are special top-level fields inside `value`, not entries in `value.modules` - If a flow needs a preprocessor, create `value.preprocessor_module` with `id: preprocessor` - If a flow needs a failure handler, create `value.failure_module` with `id: failure` - Do NOT create regular modules inside `value.modules` named `preprocessor` or `failure` - `preprocessor_module` and `failure_module` only support `script` or `rawscript` - `preprocessor_module` runs before normal modules and cannot reference `results.*` - `failure_module` can use the `error` object with `error.message`, `error.step_id`, `error.name`, and `error.stack` Correct shape: ```yaml value: preprocessor_module: id: preprocessor value: type: rawscript ... failure_module: id: failure value: type: rawscript ... modules: - id: process_event value: type: rawscript ... ``` Incorrect shape: ```yaml value: modules: - id: preprocessor ... - id: process_event ... - id: failure ... ``` ## Module ID Rules - Must be unique across the entire flow - Use underscores, not spaces (e.g., `fetch_data` not `fetch data`) - Use descriptive names that reflect the step's purpose ## AI Agent Modules An `aiagent` module runs an LLM that can call tools. Each entry of `value.tools` is a module-shaped object with an extra `value.tool_type`: `flowmodule` for a script/flow tool, `mcp` for an MCP server tool, `websearch` for web search. ```json { "id": "support_agent", "summary": "AI agent for customer support", "value": { "type": "aiagent", "input_transforms": { "provider": { "type": "static", "value": { "kind": "openai", "resource": "$res:f/ai_providers/openai", "model": "gpt-4o" } }, "output_type": { "type": "static", "value": "text" }, "user_message": { "type": "javascript", "expr": "flow_input.query" }, "system_prompt": { "type": "static", "value": "You are a helpful assistant." } }, "tools": [ { "id": "search_docs", "summary": "search_documentation", "description": "Search the product documentation. Use it whenever the user asks how a feature works.", "value": { "tool_type": "flowmodule", "type": "rawscript", "language": "bun", "content": "export async function main(query: string) { return ['doc1', 'doc2']; }", "input_transforms": { "query": { "type": "static", "value": "" } } } } ] } } ``` - `provider` is an object, not a bare resource string: `{ "kind": <provider kind>, "resource": "$res:<path>", "model": <model id> }`. Required unless the module links to a saved agent through `value.agent`. Static is right for a flow run from a form; a chat flow wires its fields to flow inputs instead — see below ### Chat-Mode Flows A flow with `value.chat_input_enabled: true` is run from a chat instead of a form: the composer sends one message per turn and renders the conversation. It needs a required `user_message` string input, read by the agent. Any other flow input the composer does not edit itself is asked for under Configure inputs. **A static `provider` gives a chat that cannot change its model.** Feed it from flow inputs instead, either way round: one input carrying the whole object (`"expr": "flow_input.model_config"`) makes every field editable, or wire it field by field to fix some and expose others. A field the chat can write becomes a control in the composer — a provider picker, a model list, a thinking control — and a field left static is fixed, with no control drawn for it. `kind` is the one exception: the composer writes it only together with `resource`, since a provider is picked as a pair, so a `kind` input wired on its own stays askable under Configure inputs and nothing the run needs becomes unreachable. ```json { "id": "chat_agent", "value": { "type": "aiagent", "input_transforms": { "provider": { "type": "javascript", "expr": "({ kind: 'anthropic', resource: '$res:f/ai/claude', model: flow_input.model, reasoning_effort: flow_input.thinking })" }, "user_message": { "type": "javascript", "expr": "flow_input.user_message" }, "user_attachments": { "type": "javascript", "expr": "flow_input.files" }, "memory": { "type": "static", "value": { "kind": "compaction" } }, "streaming": { "type": "static", "value": true }, "output_type": { "type": "static", "value": "text" } }, "tools": [] } } ``` - Wiring field by field means one object literal whose values are literals or bare `flow_input.x` references. A spread, a call or a computed key leaves the composer unable to tell which input feeds which field, so it offers no control at all — a bare `flow_input.x` for the whole object is read instead as that one input carrying every field - `memory` is what lets the agent see earlier turns; without it every message starts from nothing - `streaming` on makes the answer and its thinking appear token by token instead of all at once - `user_attachments` points at a flow input typed as an array of s3 objects (`{ "type": "array", "items": { "type": "object", "resourceType": "s3object" } }`), so files sent with a message reach the agent - Running one needs a `memory_id` **query parameter** — not a flow argument — naming the conversation the turn belongs to: a fresh UUID starts one, reusing a UUID continues it. The chat supplies it itself; a run driven any other way has to pass it or the server refuses the job ### Tool Naming Rules These rules cover `flowmodule` tools, the ones the agent calls by name. A `websearch` tool's `summary` is a plain label (`Web Search`), and an `mcp` tool exposes the MCP server's own tool names, so neither is name-checked at all — leave those summaries as they are. - A flowmodule tool's `summary` is the **name the agent calls it by**, not a human label. Put the human-readable explanation in `description` - `summary` must match `^[a-zA-Z0-9_]+$`: letters, numbers and underscores only. No spaces, dashes, dots or accents — `search_documentation`, never `Search documentation` - Always set `summary`. It must be unique among that agent's tools, and must not be one of the reserved ids (`do`, `bg`, `ctx`, `state`, `if`, `else`, `for`, `delete`, `while`, `new`, `in`, `failure`, `preprocessor`, `as`, `Input`, `Result`, `Trigger`) - A tool name outside that character set fails any run that offers the tool to the agent, with `Invalid tool name`. `wmill lint <flow folder>` reports it before anything runs. - Tool `id` follows the same rules as any module ID — unique across the flow, underscores not spaces - `description` is optional free text telling the agent when and how to call the tool. Set it whenever the name alone does not make that obvious; it overrides the description derived from the underlying script ## AI Decision Modules An `aidecision` module asks a decision model (TypeSafe's Jev or Cloudflare's Clef) typed questions about a `state` and answers each with calibrated probabilities instead of text. Prefer it over an `aiagent` when the step is a judgment (classify, route, score or a yes/no check) that needs no tools and no free text: it is faster, cheaper, and its answers have a fixed shape. ```json { "id": "triage", "summary": "Classify the ticket", "value": { "type": "aidecision", "input_transforms": { "provider": { "type": "static", "value": { "kind": "typesafe", "resource": "$res:f/ai/typesafe", "model": "jev-latest" } }, "state": { "type": "javascript", "expr": "flow_input.message" },
Voir sur GitHub
Ce SKILL.md est tres volumineux, SkillsMP affiche donc ici seulement la premiere section. Voir sur GitHub