mcpField
P
psychopomp

psychopomp

github.com/kitlangtonCreative & media41389 installsUpdated 1w ago
Repository

About psychopomp

psychopomp is a skill for creating narrated explainer reels from verified software changes. It guides authors through research, scripting, scene construction, review, and rendering, with visuals timed to narration.

Key features

  1. 01Fact-check changes against diffs and tests
  2. 02Write clip-based narration scripts
  3. 03Compose animated scenes with reusable actors
  4. 04Align visual beats to transcript phrases
  5. 05Review and render complete reels

Use cases

  1. 01Explain a pull request through animated scenes
  2. 02Create narrated before-and-after code walkthroughs
  3. 03Show behavior changes with timed callouts and counters

Install

Copy the command and run it in a terminal.

$npx skills add https://github.com/kitlangton/psychopomp --skill psychopomp

After installing, type/psychopompin Claude Code to confirm it loaded.

Bundled filesSTORY.md

Details

From the project README

Psychopomp explainer reels

An explainer reel is several independently authored Scene Plans joined on one clock: behavior stories on the GPU Stage (orbs, cards, beams, packets, camera, bloom), pinned Callouts, Rolling Numbers, changes as animated Stepped Diffs, captions in a terminal voice, and optional spoken narration. When narrated, every visual beat waits for a phrase in the transcript, so re-voicing re-times the film automatically.

The working examples are in scenes/pr-walkthrough: src/flagship.rs (#50825 as a Stage film that zooms into its code), src/stop_stage.rs (#50042 combining Stage, Callouts, Rolling Number, and a Zoom into an annotated code diff), and src/lib.rs (the five-PR reel with sequence diagrams). Copy their shape; do not start from a blank crate. Component payloads, channels, and commands live in SCENE_PLANS.md ("Make A Narrated Explainer Reel") and terms in CONTEXT.md; read both before authoring. Engineering rules are in AGENTS.md.

Steps

  1. Establish the facts. For each change, read the actual diff and PR description (gh pr diff, gh pr view) and write one sentence each for the broken behavior, the fixed behavior, and the change. Done when every claim you will narrate is traceable to code, a test, or a reproduced run; mark anything you could not verify and keep it out of the script.

  2. Write the script as scenes/<name>/narration/script.json, one clip per part: intro, then per change <slug>-before, <slug>-after, <slug>-code, then outro. Follow STORY.md when writing it. Done when each clip is under about 30 seconds spoken and every planned visual beat has a distinct anchor phrase in it.

  3. Voice it (optional). Draft first with no credentials (--draft uses macOS say), or synthesize final narration with ElevenLabs (engine: "elevenlabs", default model eleven_v4) or Fish Audio (engine: "fish"):

    {
      "engine": "elevenlabs",
      "model": "eleven_v4",
      "voice": "<elevenlabs-voice-id>",
      "settings": { "stability": 0.5, "similarity": 0.7 },
      "clips": [
        { "id": "change-before", "text": "[warm, conversational voice] ..." }
      ]
    }
    
    # Draft (no API key required)
    bun scripts/narrate.ts scenes/<name>/narration/script.json --draft
    
    # ElevenLabs (requires ELEVENLABS_API_KEY and script.voice)
    ELEVENLABS_API_KEY=... bun scripts/narrate.ts scenes/<name>/narration/script.json
    
    # Fish Audio (requires FISH_AUDIO_API_KEY)
    FISH_AUDIO_API_KEY=... bun scripts/narrate.ts scenes/<name>/narration/script.json
    

    scripts/narrate.ts normalizes loudness, transcribes word timings with Whisper, and writes <id>.mp3, <id>.words.json, and narration.json. When switching voices or engines, rebuild the Scene Plan against the new word timings.

    Or declare the narration in the Scene Program with psychopomp-media (SCENE_PLANS.md, "Declare Narration And Sound"): media.say(id, &voice, text), media.dialogue, media.sfx, and audio.derive(Effect::pitch(..)), then media.finish()?. Each line is generated once and recorded in media.lock.json; later runs call nothing unless a declaration changed, and ElevenLabs words come from its character alignment with the script's spelling. Check the cost first with PSYCHOPOMP_MEDIA=plan cargo run -p <crate> (zero API calls), time with PSYCHOPOMP_MEDIA=draft, and clean up with PSYCHOPOMP_MEDIA=prune. Move a narrate.ts scene with cargo run -p psychopomp-media -- adopt scenes/<name>/narration (no regeneration). Done when a plain run reports every resource =.

  4. Author the Scene Program in scenes/<name> (the workspace picks it up; add it to verify.json with a few key times). Use psychopomp::score (SCENE_PLANS.md, "Author With The Score DSL") so PlanBuilder is the only mut binding and every actor (Stage, Camera, Caption, Callout, RollingNumber, Checklist, Meter, Bars, Subtitles, Confetti, Terminal, Chat, ChangedFiles, LowerThird, Lens) is an immutable value whose methods return composable Beats (.then, .also, .with, .on_end, .after, .early, stagger, each, at!(scene, time => ...)). Schedule clips with Narration::reading, build elements with StageElement::card|orb|beam|packet|label|ring and relative psychopomp::layout::Placement envelopes on StagePost::RESTRAINED, frame with Caption::header|chip|footer, and play sounds with sfx::*.beat(id, gain_db) (or audio.beat(gain_db) for psychopomp-media). Load the explainer-motion skill and apply it to every beat: cards settle_in, beams connect, messages send, replies wait for .reply(), impacts hit, the hero orb_in, the fix's rewind; use Callout to pin annotations to Stage elements or Editor code ranges and RollingNumber for live counters/timers; drive camera moves through stage.camera() (establish, frame, follow, release, orbit, whip, dolly_zoom). Reach for the visualization and text overlays instead of hand-building them from Stage labels and rings: Checklist for checks that run and resolve, Meter (MeterPlan::countdown) for a timeout ring, Bars for before/after numbers, Confetti for a success beat, and Subtitles (SubtitlesPlan::from_spoken) to burn in word-timed captions. Changes are Diffs of keep, add(step), and remove(step) lines, each step keyed to a phrase. Done when cargo run -p <crate> writes the reel and cargo run --release -- plan validate <reel> reports valid. A missing phrase panics with its clip: change the anchor to words the transcript actually contains.

  5. Review before rendering. Get segment spans from plan inspect <reel>, then contact-sheet each segment at its before, switch, after, and code beats:

    bun scripts/sheet.ts <reel> t1,t2,... --theme neutral --shutter --out output/sheet.jpg
    

    When tuning the look, set PSYCHOPOMP_SHADER_DIR=crates/psychopomp-render/src/render and edit stage.wgsl/stage_post.wgsl: every frame and sheet picks up shader edits without a Rust rebuild. Then render one behavior segment with audio (plan render <reel> out.mp4 --cue <scene-id>) or a 2–3 second window (--range a..b) and inspect frames extracted during motion. Done when every segment has been looked at and no text overlaps, clips, or reads against the wrong chip.

  6. Render and verify the whole reel once, after the sheets and short windows pass. Run it in the foreground with a long timeout and never end a turn while it is still running:

    cargo run --release -- plan render <reel> output/<name>.mp4 --theme neutral
    

    Done when ffprobe shows 1920x1080, 60 fps, AAC audio, and a duration matching plan inspect, and loudness is near -16 LUFS with peaks under -1 dBFS.

  7. Deliver the MP4 path. Commit the scene, narration assets, and any engine changes in Psychopomp with conventional messages.

Improving Psychopomp

When a reel needs something the engine cannot do, add it to Psychopomp rather than working around it in one scene: plan types and validation in crates/psychopomp, strict-channel preflight in plan_runtime, pixels in render, GPU-free tests plus an #[ignore] GPU test, and the docs AGENTS.md asks you to keep current. Run cargo run --release -- verify baseline before the change and verify compare --expect <scenes you changed> after it to prove every other showroom and film is untouched.

Build from small reusable pieces. Interpolation, easing, curves, shape ports, and connectors belong in psychopomp::math (organized like pmndrs math: core lerp/ remap/smoothstep, easing, curve, shapes, random; glam vectors). Extend it instead of inlining math in a renderer or scene, and split renderers into one small helper per element, as render/stage.rs does. Keep cargo fmt --check, cargo clippy --workspace --all-targets --all-features -- -D warnings, and cargo test --workspace green.

Comments

Markdown supported