Configuration

August 26, 2026 · View on GitHub

Every knob in Watch Skill, in one place. There are three layers:

  1. Environment variables / .env — every field of the typed settings object (src/watch_skill/config.py) is settable as an environment variable with the WATCHSKILL_ prefix, or as a line in a .env file in the directory the process starts from.
  2. CLI flags — per-invocation overrides on watch-skill subcommands.
  3. Defaults — sensible for an 8 GB-RAM machine with no GPU and no API keys.

Precedence (highest wins): CLI flag > process environment > .env in the current working directory > default.

Example — three ways to set the extracted frame width to 1024 px:

watch-skill watch video.mp4 --resolution 1024        # this run only
WATCHSKILL_FRAME_WIDTH=1024 watch-skill watch video.mp4
echo "WATCHSKILL_FRAME_WIDTH=1024" >> .env           # every run from this directory

Secrets (SecretStr fields) are never logged and never appear in error payloads.

Environment variables

Storage

VariableTypeDefaultEffect
WATCHSKILL_DATA_DIRpath~/.watch-skillRoot for the cache, index, frames, loops, lessons, health log, and managed binaries. A pre-rename ~/.agentvision/ dir is migrated here automatically once (only when this is left at its default).
WATCHSKILL_BIN_DIRpath<data_dir>/binWhere managed portable binaries (ffmpeg, yt-dlp, deno) are bootstrapped. Binaries here are preferred over PATH so a self-healing update controls what actually runs.
WATCHSKILL_CACHE_MAX_BYTESint21474836480 (20 GiB)Download-cache size cap; least-recently-used entries are evicted beyond it.

Derived paths (not directly settable — they follow data_dir): <data_dir>/cache (downloads), <data_dir>/index.db (SQLite index), <data_dir>/frames (kept frames), <data_dir>/loops (loop iterations), <data_dir>/lessons.db (lessons store), <data_dir>/evals (exported eval cases), <data_dir>/health.jsonl (incident log), <data_dir>/models/ocr (per-script OCR models).

Perception

VariableTypeDefaultEffect
WATCHSKILL_MAX_VIDEO_HEIGHTint720Ceiling for downloaded video height, 144–1080. 720p answers almost every question a video can answer; 4K costs minutes of transfer and gigabytes of disk to answer it no better. Every rung of the format selector carries this cap and an audio stream — a video-only fallback surfaces later as a mysteriously empty transcript. A source that yt-dlp confirms has no audio stream is downloaded video-only and reports audio_status: audio_unavailable; an unanswerable probe reports audio_unknown and does not authorise that path.
WATCHSKILL_FRAME_WIDTHint512Extracted frame width in pixels. Higher = sharper OCR, more tokens per frame if sent to a vision model.
WATCHSKILL_FRAME_CAPint100Hard cap on frames per analysis, regardless of duration.
WATCHSKILL_MAX_FPSfloat2.0Universal sampling-rate ceiling; even focused mode never samples denser than this.
WATCHSKILL_PHASH_DISTANCEint6Max Hamming distance between perceptual hashes for two frames to count as near-duplicates (and be deduplicated). Lower = keep more similar frames.
WATCHSKILL_OCR_ENABLEDbooltrueRun OCR on kept frames. Per-script recognition models (Arabic, Cyrillic, Korean, …) auto-download on first use.

Transcription

VariableTypeDefaultEffect
WATCHSKILL_SUBTITLE_LANGSstren.*yt-dlp --sub-langs pattern for platform captions. The video's original-language track is fetched and preferred over auto-translations regardless of this pattern.
WATCHSKILL_LOCAL_WHISPER_ENABLEDbooltrueUse local faster-whisper as the fallback when a video has no captions.
WATCHSKILL_WHISPER_MODELstrautofaster-whisper model size (tinylarge-v3). auto picks by available RAM/VRAM.
WATCHSKILL_CLOUD_STT_ENABLEDboolfalseOpt-in cloud speech-to-text. Only extracted mono-16kHz audio may be sent, never the video file (enforced by tests).
WATCHSKILL_DIARIZATION_ENABLEDboolfalseLabel transcript segments by speaker. Needs the diarize extra (pyannote, torch) plus a Hugging Face token.
WATCHSKILL_HUGGINGFACE_TOKENsecretunsetHugging Face token for the gated pyannote diarization models. Accept the model terms on hf.co first.

API keys and vision providers

All keys are optional. With none set, Watch Skill runs local-only: captions, local Whisper, local OCR, local embeddings. Vision-dependent features (scene descriptions, the answer verify pass, THE LOOP's critic) need at least one provider — cloud or a local Ollama.

The agent client and model provider are independent. Configure the provider whose key you already use; all skills, MCP clients, framework adapters, and REST callers share the same engine settings:

watch-skill setup-vision --provider anthropic --api-key <KEY>
watch-skill setup-vision --provider openai --api-key <KEY>
watch-skill setup-vision --provider gemini --api-key <KEY>
watch-skill setup-vision --provider openrouter --api-key <KEY>
watch-skill setup-vision --provider ollama

Every supported provider

Anthropic, Gemini, and Ollama each have their own wire format. The rest speak OpenAI's /chat/completions, so they are registry entries rather than code and all accept --base-url to reach a regional endpoint or a proxy.

Provider--providerKey variableDefault host
AnthropicanthropicWATCHSKILL_ANTHROPIC_API_KEYapi.anthropic.com
OpenAIopenaiWATCHSKILL_OPENAI_API_KEYapi.openai.com
Google GeminigeminiWATCHSKILL_GEMINI_API_KEYgenerativelanguage.googleapis.com
OpenRouteropenrouterWATCHSKILL_OPENROUTER_API_KEYopenrouter.ai
GroqgroqWATCHSKILL_GROQ_API_KEYapi.groq.com
Together AItogetherWATCHSKILL_TOGETHER_API_KEYapi.together.xyz
FireworksfireworksWATCHSKILL_FIREWORKS_API_KEYapi.fireworks.ai
DeepSeekdeepseekWATCHSKILL_DEEPSEEK_API_KEYapi.deepseek.com
xAIxaiWATCHSKILL_XAI_API_KEYapi.x.ai
MistralmistralWATCHSKILL_MISTRAL_API_KEYapi.mistral.ai
MiniMaxminimaxWATCHSKILL_MINIMAX_API_KEYapi.minimax.io
MoonshotmoonshotWATCHSKILL_MOONSHOT_API_KEYapi.moonshot.ai
Z.aizaiWATCHSKILL_ZAI_API_KEYapi.z.ai
Qwen (DashScope)qwenWATCHSKILL_QWEN_API_KEYdashscope-intl.aliyuncs.com
Ollama (local)ollamanone127.0.0.1:11434
Anything elsecustomWATCHSKILL_CUSTOM_API_KEYset --base-url

custom covers any server speaking the OpenAI format — vLLM, LM Studio, llama.cpp, LiteLLM, Azure OpenAI, or a company gateway:

watch-skill setup-vision --provider custom \
  --base-url http://127.0.0.1:8000/v1 \
  --api-key none --model my-vision-model

Every OpenAI-compatible entry also has a WATCHSKILL_<PROVIDER>_BASE_URL variable. Leave it empty to use the default host above.

Use --model for one model at both tiers, or --cheap-model and --strong-model for separate routing. Add --verify to run a live probe. Keys are written to the local .env, whose previous version is backed up.

Model names in the table's defaults move fast. If a vendor renames a model, pass --cheap-model / --strong-model rather than waiting for a release.

VariableTypeDefaultEffect
WATCHSKILL_ANTHROPIC_API_KEYsecretunsetAnthropic API key (vision tiers).
WATCHSKILL_OPENAI_API_KEYsecretunsetOpenAI API key (vision tiers; also a cloud-STT backend).
WATCHSKILL_GEMINI_API_KEYsecretunsetGoogle Gemini API key (vision tiers).
WATCHSKILL_GROQ_API_KEYsecretunsetGroq API key (preferred cloud-STT backend when cloud STT is opted in).
WATCHSKILL_OPENROUTER_API_KEYsecretunsetOpenRouter API key — one key routes to many vision models, including :free variants.
WATCHSKILL_OLLAMA_BASE_URLstrhttp://127.0.0.1:11434Base URL of a local (or remote) Ollama server. Keyless.
WATCHSKILL_VISION_CHEAP_PROVIDERstranthropicProvider for bulk work: scene descriptions, first verify pass. Any name from the provider table above.
WATCHSKILL_VISION_CHEAP_MODELstrclaude-haiku-4-5-20251001Model for the cheap tier.
WATCHSKILL_VISION_STRONG_PROVIDERstranthropicProvider for final answers, low-confidence verification, and the loop critic.
WATCHSKILL_VISION_STRONG_MODELstrclaude-sonnet-5Model for the strong tier.
WATCHSKILL_COST_CEILING_USDfloat1.0Pre-call cost guard: a single cloud vision call whose estimated cost exceeds this raises vision.cost_ceiling instead of running.
WATCHSKILL_COST_POLICYstrcheapestWhich model tiers may be used: cheapest (cheapest path that clears confidence), quality_first (straight to the strong tier), offline_only (local providers only). offline_only is now enforced everywhere, not just in the answer ladder: no frame, audio payload, or transcript reaches a cloud provider, including at indexing time. See cost.md.
WATCHSKILL_OFFLINEboolfalseHard offline mode: zero outbound network calls, source acquisition included. A remote URL returns acquire.offline_denied unless it is already cached locally. Separate from COST_POLICY, which governs money rather than data leaving the machine.
WATCHSKILL_SCENE_DESCRIPTIONSstrautoWhere indexing-time scene descriptions run: off, local, cloud, auto. auto picks local and never upgrades itself to cloud — a configured API key is not consent to upload every frame of every video.
WATCHSKILL_PROVIDER_ALLOWLISTstrunsetComma-separated providers that may be called at all. Empty means any configured provider. A provider off the list is refused with policy.*_denied before its key is read.
WATCHSKILL_COST_CEILING_RUN_USDfloat5.0Ceiling for everything one run spends together — indexing descriptions, answers, loop critics, library synthesis, extraction, verification — not just follow-up questions. Passing it raises policy.run_cost_ceiling.
WATCHSKILL_OCR_BACKENDstrautoauto = RapidOCR, with tesseract auto-routed ONLY for scripts RapidOCR cannot read (Lao/Khmer/Myanmar/Tibetan). Force rapidocr/tesseract/surya to override; surya is opt-in only — its models want more RAM than an 8 GB box has.
WATCHSKILL_EMBEDDING_MODELstrunsetOpt-in retrieval upgrade for NEW indexes: any fastembed model, e.g. BAAI/bge-m3 or intfloat/multilingual-e5-large. Existing indexes keep the model pinned in their meta (vectors from two models never mix). The big models cost ~2 GB+ RAM at query time — skip this on 8 GB machines.
WATCHSKILL_WEBHOOK_URLstrunsetPOST every monitor event here as JSON (n8n/Zapier/your endpoint). At-least-once, 3 attempts with backoff; events.jsonl is written regardless. Schema in docs/packs/monitoring-ops.md.
WATCHSKILL_WEBHOOK_SECRETsecretunsetWhen set, events carry X-WatchSkill-Signature: sha256=<HMAC-SHA256 of the body> for receiver-side verification.
WATCHSKILL_VISION_BATCH_SIZEint8Frames per describe_frames call. Use 2–4 for small local models — large image batches overflow their context.
WATCHSKILL_VISION_TIMEOUT_SECONDSfloat180.0HTTP timeout for cloud vision calls.
WATCHSKILL_VISION_LOCAL_TIMEOUT_SECONDSfloat900.0Timeout for local (Ollama) vision calls — CPU model loads can take minutes.
WATCHSKILL_CRITIC_FRAME_CAPint10Max frames sent to the loop critic in one call. Use 4 for local models.

Self-healing answers

VariableTypeDefaultEffect
WATCHSKILL_RETRIEVAL_OCR_DEDUP_ENABLEDbooltrueCollapse runs of near-identical OCR from one persistent on-screen text into a single representative before evidence is selected. Off, a static caption read on a dozen adjacent frames competes for a dozen top-K slots and crowds out transcript.
WATCHSKILL_RETRIEVAL_OCR_DEDUP_WINDOW_SECONDSfloat10.0Gap tolerance when chaining OCR readings into one occurrence. Consecutive readings closer than this continue the same run, so a caption held for a minute is one cluster while the same text recurring later stays separate.
WATCHSKILL_RETRIEVAL_OCR_DEDUP_SIMILARITYfloat0.88How alike two normalized OCR reads must be to count as the same text. Below 1.0 so recognition noise (decislon for decision) clusters; high enough that different nearby captions stay distinct.
WATCHSKILL_ANSWER_CONFIDENCE_FLOORfloat0.35Below this after the full escalation ladder, the answer states plainly that the video does not clearly show it (the honest floor).
WATCHSKILL_ANSWER_CONFIDENCE_TARGETfloat0.6Escalation stops as soon as confidence clears this bar.
WATCHSKILL_ANSWER_VERIFY_ENABLEDbooltrueWhen a vision provider is configured, show the model the exact frames it is about to cite and require confirmation before answering. Degrades gracefully (model-free answers) when no provider is reachable.
WATCHSKILL_ANSWER_TOKEN_BUDGETint8000Per-question token ceiling; the escalation ladder stops (and the answer says budget_stopped) rather than exceed it.
WATCHSKILL_ANSWER_DEADLINE_SECONDSfloat25.0Per-question wall-clock ceiling. The model-free escalation rungs cost 0 tokens, so the token budget cannot bound them — this does. A rung is skipped or shortened (and the answer says deadline_stopped) rather than overrun an interactive MCP client's timeout. Set 0 to opt out for batch/offline runs where latency does not matter.
WATCHSKILL_ANSWER_STEP_RESERVE_SECONDSfloat6.0Headroom one escalation rung must have left before it may start, so a rung cannot begin a unit of work it has no time to finish.
WATCHSKILL_ANSWER_RESAMPLE_SECONDS_PER_FRAMEfloat2.5Starting estimate for one dense-resample frame (ffmpeg extract + OCR + indexing); refined per process from what windows actually cost on this machine.
WATCHSKILL_ANSWER_ESCALATION_WARMUP_SECONDSfloat40.0One-off cost of the first escalation in a process — the OCR engine is a per-process singleton, so window one pays a model load later windows do not. Counted against the deadline, which is why a fresh server's first ask answers from the index alone.
WATCHSKILL_ANSWER_RESAMPLE_WIDTHfloat8.0Window in seconds around a candidate timestamp for the dense re-sampling escalation step.
WATCHSKILL_ANSWER_RESAMPLE_RESOLUTIONint1024Frame width in px for escalation re-sampling — higher than the indexing default so zoom crops have pixels to work with.
WATCHSKILL_ANSWER_CACHE_ENABLEDbooltrueSemantic answer cache, per video. Repeat questions return the cached answer at zero model cost.
WATCHSKILL_ANSWER_CACHE_SIMILARITYfloat0.92Cosine similarity above which a cached question counts as a repeat.
WATCHSKILL_LESSONS_ENABLEDbooltrueLocal lessons store: learn from reported mistakes and inject relevant guidance into future asks. Never uploaded anywhere.
WATCHSKILL_LESSONS_INJECTION_TOKEN_CAPint300Max prompt tokens the injected "learned corrections" section may consume.
WATCHSKILL_LESSONS_MAX_COUNTint500Global cap on stored lessons; least-recently-used are pruned.

Surfaces

VariableTypeDefaultEffect
WATCHSKILL_RESPONSE_FRAME_CAPint12Max image blocks per MCP or REST response (frames beyond the cap are evenly sampled, first and last kept).
WATCHSKILL_MCP_INLINE_UIboolfalseAttach the viewer page to MCP results as a ui:// resource. Clients that render it (Goose, LibreChat, mcp-ui inspectors) show a scrubbable timeline inline; the rest ignore the block. Off by default — it is a large payload most clients cannot use yet. Text stays the first block, so turning it on never changes what a non-rendering client sees.
WATCHSKILL_API_BEARER_TOKENsecretunsetBearer token for the REST API. Unset = local only: the API refuses to bind to a non-loopback host without it (config.public_bind_no_token).

Read outside the settings object

VariableTypeDefaultEffect
WATCHSKILL_COBALT_API_URLstrunsetURL of a self-hosted cobalt instance to use as an acquisition fallback between yt-dlp and direct ffmpeg. The public api.cobalt.tools requires auth and is never used; without this variable the cobalt step is skipped entirely. Read from the process environment only (not .env).
WATCHSKILL_HOMEpath~/watch-skillInstaller-only (scripts/install.sh): where the macOS/Linux one-liner clones the repo. Not read by the engine.

Boolean variables accept the usual pydantic forms: 1/0, true/false, yes/no (case-insensitive).

CLI flags

Global shape: watch-skill [COMMAND] [ARGS] [OPTIONS]. Progress goes to stderr and results to stdout, so output pipes cleanly. Verified against uv run watch-skill <command> --help for every command below.

watch-skill watch SOURCE [QUESTION]

Watch a video: acquire → scenes → frames → OCR → transcript → report. SOURCE is a URL (any of yt-dlp's 1800+ sites), a direct media URL, an HLS/DASH manifest, or a local path. QUESTION is optional and echoed into the output for the calling agent.

FlagTypeDefaultEffect
--starttime (SS, MM:SS, HH:MM:SS)noneRange start; with --end, switches to the denser focused-mode frame budget.
--endtimenoneRange end.
--max-framesintconfig frame_cap (100)Override the frame cap for this run.
--resolutionintconfig frame_width (512)Frame width in px.
--timestampscomma-separated timesnonePin frames at exact absolute times, in addition to scene sampling.
--transcript-onlyflagoffSkip frame extraction entirely; captions-first fast path.
--no-ocrflagoffSkip the OCR pass.
--no-whisperflagoffDisable the local Whisper fallback (captions only).
--cloud-sttflagoffOpt in to cloud STT for extracted audio, this run only.
--whisper-modelstrconfig whisper_model (auto)faster-whisper size (tinylarge-v3).
--diarizeflagoffLabel transcript by speaker (needs the diarize extra + HF token).
--durationfloatnoneBound live-stream capture to N seconds.
--out-dirpathtemp dirWorking directory for intermediate files.
--no-cacheflagoffBypass the download cache.
--index/--no-indexflag--indexPersist the result to the searchable index (enables ask/search later).

watch-skill ask VIDEO QUESTION

Ask an already-indexed video a question (self-healing answer engine). VIDEO is a video_id or the original source URL/path.

FlagTypeDefaultEffect
--max-framesint6Max evidence frame paths listed.
--framesflagoffAlways list evidence frame paths (default: only when the engine is uncertain).
--no-verifyflagoffSkip the model verify pass.
--no-cacheflagoffBypass the semantic answer cache.

watch-skill serve

Run the MCP server.

FlagTypeDefaultEffect
--httpflagoffStreamable HTTP transport instead of stdio (endpoint /mcp).
--hoststr127.0.0.1Bind host (HTTP mode).
--portint8747Bind port (HTTP mode).

watch-skill api

Run the REST API (FastAPI; OpenAPI spec at /openapi.json).

FlagTypeDefaultEffect
--hoststr127.0.0.1Bind host. Non-loopback binds require WATCHSKILL_API_BEARER_TOKEN.
--portint8748Bind port.

watch-skill doctor

Check (and self-heal) dependencies: ffmpeg, yt-dlp, deno, disk, GPU, API keys.

FlagTypeDefaultEffect
--fix/--no-fixflag--fixAuto-remediate fixable issues (install ffmpeg/yt-dlp/deno, self-update stale yt-dlp).
--jsonflagoffEmit machine-readable JSON to stdout. Exit code 1 when any check fails.

watch-skill forget VIDEO

Remove one video from the index: its rows, cached answers, and frames directory. VIDEO is a video_id or the original source. No flags.

watch-skill stats

Print lifetime answer count and estimated tokens saved vs raw-frame injection. No flags.

watch-skill list

List indexed videos (id duration title). No flags.

watch-skill search QUERY

Hybrid keyword + semantic search across every indexed video; prints timestamped hits grouped by video. No flags.

watch-skill capture TARGET

Record a URL session (headless browser), the screen (screen:), a window (window:<exact title>), or adopt an existing video file.

FlagTypeDefaultEffect
--durationfloat10.0Recording length in seconds.
--scriptJSON stringnoneInteraction script: a JSON list of steps (goto/click/fill/scroll/wait) executed in the browser session.
--out-dirpathtemp dirWhere the recording lands.

watch-skill loop start TARGET PASS_CRITERIA

Capture + critique the first loop iteration; prints loop_id and structured issues. Requires a vision provider (the critic is a model call).

FlagTypeDefaultEffect
--scriptJSON stringnoneSame interaction-script format as capture.
--max-iterationsint5Stop condition.
--durationfloat8.0Recording length per iteration in seconds.

watch-skill loop iterate LOOP_ID

Re-capture + re-critique after you applied fixes; diffs against the previous iteration. No flags.

watch-skill loop status LOOP_ID

Show a loop's persisted state and score history. No flags.

watch-skill setup

Detect installed AI agents (Claude Code, Claude Desktop, Cursor, Codex CLI, Windsurf, Gemini CLI, …) and write the MCP config into each one, backing up any existing file first.

FlagTypeDefaultEffect
--yes, -yflagoffConfigure all detected agents without prompting.
--onlystrall detectedComma list of agent keys to restrict to (e.g. cursor,codex).

watch-skill clean

Reclaim disk: bounded cache, bounded loop archives, orphaned frames.

FlagTypeDefaultEffect
--cacheflagoffEvict the download cache down to its size cap.
--all-cacheflagoffEmpty the download cache entirely.
--loopsflagoffKeep only the most recent loops.
--keep-loopsint10How many recent loops --loops keeps.
--orphansflagoffRemove frame dirs for videos no longer in the index.
--cache-answersflagoffClear the semantic answer cache.
--allflagoffShorthand for --cache --loops --orphans.
--dry-runflagoffReport what would be freed; delete nothing.

watch-skill lessons add VIDEO QUESTION WRONG CORRECTION

Report a wrong answer + its correction; the system classifies it, stores a lesson, and (for mechanical error classes) immediately re-asks the question to validate that the lesson works.

FlagTypeDefaultEffect
--sessionstrnoneSession id to group the lesson under (bulk-removable later).
--no-reaskflagoffSkip the immediate re-ask validation.

watch-skill lessons list

List stored lessons, newest first ([✓] marks validated ones).

FlagTypeDefaultEffect
--sessionstrnoneFilter to one session.
--limitint20Max rows.

watch-skill lessons rm [IDS]...

Remove lessons by id, or a whole session.

FlagTypeDefaultEffect
--sessionstrnoneRemove every lesson in this session.

watch-skill lessons export-evals

Convert every lesson into a replayable eval case under <data_dir>/evals. No flags.

watch-skill evals run

Replay the lesson-derived eval suite against the current system and print the pass rate. No flags.

watch-skill profiles show

Show the active adaptive per-content-type profiles (data aggregated from lesson statistics, not code). No flags.

watch-skill profiles reset

Drop all adaptive profiles (the lessons themselves stay). No flags.

watch-skill version

Print the Watch Skill version. No flags.

MCP tool parameters

The MCP tools accept per-call parameters (budget, max_frames, include_frames, verify, window, …) that override the same settings for one call. They are documented per tool in tools/README.md.

Durable background jobs

Durable background work. A job id survives a restart, so an agent that reconnects after a crash can still collect its result.

watch-skill jobs list [--state queued|running|succeeded|failed|cancelled]
watch-skill jobs status <job_id> [--events]   # --events prints the append-only log
watch-skill jobs cancel <job_id>
watch-skill jobs worker [--kind watch] [--max-jobs N] [--idle-exit]
watch-skill jobs recover                      # re-queue jobs whose worker died

cancel on a queued job stops it immediately; on a running job it is acknowledged at the next stage checkpoint, and the partial work is discarded rather than half-committed. Any number of workers may run at once — a job is claimed under a lease, so exactly one gets each job.