Markitdown
ImCa0/just-laws
Convert files and office documents to Markdown. An agent skill from ImCa0/just-laws.
Stitch a manifest of terminal recordings into one narrated MP4 (title/description card, then clip, repeated) and optionally upload it privately to YouTube.
$ npx skills add vfarcic/dot-agent-deck --skill demo-reel -a claude-codeProject install by default; add -g for ~/.claude/skills/.
$ gh skill install vfarcic/dot-agent-deck demo-reel --agent claude-codeProject scope by default; add --scope user for a personal install. Needs GitHub CLI 2.90.0 or later (public preview).
$ git clone --depth 1 https://github.com/vfarcic/dot-agent-deck.git skills-src && mkdir -p .claude/skills && cp -r skills-src/.claude/skills/demo-reel .claude/skills/demo-reel && rm -rf skills-srcUse ~/.claude/skills/ instead of .claude/skills for a personal install. The folder must contain SKILL.md.
Claude Code skills documentation · loads skills from .claude/skills/
Install the "demo-reel" agent skill from https://github.com/vfarcic/dot-agent-deck/tree/main/.claude/skills/demo-reel into .claude/skills/demo-reel/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "demo-reel", then confirm the skill loads.Claude Code copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$skill-installer install https://github.com/vfarcic/dot-agent-deck/tree/main/.claude/skills/demo-reelType this inside Codex. $skill-installer <name> installs a curated skill from openai/skills. The installer writes to $CODEX_HOME/skills (default ~/.codex/skills). Restart Codex if the skill does not show up.
$ npx skills add vfarcic/dot-agent-deck --skill demo-reel -a codexProject install goes to .agents/skills/; add -g for ~/.codex/skills/.
$ gh skill install vfarcic/dot-agent-deck demo-reel --agent codexProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/vfarcic/dot-agent-deck.git skills-src && mkdir -p .agents/skills && cp -r skills-src/.claude/skills/demo-reel .agents/skills/demo-reel && rm -rf skills-srcUse ~/.agents/skills/ instead of .agents/skills for a personal install.
Codex skills documentation · loads skills from .agents/skills/
Install the "demo-reel" agent skill from https://github.com/vfarcic/dot-agent-deck/tree/main/.claude/skills/demo-reel into .agents/skills/demo-reel/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "demo-reel", then confirm the skill loads.Codex copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ npx skills add vfarcic/dot-agent-deck --skill demo-reel -a cursorProject install goes to .agents/skills/; add -g for ~/.cursor/skills/.
$ gh skill install vfarcic/dot-agent-deck demo-reel --agent cursorProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/vfarcic/dot-agent-deck.git skills-src && mkdir -p .cursor/skills && cp -r skills-src/.claude/skills/demo-reel .cursor/skills/demo-reel && rm -rf skills-srcUse ~/.cursor/skills/ instead of .cursor/skills for a personal install.
Cursor skills documentation · loads skills from .cursor/skills/, .agents/skills/, .claude/skills/, .codex/skills/
Install the "demo-reel" agent skill from https://github.com/vfarcic/dot-agent-deck/tree/main/.claude/skills/demo-reel into .cursor/skills/demo-reel/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "demo-reel", then confirm the skill loads.Cursor copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ gemini skills install https://github.com/vfarcic/dot-agent-deck.git --path .claude/skills/demo-reel--scope user (default) or --scope workspace; --path is the subfolder of the repo that holds the skill; --consent skips the security confirmation prompt.
$ npx skills add vfarcic/dot-agent-deck --skill demo-reel -a gemini-cliProject install goes to .agents/skills/; add -g for ~/.gemini/skills/.
$ gh skill install vfarcic/dot-agent-deck demo-reel --agent gemini-cliProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/vfarcic/dot-agent-deck.git skills-src && mkdir -p .gemini/skills && cp -r skills-src/.claude/skills/demo-reel .gemini/skills/demo-reel && rm -rf skills-srcUse ~/.gemini/skills/ instead of .gemini/skills for a personal install, then run /skills reload.
Gemini CLI skills documentation · loads skills from .gemini/skills/, .agents/skills/
Install the "demo-reel" agent skill from https://github.com/vfarcic/dot-agent-deck/tree/main/.claude/skills/demo-reel into .gemini/skills/demo-reel/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "demo-reel", then confirm the skill loads.Gemini CLI copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ gh skill install vfarcic/dot-agent-deck demo-reelInstalls for Copilot at project scope by default; add --scope user for a personal install. Preview a skill first with gh skill preview. Needs GitHub CLI 2.90.0 or later (public preview).
$ npx skills add vfarcic/dot-agent-deck --skill demo-reel -a github-copilotProject install goes to .agents/skills/; add -g for ~/.copilot/skills/.
$ git clone --depth 1 https://github.com/vfarcic/dot-agent-deck.git skills-src && mkdir -p .github/skills && cp -r skills-src/.claude/skills/demo-reel .github/skills/demo-reel && rm -rf skills-srcUse ~/.copilot/skills/ instead of .github/skills for a personal install. Commit .github/skills so cloud agent and code review can use it.
GitHub Copilot skills documentation · loads skills from .github/skills/, .claude/skills/, .agents/skills/
Install the "demo-reel" agent skill from https://github.com/vfarcic/dot-agent-deck/tree/main/.claude/skills/demo-reel into .github/skills/demo-reel/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "demo-reel", then confirm the skill loads.GitHub Copilot copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ npx skills add vfarcic/dot-agent-deck --skill demo-reel -a opencodeOpenCode documents no install command of its own. Project install goes to .agents/skills/; add -g for ~/.config/opencode/skills/.
$ gh skill install vfarcic/dot-agent-deck demo-reel --agent opencodeProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/vfarcic/dot-agent-deck.git skills-src && mkdir -p .opencode/skills && cp -r skills-src/.claude/skills/demo-reel .opencode/skills/demo-reel && rm -rf skills-srcUse ~/.config/opencode/skills/ instead of .opencode/skills for a personal install.
OpenCode skills documentation · loads skills from .opencode/skills/, .claude/skills/, .agents/skills/
Install the "demo-reel" agent skill from https://github.com/vfarcic/dot-agent-deck/tree/main/.claude/skills/demo-reel into .opencode/skills/demo-reel/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "demo-reel", then confirm the skill loads.OpenCode copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
demo-reelStitch a manifest of terminal recordings into one narrated MP4 (title/description card, then clip, repeated) and optionally upload it privately to YouTube.
Demo Reel is an agent skill from vfarcic/dot-agent-deck. Stitch a manifest of terminal recordings into one narrated MP4 (title/description card, then clip, repeated) and optionally upload it privately to YouTube. Repo-agnostic engine driven only by a manifest.json; runnable by an agent or directly as reel.sh. Use when asked to build a demo reel / narrated video from a set of asciinema casts, gifs, or mp4 clips.
Its SKILL.md is about 5.2k tokens, which your agent loads only when the skill is triggered. The skill folder holds 9 other files (for example `reel.sh`, `retime.sh` and `tests/fixtures/manifest.json`).
It works with YouTube. The repository describes itself as: A rich terminal dashboard for monitoring and controlling multiple AI coding agent sessions. The licence is MIT.
2 steps, taken from the first numbered list in SKILL.md.
Read from SKILL.md and the folder at commit 9cc3e60. It shows what the files ask for, not the result of running them.
Pre-approves nothing: there is no allowed-tools line, so your agent's usual permission prompts apply.
From allowed-tools in the SKILL.md frontmatter.
Ships script files (Shell), which the agent can run.
Shell commands in SKILL.md call:
ffmpegFrom the folder's file list and the shell code blocks in SKILL.md.
Hosts in commands or code, which the agent is likely to contact:
youtu.beFrom URLs in SKILL.md, links to its own repository left out.
Names these keys or tokens, usually read from environment variables:
YOUTUBE_CLIENT_SECRETYOUTUBE_REFRESH_TOKENFrom names ending in _API_KEY, _TOKEN, _SECRET, _KEY or _PASSWORD in SKILL.md.
Demo Reel loads about 5.2k tokens when it runs. Until then it costs about 92 tokens; SKILL.md has 2,993 words of instructions outside code blocks.
Estimates: characters ÷ 4, the usual rule of thumb; real counts depend on the model's tokenizer. Scripts and assets cost tokens only if the agent reads them.
The automated check noted patterns worth knowing about, such as sudo or a known installer.
his repo they are sourced from `vals` / `.env.vals.yaml`:Automated static check — not a guarantee. Review scripts before installing. It scans the text of SKILL.md for risky patterns (piping downloads into a shell, reading credential files, hidden Unicode, destructive commands); files beside SKILL.md are not scanned.
The full file from vfarcic/dot-agent-deck at commit 9cc3e60, republished under its MIT licence (© vfarcic). 2,993 words, ~5,202 tokens.
.claude/skills/demo-reel/SKILL.md (or your agent's skills folder). This skill also uses 7 other files; get the full folder from GitHub.A reusable, repo-agnostic engine that turns an ordered manifest of {title, description, clip} entries into a single narrated MP4: for each entry it renders a title/description card, plays that entry's clip, then moves to the next — concatenated in manifest order. With --publish it uploads the result private to YouTube and prints the URL.
The engine knows nothing about Rust, tests, PRDs, or any specific repo. Its only input is a manifest.json. It is invocable by an agent (via this skill) and directly by a human or CI (reel.sh manifest.json --out reel.mp4).
Status: the full engine pipeline is wired. A run validates the manifest and prerequisites, renders a card per entry, stitches
[card, clip, …]into one uniform MP4 (reel.sh→ffmpeg), and — with--publishand credentials present — uploads it private to YouTube (upload.sh) and prints the URL. The stitch path is covered by a re-runnable local smoke (task reel-smoke); the live upload is verified by code review plus a documented one-line manual step (see Verifying the upload path).
reel.sh MANIFEST [--out OUT.mp4] [--title TITLE] [--publish]| Argument / option | Meaning |
|---|---|
MANIFEST | Path to a manifest.json (see Manifest contract below). Required, positional. |
--out OUT.mp4 | Where to write the stitched MP4. Default: reel.mp4. |
--title TITLE | Title for the uploaded video (used only with --publish). Default: the basename of --out without its extension (e.g. reel for reel.mp4). The engine is repo-agnostic and has no notion of a PRD, so a descriptive title is the caller's job — the dot-agent-deck adapter composes one and passes it through here. |
--publish | After stitching, upload the MP4 private to YouTube and print the URL. Requires the YouTube OAuth credentials (see Prerequisites). |
-h, --help | Print usage and exit. |
Examples:
reel.sh manifest.json --out reel.mp4 # stitch only, no upload
reel.sh manifest.json --out reel.mp4 --publish # stitch + upload private (title = "reel")
reel.sh manifest.json --out reel.mp4 --title "My demo reel" --publish # stitch + upload with an explicit titlemanifest.json is the only contract between a caller and the engine. It is a JSON array of one or more objects, in the order the segments should appear:
[
{
"title": "mouse/button/001 — inline-shortcut label",
"description": "Start the app, focus the dashboard, and confirm the Button widget renders its inline-shortcut label.",
"clip": "recordings/mouse-button-001/full-stream.cast"
},
{
"title": "Second segment",
"description": "What this clip shows, in 1–3 plain-English sentences.",
"clip": "clips/second.mp4"
}
]The engine rejects a manifest that breaks any of these rules, with a specific message and a non-zero exit:
title,
description, and clip.clip is a path to an existing .cast (asciinema v2), .gif, or .mp4
file. Paths are resolved relative to the current working directory. The
format is intentionally open: a cast renderer is just one optional
front-end, so an already-rendered gif/mp4 can be fed directly (this is
what lets a different recording tool reuse the same engine).The engine checks these before doing any work and fails fast with an actionable message naming exactly what is missing; it never self-installs anything.
Always required (CLIs on PATH):
| CLI | Used for | Package |
|---|---|---|
agg | render an asciinema cast to frames | nix asciinema-agg |
ffmpeg | stitch and encode the final MP4 | nix ffmpeg |
jq | parse and validate the manifest | nix jq |
curl | upload to YouTube (only with --publish) | nix curl |
Required only with --publish — YouTube Data API v3 OAuth credentials, read from the environment (never hardcoded). In this repo they are sourced from vals / .env.vals.yaml:
| Env var | Meaning |
|---|---|
YOUTUBE_CLIENT_ID | OAuth client id |
YOUTUBE_CLIENT_SECRET | OAuth client secret |
YOUTUBE_REFRESH_TOKEN | OAuth refresh token (minted once via a human consent flow) |
Stitch-only runs (no --publish) do not require any credentials. The one-time OAuth provisioning is documented in docs/develop/demo-reel.md.
For each manifest entry, in order:
CARD_COLSxCARD_ROWS grid (84x20 — shaped
~16:9 in character cells so a card fills the output canvas rather than
letterboxing inside it). CARD_ROWS is a minimum: a long description grows
the grid taller and the font shrinks to match, so text scales instead of
clipping. The card's hold duration is a flat CARD_HOLD seconds
(default 4s, env-overridable), independent of how much text the card
carries: a fixed, deliberately short hold keeps the reel moving, and a viewer
who wants to read a long description pauses the video rather than the reel
parking on every long card. The hold is enforced at the ffmpeg level — a
single painted still is frozen from the rendered card and looped to exactly
the hold duration — so it is decoupled from agg's idle handling (which would
otherwise collapse the static tail to a couple of seconds)..cast is first re-timed (retime.sh rewrites its event
timestamps for a watchable cadence — see below), then rendered through agg at
its recorded terminal grid; a pre-rendered gif/mp4 is used as-is (no
re-timing).Both are rendered at an agg font size fitted to the output canvas, not a
fixed one. A terminal grid carries no pixel size of its own, so choosing the font
is how the engine gets glyphs rasterized at output resolution: a 68x16 cast at
font 16 is only 674x381, which on a 1080p canvas is a ~2.8x upscale of already-
rasterized text (mush); at the fitted font (45) it renders 1896x1071 and stays
sharp. Set CLIP_FONT_SIZE / CARD_FONT_SIZE to pin a font and skip the fit.
e2e casts are recorded at machine speed, so their raw timeline is unwatchable: a keypress and the full repaint it triggers land within a millisecond of each other, while short real waits (daemon startup, polling, debounce) sit between them. A single global agg --speed cannot fix this — slowing everything stretches the waits into dead air and still cannot spread coincident events apart. So before rendering, every .cast clip is passed through retime.sh, which rebuilds the timeline from the event payloads (rendering then runs at CLIP_SPEED 1.0):
The contract: the re-timer re-distributes time, it does not manufacture it. Time reclaimed from dead air is re-spent holding operations, so the output totals about max(MIN_BUDGET, the input duration) — and never more than max(MIN_BUDGET, MAX_STRETCH × input), which is a hard ceiling that compresses everything proportionally if the base gaps alone would exceed it. When there is no dead air to reclaim, nothing is held and the clip plays at roughly real time. No cast can come out as a slideshow.
Two measured worked examples. A 28.7s cast of a real agent working, already smoothly paced (no gap over 0.03s), comes out at 32.3s — 1.12×, essentially unchanged. A synthetic cast of 6 repaints separated by 3s waits — 15s that is almost entirely dead air — comes out at 7s, with each repaint held the full OP_HOLD 1.4s: the waits are gone, every operation is now visible, and the whole thing is shorter than the original.
Each event is classified into one of three kinds, by payload size and content:
SIZE_THRESHOLD bytes is a full-region repaint (opening a deck/form/pane). Consecutive large chunks within COALESCE_GAP of each other are one logical repaint and are coalesced into a single step. An op is held after the repaint (up to OP_HOLD) so the new state is actually visible — budget permitting.TYPE_GAP apart, so typing replays at a readable speed.IDLE_CAP) and is never spread.Any single gap is clamped to IDLE_CAP, which is what kills dead air while still reading as a pause.
The final frame is held for a sized beat, not agg's default. Left alone, agg holds a clip's last frame for its default --last-frame-duration (3s in the pinned 1.9.0) on top of whatever tail the cast already spends on that final state — issue #365 measured PRD #341's reel ending on 5.27s of one identical frame, ~2.2s of cast tail plus agg's hold. So reel.sh sizes the hold per clip. It asks retime.sh --trailing how many seconds the retimed cast runs after its last visible change, renders the cast with that tail cut out (retime.sh --cut-tail moves the tail's events onto the last change's timestamp, so their cursor state still lands in the final frame), and passes --last-frame-duration max(CLIP_FINAL_DWELL, tail + CLIP_LAST_FRAME). The tail is folded into the hold rather than left in the cast because agg emits a frame only when the image changes: a tail of ticks that move nothing renders as no time at all, while one whose ticks move the cursor renders up to the last move, and telling the two apart takes a terminal emulator. CLIP_LAST_FRAME (default 1s) is the closing beat after the tail; CLIP_FINAL_DWELL (default 2s) is the least total time the final state stays on screen. The floor is for a cast that ends on its payoff — a test that stops recording the moment its assertion matches paints the answer at or near its last event, with little or no tail, and would otherwise show it for about one second. At the defaults, a cast with a tail of at least 1s gets nothing from the floor, so a clip with issue #365's ~2.2s tail ends on ~3.2s of final state instead of 5.27s. Both are env-overridable, in seconds of reel time (the tail is divided by CLIP_SPEED, which agg applies to the cast but not to its last-frame hold). Cards are unaffected: their hold is CARD_HOLD, enforced at the ffmpeg level.
A visible change here is any event that is not inert, and inert is deliberately narrower than the re-timer's tick: only SGR attributes (which affect later writes, not what is painted), cursor show/hide, and cursor positioning or movement — the render-loop tail. A tick is anything that prints no characters, which also covers erase, scroll and newline; those do change the screen, and counting them as tail would overstate how long the final state has been up and shorten its hold.
Why content and not size alone. PRD #339's published reel turned a 15.5s cast into a 161s video (10.4×), and the classifier is why: it called every small payload a keystroke. A ratatui render loop emits a per-frame tail of
SGR-reset + show-cursor + cursor-positionthat prints nothing at all, and those tails are the overwhelming majority of a cast's events — so each was given its own 100ms "typed char" step, and each coalesced repaint was then unconditionally held 1.4s on top. Re-measured on that test's replacement recording (28.7s, 1621 events: 1565 ticks, 44 real typed chars, 12 repaints), the old re-timer produces 172.4s — 1609 × 0.1s of fabricated typing cadence, which is ~93% of the total — while the new one produces 32.3s. Both failure modes are closed:tickevents are no longer mistaken for typing, andOP_HOLDis granted only out of reclaimed slack under the duration budget.
retime.sh is repo-agnostic (it operates on any .cast) and standalone (retime.sh [INPUT.cast] [--out OUT.cast] [--trailing | --cut-tail], reading stdin / writing stdout by default; --trailing and --cut-tail measure and remove the final-state tail described above instead of re-timing). Its tunables are env-overridable, like the engine's CLIP_SPEED — SIZE_THRESHOLD (80) is in bytes, MAX_STRETCH (1.6) is a ratio, everything else is in seconds: TYPE_GAP (0.1), OP_HOLD (1.4, a maximum now), IDLE_CAP (0.4), MIN_BUDGET (8), COALESCE_GAP (0.05). (IDLE_THRESHOLD is gone — every gap is simply clamped to IDLE_CAP, so there is no separate "is this a real wait" threshold.) CLIP_SPEED (default 1.0) remains as a global multiplier layered on top of the re-timer for the rare clip that wants a uniform nudge; note that it multiplies duration outside the re-timer's budget.
Every segment is then normalized (ffmpeg scale + pad) to one common resolution at a constant fps and yuv420p, so all segments share resolution/fps/pixfmt and concat into a single uniform stream (reel.mp4 by default).
That resolution is a fixed, landscape 16:9 canvas — REEL_WxREEL_H, default 1920x1080, a normal laptop screen — and is deliberately not derived from the segments. It used to be the per-axis max across every native segment, which silently produced a canvas whose aspect ratio belonged to neither a card nor a clip: PRD #339's reel took its width from the card (1140) and its height from a portrait 60x50 clip (1142), came out 1140x1142, and showed the terminal in a ~585px centre strip with black bars either side.
Segments are fit into the canvas (scale preserving aspect, then pad) and are never cropped — cropping a terminal would cut off content. So the canvas fixes the frame, but how much of it a clip fills remains a property of the recording:
The engine warns (non-fatally, per segment) when a clip's aspect is more than ASPECT_TOLERANCE (1.35) off the canvas, naming the percentage of the frame it will actually cover.
Every run reports these to stderr, so a bad artifact is visible before it is published rather than after someone watches it. All are non-fatal — the engine's job is to render what it was given — but each one names a specific thing to fix:
| Check | What it catches |
|---|---|
Cast integrity — a cursor-position escape (ESC[row;colH, ESC[colG) addressing a column beyond the cast header's width | The terminal was resized mid-recording. asciinema v2 stores one fixed size in its header and has no resize event, so the recorder writes the final size and every earlier, wider frame hard-wraps into garbage on replay. This is exactly what made PRD #339's clip unreadable (column 68 in a 60-column header). Re-record at one fixed size. |
Re-timing ratio — clip N: re-timed 15.5s -> 24.5s (1.58x) | A clip being stretched. The ratio is bounded by MAX_STRETCH, so anything wildly larger means a tunable (or CLIP_SPEED) was overridden into producing a slideshow. |
Aspect — a segment more than ASPECT_TOLERANCE off the canvas | A recording that will letterbox badly, with the percentage of the frame it will actually cover. |
Still worth doing by eye before publishing: extract a frame and look at it (ffmpeg -ss <t> -i reel.mp4 -frames:v 1 -update 1 frame.png). No automated check substitutes for seeing the thing.
A re-runnable smoke builds a reel from a tiny self-contained fixture (two hand-written .cast clips + a manifest under .claude/skills/demo-reel/tests/fixtures/) in stitch-only mode (no network, no credentials) and asserts the result with ffprobe: non-empty file, exactly one video stream at the expected resolution (a single stream proves there is no resolution/fps/pixfmt seam between segments), yuv420p, constant 30/1 fps, and a duration between the sum of the per-card holds and the engine's own upper bound on it (card holds + each clip's re-timing budget + a small per-clip allowance for agg's trailing hold). That upper bound is the regression guard for the 10.4x stretch above. It is local-only (never CI):
task reel-smoke
# or directly:
.claude/skills/demo-reel/tests/smoke.shupload.sh creates the video with privacyStatus: private, and reel.sh --publish passes no privacy flag at all, so the automated path can only ever produce a private video. upload.sh --privacy unlisted|public is the explicit escape hatch for a hand-run.
Why the default moved off unlisted: unlisted means anyone with the link can watch, and in this project's flow the link is written into the PR body and the changelog fragment, which flows into the public release notes. A reel clip is only eligible if the recorded run spun up a real agent, so every cast the reel stitches was written by a process holding live credentials. Uploading unlisted therefore put a credential-bearing recording on a publicly-reachable URL with no human between the two. Private keeps the same automation — the channel owner can always watch their own private videos, so an agent still uploads unattended and the link still goes in the PR — while making the human review a publication gate instead of a merge gate: flipping the video to unlisted before a release is a deliberate step somebody takes.
The video id survives a private → unlisted flip, so the link already in the PR and the changelog starts working with no re-upload and no link edit.
What private does not do. The upload has already happened; the bytes sit on Google's servers either way. Private contains a leak to the owner's own account. It does not undo one. And redaction upstream of this is a blocklist that can only remove values the harness registered — see docs/develop/e2e-lanes.md for its known gaps.
The live YouTube upload cannot be a routine automated test, so it is verified by code review of upload.sh plus a one-time manual check: with the three YOUTUBE_* credentials exported, run
.claude/skills/demo-reel/reel.sh some-manifest.json --out reel.mp4 --publishand confirm it prints an https://youtu.be/<id> URL that opens a private video — signed in as the channel owner it plays normally and shows a Private badge; signed out, or as any account the owner has not deliberately shared it with, it is unavailable. That asymmetry is the point (see Privacy below). All hosting lives in upload.sh alone, so swapping hosts later does not touch the rest of the engine.
--out without a value)
prints usage to stderr and exits non-zero.agg/ffmpeg/jq/curl) is a hard failure: it is
reported by name in the pre-flight check before any work starts; the message
points at docs/develop/demo-reel.md or to asking the agent, and does not
embed setup steps.--publish credentials degrade gracefully: the reel is still
stitched and the local MP4 is kept; only the upload is skipped, with a
"reel is at <path>; could not publish (missing …)" note. Stitch-only runs
never need credentials.upload.sh passes the API's raw
error through rather than swallowing it.© vfarcic, MIT. Rendered from Markdown: HTML in the file is shown as text, images as links, and headings moved down two levels. Raw file
SKILL.md and 7 other files in .claude/skills/demo-reel of vfarcic/dot-agent-deck.
Open the folder on GitHubat commit 9cc3e60
Demo Reel next to the 5 skills that share the most tags, products or categories with it. Stars are the repository's; “used in” counts other GitHub owners with a copy.
| Skill | Stars | Used in | Tokens | Auto-check | Licence | Repo updated |
|---|---|---|---|---|---|---|
| Demo Reel this skillvfarcic/dot-agent-deck | 109 | — | ~5.2k | Automated safety check: Notes | MIT | |
| MarkitdownImCa0/just-laws | 781 | 14 repos | ~3.2k | Automated safety check: Notes | MIT | |
| Banner Design Systemnextlevelbuilder/ui-ux-pro-max-skill | 135k | 1 repos | ~1.8k | Automated safety check: Pass | MIT | |
| Agent ReachPanniantong/Agent-Reach | 95k | — | ~1.4k | Automated safety check: Pass | MIT | |
| Last30daysmvanhorn/last30days-skill | 64k | — | ~7.9k | Automated safety check: Notes | MIT | |
| Baoyu URL To Markdownsdyckjq-lab/llm-wiki-skill | 2.5k | 2 repos | ~3.2k | Automated safety check: Pass | None |
ImCa0/just-laws
Convert files and office documents to Markdown. An agent skill from ImCa0/just-laws.
nextlevelbuilder/ui-ux-pro-max-skill
Walks through designing a banner for social media, ads, a website hero or print, from gathering requirements to building 2 or 3 art-direction options in HTML and CSS.
Panniantong/Agent-Reach
Routes web research and platform lookups across 16 sites, including Twitter, Reddit, YouTube, Bilibili, Xiaohongshu and GitHub, through one command-line tool.
mvanhorn/last30days-skill
Research what people actually say about any topic in the last 30 days.
sdyckjq-lab/llm-wiki-skill
Fetch any URL and convert to markdown using Chrome CDP. An agent skill from sdyckjq-lab/llm-wiki-skill.
AgriciDaniel/claude-seo
Pulls real Google data for SEO work: Search Console, PageSpeed Insights, CrUX field data, the Indexing API and GA4 organic traffic, through /seo google commands.
vfarcic/dot-agent-deck
Bring the base that every dispatched unit is cut from up to date before the first dot-agent-deck dispatch of a batch in this repo, and check the base dispatch reports afterwards.
vfarcic/dot-agent-deck
Choose the shape of a unit you are about to dispatch in this repo — one agent (--single) or a team (--orchestration '<name') — from divisibility criteria instead of asking, and report the shape you…
vfarcic/dot-agent-deck
Check that a change to the user-facing docs covers both clients (the TUI and the desktop app) unless the feature exists in only one, and decide whether it needs a new or updated screenshot, then…
vfarcic/dot-agent-deck
Generate a feature request prompt for another dot-ai project.
vfarcic/dot-agent-deck
Take committed work from a branch to a verified pull request — push, open the PR, settle CI and the automated review, answer and resolve every finding, and hand off.
vfarcic/dot-agent-deck
Publish the docs site to GHCR with a main-<sha tag and bump site/helm/values.yaml so Argo CD picks it up — without cutting a SemVer release.
Works with
Stitch a manifest of terminal recordings into one narrated MP4 (title/description card, then clip, repeated) and optionally upload it privately to YouTube. Demo Reel is an agent skill from vfarcic/dot-agent-deck. Stitch a manifest of terminal recordings into one narrated MP4 (title/description card, then clip, repeated) and optionally upload it privately to YouTube.
Demo Reel fits situations like: asked to build a demo reel / narrated video from a set of asciinema casts.
Run `npx skills add vfarcic/dot-agent-deck --skill demo-reel -a claude-code`. Or copy the skill folder (.claude/skills/demo-reel in vfarcic/dot-agent-deck) into .claude/skills/demo-reel in your project. Claude Code loads it when a task matches its description.
Run `npx skills add vfarcic/dot-agent-deck --skill demo-reel -a codex`. Or copy the skill folder (.claude/skills/demo-reel in vfarcic/dot-agent-deck) into .agents/skills/demo-reel in your project. Codex loads it when a task matches its description.
Cursor, Gemini CLI, GitHub Copilot and OpenCode also load SKILL.md folders. With the skills CLI, run `npx skills add vfarcic/dot-agent-deck --skill demo-reel -a cursor` (or -a gemini-cli, github-copilot or opencode for the others). To copy it by hand, put the folder in .cursor/skills/demo-reel, .gemini/skills/demo-reel, .github/skills/demo-reel and .opencode/skills/demo-reel in your project.
Going by SKILL.md and its folder, Demo Reel needs a shell for the scripts in its folder, the command-line tools its instructions call (ffmpeg) and credentials named YOUTUBE_CLIENT_SECRET and YOUTUBE_REFRESH_TOKEN. Our summary lists: A Bash shell; A credential in YOUTUBE_CLIENT_SECRET; A credential in YOUTUBE_REFRESH_TOKEN.
SKILL.md names 1 domain. In commands or code: youtu.be; the agent is likely to contact it when it follows the instructions. This is read from the text; nothing was executed.
Our automated static check of SKILL.md found notes only (mentions a .env file), nothing it rates as a warning. It is not a guarantee. Review the folder before installing.
Demo Reel is published under the MIT licence (the repository's licence). It allows redistribution, so the full SKILL.md is shown on this page.
About 5.2k tokens (SKILL.md is roughly 21k characters). Agents keep only the skill's name and description in context until a task matches; then they load SKILL.md in full.
Skills that share tags, products or a category with Demo Reel: Markitdown (ImCa0/just-laws, 781 stars), Banner Design System (nextlevelbuilder/ui-ux-pro-max-skill, 135k stars), Agent Reach (Panniantong/Agent-Reach, 95k stars) and Last30days (mvanhorn/last30days-skill, 64k stars). The comparison table on this page puts their stars, adoption, token cost, safety result and licence side by side.
vfarcic (a GitHub user) maintains it in vfarcic/dot-agent-deck, which has 109 GitHub stars. The repository holds 23 skills in this directory. The repository was last updated on October 10, 2026.
Source: vfarcic/dot-agent-deck on GitHub. Facts on this page come from the repository at the commit we read; the author's words are quoted as theirs.