fix(cli): add snapshot --no-clean to stop wiping prior output#1891
fix(cli): add snapshot --no-clean to stop wiping prior output#1891miguel-heygen wants to merge 1 commit into
Conversation
Every `snapshot` run unconditionally deleted existing .png/.jpg files in the output directory before capturing new ones, so two consecutive runs with different --at sets clobbered each other with no way to opt out. Extracted the wipe into cleanSnapshotDir(), matching the existing computeSnapshotTimes/tailFrameTime extraction pattern in this file so it's unit-testable directly, and gated it behind a new --clean flag (default true, preserving current behavior) following the same default-true/ --no-x-to-opt-out convention already established by --end/--no-end.
james-russo-rames-d-jusso
left a comment
There was a problem hiding this comment.
Reviewed at 43de93a.
🟢 Approve — well-scoped --no-clean opt-out for snapshot
What's right:
- Fix matches the report cleanly: two consecutive
snapshotruns with different--atsets no longer clobber each other; existing default (--clean=true) preserves current wipe behavior for the single-run case. - Convention alignment with the neighboring
--end/--no-endflag in the same command — default-true +--no-x-to-opt-out — keeps the CLI feel consistent. ExtractedcleanSnapshotDir()mirrors the siblingcomputeSnapshotTimes/tailFrameTimeextraction pattern for direct unit-testability. - Test coverage geometry is exactly right for the extraction:
- Deletes images ✓
- Preserves non-image files (
descriptions.md) — critical, since that file is emitted alongside snapshots by thedescribemode ✓ - No-throw on missing dir ✓
- Extension regex
\.(png|jpg|jpeg)$matches the pre-PR inline regex verbatim — no scope change (won't accidentally delete.webp/.gifetc., but also won't accidentally start deleting them either — parity with existing behavior). - New
--cleanboolean flag threading (command →captureSnapshotsopts →cleanSnapshotDircall site) is complete:args.clean !== falsegate atsnapshot.ts:657matches the same pattern used forargs.end !== falsetwo lines up.
Minor observations:
- nit — the
--no-cleandocstring on the flag is good, but if two runs write different--atframes to the same dir, they'll interleave with differentframe-NN-at-…prefixes and thecontact-sheet.jpggets regenerated per-run overwriting the last. Might be worth noting in the docstring that the contact sheet is per-run even with--no-clean. Not a blocker. - nit —
cleanSnapshotDir's catch-alltry { … } catch { }(best-effort) silently swallows anything other than ENOENT (permission errors on a real dir, for instance). Preserves the pre-PR behavior (the inline block did the same), so no regression, but the JSDoc could mention "swallows all errors" for the next reader.
Reviewed by AI Employee Rames D Jusso — bot-authored PR posture: comment-only, stamps require James Russo's explicit go-ahead.
vanceingalls
left a comment
There was a problem hiding this comment.
Reviewed at 43de93a.
🟢 R1 verdict — LGTM, co-signing Rames
Independently verified: --clean default-true with --no-clean opt-out follows the same convention as sibling --end/--no-end; extracted cleanSnapshotDir is unit-testable with the same shape as sibling computeSnapshotTimes/tailFrameTime; args.clean !== false gate at the call site is symmetric with args.end !== false two lines up. Rames covered the convention alignment, extension-regex parity, test-geometry, and best-effort-swallow nuance — all reproduce independently.
Runtime-interop cross-PR notes (none blocking)
- vs #1913: different directories (
snapshots/vsbrowsers/), different processes. No overlap. - vs #1881: both touch
snapshot.ts.--no-cleanand repeated---atdetection are orthogonal —countRepeatedAtFlaginspectsprocess.argvand warns;--cleanis a citty-parsed boolean flag threaded through opts. Merge-order independent, no cross-PR line conflict.
Rames flagged two nits (contact-sheet regen even under --no-clean, silent error-swallow in cleanSnapshotDir's catch). Both are pre-PR behaviors preserved verbatim — I agree with his non-blocking classification.
Fine to merge from a runtime-interop perspective.
Review by Via (runtime-interop lens) — co-signing Rames
Summary
Two independent reports of the same friction:
snapshotunconditionally deletes existing.png/.jpgfiles in the output directory before capturing new ones, so running it twice with different--atsets clobbers the first run's output with no way to opt out — "each run wipes snapshots/, so two consecutive runs for different timestamp sets clobber each other."(The same report also mentioned
snapshot"silently rounds requested timestamps" and "appends an unrequested extra frame" — both turned out to already be intentional, documented, tested behavior:computeSnapshotTimes/tailFrameTimeround to millisecond precision deliberately and always append a readable end-of-timeline frame to avoid a known blank-at-exact-duration artifact, with an existing--no-endflag to opt out. Only the directory-wipe had no equivalent opt-out.)Fix
Extracted the wipe into
cleanSnapshotDir(), matching this file's existingcomputeSnapshotTimes/tailFrameTimeextraction pattern so it's unit-testable directly, and gated it behind a new--cleanflag (defaulttrue, preserving current behavior) — following the exact same default-true/--no-x-to-opt-out convention already established by--end/--no-endin this same command.Test plan
bunx vitest run packages/cli/src/commands/snapshot.test.ts— 12 tests pass (9 existing + 3 new)cleanSnapshotDirdeletes existing image files, only removes image files (leaves e.g.descriptions.mduntouched), and doesn't throw on a nonexistent directorybun run build— full monorepo typecheck passes