Create workflow.ts files for Output SDK workflows. Use when defining workflow functions, orchestrating steps, or fixing workflow structure issues.
Create workflow.ts files for Output SDK workflows. Use when defining workflow functions, orchestrating steps, or fixing workflow structure issues.
/plugin marketplace add growthxai/output-claude-plugins/plugin install growthxai-outputai-plugins-outputai@growthxai/output-claude-pluginsThis skill is limited to using the following tools:
This skill documents how to create workflow.ts files for Output SDK workflows. The workflow file contains the main orchestration logic that coordinates step execution.
// CORRECT - Import from @output.ai/core
import { workflow, z } from '@output.ai/core';
// WRONG - Never import z from zod
import { z } from 'zod';
All imports MUST use .js extension:
// CORRECT
import { stepName } from './steps.js';
import { WorkflowInputSchema } from './types.js';
// WRONG - Missing .js extension
import { stepName } from './steps';
import { WorkflowInputSchema } from './types';
CRITICAL: The workflow fn must be deterministic. No direct I/O operations are allowed in the workflow function.
// WRONG - Direct I/O in workflow
export default workflow({
// ...
fn: async (input) => {
const response = await fetch('https://api.example.com'); // NEVER do this!
return response.json();
}
});
// CORRECT - Delegate I/O to steps
export default workflow({
// ...
fn: async (input) => {
const result = await fetchDataStep(input); // Steps handle I/O
return result;
}
});
Related Skill: output-error-nondeterminism
import { workflow, z } from '@output.ai/core';
import { stepOne, stepTwo } from './steps.js';
import { WorkflowInputSchema, WorkflowOutput } from './types.js';
export default workflow({
name: 'workflowName',
description: 'Brief description of what the workflow does',
inputSchema: WorkflowInputSchema,
outputSchema: z.object({ /* output shape */ }),
fn: async (input): Promise<WorkflowOutput> => {
// Orchestrate step calls
const result = await stepOne(input);
const final = await stepTwo(result);
return final;
}
});
Unique identifier for the workflow. Use camelCase.
name: 'contentUtilsImageInfographicNano'
Human-readable description of the workflow's purpose.
description: 'Generate high-quality infographic images using AI-powered ideation'
Schema for validating workflow input. Import from types.ts.
inputSchema: WorkflowInputSchema
Related Skill: output-dev-types-file
Schema for validating workflow output.
outputSchema: z.object({
results: z.array(z.string()),
metadata: z.object({
processedAt: z.string()
})
})
The workflow execution function. Must be deterministic.
fn: async (input): Promise<WorkflowOutput> => {
// Step orchestration only - no direct I/O
const result = await processStep(input);
return result;
}
Based on a real workflow (image_infographic_nano):
import { workflow, z } from '@output.ai/core';
import {
generateImageIdeas,
generateImages,
validateReferenceImages
} from './steps.js';
import {
WorkflowInput,
WorkflowInputSchema,
WorkflowOutput
} from './types.js';
import { normalizeReferenceImageUrls } from './utils.js';
export default workflow({
name: 'contentUtilsImageInfographicNano',
description: 'Generate high-quality infographic images using Google Gemini 3 Pro Image model with AI-powered ideation',
inputSchema: WorkflowInputSchema,
outputSchema: z.array(z.string()),
fn: async (rawInput: WorkflowInput): Promise<WorkflowOutput> => {
// Pre-process input (pure function - OK in workflow)
const input = {
...rawInput,
referenceImageUrls: normalizeReferenceImageUrls(rawInput.referenceImageUrls)
};
// Conditional step execution
if (input.referenceImageUrls && input.referenceImageUrls.length > 0) {
await validateReferenceImages({
referenceImageUrls: input.referenceImageUrls as string[]
});
}
// Sequential step execution
const ideas = await generateImageIdeas({
content: input.content,
numberOfIdeas: input.numberOfIdeas,
colorPalette: input.colorPalette,
artDirection: input.artDirection
});
// Parallel step execution
const generations = await Promise.all(
ideas.map(idea =>
generateImages({
input: {
referenceImageUrls: input.referenceImageUrls,
aspectRatio: input.aspectRatio,
resolution: input.resolution,
numberOfGenerations: input.numberOfGenerations,
storageNamespace: input.storageNamespace
},
prompt: idea
})
)
);
return generations.flat();
}
});
Execute steps one after another:
fn: async (input) => {
const step1Result = await stepOne(input);
const step2Result = await stepTwo(step1Result);
const step3Result = await stepThree(step2Result);
return step3Result;
}
Execute independent steps concurrently:
fn: async (input) => {
const [resultA, resultB, resultC] = await Promise.all([
stepA(input),
stepB(input),
stepC(input)
]);
return { resultA, resultB, resultC };
}
Execute steps based on conditions:
fn: async (input) => {
if (input.includeImages) {
await processImages(input);
}
const result = input.mode === 'fast'
? await quickProcess(input)
: await detailedProcess(input);
return result;
}
Process multiple items in parallel:
fn: async (input) => {
const results = await Promise.all(
input.items.map(item => processItem({ item }))
);
return { processedItems: results };
}
Chain multiple transformations:
fn: async (input) => {
const extracted = await extractData(input);
const transformed = await transformData(extracted);
const validated = await validateData(transformed);
const enriched = await enrichData(validated);
return enriched;
}
workflow and z imported from @output.ai/core.js extensionname is camelCase and uniquedescription clearly explains the workflowinputSchema imported from types.tsoutputSchema matches actual return typefn is deterministic (no direct I/O)output-dev-step-function - Creating step functions that handle I/Ooutput-dev-types-file - Defining input/output schemasoutput-dev-folder-structure - Where workflow.ts belongsoutput-error-nondeterminism - Fixing determinism violationsoutput-error-zod-import - Fixing schema import issuesThis skill should be used when the user asks to "create a slash command", "add a command", "write a custom command", "define command arguments", "use command frontmatter", "organize commands", "create command with file references", "interactive command", "use AskUserQuestion in command", or needs guidance on slash command structure, YAML frontmatter fields, dynamic arguments, bash execution in commands, user interaction patterns, or command development best practices for Claude Code.
This skill should be used when the user asks to "create an agent", "add an agent", "write a subagent", "agent frontmatter", "when to use description", "agent examples", "agent tools", "agent colors", "autonomous agent", or needs guidance on agent structure, system prompts, triggering conditions, or agent development best practices for Claude Code plugins.
This skill should be used when the user asks to "create a hook", "add a PreToolUse/PostToolUse/Stop hook", "validate tool use", "implement prompt-based hooks", "use ${CLAUDE_PLUGIN_ROOT}", "set up event-driven automation", "block dangerous commands", or mentions hook events (PreToolUse, PostToolUse, Stop, SubagentStop, SessionStart, SessionEnd, UserPromptSubmit, PreCompact, Notification). Provides comprehensive guidance for creating and implementing Claude Code plugin hooks with focus on advanced prompt-based hooks API.