Skip to main content

Installation

The sirenspec CLI is installed automatically when you add the package:
uv add sirenspec
# or
pip install sirenspec
After installation, the sirenspec command is available on your PATH. You can also check the installed version:
sirenspec --version

sirenspec init

Scaffold a new workflow interactively. Prompts for a template and provider, then writes a workflow.yaml and .env.example in the target directory. The generated workflow passes sirenspec validate immediately.
sirenspec init [OPTIONS]
Options:
OptionShortDescription
--output PATH-oDirectory for the generated files. Defaults to the current working directory.
Exit codes:
CodeMeaning
0Files were created successfully.
1The generated workflow failed validation (rare — indicates a template bug).
Example session:
$ sirenspec init

Welcome to SirenSpec! Let's scaffold a new workflow.

Choose a template:
  1. simple-agent          — Single agent that answers a question
  2. sequential-pipeline   — Two agents in sequence
  ...

Template [1]: 1

Choose a provider:
  1. openai
  2. anthropic

Provider [1]: 1

Enable guardrails? (injection + length) [Y/n]: Y

✓ workflow.yaml created
✓ .env.example created

Next steps:
  Copy .env.example → .env and add your API key
  sirenspec validate workflow.yaml
  sirenspec run workflow.yaml

sirenspec run

Execute a workflow and display the results. By default, node execution is rendered with Rich formatting to the terminal (streaming per-node view). Use --trace or --output json to output the full JSON execution trace instead.
sirenspec run <workflow-file> [OPTIONS]
Arguments:
ArgumentDescription
workflow-filePath to the workflow YAML file.
Options:
OptionShortDescription
--input TEXT-iUser input message. Overrides input.message in the YAML.
--tracePrint full JSON trace to stdout (disables per-node panels).
--output TEXTOutput format. Use "json" for raw JSON trace (equivalent to --trace).
--trace-file TEXTWrite full JSON trace to this file path (in addition to streaming output).
--quietSuppress node panels; print only the summary (useful for non-TTY environments).
--no-streamDisable per-token streaming inside node panels. Panels and summary still render.
Exit codes:
CodeMeaning
0Workflow completed successfully.
1File not found, validation error, execution error, or guardrail violation.
Examples:
# Run with streaming output (default)
sirenspec run workflow.yaml --input "What is the capital of France?"

# Print full JSON trace
sirenspec run workflow.yaml --input "Hello" --trace | jq '.output'
sirenspec run workflow.yaml --input "Hello" --output json | jq '.output'

# Suppress node panels but keep summary
sirenspec run workflow.yaml --input "Hello" --quiet

# Write trace to a file and see streaming output
sirenspec run workflow.yaml --input "Hello" --trace-file trace.json
Streaming Output Format: When run in a TTY without --trace or --output json, the CLI renders each completed node as a Rich panel:
╭─ answer ─────────────────────────────╮
│ Paris is the capital of France.      │
╰───────────────────────────────────────╯
  ↓ output.reply

Run complete  │  1 node  │  24 tokens  │  cost unavailable (pricing not configured)
For swarm nodes (type: swrm), the output shows a dedicated parallel execution block:
─── Swarm: analyze [3 agents] ─────────────────────────
╭─── sentiment ─────────────────────────────────────╮
│ The market sentiment is cautiously optimistic...   │
╰───────────────────────────────────────────────────╯
╭─── risk ─────────────────────────────────────────╮
│ 1. Rising interest rates                          │
│ 2. Geopolitical uncertainty                       │
│ 3. Earnings weakness...                           │
╰───────────────────────────────────────────────────╯
╭─── opportunity ───────────────────────────────────╮
│ - Beaten-down tech sector                         │
│ - Infrastructure momentum...                      │
╰───────────────────────────────────────────────────╯
─── Swarm complete: 3/3 succeeded  (2.8s total) ───
╭─── analyze ───────────────────────────────────────╮
│ Based on the three perspectives, this stock...    │
╰───────────────────────────────────────────────────╯
  ↓ output.analyze
When synthesis is configured, its output is shown in a final panel after the swarm footer. If any agent fails, the footer shows the failure count (e.g., 2/3 succeeded, 1 failed). Failed agents display their error message in a red-bordered panel. JSON Trace Format: When --trace, --output json, or --trace-file is used, the full execution trace is written as JSON:
{
  "workflow": { "version": "0.1" },
  "input": { "message": "What is the capital of France?" },
  "nodes": [
    {
      "id": "answer",
      "type": "agent",
      "output": "Paris is the capital of France.",
      "writes": "output.reply",
      "status": "success",
      "error": null,
      "tokens": 24,
      "usage": {
        "prompt_tokens": 18,
        "completion_tokens": 6,
        "estimated_usd": null
      },
      "guardrails_passed": [
        "InjectionGuardrail.check_input",
        "InjectionGuardrail.check_output"
      ],
      "duration_ms": 312.5
    }
  ],
  "output": {
    "reply": "Paris is the capital of France."
  },
  "summary": {
    "total_tokens": 24,
    "total_usage": {
      "prompt_tokens": 18,
      "completion_tokens": 6,
      "total_tokens": 24,
      "estimated_usd": null
    },
    "total_duration_ms": 312.5,
    "status": "success"
  }
}
Trace fields:
FieldDescription
workflow.versionSchema version from the YAML file.
input.messageThe user input that was used.
nodesOrdered list of executed nodes.
nodes[].idNode identifier.
nodes[].typeNode type: "agent", "tool", "swrm", "factory", or "workflow".
nodes[].outputThe node’s output value (string, dict, list, or null).
nodes[].writesContext path where the output was written.
nodes[].status"success", "skipped", or "failed".
nodes[].skip_reasonWhy a node was skipped: "branch_not_taken" (no incoming edge’s when: matched) or "budget_exceeded" (workflow budget exhausted). null for executed nodes. Branch-not-taken nodes are suppressed from the terminal panels but still appear in the JSON trace.
nodes[].errorError message if the node failed, otherwise null.
nodes[].tokensTotal tokens used by this node.
nodes[].usageDetailed token usage with prompt_tokens, completion_tokens, total_tokens, and estimated_usd.
nodes[].guardrails_passedList of guardrail check methods that succeeded.
nodes[].duration_msWall-clock time for this node in milliseconds.
outputFinal output context (all output.* paths).
summary.total_tokensSum of tokens across all nodes.
summary.total_usageAggregated token usage with detailed breakdown.
summary.total_duration_msSum of durations across all nodes.
summary.status"success" or "failed".
summary.budgetPresent only when a budget: block is configured. Reports the configured ceilings plus tokens_used, estimated_usd, duration_s, exceeded, violations, and skipped_remaining. See YAML Reference → budget.

sirenspec explain

Print a human-readable execution plan for a workflow without making any LLM calls.
sirenspec explain <workflow-file> [OPTIONS]
Arguments:
ArgumentDescription
workflow-filePath to the workflow YAML file.
Options:
OptionShortDescription
--format TEXT-fOutput format: "text" (default) for human-readable or "json" for machine-readable.
Exit codes:
CodeMeaning
0Plan generated successfully.
1File not found, validation error, or structural errors in the workflow graph.
Examples:
# Print text-based execution plan
sirenspec explain workflow.yaml

# Output as JSON for programmatic use
sirenspec explain workflow.yaml --format json
sirenspec explain workflow.yaml -f json
Text Output Format: The text format shows the execution order, node types, guardrails, and edges in a human-readable table:
Workflow: router  │  3 nodes  │  2 agents
  
1. triage            agent=router (openai:gpt-4o-mini)  guardrails=[injection, length]
   writes → working.triage.result
   ↳ [working.triage.intent == "refund"]        → handle_refund
   ↳ [working.triage.intent == "general"]       → handle_general

2. handle_refund     agent=processor (anthropic:claude-3-5-sonnet-20241022)
   writes → output.refund_response
   ↳ [default]                                  → end

3. handle_general    agent=processor (anthropic:claude-3-5-sonnet-20241022)
   writes → output.general_response
   ↳ [default]                                  → end

Guardrails (workflow-level): injection, length
JSON Output Format: The JSON format is useful for integration into CI/CD systems or analysis tools:
{
  "workflow_name": "router",
  "node_count": 3,
  "agent_count": 2,
  "execution_order": ["triage", "handle_refund", "handle_general", "end"],
  "nodes": [
    {
      "id": "triage",
      "type": "agent",
      "type_info": "agent=router (openai:gpt-4o-mini)",
      "agent": "router",
      "model": "openai:gpt-4o-mini",
      "writes": "working.triage.result",
      "guardrails": ["injection", "length"],
      "outgoing_edges": [
        {
          "to": "handle_refund",
          "when": "working.triage.intent == \"refund\""
        },
        {
          "to": "handle_general",
          "when": "working.triage.intent == \"general\""
        }
      ]
    }
  ],
  "workflow_guardrails": ["injection", "length"],
  "warnings": [],
  "errors": []
}

sirenspec test

Discover and run YAML test fixtures. Each fixture file (*.test.yaml) contains a workflow definition plus a set of assertions that are validated during execution.
sirenspec test <test-path> [OPTIONS]
Arguments:
ArgumentDescription
test-pathPath to a single test fixture file or a directory of *.test.yaml files.
Options:
OptionDescription
--mockReplay LLM responses from a cassette file (no live API calls). Requires --cassette.
--recordRun live and record LLM responses into a cassette file. Requires --cassette. Mutually exclusive with --mock.
--cassette PATHPath to the cassette file for --mock or --record. Required when either is used.
Exit codes:
CodeMeaning
0All fixtures passed assertions.
1One or more fixtures failed assertions or encountered execution errors.
2Configuration error (e.g., missing --cassette when using --mock).
Examples:
# Run a single test fixture with live LLM calls
sirenspec test tests/answer.test.yaml

# Run all fixtures in a directory with live calls
sirenspec test tests/

# Replay recorded LLM responses from a cassette
sirenspec test tests/ --mock --cassette cassettes/responses.yaml

# Record live LLM responses into a cassette
sirenspec test tests/ --record --cassette cassettes/responses.yaml
Output format: For each fixture, a per-fixture result is printed:
PASS tests/answer.test.yaml
  ✓ assertion[0]: output matches expected
  ✓ assertion[1]: tokens < 100

FAIL tests/edge_case.test.yaml
  ✗ assertion[0]: status is success
After all fixtures, a summary line is printed:
3/3 fixtures passed

sirenspec validate

Validate a workflow YAML file without executing it or making any API calls.
sirenspec validate <workflow-file>
Arguments:
ArgumentDescription
workflow-filePath to the workflow YAML file.
Exit codes:
CodeMeaning
0File is valid.
1File not found or validation failed.
Examples:
sirenspec validate workflow.yaml
# ✓ workflow.yaml is valid (2 agents, 3 nodes)

sirenspec validate broken.yaml
# ✗ Validation failed: agents.assistant.model: field required
What is validated:
  • YAML syntax (including duplicate key detection).
  • Required fields (version, agents, nodes).
  • Agent fields (model, system).
  • Node fields (agent, writes).
  • Edge references — all from and to values must refer to existing nodes.
  • Node agent references — all agent values must refer to existing agents.
After model validation, a static template linter runs over every template-bearing field (agent/swrm/synthesis prompts, factory/workflow inputs, for_each, swarm_size):
RuleLevelTrigger
working_dot_node_iderror (blocks load){{ working.<node_id>.* }} where <node_id> is a known node — the canonical form is {{ <node_id>.output }}.
unknown_namespacewarningA top-level template name that is neither reserved (inputs, env, item, index, total) nor a known node ID — usually a typo.
Errors raise WorkflowLintError and fail validation; warnings are printed without blocking. The same linter runs inside load_workflow() before any LLM call is made. Provider credentials and when: expressions are not evaluated during validation.

sirenspec render

Render a workflow as a diagram without executing it or making any API calls.
sirenspec render <workflow-file> --target <format> [OPTIONS]
Arguments:
ArgumentDescription
workflow-filePath to the workflow YAML file.
Options:
OptionShortDescription
--target TEXTRender target format. Currently only mermaid is supported.
--output TEXT-oWrite output to a file instead of stdout.
Exit codes:
CodeMeaning
0Rendered successfully.
1File not found, validation error, or unsupported target.
Examples:
# Print a Mermaid flowchart to stdout
sirenspec render workflow.yaml --target mermaid

# Save to a file
sirenspec render workflow.yaml --target mermaid --output diagram.md
sirenspec render workflow.yaml --target mermaid -o diagram.md
Output format: The mermaid target produces a graph TD flowchart. Unconditional edges are plain arrows; conditional edges are labeled with their when: expression.
graph TD
    triage[triage]
    handle_refund[handle_refund]
    handle_general[handle_general]
    triage -->|"working.triage.intent == 'refund'"| handle_refund
    triage -->|"working.triage.intent == 'general'"| handle_general
Paste this into any Mermaid renderer (GitHub markdown, Mermaid Live Editor, etc.) to visualise the workflow graph.