# Workflow script API This is the full reference for workflow scripts. You rarely write them by hand, because the model does. It is useful for reviewing what the model wrote, editing a persisted script, or writing a saved workflow. The model gets the same information from `/workflow-authoring`. - [The `workflow` tool](#the-workflow-tool) - [Script format](#script-format) - [Globals](#globals) - [Limits](#limits) - [Language rules](#language-rules) - [Resume](#resume) - [Saved workflows](#saved-workflows) - [Patterns](#patterns) ## The `workflow` tool | Input | Meaning | |---|---| | `script` | The full script, inline. | | `scriptPath` | Run a script file, usually the persisted copy of an earlier run after editing it. Takes precedence over `script` and `name`. Only files the session may read are accepted (see [Security model](../README.md#security-model)). | | `name` | Run a saved or bundled workflow by its `meta.name`. | | `args` | Any JSON value. It is exposed verbatim as the global `args` (`undefined` when omitted). Pass arrays and objects as real JSON, not as a string. | | `resumeFromRunId` | Relaunch a previous run and reuse its cached agent results. | | `budget` | A token ceiling. The model sets it only when you state a budget. It is not part of Claude Code's tool input (see [PARITY](PARITY.md) P34). | At least one of `script`, `name` or `scriptPath` is required. The tool returns immediately: ```json { "status": "async_launched", "taskId": "…", "taskType": "local_workflow", "workflowName": "…", "runId": "wf_…", "summary": "…", "transcriptDir": "…", "scriptPath": "…" } ``` If the script fails its syntax or meta check, the result also has `error` set and nothing runs. Every launch persists the script and returns its path as `scriptPath`, so an edited copy can be relaunched with `{ scriptPath }`. When the run finishes, stops or fails, its return value arrives in the parent session as a ``, with the status and usage (agent count, tokens, duration). That is the only way the result reaches the model. `workflow_control status` shows progress, never the result. ## Script format ```js export const meta = { name: 'find-flaky-tests', description: 'Find flaky tests and propose fixes', whenToUse: 'When CI shows intermittent failures. args: a CI log path', phases: [ { title: 'Scan', detail: 'look for retried tests' }, { title: 'Fix', detail: 'one agent per flaky test' }, ], } phase('Scan') const flaky = await agent(`List tests that were retried in ${args}.`, { schema: { type: 'object', required: ['tests'], properties: { tests: { type: 'array', items: { type: 'string' } } } }, }) phase('Fix') const fixes = await pipeline(flaky.tests, (t) => agent(`Propose a fix for the flaky test ${t}`, { label: t })) return fixes.filter(Boolean) ``` **`meta`** must be the **first statement** and a **pure literal**: no variables, function calls, spreads, computed keys or template interpolation. For an inline script, anything else is an error. A saved workflow with a non-literal `meta` is not registered as a command. | Field | Required | Meaning | |---|---|---| | `name` | yes | The workflow's name, and its `/` command when saved. | | `description` | yes | One line, shown to the user. | | `whenToUse` | no | Shown in the `/` command description and the `workflow` tool description. Say what `args` it expects, so `/` passes structured JSON. | | `phases` | no | `[{ title, detail?, model? }]`. **Labels only**: they don't assign agents to groups. Call `phase(title)` with the same title before that stage's agents, or pass `agent(…, { phase })`. | **The body** is plain JavaScript with top-level `await` and top-level `return`. The returned value is the workflow's result. ## Globals ### `agent(prompt, opts?)` Runs one subagent: an opencode child session titled `[wf:]