Releases: PBNZ/watch-local
Release list
v0.7.2 -- documentation corrections
Documentation-correction release. No behaviour changes — but two
explanations published in 0.7.1 turned out to be wrong, both in the
direction of sounding more certain than the evidence supported.
"The workstation runs the CUDA build forced onto CPU" — refuted
0.7.1 offered this as the leading explanation for a 16-core workstation
trailing a 6-core laptop, and told readers not to treat -Device cpu on
a GPU box as representative of a CPU-only install. A second, genuinely
CPU-only runtime was provisioned alongside the GPU one to test it:
- There is no separate CPU build. PyPI ships one
ctranslate2wheel
with CUDA compiled in, and all four native binaries are
byte-identical across the two installs (sha256). - A CPU run never loads the NVIDIA libraries.
cuda_pathsputs them
on the DLL search path even for-Device cpu, but after a real int8
decodecublas64_12,cublasLt64_12,cudnn_ops64_9,
cudnn_graph64_9andnvrtc64_120_0are all unloaded. - The two stacks measure the same. Pinned to identical package
versions so they differed by exactly the threenvidia-*wheels, four
of five models produced identical segment counts and identical WER to
the decimal.
-Device cpu on a GPU machine is a faithful CPU-only measurement, so
contributors do not need GPU-free hardware to submit CPU numbers.
large-v3's poor WER had the wrong cause
0.7.1 explained it as the model "transcribing more literally — ~5% more
words than the captions contain". Those surplus words are repeated text
from decodes that got stuck: the runs that produced them tripped
watch-local's own repetition detector 1–5 times each.
On the reference fixture large-v3 degenerated in 8 of 8 GPU runs and
2 of 3 CPU runs. No other model has tripped the detector once. The
single clean run emitted 5,713 words against a 5,778-word reference —
fewer than the captions — and scored 5.9% rather than 13–17%.
Root cause is upstream and verified in faster_whisper/transcribe.py:
the default temperature ladder retries a segment judged "too repetitive"
(compression_ratio_threshold=2.4), and above temperature 0 decoding
switches from beam search to stochastic sampling — so identical audio
legitimately yields different transcripts, segment counts and runtimes.
large-v3 figures are now published as ranges with run counts attached.
tiny through medium reproduce to within contention noise and are
unchanged.
Also fixed while re-checking every number against the raw data
- "the laptop is roughly 2x faster than the i9" was stale — that
ratio came from the 16-thread sweep 0.7.1 withdrew. The default-thread
tables give 1.30–1.58x, and the page contradicted itself twice. - "
large-v3on GPU varied 16% between two identical runs" predated the
six-run repeat table added here. Eight GPU runs now span 255–414 s —
the slowest 62% slower than the fastest. - The order-reversed venv pairing now reports all three pairs,
including the contaminatedsmallpair that moved against the
conclusion. Omitting it would have been the same failure this release
is retracting. /watch's runtime CPU warning still carried the old 57–75 min range;
it now says 49–75 min and attributes the spread to run-to-run decode
instability rather than to the two machines measured.- The
large-v3row of the thread-count table now carries a warning: its
+28% is two single degenerate draws, and two runs at a fixed thread
count already span 51%.
Full detail: docs/benchmarks.md
🤖 Generated with Claude Code
watch-local 0.7.1
watch-local 0.7.1
Correction release. Re-running the benchmarks properly — all five models, CPU and GPU, on the one reference video every published table is supposed to use — overturned a performance claim from 0.7.0 and uncovered a caption-parsing bug that had been quietly skewing /watch's own transcript comparison.
Caption HTML entities were never decoded
parse_vtt stripped cue tags but left entities encoded. Creator captions pad line ends with , so every word tokenizer downstream read nbsp as a real word — 992 of 6,770 reference words on the reference video, more frequent than "the" (342).
That inflated benchmark WER (small scored 19.7% instead of 6.1%) and, more importantly, depressed /watch's own creator-vs-Whisper length_ratio / word_jaccard / trigram_jaccard — which can push a perfectly good transcript pair into the report's "major divergence" callout. Entities are now unescaped after tag-stripping (so a decoded < can't be re-read as markup), U+00A0 is folded to a space, and whitespace collapsed.
0.7.0's CPU thread auto-sizing made things 15–47% slower — withdrawn
0.7.0 sized unpinned CPU runs to the physical core count and published it as "~24% faster". A five-model A/B on the reference video — same machine, same audio, only the thread count changed — says the opposite:
| Model | 4 threads (restored default) | 16 threads (0.7.0) | Cost |
|---|---|---|---|
tiny |
121.2 s | 178.5 s | +47% |
base |
203.8 s | 280.5 s | +38% |
small |
558.7 s | 719.1 s | +29% |
medium |
1,544.2 s | 1,781.7 s | +15% |
large-v3 |
4,479.5 s | 5,739.1 s | +28% |
Mean cores actually used was ~3 in both columns. Asking for 16 didn't deliver 16 — it delivered the same ~3 plus the overhead of coordinating threads with nothing to do, because Whisper's autoregressive decoder doesn't parallelise. The idle cores #34 reported are idle because they cannot be used, not because of a bad setting.
The original "+24%" sweep has never reproduced — not on the reference video, not on the original clip, not after a 15-minute idle period — and its companion core-count figures predate the process-tree recycled-PID guard. An unpinned CPU run now uses faster-whisper's own setting again. W_CPU_THREADS, setup.ps1 -SetCpuThreads, and benchmark.ps1 -CpuThreads all remain for anyone whose hardware disagrees.
If you want CPU transcription to finish sooner, pick a smaller model. That lever is worth 37×; the thread count is worth nothing reliable.
Published benchmarks now use one video
Every table in docs/benchmarks.md is measured on the same clip, so they can finally be compared: GPU and CPU on an i9-12900K + RTX PRO 5000, alongside the contributed 6-core laptop. The fixture URL is now spelled out in the script help and the docs, not just buried in a parameter default.
A finding worth the read: the 6-core laptop beats the 16-core workstation by ~1.5× on identical audio. Do not size a CPU transcription box by core count.
Added
benchmark.ps1 -SkipWarm— skip the untimed warm pass, halving a run, when every model is already cached. A CPU sweep includinglarge-v3otherwise spends an hour just warming.
Install
/plugin marketplace add PBNZ/watch-local
/plugin install watch-local@watch-local
or use the attached zip for offline/pinned installs.
watch-local 0.7.0
watch-local 0.7.0
Performance and measurement release: CPU transcription now uses the whole machine, 2× hallucination loops no longer slip through unflagged, and there is a benchmark harness so none of this has to be taken on faith.
CPU transcription used ~4 threads on any machine (#34)
faster-whisper's cpu_threads=0 default resolves inside ctranslate2 to "OMP_NUM_THREADS or 4" — a constant, not a function of the host — so a 12-thread laptop transcribed on 4 threads and left the rest idle.
Unpinned CPU runs now size themselves to the machine's physical core count, which measurement puts on the plateau. On a 16C/24T box, same clip and model:
cpu_threads |
Time | vs the old default |
|---|---|---|
| 0 (= the old default, 4) | 169.2 s | — |
| 8 | 127.0 s | +25% |
| 16 (physical cores — the new default) | 128.8 s | +24% |
| 24 (every logical CPU) | 151.7 s | +10% |
Note the last row: piling onto SMT siblings gives most of the win back, so "use everything" is nearly as bad as using four. Physical cores also lands correctly on non-SMT machines like Apple Silicon. The count is capped by process affinity, so taskset and cgroup limits still hold. Pin it yourself with setup.ps1 -SetCpuThreads N, or -UnsetCpuThreads for auto.
2× repetition loops passed through silently (#35)
The collapser only acted on runs of 3 or more, so a doubled hallucination was neither collapsed nor recorded — the report showed no "unreliable span" caveat.
A doubled segment is now caught when its text could not physically have been spoken in the time it claims. Two details matter: the rate is judged on the fastest occurrence rather than the run average (the usual loop is a normally-paced segment followed by a near-instant copy, which an average hides), and the ceiling sits above the fastest documented human speech, so rapped or chanted repeats survive while a stuck decode overshoots it tenfold. Replayed over 8,477 segments of real transcripts it fires 4 times — every one an unmistakable loop.
A benchmark harness (#37)
scripts/benchmark.ps1 measures per-model wall time, speed vs real-time, mean core usage, peak RSS, and WER against captions, emitting report.md + results.json + results.csv.
benchmark.ps1 -Slug last -Models small,medium # reuse a watched video
benchmark.ps1 -Slug last -Device cpu -CpuThreads 0,8,16 # thread scalingResource sampling is per-process and cross-platform. Two things it gets right that are easy to get wrong: it walks the process tree (on Windows the uv venv's python.exe is a trampoline whose child does all the work, so measuring the launched PID reports ~5 MB and ~0 CPU), and it ignores processes older than the run so a recycled PID cannot fold a stranger's CPU into your numbers.
Every report also states its caption provenance — WER against human-edited creator captions is a quality signal, while WER against the platform's own auto-captions only measures drift between two recognisers.
Published benchmarks and per-model guidance (#36)
docs/benchmarks.md publishes CPU reference numbers (contributed, with hardware and caveats attached) alongside GPU numbers, and docs/benchmarking.md documents the method. The headline for GPU-less machines: large-v3 ran 57 minutes for a 33-minute video and gained nothing over medium on clean English. /watch-setup and /watch's warning now cite those measured times instead of a vague "slow".
Install
/plugin marketplace add PBNZ/watch-local
/plugin install watch-local@watch-local
or use the attached zip for offline/pinned installs.
watch-local 0.6.2
watch-local 0.6.2
Patch release: the SessionStart hook no longer requires a system Node (#39).
Native Claude Code installs ship as a self-contained binary that bundles its own runtime and never puts node on PATH, so the hook's node check-setup.mjs invocation failed with exit 127 on every session start — silently killing both the "run /watch-setup first" nudge and the Linux/macOS missing-pwsh pre-flight. The hook is now a POSIX sh script invoked with sh, which is present wherever Claude Code can run hooks at all: /bin/sh on Linux/macOS, Git Bash on Windows (a hard requirement of the Windows install). Behavior is unchanged; only the interpreter is. A new .gitattributes pins *.sh to LF so the script cannot check out with CRLF line endings.
See CHANGELOG.md for details.
Install
/plugin marketplace add PBNZ/watch-local
/plugin install watch-local@watch-local
or use the attached zip for offline/pinned installs.
watch-local 0.6.1
watch-local 0.6.1
Patch release so the distribution zip matches main exactly.
Get-PartialSHA256now disposes its SHA256 hasher (#28) — the project's first community contribution, by @Dodothereal (PR #30). Thank you!- LICENSE copyright holder line corrected to
PBNZ.
No functional changes beyond the above; see the 0.6.0 notes for the full hardening release this builds on, and CHANGELOG.md for details.
Install
/plugin marketplace add PBNZ/watch-local
/plugin install watch-local@watch-local
or use the attached zip for offline/pinned installs.
watch-local 0.6.0
watch-local 0.6.0
Pre-launch hardening release: every finding from the July 2026 public review sweep (issues #2–#27) is addressed, plus community contributions (#28 via PR #30, thanks @Dodothereal; #25 groundwork nudged by PR #29, thanks @hansi2007).
Security
- Prompt-injection hardening for the
/watchreport (#2): video-derived fields (title, uploader, transcripts) are neutralized and explicitly framed as untrusted data; SKILL.md instructs the agent to never follow instructions found in video content. Residual risk documented in SECURITY.md. - Scope-guard fixes (#8, #9): case-correct containment on Linux/macOS and working symlink/junction resolution on both PowerShell engines (fail-closed on unresolvable links).
- Hardened CI: least-privilege workflow permissions + SHA-pinned actions (#23); the skill-safety scanner now scans every markdown file, detects homoglyph evasion, and uses a URL prefix allowlist (#22).
- sha256-pin trust boundary documented precisely (#26); LICENSE retains the upstream author's copyright notice (#21).
Reliability
- Staged UNC copies are reclaimed on every exit path; staging free-space check runs before the copy; new
setup.ps1 -PurgeStaging(#5, #15). - Disk pre-flight measures the correct volume on Linux/macOS (#3).
- save-here writes source-link.txt on the primary path, reports truthfully, tolerates old schemas, and honors the documented remove-canonical prompt (#4, #12, #20).
- Merge-failed downloads are a hard error instead of a silent "no audio track" (#10);
-Fpsand-Screenshotsinput validation (#13, #14); CUDA probe survives quotes in install paths (#11); Rosetta 2 preflight on Apple Silicon (#19); model-name validation in onboarding (#16, #17); cross-platform build-zip (#18).
Testing
- Confirmed destructive-purge deletion paths covered end to end (#7); Pester discovery errors now fail CI (#6); download hash-mismatch gate tested (#24); new weekly integration workflow; full suite verified on PowerShell 5.1 + 7, plus integration and a real
/watchsmoke run.
Full details in CHANGELOG.md.
Install
/plugin marketplace add PBNZ/watch-local
/plugin install watch-local@watch-local
or use the attached zip for offline/pinned installs.
watch-local 0.5.0
watch-local 0.5.0
First stable release of watch-local — a fully local Claude Code plugin for
/watch-ing videos (URL, local file, or SMB/UNC share). Downloads with yt-dlp,
extracts frames with ffmpeg, and always transcribes locally with
faster-whisper — NVIDIA GPU (NVDEC + CUDA) when detected, CPU otherwise. No
Docker, no admin rights, no cloud API keys.
Promoted from 0.5.0-rc.2 with no functional changes. The 0.5.0 line removed
Docker in favour of a self-provisioned portable runtime, added GPU
auto-detection, and fixed non-interactive onboarding — see
CHANGELOG.md
for the full list.
Install
From GitHub (easiest):
/plugin marketplace add PBNZ/watch-local
/plugin install watch-local@watch-local
From the zip (offline / pinned version): download
watch-local-marketplace-0.5.0.zip below, extract it into a stable folder, then
/plugin marketplace add that folder. Full steps are in the
README.
Then run /watch-setup once to provision the portable runtime and warm the
whisper model.
watch-local 0.5.0-rc.2
Bug-fix release on 0.5.0-rc.1, remediating a first-run report from a CPU-only work laptop.
Fixed
/watch-setup -Yes(non-interactive onboarding) failed at Step 1 on every machine. The wizard's child-spawn helper returned the child's stdout AND its exit code on one pipeline;setup.ps1 -DetectGpualways prints a JSON gpu block, so the "exit code" arrived as an array, the-ne 0guard element-filtered it to a truthy collection, and the wizard aborted (with a garbledexit { ...json... } 0message) before writing the setup marker -- even when detection succeeded, on GPU and CPU machines alike. Child spawns now go through_lib.ps1's newInvoke-WLChild, which sends child stdout to the host and returns only a scalar[int]exit code (Pester regression tests added).- Runtime provisioning died with "
Get-FileHashis not recognized" underpowershell.exeon hosts with a pollutedPSModulePath(PS7 module dirs listed on the 5.1 path shadow the built-in Utility module with the incompatible PS7 copy). Download verification now hashes via .NET (Get-WLFileSHA256, no module dependency), andGet-WLPSEnginefalls back topwshfor child spawns when 5.1 cannot resolveGet-FileHash/Expand-Archive. - Silenced the harmless-but-alarming huggingface_hub cache-symlink warning (Windows without Developer Mode) via
HF_HUB_DISABLE_SYMLINKS_WARNING=1in the whisper worker env.
Changed
- All documented launcher invocations (SKILL.md, command docs, smoke harness) now pass
-NoProfile-- user profiles printed noise and started background tasks inside captured setup output. New SKILL.md troubleshooting entry covers the polluted-PSModulePathfailure mode andpwsh -NoProfileas a supported engine on Windows.
Full changelog: v0.5.0-rc.1...v0.5.0-rc.2
watch-local 0.5.0-rc.1
BREAKING: Docker removed. The plugin now provisions its own fully portable runtime -- nothing to install on the host, and deleting one folder removes every trace. Built for machines that cannot run Docker Desktop (work-laptop licensing), and it turned out simpler AND faster.
Highlights
- Self-provisioned portable runtime.
/watch-setupdownloads pinned, sha256-verified standalone tools (yt-dlp, ffmpeg, deno, uv) plus a uv-managed CPython venv with faster-whisper into%LOCALAPPDATA%\watch-local\runtime\(XDG equivalent on Linux/macOS). No admin rights, nothing on system PATH, no registry entries. Uninstall = remove the plugin + delete that one folder. - Native GPU whisper via pip CUDA wheels (
nvidia-cublas-cu12+nvidia-cudnn-cu12). Verified on Blackwell sm_120: ~26% faster than the Docker path on the same audio (73.5s vs 99.2s wall, 97.4% word overlap, large-v3). NVDEC decode unchanged, now through a portable static ffmpeg. - Native GPU detection (host
nvidia-smi+ real NVDEC decode probe) with a new venv-levelcuda_whispersignal -- a GPU whose CUDA libraries fail to load degrades to cpu/int8 instead of failing the run. Docker-era configs migrate automatically on their first/watch. - Clear PowerShell 7 guidance on Linux/macOS. The SessionStart hook is now a tiny Node script that tells you exactly how to install
pwshwhen it is missing (it is not preinstalled there) -- the sole non-Windows prerequisite. - grab-frames now works on UNC-source jobs (native ffmpeg reads
\\server\sharedirectly). - Runtime maintenance:
setup.ps1 -UpdateYtDlp(self-update for YouTube breakage) andsetup.ps1 -UpdateRuntime(re-converge to the pinned manifest). - Test matrix hardened: suite runs green under Windows PowerShell 5.1, pwsh-on-Windows, and pwsh-on-Linux; CI covers all three legs; new
tests/linux/container harness performed a real cold-install of the linux_x64 runtime plus integration and a live/watchsmoke.
Full details in CHANGELOG.md.
Verified end-to-end on Windows 11 + RTX PRO 5000 Blackwell (GPU whisper + NVDEC + real /watch runs on URL/local/UNC sources) and on Ubuntu in a container (cold-install provisioning, CPU whisper, live smoke). 101 pytest + 62 Pester green on all three engine legs; strict plugin validation + safety scans pass. Still pre-release: macOS and locked-down work laptops have not yet had real-world runs.
watch-local 0.4.0-rc.1
GPU auto-detection, NVDEC-accelerated decode, and a fully working CPU-only mode (including non-Windows hosts).
Highlights
- Reliable GPU detection, persisted in config. Setup (and the new
setup.ps1 -DetectGpu) probes through docker itself:nvidia-smifor identity (name, VRAM, driver, compute cap) plus a real NVDEC decode test (h264_cuvidon a generated clip). Pre-upgrade installs migrate automatically on their first/watch. - NVDEC video decode for frame extraction. With a detected NVDEC-capable GPU, the tools/screenshots/grab-frames containers run with GPU access and ffmpeg decodes on the GPU (
-hwaccel cuda, decode-only offload). This removes the multi-core CPU spike during frame extraction on long videos. - CPU-only mode -- the plugin now fully works without an NVIDIA GPU. New slim
watch-local/whisper:cpuimage (~1 GB instead of ~8.7 GB); whisper runsdevice=cpu/compute_type=int8. Missing GPU is no longer a fatal setup error. - Non-Windows hosts (CPU mode). Launcher scripts run under PowerShell 7 on Linux/macOS: XDG-based default dirs, engine-aware child processes, cross-platform hook. Newer and less battle-tested than the Windows path.
- Report transparency. Every report carries a
**Compute:**line; the**Whisper:**line states its device. - Repo: adopted the RepoKit living-docs pattern (docs/STATE.json + rendered state blocks + CI check).
Full details in CHANGELOG.md.
Verified end-to-end on Windows 11 + RTX PRO 5000 Blackwell (GPU mode incl. NVDEC probe + real /watch run) and via the CPU whisper container (cpu/int8). 101 pytest + 53 Pester green; strict plugin validation + safety scans pass.