Your First Workflow
In this tutorial, you’ll build a multi-stage automation workflow that chains agents together to accomplish complex tasks. Unlike tests (which focus on validation), workflows are optimized for production automation with features like stage management, loops, and cumulative context.
What You’ll Build
Section titled “What You’ll Build”A complete automation pipeline that:
- Analyzes a codebase for issues
- Fixes the issues found
- Tests the fixes
- Retries if tests fail (with a loop)
- Reports results
We’ll use vibeWorkflow
instead of vibeTest
to optimize for automation.
vibeTest vs vibeWorkflow
Section titled “vibeTest vs vibeWorkflow”Before we start, understand the difference:
Feature | vibeTest | vibeWorkflow |
---|---|---|
Purpose | Testing & evaluation | Production automation |
Assertions | ✅ Uses expect() | ❌ No assertions (logs instead) |
Failure Mode | Fails test on error | Continues, reports errors |
Context | VibeTestContext (with matchers) | WorkflowContext (with stages) |
Use Case | Benchmarking, quality gates | CI/CD, deployments, migrations |
Basic Workflow
Section titled “Basic Workflow”Let’s start with a simple 3-stage workflow:
-
Create the workflow file
Create
workflows/refactor-pipeline.ts
:import { vibeWorkflow } from '@dao/vibe-check';vibeWorkflow('refactor pipeline', async (wf) => {// Stage 1: Analyze codeconst analyze = await wf.stage('analyze', {prompt: '/analyze src/ --find-issues'});// Stage 2: Fix issuesconst fix = await wf.stage('fix', {prompt: `/fix issues from ${analyze.bundleDir}/summary.json`});// Stage 3: Run testsconst test = await wf.stage('test', {prompt: '/test'});// Log resultsconsole.log('Pipeline complete!');console.log('Issues found:', analyze.files.stats().total);console.log('Files fixed:', fix.files.stats().modified);console.log('Tests passed:', test.tools.succeeded().length);}); -
Run the workflow
Terminal window vitest workflows/refactor-pipeline.ts -
See the output
✓ workflows/refactor-pipeline.ts (1)✓ refactor pipeline (45.2s)- Stage: analyze (12.1s)- Stage: fix (28.3s)- Stage: test (4.8s)Pipeline complete!Issues found: 8Files fixed: 5Tests passed: 12
Understanding Workflow Context
Section titled “Understanding Workflow Context”The WorkflowContext
(passed as wf
) provides:
wf.stage(name, opts)
Section titled “wf.stage(name, opts)”Execute one stage of the workflow:
const result = await wf.stage('stage-name', { prompt: 'What to do', model: 'claude-3-5-sonnet-latest', // Optional workspace: '/path/to/workspace', // Optional // ... all RunAgentOptions});
Returns an AgentExecution
(awaitable) with the RunResult
.
Cumulative State
Section titled “Cumulative State”Access data across all stages:
vibeWorkflow('multi-stage', async (wf) => { await wf.stage('stage1', { prompt: 'Create files' }); await wf.stage('stage2', { prompt: 'Modify files' });
// Access all files changed across both stages const allFiles = wf.files.allChanged(); console.log('Total files changed:', allFiles.length);
// Get files from specific stage const stage1Files = wf.files.byStage('stage1'); const stage2Files = wf.files.byStage('stage2');
// Access all tool calls with stage context const allTools = wf.tools.all(); for (const { stage, call } of allTools) { console.log(`[${stage}] Used ${call.name}`); }});
Timeline Events
Section titled “Timeline Events”Access unified timeline across stages:
// Iterate over all events with stage contextfor await (const { stage, evt } of wf.timeline.events()) { console.log(`[${stage}] ${evt.type} at ${evt.timestamp}`);}
Passing Data Between Stages
Section titled “Passing Data Between Stages”Stages can communicate via:
1. File System (Recommended)
Section titled “1. File System (Recommended)”vibeWorkflow('data passing', async (wf) => { // Stage 1: Analyze and write report const analyze = await wf.stage('analyze', { prompt: 'Analyze src/ and write report to analysis.json' });
// Stage 2: Read report and fix const fix = await wf.stage('fix', { prompt: 'Read analysis.json and fix all issues' });});
2. Bundle Directory
Section titled “2. Bundle Directory”vibeWorkflow('using bundles', async (wf) => { const analyze = await wf.stage('analyze', { prompt: 'Analyze code' });
// Pass bundle path to next stage const fix = await wf.stage('fix', { prompt: `Fix issues. Analysis results: ${analyze.bundleDir}` });});
3. Context Parameter (Advanced)
Section titled “3. Context Parameter (Advanced)”vibeWorkflow('with context', async (wf) => { const analyze = await wf.stage('analyze', { prompt: 'Analyze code' });
// Pass previous result as context const fix = await wf.stage('fix', { prompt: 'Fix issues found', context: analyze // Passes full RunResult });});
Loop Patterns with until()
Section titled “Loop Patterns with until()”The until()
helper enables retry logic and iterative workflows:
Basic Retry Loop
Section titled “Basic Retry Loop”vibeWorkflow('retry until success', async (wf) => { // Run until tests pass (max 3 attempts) const results = await wf.until( (latest) => latest.tools.succeeded().length > 0, () => wf.stage('test', { prompt: '/test' }), { maxIterations: 3 } );
console.log(`Tests passed after ${results.length} attempts`);});
Convergence Loop
Section titled “Convergence Loop”vibeWorkflow('fix until clean', async (wf) => { let iteration = 0;
const results = await wf.until( (latest) => { // Stop when no files changed (converged) const filesChanged = latest.files.stats().total; console.log(`Iteration ${iteration++}: ${filesChanged} files changed`); return filesChanged === 0; }, () => wf.stage('fix-iteration', { prompt: '/fix --auto' }), { maxIterations: 5 } );
console.log(`Converged after ${results.length} iterations`);});
Cost-Aware Loop
Section titled “Cost-Aware Loop”vibeWorkflow('fix with budget', async (wf) => { let totalCost = 0;
const results = await wf.until( (latest) => { totalCost += latest.metrics.totalCostUsd ?? 0;
// Stop if all issues fixed OR budget exceeded const issuesRemaining = latest.tools.failed().length; return issuesRemaining === 0 || totalCost > 10.0; }, () => wf.stage('fix', { prompt: '/fix' }), { maxIterations: 10 } );
console.log(`Total cost: $${totalCost.toFixed(2)}`);});
Complete Example: CI/CD Pipeline
Section titled “Complete Example: CI/CD Pipeline”Here’s a production-ready workflow with error handling, retries, and reporting:
import { vibeWorkflow } from '@dao/vibe-check';import { writeFileSync } from 'fs';
vibeWorkflow('ci/cd pipeline', async (wf) => { console.log('🚀 Starting CI/CD pipeline...\n');
// Stage 1: Lint and format console.log('📝 Stage 1: Linting...'); const lint = await wf.stage('lint', { prompt: 'Run linter and fix all issues automatically' });
if (lint.tools.failed().length > 0) { console.error('❌ Linting failed'); return; // Exit early } console.log('✅ Linting passed\n');
// Stage 2: Type checking console.log('🔍 Stage 2: Type checking...'); const typecheck = await wf.stage('typecheck', { prompt: 'Run TypeScript type checker and fix all errors' });
if (typecheck.tools.failed().length > 0) { console.error('❌ Type check failed'); return; } console.log('✅ Type check passed\n');
// Stage 3: Run tests with retries console.log('🧪 Stage 3: Testing...'); const testResults = await wf.until( (latest) => { // Check if tests passed const bashCalls = latest.tools.all().filter(t => t.name === 'Bash'); const testRun = bashCalls.find(t => t.input.command?.includes('vitest') ); return testRun?.ok ?? false; }, () => wf.stage('test-retry', { prompt: '/test --fix-failures' }), { maxIterations: 3 } );
if (testResults[testResults.length - 1].tools.succeeded().length === 0) { console.error('❌ Tests failed after 3 attempts'); return; } console.log(`✅ Tests passed (${testResults.length} attempts)\n`);
// Stage 4: Build console.log('📦 Stage 4: Building...'); const build = await wf.stage('build', { prompt: '/build --production' });
if (build.tools.failed().length > 0) { console.error('❌ Build failed'); return; } console.log('✅ Build succeeded\n');
// Generate report const report = { pipeline: 'ci/cd', stages: { lint: { filesChanged: lint.files.stats().total, cost: lint.metrics.totalCostUsd }, typecheck: { filesChanged: typecheck.files.stats().total, cost: typecheck.metrics.totalCostUsd }, test: { attempts: testResults.length, cost: testResults.reduce((sum, r) => sum + (r.metrics.totalCostUsd ?? 0), 0 ) }, build: { duration: build.metrics.durationMs, cost: build.metrics.totalCostUsd } }, totalCost: wf.files.allChanged().reduce((sum, f) => { // Calculate from all stages (simplified) return sum; }, 0) };
writeFileSync('pipeline-report.json', JSON.stringify(report, null, 2)); console.log('📊 Report saved to pipeline-report.json'); console.log('\n✅ Pipeline complete!');});
Workflow Defaults
Section titled “Workflow Defaults”Set defaults for all stages to avoid repetition:
vibeWorkflow('deployment', async (wf) => { // All stages inherit these defaults await wf.stage('deploy-backend', { prompt: '/deploy backend' // Uses workspace and model from defaults });
await wf.stage('deploy-frontend', { prompt: '/deploy frontend' // Uses workspace and model from defaults });
// Override defaults for specific stage await wf.stage('deploy-docs', { prompt: '/deploy', workspace: '/path/to/docs-repo', // Override workspace model: 'claude-3-5-haiku-latest' // Override model });
}, { // Workflow options with defaults timeout: 600000, // 10 minutes defaults: { workspace: '/path/to/main-repo', model: 'claude-3-5-sonnet-latest' }});
Multi-Workspace Workflows
Section titled “Multi-Workspace Workflows”Some workflows span multiple repositories:
vibeWorkflow('full-stack deployment', async (wf) => { // Deploy backend (main repo) await wf.stage('deploy-backend', { workspace: '/repos/backend', prompt: '/deploy --production' });
// Deploy frontend (different repo) await wf.stage('deploy-frontend', { workspace: '/repos/frontend', prompt: '/deploy --production' });
// Update docs (third repo) await wf.stage('update-docs', { workspace: '/repos/docs', prompt: '/update-version' });});
Error Handling
Section titled “Error Handling”Unlike tests, workflows should handle errors gracefully:
vibeWorkflow('resilient pipeline', async (wf) => { const analyze = await wf.stage('analyze', { prompt: '/analyze' });
// Check for failures if (analyze.tools.failed().length > 0) { console.error('Analysis failed:', analyze.tools.failed());
// Try recovery stage const recover = await wf.stage('recover', { prompt: '/recover-from-failure' });
if (recover.tools.failed().length > 0) { console.error('Recovery failed. Aborting pipeline.'); return; // Exit workflow } }
// Continue with next stage await wf.stage('deploy', { prompt: '/deploy' });});
Workflow Modifiers
Section titled “Workflow Modifiers”Like vibeTest
, workflows support modifiers:
vibeWorkflow.skip('not ready', async (wf) => { // Workflow is skipped});
vibeWorkflow.only('focus on this', async (wf) => { // Only this workflow runs});
vibeWorkflow.todo('implement later', async (wf) => { // Marked as TODO});
Real-World Examples
Section titled “Real-World Examples”Database Migration
Section titled “Database Migration”vibeWorkflow('database migration', async (wf) => { // Backup database await wf.stage('backup', { prompt: '/backup database --timestamp' });
// Run migration const migrate = await wf.stage('migrate', { prompt: '/migrate database --production' });
// Verify migration const verify = await wf.stage('verify', { prompt: '/verify migration succeeded' });
if (verify.tools.failed().length > 0) { // Rollback if verification failed await wf.stage('rollback', { prompt: '/rollback migration' }); }});
Monorepo Refactor
Section titled “Monorepo Refactor”vibeWorkflow('monorepo refactor', async (wf) => { const packages = ['@app/core', '@app/ui', '@app/api'];
for (const pkg of packages) { await wf.stage(`refactor-${pkg}`, { workspace: `/monorepo/packages/${pkg}`, prompt: '/refactor --modernize' }); }
// Update dependencies after all refactors await wf.stage('update-deps', { workspace: '/monorepo', prompt: '/update dependencies --all-packages' });});
Documentation Generation
Section titled “Documentation Generation”vibeWorkflow('generate docs', async (wf) => { // Generate API docs from code await wf.stage('api-docs', { prompt: '/generate-docs --api' });
// Generate guides from examples await wf.stage('guides', { prompt: '/generate-docs --guides' });
// Build and deploy await wf.stage('deploy-docs', { workspace: '/repos/docs-site', prompt: '/build-and-deploy' });});
Best Practices
Section titled “Best Practices”✅ DO: Log Progress
Section titled “✅ DO: Log Progress”vibeWorkflow('pipeline', async (wf) => { console.log('Starting stage 1...'); const result = await wf.stage('stage1', { prompt: '...' }); console.log(`Stage 1 complete: ${result.files.stats().total} files changed`);});
✅ DO: Handle Errors Gracefully
Section titled “✅ DO: Handle Errors Gracefully”if (result.tools.failed().length > 0) { console.error('Stage failed, attempting recovery...'); await wf.stage('recover', { prompt: '/recover' });}
✅ DO: Use Descriptive Stage Names
Section titled “✅ DO: Use Descriptive Stage Names”await wf.stage('analyze-typescript-errors', { prompt: '...' });await wf.stage('fix-typescript-errors', { prompt: '...' });await wf.stage('verify-typescript-errors-fixed', { prompt: '...' });
❌ DON’T: Use expect() Assertions
Section titled “❌ DON’T: Use expect() Assertions”// ❌ Bad: Workflows don't use assertionsvibeWorkflow('bad', async (wf) => { const result = await wf.stage('test', { prompt: '...' }); expect(result).toCompleteAllTodos(); // Wrong!});
// ✅ Good: Log and handle errorsvibeWorkflow('good', async (wf) => { const result = await wf.stage('test', { prompt: '...' }); if (result.todos.some(t => t.status !== 'completed')) { console.error('Some TODOs incomplete'); }});
❌ DON’T: Forget to await stages
Section titled “❌ DON’T: Forget to await stages”// ❌ Bad: Missing awaitconst result = wf.stage('test', { prompt: '...' });
// ✅ Good: Always awaitconst result = await wf.stage('test', { prompt: '...' });
Next Steps
Section titled “Next Steps”You’ve learned how to build automation workflows! Now explore:
- First Evaluation → - Matrix testing and benchmarking
- vibeWorkflow API → - Complete workflow API reference
- Building Workflows Guide → - Advanced patterns
- Loop Patterns Guide → - Mastering
until()
- Error Handling Guide → - Production-ready error strategies
Reference
Section titled “Reference”- WorkflowContext → - Complete context interface
- runAgent() → - Agent execution options
- RunResult → - Captured execution data