Files
attune/docs/QUICKREF-output-formats.md

4.0 KiB

Quick Reference: Action Output Formats

TL;DR

Actions can specify how their stdout should be parsed:

  • text (default): No parsing, raw stdout only
  • json: Parse last line as JSON
  • yaml: Parse entire output as YAML
  • jsonl: Parse each line as JSON, return array

Action Definition

name: my_action
output_format: json  # text | json | yaml | jsonl
output_schema:
  type: object  # Use 'array' for jsonl
  properties:
    result: { type: string }

Format Behaviors

Format Parses Result Best For
text Nothing null Simple messages, logs
json Last line Object/Value API responses, single results
yaml Entire stdout Object/Value Configs, nested data
jsonl Each line Array Lists, streaming, batches

Examples

Text (no parsing)

echo "Hello, World!"
# Result: null (stdout captured separately)

JSON (last line)

echo "Processing..."
echo '{"status": 200, "data": "success"}'
# Result: {"status": 200, "data": "success"}

YAML (entire output)

cat <<EOF
version: 1.0
settings:
  enabled: true
  timeout: 30
EOF
# Result: {"version": "1.0", "settings": {...}}

JSONL (each line → array)

echo '{"id": 1, "name": "Alice"}'
echo '{"id": 2, "name": "Bob"}'
echo '{"id": 3, "name": "Charlie"}'
# Result: [{"id": 1, ...}, {"id": 2, ...}, {"id": 3, ...}]

Action Script Templates

Bash + JSON

#!/bin/bash
result=$(do_work)
echo "{\"result\": \"$result\", \"status\": \"ok\"}"

Python + JSON

#!/usr/bin/env python3
import json
result = do_work()
print(json.dumps({"result": result, "status": "ok"}))

Bash + JSONL

#!/bin/bash
for item in $(ls); do
  size=$(stat -f%z "$item")
  echo "{\"name\": \"$item\", \"size\": $size}"
done

Python + JSONL

#!/usr/bin/env python3
import json
for item in get_items():
    print(json.dumps({"id": item.id, "value": item.value}))

Common Patterns

Informational + Result (JSON)

echo "Starting process..." >&2  # Log to stderr
echo "Processing 100 items..." >&2
echo '{"processed": 100, "errors": 0}'  # JSON on last line

Mixed Output (JSONL)

echo "Scanning directory..." >&2  # Non-JSON ignored
echo '{"file": "a.txt", "size": 1024}'  # Valid JSON
echo "Found 2 files" >&2  # Non-JSON ignored
echo '{"file": "b.txt", "size": 2048}'  # Valid JSON

Execution Result Structure

{
  "exit_code": 0,
  "succeeded": true,
  "duration_ms": 142,
  "stdout": "raw output here",
  "stderr": "logs here",
  "data": { /* parsed result based on output_format */ }
}

Best Practices

DO

  • Use text for simple logging/messages
  • Use json for structured single results
  • Use jsonl for lists and batches
  • Write one JSON object per line (no pretty-print)
  • Log to stderr, output to stdout
  • Use non-zero exit codes for failures

DON'T

  • Mix error messages in stdout (use stderr)
  • Pretty-print JSON across multiple lines
  • Assume parsing will always succeed
  • Use jsonl without type: array in schema

Troubleshooting

No result parsed?

  • Check exit code is 0
  • Verify JSON is on last line (json)
  • Ensure one JSON per line (jsonl)
  • Check for syntax errors in output
  • Parsing failures don't cause execution failure

JSONL returning empty array?

  • Check each line is valid JSON
  • Ensure no trailing empty lines
  • Invalid lines are silently skipped

Result is null but expected data?

  • Verify output_format matches output
  • Check stdout contains expected format
  • Parsing is best-effort (no errors thrown)

Database

-- Check action output format
SELECT ref, output_format FROM action WHERE ref = 'core.http_request';

-- Update action output format
UPDATE action SET output_format = 'jsonl' WHERE ref = 'mypack.myaction';

See Also