Actions Visualizer
See your GitHub and Gitea Actions workflows as a graph — and simulate a run before you push.
It is the graph GitHub shows you after a run, except you get it while you are still writing the file.


Highlights
|
|
| Laid out like GitHub |
Jobs at the same depth share a card, matrix jobs get their own tabbed card, and triggers live in the header rather than as boxes in the graph. |
| Simulates a run |
Pick an event, a ref and any workflow_dispatch inputs. Every job whose if: depends on them updates instantly. |
| Plays through a run |
Step through the workflow deciding what happens. Fail a step and watch if: failure() fire, continue-on-error absorb it, and the jobs downstream skip. |
| Honest about the unknown |
A condition that depends on a secret or a step output is marked undecided rather than guessed at — and you can pin a value to decide it. |
| Catches real mistakes |
Missing needs: targets, circular dependencies, always-false conditions and job conditions using a context GitHub only gives to steps — in the Problems panel, not just the graph. |
| Fixes safe mistakes |
Precise Quick Fixes remove duplicate or self-referential dependencies, with a source-wide Fix All that never applies behavior-changing edits. |
| Job details at a click |
Select a job to see its condition, what it needs and what needs it, runner, environment, matrix, outputs, timeout, concurrency and permissions — with its dependency path lit up in the graph. |
| Live |
Re-renders as you type; click any job or step to jump to its line in the YAML, or open a local reusable workflow or action it uses. |
| Fully keyboard accessible |
Arrow keys, Enter for details, Space to expand, ? for every shortcut, with labels for screen readers. |
| GitHub and Gitea |
.github/workflows and .gitea/workflows — the syntax is the same. |
Getting started
- Open a workflow file under
.github/workflows/ or .gitea/workflows/.
- Click the graph icon in the editor title bar, or run Actions Visualizer: Open Workflow Graph to
the Side from the Command Palette.
- Click a trigger chip in the header to simulate that event. Fill in any inputs, the ref, and any
values the preview cannot work out on its own.
- Click a job to see its details beside the graph and jump to it in the YAML. Click its chevron to
show its steps in place.
The preview follows your active editor: opening another workflow moves the existing panel rather than
adding a second one, and switching to a file that is not a workflow leaves the graph as it was. It
also works on a draft outside a workflows directory, as long as the file has both on: and jobs:,
and it comes back showing the same workflow after VS Code reloads.
Keyboard and pointer
|
|
| Move through the jobs in order |
↑ ↓, or Home / End |
| Move to the next or previous column |
→ ← |
| Show a job's details and reveal it in the YAML |
Enter, or click |
| Close the details |
Esc, or click empty canvas |
| Show or hide a job's steps |
Space, the chevron, double-click or Alt-click |
| Reveal a step in the YAML |
click it, in the graph or in the details |
| Pan |
drag, or scroll |
| Zoom |
+ -, Ctrl/⌘ + scroll, or pinch |
| Fit the graph to the view, or show it at actual size |
0, 1 |
| Answer a playthrough step: succeeded, failed, undo |
S, F, Backspace |
| List every shortcut |
? |
Inspecting a job
Select a job and its details open beside the graph: the condition that decides it, what it needs
and what needs it, its runner, environment, matrix, outputs, timeout, concurrency and permissions,
and its steps. The jobs it depends on and the jobs that depend on it are lit up in the graph. Click a
step to go to its line in the YAML, and follow a local reusable workflow or action with Open.

Simulating a run
The header is interactive. Click a trigger and the graph re-evaluates every job's if: against it:
- A job that would run keeps its green check.
- A job that would be skipped is dimmed and struck through, with the reason on hover. It stays
exactly where it was, so nothing moves as you toggle.
- A job that cannot be decided gets an amber
? rather than a guess.
Steps are evaluated too, against the wider set of contexts GitHub gives them. Skips propagate along
needs: the way GitHub does, and always() / !cancelled() opt out of that.
Type a ref that the workflow's branches:, tags:, branches-ignore: or tags-ignore: filters
reject and the graph says the event would not fire at all, instead of pretending everything runs.
For a pull request the field is the base branch, because that is what its filters match. The
run itself happens on refs/pull/<number>/merge, so github.ref stays unknown rather than wrongly
equalling the base branch — and github.base_ref is the branch you picked. Outside pull requests,
github.head_ref is empty, as it is on GitHub.
When a condition depends on something no preview can know — a secret, a step output, another job's
output — and nothing else decides it, the header offers a field for it. Fill it in and every
condition that depends on it decides.
The status bar sums it up: how many jobs would run, be skipped, or are undecided.
What the expression evaluator supports
The full GitHub Actions expression language: contexts, property and index access, the * object
filter, and every comparison and logical operator with GitHub's coercion rules — including
case-insensitive string equality, cross-type numeric casting, and && / || returning an operand
rather than a boolean. A condition decides on either side of && and ||: secrets.X && false is
false whatever the secret holds. Built-in functions: contains, startsWith, endsWith, format, join,
toJSON, fromJSON, and the status functions success, always, failure and cancelled.
Context availability is modelled too. A job-level if: sees only github, needs, vars and
inputs; a step-level one additionally sees env, matrix, job, runner, steps and
strategy. Using the wrong one is reported as a problem, because GitHub evaluates it as empty.
Playing through a run
Press ▶ to walk the workflow a step at a time. At each step you say what happened, and the graph
follows the consequences:
- Failing a step fails its job — unless the step declares
continue-on-error.
- Steps whose
if: uses failure() or always() run exactly when they would in a real run.
- A job whose dependency failed is skipped, unless it opts back in with
always(). When it does,
its own status starts clean rather than inheriting the failure.
- Outputs flow. The bar offers a field for every output the step is expected to produce — read
from
>> $GITHUB_OUTPUT in its script and from wherever the workflow reads it back. Fill one in
and it reaches steps.<id>.outputs.*, then needs.<job>.outputs.* for the jobs downstream.
Undo takes back the last answer, restart empties the run, and stop returns to the static graph with
your event, ref, inputs and pins untouched. Select a job before pressing play, or use Play this
job in its details, to walk only that job.
Nothing is executed and nothing is contacted — you are deciding what would happen, not running it.
Reading the graph
| Element |
Meaning |
| A card |
One or more jobs at the same depth sharing the same needs: |
Matrix: build tab |
A matrix job; Show all jobs in its details expands it into one row per combination |
| Green check |
The job would run for the simulated event |
| Grey circle with a slash, struck-through name |
The job would be skipped; hover or select it for why |
Amber ? |
The condition cannot be decided statically; pin its value in the header |
| Pulsing blue ring with a dot |
The step a playthrough is waiting on |
| Red ✕ |
A step or job you marked as failed |
| Dotted grey circle |
Not reached yet in a playthrough |
| Red dashed card |
A needs: reference to a job that does not exist |
| Solid line with end dots |
A needs: dependency |
| Faded dashed line |
Every job behind this dependency is skipped |
| Blue line |
Part of the selected job's dependency path, upstream or downstream |
| Right-hand grey text |
The job's runs-on |
Problems and Quick Fixes
Actions Visualizer underlines the exact dependency, condition or outputs block responsible for a
workflow problem. Open the editor's Quick Fix menu to apply a targeted correction:
- Duplicate and self-referential
needs: entries are preferred safe fixes and can be cleaned up
together with Fix all safe Actions Visualizer issues.
- Removing an undefined dependency, an always-false condition or an unconsumed outputs block can
change workflow behaviour, so these actions are clearly named, never preferred and only run when
selected individually.
- Problems without a generally correct rewrite, such as a job condition using a step-only context,
a
needs: cycle or an if: that does not parse, remain precise diagnostics rather than offering
a guessed edit.
Settings
| Setting |
Default |
Description |
actions-visualizer.showSteps |
collapsed |
How much step detail job rows start with: collapsed, expanded or never. |
actions-visualizer.expandMatrix |
false |
Expand every matrix card to one row per combination by default. |
actions-visualizer.direction |
LR |
Direction the graph flows in: LR or TB. |
actions-visualizer.liveUpdate |
true |
Re-render the graph while you edit the workflow file. |
actions-visualizer.updateDelayMs |
300 |
Debounce in milliseconds before a live update re-renders. |
Commands
| Command |
Description |
| Actions Visualizer: Open Workflow Graph to the Side |
Opens the graph beside the YAML |
| Actions Visualizer: Open Workflow Graph |
Opens the graph in the current column |
| Actions Visualizer: Export Workflow Graph as SVG |
Saves the current graph as a standalone SVG in the current theme's colours; also on the graph's toolbar |
Good to know
- Nothing ever runs. The simulation is entirely static and never contacts GitHub or Gitea, so
anything only a real run could know stays marked unknown rather than guessed.
- Layout happens in the extension host, not the webview, so the graph is computed once and the
webview only draws it. Large workflows stay responsive.
- A workflow that does not parse shows the YAML error rather than a blank panel, so the preview
stays useful mid-edit.
- Matrix expansion is capped at 50 rows. When a matrix is larger the graph says so, rather than
silently dropping combinations.
Contributing
npm install
npm test # unit tests
npm run test:e2e # extension host tests plus the Playwright webview tests
npm run validate # everything CI runs
See AGENTS.md for the architecture and the conventions to follow.
Feedback
Bugs and feature requests are welcome in
the issue tracker.
License
MIT — see LICENSE.
| |