Skip to content

Variables

Variables in FlowState store values that persist across states within a workflow instance. They enable dynamic arguments, captured output, and conditional logic.

Workflows can declare default variable values in the variables section:

name: my-workflow
start-at: first-state
variables:
environment: development
retry_count: "3"
log_level: info
states:
first-state:
tool: bash
arguments:
command: 'echo "Running in {{ environment }}"'

Default values are loaded when the instance is created. They can be overridden when calling the workflow as a subflow.

Use {{ variable_name }} syntax to substitute variable values into arguments:

greet:
tool: bash
arguments:
command: 'echo "Hello, {{ user_name }}"'

Substitution happens before tool execution. If a variable is undefined, FlowState raises an error.

Variable references are whitespace-insensitive:

# All equivalent
command: 'echo "{{ name }}"'
command: 'echo "{{name}}"'
command: 'echo "{{ name }}"'

Captured variable values are trimmed of leading/trailing whitespace.

Include file contents directly in arguments with {{ file(path) }}:

send-prompt:
tool: claude
arguments:
prompt: "{{ file(./prompts/review.md) }}"
  • ./ - Relative to the instance directory
  • @/ - Relative to the project root (directory containing .flowstate/)
  • Absolute paths are used as-is
# From instance directory
prompt: "{{ file(./context.txt) }}"
# From project root
prompt: "{{ file(@/prompts/template.md) }}"
# Absolute path
prompt: "{{ file(/etc/config.txt) }}"

Get the absolute path to a file with {{ path(path) }}:

process-file:
tool: bash
arguments:
command: 'wc -l "{{ path(./data.txt) }}"'

This is useful when tools need absolute paths rather than file contents.

Evaluate comparisons that return "true" or "false":

# Equality
value: "{{ status == 'ready' }}"
# Inequality
value: "{{ env != 'production' }}"

Comparisons are string-based. Quotes around the comparison value are required.

Used primarily with the switch tool:

check-ready:
tool: switch
arguments:
value: "{{ status == 'ready' }}"
goto:
"true": proceed
"false": wait
_: error

Capture tool stdout to variables or files using the output field.

get-version:
tool: bash
arguments:
command: cat VERSION
output: var(app_version)
next: use-version
use-version:
tool: bash
arguments:
command: 'echo "Version: {{ app_version }}"'

Variable names must:

  • Start with a letter or underscore
  • Contain only letters, numbers, and underscores
  • Not be a reserved name (_, true, false)
save-output:
tool: bash
arguments:
command: docker logs myapp
output: file(./logs/docker.log)

Parent directories are created automatically if they don’t exist.

Combine variables with file output:

save-report:
tool: bash
arguments:
command: ./generate-report.sh
output: file(./reports/{{ date }}-report.txt)

Variables in subflows are namespaced to prevent collisions.

call-helper:
tool: sub-flow
arguments:
workflow: helper-workflow
variables:
input_file: "{{ source_file }}"
mode: fast

Variables set by a subflow are accessible in the parent with the namespace prefix:

# In parent workflow, after subflow completes:
use-result:
tool: bash
arguments:
command: 'echo "{{ helper-workflow::result }}"'

The namespace is the subflow name followed by ::.

When multiple sources define the same variable:

  1. Subflow-passed variables (highest priority)
  2. Captured output during execution
  3. Workflow defaults from variables section
  4. Undefined - raises an error
variables:
mode: normal
check-mode:
tool: switch
arguments:
value: "{{ mode }}"
goto:
fast: quick-process
normal: standard-process
_: standard-process
variables:
output_dir: ./results
save-output:
tool: bash
arguments:
command: ./process.sh > "{{ path({{ output_dir }}/output.txt) }}"
# Capture first result
step-1:
tool: bash
arguments:
command: echo "part1"
output: var(result1)
next: step-2
# Capture second result
step-2:
tool: bash
arguments:
command: echo "part2"
output: var(result2)
next: combine
# Combine results
combine:
tool: bash
arguments:
command: 'echo "{{ result1 }} {{ result2 }}"'