diff --git a/cli/src/commands/flow/flow.ts b/cli/src/commands/flow/flow.ts index 29e43ef457..d778b3c4ee 100644 --- a/cli/src/commands/flow/flow.ts +++ b/cli/src/commands/flow/flow.ts @@ -542,6 +542,7 @@ async function preview( data?: string; silent: boolean; remote?: boolean; + step?: string; } & SyncOptions, flowPath: string ) { @@ -639,18 +640,35 @@ async function preview( const input = opts.data ? await resolve(opts.data) : {}; + log.debug(`Flow value: ${JSON.stringify(localFlow.value, null, 2)}`); + + // Single-step mode: run only the named module's runnable. + // The full-flow prep above (inline-script replacement, local PathScript + // substitution, tempScriptRefs build) is exactly what the single step needs + // too — PathScript modules have already been rewritten to inline rawscript + // when `useLocalPathScripts` is set, and tempScriptRefs covers relative + // imports in inline scripts. + // Compute the flow's windmill path (e.g. "f/cli_smoke/myrelflow"). Used as + // the anchor for relative-import resolution: inline scripts in this flow are + // treated as living at "/", so "./util" resolves to + // "/util" — matching the keys in temp_script_refs. + const flowWmPath = flowPath.substring(0, flowPath.indexOf(".flow")).replaceAll(SEP, "/"); + + if (opts.step) { + await previewStep(opts.step, localFlow, flowWmPath, workspace, input, tempScriptRefs, opts.silent); + return; + } + if (!opts.silent) { log.info(colors.yellow(`Running flow preview for ${flowPath}...`)); } - log.debug(`Flow value: ${JSON.stringify(localFlow.value, null, 2)}`); - // Run the flow preview — start the job, then poll for completion const jobId = await wmill.runFlowPreview({ workspace: workspace.workspaceId, requestBody: { value: localFlow.value, - path: flowPath.substring(0, flowPath.indexOf(".flow")).replaceAll(SEP, "/"), + path: flowWmPath, args: input, temp_script_refs: tempScriptRefs, }, @@ -677,6 +695,108 @@ async function preview( } } +async function previewStep( + stepId: string, + localFlow: FlowFile, + flowWmPath: string, + workspace: { workspaceId: string }, + baseArgs: Record, + tempScriptRefs: Record | undefined, + silent: boolean, +) { + const module = findStepInFlowValue(localFlow.value, stepId); + if (!module) { + const available = collectStepIds(localFlow.value).join(", ") || "(none)"; + throw new Error(`Step '${stepId}' not found in flow. Available steps: ${available}`); + } + + // The preprocessor module receives args via _ENTRYPOINT_OVERRIDE so the + // runner picks the preprocessor entrypoint (matches frontend behavior in + // copilot/chat/flow/core.ts). + const args = + stepId === "preprocessor" + ? { _ENTRYPOINT_OVERRIDE: "preprocessor", ...baseArgs } + : baseArgs; + + const moduleValue = module.value; + let jobId: string; + if (moduleValue?.type === "rawscript") { + log.info(colors.yellow(`Previewing step '${stepId}' (rawscript, ${moduleValue.language})...`)); + jobId = await wmill.runScriptPreview({ + workspace: workspace.workspaceId, + requestBody: { + content: moduleValue.content ?? "", + language: moduleValue.language, + // Anchor relative imports to "/" so + // temp_script_refs (keyed by Windmill paths) resolve correctly. + // Without `path`, the worker defaults to "tmp/main" and "../foo" + // resolves to "tmp/foo", missing every entry in temp_script_refs. + path: `${flowWmPath}/${stepId}`, + flow_path: flowWmPath, + args, + temp_script_refs: tempScriptRefs, + }, + }); + } else if (moduleValue?.type === "script") { + // Falls through here only when the deployed PathScript is what we want — + // either --remote was passed, or no local file exists for this path. + log.info(colors.yellow(`Previewing step '${stepId}' (script ${moduleValue.path})...`)); + const script = moduleValue.hash + ? await wmill.getScriptByHash({ + workspace: workspace.workspaceId, + hash: moduleValue.hash, + }) + : await wmill.getScriptByPath({ + workspace: workspace.workspaceId, + path: moduleValue.path, + }); + jobId = await wmill.runScriptPreview({ + workspace: workspace.workspaceId, + requestBody: { + content: script.content, + language: script.language as any, + // Anchor to the script's own deployed path so its relative imports + // resolve against the workspace tree (or temp_script_refs). + path: moduleValue.path, + flow_path: flowWmPath, + args, + temp_script_refs: tempScriptRefs, + }, + }); + } else if (moduleValue?.type === "flow") { + log.info(colors.yellow(`Previewing step '${stepId}' (flow ${moduleValue.path})...`)); + jobId = await wmill.runFlowByPath({ + workspace: workspace.workspaceId, + path: moduleValue.path, + requestBody: args, + }); + } else { + throw new Error( + `Cannot preview step of type '${moduleValue?.type ?? "unknown"}'. Supported types: rawscript, script, flow.` + ); + } + + const { result, success } = await pollForJobResult(workspace.workspaceId, jobId); + + if (!success) { + if (silent) { + console.log(JSON.stringify(result)); + } else { + log.info(colors.red.bold(`Step '${stepId}' failed:`)); + log.info(JSON.stringify(result, null, 2)); + } + process.exitCode = 1; + return; + } + + if (silent) { + console.log(JSON.stringify(result)); + } else { + log.info(colors.bold.underline.green(`Step '${stepId}' completed`)); + log.info(JSON.stringify(result, null, 2)); + } +} + function findStepInFlowValue(flowValue: any, stepId: string): any | undefined { if (!flowValue) return undefined; if (flowValue.failure_module?.id === stepId) return flowValue.failure_module; @@ -732,130 +852,6 @@ function collectStepIds(flowValue: any): string[] { return ids; } -async function testStep( - opts: GlobalOptions & { - data?: string; - silent: boolean; - json?: boolean; - } & SyncOptions, - flowPath: string, - stepId: string -) { - if (opts.silent || opts.json) log.setSilent(true); - opts = await mergeConfigWithConfigFile(opts); - const workspace = await resolveWorkspace(opts); - await requireLogin(opts); - - // Normalize flow path (same logic as `preview`). - const isFlowDir = flowPath.endsWith(".flow") || flowPath.endsWith(".flow" + SEP) - || flowPath.endsWith("__flow") || flowPath.endsWith("__flow" + SEP); - if (!isFlowDir) { - if (flowPath.endsWith("flow.yaml") || flowPath.endsWith("flow.json")) { - // Use dirname so a bare "flow.yaml" (no parent dir) becomes "." - // instead of "" — the latter, after appending SEP below, becomes "/" - // and silently reads from filesystem root. - flowPath = dirname(flowPath); - } else { - throw new Error( - "Flow path must be a .flow/__flow directory or a flow.yaml file" - ); - } - } - if (!flowPath.endsWith(SEP)) flowPath += SEP; - - const localFlow = (await yamlParseFile(flowPath + "flow.yaml")) as FlowFile; - const fileReader = async (path: string) => await readTextFile(flowPath + path); - await replaceInlineScripts(localFlow.value.modules, fileReader, log, flowPath, SEP); - if (localFlow.value.failure_module) { - await replaceInlineScripts([localFlow.value.failure_module], fileReader, log, flowPath, SEP); - } - if (localFlow.value.preprocessor_module) { - await replaceInlineScripts([localFlow.value.preprocessor_module], fileReader, log, flowPath, SEP); - } - - const module = findStepInFlowValue(localFlow.value, stepId); - if (!module) { - const available = collectStepIds(localFlow.value).join(", ") || "(none)"; - throw new Error(`Step '${stepId}' not found in flow. Available steps: ${available}`); - } - - const baseArgs = opts.data ? await resolve(opts.data) : {}; - const args = - stepId === "preprocessor" - ? { _ENTRYPOINT_OVERRIDE: "preprocessor", ...baseArgs } - : baseArgs; - - const moduleValue = module.value; - let jobId: string; - if (moduleValue?.type === "rawscript") { - if (!opts.silent && !opts.json) { - log.info(colors.yellow(`Testing rawscript step '${stepId}' (${moduleValue.language})...`)); - } - jobId = await wmill.runScriptPreview({ - workspace: workspace.workspaceId, - requestBody: { - content: moduleValue.content ?? "", - language: moduleValue.language, - args, - }, - }); - } else if (moduleValue?.type === "script") { - if (!opts.silent && !opts.json) { - log.info(colors.yellow(`Testing script step '${stepId}' (${moduleValue.path})...`)); - } - const script = moduleValue.hash - ? await wmill.getScriptByHash({ - workspace: workspace.workspaceId, - hash: moduleValue.hash, - }) - : await wmill.getScriptByPath({ - workspace: workspace.workspaceId, - path: moduleValue.path, - }); - jobId = await wmill.runScriptPreview({ - workspace: workspace.workspaceId, - requestBody: { - content: script.content, - language: script.language as any, - args, - }, - }); - } else if (moduleValue?.type === "flow") { - if (!opts.silent && !opts.json) { - log.info(colors.yellow(`Testing flow step '${stepId}' (${moduleValue.path})...`)); - } - jobId = await wmill.runFlowByPath({ - workspace: workspace.workspaceId, - path: moduleValue.path, - requestBody: args, - }); - } else { - throw new Error( - `Cannot test step of type '${moduleValue?.type ?? "unknown"}'. Supported types: rawscript, script, flow.` - ); - } - - const { result, success } = await pollForJobResult(workspace.workspaceId, jobId); - - if (!success) { - if (opts.silent || opts.json) { - console.log(JSON.stringify(result)); - } else { - log.info(colors.yellow.bold(`Step '${stepId}' failed:`)); - log.info(JSON.stringify(result, null, 2)); - } - process.exitCode = 1; - return; - } - - if (opts.silent || opts.json) { - console.log(JSON.stringify(result)); - } else { - log.info(colors.bold.underline.green(`Step '${stepId}' completed`)); - log.info(JSON.stringify(result, null, 2)); - } -} - export async function generateLocks( opts: GlobalOptions & { yes?: boolean; @@ -1072,7 +1068,7 @@ const command = new Command() .action(run as any) .command( "preview", - "preview a local flow without deploying it. Runs the flow definition from local files and uses local PathScripts by default." + "preview a local flow without deploying it. Runs the flow definition from local files and uses local PathScripts by default. Pass --step to run only one module in isolation (resolves nested steps inside branchone/branchall/forloopflow/whileloopflow plus the special preprocessor/failure modules; supported step types: rawscript, script, flow)." ) .arguments("") .option( @@ -1087,22 +1083,11 @@ const command = new Command() "--remote", "Use deployed workspace scripts for PathScript steps instead of local files." ) + .option( + "--step ", + "Run only the named step instead of the whole flow. Honors --data as the step's args and --remote / local-PathScript resolution the same way the full-flow preview does." + ) .action(preview as any) - .command( - "test-step", - "Test a single step of a local flow in isolation. Resolves the step by id (including nested branchone/branchall/forloopflow/whileloopflow and the failure/preprocessor modules), runs only that step's runnable. Supported step types: rawscript, script (PathScript), flow (PathFlow)." - ) - .arguments(" ") - .option( - "-d --data ", - "Step inputs as a JSON string or a file using @ or stdin using @-." - ) - .option( - "-s --silent", - "Do not output anything other then the final output. Useful for scripting." - ) - .option("--json", "Output the result as JSON (same as --silent)") - .action(testStep as any) .command( "generate-locks", 'DEPRECATED: re-generate flow lock files. Use "wmill generate-metadata" instead.' diff --git a/cli/src/guidance/skills.gen.ts b/cli/src/guidance/skills.gen.ts index 7be08aae19..714fdec222 100644 --- a/cli/src/guidance/skills.gen.ts +++ b/cli/src/guidance/skills.gen.ts @@ -5167,8 +5167,7 @@ Once the flow has real content, **offer** to open the visual preview as a one-se After writing, tell the user which command fits what they want to do: -- \`wmill flow preview \` — **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. -- \`wmill flow test-step \` — runs a single step of the local flow in isolation. Use when iterating on one module (rawscript / script / flow types) and you don't want to wait for upstream steps. Supports nested steps inside branchone/branchall/forloopflow/whileloopflow, plus the special \`preprocessor\` and \`failure\` modules by id. Pass step args with \`-d ''\`. +- \`wmill flow preview \` — **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 \` to run only one module in isolation (see "Single-step vs whole-flow preview" below). - \`wmill flow run \` — 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 \`.lock\` and \`.script.yaml\` files. By default it scans **scripts, flows, and apps** across the workspace; pass \`--skip-flows --skip-apps\` (or run from a subdirectory) to limit the scope when you only care about the flow you edited. - \`wmill sync push\` — deploy local changes to the workspace. Only suggest/run this when the user explicitly asks to deploy/publish/push — not when they say "run", "try", or "test". @@ -5185,11 +5184,11 @@ 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. -### Test a single step vs preview the whole flow +### Single-step vs whole-flow preview -Use \`flow test-step \` 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 fetched from the workspace; flow: the subflow by path) and is much faster than running the whole flow when previous steps are slow or expensive. +Use \`flow preview --step \` 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 \` when steps depend on each other's outputs, when the user is validating the overall control flow, or when \`test-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). +Use \`flow preview \` (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 @@ -6845,14 +6844,11 @@ flow related commands - \`flow run \` - run a flow by path. - \`-d --data \` - 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 \` - preview a local flow without deploying it. Runs the flow definition from local files and uses local PathScripts by default. +- \`flow preview \` - preview a local flow without deploying it. Runs the flow definition from local files and uses local PathScripts by default. Pass --step to run only one module in isolation (resolves nested steps inside branchone/branchall/forloopflow/whileloopflow plus the special preprocessor/failure modules; supported step types: rawscript, script, flow). - \`-d --data \` - 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 test-step \` - Test a single step of a local flow in isolation. Resolves the step by id (including nested branchone/branchall/forloopflow/whileloopflow and the failure/preprocessor modules), runs only that step's runnable. Supported step types: rawscript, script (PathScript), flow (PathFlow). - - \`-d --data \` - Step inputs 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. - - \`--json\` - Output the result as JSON (same as --silent) + - \`--step \` - Run only the named step instead of the whole flow. Honors --data as the step's args and --remote / local-PathScript resolution the same way the full-flow preview does. - \`flow new \` - create a new empty flow - \`--summary \` - flow summary - \`--description \` - flow description diff --git a/system_prompts/auto-generated/cli/cli-commands.md b/system_prompts/auto-generated/cli/cli-commands.md index 755bf8ced8..b02aabefe0 100644 --- a/system_prompts/auto-generated/cli/cli-commands.md +++ b/system_prompts/auto-generated/cli/cli-commands.md @@ -148,14 +148,11 @@ flow related commands - `flow run ` - run a flow by path. - `-d --data ` - 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 ` - preview a local flow without deploying it. Runs the flow definition from local files and uses local PathScripts by default. +- `flow preview ` - preview a local flow without deploying it. Runs the flow definition from local files and uses local PathScripts by default. Pass --step to run only one module in isolation (resolves nested steps inside branchone/branchall/forloopflow/whileloopflow plus the special preprocessor/failure modules; supported step types: rawscript, script, flow). - `-d --data ` - 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 test-step ` - Test a single step of a local flow in isolation. Resolves the step by id (including nested branchone/branchall/forloopflow/whileloopflow and the failure/preprocessor modules), runs only that step's runnable. Supported step types: rawscript, script (PathScript), flow (PathFlow). - - `-d --data ` - Step inputs 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. - - `--json` - Output the result as JSON (same as --silent) + - `--step ` - Run only the named step instead of the whole flow. Honors --data as the step's args and --remote / local-PathScript resolution the same way the full-flow preview does. - `flow new ` - create a new empty flow - `--summary ` - flow summary - `--description ` - flow description diff --git a/system_prompts/auto-generated/prompts.ts b/system_prompts/auto-generated/prompts.ts index 657408471d..9ce0d56e01 100644 --- a/system_prompts/auto-generated/prompts.ts +++ b/system_prompts/auto-generated/prompts.ts @@ -2698,14 +2698,11 @@ flow related commands - \`flow run \` - run a flow by path. - \`-d --data \` - 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 \` - preview a local flow without deploying it. Runs the flow definition from local files and uses local PathScripts by default. +- \`flow preview \` - preview a local flow without deploying it. Runs the flow definition from local files and uses local PathScripts by default. Pass --step to run only one module in isolation (resolves nested steps inside branchone/branchall/forloopflow/whileloopflow plus the special preprocessor/failure modules; supported step types: rawscript, script, flow). - \`-d --data \` - 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 test-step \` - Test a single step of a local flow in isolation. Resolves the step by id (including nested branchone/branchall/forloopflow/whileloopflow and the failure/preprocessor modules), runs only that step's runnable. Supported step types: rawscript, script (PathScript), flow (PathFlow). - - \`-d --data \` - Step inputs 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. - - \`--json\` - Output the result as JSON (same as --silent) + - \`--step \` - Run only the named step instead of the whole flow. Honors --data as the step's args and --remote / local-PathScript resolution the same way the full-flow preview does. - \`flow new \` - create a new empty flow - \`--summary \` - flow summary - \`--description \` - flow description diff --git a/system_prompts/auto-generated/skills/cli-commands/SKILL.md b/system_prompts/auto-generated/skills/cli-commands/SKILL.md index 85fc02746d..4b3e72a8b7 100644 --- a/system_prompts/auto-generated/skills/cli-commands/SKILL.md +++ b/system_prompts/auto-generated/skills/cli-commands/SKILL.md @@ -153,14 +153,11 @@ flow related commands - `flow run ` - run a flow by path. - `-d --data ` - 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 ` - preview a local flow without deploying it. Runs the flow definition from local files and uses local PathScripts by default. +- `flow preview ` - preview a local flow without deploying it. Runs the flow definition from local files and uses local PathScripts by default. Pass --step to run only one module in isolation (resolves nested steps inside branchone/branchall/forloopflow/whileloopflow plus the special preprocessor/failure modules; supported step types: rawscript, script, flow). - `-d --data ` - 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 test-step ` - Test a single step of a local flow in isolation. Resolves the step by id (including nested branchone/branchall/forloopflow/whileloopflow and the failure/preprocessor modules), runs only that step's runnable. Supported step types: rawscript, script (PathScript), flow (PathFlow). - - `-d --data ` - Step inputs 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. - - `--json` - Output the result as JSON (same as --silent) + - `--step ` - Run only the named step instead of the whole flow. Honors --data as the step's args and --remote / local-PathScript resolution the same way the full-flow preview does. - `flow new ` - create a new empty flow - `--summary ` - flow summary - `--description ` - flow description diff --git a/system_prompts/auto-generated/skills/write-flow/SKILL.md b/system_prompts/auto-generated/skills/write-flow/SKILL.md index 23636a3166..fd8eacb9c0 100644 --- a/system_prompts/auto-generated/skills/write-flow/SKILL.md +++ b/system_prompts/auto-generated/skills/write-flow/SKILL.md @@ -46,8 +46,7 @@ Once the flow has real content, **offer** to open the visual preview as a one-se After writing, tell the user which command fits what they want to do: -- `wmill flow preview ` — **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. -- `wmill flow test-step ` — runs a single step of the local flow in isolation. Use when iterating on one module (rawscript / script / flow types) and you don't want to wait for upstream steps. Supports nested steps inside branchone/branchall/forloopflow/whileloopflow, plus the special `preprocessor` and `failure` modules by id. Pass step args with `-d ''`. +- `wmill flow preview ` — **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 ` to run only one module in isolation (see "Single-step vs whole-flow preview" below). - `wmill flow run ` — 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 `.lock` and `.script.yaml` files. By default it scans **scripts, flows, and apps** across the workspace; pass `--skip-flows --skip-apps` (or run from a subdirectory) to limit the scope when you only care about the flow you edited. - `wmill sync push` — deploy local changes to the workspace. Only suggest/run this when the user explicitly asks to deploy/publish/push — not when they say "run", "try", or "test". @@ -64,11 +63,11 @@ 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. -### Test a single step vs preview the whole flow +### Single-step vs whole-flow preview -Use `flow test-step ` 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 fetched from the workspace; flow: the subflow by path) and is much faster than running the whole flow when previous steps are slow or expensive. +Use `flow preview --step ` 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 ` when steps depend on each other's outputs, when the user is validating the overall control flow, or when `test-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). +Use `flow preview ` (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 diff --git a/system_prompts/base/flow-cli.md b/system_prompts/base/flow-cli.md index 3f814d12c0..b783b22526 100644 --- a/system_prompts/base/flow-cli.md +++ b/system_prompts/base/flow-cli.md @@ -41,8 +41,7 @@ Once the flow has real content, **offer** to open the visual preview as a one-se After writing, tell the user which command fits what they want to do: -- `wmill flow preview ` — **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. -- `wmill flow test-step ` — runs a single step of the local flow in isolation. Use when iterating on one module (rawscript / script / flow types) and you don't want to wait for upstream steps. Supports nested steps inside branchone/branchall/forloopflow/whileloopflow, plus the special `preprocessor` and `failure` modules by id. Pass step args with `-d ''`. +- `wmill flow preview ` — **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 ` to run only one module in isolation (see "Single-step vs whole-flow preview" below). - `wmill flow run ` — 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 `.lock` and `.script.yaml` files. By default it scans **scripts, flows, and apps** across the workspace; pass `--skip-flows --skip-apps` (or run from a subdirectory) to limit the scope when you only care about the flow you edited. - `wmill sync push` — deploy local changes to the workspace. Only suggest/run this when the user explicitly asks to deploy/publish/push — not when they say "run", "try", or "test". @@ -59,11 +58,11 @@ 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. -### Test a single step vs preview the whole flow +### Single-step vs whole-flow preview -Use `flow test-step ` 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 fetched from the workspace; flow: the subflow by path) and is much faster than running the whole flow when previous steps are slow or expensive. +Use `flow preview --step ` 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 ` when steps depend on each other's outputs, when the user is validating the overall control flow, or when `test-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). +Use `flow preview ` (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