Skip to content

feat: draw agent workflows in the terminal and export them as Mermaid - #179

Merged
PaulAsjes merged 4 commits into
mainfrom
pma/workflow-diagram
Oct 28, 2026
Merged

PaulAsjes merged 4 commits into
mainfrom
pma/workflow-diagram

Conversation

@PaulAsjes

@PaulAsjes PaulAsjes commented Oct 27, 2026 •

Copy link
Copy Markdown
Collaborator

Summary

Agent workflows are stored as raw node and edge JSON, which is hard to read and harder to review in a diff. A new local-only command, agents workflow diagram, turns a workflow into a flowchart. By default it draws it in the terminal; --format mermaid prints Mermaid source to save, and --format mermaid-md wraps it in a fence to paste into docs and PR comments, where GitHub renders it. It needs no API key or network access. Closes #170, apart from the GitHub Action, which is left for a follow-up.

Example

A small workflow: an LLM condition leads to a tool, whose result branches to an apology or the end.

elevenlabs agents workflow diagram agent_configs/order-lookup.json draws it in the terminal. Each condition is a numbered key on its edge, written out in full underneath:

         ╭───────╮
         │ Start │
         ╰───────╯
             │
             │
             │
             │
           ╭───╮
           │ 1 │
           ╰───╯
             │
             │
             ▼
   ┌───────────────────┐
   ║       Tool        ║
   ║ tool_lookup_order ║
   └───────────────────┘
    │                 │
    └───┐          ┌──┘
        │          │
        │          │
    ╭───╮          ╭───╮
    │ 2 │          │ 3 │
    ╰───╯          ╰───╯
      │              │
      │              │
      ▼              │
╭───────────╮        ▼
│    Say    │    ╭───────────────╮
│ Apologize │    │ End (success) │
╰───────────╯    ╰───────────────╯

  1  The caller has given an order number
  2  failure
  3  success

elevenlabs agents workflow diagram agent_configs/order-lookup.json --format mermaid prints Mermaid source (--format mermaid-md adds a ```mermaid fence):

flowchart TD
    n_apologize("Say · Apologize")
    n_done(["End (success)"])
    n_lookup[["Tool · tool_lookup_order"]]
    n_start_node(["Start"])
    n_lookup -->|"failure"| n_apologize
    n_lookup -->|"success"| n_done
    n_start_node -->|"The caller has given an order number"| n_lookup
    classDef t_start fill:#d1fae5,stroke:#059669,color:#064e3b
    class n_start_node t_start
    classDef t_end fill:#fee2e2,stroke:#dc2626,color:#7f1d1d
    class n_done t_end
    classDef t_say fill:#e0f2fe,stroke:#0284c7,color:#0c4a6e
    class n_apologize t_say
    classDef t_tool fill:#fef3c7,stroke:#d97706,color:#78350f
    class n_lookup t_tool

which GitHub renders as:

flowchart TD
    n_apologize("Say · Apologize")
    n_done(["End (success)"])
    n_lookup[["Tool · tool_lookup_order"]]
    n_start_node(["Start"])
    n_lookup -->|"failure"| n_apologize
    n_lookup -->|"success"| n_done
    n_start_node -->|"The caller has given an order number"| n_lookup
    classDef t_start fill:#d1fae5,stroke:#059669,color:#064e3b
    class n_start_node t_start
    classDef t_end fill:#fee2e2,stroke:#dc2626,color:#7f1d1d
    class n_done t_end
    classDef t_say fill:#e0f2fe,stroke:#0284c7,color:#0c4a6e
    class n_apologize t_say
    classDef t_tool fill:#fef3c7,stroke:#d97706,color:#78350f
    class n_lookup t_tool
Loading
Details
  • Usage. diagram takes a config file path, or an agent ID or name from agents.json, or --all, which covers every agent and registered branch. --format is ascii (the default; box-drawing characters, so not strictly ASCII), mermaid or mermaid-md. This started as a separate export command; it was folded into --format after review, since "export" suggested a matching import. Agent lookup reuses the ID-then-name resolution from agents test, moved into a shared find_agent with no change in behaviour.
  • Rendering. Both commands render from serde_json::Value rather than the generated types, so unknown node or condition types render generically instead of failing to deserialize. Each node type gets its own shape and colour. Edges show their condition's label, the LLM condition text, success/failure, or expression. Backward conditions are drawn as dotted reverse arrows.
  • Export output is sorted by ID so it diffs cleanly. Labels are entity-escaped, so prompt text containing quotes, pipes or <b> can't break the diagram. Node IDs are made Mermaid-safe: they are prefixed, which also keeps end from clashing with the Mermaid keyword, and colliding IDs get a suffix. An edge pointing at a node that doesn't exist is drawn as a missing: box rather than dropped.
  • New dependency: the terminal drawing uses mmdflux 2.6.1 (MIT, pure Rust, default-features = false). It pulls in pest, psm/stacker and two small Unicode crates, all MIT or MIT/Apache. No existing crate changes version; the Cargo.lock diff also qualifies existing syn references, because pest brings in syn 3. I also trialled mermaid-text 0.57.0. It printed entity escapes and quotes literally, so it was rejected.
  • Terminal output differs from the Mermaid source.
    • mmdflux doesn't decode #quot; entities, so labels use plain lookalike characters instead.
    • Node labels are wrapped to 24 columns.
    • Update-state nodes are drawn as cylinders, because mmdflux has no parallelogram shape and prints its delimiters as text.
  • Edge conditions are numbered keys plus a legend in the terminal.
    • Written on the edges, the conditions made a real three-way fan-out about 200 columns wide; with keys it's 92.
    • As plain edge labels, mmdflux put keys beside the wrong edge in a four-way fan-out. So each key is a small node on its edge (A --- k_1(["1"]) --> B), which the layout has to keep on that edge's path.
    • Keys are numbered breadth-first from the start node, so they read top to bottom.
  • Keeping busy workflows readable in the terminal. mmdflux crosses and merges lines in dense graphs; a mock 13-node workflow drew 122 columns wide, with lines running through boxes. Now:
    • A step back ends in a stub (6 ↩), and the legend names where it goes (Changes topic (back to 'Ask for the order number')). Drawn in full, back-edges ran up past everything in between.
    • An ending that several paths lead to (an end or a transfer node, which has no way out) is drawn once per path, so links from high up don't run down the side.
    • A fan-out that still has crossing or merging lines gets a wider box (three widths tried), then every order of its branches (up to 5! drawings). The search keeps whichever has the fewest tangle characters (┼, ├, ┬, ...) and stops at none.
    • When the drawing is wider than the terminal (or COLUMNS), long IDs are shortened (tool_01j…sfb67); if it still doesn't fit, a note on stderr points at --format mermaid. This adds terminal_size 0.4 (MIT/Apache).
    • None of this changes the Mermaid output.
  • Colour is on when stdout is a terminal and NO_COLOR is unset. It comes from the same classDef colours the Mermaid output uses.
  • Follow-up (not in this PR): a GitHub Action that posts before/after diagrams on PRs touching agent_configs/. It needs a released CLI that includes --format mermaid.

Testing

  • cargo test: all suites pass, including 25 unit tests in diagram.rs and 9 integration tests in tests/workflow_diagram_test.rs. The integration tests run the binary with no API key set.
  • Five representative workflows are snapshot-tested against their expected Mermaid, including a busy 13-node one. All five also have to draw in the terminal with every legend line present, and the busy one must draw with no crossing or merging lines both at full size and at 104 columns.
  • Ran mermaid 11.4.1's own parser over the example outputs, a deliberately awkward workflow (quotes, pipes, HTML, #, an end node ID, a missing node) and the README example. All parsed, and a deliberately broken diagram was rejected.
  • Drew a real production agent's workflow (three subagents fanning out from start) in a 104-column terminal; it fits at 92 columns.
  • cargo clippy --all-targets: no new warnings.
  • Not verified: how the colours look in light and dark terminal themes, what the Mermaid output looks like rendered in GitHub's light and dark themes, and how much mmdflux adds to the release binary.

🤖 Generated with Claude Code

`elevenlabs agents workflow diagram <agent>` draws an agent's workflow as
box-drawing text, with each edge condition as a numbered key and a legend
underneath. `elevenlabs agents workflow export <agent> [--markdown]` prints
the same workflow as Mermaid source for docs and PR comments. Both take a
config path, agent ID or name, or --all, and read local files only.

Closes #170 (the GitHub Action is left for a follow-up).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@PaulAsjes
PaulAsjes marked this pull request as ready for review October 27, 2026 10:56
@PaulAsjes
PaulAsjes requested a review from kraenhansen October 27, 2026 10:58
…nal diagram

mmdflux ends a round `( )` node at the first `)`, even inside the quoted
label, so "Confirm (booking)" failed to draw. In the terminal, a round
node's brackets are now same-width lookalikes; export is unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@kraenhansen

kraenhansen commented Oct 27, 2026 •

Copy link
Copy Markdown
Member

The mermaid chart can be easily panned / zoomed, so I'm curious as to how useful the ASCII / terminal variant would be with a large workflow 🤔 Also - not against, it won't hurt.

The word "export" in elevenlabs agents workflow export agent_configs/order-lookup.json doesn't hint to me that this is a 1-way destructive visualisation and I would expected a command like that to be paired with an "import" command.

Would it make sense to overload the "diagram" command and have an option to render it in one of three formats:

  • ascii
  • mermaid
  • mermaid-md

And then render to stdout by default and optionally to an --output file (or from positional argument)

Steps back end in a stub named in the legend, endings that several paths
lead to are drawn once per path, and a fan-out that still tangles is
widened or reordered. Long IDs are shortened when the drawing is wider
than the terminal.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
One command for every rendering: --format ascii (the default, drawn in
the terminal), mermaid, or mermaid-md (in a ```mermaid fence). "export"
suggested a matching import, and the outputs only differ in format.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@PaulAsjes

Copy link
Copy Markdown
Collaborator Author

@kraenhansen good shouts, we now overload diagram with a --format flag. Can you take another look please?

@kraenhansen kraenhansen left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great stuff! 🎉

@PaulAsjes
PaulAsjes merged commit 3965a56 into main Oct 28, 2026
16 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Visualize agent workflows as Mermaid diagrams (+ GitHub Action for PR comments)

2 participants