Skip to main content

Deterministic, agent-native terminal demo pipeline — turn YAML screenplays into repeatable GIF/MP4 terminal demos

Project description

terminal-demo-studio

PyPI Python CI License

Turn YAML screenplays into deterministic GIF/MP4 terminal demos. Capture any TUI — Claude Code, Codex, htop, vim — with full keyboard interaction, approval-prompt automation, and safety controls.

Onboarding Neon


What it does

  • YAML in, GIF/MP4 out. Define a screenplay, get a repeatable demo video. No screen recording by hand.
  • Three execution lanes. Polished scripted renders, command/assert automation, or full-screen TUI capture with live keyboard interaction.
  • Captures complex TUIs. Claude Code, Codex, htop, vim, any interactive terminal app — rendered through a real terminal emulator (Kitty), not a text-mode simulator.
  • Agent-native. Machine-readable output contract (STATUS, RUN_DIR, MEDIA_GIF, SUMMARY, EVENTS) that agents can parse and act on.
  • Safe by default. Prompt-loop policies, lint gates, media redaction, bounded waits, and failure bundles with redacted diagnostics.

Quickstart

pip install terminal-demo-studio
tds init --destination my_demo
tds render my_demo/screenplays/getting_started.yaml --mode scripted --local --output gif --output-dir my_demo/outputs

That's it. Your GIF is in my_demo/outputs/.

Using Docker (zero local dependencies)

tds render my_demo/screenplays/getting_started.yaml --docker --output gif --output-dir my_demo/outputs

Docker mode bundles all system dependencies (vhs, ffmpeg, kitty, xvfb) automatically.


Platform support

Windows macOS Linux
Scripted (--mode scripted) Docker or local (vhs + ffmpeg) Local or Docker Local or Docker
Interactive (--mode interactive) Local Local Local
Visual (--mode visual) Docker Docker or local (kitty + xvfb) Local or Docker
pip install Yes Yes Yes
Python 3.11+ Yes Yes Yes

tds render auto-selects Docker when local dependencies are missing. Use --local to force local mode or --docker to force Docker mode.


Three execution lanes

Scripted (--mode scripted)

Cinematic, deterministic renders for marketing and docs. Compiles YAML actions into VHS tape format, renders through a headless terminal, and produces pixel-perfect GIF/MP4.

tds render screenplay.yaml --mode scripted --output gif

Interactive (--mode interactive)

Command/assert automation. Runs commands via subprocess, evaluates wait conditions and assertions, logs runtime events. No video output — pure execution verification.

tds run screenplay.yaml --mode interactive --output-dir outputs

Visual (--mode visual)

Full-screen TUI capture. Launches a Kitty terminal on a virtual X display, sends keystrokes, captures video with FFmpeg. Handles approval prompts automatically via configurable policies.

tds run screenplay.yaml --mode visual --output mp4

Capturing complex TUIs

The visual lane can capture any interactive terminal application. Here are real demos generated from YAML screenplays:

Claude Code (autonomous_video)

Captures a real Claude Code session — onboarding flow, interactive prompts, and all.

Claude Code Demo

GIF · MP4 · YAML

Codex (autonomous_video)

Builds and verifies a hello-world app through the Codex TUI.

Codex Demo

GIF · MP4 · YAML


Showcase gallery

All scripted demos below were generated from YAML screenplays in this repo.

Demo Theme Preview
Onboarding Neon TokyoNightStorm
Bugfix Glow Catppuccin Mocha
Recovery Retro GruvboxDark
Policy Guard Nord
Menu Contrast Dracula
Nightshift Speedrun TokyoNightStorm

Regenerate all showcase media:

./scripts/render_showcase_media.sh

Screenplay format

title: "My Demo"
output: "my_demo"
settings:
  width: 1440
  height: 900
  theme: "TokyoNightStorm"
  font_family: "Menlo"
  framerate: 30
scenarios:
  - label: "Setup and run"
    execution_mode: "scripted_vhs"  # or autonomous_pty / autonomous_video
    setup:
      - "npm install"
    actions:
      - type: "npm start"
      - wait_for: "Server running"
        wait_mode: "screen"
        wait_timeout: "10s"
      - type: "curl localhost:3000"
      - wait_for: "Hello"
        wait_mode: "screen"
        wait_timeout: "5s"

Action types

Action Lanes Description
type / command all Type text (scripted) or execute command (autonomous)
key / hotkey scripted, visual Send a keystroke (Enter, ctrl+c, Escape)
input visual Type raw text without pressing Enter
wait_for all Wait for text to appear on screen
wait_stable / sleep all Pause for a duration
assert_screen_regex interactive, visual Assert screen content matches regex
expect_exit_code interactive Assert command exit code

CLI reference

tds render <screenplay.yaml>       Render a screenplay to GIF/MP4
    --mode auto|scripted|interactive|visual
    --docker | --local              Runtime location
    --output gif|mp4                Output format (repeat for both)
    --output-dir PATH               Output directory
    --playback sequential|simultaneous
    --agent-prompts auto|manual|approve|deny
    --redact auto|off|input_line
    --template TEMPLATE             Use built-in template instead of file
    --keep-temp                     Keep intermediate files

tds run <screenplay.yaml>          Alias for render (same options)

tds validate <screenplay.yaml>     Validate YAML schema
    --json-schema                   Print JSON schema
    --explain                       Show screenplay summary

tds lint <screenplay.yaml>         Lint for policy and safety issues
    --json                          JSON output
    --strict                        Treat warnings as errors

tds new <name>                     Create new screenplay from template
    --template TEMPLATE             Template name (default: before_after_bugfix)
    --list-templates                List available templates
    --destination PATH              Output directory

tds init                           Initialize workspace with starter screenplay
    --destination PATH              Workspace root (default: .)
    --template TEMPLATE             Starter template
    --name NAME                     Screenplay name

tds doctor                         Check dependency health
    --mode auto|scripted|interactive|visual

tds debug <run_dir>                Inspect a completed run
    --json                          JSON output

Docker mode

Docker bundles all system dependencies (vhs, ffmpeg, kitty, xvfb, starship) in a single container image. The image is content-addressed and cached.

# Explicit Docker mode
tds render screenplay.yaml --docker --output gif

# Auto mode (uses Docker if available, falls back to local)
tds render screenplay.yaml --output gif

# Force rebuild the Docker image
tds render screenplay.yaml --docker --rebuild --output gif

Environment variables for Docker execution:

Variable Default Description
TDS_DOCKER_HARDENING true Enable --cap-drop ALL, --security-opt no-new-privileges
TDS_DOCKER_PIDS_LIMIT 512 Container PID limit
TDS_DOCKER_READ_ONLY false Read-only root filesystem
TDS_DOCKER_NETWORK (none) Docker network mode
TDS_DOCKER_IMAGE_RETENTION 3 Number of cached images to keep

Safety and reliability

Prompt policy

The visual lane can automatically handle approval prompts from AI agents (Claude Code, Codex):

agent_prompts:
  mode: "approve"           # auto | manual | approve | deny
  prompt_regex: "(?i)(proceed|confirm|allow)"
  allow_regex: "safe operation"
  allowed_command_prefixes: ["npm", "git"]
  max_rounds: 5
  approve_key: "y"
  deny_key: "n"

Lint gates

tds lint screenplay.yaml --strict

Catches unsafe configurations before execution: unbounded approval, missing prompt regex, unsupported actions per lane.

Media redaction

tds render screenplay.yaml --redact auto

Modes: auto (redact detected secrets), off, input_line (mask typed input lines). Sensitive values from environment variables (OPENAI_API_KEY, ANTHROPIC_API_KEY, etc.) are automatically detected and masked.

Failure bundles

Failed runs produce a diagnostic bundle at failure/:

  • reason.txt — redacted failure reason
  • screen.txt — redacted terminal snapshot
  • step.json — failed step metadata
  • video_runner.log — redacted process logs

GitHub Action

Add to your CI workflow:

- uses: tomallicino/terminal-demo-studio/.github/actions/render@main
  with:
    screenplay: examples/showcase/onboarding_tokyo_neon.yaml
    mode: scripted_vhs
    outputs: gif
    output_dir: outputs
    upload_artifact: true
    comment_pr: true

See the GitHub Action guide for full options.


Agent integration

Install as a skill

npx skills add tomallicino/terminal-demo-studio --skill terminal-demo-studio

Example agent prompt

Render examples/showcase/policy_nord_guard.yaml in scripted mode.
Return STATUS, RUN_DIR, MEDIA_GIF, MEDIA_MP4, and SUMMARY.
If status is failed, run `tds debug <run_dir> --json` and summarize root cause.

Output contract

Every tds render / tds run emits machine-readable keys:

STATUS=success
RUN_DIR=outputs/.terminal_demo_studio_runs/run-abc123
MEDIA_GIF=outputs/.terminal_demo_studio_runs/run-abc123/media/demo.gif
MEDIA_MP4=outputs/.terminal_demo_studio_runs/run-abc123/media/demo.mp4
SUMMARY=outputs/.terminal_demo_studio_runs/run-abc123/summary.json
EVENTS=outputs/.terminal_demo_studio_runs/run-abc123/runtime/events.jsonl

Artifact contract

Each run writes .terminal_demo_studio_runs/<run-id>/ with:

manifest.json           Run metadata
summary.json            Execution summary (status, lane, media paths)
media/*.gif|*.mp4       Rendered output
scenes/scene_*.mp4      Per-scenario videos (scripted, visual)
tapes/scene_*.tape      VHS tape files (scripted)
runtime/events.jsonl    Event log (autonomous lanes)
failure/*               Diagnostic bundle on failure

Additional docs


License

MIT (LICENSE)

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

terminal_demo_studio-0.2.0.tar.gz (64.6 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

terminal_demo_studio-0.2.0-py3-none-any.whl (57.2 kB view details)

Uploaded Python 3

File details

Details for the file terminal_demo_studio-0.2.0.tar.gz.

File metadata

  • Download URL: terminal_demo_studio-0.2.0.tar.gz
  • Upload date:
  • Size: 64.6 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for terminal_demo_studio-0.2.0.tar.gz
Algorithm Hash digest
SHA256 7db9aa54a7707653d9d67cdceac1b4aa040754d8e46fc999c3cbd595c705c0a7
MD5 a44f1f3633386355e44c6904251dab06
BLAKE2b-256 85292143141c56f42dc1fdf629cc14fda5db2de5e3a3b467fb5cc265d079758b

See more details on using hashes here.

Provenance

The following attestation bundles were made for terminal_demo_studio-0.2.0.tar.gz:

Publisher: publish.yml on tomallicino/terminal-demo-studio

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file terminal_demo_studio-0.2.0-py3-none-any.whl.

File metadata

File hashes

Hashes for terminal_demo_studio-0.2.0-py3-none-any.whl
Algorithm Hash digest
SHA256 ca07b864cb76046a1019881f4679acbc6f902a36ca6b1b32ee307ddff02a2946
MD5 f506bc5658ac1df864903b9f62838f29
BLAKE2b-256 653dac01f86d84550764de42ee3f8d7835df4c9768f15628595c6245804c6a5c

See more details on using hashes here.

Provenance

The following attestation bundles were made for terminal_demo_studio-0.2.0-py3-none-any.whl:

Publisher: publish.yml on tomallicino/terminal-demo-studio

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page