Deterministic terminal visuals and executable sessions from YAML
Features · Installation · Quick Start · Terminal Sessions · Configuration · Automation
Shellfie compiles YAML into deterministic terminal images, animations, and semantic transcripts. When explicitly requested, it can also run real PTY sessions, assert their output, record cassettes, and replay them offline.
Generated by Shellfie from examples/demo_animation.yml.
Features
- Deterministic compose mode that never executes configuration content
- Optional PTY execution with waits, assertions, captures, and redaction
- Editable recordings and offline cassette replay
- PNG, WebP, native SVG, HTML, GIF, APNG, MP4, WebM, and PNG sequence output
- Plain text, ANSI, JSON, and asciinema v2 semantic transcripts
- macOS, Ubuntu, Windows, and custom window styles, plus a headless mode
- ANSI colors and controls, Unicode grapheme handling, and configurable ambiguous width
- JSON Schemas, source-aware diagnostics, formatting, compilation, completion, and watch commands
- Resource limits, path policies, dependency preflight, and atomic output replacement
Installation
Add Shellfie to your Gemfile:
gem "shellfie"
Then install:
bundle install
Or install it directly:
gem install shellfie
Requirements
- Ruby 3.0+
- ImageMagick
- ffmpeg for MP4, WebM, and APNG output
Install media dependencies on macOS:
brew install imagemagick ffmpeg
On Ubuntu or Debian:
sudo apt install imagemagick ffmpeg
Static and animated composition works on macOS, Linux, and Windows. Live PTY sessions are not supported on native Windows.
Quick Start
Create terminal.yml:
version: 1
theme: macos
title: "Terminal"
lines:
- prompt: "$ "
command: "echo hello"
- output: "hello"
Generate an image:
shellfie generate terminal.yml -o terminal.png
Without -o, Shellfie writes terminal.png beside the input. shf is available as a short alias for shellfie.
Output Formats
| Kind | Formats |
|---|---|
| Static | PNG, WebP, native selectable-text SVG, accessible HTML |
| Animated | GIF, WebP, APNG, MP4, WebM, event-duration PNG sequence with timeline.json |
| Semantic | TXT, ANSI, JSON, asciinema v2 (asciicast or cast) |
Use svg-raster only when the legacy PNG-backed SVG behavior is required.
CLI
| Command | Purpose |
|---|---|
generate |
Render a version 1 compose configuration |
run |
Execute and render a version 2 terminal session |
record |
Execute a session and save a cassette or editable YAML |
replay |
Render a cassette without executing its commands |
new / init |
Create or print starter configuration |
format |
Normalize YAML formatting |
compile |
Print the resolved configuration or session IR |
validate |
Validate input as text, JSON, SARIF, or JUnit |
inspect |
Show resolved settings, dimensions, fonts, and resource estimates |
schema |
Print the version 1 or version 2 JSON Schema |
completion |
Print bash, zsh, fish, or PowerShell completion |
watch |
Regenerate when a configuration or include changes |
themes |
List available themes and color schemes |
doctor |
Check media tools, formats, fonts, policies, and storage |
version |
Show the installed Shellfie version |
Terminal Sessions
Version 2 sessions execute commands only through run or record; review a session before running it. A minimal session looks like this:
version: 2
mode: run
title: "Recorded shell"
theme: macos
terminal:
shell: /bin/sh
columns: 80
rows: 16
timeout: 10s
steps:
- type: printf 'hello from shellfie\n'
speed: 30cps
- key: enter
- expect:
screen_contains: hello from shellfie
exit_status: 0
- capture: complete
outputs:
- path: session.svg
format: svg
capture: complete
- path: session.txt
format: txt
Run, record, or replay it:
shellfie run session.yml
shellfie record session.yml --cassette session.json
shellfie record session.yml --yaml editable-recording.yml
shellfie replay session.json -o session.gif --animate
Session Actions
| Action | Purpose |
|---|---|
run |
Execute a command directly, hidden by default |
type |
Type visible text at a configurable rate |
key |
Send Enter, arrows, modifiers, and other terminal keys |
sleep |
Add a deterministic presentation pause |
wait |
Wait for screen, line, prompt, exit, or stable-screen conditions |
expect |
Assert screen text, lines, exit status, cursor, elapsed time, or a text golden |
hide / show |
Control whether preparation and cleanup are recorded |
capture |
Name an intermediate screen for a specific output |
Sessions also support requires, step-level working directories, multiple outputs, reusable includes, environment allowlists, total and per-step timeouts, and redaction patterns. Set terminal.cwd_policy: root to confine working directories to the session root. Use async: true before interacting with a long-running or full-screen process, then synchronize with wait.
Variables use {{name}} placeholders. Reusable step_sets expand through use, repeat repeats a step or set, and if can select steps by OS, shell, Ruby requirement, or explicitly configured environment values. Expansion is bounded and cycles are rejected.
Authoring
Create and inspect configurations without rendering them:
shellfie new terminal.yml --template static
shellfie new demo.yml --template animation
shellfie new session.yml --template run
shellfie format terminal.yml
shellfie compile terminal.yml --format json
shellfie validate terminal.yml --format sarif
shellfie inspect terminal.yml --json
shellfie watch terminal.yml -o terminal.png
Templates include static, animation, run, tui, ci, and theme-gallery. JSON Schemas live in schema/; add the following directive for editor validation:
# yaml-language-server: $schema=../schema/shellfie-v1.schema.json
Use schema version 2 for executable sessions. Run shellfie schema 1 or shellfie schema 2 to print either schema, and shellfie completion zsh to generate shell completion.
Automation
Use the Docker-based GitHub Action to generate an output or fail when a committed output is stale:
- uses: ydah/shellfie@main
with:
input: examples/simple.yml
output: docs/terminal.png
check: "true"
Or build and run the included container locally:
docker build -t shellfie .
docker run --rm -v "$PWD:/work" shellfie \
generate /work/examples/simple.yml -o /work/terminal.png --force
Use --manifest manifest.json during generation to record configuration and output hashes, Ruby, OS, ImageMagick, ffmpeg, and resolved font fingerprints.
Configuration
Compose Configuration
| Key | Purpose |
|---|---|
theme |
macos, ubuntu, windows, or custom |
window_theme |
Window chrome to use with a custom theme |
color_scheme |
Built-in palette: dracula, one_dark, solarized_dark, or catppuccin_mocha |
colors |
Individual color overrides |
window |
Width, height, padding, wrapping, clipping, scrolling, and terminal policies |
font |
Family, fallback families, size, and line height |
animation |
Typing, timing, playback, loop, palette, and encoder controls |
cursor |
Cursor style and color |
headless |
Hide window chrome |
lines |
Initial or static terminal content |
frames |
Animated terminal events |
limits |
Source, output-frame, pixel, and temporary-storage ceilings |
Static content uses lines; animations add frames:
version: 1
theme: macos
title: "Demo"
window:
width: 600
padding: 20
visible_lines: 8
overflow: clip
animation:
typing_speed: 50
framerate: 30
playback_speed: 1.0
cursor_blink: true
loop: true
seed: 1
lines:
- output: "Ready"
frames:
- prompt: "$ "
type: "echo hello"
delay: 500
- output: "hello"
delay: 1000
A frame delay is applied after its action; on a type frame it overrides animation.command_delay. When both lines and frames are present, lines initialize the screen before animated events run.
Generate Options
| Option | Description |
|---|---|
-o, --output PATH |
Output path or {name}-{theme}-{scale}.{format} template |
--preset NAME |
Exact readme, ogp, widescreen, standard, or vertical canvas |
-t, --theme NAME |
Override the theme |
-a, --animate |
Render animation |
-s, --scale FACTOR |
Output scale: 1, 2, or 3 |
-w, --width PIXELS |
Override width |
--format FORMAT |
Select an output format |
--check |
Fail if an existing output is stale without replacing it |
--jobs N |
Render up to 32 inputs in parallel after preflight |
--typing-rate CPS |
Override typing rate in characters per second |
--framerate FPS |
Set output timing precision |
--seed N |
Set the deterministic animation seed |
--playback-speed FACTOR |
Scale the final timeline |
--overflow MODE |
clip, wrap, or scroll |
--no-shadow |
Disable the window shadow |
--transparent |
Use a transparent background |
--no-header |
Hide window chrome |
--force |
Replace an existing output |
Advanced Controls
- Set
animation.directiontoforward,reverse, orping_pong;animation.loop_offsetselects a different initial frame. - Tune GIF colors and optimization, WebP quality and lossless mode, APNG prediction and loop count, and scroll easing under
animation. - Set
window.ambiguous_widthto1or2; inspection and manifests report the active Unicode and width-table profile. - Set
window.osc_policy: preserveto keep boundedhttp,https, andmailtoOSC 8 links in SVG and HTML. - Set
window.graphics_policy: errorto reject SIXEL, Kitty, or iTerm2 graphics instead of discarding them. - Use
includefor shared YAML. Setinclude_policy: rootto reject paths outside the root configuration directory, including symlink escapes. - Set resource ceilings under
limits; Shellfie checks source size, pixels, frame work, and temporary storage before expensive rendering.
How It Works
- Configuration parsing resolves includes, validates input, and reports source locations.
- Compose, live PTY, or cassette input is normalized into terminal events.
- The terminal screen applies Unicode, ANSI, cursor, erase, alternate-screen, and scroll behavior.
- Raster, SVG, HTML, video, or semantic renderers produce the requested outputs.
- Output files are written atomically after dependency, collision, and resource preflight.
generate is deterministic and never runs configuration content. run and record opt into PTY execution. replay uses a cassette and does not execute the recorded commands.
Development
bundle install
bundle exec rspec
gem build shellfie.gemspec
Contributing
Bug reports and pull requests are welcome. See CONTRIBUTING.md and SECURITY.md before submitting changes or security reports.
License
Released under the MIT License.