Releases: Davidb-2107/capcut-cli-david
Release list
v2.7.0
Full Changelog: v2.6.0...v2.7.0
v2.6.0
v2.5.0
v2.4.0
v2.3.0
[2.3.0] — 2026-07-14
Minor release. Per-card caption color cycling — alternate a caption's base color across a repeating palette, card by card (the "1 word / alternating color" karaoke look).
Added
import-captions --color-cycle "#hex,#hex,..."— cardi's base text color becomescycle[i % n], overriding the uniform--color/default#FFFFFF. Applies to both the lean and--clone-stylepaths. Verified against a real CapCut draft:restyle(font/stroke/shadow preset swap) preserves the cycled colors unchanged.
Compatibility
import-captionswithout--color-cycleis byte-identical to 2.2.0 — the cycle is an additive, opt-in override.
v2.2.0
v2.1.0
v2.0.1
2.0.1 — tracks -H tolerates unnamed tracks
Patch release. One bugfix, no API changes, no new verbs. tracks -H no longer
crashes on drafts containing tracks written by other tools.
The bug it fixes
track.name is optional on disk: tracks created by cutcli audios add /
cutcli videos add (and potentially CapCut itself) carry no name field. The
Track interface declared it required (name: string), so the unguarded
t.name.padEnd(…) in the -H table formatter passed the compiler and crashed at
runtime on the first unnamed track:
$ capcut-david tracks "<draft>" -H
{"error":"Cannot read properties of undefined (reading 'padEnd')"} # exit != 0
The JSON output had the same root cause in a quieter form: the name key was
silently absent for those tracks.
Found on a real production draft (Stickman A/B rire-de-tout_AB-A, 2026-07-12):
a cutcli-created SFX track and a cutcli-created video track, both unnamed.
Highlights
- 🔧
cmdTracksnormalizesnameto""(t.name ?? "") at the data-map level —
one guard covers both the-Htable and the JSON output. - 🧬 Type honesty:
Track.name?: string— the compiler now rejects any future
unguarded string use. All othertrack.nameuses insrc/are===comparisons
(undefined-safe, unchanged). - 🧪 499 tests (+1 regression: unnamed track renders in
-H, exit 0, JSON
name === ""). Typecheck clean.
Migration
None. Read-only inspection path; no draft output changes — the byte-identity
contract (v1.3.0) is untouched.
Compatibility
- CapCut ≥ 5.x desktop (Windows + macOS) — unchanged.
- Node
>= 18— unchanged. - Runtime dependencies: zero — unchanged.
v2.0.0
What's Changed
- chore(lint): clear Biome format+lint baseline — CI green by @Davidb-2107 in #1
New Contributors
- @Davidb-2107 made their first contribution in #1
Full Changelog: v1.16.0...v2.0.0
v1.16.0
1.16.0 — caption position goes native (import-captions --transform-y)
Minor release. import-captions learns where to put the captions: the new
--transform-y <n> flag sets clip.transform.y on every rebuilt caption segment,
which used to be hardcoded to the screen centre ({x:0, y:0}). Output is
byte-identical whenever the flag is not used.
The problem it solves
import-captions REPLACES the text track and rebuilds each segment at transform
{0,0} — centre screen. Pipelines that want captions lower (TikTok mi-bas /
lower-third) had to re-pin transform.y AFTER the import with an orchestrator-side
patch step (Niche_PC's step 3b restore_caption_transform), or order the chain so
a final restyle re-grafts the position from a gabarit. With the position native
to the import, the re-pin step disappears and import-first chains keep their
position without a trailing restyle.
Highlights
- 📍
import-captions --transform-y <n>— every rebuilt caption segment gets
clip.transform.y = n. Global flag only (no per-card field).transform.xis
never touched. - ➖ Negatives and zero allowed. The value is validated as a finite number
(newparseFiniteFlag) — unlike the strictly-positive size flags.-0.4=
mi-bas,-0.6= lower third,0= centre. - 🧬 Clone-style aware. The segment build is shared, so the flag works
identically with and without--clone-style. - 🛡️ Validation. Non-numeric (
abc), empty ("") and whitespace-only values die
with a clear CliError — theNumber("") === 0JavaScript coercion trap is guarded
explicitly. The two malformed spellings that would otherwise degrade to ignored
positionals are refused as well:--transform-y=-0.4(equals syntax) and a
value-less trailing--transform-yboth die instead of silently importing your
captions recentered at y=0 with exit 0. - 🚩 Version-gate friendly.
--transform-yappears in--help, so an
orchestrator can probe the help text before relying on the flag. This matters:
a ≤ 1.15.x engine treats unknown flags as ignored positionals and exits 0 —
your captions would import recentered with no error. - 🔒 Byte-identity locked. A new oracle test freezes the exact v1.15.0
segment bytes (ids normalized) for the flag-absent path. - 🧪 494 tests (+16). Typecheck clean; lint baseline unchanged.
Usage
# batch captions pinned mi-bas, cloning the draft's existing caption style
$ capcut-david import-captions <draft> captions-styled.json \
--clone-style --color "#FFFFFF" --transform-y -0.4
# preflight an orchestrator (version gate): is the flag supported?
$ capcut-david --help | grep -q -- "--transform-y" || echo "engine too old"
Compatibility
- No
--transform-y→ byte-identical output to 1.15.0 (oracle-locked). - A later
restylegrafts the keys present in the preset'ssegmentblock onto
every caption segment — a position-bearing preset (one whosesegment.clip
carries a transform, like the vault gabarits) then overwrites the imported y.
A preset withoutsegment.clip(e.g. everymake-presetoutput, which emits
segment: {}) leaves the imported position intact. Put the position-bearing
import-captionsLAST in the caption chain, or keep carrying the position via
a clip-bearing restyle preset. - One deliberate side effect:
--transform-yis parsed globally, soadd-text
andset-textno longer swallow a literal--transform-y <n>into the rendered
text — the pair is consumed and ignored (and a non-numeric next token dies).
On ≤ 1.15.x the same invocation visibly garbled the caption text. The vertical
position flag onadd-textis and remains--y. - No other schema, dependency or behavior change; read commands untouched.