Skip to content

Command Types

1. Shell Commands

# Simple shell command
- shell: "cargo test"

# With output capture
- shell: "ls -la | wc -l"
  capture_output: "file_count"

# With failure handling
- shell: "cargo clippy"
  on_failure:
    claude: "/fix-warnings ${shell.output}"

# With nested failure handlers (multi-level error recovery)
- shell: "cargo test"
  on_failure:
    claude: "/debug-test-failures ${shell.output}"
    on_failure:
      shell: "notify-team.sh 'Tests failed and debugging unsuccessful'"

# With timeout
- shell: "cargo bench"
  timeout: 600  # seconds

# With conditional execution
- shell: "cargo build --release"
  when: "${tests_passed}"

# Working directory control (use shell cd command)
- shell: "cd crates/prodigy-core && cargo test"

# Environment variables (use shell syntax)
- shell: "PATH=/custom/bin:$PATH rustfmt --check src/**/*.rs"

2. Claude Commands

# Simple Claude command
- claude: "/prodigy-analyze"

# With arguments
- claude: "/prodigy-implement-spec ${spec_file}"

# With commit requirement
- claude: "/prodigy-fix-bugs"
  commit_required: true

# With output capture
- claude: "/prodigy-generate-plan"
  capture_output: "implementation_plan"

3. Foreach Commands

Iterate over a list with optional parallelism.

- foreach:
    input: "find . -name '*.rs' -type f"  # Command
    # OR
    # input: ["file1.rs", "file2.rs"]    # List

    parallel: 5  # Number of parallel executions (or true/false)

    do:
      - claude: "/analyze-file ${item}"
      - shell: "cargo check ${item}"

    continue_on_error: true
    max_items: 50

Variables and Error Handling: - ${item}: Current item value available in loop body - continue_on_error: true (default): Failed items don't stop the loop - Parallel execution caveat: Output order is not guaranteed when using parallel - No built-in result aggregation: Use write_file commands to collect results if needed

Example with result collection:

- foreach:
    input: ["module1", "module2", "module3"]
    parallel: 3
    do:
      - shell: "cargo test --package ${item}"
      - write_file:
          path: "results/${item}.txt"
          content: "Test result: ${shell.output}"
          create_dirs: true

4. Write File Commands

Create or overwrite files with content from variables or literals. Supports text, JSON, and YAML formats with automatic validation and formatting.

# Write plain text file
- write_file:
    path: "output/result.txt"
    content: "Build completed at ${shell.output}"
    format: text
    mode: "0644"
    create_dirs: true

# Write JSON file with validation
- write_file:
    path: "config/generated.json"
    content: |
      {
        "version": "${version}",
        "timestamp": "${timestamp}",
        "items": ${items_json}
      }
    format: json

# Write YAML file with formatting
- write_file:
    path: ".prodigy/metadata.yml"
    content: |
      workflow: ${workflow.name}
      iteration: ${workflow.iteration}
      results:
        success: ${map.successful}
        total: ${map.total}
    format: yaml

WriteFileConfig Fields: - path - File path to write (supports variable interpolation) - content - Content to write (supports variable interpolation) - format - Output format: text (default), json, yaml - mode - File permissions in octal (default: "0644") - create_dirs - Create parent directories if they don't exist (default: false)

Format Validation: - json - Validates JSON syntax and pretty-prints output - yaml - Validates YAML syntax and formats output - text - Writes content as-is without validation

Best Practices: - Use format validation for config files: Set format: json or format: yaml when generating configuration files to catch syntax errors early - Set appropriate permissions: Use mode field to control file permissions (e.g., "0600" for sensitive files) - Handle nested paths: Set create_dirs: true when writing to paths that may not exist - Combine with validation: Use validate field to ensure generated files meet requirements before proceeding - For logs and documentation: Use format: text to write content as-is without validation overhead

5. Validation Commands

Validate implementation completeness with automatic retry.

Warning: The command field in ValidationConfig is deprecated. Use shell instead for shell commands or claude for Claude commands. The command field is still supported for backward compatibility but will be removed in a future version.

- claude: "/implement-auth-spec"
  validate:
    shell: "debtmap validate --spec auth.md --output result.json"
    result_file: "result.json"
    threshold: 95  # Percentage completion required (default: 100.0)
    timeout: 60
    expected_schema: "validation-schema.json"  # Optional JSON schema

    # What to do if incomplete
    on_incomplete:
      claude: "/complete-implementation ${validation.gaps}"
      max_attempts: 3
      fail_workflow: true
      commit_required: true
      prompt: "Implementation incomplete. Continue?"  # Optional interactive prompt

ValidationConfig Fields: - shell or claude - Single validation command (use shell, not deprecated command) - commands - Array of commands for multi-step validation - result_file - Path to JSON file with validation results - threshold - Minimum completion percentage (default: 100.0) - timeout - Timeout in seconds - expected_schema - JSON schema for validation output structure

OnIncompleteConfig Fields: - shell or claude - Single gap-filling command - commands - Array of commands for multi-step gap filling - max_attempts - Maximum retry attempts (default: 2) - fail_workflow - Whether to fail workflow if validation incomplete (default: true) - commit_required - Whether to require commit after gap filling (default: false) - prompt - Optional interactive prompt for user guidance

Alternative: Array format for multi-step validation

- claude: "/implement-feature"
  validate:
    # When using array format, ValidationConfig uses default threshold (100.0)
    # and creates a commands array
    - shell: "run-tests.sh"
    - shell: "check-coverage.sh"
    - claude: "/validate-implementation --output validation.json"
      result_file: "validation.json"

Alternative: Multi-step gap filling

- claude: "/implement-feature"
  validate:
    shell: "validate.sh"
    result_file: "result.json"
    on_incomplete:
      commands:
        - claude: "/analyze-gaps ${validation.gaps}"
        - shell: "run-fix-script.sh"
        - claude: "/verify-fixes"
      max_attempts: 2

Command Reference

Command Fields

All command types support these common fields:

Source: src/config/command.rs:320-401

Field Type Description
id string Unique identifier for referencing outputs
timeout number Command timeout in seconds
commit_required boolean Whether command should create a git commit
when string Conditional execution expression
capture_output boolean/string Capture command output to a variable (boolean for default "output" var, string for custom name)
capture_format enum Output parsing format: string (default), number, json, lines, boolean
on_success object Command to run on success
on_failure object Error handling configuration (see OnFailure Handler section below)
validate object Validation configuration for implementation completeness
output_file string Redirect command output to a file path

Note on Field Availability: All fields in this table are available in the user-facing YAML WorkflowStepCommand format (src/config/command.rs:320-401). Additional fields like working_dir, env, on_exit_code, and capture exist in the internal WorkflowStep representation (src/cook/workflow/executor/data_structures.rs:35-157) but are NOT exposed in YAML. See the Technical Notes section below for workarounds.

OnFailure Handler Configuration

The on_failure field accepts an OnFailureConfig which supports multiple configuration formats:

Source: src/cook/workflow/on_failure.rs:67-115

Simple Format - Single Command:

- shell: "cargo clippy"
  on_failure:
    claude: "/fix-warnings ${shell.output}"

Advanced Format - With Retry Control:

- shell: "cargo test"
  on_failure:
    claude: "/debug-test-failures ${shell.output}"
    max_retries: 3              # Maximum retry attempts (default: 1)
    fail_workflow: false        # Continue workflow even after failure handling
    retry_original: true        # Retry original command after handler (default: false)

Detailed Format - With Handler Strategy:

For fine-grained control over failure handling behavior, use the Detailed format with FailureHandlerConfig (src/cook/workflow/on_failure.rs:9-49):

- shell: "cargo build"
  on_failure:
    commands:
      - claude: "/analyze-build-errors ${shell.output}"
      - shell: "cargo clean && cargo build"
    strategy: recovery          # recovery, fallback, cleanup, or custom
    timeout: 300                # Handler timeout in seconds
    fail_workflow: false        # Whether to fail workflow after handling
    handler_failure_fatal: true # Whether handler failure should be fatal

Handler Strategies (src/cook/workflow/on_failure.rs:9-22): - recovery (default) - Attempt to fix the problem and retry the original operation. Use this when failures are transient or can be resolved programmatically. - fallback - Use an alternative approach when the primary method fails. Best for scenarios with multiple valid execution paths. - cleanup - Perform resource cleanup before failing. Use for releasing locks, closing connections, or removing temporary files. - custom - Custom handler logic for specialized error recovery patterns not covered by other strategies.

Real-World Examples:

From workflows/coverage.yml (lines 13-17):

- shell: "just test"
  on_failure:
    claude: "/prodigy-debug-test-failure --spec ${coverage.spec} --output ${shell.output}"
    max_attempts: 3
    fail_workflow: false

From workflows/implement.yml (lines 20-30):

- shell: "just test"
  on_failure:
    claude: "/prodigy-debug-test-failure --spec $ARG --output ${shell.output}"
    max_attempts: 5
    fail_workflow: false

Nested Failure Handlers:

- shell: "cargo test"
  on_failure:
    claude: "/debug-test-failures ${shell.output}"
    on_failure:
      shell: "notify-team.sh 'Tests failed and debugging unsuccessful'"

Planned Feature: CaptureStreams

Note: The capture_streams field is defined in WorkflowStepCommand (src/config/command.rs:394-396) but is reserved for future use and not yet functional in YAML workflows.

Why it's not available yet: The field exists as a String placeholder in the YAML parser, but the execution engine doesn't yet support fine-grained stream capture control. This feature is planned to provide selective capture of stdout, stderr, exit codes, success status, and execution duration.

Current Approach: Use the capture_output and capture_format fields to control output capture, which cover most common use cases:

# Capture stdout as string (most common use case)
- shell: "cargo test"
  capture_output: "test_output"
  capture_format: "string"

# Capture exit status as boolean
- shell: "cargo test"
  capture_output: "test_passed"
  capture_format: "boolean"

# Capture and parse JSON output
- shell: "cargo metadata --format-version 1"
  capture_output: "project_info"
  capture_format: "json"

When will this be available? This feature requires execution engine changes to support the internal CaptureStreams type (src/cook/workflow/executor/data_structures.rs:83). Until then, use the capture_output and capture_format fields which cover most common use cases.

Capture Format Examples

The capture_format field controls how captured output is parsed:

# String format (default) - raw text output
- shell: "git rev-parse HEAD"
  capture: "commit_hash"
  capture_format: "string"

# Number format - parses numeric output
- shell: "wc -l < file.txt"
  capture: "line_count"
  capture_format: "number"

# JSON format - parses JSON output
- shell: "cargo metadata --format-version 1"
  capture: "project_metadata"
  capture_format: "json"

# Lines format - splits output into array of lines
- shell: "git diff --name-only"
  capture: "changed_files"
  capture_format: "lines"

# Boolean format - true if command succeeds, false otherwise
- shell: "grep -q 'pattern' file.txt"
  capture: "pattern_found"
  capture_format: "boolean"

Deprecated Fields

Warning: The following fields are deprecated but still supported for backward compatibility. They will be removed in a future version. Please migrate to the recommended alternatives.

These fields are deprecated:

  • test: - Use shell: with on_failure: instead
  • command: in ValidationConfig - Use shell: instead
  • Nested commands: in agent_template and reduce - Use direct array format instead
  • Legacy variable aliases ($ARG, $ARGUMENT, $FILE, $FILE_PATH) - Use modern ${item.*} syntax

Using capture_output Field

The capture_output field supports both Boolean and String variants for backward compatibility:

Source: src/config/command.rs:366-368 (CaptureOutputConfig enum: src/config/command.rs:403-411)

# String variant - captures to named variable (recommended)
- shell: "git rev-parse HEAD"
  capture_output: "commit_hash"

# Boolean variant - captures to default "output" variable
- shell: "ls -la | wc -l"
  capture_output: true

Migration Guide - capture_output Variants:

The capture_output field accepts both boolean and string values for backward compatibility:

Syntax Variable Name When to Use
capture_output: true ${output} Quick capture to default variable
capture_output: "myvar" ${myvar} Recommended: explicit variable name

Before and After Examples:

# Boolean variant - captures to ${output}
# BEFORE:
- shell: "git rev-parse HEAD"
  capture_output: true
# Access with: ${output}

# AFTER (recommended):
- shell: "git rev-parse HEAD"
  capture_output: "commit_hash"
# Access with: ${commit_hash}

# String variant - already correct
- shell: "ls -la | wc -l"
  capture_output: "file_count"
# Access with: ${file_count}

Note: The internal WorkflowStep struct (src/cook/workflow/executor/data_structures.rs:75) uses a capture field internally, but this is NOT exposed in the user-facing YAML WorkflowStepCommand struct. Always use capture_output for capturing command output in YAML workflows.

You can then reference captured values using ${commit_hash} or ${output} in subsequent commands.


Technical Notes

Internal Implementation Fields (for contributors) The following fields exist in the internal WorkflowStep struct (src/cook/workflow/executor/data_structures.rs:35-157) but are NOT available in the user-facing WorkflowStepCommand YAML syntax (src/config/command.rs:320-401). These are implementation details managed by Prodigy's execution engine during workflow execution: **Execution Control Fields (Internal Only):** - `handler` - Internal HandlerStep for execution routing - `retry` - Internal RetryConfig for automatic retry logic - `auto_commit` - Internal commit tracking - `commit_config` - Internal commit configuration - `step_validate` - Internal validation state - `skip_validation` - Internal validation control - `validation_timeout` - Internal validation timing - `ignore_validation_failure` - Internal validation handling **Environment and Context Fields (Planned/Not Yet Exposed in YAML):** - `working_dir` - Working directory control (WorkflowStep:99, NOT in WorkflowStepCommand) - `env` - Environment variable overrides (WorkflowStep:103, NOT in WorkflowStepCommand) - `on_exit_code` - Exit code specific handlers (WorkflowStep:119, NOT in WorkflowStepCommand) - `capture` - Variable name for output (WorkflowStep:75, use `capture_output` in YAML instead) **Why the separation?** WorkflowStepCommand defines what users can write in YAML. WorkflowStep is the internal runtime representation with additional fields populated during execution. Some features exist in the execution layer but aren't yet exposed in the YAML syntax. **Workarounds for missing YAML fields:** Since `cwd`, `env`, and `on_exit_code` are NOT available in WorkflowStepCommand, use these shell-level alternatives:
# Instead of cwd field (NOT available):
# ❌ - shell: "cargo test"
#      cwd: "crates/prodigy-core"

# ✓ Use shell cd command:
- shell: "cd crates/prodigy-core && cargo test"

# Instead of env field (NOT available):
# ❌ - shell: "rustfmt --check src/**/*.rs"
#      env:
#        PATH: "/custom/bin:${PATH}"

# ✓ Use shell environment syntax:
- shell: "PATH=/custom/bin:$PATH rustfmt --check src/**/*.rs"

# Instead of on_exit_code field (NOT available):
# ❌ - shell: "cargo build"
#      on_exit_code:
#        1:
#          claude: "/fix-compile-errors"

# ✓ Use on_failure with conditional logic:
- shell: "cargo build"
  on_failure:
    claude: "/fix-compile-errors ${shell.output}"
These planned features may be exposed in future versions of Prodigy.

Cross-References

For more information on related topics: - Variable Interpolation: See the Variables chapter for details on using captured outputs like ${variable_name} in subsequent commands - Environment Variables: See the Environment Variables chapter for global env, secrets, and profiles - Error Handling: See the Error Handling chapter for advanced on_failure strategies and retry patterns - MapReduce Workflows: See the MapReduce chapter for large-scale parallel command execution with agent templates

Example: Using Captured Output in Subsequent Commands

# Capture build output and use it in later commands
- shell: "cargo build --release 2>&1"
  capture: "build_output"
  capture_format: "string"

# Use the captured output in Claude command
- claude: "/analyze-warnings '${build_output}'"
  when: "${build_output contains 'warning'}"

# Store output to file for later analysis
- write_file:
    path: "logs/build-${workflow.iteration}.log"
    content: "${build_output}"
    create_dirs: true