# screenshot-rename *A Claude Code skill that turns a folder of timestamp-named screenshots*
*into a folder of human-readable, searchable filenames —*
*using parallel Haiku vision agents.* [**Homepage**](https://pages.tojo.team/cardinale/screenshot-rename/)  ·  [SKILL.md](SKILL.md)  ·  [pipeline.py](pipeline.py)  ·  [MIT](LICENSE)
Before / after — five real renames, including U+202F handling and user-keyword preservation

``` CleanShot 2026-04-15 at 09.14.07.png ↓ CleanShot - Shamel Studio Affiliate Referral Code Modal - 2026-04-15 at 09.14.07.png ``` Built for [CleanShot](https://cleanshot.com)-style screenshot folders, but works on any directory of `.png` / `.gif` / `.mp4` / `.pdf` files named only by timestamp. Recognizes both **`CleanShot ...`** and Apple **`Screenshot ...`** filenames in the same pass, preserves any leading user-typed keyword prefix, and is safe to re-run on a folder that's already partially renamed. ## Highlights - **Parallel** — describes ~200 files in 3 minutes using 10 concurrent Haiku subagents. - **Safe** — pre-builds the full rename plan in memory, validates uniqueness and target collisions, then renames atomically with a file-count audit. Designed after losing 4 files to a `mv` overwrite during prototyping. - **Multi-prefix** — same pipeline handles `CleanShot ...`, Apple `Screenshot ...`, and files with hand-typed leading keywords (e.g. `jojo travel CleanShot ...`). - **Idempotent** — re-running on a folder skips files already in the renamed `App - Description - timestamp.ext` form. No description-stacking. - **Handles video / PDF** — extracts the first frame so vision agents can describe them. - **Resizes for the vision tool** — Retina screenshots exceed Read's image cap; pipeline downsamples to 1568 px max. ## A session, end to end
Claude Code session — user prompt, parallel Haiku fan-out across ten batches, receipt of the run
The skill activates when you ask Claude conversationally. Behind the scenes it preps the folder, fans out ten Haiku agents in a single round-trip, validates the resulting plan, then applies the renames in a single Python pass with a file-count audit at the end. ## Installation This is a Claude Code skill. Drop the repo into `~/.claude/skills/`: ```bash git clone https://gitea.tojo.team/cardinale/screenshot-rename.git \ ~/.claude/skills/screenshot-rename ``` In your next Claude Code session, ask: > rename all the cleanshot files in `~/Documents/Screenshots/` based on their content The skill will activate automatically from its description. ## Usage from the command line You can also drive the pipeline directly: ```bash # 1. Prep — extract frames, resize, build batches python3 pipeline.py prep --src "/path/to/folder" --batch-size 19 # 2. (In a Claude Code session, dispatch one Haiku subagent per # /tmp/screenshot-rename/full-batch-NN file using the prompt template # in SKILL.md.) # 3. Plan — aggregate descriptions, validate, build rename map python3 pipeline.py plan --src "/path/to/folder" # 4. Execute — apply the plan, audit file count python3 pipeline.py execute --src "/path/to/folder" ``` The dispatch step (#2) currently requires a Claude Code session. ## What the parser accepts | Form | Recognized | Becomes | |---|---|---| | `CleanShot 2026-MM-DD at HH.MM.SS.png` | yes | `CleanShot - - 2026-MM-DD at HH.MM.SS.png` | | `Screenshot 2026-MM-DD at H.MM.SS PM.png` (with U+202F) | yes — U+202F normalized to ASCII space | `Screenshot - - 2026-MM-DD at H.MM.SS PM.png` | | ` CleanShot 2026-MM-DD at HH.MM.SS.png` | yes — keywords title-cased and prepended to the AI description | `CleanShot - - 2026-MM-DD at HH.MM.SS.png` | | `App - - 2026-MM-DD at HH.MM.SS.png` | already renamed → **skipped** | (unchanged) | ## The gotchas this skill encodes This skill exists because every one of these caused real damage during development: 1. The `Read` tool has an image-size cap. Resize first. 2. Vision can't read `.mp4` or multi-page `.pdf` directly. Extract a frame. 3. **Bash regex `[[ =~ ]]` does NOT populate `BASH_REMATCH` in zsh.** Targets become empty. Loops collide on the same filename. Files vanish. Use Python for any filename mutation. 4. `mv` silently overwrites. Use `mv -n` or `os.rename` with explicit pre-existence check. 5. Pre-build the entire rename plan in memory and validate uniqueness before any `mv`. 6. Audit `len(os.listdir(DEST))` before and after. Equal count == proof no overwrites. 7. iCloud-synced files in Time Machine local snapshots are file-provider stubs, not bytes. External backups (Backblaze, Time Machine to physical disk) are the real recovery source. 8. `Bash run_in_background` may exit early on `while read` loops. Run renames foreground via Python. 9. Haiku occasionally returns the resized `.jpg` filename instead of the original `.png`. Validator must try alt extensions. 10. Always preserve the original `.mp4` / `.pdf` extension — describe via the extracted frame, rename the source. 11. **macOS `Screenshot` filenames contain U+202F (NARROW NO-BREAK SPACE)** before AM/PM. Haiku echoes it as ASCII space, so a verbatim filename lookup misses every Screenshot file. Normalize on both sides of the lookup; emit ASCII space in the new name. 12. **Re-running is only safe if the parser skips already-renamed files.** Detect `^App - .+ - timestamp.ext$` and exclude. 13. **Leading user-typed keyword prefix is signal, not noise.** Title-case the keywords and prepend them to the AI description before assembling the new name. The full discussion is in [SKILL.md](SKILL.md#the-critical-gotchas-every-one-of-these-caused-real-pain). ## Real-world impact | Run | Files | What happened | |---|---|---| | 1 | 196 CleanShot | Lost 4 to the bash-regex-in-zsh gotcha (#3). | | 2 | 196 CleanShot | Rebuilt with Python and `mv -n` — 189 renamed cleanly, zero loss. | | 3 | 20 mixed (CleanShot + Apple Screenshot + one user-prefixed) | First plan attempt dropped every Screenshot file with a misleading `NO_DESC` error. Diagnosed the U+202F gotcha (#11) via `repr()` of the live filename. After adding U+202F normalization, multi-prefix support, and keyword preservation — all 20 renamed in one pass. | ## Roadmap - Direct Anthropic API mode (no Claude Code session required) — needs `ANTHROPIC_API_KEY` - Custom prompt templates per-folder - Optional preservation of dots in technical strings (`v2.1` currently becomes `V21`) - Dry-run flag on `execute` ## License MIT — see [LICENSE](LICENSE).