### Example JSON Workflow Definition Source: https://github.com/mark-hingston/opencode-workflows/blob/main/README.md A comprehensive example of a JSON workflow definition for deploying to production. It includes steps for checking git status, running tests, awaiting approval, and executing the deployment script. ```json { "$schema": "https://raw.githubusercontent.com/mark-hingston/opencode-workflows/main/schemas/workflow.schema.json", // Unique workflow identifier "id": "deploy-prod", "description": "Deploys the application to production", "inputs": { "version": "string" }, "steps": [ { "id": "check-git", "type": "shell", "command": "git status --porcelain", "description": "Ensure git is clean" }, { "id": "run-tests", "type": "shell", "command": "npm test", "after": ["check-git"] }, { "id": "ask-approval", "type": "suspend", "description": "Wait for user to approve deployment", "after": ["run-tests"] }, { "id": "deploy-script", "type": "shell", "command": "npm run deploy -- --tag {{inputs.version}}", "after": ["ask-approval"] } ] } ``` -------------------------------- ### Install opencode-workflows Plugin Source: https://github.com/mark-hingston/opencode-workflows/blob/main/README.md Add the opencode-workflows plugin to your OpenCode configuration to automatically load the newest version on startup. ```json // opencode.jsonc { "plugin": ["opencode-workflows@latest"] } ``` -------------------------------- ### Schedule Workflow Execution with Cron Source: https://github.com/mark-hingston/opencode-workflows/blob/main/README.md Configure a workflow to run on a cron schedule. This example sets up a nightly backup to run daily at 2 AM. ```json { "id": "nightly-backup", "trigger": { "schedule": "0 2 * * *" }, "steps": [ { "id": "backup", "type": "shell", "command": "backup.sh" } ] } ``` -------------------------------- ### Iterator with Multiple Steps Source: https://github.com/mark-hingston/opencode-workflows/blob/main/README.md Use 'runSteps' to execute a sequence of steps for each item in the iterator. This example clones a repository and then runs tests within it. ```yaml - id: process-repos type: iterator items: "{{inputs.repos}}" runSteps: - id: clone type: shell command: git clone {{inputs.item.url}} - id: test type: shell command: npm test cwd: "./{{inputs.item.name}}" ``` -------------------------------- ### Install isolated-vm for Enhanced Security Source: https://github.com/mark-hingston/opencode-workflows/blob/main/README.md For production environments handling untrusted code, install the 'isolated-vm' package to enable true V8 isolate sandboxing. This provides memory isolation and prevents sandbox escape attacks. ```bash npm install isolated-vm ``` -------------------------------- ### Text Log Format Example Source: https://github.com/mark-hingston/opencode-workflows/blob/main/README.md The default text log format for opencode-workflows, displaying log level and contextual information in a human-readable format. ```text [workflow] [INFO] [workflow=deploy-prod run=abc-1234 step=build duration=5000ms] Step completed ``` -------------------------------- ### JSON Log Format Example Source: https://github.com/mark-hingston/opencode-workflows/blob/main/README.md Example of the structured JSON log format, including timestamp, level, message, and contextual identifiers. ```json {"timestamp":"2024-01-15T10:30:00.000Z","level":"info","message":"Workflow deploy-prod completed successfully","workflowId":"deploy-prod","runId":"abc-123","durationMs":45000} ``` -------------------------------- ### Programmatic Trigger Setup and File Change Simulation Source: https://context7.com/mark-hingston/opencode-workflows/llms.txt Set up workflow triggers programmatically and simulate file change events for testing. This TypeScript code demonstrates internal usage for trigger management. ```typescript // Programmatic trigger setup (internal usage) import { setupTriggers, handleFileChange, createTriggerState } from "opencode-workflows/triggers"; const state = createTriggerState(); const result = setupTriggers(state, definitions, runner, log); console.log(`${result.cronCount} cron(s), ${result.eventCount} file-change trigger(s)`); // Manually fire a file change (simulating an editor save event) const triggered = handleFileChange(state, definitions, runner, log, "src/api/auth.ts"); console.log("Triggered workflows:", triggered); // ["test-on-save-yaml"] ``` -------------------------------- ### Secure API Deployment Workflow Source: https://github.com/mark-hingston/opencode-workflows/blob/main/README.md Example of a workflow that deploys a service and notifies via HTTP, using sensitive inputs for API tokens and webhook secrets. Sensitive inputs are masked in logs and encrypted in the database. ```json { "id": "secure-deploy", "description": "Deploy with encrypted credentials", "inputs": { "version": "string", "apiToken": "string", "webhookSecret": "string" }, "secrets": ["apiToken", "webhookSecret"], "steps": [ { "id": "deploy", "type": "shell", "command": "deploy --version={{inputs.version}} --token={{inputs.apiToken}}" }, { "id": "notify", "type": "http", "method": "POST", "url": "https://api.example.com/webhooks", "headers": { "Authorization": "Bearer {{inputs.webhookSecret}}" }, "body": { "version": "{{inputs.version}}", "status": "deployed" }, "after": ["deploy"] } ] } ``` -------------------------------- ### Workflow Tool: Graph Command Alias Source: https://github.com/mark-hingston/opencode-workflows/blob/main/README.md An example of the 'workflow graph' command using a slash alias, if configured. This provides a shorthand for visualizing workflow graphs. ```bash /workflow graph deploy-prod ``` -------------------------------- ### Trigger Workflow on File Changes Source: https://github.com/mark-hingston/opencode-workflows/blob/main/README.md Set up a workflow to trigger when files matching a glob pattern change. This example runs tests when any TypeScript file in the 'src' directory changes. ```json { "id": "test-on-save", "trigger": { "event": "file.change", "pattern": "src/**/*.ts" }, "steps": [ { "id": "run-tests", "type": "shell", "command": "npm test" } ] } ``` -------------------------------- ### TypeScript Workflow Definition for Production Deployment Source: https://github.com/mark-hingston/opencode-workflows/blob/main/README.md Define a production deployment workflow using TypeScript for type safety and code reuse. This example includes build and deploy steps. ```typescript // .opencode/workflows/deploy.ts import type { WorkflowDefinition } from "opencode-workflows"; const workflow: WorkflowDefinition = { id: "deploy-prod", description: "Deploy to production with type safety", inputs: { version: "string", environment: "string", }, steps: [ { id: "build", type: "shell", command: "npm run build", }, { id: "deploy", type: "shell", command: "deploy --version={{inputs.version}} --env={{inputs.environment}}", after: ["build"], }, ], }; export default workflow; ``` -------------------------------- ### Iterator Results Collection Source: https://github.com/mark-hingston/opencode-workflows/blob/main/README.md This example shows how to use the results from a previous iterator step ('lint-files') in a subsequent shell step. It references the count of processed items. ```json { "id": "use-results", "type": "shell", "command": "echo 'Processed {{steps.lint-files.count}} files'", "after": ["lint-files"] } ``` -------------------------------- ### Iterator with RunSteps Output Source: https://github.com/mark-hingston/opencode-workflows/blob/main/README.md When using 'runSteps', the output for each iteration is an object containing the results of all sequential steps. This example shows the structure for 'clone' and 'test' steps. ```json { "results": [ { "clone": { "stdout": "...", "exitCode": 0 }, "test": { "stdout": "...", "exitCode": 0 } }, { "clone": { "stdout": "...", "exitCode": 0 }, "test": { "stdout": "...", "exitCode": 0 } } ], "count": 2 } ``` -------------------------------- ### YAML Workflow Definition for Code Review Source: https://github.com/mark-hingston/opencode-workflows/blob/main/README.md Define a multi-agent code review workflow in YAML. This example shows how to read a file and use agents for security analysis and synthesizing a report. ```yaml $schema: https://raw.githubusercontent.com/mark-hingston/opencode-workflows/main/schemas/workflow.schema.json id: code-review name: Multi-Agent Code Review description: Parallel expert code review with synthesis inputs: file: string steps: - id: read_file type: file description: Read the source file action: read path: "{{inputs.file}}" - id: security_review type: agent description: Security vulnerability analysis agent: security-reviewer after: [read_file] # Multi-line prompts are clean and readable! prompt: | Review this code for security issues: {{steps.read_file.content}} - id: synthesize type: agent description: Combine reviews into a report agent: tech-lead after: [security_review] prompt: | Combine these reviews into a prioritized report: ## Security Review {{steps.security_review.response}} ``` -------------------------------- ### Dynamic TypeScript Workflow Factory Function Source: https://context7.com/mark-hingston/opencode-workflows/llms.txt Generates workflow steps dynamically at runtime, for example, to deploy to multiple regions based on fetched data. Uses an async function that returns a `WorkflowDefinition`. ```typescript // Dynamic factory function — generates steps at runtime export default async function(): Promise { const regions = await fetchRegions(); // ["us-east-1", "eu-west-1"] return { id: "multi-region-deploy", steps: regions.map((region, i) => ({ id: `deploy-${region}`, type: "shell" as const, command: `deploy --region ${region}`, after: i > 0 ? [`deploy-${regions[i - 1]}`] : [], })), }; } ``` -------------------------------- ### Pass Changed File Path as Input Source: https://github.com/mark-hingston/opencode-workflows/blob/main/README.md Trigger a workflow on file changes and pass the path of the changed file to a shell command. This example lints files matching specific TypeScript extensions. ```json { "id": "lint-on-save", "trigger": { "event": "file.change", "pattern": "**/*.{ts,tsx}" }, "steps": [ { "id": "lint", "type": "shell", "command": "eslint {{inputs.changedFile}}" } ] } ``` -------------------------------- ### Read from File Source: https://github.com/mark-hingston/opencode-workflows/blob/main/README.md Use the File step with the 'read' action to read the content of a file from the specified path. The content will be available in the step's output. ```json { "id": "read-config", "type": "file", "action": "read", "path": "./config.json" } ``` -------------------------------- ### Input Validation Error Message Source: https://github.com/mark-hingston/opencode-workflows/blob/main/README.md Example of an error message displayed when required workflow inputs are not provided. It lists the missing inputs and suggests how to supply them. ```text $ workflow tool call (mode=run, workflowId=deploy-prod) Missing required input(s) for workflow **deploy-prod**: - **version** (string) Usage: supply `version` via the workflow tool (mode=run workflowId=deploy-prod params.version=) or `/workflow run deploy-prod version=` if you've configured a slash alias. ``` -------------------------------- ### Define Step Dependencies in JSON Source: https://github.com/mark-hingston/opencode-workflows/blob/main/README.md Declare dependencies for a shell step using the 'after' field. This ensures the 'deploy' step only runs after 'build' and 'test' have completed. ```json { "id": "deploy", "type": "shell", "command": "deploy.sh", "after": ["build", "test"] } ``` -------------------------------- ### Run Multi-Agent Code Review Workflow Source: https://github.com/mark-hingston/opencode-workflows/blob/main/README.md Command to execute the 'code-review' workflow using the workflow tool, specifying the input file. Alternatively, a slash alias can be used if configured. ```json { "mode": "run", "workflowId": "code-review", "params": { "file": "src/api/auth.ts" } } ``` ```bash /workflow run code-review file=src/api/auth.ts ``` -------------------------------- ### Write to File Source: https://github.com/mark-hingston/opencode-workflows/blob/main/README.md The File step with the 'write' action creates or overwrites a file at the specified path with the given content. Content can be a string or an interpolated value. ```json { "id": "write-version", "type": "file", "action": "write", "path": "./version.txt", "content": "{{inputs.version}}" } ``` -------------------------------- ### File Operations: Read, Write, Delete Source: https://context7.com/mark-hingston/opencode-workflows/llms.txt The File step allows reading, writing, or deleting files. Content from read operations is available via step output. ```yaml id: version-bump inputs: version: string steps: - id: read-config type: file action: read path: ./package.json - id: write-version type: file action: write path: ./VERSION.txt content: "{{inputs.version}}" after: [read-config] - id: delete-lock type: file action: delete path: ./deploy.lock after: [write-version] ``` -------------------------------- ### Load Workflows from Directories Source: https://context7.com/mark-hingston/opencode-workflows/llms.txt Scan directories for workflow definition files (.json, .yaml, .ts, etc.), validate them, infer dependencies, and check for circular references. TypeScript/JS files are loaded dynamically without a build step. ```typescript import { loadWorkflows, createLogger } from "opencode-workflows/loader"; const log = createLogger({ verbose: true }); const { workflows, errors } = await loadWorkflows( process.cwd(), log, [".opencode/workflows", "~/.opencode/workflows"] // default dirs ); if (errors.length > 0) { console.error("Failed to load some workflows:", errors); } for (const [id, def] of workflows) { console.log(`Loaded: ${id} — ${def.steps.length} step(s)`); } // Output: Loaded: deploy-prod — 5 step(s) // Output: Loaded: code-review — 7 step(s) ``` -------------------------------- ### Invoke OpenCode Tools Source: https://github.com/mark-hingston/opencode-workflows/blob/main/README.md Use the 'tool' type to invoke pre-defined OpenCode tools. Specify the tool name and its arguments. ```json { "id": "send-notification", "type": "tool", "tool": "slack_send", "args": { "channel": "#releases", "text": "Deployed {{inputs.version}}" } } ``` -------------------------------- ### Secure Shell Execution with Arguments Source: https://github.com/mark-hingston/opencode-workflows/blob/main/README.md In 'shell' steps, use 'safe: true' with an 'args' array to prevent shell injection when handling user input. This bypasses the shell interpreter. ```yaml - id: secure-echo type: shell command: echo safe: true args: ["Hello", "{{inputs.userInput}}"] ``` -------------------------------- ### Configure HTTP Request Source: https://github.com/mark-hingston/opencode-workflows/blob/main/README.md Use the HTTP step to make external API requests. Specify the method, URL, headers, and body. Set failOnError to true to halt the workflow if the request fails. ```json { "id": "notify-slack", "type": "http", "method": "POST", "url": "https://hooks.slack.com/services/xxx", "headers": { "Content-Type": "application/json" }, "body": { "text": "Deployed {{inputs.version}}" }, "failOnError": true } ``` -------------------------------- ### Execute Shell Commands Source: https://github.com/mark-hingston/opencode-workflows/blob/main/README.md Use the 'shell' type to execute shell commands. Configure working directory, environment variables, and retry logic. 'failOnError' defaults to true. ```json { "id": "build", "type": "shell", "command": "npm run build", "cwd": "./packages/app", "env": { "NODE_ENV": "production" }, "failOnError": true, "timeout": 60000, "retry": { "attempts": 3, "delay": 1000 } } ``` -------------------------------- ### Configure opencode-workflows Logging Source: https://github.com/mark-hingston/opencode-workflows/blob/main/README.md Configure structured logging for observability. Use JSON format for log aggregation systems or a custom output handler. Logs include contextual information like workflowId, runId, and stepId. ```typescript import { createLogger } from "opencode-workflows/loader"; // JSON format for log aggregation (e.g., DataDog, Splunk, CloudWatch) const logger = createLogger({ format: "json", verbose: true }); ``` ```typescript // Custom output handler const logger = createLogger({ output: (entry) => { // entry: { timestamp, level, message, workflowId?, runId?, stepId?, durationMs?, metadata? } myLogAggregator.send(entry); } }); ``` -------------------------------- ### Workflow Tool: Graph Command Source: https://github.com/mark-hingston/opencode-workflows/blob/main/README.md Use the 'workflow graph' command to visualize the workflow's step dependencies as a Mermaid diagram. This helps in understanding the execution flow and relationships between steps. ```json { "mode": "graph", "workflowId": "deploy-prod" } ``` -------------------------------- ### Iterate and Deploy Services Source: https://github.com/mark-hingston/opencode-workflows/blob/main/README.md This Iterator configuration demonstrates accessing nested properties of objects within the 'items' array. Each service is deployed using its name and region. ```json { "id": "deploy-services", "type": "iterator", "items": "{{inputs.services}}", "runStep": { "type": "shell", "command": "deploy {{inputs.item.name}} --region {{inputs.item.region}}" } } ``` -------------------------------- ### Dynamic Workflow Generation with Eval Step Source: https://github.com/mark-hingston/opencode-workflows/blob/main/README.md Eval steps can dynamically generate workflows by returning a WorkflowDefinition object. This enables agentic planning where the workflow structure is determined at runtime. The generated workflow is then validated and executed as a sub-workflow. ```json { "id": "plan-deployment", "type": "eval", "script": "return { workflow: { id: 'dynamic-deploy', steps: inputs.services.map(s => ({ id: `deploy-${s}`, type: 'shell', command: `deploy ${s}` })) } };" } ``` -------------------------------- ### Create a Structured Logger Source: https://context7.com/mark-hingston/opencode-workflows/llms.txt Create a logger instance for structured logging, supporting text or JSON output, custom handlers, and verbose debug mode. Useful for consistent logging across workflow steps. ```typescript import { createLogger } from "opencode-workflows/loader"; // Human-readable text logger (default) const log = createLogger({ verbose: true }); log.info("Workflow started", { workflowId: "deploy-prod", runId: "abc-123" }); // Output: [workflow] [INFO] [workflow=deploy-prod run=abc-1234] Workflow started ``` ```typescript // JSON structured logger for log aggregation (DataDog, Splunk, etc.) const jsonLog = createLogger({ format: "json" }); jsonLog.warn("Step retrying", { workflowId: "build", stepId: "compile", durationMs: 500 }); // Output: {"timestamp":"2024-01-15T10:30:00.000Z","level":"warn","message":"Step retrying","workflowId":"build","stepId":"compile","durationMs":500} ``` ```typescript // Custom output handler const customLog = createLogger({ output: (entry) => { // entry: StructuredLogEntry { timestamp, level, message, workflowId?, runId?, stepId?, durationMs? } myObservabilityService.send(entry); }, }); ``` ```typescript // Suppress console output (used internally by plugin to avoid TUI interference) const silentLog = createLogger({ console: false }); ``` -------------------------------- ### Direct LLM Call with System Prompt Source: https://github.com/mark-hingston/opencode-workflows/blob/main/README.md Use the 'agent' type for direct LLM calls when a named agent is not suitable. Include a 'prompt' and an optional 'system' prompt. 'maxTokens' limits the response length. ```json { "id": "generate-changelog", "type": "agent", "prompt": "Generate a changelog for version {{inputs.version}}", "system": "You are a technical writer.", "maxTokens": 1000 } ``` -------------------------------- ### Register Slash Commands in opencode.jsonc Source: https://github.com/mark-hingston/opencode-workflows/blob/main/README.md Define custom slash commands in your opencode.jsonc file to create shortcuts for workflow execution. This allows for easier invocation of predefined workflows directly from the OpenCode interface. ```jsonc // opencode.jsonc or ~/.config/opencode/opencode.jsonc { "plugin": ["opencode-workflows@latest"], "command": { "workflow-list": { "template": "Use the workflow tool with mode=list", "description": "List all workflows" }, "workflow-graph": { "template": "Use the workflow tool with mode=graph and workflowId=$ARGUMENTS", "description": "Show a workflow DAG as Mermaid" }, "workflow-run": { "template": "Use the workflow tool with mode=run and workflowId=$ARGUMENTS", "description": "Start a workflow run" }, "workflow-status": { "template": "Use the workflow tool with mode=status and runId=$ARGUMENTS", "description": "Check workflow run status" } } } ``` -------------------------------- ### Iterate and Lint Files Source: https://github.com/mark-hingston/opencode-workflows/blob/main/README.md The Iterator step processes each item in an array using a defined 'runStep'. Use '{{inputs.item}}' to reference the current item and '{{inputs.index}}' for its position. ```json { "id": "lint-files", "type": "iterator", "items": "{{steps.find-files.result}}", "runStep": { "type": "shell", "command": "eslint {{inputs.item}}" } } ``` -------------------------------- ### Workflow Triggers: Cron and File Change Source: https://context7.com/mark-hingston/opencode-workflows/llms.txt Automate workflow execution using cron schedules or file system events. File change triggers debounce events and provide the changed file path as input. ```yaml # Cron trigger — runs daily at 2am id: nightly-backup trigger: schedule: "0 2 * * *" steps: - id: backup type: shell command: ./scripts/backup.sh --- # File change trigger — runs tests on every TypeScript save id: test-on-save-yaml trigger: event: file.change pattern: "src/**/*.{ts,tsx}" steps: - id: run-tests type: shell command: npm test - id: lint-changed type: shell command: eslint {{inputs.changedFile}} after: [run-tests] ``` -------------------------------- ### Multi-Agent Code Review Workflow Source: https://github.com/mark-hingston/opencode-workflows/blob/main/README.md This JSON defines a workflow that chains multiple specialized agents for parallel code review (security, performance, quality) and then synthesizes their findings. It includes a suspend step for human approval before generating fixes. Assumes named agents are configured in OpenCode. ```json { "id": "code-review", "name": "Multi-Agent Code Review", "description": "Parallel expert review with synthesis", "inputs": { "file": "string" }, "steps": [ { "id": "read_file", "type": "tool", "tool": "read", "args": { "filePath": "{{inputs.file}}" } }, { "id": "security_review", "type": "agent", "agent": "security-reviewer", "prompt": "Review this code for security issues:\n\n{{steps.read_file.result}}", "after": ["read_file"] }, { "id": "perf_review", "type": "agent", "agent": "performance-reviewer", "prompt": "Review this code for performance issues:\n\n{{steps.read_file.result}}", "after": ["read_file"] }, { "id": "quality_review", "type": "agent", "agent": "quality-reviewer", "prompt": "Review this code for quality issues:\n\n{{steps.read_file.result}}", "after": ["read_file"] }, { "id": "synthesize", "type": "agent", "agent": "tech-lead", "prompt": "Combine these reviews into a single report:\n\n## Security\n{{steps.security_review.response}}\n\n## Performance\n{{steps.perf_review.response}}\n\n## Quality\n{{steps.quality_review.response}}", "after": ["security_review", "perf_review", "quality_review"] }, { "id": "approve_fixes", "type": "suspend", "message": "Review complete:\n\n{{steps.synthesize.response}}\n\nResume to generate fixes.", "after": ["synthesize"] }, { "id": "generate_fixes", "type": "agent", "agent": "code-fixer", "prompt": "Fix the critical and high severity issues:\n\nOriginal:\n{{steps.read_file.result}}\n\nIssues:\n{{steps.synthesize.response}}", "after": ["approve_fixes"] } ] } ``` -------------------------------- ### Resume Workflow with Data Source: https://github.com/mark-hingston/opencode-workflows/blob/main/README.md Resume a suspended workflow using the workflow tool with 'mode: resume', providing the 'runId' and 'resumeData'. ```json { "mode": "resume", "runId": "", "resumeData": { "approved": true, "comment": "LGTM" } } ``` -------------------------------- ### Workflow Plugin Configuration Source: https://context7.com/mark-hingston/opencode-workflows/llms.txt Configures the opencode-workflows plugin, specifying workflow directories, database path, timeouts, verbosity, and limits on completed runs and run age. Environment variables can override these defaults. ```typescript import type { WorkflowPluginConfig } from "opencode-workflows"; const config: WorkflowPluginConfig = { workflowDirs: [".opencode/workflows", "~/.opencode/workflows"], dbPath: ".opencode/data/workflows.db", timeout: 300000, // 5 minutes verbose: false, maxCompletedRuns: 1000, maxRunAge: 30, // days before terminal runs are deleted }; // Environment variable overrides: // WORKFLOW_DIRS — comma-separated directory list // WORKFLOW_DB_PATH — SQLite database path // WORKFLOW_TIMEOUT — global timeout in milliseconds // WORKFLOW_VERBOSE — "true" / "false" // WORKFLOW_MAX_COMPLETED_RUNS — integer // WORKFLOW_MAX_RUN_AGE — integer (days) // WORKFLOW_ENCRYPTION_KEY — exactly 32-character AES-256 key ``` -------------------------------- ### Initializing and Running Workflows with WorkflowRunner Source: https://context7.com/mark-hingston/opencode-workflows/llms.txt WorkflowRunner executes compiled workflows using the Mastra engine with SQLite persistence. It supports timeouts, session-aware progress reporting, and lifecycle hooks. The `run()` method returns a `runId` immediately, with execution continuing in the background. ```typescript import { WorkflowRunner, WorkflowStorage } from "opencode-workflows/utils"; const storage = new WorkflowStorage( { dbPath: ".opencode/data/workflows.db", encryptionKey: process.env.WORKFLOW_ENCRYPTION_KEY }, log ); await storage.init(); const runner = new WorkflowRunner(factory, log, storage, { timeout: 300000, // 5 minute global timeout maxCompletedRuns: 1000, }); await runner.init(); // Restores persisted active runs // Start a workflow (returns immediately with runId) const runId = await runner.run("deploy-prod", { version: "2.4.1" }, { sessionId: "sess_abc123", }); console.log("Started run:", runId); // "Started run: 550e8400-e29b-41d4-a716-446655440000" // Poll status const run = runner.getStatus(runId); console.log(run?.status); // "running" | "completed" | "suspended" | "failed" // Resume a suspended workflow (e.g., after human approval) await runner.resume(runId, { approved: true, comment: "LGTM" }); // Cancel a running workflow await runner.cancel(runId); // List recent runs const runs = runner.listRuns("deploy-prod"); // filter by workflow ID const suspended = runner.getSuspendedRuns(); // Load older runs from storage with pagination const olderRuns = await runner.loadMoreRuns(100, 0, "deploy-prod"); const total = await runner.countStorageRuns("deploy-prod"); ``` -------------------------------- ### Static TypeScript Workflow Definition Source: https://context7.com/mark-hingston/opencode-workflows/llms.txt Defines a CI/CD workflow with steps for testing, type checking, building, deploying, and notifications. Uses inputs, secrets, and environment variables. Configure notifications via HTTP POST requests. ```typescript // .opencode/workflows/ts-build-and-deploy.ts import type { WorkflowDefinition } from "opencode-workflows"; const workflow: WorkflowDefinition = { id: "ts-build-and-deploy", inputs: { environment: "string", version: "string", dryRun: "boolean", }, secrets: ["deployToken"], steps: [ { id: "test", type: "shell", command: "npm test" }, { id: "typecheck", type: "shell", command: "npm run typecheck", after: ["test"] }, { id: "build", type: "shell", command: "npm run build", after: ["test"], env: { NODE_ENV: "production", BUILD_VERSION: "{{inputs.version}}" }, }, { id: "deploy", type: "shell", command: "npm run deploy -- --env={{inputs.environment}} --version={{inputs.version}}", after: ["build", "typecheck"], condition: "{{inputs.dryRun}}==false", }, { id: "notify", type: "http", method: "POST", url: "https://hooks.slack.com/services/YOUR/WEBHOOK/URL", headers: { "Content-Type": "application/json" }, body: { text: "Deployed {{inputs.version}} to {{inputs.environment}}" }, after: ["deploy"], failOnError: false, }, ], onFailure: [ { id: "notify-failure", type: "http", method: "POST", url: "https://hooks.slack.com/services/YOUR/WEBHOOK/URL", body: { text: "FAILED: {{inputs.error.message}}" }, }, ], }; export default workflow; ``` -------------------------------- ### Execute JavaScript with Eval Step Source: https://github.com/mark-hingston/opencode-workflows/blob/main/README.md Use the 'eval' step type to execute JavaScript code in a sandboxed environment. The script has access to inputs, steps, and environment variables. Ensure scriptTimeout is set appropriately. ```json { "id": "calculate-shards", "type": "eval", "script": "return inputs.items.filter(x => x.enabled).map(x => x.id);", "scriptTimeout": 5000 } ``` -------------------------------- ### Configuring and Managing Workflow Storage with WorkflowStorage Source: https://context7.com/mark-hingston/opencode-workflows/llms.txt WorkflowStorage persists workflow runs to a SQLite database, supporting WAL mode, composite indexes, and optional AES-256-GCM encryption for sensitive inputs. It provides methods for initialization, secret management, data loading, and cleanup. ```typescript import { WorkflowStorage } from "opencode-workflows/utils"; const storage = new WorkflowStorage({ dbPath: ".opencode/data/workflows.db", encryptionKey: "my-32-char-secret-key-for-aes256!", // exactly 32 chars verbose: false, }, log); await storage.init(); // Register which inputs are secrets (encrypted at rest) storage.setWorkflowSecrets("deploy-prod", ["apiToken", "dbPassword"]); // Load all runs with pagination (newest first) const page1 = await storage.loadAllRuns(undefined, 50, 0); const page2 = await storage.loadAllRuns(undefined, 50, 50); // Load only active (non-terminal) runs for restoration on restart const activeRuns = await storage.loadActiveRuns(); // Delete terminal runs older than 30 days const cutoff = new Date(Date.now() - 30 * 24 * 60 * 60 * 1000); const deletedCount = await storage.deleteRunsOlderThan(cutoff); console.log(`Deleted ${deletedCount} old runs`); // Close gracefully (frees LibSQL client) await storage.close(); ``` -------------------------------- ### Resume Workflow via Slash Alias Source: https://github.com/mark-hingston/opencode-workflows/blob/main/README.md Alternatively, resume a workflow using a slash alias command, specifying the 'runId' and resume data in JSON format. ```bash /workflow resume {"approved": true, "comment": "LGTM"} ``` -------------------------------- ### Dynamic TypeScript Workflow Generation Source: https://github.com/mark-hingston/opencode-workflows/blob/main/README.md Export an async function to dynamically generate workflows at runtime, useful for configurations fetched from external sources like APIs or files. ```typescript // .opencode/workflows/multi-deploy.ts import type { WorkflowDefinition } from "opencode-workflows"; export default async function(): Promise { // Could fetch configuration from an API or file const regions = ["us-east-1", "eu-west-1", "ap-south-1"]; return { id: "multi-region-deploy", steps: regions.map((region, index) => ({ id: `deploy-${region}`, type: "shell" as const, command: `deploy --region ${region}`, after: index > 0 ? [`deploy-${regions[index - 1]}`] : undefined, })), }; } ``` -------------------------------- ### Workflow Definition - Agent Step Source: https://context7.com/mark-hingston/opencode-workflows/llms.txt Defines a workflow step that invokes a named OpenCode agent or makes a direct inline LLM call. Output is available via `{{steps.stepId.response}}`. ```APIDOC ## Workflow Definition — Agent Step Invokes a named OpenCode agent (via `@agent` mention syntax) or makes a direct inline LLM call. Output available as `{{steps.stepId.response}}`. ### Example Workflow Definition ```yaml id: code-review-yaml inputs: file: string steps: - id: read_file type: file action: read path: "{{inputs.file}}" - id: security_review type: agent agent: security-reviewer # Named agent defined in OpenCode after: [read_file] prompt: | Review this code for security issues: {{steps.read_file.content}} - id: inline_summary type: agent system: "You are a concise technical writer." prompt: "Summarize these findings in 3 bullet points: {{steps.security_review.response}}" maxTokens: 300 after: [security_review] ``` ``` -------------------------------- ### Template Interpolation Utilities Source: https://context7.com/mark-hingston/opencode-workflows/llms.txt Utilize functions for interpolating strings with context variables, preserving types, and handling secrets securely. Supports inputs, steps, environment variables, and run context. ```typescript import { interpolate, interpolateValue, interpolateWithSecrets, getNestedValue } from "opencode-workflows/utils"; const ctx = { inputs: { version: "2.4.1", count: 5, config: { host: "db.prod" } }, steps: { build: { stdout: "Build OK", exitCode: 0 } }, env: process.env, run: { id: "run-uuid", workflowId: "deploy-prod", startedAt: "2024-01-15T10:30:00Z" }, }; // String interpolation interpolate("Deploying {{inputs.version}} on {{env.HOSTNAME}}", ctx); // "Deploying 2.4.1 on myserver" // Type preservation — returns number 5, not string "5" interpolateValue("{{inputs.count}}", ctx); // 5 (number) interpolateValue("Count: {{inputs.count}}", ctx); // "Count: 5" (string) // Nested access interpolate("Host: {{inputs.config.host}}", ctx); // "Host: db.prod" interpolate("Build: {{steps.build.stdout}}", ctx); // "Build: Build OK" interpolate("Run ID: {{run.id}}", ctx); // "Run ID: run-uuid" // Secrets-aware interpolation const result = interpolateWithSecrets( "deploy --token {{inputs.apiToken}} --env {{inputs.env}}", { ...ctx, inputs: { apiToken: "sk-secret123", env: "prod" } }, ["apiToken"] // secret inputs ); console.log(result.value); // "deploy --token sk-secret123 --env prod" console.log(result.masked); // "deploy --token *** --env prod" console.log(result.containsSecrets); // true ``` -------------------------------- ### Suspend Workflow with Resume Schema Source: https://github.com/mark-hingston/opencode-workflows/blob/main/README.md Define a 'resumeSchema' for 'suspend' steps to enforce structured input when resuming. Resume data is validated and available via 'steps..data'. ```json { "id": "approval", "type": "suspend", "message": "Review the changes and provide approval.", "resumeSchema": { "approved": "boolean", "comment": "string" } } ``` -------------------------------- ### Conditional Step Execution Source: https://github.com/mark-hingston/opencode-workflows/blob/main/README.md Control the execution of a step based on a condition. The step will be skipped if the condition evaluates to false, 0, or an empty string. ```json { "id": "deploy-prod", "type": "shell", "command": "deploy.sh", "condition": "{{inputs.environment}}" } ``` -------------------------------- ### HTTP Request with Interpolation Source: https://context7.com/mark-hingston/opencode-workflows/llms.txt Use the HTTP step to make external requests. Supports interpolated URLs, headers, and bodies. Output includes status, parsed JSON body, raw text, and headers. ```yaml id: webhook-notify-yaml inputs: message: string channel: string steps: - id: get-git-info type: shell command: git log -1 --format='%h - %s (%an)' - id: notify-slack type: http method: POST url: "{{env.SLACK_WEBHOOK_URL}}" headers: Content-Type: application/json body: channel: "{{inputs.channel}}" text: "{{inputs.message}}" attachments: - color: "#36a64f" fields: - title: Latest Commit value: "{{steps.get-git-info.stdout}}" failOnError: false after: [get-git-info] - id: check-result type: shell command: echo "Slack responded with status {{steps.notify-slack.status}}" after: [notify-slack] ``` -------------------------------- ### JSON vs YAML for Multi-line Strings Source: https://github.com/mark-hingston/opencode-workflows/blob/main/README.md Compares JSON and YAML syntax for defining multi-line strings, such as prompts or shell scripts. YAML's block scalar operator `|` provides a cleaner and more readable format. ```json "prompt": "Review this code.\n\nLook for:\n1. Security bugs\n2. Performance issues" ``` ```yaml prompt: | Review this code. Look for: 1. Security bugs 2. Performance issues ``` -------------------------------- ### WorkflowFactory Source: https://context7.com/mark-hingston/opencode-workflows/llms.txt Compiles WorkflowDefinition objects into executable Mastra workflows. Supports lazy compilation and automatic grouping of parallel steps. ```APIDOC ## WorkflowFactory class Compiles `WorkflowDefinition` objects into executable Mastra workflows. Supports lazy compilation (workflows are compiled on first access, not at startup) for fast initialization with many definitions. Steps at the same DAG level are automatically grouped for parallel execution. ```typescript import { WorkflowFactory } from "opencode-workflows/utils"; const factory = new WorkflowFactory(opencodeClient); // Register definitions without compiling (lazy) factory.register({ id: "build-and-test", inputs: { branch: "string" }, steps: [ { id: "checkout", type: "shell", command: "git checkout {{inputs.branch}}" }, { id: "install", type: "shell", command: "npm ci", after: ["checkout"] }, { id: "test", type: "shell", command: "npm test", after: ["install"] }, { id: "build", type: "shell", command: "npm run build", after: ["install"] }, // "test" and "build" share the same DAG level → run in parallel ], }); // Compiled on first get() const compiled = factory.get("build-and-test"); console.log(compiled?.stepCount); // 4 console.log(factory.list()); // ["build-and-test"] // Immediate compilation factory.compile(myWorkflowDefinition); // Bulk registration factory.registerAll([def1, def2, def3]); ``` ``` -------------------------------- ### Workflow Definition - Shell Step Source: https://context7.com/mark-hingston/opencode-workflows/llms.txt Defines a workflow step that executes shell commands. Supports environment variables, working directory, retry, safe mode, and per-step timeout. ```APIDOC ## Workflow Definition — Shell Step Executes shell commands with support for environment variables, working directory, retry, safe mode (bypass shell for injection prevention), and per-step timeout. ### Example Workflow Definition ```yaml # .opencode/workflows/deploy-prod.yaml id: deploy-prod inputs: version: string steps: - id: check-git type: shell command: git status --porcelain failOnError: true - id: run-tests type: shell command: npm test cwd: ./packages/app env: NODE_ENV: test retry: attempts: 3 delay: 2000 timeout: 60000 - id: deploy type: shell # Safe mode: spawn directly, no shell injection possible safe: true command: deploy args: ["--version", "{{inputs.version}}", "--env", "production"] after: [run-tests] ``` ``` -------------------------------- ### Eval Step for JavaScript Execution Source: https://context7.com/mark-hingston/opencode-workflows/llms.txt The Eval step executes sandboxed JavaScript. It can return plain values or dynamic workflow definitions for agentic planning. Supports `scriptTimeout`. ```yaml id: dynamic-deploy inputs: services: array steps: - id: calculate-shards type: eval script: | const enabled = inputs.services.filter(s => s.enabled); return { count: enabled.length, ids: enabled.map(s => s.id) }; scriptTimeout: 5000 - id: plan-deployment type: eval after: [calculate-shards] # Returns { workflow: WorkflowDefinition } → executes as sub-workflow script: | return { workflow: { id: "dynamic-service-deploy", steps: inputs.services .filter(s => s.enabled) .map((s, i) => ({ id: `deploy-${s.id}`, type: "shell", command: `deploy-service ${s.id} --region ${s.region}`, })) } }; ``` -------------------------------- ### WorkflowRunner Source: https://context7.com/mark-hingston/opencode-workflows/llms.txt Executes compiled workflows via the Mastra engine with SQLite persistence, timeout control, and session-aware progress reporting. ```APIDOC ## WorkflowRunner class Executes compiled workflows via the Mastra engine with SQLite persistence, timeout control, session-aware progress reporting, and lifecycle hooks (`onFailure`, `finally`). Workflow execution is non-blocking — `run()` returns a `runId` immediately and execution continues in the background. ```typescript import { WorkflowRunner, WorkflowStorage } from "opencode-workflows/utils"; const storage = new WorkflowStorage( { dbPath: ".opencode/data/workflows.db", encryptionKey: process.env.WORKFLOW_ENCRYPTION_KEY }, log ); await storage.init(); const runner = new WorkflowRunner(factory, log, storage, { timeout: 300000, // 5 minute global timeout maxCompletedRuns: 1000, }); await runner.init(); // Restores persisted active runs // Start a workflow (returns immediately with runId) const runId = await runner.run("deploy-prod", { version: "2.4.1" }, { sessionId: "sess_abc123", }); console.log("Started run:", runId); // "Started run: 550e8400-e29b-41d4-a716-446655440000" // Poll status const run = runner.getStatus(runId); console.log(run?.status); // "running" | "completed" | "suspended" | "failed" // Resume a suspended workflow (e.g., after human approval) await runner.resume(runId, { approved: true, comment: "LGTM" }); // Cancel a running workflow await runner.cancel(runId); // List recent runs const runs = runner.listRuns("deploy-prod"); // filter by workflow ID const suspended = runner.getSuspendedRuns(); // Load older runs from storage with pagination const olderRuns = await runner.loadMoreRuns(100, 0, "deploy-prod"); const total = await runner.countStorageRuns("deploy-prod"); ``` ``` -------------------------------- ### Workflow Definition: Shell Step Source: https://context7.com/mark-hingston/opencode-workflows/llms.txt Define shell steps for executing commands, including environment variables, working directory, retries, and timeouts. Use `safe: true` to prevent shell injection. ```yaml # .opencode/workflows/deploy-prod.yaml id: deploy-prod inputs: version: string steps: - id: check-git type: shell command: git status --porcelain failOnError: true - id: run-tests type: shell command: npm test cwd: ./packages/app env: NODE_ENV: test retry: attempts: 3 delay: 2000 timeout: 60000 - id: deploy type: shell # Safe mode: spawn directly, no shell injection possible safe: true command: deploy args: ["--version", "{{inputs.version}}", "--env", "production"] after: [run-tests] ```