From outputai
Creates workflow.ts files for Output SDK workflows with correct TypeScript imports from @outputai/core, .js ES module extensions, deterministic fn delegating I/O to steps, and Zod schemas. Use for defining functions, orchestrating steps, or fixing structure.
npx claudepluginhub growthxai/output --plugin outputaiThis 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.
Outlines standard folder structure conventions for Output SDK workflows. Use when creating new workflows, reorganizing files, understanding file placement, or reviewing compliance.
Builds durable, resumable TypeScript workflows with Vercel Workflow DevKit for long-running processes, AI agents, background jobs, multi-step pipelines, webhooks, and event-driven systems.
Mandates invoking relevant skills via tools before any response in coding sessions. Covers access, priorities, and adaptations for Claude Code, Copilot CLI, Gemini CLI.
Share bugs, ideas, or general feedback.
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 @outputai/core
import { workflow, z } from '@outputai/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 '@outputai/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 '@outputai/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 @outputai/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-evaluator-function - Using steps in evaluator functionsoutput-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 issues