All notable changes to capcut-cli are documented here. The format follows Keep a Changelog; the project follows Semantic Versioning.
Five features in one release — the next slice of the opportunity backlog, bundled. Two build on prior art from the capcut-cli-david fork (thanks @Davidb-2107). The sixth backlog item, keyframeable mask geometry (#44), is deliberately not in this release: no public ground truth exists for the on-disk encoding, and a guessed encoding would silently no-op in the app — it ships once a real app-authored mask-keyframe draft is captured.
duplicate <project> <segment-id> [--track <track-name>] [--new-track]— duplicate a segment at the SAME timeline position and duration onto a track that renders above the source: the PIP local-retouch flow from #44 (copy the clip above itself, thenmaskthe copy) without hand-editing JSON. By default — and with the explicit--new-track— the copy goes onto a fresh track of the same type inserted directly after the source track in the tracks array:sortTracksis stable within a type and a later same-type track renders above, so the copy sits exactly on top of its source (a second copy gets a unique<name>-copy-2track name).--track <track-name>places the copy onto that existing same-type track instead, and exits 1 with a clear error when the target range is occupied there, when the track is missing, or when its type does not match the source segment's;--trackand--new-tracktogether are rejected. ID hygiene: the new segment gets a fresh id; the source material entry — media included — is cloned with a fresh id (the media FILE on disk stays shared), so material-level edits on the copy (crop,mix-mode,replace-media) never leak to the source segment underneath it; every per-segment companion referenced viaextra_material_refs(speed, placeholder_info, sound_channel_mapping, vocal_separation, canvas, material_color, masks, animations, ...) is cloned the same way — the app treats those as per-segment instances, so two segments never share one. Embedded keyframe list/entry ids are re-minted on the copy. The timeline duration is unchanged. Prints{ new_segment_id, track_name, cloned_materials }plus the copy'smaterial_id,track_id, andnew_track; mutating, atomic save with.bak+ history snapshot, honors--dry-run.register <project-dir> [--apply] [--drafts <dir>]— the meta-repair sidecar for EXISTING drafts.initregisters only the drafts it creates, so an existing folder missing itsdraft_meta_info.jsonsidecar or its entry in the store'sroot_meta_info.jsonis invisible to the CapCut app with no repair path (doctorchecks the environment,diagnoseis read-only).registerderives id/name/duration fromdraft_content.json— a read-only source that is never written — and reports per target (needs_repair, per-targetstate/action/detail/stale_fields). Accepts the project directory or itsdraft_content.jsonpath (any other filename exits 1). Plan-only by default (always exit 0);--applyrecreates a missing/corrupt sidecar and inserts/updates the index entry — new entries clone the shape of an existing entry so they match the installed CapCut version, updates repair only the stale identifying fields (draft_id,draft_fold_path,draft_json_file,draft_root_path,tm_duration) in place and preserve everything else, including a non-emptydraft_name(CapCut's display name is user data). Writes are atomic (temp+fsync+rename) with a.bakper file that already existed;applied/backupslist exactly the files written. Idempotent: a re-run writes nothing and reportsapplied: [], exit 0. The store root is the draft's parent directory and must be known — aroot_meta_info.jsonbeside the draft folder, a managedcom.lveditor.draftpath (init's default location), or an explicit--drafts <dir>(which, likeinit, creates the index on a fresh store); a draft outside any known store root is reported explicitly and nothing is written. An unreadableroot_meta_info.jsonis never rewritten (it lists every draft) and is reported blocked instead.--applyrefuses while the editor is running and when a target changed on disk since the plan read, unless--force-write;--apply --dry-runpreviews (would_apply,applied: []). Exits 2 on--applywhen a target stays blocked (unknown store root, unreadable index).diagnosenow recommends theregisterplan form when it seesdraft_meta_info.jsonmissing.crop <project> <segment-id> [--ratio <r> | --rect <x,y,w,h> | --reset]— read and set the crop on a video/photo segment's source material: the 8-corner normalized struct CapCut stores onmaterials.videos[], which the factory wrote full-frame at creation and no command could read or edit before. With no flags the command is read-only: it prints the material's crop struct as JSON plus the stored sourcewidth/height(cropisnullwhen the material carries none) and writes nothing.--ratio <free|1:1|16:9|9:16|4:3|3:4>computes the centered maximal crop of that aspect against the source dimensions stored in the draft — when the dims are missing or zero it exits 1 with an error that points at--rect(freerestores the full frame without needing dims).--rect <x,y,w,h>sets an explicit normalized rect, all values 0..1 fractions of the source frame, validated asx,y >= 0,w,h > 0,x+w <= 1,y+h <= 1(a float-ulp tolerance keeps sums like0.3 + 0.7valid, and the written corners are clamped to 1);--rectoverrides--ratiowhen both are given.--resetrestores the full frame. The corner mapping matches the factory default exactly — y grows downward:upper_left = (x, y),upper_right = (x+w, y),lower_left = (x, y+h),lower_right = (x+w, y+h). When the material carries acrop_ratiofield it is stamped"free"— CapCut's preset enum values are not published, so the app recomputes from the corner points; stated in--help. Mutating command: registered in the mutating set, writes atomically with a.baksnapshot like its siblings, honors--dry-run, and prints the resulting crop JSON on write. The new--ratio/--rect/--resetflags are release-scoped tocrop, so free-text positionals of other commands containing those substrings survive verbatim.caption/import-srt— per-word keyword emphasis and per-cue colour cycling as ergonomic flags, replacing hand-written--stylesJSON for the viral-caption workflow (prior art: capcut-cli-david--keyword-sizev1.15 andimport-captions --color-cycle):--highlight-words <w1,w2,...|@file>— case-insensitive whole-word matches per cue get an emphasis text range;@filereads one word/phrase per line (phrases match across spaces). Word boundaries are Unicode-aware, sofürmatches inGrüße für allebut never insidefürs, andcapnever matches insidecapcut. Overlapping matches (e.g.New York+York) keep the earlier one.--keyword-color <#RRGGBB>— emphasis colour; defaults to#FFD700, the same goldcaption --karaokepaints the active word with (now the sharedKARAOKE_HIGHLIGHT_COLORconstant). Requires--highlight-words.--keyword-size <multiplier>— emphasis size as a multiplier on the cue's base font size (style-ref/preset/--font-sizeaware), default 1.2 when--highlight-wordsis present. Validated: must be > 0 and <= 10. Requires--highlight-words.--color-cycle <#hex1,#hex2,...>— rotates the BASE text colour per cue in list order, wrapping around; an independent axis from keyword emphasis. Precedence: explicit--colorstill sets the base colour for all cues unless--color-cycleis given (then the cycle wins per cue).- Precedence contract (documented in
--help): keyword emphasis ranges sit on top of base/karaoke styling and override the matched words' colour/size; with--karaoke, karaoke ranges are built first and keyword matches override those words while inheriting their bold — the v0.13 "explicit flags beat preset ranges" spirit. - One offset scheme. Emphasis ranges are computed in the exact code-unit → UTF-16LE-byte scheme
text-ranges/setTextRangesand the karaoke writer already use — correct for multibyte text (umlauts, CJK); no second offset scheme was introduced. - The four flags are release-scoped like the v0.13 parser additions: on commands that don't declare them (everything except
captionandimport-srt) the tokens fall through to free-text positionals verbatim. - JSON output gains
keyword_matches/color_cycleonly when the flags are used; with no flags, behaviour and output are byte-identical to v0.13.
- Every user-supplied text/JSON read now tolerates a leading UTF-8 BOM (
U+FEFF), the byte prefix Windows PowerShell'sSet-Content(and some editors) writes — previously the draft failed to load with a JSON parse error,.capcutrcwas silently ignored, and the SRT/ASS/JSONL parsers misread the first token. Covered paths:draft_content.json/draft_meta_info.json/root_meta_info.jsonand every other draft store candidate,--presetfiles,@filearguments (e.g.text-ranges --styles @ranges.json,--highlight-words @words.txt), stdin (import-srt -,import-ass -,batch,keyframe --batchJSONL), subtitle files (import-srt,import-ass,quickstart --srt,compilecaptions ops),compilespecs, template files, and.capcutrc. The CLI never writes a BOM: saving a BOM'd draft drops it (atomic write,.bakand history snapshots preserve the loaded content), the concurrent-change guard no longer reports a BOM-only difference as "changed on disk", andfixturebundles are emitted BOM-free. Output for BOM-free files is byte-identical to before. Prior art: capcut-cli-davideb2f0e0(thanks @Davidb-2107).
- Wider README hero /
media/og-card.pngbanner (1280x640, 2:1) replacing the 0.13.1 card. Docs-only release; no code changes.
- Refreshed the README hero /
media/og-card.pngsocial card (EN + zh reference the same asset). Docs-only release; no code changes since 0.13.0.
Six features in one release — the top of the opportunity backlog, bundled. Two build on prior art from the capcut-cli-david fork (thanks @Davidb-2107); see #36.
sync-timelines <project-dir> [--apply] [--force-write]— reconcile a CapCut >= 8.7 draft whosetemplate-2.tmp/draft_info.jsontimeline mirror has drifted fromdraft_content.json, so CLI edits are honored by the app instead of silently ignored.draft_content.jsonis always canonical and is treated read-only. Accepts only a project directory or itsdraft_content.json(any other filename exits 1). Plan-only by default: the per-target drift report includes each mirror's mtime plusnewer_mirrors/canonical_stale, and warns whendraft_content.jsonis older than a drifted mirror (you would overwrite newer edits).--applyrefuses that direction unless you add--force-write(exit 1), then rewrites only the drifted mirrors — the canonical file is never re-sorted or backed up, and in-sync mirrors are left untouched;reconciled/backupslist exactly the files written. No-ops with exit 0 when all targets already agree. An unreadable (binary/encrypted) mirror is reported unreconcilable (ok:false,in_sync:false, exit 2) and stays idempotent on re-run instead of pretending success.--apply --dry-runreports the plan only (would_reconcile,reconciled: []).diagnosenow recommends the plan form (with a back-up caution) instead of deferring to issue #35. Closes #39.lint <project> --fix— auto-repair mechanically-fixable draft defects, now four codes:cue-too-long(trims over-long captions to the configured cap),caption-overlap(shortens overlapping pairs so each ends where the next begins),line-too-long(greedy word wrap that swaps spaces for newlines 1:1, keeping styled-range byte offsets valid; never splits words — instances the wrap cannot actually fix, i.e. space-less/CJK text and over-cap single words, are stampedfixable:false), andcaption-gap-too-small(pulls the earlier caption's end back to restore the minimum gap; never moves starts, never creates a new overlap). The gap repair honors a hard floor — exportedMIN_CAPTION_DURATION_US = 100_000(100ms) — so a shrink that would land a caption below the floor is skipped and stampedfixable:falsefor that instance instead of collapsing it. Writes atomically with a.baksnapshot; combine with--dry-runto preview.missing-materialandmissing-filestay report-only deliberately: the only mechanical repairs would delete user timeline content or act on host-dependent paths. Closes #40.lint— new report-only ruleunknown-effect-slug(info severity, exit 0): flags effect/filter/animation resource ids in the draft that are not in the bundled enum table, surfacing them before CapCut silently drops them (the silent-failure mode reported across ecosystem tools, e.g. GuanYixuan/pyCapCut#12). Info rather than warning because store-downloaded effects on app-authored drafts are legitimate; only CLI-written stale slugs are at risk.export-srt <project> [--granularity line|word] [--format srt|vtt]— word-level caption export. Captions created bycaption --karaokecarry real per-word timing and export it exactly; plain captions interpolate word timing proportionally by word length (stated in--help). SRT + word emits one cue per word; VTT + word emits one cue per phrase with inline<hh:mm:ss.mmm>karaoke timestamps for burn-in pipelines. Defaults (line,srt) reproduce the previous output byte-identically.keyframe ... --easing <linear|ease-in|ease-out|ease-in-out>— CapCut-native easing curves, also accepted per-line (easingkey) inkeyframe --batchJSONL and in thecompilespec's keyframe op. The app does not store named curve types: the UI writesFreeCurveInOutbezier control handles on both keyframes of the eased segment, and the emitted encodings are locked against a UI-oracle capture (prior art: capcut-cli-david). Theken-burnsskill default changed linear → ease-out to match what the CapCut UI itself produces.detect-scenes <video> [--threshold <0..1>] [--min-gap <s>] [--limit <n>] [--json]— deterministic ffmpeg scene-cut detection (no AI, zero new dependencies) to seed the long-form → shorts flow: prints detected cut points (seconds,hh:mm:ss.mmmtimecode, scene score) plus a ready-to-use contiguous segment list in seconds and draft-native microseconds. Follows theprobe/renderexternal-binary pattern, including a clear actionable error when ffmpeg is missing.make-preset <project> <text-segment-id> --out <preset.json>— extract a hand-tuned text style (font, colors, style flags, alignment/transform, bubble, karaoke/multi-style ranges) from an existing draft into a versioned, portable preset file; apply it with the new--preset <file>flag onadd-text,text-style, andcaption. Addresses the recurring ecosystem ask for programmatic font/style reuse (GuanYixuan/pyJianYingDraft#192, Hommy-master/capcut-mate#57). Contract:- Explicit flags beat the preset — including its ranges.
--color/--font-sizeoverride every capturedtext_rangesblock over the covered span, not just the base style, so a karaoke/highlight preset re-renders in the flag color/size; applying the same preset without those flags preserves the per-range styling. - Presets are schema-validated on load.
transformmust be an object with finite numericx/y,bubblemust carry non-emptyeffect_id/resource_id,text_rangesmust be an array of objects with integerstart/endwhereend > startand correctly typed style fields. A malformed preset is rejected with a clear error and is never written into the draft. - Rangeless preset onto a multi-range segment collapses the ranges. Applying a preset that carries no
text_rangesonto a segment that still holds multiple range blocks (leftover karaoke/highlight) collapses them to the single uniform preset style spanning the whole text — consistent with the "applies the full preset" contract. A preset that itself carries ranges applies them unchanged. Documented in--help. make-preset --dry-runwrites nothing — the--outfile is neither created nor overwritten, and the JSON output reportsdryRun:truewithwritten:false(a normal run reportswritten:true).
- Explicit flags beat the preset — including its ranges.
- Parser: the value-consuming flags introduced in this release (
--threshold,--min-gap,--limit,--json,--granularity,--format,--easing,--preset,--apply) are scoped to the commands that declare them instead of being consumed globally, so free-text positionals that contain a flag-like substring survive verbatim (e.g.add-text ... New Year --limit 5 drinksstores the literal text). Flags that earlier releases already parsed globally are unchanged.
docs/version-support.md— the CapCut 8.7 row now namessync-timelinesas the repair path for drifted mirrors.docs/draft-schema/03-keyframes-and-animations.md— documents theFreeCurveInOutbezier-handle easing encoding.
quickstart <name> [--video <f>] [--audio <f>] [--srt <f>]— the one-command path from a single file to an editable draft. Creates the draft, adds the input (durations from ffprobe when available, a 5s placeholder otherwise), lints it with the same checks aslint, inspects the storage layout likediagnose, and prints the exact open-in-CapCut step. Exit 0 when created and lint-clean, 2 when created with lint errors. Reduces first-run friction for a CLI that now has 50+ commands.fixture <project> --out <dir>— build a shareable, redacted compatibility bundle. Copies only the timeline JSON (neverassets/media), redacts user home paths and email addresses, and writes a reporter README plus a diagnose report. Automates the "attach a sanitized project folder" step in the version-support flow so reporters can safely contribute the real CapCut 8.7 (issue #35) fixtures the storage adapter still needs.replace-media <project> <segment-id> <new-file> [--retime]— swap a segment's source clip in place (placeholder/proxy > final render) while preserving its timeline position, timing, effects, and keyframes. Copies the file intoassets/and refreshes intrinsic duration/dimensions via ffprobe. Distinct fromrelink, which only repairs broken paths by basename. Warns when the new clip is shorter than the segment uses;--retimefits the segment to the new clip. Honors--dry-run(no write, no copy). This is the assemble-with-placeholders-then-swap-in-finals workflow that fits the CLI's local, deterministic, agent-drivable positioning.
docs/jianying-encryption.md— decision record for JianYing 6.0+ draft encryption: detect, do not decrypt, with the rationale (legal posture, algorithm in flux) and the tripwires that would reopen the decision.capcut decryptnow links to it.docs/version-support.md— the reporting flow and the CapCut 8.7 row now referencecapcut fixturefor one-command sanitized bundles.
- Synchronized the English and Chinese READMEs with the shipped v0.11 surface: version-aware storage, v0.11.2 Windows fixes, six templates, 13 enum categories, 205 tests, the full cross-platform CI matrix, and the current GitHub Action reference.
- Windows now resolves the bundled template and spawned
serveCLI through proper filesystem paths instead of URL pathnames, fixinginit,compile, and queued jobs on drive-letter paths. - ESM test imports use
file:URLs on Windows, and a single-filerestorepreserves the backup's exact bytes. - The full Node 20 suite now passes on Windows, macOS, and Linux in GitHub Actions.
- GitHub Actions on Windows now uses Node's built-in test discovery instead of relying on POSIX shell expansion of
test/*.test.mjs.
A reliability and automation release spanning the full draft lifecycle. It closes the highest-value gaps found in the v0.10.1 repository audit while preserving the zero-runtime-dependency core.
- CapCut 8.7+ draft store — version-aware discovery of
draft_content.json,draft_info.json,draft_meta_info.json, andtemplate-2.tmp, including nested/string JSON envelopes. Every readable timeline target is synchronized on write. diagnose— redacted storage report with canonical-file selection, hashes, timeline divergence, editor-process detection, and--bundle <report.json>output for compatibility reports.- Command contract v2 —
describenow exposes usage, typed positionals/options, defaults/enums, mutability, prerequisites, output form, and exit codes for every command. Help, completions, generated docs, and the typedrunCommand()library API consume the registry. compilev2 — stable item refs, source timing, speed/volume/opacity/transforms, transitions, filters, effects, keyframes, audio fades, templates, SRT captions, text styles/ranges, plus--check/--planvalidation without writes.- Caption adapters + karaoke — explicit OpenAI Whisper, whisper.cpp, and faster-whisper dialects; word-timestamp parsing/grouping;
--karaoke,--max-words,--max-chars, and--max-gap-msgenerate time-varying highlighted caption segments. - Full media probing — ffprobe duration, FPS, display rotation, dimensions, codecs, audio presence/channels, and a path+mtime cache.
add-video,add-audio, andcompilecan infer omitted durations. - Higher-fidelity proxy rendering — optional
--all-video-trackscomposition with transforms/opacity, audio fades, draft caption colour/size/position, explicit skipped-feature reports, and FFmpeg capability detection/fallbacks. - Reliable
serverunner — bounded async workers, per-project serialization, stable job-ID deduplication, retry/backoff, configurable timeout/output limits, and safe capture for outputs larger than 64 KiB. - Cross-platform CI smoke matrix — Node 20 tests on Ubuntu, macOS, and Windows in addition to the existing Node 18/20/22 Linux matrix.
- Conflict-safe atomic persistence — writes are prepared and fsynced before same-directory rename, every synchronized target receives a backup/history snapshot, changed-on-disk drafts are refused, and managed drafts are protected while CapCut/JianYing is running.
--force-writeis the explicit override. - Transactional
batch— all operations validate against cloned state and commit once. Any failure writes nothing by default;--continue-on-errorintentionally commits only successful operations and exits non-zero. doctor— now reports ffprobe and detailed FFmpeg filter/encoder capabilities alongside Whisper and project-directory checks.- Lint gate — warnings now fail
npm run lint; the existing lint debt was removed.
- Large JianYing enum and
serveresults no longer truncate at the macOS 64 KiB synchronous pipe boundary. - Proxy-render tests no longer assume every installed FFmpeg build includes
drawtext; caption burn falls back cleanly when it is absent. - Stale roadmap, version-support, Chinese README, skill reference, test-count, and release metadata claims were synchronized with the shipped surface.
Two commands that close the two biggest gaps in a headless CapCut workflow: seeing the result, and authoring a whole draft in one shot. No breaking changes; still zero npm-dep and JSON-by-default. Both shell out to ffmpeg only when actually rendering, the same opt-in external-binary pattern caption uses for whisper.
render— a low-res ffmpeg proxy preview of a draft, so you can watch an edit without opening CapCut. Flattens the main video track (per-segment source trim + speed), scales to a proxy size (--scale, default 0.5), mixes every audio-track segment, and optionally burns the text segments in with--burn-captions. It is explicitly a preview, not CapCut's final render (no multi-track video compositing, no effects/transitions). The ffmpeg command is built by a pure, deterministicbuildRenderPlanthat is unit-tested without invoking ffmpeg;--dry-runprints that plan instead of executing (and needs no ffmpeg). Read-only — never mutates the draft.compile— builds a whole draft from a declarative JSON spec (the inverse ofdescribe): instead of chaining dozens of mutatingadd-*commands, an agent emits one spec andcompileconstructs the draft atomically via the same proven factory functions the imperative commands use. Times are in seconds (converted to CapCut's microseconds); media paths resolve relative to the spec file. The full spec is validated — and every media file checked to exist — before anything is written, so a bad spec fails clean. Writes bothdraft_content.jsonanddraft_info.jsonso every downstream command reads the same data.
Ten new commands/capabilities across inspection, maintenance, composition, and agent-integration. No breaking changes; still zero-dep, JSON-by-default, pipeable.
describe— emits the full command surface as JSON (name, version, global flags, every command + summary) so LLM/agent callers get a tool spec instead of scraping--help. A test enforces that every command has a summary, so nothing ships undescribed.prune— removes materials no segment references. The referenced set is the union of every segment'smaterial_idandextra_material_refs[], so masks/effects/animations/fades referenced indirectly are never wrongly deleted. Pairs with--dry-run.relink— repairs broken media paths.--dir <folder>repoints each missing material to a same-basename file in the folder;--from <p> --to <q>prefix-replaces paths. Reports relinked / still-missing / present counts. Pairs with--dry-run.timeline— shows the track/segment layout. JSON default returns lanes with computed columns;-Hrenders ASCII bars (--cols N, default 60). Makes layout/track-order issues diagnosable without opening CapCut.projects— lists CapCut/JianYing draft folders on disk (scans the per-OS default dirs or--drafts <dir>), with an optional name-substring filter and--namesto read each draft's title. No more pasting 40-char UUID paths.- Multi-step undo — every write now also keeps a rolling snapshot history under
<draftdir>/.capcut-cli-history/(capped at 20).restore --step Nrolls back N writes (step 1 == the.bak);restore --listshows the history. Plainrestoreis unchanged. diff— compare two drafts: segments added/removed/changed (start/duration/material/speed/volume), and materials added/removed/changed (a text edit mutates the material in place, so this is whereset-textshows up). Read-only.concat— append one draft onto another's timeline: B's segments are time-shifted by A's duration, and any B material/segment id that collides with A is reassigned a fresh uuid (with references rewritten) so the merge stays valid. Writes to--outor in place.config— defaults (draftsdir,jianying,cols) can be set in a.capcutrc(cwd, then home; CLI flags win).capcut configprints the resolved file and effective values.- Windows
export --batch— the Windows path now ships: PowerShell opens each draft and sends CapCut's export shortcut (Ctrl+E). Same experimental UI-automation caveat as macOS. (Live render is host-dependent; the script generation is unit-tested.)
Safety, discoverability, and a long-overdue track-order fix. No breaking changes; everything stays zero-dep, JSON-by-default, and pipeable.
- Global
--dry-run(#15) — any draft-mutating command now honors--dry-run: it computes and prints the normal JSON result with"dryRun":trueadded, but leaves the draft and its.bakuntouched. Gated centrally insaveDraft, so it covers every write command at once.translate/export --batchkeep their existing dry-run behavior. restorecommand (#16) —capcut restore <project>undoes the last write by copying<draft>.bakback over the draft. Single-step (only one backup generation is kept); exits non-zero with a clear message when no.bakexists. Honors--dry-run.- Shell completions (#18, #19, #20) —
capcut completions <bash|zsh|fish>generates a completion script for command names and global flags.
- Track order scrambled on import (#21) — tracks were written in the order edit commands ran, but CapCut lays out the timeline from the tracks-array order, not from per-segment
render_index, so building a draft incrementally produced a jumbled timeline.saveDraftnow normalizes the tracks array to the canonical bottom→top layer order (video → audio → sticker → effect → filter → text) on every save; the sort is stable so same-type tracks keep their authored order. Also exported assortTracksfrom the library entry point.
- README — added a from-source install path and a consolidated Prerequisites note (Node ≥ 18, whisper for
caption,ANTHROPIC_API_KEYfortranslate); a worked-example block for the v0.4/v0.5 commands that had none (mix-mode,audio-fade,add-filter,bubble-text,add-cover,add-sfx,chroma,import-ass);--dry-run/restoreusage; and a Troubleshooting table covering the CapCut-must-be-closed footgun, track-order normalization,.bakrecovery, whisper/API-key setup, and the--fade-outflag. CONTRIBUTING.md— build / test / lint commands, thenpm testpre-commit gate, and PR conventions.
- Pre-commit hook rebuilds
dist/before tests (#23) — the hook rantest:fast(no build step), so it could pass-or-fail against a staledist/. It now runsnpm test, which builds first.
templatescommand (#13) —capcut templateslists the bundled templates (slug + description). JSON by default,-Hfor a table.- Global
--version/-vflag (#12) — print the installed CLI version without a subcommand.
- Independent / non-affiliation disclaimer + trademark notice — README and metadata clarify the project is unofficial and not affiliated with ByteDance; "CapCut" / "JianYing" are used nominatively.
- Pinned Biome to 2.4.15 (#14) and cleared auto-fixable lint debt.
Distribution and integration release. No breaking changes to existing commands; everything stays zero-dep, JSON-by-default, and pipeable.
capcut doctor— environment preflight that inspects the machine, not a draft: Node version (hard requirement, ≥ 18), a whisper binary onPATH(forcaption),ANTHROPIC_API_KEY(fortranslate), and the default per-OS CapCut/JianYing project directory. JSON by default,-Hfor a human checklist. Exits1only on a hard failure.- Importable Node library —
import { loadDraft, saveDraft, findSegment, findMaterial, getTracksByType, extractText, updateTextContent, lintDraft, detectVersion, runDoctor } from "capcut-cli", with types. Newsrc/lib.tsentry point;package.jsonexports/main/typesmap todist/lib.js;tsconfignow emits.d.ts. Importing the package no longer executes the CLI. - Dockerfile +
.dockerignore— zero-dep multi-stage build; the final image is Node +dist/+templates/. Drafts mount at/work. Also runsserveover a stdin pipe. - GitHub Action (
action.yml) — composite action wrappingcapcut lintso drafts can be gated in CI;lintexit code2(errors) fails the job.uses: renezander030/capcut-cli@v0.6. - Three new shipped templates —
caption-pop(bold white center subtitle),lower-third(handle/name attribution),hook-question(large top-of-frame hook). Catalogue grows 3 → 6, all validated by the roundtrip suite. serve-automation.mdexample — JSONL job/result contract and four integration paths (local pipe, n8n Execute Command, cloud builders via webhook→queue-file, Docker).
- GitHub Actions CI — test matrix across Node 18 / 20 / 22 plus a Biome lint job, on every push and pull request.
- Fuzz / injection test suite — 12 malformed
draft_content.jsoninputs (non-JSON, truncated, wrong-shape, prototype-pollution attempts, deep nesting) across six read commands assert graceful failure: no hang, no leaked stack trace, single-line JSON error on stderr. Plus a prototype-pollution non-regression check. - Test suite grew to 113 passing tests (doctor, fuzz, library, and the three new templates added their own coverage).
Six new commands voted in from Discussion #1, shipped as a single release. All keep the zero-dep, JSON-by-default, pipeable design.
capcut mix-mode <project> <segment-id> <mode>— set blend mode on a video segment. Writesmix_modeon the video material (not the segment) since CapCut keys blend modes offmaterials.videos[]. 12 modes:normal,multiply,screen,overlay,soft-light,hard-light,color-dodge,color-burn,darken,lighten,difference,exclusion. Rejects non-video/photo segments.capcut audio-fade <project> <segment-id> [--in <sec>] [--fade-out <sec>]— fade-in / fade-out on an audio segment via a realmaterials.audio_fades[]entry ({id, fade_in_duration, fade_out_duration, fade_type, type:audio_fade}), referenced fromsegment.extra_material_refs. Re-applying replaces the existing fade instead of stacking. Rejects on non-audio segments. (Note:--outcollides with the global output-path flag, so this command uses--fade-out.)capcut add-cover <project> <image-path> [--time <ms>]— set the draft's cover frame (thumbnail) to a local image. Writes a populated object on the draft root'scoverfield (wasnullin every template). Shape includespath,type:image,time,time_ms(both — CapCut versions disagree on the unit), and acustom_cover_iduuid. Validates the image path exists.--timedefaults to 0.capcut add-filter <project> <slug> <start> <duration>+capcut enums --filters— colour-filter track separate fromadd-effect. Samematerials.video_effects[]storage buttype:filterandcategory_name:Filterso CapCut shows it in the filter rail. 10-slug starter catalogue for the CapCut namespace (vintage,warm,cool,bw,sepia,vivid,contrast,faded,dramatic,soft); JianYing namespace delegates to the 468 entries inenums.jsonvia--jianying.capcut bubble-text <project> <text-segment-id> --bubble <slug>+capcut enums --bubbles— speech-bubble shape on a text segment. Writes amaterials.filters[]entry (type:text_shape, matching pyJianYingDraft'sTextBubble.export_json) plus stampsbubble_effect_id/bubble_resource_idon the text material — some CapCut versions read from the material directly, others fromfilters[]. 7-slug starter catalogue (rectangle,rounded,cloud,oval,star,heart,burst) plus--effect-id/--resource-idpassthrough for users with custom ids.capcut import-ass <project> <ass-path-or-->— ASS / SSA subtitle import alongsideimport-srt. Zero-dep parser (src/ass.ts) reads[Events]/Dialoguelines, honours theFormatheader, strips inline overrides ({\\b1\\an8}) and\\Nline breaks. Time formatH:MM:SS.cc(centiseconds → microseconds). Shares the cue-to-segments pipeline withimport-srt— same--track-name,--style-ref,--time-offset, and text-style flag surface.
readFileSync("/dev/stdin", ...)→readFileSync(0, ...)in three call sites (keyframe --batch,import-srt,servequeue). FixesENXIO: no such device or addresswhen the CLI was invoked with a piped stdin viachild_process.spawn. The/dev/stdindevice node fails to open in that mode on Linux; fd-0 always works.capcut initfalls back to a bundled template attemplates/_init/when the upstream../CapCutAPI/templatedirectory isn't present. Previously broke on every machine that didn't have the Python project cloned alongside.
- Test suite grew from 60 → 91 passing tests across 53 suites (six new test files, one per shipped command).
- Husky pre-commit gate stayed green throughout the v0.5 cycle — every feature commit includes its tests and passes before being pushed.
0.3.2 — 2026-05-15
- Workflow diagram (Mermaid) at the top of both READMEs showing how
capcut-clifits into a viral-shorts pipeline (long video → cut → LLM hook → CLI edits → CapCut render → publish). GitHub renders Mermaid natively; no committed image. Mirrored inREADME.zh-CN.mdwith 小红书 / 抖音 / 视频号 labels. - Comparison table vs
pyJianYingDraft(Python, JianYing-only),CapCutAPI(Python + HTTP server), andcutcli(Go, closed-source). Shows the unique positioning: onlycapcut-cliis zero-dep Node + cross-namespace (CapCut + JianYing) + has a shipped schema reference + ships built-in templates. - Feature checklist — categorized list of every shipped command with ✅ / ⬜ / 🚫 status and anchor links to the relevant docs section. 10 categories: Project I/O, Add content, Edit, Decorators, Templates, Import & discovery, Source materials, Cross-platform, Output, Quality, Roadmap. Mirrors the structure that drove
pyJianYingDraftto 3,266 stars.
No code changes; CLI surface is bit-for-bit identical to v0.3.1.
0.3.1 — 2026-05-15
docs/draft-schema/— 7-file reference for the CapCut / JianYing project JSON: overview, tracks-and-segments, materials, keyframes-and-animations, effects-filters-stickers-masks-transitions, CapCut↔JianYing version differences. Practical, field-level, derived from real drafts +pyJianYingDraft. Closes the most-asked question for anyone writing tooling against the format: "what's the JSON shape?"node:testfixture-backed test suite — 36 tests across 5 test files (inspect,edit,create,template,decorators) covering the major CLI surface against the canonicaltest/draft_content.jsonfixture. ~1 second total runtime.- Husky pre-commit hook + Biome lint — every commit runs
lint-staged(Biome check/format on staged files only) followed by the fullnode:testsuite. Cheap (<10s on a clean tree), catches regressions before they hit npm. Skipping with--no-verifyshould be rare. npm run test/test:fast/lint/lint:fix/formatscripts inpackage.json.
- Test runner: shell-based
scripts/_test.sh(which tests skill wrappers) remains, but the canonical CLI test suite is nowtest/*.test.mjsvianode --test. CI-friendly, parallel, cross-platform.
0.3.0 — 2026-05-15
Five phases of new commands ported from the upstream Python project (sun-guannan/VectCutAPI / CapCutAPI), all keeping the original zero-dep, local-only, JSON-by-default, pipeable design. No new runtime, no network beyond the Wikimedia gate, no Python at runtime.
capcut keyframe— add keyframe(s) to a segment forposition_x,position_y,rotation,scale_x,scale_y,uniform_scale,alpha,saturation,contrast,brightness,volume. Single-shot and--batch(JSONL on stdin) modes. Value parsing accepts"50%","+0.5","45deg". Writes tocommon_keyframeson the segment, appends to existing per-property lists, sorted by time offset.capcut transition— attach a transition between segments. Starter catalogue:dissolve,rgb-glitch,radial-blur,horizontal-blur,vertical-blur-ii,twinkle-zoom,urban-glitch,shake-3.--duration <s>override.capcut mask— attach a mask:linear | mirror | circle | rectangle | heart | star. Flags:--center-x,--center-y,--size,--rotation,--feather,--invert,--rect-width,--round-corner.capcut mask <project> <id> --offremoves all masks.capcut bg-blur— background blur level 1–4 (light → maximum, mapping to0.0625 / 0.375 / 0.75 / 1.0).--offto clear.capcut text-style— rich text styling on an existing text segment:--alpha,--vertical,--fixed-width/-height,--shadow(+--shadow-alpha/-angle/-color/-distance/-smoothing),--border-width/-color/-alpha,--bg-color/-alpha/-style/-round-radius/-width/-height/-h-offset/-v-offset.capcut text-anim— text intro/outro animations. Slugs:fade-in,fade-out,typewriter,pop-up,throw-out,blur-text-in,zoom-in-text. Per-side duration overrides.
capcut add-sticker— create a sticker track + segment from a CapCut resource id, with--x/-y/-scale/-rotation/-track-nametransforms.capcut add-effect— scene/character effect on its own effect track. Starter catalogue (CapCut namespace):shake,vhs,cinematic,light-leak,film-grain,chromatic,vignette.--params <json-array>of 0–100 effect parameters.capcut image-anim— intro/outro/combo animations on video / image segments. Slugs:fade-in,flash-in,pulsing-zooms,scroll-up,stripe-merge,zoom-out,fade-out,blur-out,smoke.
capcut import-srt— parse an SRT file and create one text segment per cue. Accepts a file path or-for stdin. Flags:--track-name,--time-offset <s>,--style-ref <segment-id>(copy styling from an existing text segment), plus explicit text-style flags. Zero-dep parser; singlesaveDraftfor the whole file (fast on hundreds of cues).capcut enums— list valid enum values for AI agents:--transitions,--masks,--text-intros/-outros/-loop-anims,--image-intros/-outros/-combos,--scene-effects,--character-effects,--audio-effects,--fonts. Output is JSON by default (slug,member,name, effect/resource ids, md5, durations) or a human-readable table with-H. Reads from a committedenums.jsonextracted frompyJianYingDraft(13 categories × 2 namespaces, ~790 KB).
capcut text-ranges— multi-style text. Different styling per character range in a single text segment.--styles @path.jsonor inline JSON:[{"start":0,"end":5,"font_color":"#FFD700","font_size":18,"bold":true},…]. Sorts + validates non-overlap, emits baseline-style fillers for gaps so CapCut renders the whole text. Unlocks word-level highlight captions.--jianyingglobal flag — threaded throughtransition,mask,text-anim,image-anim,add-effect, andenums. Selects the JianYing enum namespace (default is CapCut). Lookup falls back tomembername, socapcut transition <project> <id> "_3D空间" --jianyingworks.
add-video/add-audioaccept Wikimedia URLs —commons.wikimedia.org,*.wikipedia.org,upload.wikimedia.orgpage URLs and direct CDN URLs all resolve through the Commons imageinfo API to a canonicalFile:title.- License classifier + refusal gate —
permissive(CC*, PD, CC0, etc.),fair-use,restrictive(NC, ND, ©),unknown. Restrictive/unknown require--force-license. Fair-use downloads with a warning. Output JSON carries awikimediablock withartist,credit,description_url, license raw + class, dimensions, mime — drop-in attribution for YouTube descriptions. - Single on-disk copy — assets download directly into
<draft>/assets/<kind>/. No temp-dir churn;addVideo/addAudiocopyFileSyncbecomes a no-op.
- Ready-made templates ship in
templates/:gold-title.json,end-card.json,subscribe-cta.json. Use directly viacapcut apply-template ./project ./node_modules/capcut-cli/templates/<name>.json <start> <duration>. .github/FUNDING.yml— enables GitHub Sponsors + Gumroad links on the repo sidebar.--helpfooter — everycapcut --helpnow ends with links to the full viral-shorts pipeline (Gumroad / Stripe), guides, Sponsors, and contact.
skills/capcut-edit/reorganised intoreferences/+scripts/+assets/.SKILL.mdtrimmed;references/api-reference.mdcovers every command and flag;references/workflows.mddocuments whichscripts/*.shto call (not how to reconstruct them);references/pitfalls.mdcovers the gotchas (close-project-first,.bak,clip=nullon audio, etc.).- Wrapper scripts:
fade-in.sh,fade-out.sh,anim.sh,ken-burns.sh,long-to-short.sh,stamp-cta.sh. All covered byscripts/_test.sh(7/7 passing).
npm run buildnow doestsc && cp src/enums.json dist/enums.jsonso the runtime reads the dist copy viaimport.meta.url.npm run extract-enumsregeneratessrc/enums.jsonfrompyJianYingDraft.
- All five phases keep capcut-cli zero-dep at runtime — no Python, no FFmpeg, no network beyond the explicit Wikimedia opt-in (which is
fetch-based, no external deps). - HTTP server, MCP server, ffprobe-based duration probing, FFmpeg letterboxing, and cloud rendering remain explicitly out of scope per
PLAN.md.
0.2.2 — 2026-04-26
- README CTAs to Viral Story Shorts Blueprint (Gumroad).
0.2.1 — 2026-04-26
- npm tarball now includes
examples/and Chinese README.
0.2.0 — 2026-04-26
- Long-form videos to shorts, end to end.
