---
name: "codex-conversation-capture"
description: "Capture a Codex conversation (active, historical or already archived) as images or as a smooth-scrolling video, with size and format (png/jpg/webp, mp4/mov/webm/gif) chosen per task, saved into the asset library under the naming rule. Triggers: conversation screenshot, chat screenshot, export chat history as images, scrolling screenshot, record this conversation as a video, long-conversation screen recording, archived conversation, turn historical conversations into assets, export webm/gif, 4K portrait, put a conversation into a video; window screenshot, desktop screenshot, window recording, desktop recording, recording with the mouse cursor, grab a program's screen, add visual verification for computer use."
---

> Original skill · commissioned by the user · English edition, 2026-11-12

# Conversation assets / desktop assets capture (screenshots + recording)

## What it can do
Rebuild any Codex conversation (active, historical, archived — all work) **offline into a single screenshot-ready conversation page**, then output two kinds of video assets:

- **Still frames**: sliced by aspect ratio into `1080×1920` (or 16:9 / 4:5 / 1:1) images at 2x pixel density, ready for image posts, cards and cover images.
- **Scrolling video**: an MP4 that scrolls smoothly through the whole conversation (H.264, 1080×1920 by default), with a title bar, progress bar and eased in/out.

The data is read directly from the local `thread_history` (it is not a screen capture), so: **the window does not need to be in the foreground, occlusion does not matter, and archived conversations export just as well**; fonts and layout are identical every time.

## Two paths: decide which asset you need first
| What you want | Which path | Command |
|---|---|---|
| Turn a piece of **Codex conversation** into images / a scrolling video (occlusion-proof, archived conversations included) | **Render** (rebuild HTML from thread_history) | `capture.py run --thread ...` |
| Capture what some **window / the whole desktop** looks like right now, or record it (real pixels, with the cursor) | **Pixel capture** (PrintWindow + GDI + ffmpeg) | `capture.py deskcap --pid ...` |

Decision rule: **"conversation" → render; "window / desktop / screen" → pixel capture**. Both outputs land in the asset library, but the first is immune to occlusion and has controllable layout, while the second is a real frame and can record someone else's software.

Do not use it for: producing a final cut — that goes to `video-editing` / `koubo-video`; clicking buttons and typing text inside an app — that is `computer-use` (this skill only covers "can see it", not "can click it").

## Quick start

```powershell
# 0) Self-check first (node / ffmpeg / headless browser)
python <SKILL_DIR>\scripts\capture.py doctor

# 1) Find a conversation (archived ones included by default)
python ...\capture.py list --search <keyword>
python ...\capture.py list --archived-only --limit 30

# 2) Images + video (the most common case)
python ...\capture.py run --thread <thread-id> --last-turns 5 --do both --aspect 9:16 --intro

# 3) Only the turns that fit on screen
python ...\capture.py run --search <keyword> --do both --grep <keyword> --grep-context 1
```

(`...` means `<SKILL_DIR>\scripts`; same below.)

## Choosing a conversation
| Parameter | Description |
|---|---|
| `--thread <thread-id>` | id or id prefix, the most reliable |
| `--search <keyword>` | matches title / first question; several hits raise an error and list them, add `--all` to export all |
| `--latest 3` | the 3 most recent conversations (archived included) |
| `list --archived-only` | archived only |

## Choosing which content
| Parameter | Description |
|---|---|
| `--last-turns 5` | capture only the last 5 turns (the most common choice for assets) |
| `--turns 2-6` | an explicit turn range |
| `--grep <keyword>` | only turns containing that keyword; `--grep-context 1` adds one turn before and after |
| `--mode clean` | **default**: keep only your question and the Codex reply, tool calls collapse into a "ran N steps" label, clean frame |
| `--mode full` | include reasoning, terminal commands and their output too — good for a "the AI is working" process feel |

Turn numbers are shown on the frame as `Turn N`; use `--dry-run` to check the counts before the real export.

## Output and naming rule
Asset library root: `<WORKSPACE>/asset-library/Codex-sessions` (change it with `--out` or the `CODEX_CAPTURE_LIB` environment variable).

```
asset-library/Codex-sessions/
  <YYYYMMDD-HHMM>_<topic-keyword>_<thread-id-8>/
      <YYYYMMDD-HHMM>_<topic-keyword>_<thread-id-8>_01.png        # slice 1
      <YYYYMMDD-HHMM>_<topic-keyword>_<thread-id-8>_02.png
      <YYYYMMDD-HHMM>_<topic-keyword>_<thread-id-8>_full.png      # full-page long image (skipped automatically when too tall)
      <YYYYMMDD-HHMM>_<topic-keyword>_<thread-id-8>_scroll.mp4    # scrolling video
      <YYYYMMDD-HHMM>_<topic-keyword>_<thread-id-8>_page.html     # reproducible page source
      <YYYYMMDD-HHMM>_<topic-keyword>_<thread-id-8>_manifest.json # conversation/scope/size/sha256/warnings
      00-notes.md
  _index.md                                                       # library-wide index, appended newest first
```

Naming = `<generation time>_<title keyword>_<first 8 chars of the conversation id>_<sequence/type>`, and **the extension follows the format parameters** (`.png/.jpg/.webp`, `.mp4/.mov/.webm/.gif`). **Slices are cut on turn or message boundaries first**, so a sentence is never split in half; when the page is extremely long it automatically captures fewer images and writes a warning.

## Choosing a format
| Requirement | Parameter |
|---|---|
| Universal, best platform compatibility | `--video-format mp4` (default h264) |
| Smaller file at the same quality | `--video-format mp4 --video-codec h265` |
| Into an editing suite (ProRes 422 HQ) | `--video-format mov --video-codec prores` |
| Web/browser friendly, patent-free | `--video-format webm` (vp9; `vp8` can downgrade) |
| Sticker / autoplay preview | `--video-format gif` (frame rate capped at 15 by default, large files) |
| Lossless images | `--image-format png` (default) |
| Small images, uploadable as-is | `--image-format jpg --image-quality 85` |
| Smallest images (same quality) | `--image-format webp --image-quality 88` |

Combination examples: `--image-format webp --video-format mp4 --video-codec h265` is the "size first" set; `--image-format png --video-format mov --video-codec prores` is the "post-production first" set.

## Choosing a size per task
**The canvas (`--size`) decides layout and font size, the export size (`--shot-size` / `--video-size`) decides pixels**. For more resolution do not enlarge the canvas — raise the export size.

| Purpose | Command fragment |
|---|---|
| Douyin / WeChat Channels / Reels portrait | `--video-size 1080x1920` |
| Bilibili / YouTube landscape | `--size 1920x1080 --video-size 1920x1080` |
| Xiaohongshu 3:4 | `--aspect 3:4 --shot-size 1080x1440` |
| 4K portrait asset | `--shot-size 2160x3840 --video-size 2160x3840` |
| Light preview / quick render | `--video-size 720x1280` |
| Square cover | `--size 1080x1080 --shot-size 1080x1080` |
| Portrait layout squeezed into a landscape final cut | `--size 1080x1920 --video-size 1920x1080 --fit cover` |

## Frame parameters
| Parameter | Default | Description |
|---|---|---|
| `--aspect` | `9:16` | Presets: `9:16` 1080×1920, `16:9` 1920×1080, `4:5` 1080×1350, `3:4` 1080×1440, `1:1` 1080×1080, `2:3` 1080×1620, `4:3` 1440×1080, `21:9` 2520×1080 |
| `--size WxH` | from `--aspect` | **Arbitrary canvas size**, e.g. `1200x1600`, `900x1600` (overrides `--aspect`) |
| `--shot-size WxH` | none | **Exact output pixel size for images**, e.g. `2160x2880`, `1080x1080`; the render scale factor is computed and aligned automatically |
| `--video-size WxH` | canvas width (≤1920) | **Exact output pixel size for video**, e.g. `720x1280`, `3840x2160`; rounded to even numbers automatically |
| `--fit pad\|cover` | `pad` | When the target ratio differs from the canvas: pad with black to keep everything / crop to fill |
| `--theme` | `dark` | `dark` suits video; `light` suits article illustrations |
| `--intro` | off | Add a title cover card (title + time + turn count/question count) |
| `--subtitle` | empty | Small caption on the cover, e.g. "conversation transcript" |
| `--image-format` | `png` | `png` / `jpg` / `webp` |
| `--image-quality` | 92 | jpg / webp quality 1–100 (ignored for png) |
| `--video-format` | `mp4` | `mp4` / `mov` / `webm` / `gif` |
| `--video-codec` | follows the container | mp4: `h264`/`h265`; mov: `h264`/`prores`; webm: `vp9`/`vp8` |
| `--gif-fps` | 15 | GIF frame-rate cap |
| `--fps` | 25 | Scrolling-video frame rate |
| `--duration` | automatic | Computed from page height (about 620 px/second, 6–45 s); setting it manually overrides that |
| `--scale` | 2 | Image scale factor (only in effect when `--shot-size` is not given) |
| `--video-scale` | 1.25 | Recording scale factor; raised automatically when the target width exceeds the canvas |
| `--max-shots` / `--max-frames` | 24 / 700 | Hard caps for extremely long conversations |

The full-page long image (`_full.png`) is only scaled proportionally to the target width; it is never squashed or cropped.

## Acceptance (before every delivery)
1. The command itself only reports success when the output is non-empty, the PNG dimensions are sane and the MP4 duration is ≥1 second; otherwise it errors out and exits.
2. `manifest.json` records the width, height and sha256 of every image; consult it when unsure.
3. Spot-check one frame: `ffmpeg -ss 2 -i xxx_scroll.mp4 -frames:v 1 check.png` (write it to disk only, do not paste it back into the conversation).
4. For anything longer than 30 seconds, cut content with `--last-turns` or `--grep` rather than forcing the speed up; GIF especially bloats.
5. Before delivery, check the extension and the codec: `ffprobe -select_streams v:0 -show_entries stream=codec_name,width,height -of csv=p=0 <file>`.
5. Render time is roughly 4–5 times the final duration (25 fps, 1080 wide); allow enough time for long conversations.

## Desktop assets: window screenshot / recording (deskcap)
Real pixel capture, Windows only. A covered window can still be captured (PrintWindow grabs the window's own rendering), but **a minimized window cannot be captured** — it errors out and tells you to restore it or switch to `--full`.

```powershell
# List the windows (pid / size / title) and take a pid
python ...\capture.py deskcap --list

# Capture one window (by default saved into asset-library/desktop, filename <time>_<title-keyword>_shot.png)
python ...\capture.py deskcap --pid 9148
python ...\capture.py deskcap --title WeChat --out <OUT_DIR>\wechat.png

# Capture the whole screen
python ...\capture.py deskcap --full --out <OUT_DIR>\desktop.png

# Record one window (its region only) / record the whole screen
python ...\capture.py deskcap --pid 9148 --record --seconds 8
python ...\capture.py deskcap --record --seconds 8 --fps 30
```

| Parameter | Description |
|---|---|
| `--pid <number>` | Pick a window by process (for several windows in one process, the largest main window wins) |
| `--title <word>` | Pick a window by title keyword |
| `--full` | Capture the whole screen instead of a single window |
| `--record` | Record instead of screenshot; `--seconds` (default 5), `--fps` (default 30) |
| `--out` | Output file; without it the file is auto-named into `asset-library/desktop` |
| `--lib` | Use a different asset-library root |

**Working with computer use**: if on this workstation the `computer use` screenshot reports `FrameArrived timed out` (the WGC path is unavailable, common on Win10 20H2), use deskcap for the "visual verification" step — computer use locates controls and injects input, deskcap captures the result for inspection. The colour count reported by the command is a quality gate: `<=1` means a black or empty frame and is an immediate failure.

Default asset library: `<WORKSPACE>/asset-library/desktop` (conversation assets live in `asset-library/Codex-sessions`).

## Common problems
- **Several conversations match**: use `list --search` to get the id prefix, then `--thread`.
- **The page is empty / 0 turns**: that conversation has no renderable messages (usually a very old one); confirm with `list`, then switch conversations. The skill falls back to parsing the rollout file automatically.
- **The video is too long**: capture fewer turns, or speed it up with `--duration 20`; `--max-frames` is the fuse.
- **Chrome not found**: set `CHROME_PATH` to `chrome.exe`; by default it looks for the headless shell inside `ms-playwright`.
- **Chinese text turns into boxes**: only happens when the system lacks Microsoft YaHei; run `doctor` first on a different machine.

## Dependencies
`node` (zero-dependency built-in CDP, no playwright needed), `ffmpeg`/`ffprobe`, any Chromium-based browser. Everything runs locally and no conversation content is uploaded. Desktop pixel capture additionally needs `Pillow` (it is enough that `python -c "import PIL"` imports).

## Hand-off to video skills
- Still frames → B-roll for `video-editing`, the image/text layer for `koubo-video`, illustrations for `xiaohongshu-material`.
- Scrolling video → overlay it directly on a talking-head voiceover as the "evidence shot"; for picture-in-picture, capture at 9:16 and scale it down.
- Assets are **process artefacts** and do not go into `outputs/`; only a finished final cut goes to `outputs/<YYYYMMDD>-<topic>/`.
