Repository navigation
Fix: portable skill scripts + two read-path bugs found on a JianYing 9.0.0 host - #149
Open
Phelim2023315 wants to merge 3 commits into
Open
Phelim2023315 wants to merge 3 commits into
Phelim2023315 wants to merge 3 commits into
Conversation
added 3 commits
October 12, 2026 17:22
The four skill scripts assumed they lived inside a capcut-cli checkout: - `anim.sh` hardcoded the macOS effect-cache container path. On Windows that string is written into the animation material's `path`, where the app cannot resolve it, so the animation has no effect cache. Resolve the base per platform instead — `%LOCALAPPDATA%\CapCut\User Data\Cache\effect` (JianYing: `%LOCALAPPDATA%\JianyingPro\...`) on win32, the container on darwin, `~/.local/share/CapCut/Cache/effect` elsewhere — with `CAPCUT_EFFECT_CACHE` as an override. - `ken-burns.sh`, `stamp-cta.sh` and `long-to-short.sh` computed the CLI entry as `$script_dir/../../../dist/index.js`. That is only correct inside a checkout: a skill installed as a plugin (or copied anywhere else) failed with "capcut CLI not found". They now resolve by priority `$CAPCUT_CLI` → the repo build → an installed `node_modules/capcut-cli/dist/index.js` (walking up from the script) → `capcut-cli`/`capcut` on `PATH`, keeping the repo-relative build as the fallback so existing checkouts behave exactly as before. Tests cover the resolution order (a stub CLI proves `$CAPCUT_CLI` wins, and that a missing `$CAPCUT_CLI` falls through to the repo build) and the effect-cache base (`CAPCUT_EFFECT_CACHE` is honoured; on win32 the written path is a Windows cache base, never the macOS container). SKILL.md documents the resolution order, both environment variables and the JianYing Windows draft store.
`doctor` probed three dialect names — `whisper`, `whisper-cli`, `faster-whisper` — but `caption` only ever spawned `whisper`, and when the command was not given explicitly it also assumed the openai dialect. Reproduced on a Windows host whose only Whisper is whisper.cpp's `whisper-cli.exe` (the name that build has shipped since v1.7.4, and the one Homebrew's whisper-cpp installs): `capcut doctor` reported `[✓] whisper`, and the next `caption` run died with ENOENT — with a hint suggesting --whisper-engine openai|whisper-cpp|faster-whisper, i.e. leaving the reader to work out that the default command name was the problem. Both now share `resolveWhisperCommand()` in the new `src/whisper.ts`, which also owns the PATH lookup `doctor` used to define privately. An explicit `--whisper-cmd` and an explicit `--whisper-engine` still win over detection. Dialect detection reads the resolved command, so a resolved `whisper-cli` selects the whisper-cpp invocation instead of the openai one — the second half of the same bug. The resolver takes an injectable PATH probe, so the test asserts the preference order, the whisper.cpp-only case, the faster-whisper fallback and the nothing-installed case without depending on what the test host has installed, and pins that `--whisper-cmd` still wins for dialect detection.
…peed
`TargetSegment.start` and `.duration` are typed as required, so `segmentData`
read them directly and `formatTime(undefined)` rendered `NaN`. Measured on a
JianYing 9.0.0 (Windows) project that the app opens and edits normally, where
`capcut segments <project> -H` printed:
62a041c7 text NaN:00NaN-NaN:00NaN 3.00s undefinedx smoke-test
for a segment whose `target_timerange` is `{"duration":3000000}` and which has
no `speed` — the app drops the 0 default, and `speed` is absent from segments
of that shape. The same `undefined` also reached the JSON result as
`"start_us": null`.
Both fields now fall back to their 0 / 1 defaults in `segmentData`, which is
the single source for the `segments` table, the JSON result and the text-only
table. The fix is deliberately on the read side: the two writers reachable in
isolation were checked and both write `start` — `add-text` at `start=0` writes
`{"start":0,...}`, and `init` seeds an empty draft — so nothing here guesses at
a writer that omitted it.
The test drives the fixture through `spawnCli`, deleting `start` and `speed`
first, and asserts the human table contains no `NaN`/`undefined` while the JSON
result reports the defaults.
This was referenced Oct 13, 2026
Owner
|
Thanks for putting this batch together! The Whisper and timing fixes look good. Before merge, please adjust the PATH fallback: |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Three commits, all from using the CLI on a Windows host where the installed app
is 剪映专业版 (JianYing) 9.0.0:
fix(skills): make the recipe scripts work outside a repo checkoutskills/capcut-edit/scripts/*(2 changes)fix(caption): resolve the Whisper binary the same way doctor doessrc/whisper.ts,src/caption.ts,src/doctor.tsfix(segments): tolerate a segment without target_timerange.start or speedsrc/index.tsThe first commit generalises what a Windows host had to patch locally to use the
skill at all; the other two are bugs the same host surfaced. No behaviour changes
on a normal checkout, no new runtime dependency.
1.
anim.shhardcoded the macOS effect-cache pathOn Windows that string is written into the animation material's
pathfield, sothe effect it references has no cache the app can resolve. Resolved per platform
instead:
%LOCALAPPDATA%\CapCut\User Data\Cache\effect(falls back to…\JianyingPro\…, preferring whichever exists)~/.local/share/CapCut/Cache/effectCAPCUT_EFFECT_CACHEoverrides all three, which also makes the value testable.2. The three recipe scripts computed the CLI entry from the repo layout
cli="$script_dir/../../../dist/index.js"That is only correct inside a checkout. Used as an installed plugin — or copied
anywhere else, which is how a skill actually reaches a user — all three fail with
capcut CLI not found. They now resolve in this order:$CAPCUT_CLI(explicit path, for wrappers and CI)$script_dir/../../../dist/index.js) — unchanged, and stillthe fallback, so a plain checkout behaves exactly as before
node_modules/capcut-cli/dist/index.js, walking up fromthe script
capcut-cli/capcutonPATHThe error message now names the ways to satisfy it instead of only the repo-relative
path.
Testing
New coverage, all driven through the existing helpers:
test/skill-cli-resolution.test.mjs— a stub CLI proves$CAPCUT_CLIispreferred; a non-existent
$CAPCUT_CLIfalls through to the repo build (thereal CLI then writes the keyframes); and with no override at all the
repo-relative path keeps working.
test/anim-effect-cache.test.mjs—CAPCUT_EFFECT_CACHEis honoured and thewritten
pathis<cache>/<effect_id>/<md5>; on win32 the written path is aWindows cache base and never the macOS container.
test/whisper-resolution.test.mjs— the resolver takes an injectable PATHprobe, so preference order, the whisper.cpp-only case, the faster-whisper
fallback and the nothing-installed case are asserted without depending on the
test host;
--whisper-cmdstill wins for dialect detection.test/segment-table-missing-fields.test.mjs— the fixture withstartandspeeddeleted renders noNaN/undefinedin-Hand reports the0/1defaults in JSON.
Targeted run of the touched areas (4 new files +
keyframe-easing,doctor,caption): 49/49 pass.Commit 2 —
captionanddoctordisagreed about Whisperdoctorprobedwhisper/whisper-cli/faster-whisper;captiononly everspawned
whisper, and defaulted its dialect to openai. On a host whose onlyWhisper is whisper.cpp's
whisper-cli.exe(the name that build has shipped sincev1.7.4),
doctorprinted[✓] whisperand the nextcaptionrun died withENOENT, hinting at
--whisper-enginerather than at the command name.Both now share
resolveWhisperCommand()in a newsrc/whisper.ts, which alsoowns the PATH lookup
doctorused to define privately. Dialect detection readsthe resolved command, so a resolved
whisper-cliselects the whisper-cppinvocation — the second half of the same bug.
--whisper-cmdand--whisper-enginestill win.Closes #146.
Commit 3 —
segmentsprintedNaN:00NaNfor an app-shaped segmentsegmentDatareadtarget_timerange.start/.durationandspeeddirectlyoff a type that declares them required, so a segment that omits
start(its 0default) and
speedrendered as:Measured on a JianYing 9.0.0 Windows project (the capture is now
test/fixtures/jianying-9-0-0-windows/). The fix is on the read sidedeliberately: both writers reachable in isolation write
start—add-textatstart=0writes{"start":0,...}, andinitseeds an empty draft — so nothinghere guesses at a writer that omitted it.
start_us,duration_usandspeedfall back to
0,0,1.Closes #147.
Deliberately not included
Windows also found that
--force-writehas to be passed on hosts whoseJianYing/Premiere app declares a version inside the CLI's refuse band. That is a
per-host safety decision, and the repo's own invariant is "never pass
--force-writeunless the user explicitly accepts overwriting changed-on-diskstate" — changing the scripts' default would trade away the guard for everyone.
It stays a local policy (
CAPCUT_FORCE_WRITE=0to restore), so nothing heretouches the default.
Environment note
CHANGELOG.mdgained entries under## [Unreleased]; nopackage.jsonchanges,no new runtime dependency.