Files
claude-video/tests/test_watch.py
T
bradautomates 429f3143e5 Restructure as a self-contained Agent Skills package (fix Codex install)
Move the skill into skills/watch/ so SKILL.md and its scripts/ runtime are
siblings inside one folder. `npx skills add` (Codex/Cursor/Copilot/agents) now
copies a working skill as a unit; previously it grabbed the root SKILL.md alone
and left scripts/ behind, so the skill was dead on arrival on every non-Claude
host. Mirrors the layout last30days-skill adopted for the same reason.

- skills/watch/{SKILL.md,scripts/}: self-contained skill folder
- SKILL.md: resolve a harness-agnostic $SKILL_DIR (the dir it was Read from)
  instead of the Claude-Code-only ${CLAUDE_SKILL_DIR}; guard + 19 call sites
- drop commands/watch.md: /watch derives from frontmatter (name + user-invocable)
- .codex-plugin/plugin.json: full manifest with "skills": "./skills/" + interface
- add .agents/plugins/marketplace.json, AGENTS.md, CLAUDE.md, .skillignore
- build-skill.sh: archive the skills/watch subtree (one SKILL.md, no zip -d)
- fix paths in tests, hooks hint, .gitattributes, release.yml
- relocate dev-sync.sh to repo root and fix REPO_ROOT
- README: content-ideas structure, npx skills install, star history

Verified: 37/37 tests pass; npx skills add bundles the full scripts/ runtime;
manifests valid; versions synced at 0.1.3.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-29 22:12:54 +10:00

70 lines
2.2 KiB
Python

"""End-to-end routing of --detail through watch.py on a local clip."""
from __future__ import annotations
import os
import subprocess
import sys
from pathlib import Path
WATCH = Path(__file__).resolve().parent.parent / "skills" / "watch" / "scripts" / "watch.py"
def _run(clip: Path, *args: str, env_extra: dict | None = None) -> str:
env = dict(os.environ)
env.pop("WATCH_DETAIL", None)
if env_extra:
env.update(env_extra)
proc = subprocess.run(
[sys.executable, str(WATCH), str(clip), "--no-whisper", *args],
capture_output=True, text=True, env=env,
)
assert proc.returncode == 0, proc.stderr
return proc.stdout
def test_efficient_uses_keyframe_engine(cut_clip: Path):
out = _run(cut_clip, "--detail", "efficient")
assert "(keyframe" in out
assert "**Detail:** efficient" in out
def test_balanced_uses_scene_engine(cut_clip: Path):
out = _run(cut_clip, "--detail", "balanced")
assert "(scene" in out
assert "**Detail:** balanced" in out
def test_token_burner_uses_scene_engine(cut_clip: Path):
out = _run(cut_clip, "--detail", "token-burner")
assert "(scene" in out
def test_transcript_skips_frames(cut_clip: Path):
out = _run(cut_clip, "--detail", "transcript")
assert "skipped" in out
assert "frame_0000.jpg" not in out
def test_flag_overrides_env(cut_clip: Path):
out = _run(cut_clip, "--detail", "efficient", env_extra={"WATCH_DETAIL": "balanced"})
assert "(keyframe" in out
def test_default_is_balanced(cut_clip: Path):
out = _run(cut_clip) # no flag, WATCH_DETAIL cleared
assert "**Detail:** balanced" in out
assert "(scene" in out
def test_timestamps_add_cue_frames_to_detail(cut_clip: Path):
out = _run(cut_clip, "--detail", "balanced", "--timestamps", "1,3")
assert "reason=transcript-cue" in out
assert "reason=scene-change" in out # detail frames still present (additive)
def test_timestamps_with_transcript_detail_is_cue_only(cut_clip: Path):
out = _run(cut_clip, "--detail", "transcript", "--timestamps", "1,3")
assert "reason=transcript-cue" in out
assert "reason=scene-change" not in out
assert "reason=keyframe" not in out