psychopomp
Make explainer reels and deterministic motion graphics with Psychopomp: walkthrough videos of pull requests, code changes, protocols, or lifecycles, with GPU Stage choreography, callouts, rolling numbers, sequence diagrams, animated diffs, and optional ElevenLabs or Fish Audio narration.
- License 许可
- MIT
- 所属仓库
- mikker/dotfiles
- 最近更新
- 2026-11-10
- Stars
- ★ 116
- Forks
- 4
01 前言 Front Matter
- name
- psychopomp
- description
- Make explainer reels and deterministic motion graphics with Psychopomp: walkthrough videos of pull requests, code changes, protocols, or lifecycles, with GPU Stage choreography, callouts, rolling numbers, sequence diagrams, animated diffs, and optional ElevenLabs or Fish Audio narration.
02 资源文件 1
- 参考文档STORY.md2.5 KB
03 SKILL.md 正文
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
-
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. -
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. -
Voice it (optional). Draft first with no credentials (
--draftuses macOSsay), or synthesize final narration with ElevenLabs (engine: "elevenlabs", default modeleleven_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.jsonscripts/narrate.tsnormalizes loudness, transcribes word timings with Whisper, and writes<id>.mp3,<id>.words.json, andnarration.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, andaudio.derive(Effect::pitch(..)), thenmedia.finish()?. Each line is generated once and recorded inmedia.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 withPSYCHOPOMP_MEDIA=plan cargo run -p <crate>(zero API calls), time withPSYCHOPOMP_MEDIA=draft, and clean up withPSYCHOPOMP_MEDIA=prune. Move a narrate.ts scene withcargo run -p psychopomp-media -- adopt scenes/<name>/narration(no regeneration). Done when a plain run reports every resource=. -
Author the Scene Program in
scenes/<name>(add it to the workspace). Usepsychopomp::score(SCENE_PLANS.md, "Author With The Score DSL") soPlanBuilderis the onlymutbinding 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 composableBeats (.then,.also,.with,.on_end,.after,.early,stagger,each,at!(scene, time => ...)). Schedule clips withNarration::reading, build elements withStageElement::card|orb|beam|packet|label|ringand relativepsychopomp::layout::Placementenvelopes onStagePost::RESTRAINED, frame withCaption::header|chip|footer, and play sounds withsfx::*.beat(id, gain_db)(oraudio.beat(gain_db)forpsychopomp-media). Load theexplainer-motionskill and apply it to every beat: cardssettle_in, beamsconnect, messagessend, replies wait for.reply(), impactshit, the heroorb_in, the fix'srewind; useCalloutto pin annotations to Stage elements or Editor code ranges andRollingNumberfor live counters/timers; drive camera moves throughstage.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:Checklistfor checks that run and resolve,Meter(MeterPlan::countdown) for a timeout ring,Barsfor before/after numbers,Confettifor a success beat, andSubtitles(SubtitlesPlan::from_spoken) to burn in word-timed captions. Changes areDiffs ofkeep,add(step), andremove(step)lines, each step keyed to a phrase. Done whencargo run -p <crate>writes the reel andcargo run --release -- plan validate <reel>reports valid. A missing phrase panics with its clip: change the anchor to words the transcript actually contains. -
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.jpgWhen tuning the look, set
PSYCHOPOMP_SHADER_DIR=crates/psychopomp-render/src/renderand editstage.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>) 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. -
Render and verify the whole reel:
cargo run --release -- plan render <reel> output/<name>.mp4 --theme neutralDone when
ffprobeshows 1920x1080, 60 fps, AAC audio, and a duration matchingplan inspect, and loudness is near -16 LUFS with peaks under -1 dBFS. -
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.
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.
04 同仓库更多 Skills
unslop
Cut AI tells from any writing.
stageshow
Build faux product UI that looks like a real product, as a still illustration or an animated demo with a cursor. Browser and app windows, dashboards, phones, notifications, terminals, bento grids and cursor walkthroughs, framed, masked and cropped the way a marketing page, feature card or docs hero needs them. Use whenever someone wants a fake or mocked UI, a product demo or walkthrough animation, a hero or feature-card illustration of an app, a product shot in the style of a polished marketing site, or an animated cursor doing things in an interface, even if they never say "faux UI". React and plain CSS.
clean-copy
Cut interface copy that shouldn't exist — captions that restate a label, hints that pre-empt a worry the user never had, warnings that are scar tissue from a fixed bug. Use when writing or reviewing any user-facing string (labels, hints, empty states, toasts, confirmations, button text), or when asked to clean up / tighten copy.
elyx
Elyx is a design tool with local, AI-friendly `.elyx` files for screens, components, layouts, flows, and design systems. This skill is only available to agents running in external harnesses while using the Elyx CLI. It is unavailable and must not be used when running in the ACP harness inside the Elyx app. In external harnesses, use this skill before reading, reasoning about, or answering any question that touches Elyx or `.elyx` files — authoring, editing, validating, inspecting, reviewing, refactoring, comparing, or recommending changes, including read-only analysis where you produce no edit. Do not reason about `.elyx` from source text alone; imports, instances, overrides, and layout only resolve through the tools.
uikit-app-modernization
Modernizes UIKit apps for multi-window environments by replacing legacy shared-state APIs with context-appropriate modern alternatives. This includes references to mainScreen, interfaceOrientation, application and scene lifecycle, as well as safe area inset updates.
swiftui-whats-new-27
New SwiftUI APIs, behaviors, and deprecations in the 2027 OS releases (iOS 27 and aligned macOS/watchOS/tvOS/visionOS). Consult when asked what's new in SwiftUI 27, or when working with: - @State compile errors after an SDK update ("used before being initialized", "invalid redeclaration of synthesized property") or @State becaming a macro - @ViewBuilder/@ContentBuilder ambiguous overloads or type-check regressions; drag-to-reorder in any container (List, stacks, grids, custom layouts) via reorderable()/reorderContainer and its drag-and-drop - AsyncImage caching, reloading on scroll, or a custom URLSession - Swipe actions outside List (ScrollView, LazyVStack, LazyVGrid) via swipeActions()/swipeActionsContainer() - Toolbar overflow, visibility (visibilityPriority, ToolbarOverflowMenu, toolbarMinimizeBehavior), and pinning - Presenting a confirmationDialog or alert from an optional item binding