Writing Workflow Scripts: agent() and pipeline()
- Read and modify a workflow script including the meta block, agent() calls, and pipeline() fan-outs
- Pass structured input to a saved workflow using the args global and design workflows that accept runtime parameters
- Apply common workflow patterns to audit, migration, fix-until-passing, and cross-checked research tasks
What a Workflow Script Looks Like
When Claude writes a workflow for your task, it generates a JavaScript file. You usually don't write these by hand — Claude generates them — but reading and editing the script is how you tune a workflow, save it for reuse, or adapt it for a new project. Here's the shape of a small one:
export const meta = {
name: 'audit-routes',
description: 'Audit every route handler for missing auth checks',
}
const found = await agent('List every .ts file under src/routes/.', {
schema: {
type: 'object',
required: ['files'],
properties: {
files: { type: 'array', items: { type: 'string' } }
}
},
})
const audits = await pipeline(found.files, file =>
agent('Audit ' + file + ' for missing authentication checks.', { label: file }),
)
return audits.filter(Boolean)
The file has two parts: a meta export and a script body. The body is plain JavaScript with top-level await. Every agent() or pipeline() call spawns real Claude subagents that run with full tool access.
The meta Block
The export const meta block is required. It gives the workflow its identity:
- name: the command name when saved — becomes
/audit-routesin autocomplete - description: shown in the
/autocomplete menu as the command's help text
When you save a workflow by pressing s in the /workflows view, the name from the meta block becomes the saved command's slash-command name. Keep it short and descriptive.
agent(): Spawning One Agent
agent(prompt, options) spawns a single subagent and returns its result. The agent runs with full tool access — it can read files, run shell commands, make network requests, and edit files. Results are plain text or, if you pass a schema option with a JSON Schema definition, a parsed JavaScript object matching that schema.
Key options:
- schema: a JSON Schema that causes the agent to return structured data instead of free text. Claude Code validates the output against the schema and retries if it doesn't match.
- label: a string shown next to the agent in the
/workflowsprogress view, useful for tracking which file or item each agent is processing
Agents run sequentially when called in series, in parallel when passed to pipeline().
pipeline(): Fan-Out Across Many Items
pipeline(items, fn) runs one agent per item in the items array, up to 16 concurrently. It takes a list and a function that maps each item to an agent() call:
const results = await pipeline(fileList, file =>
agent('Check ' + file + ' for null pointer dereferences.', { label: file })
)
The function can return an agent() call directly, or it can call other functions that return agent(). The runtime manages concurrency automatically — you don't specify parallelism; the runtime fills slots as agents complete.
Intermediate results live in results, a JavaScript array in the script's scope. They don't land in Claude's context window. A 500-item pipeline produces a 500-element array in the script and zero additional context in your conversation. This is the key advantage over subagents for large-scale work.
The args Global
Saved workflows can accept runtime input through a global named args. Claude converts natural language input into structured data the script can iterate on without parsing:
/triage-issues on issues 1024, 1025, and 1030
Claude passes the issue numbers as structured data to the args global. Inside the script, args is whatever data type Claude inferred — in this case, likely an array of numbers that you can directly iterate with pipeline(). If args is omitted, the global is undefined.
Design workflows with args when the same orchestration needs to run on different inputs — a different list of files, a different PR number, a different research question — without editing the script.
Saving and Reusing Workflows
After a workflow run you want to repeat, press s in the /workflows view. A save dialog lets you choose between two locations:
.claude/workflows/in your project: shared with everyone who clones the repo, runs when you're in this project~/.claude/workflows/in your home directory: available in every project, visible only to you
Saved workflows appear in / autocomplete alongside the built-in ones, with the description from the meta block as help text. If a project workflow and a personal workflow share a name, the project one runs.
In a monorepo, saving to the project location writes to the closest .claude/workflows/ directory between your working directory and the repository root. Workflows load from every .claude/workflows/ along that path, and the closest one wins when names conflict.
Common Workflow Patterns
Audit many files for the same issue: Use a discovery agent to list target files, then pipeline one audit agent per file. Add an adversarial verification step — a second agent that reviews each finding before it's reported — for higher-confidence results.
Fix until a check passes: Use a checker agent to run a validation (like tsc --noEmit), a fixer agent to address the reported errors, and a loop that repeats until the checker returns zero errors or N rounds in a row make no progress.
Migrate files in parallel: Discover files, transform each in a pipeline, and verify each result. The isolated-copy pattern — working on a copy of each file before writing the result back — prevents partial edits from breaking other parts of the codebase mid-run.
Research across many sources: Fan out readers across multiple source types (changelogs, docs, issues), collect their findings, then pass all findings to a single synthesis agent that deduplicates and ranks them. The /deep-research built-in workflow uses this exact pattern with cross-checking built in.
- export const meta is the entry point — name becomes the saved /command in autocomplete and description is the help text; both are required for any workflow you intend to save and reuse
- agent() and pipeline() are the two primitives — agent() for a single delegated task, pipeline() for processing every item in an array with up to 16 concurrent workers
- Intermediate results live in script variables, not in Claude's context — a 500-item pipeline fills a JavaScript array but adds zero tokens to the conversation, which is the core advantage over subagents for large-scale work
- args is the runtime parameter — Claude converts natural language invocation arguments into structured data the script can iterate on directly without parsing, enabling one workflow to process different inputs each run
- Saving with the s key makes a workflow a named /command — choose project scope (.claude/workflows/) to share it with teammates or personal scope (~/.claude/workflows/) to keep it available across all your projects