watch-skill 1.2.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- watch_skill-1.2.0/.claude-plugin/marketplace.json +35 -0
- watch_skill-1.2.0/.claude-plugin/plugin.json +18 -0
- watch_skill-1.2.0/.gitattributes +5 -0
- watch_skill-1.2.0/.github/CODEOWNERS +3 -0
- watch_skill-1.2.0/.github/FUNDING.yml +4 -0
- watch_skill-1.2.0/.github/ISSUE_TEMPLATE/bug_report.yml +37 -0
- watch_skill-1.2.0/.github/ISSUE_TEMPLATE/feature_request.yml +21 -0
- watch_skill-1.2.0/.github/ISSUE_TEMPLATE/new_agent_support.yml +23 -0
- watch_skill-1.2.0/.github/PULL_REQUEST_TEMPLATE.md +13 -0
- watch_skill-1.2.0/.github/dependabot.yml +41 -0
- watch_skill-1.2.0/.github/workflows/ci.yml +64 -0
- watch_skill-1.2.0/.github/workflows/docker.yml +106 -0
- watch_skill-1.2.0/.github/workflows/install.yml +138 -0
- watch_skill-1.2.0/.github/workflows/integration.yml +30 -0
- watch_skill-1.2.0/.github/workflows/release.yml +84 -0
- watch_skill-1.2.0/.gitignore +47 -0
- watch_skill-1.2.0/.mcp.json +9 -0
- watch_skill-1.2.0/.python-version +1 -0
- watch_skill-1.2.0/AGENTS.md +76 -0
- watch_skill-1.2.0/CHANGELOG.md +478 -0
- watch_skill-1.2.0/CONTRIBUTING.md +97 -0
- watch_skill-1.2.0/Dockerfile +49 -0
- watch_skill-1.2.0/LICENSE +21 -0
- watch_skill-1.2.0/PKG-INFO +410 -0
- watch_skill-1.2.0/README.md +323 -0
- watch_skill-1.2.0/SECURITY.md +65 -0
- watch_skill-1.2.0/adapters/agents-md/AGENTS.md +84 -0
- watch_skill-1.2.0/benchmarks/cost/README.md +23 -0
- watch_skill-1.2.0/benchmarks/cost/RESULTS.md +16 -0
- watch_skill-1.2.0/benchmarks/cost/run_cost_benchmark.py +214 -0
- watch_skill-1.2.0/benchmarks/perception/README.md +40 -0
- watch_skill-1.2.0/benchmarks/perception/RESULTS.md +29 -0
- watch_skill-1.2.0/benchmarks/perception/fixtures/arabic_rtl.png +0 -0
- watch_skill-1.2.0/benchmarks/perception/fixtures/cjk.png +0 -0
- watch_skill-1.2.0/benchmarks/perception/fixtures/fixtures.json +26 -0
- watch_skill-1.2.0/benchmarks/perception/fixtures/mixed_script.png +0 -0
- watch_skill-1.2.0/benchmarks/perception/fixtures/screen_text.png +0 -0
- watch_skill-1.2.0/benchmarks/perception/fixtures/sea_lao.png +0 -0
- watch_skill-1.2.0/benchmarks/perception/fixtures/subtitles.png +0 -0
- watch_skill-1.2.0/benchmarks/perception/make_fixtures.py +117 -0
- watch_skill-1.2.0/commands/setup-watch-skill.md +100 -0
- watch_skill-1.2.0/docs/DECISIONS.md +211 -0
- watch_skill-1.2.0/docs/README.md +63 -0
- watch_skill-1.2.0/docs/ROADMAP.md +69 -0
- watch_skill-1.2.0/docs/agents/README.md +99 -0
- watch_skill-1.2.0/docs/agents/agent-zero.md +66 -0
- watch_skill-1.2.0/docs/agents/claude-code.md +61 -0
- watch_skill-1.2.0/docs/agents/claude-desktop.md +57 -0
- watch_skill-1.2.0/docs/agents/cline.md +58 -0
- watch_skill-1.2.0/docs/agents/codex-cli.md +56 -0
- watch_skill-1.2.0/docs/agents/continue.md +60 -0
- watch_skill-1.2.0/docs/agents/cursor.md +63 -0
- watch_skill-1.2.0/docs/agents/frameworks.md +134 -0
- watch_skill-1.2.0/docs/agents/gemini-cli.md +56 -0
- watch_skill-1.2.0/docs/agents/github-copilot-cli.md +66 -0
- watch_skill-1.2.0/docs/agents/goose.md +67 -0
- watch_skill-1.2.0/docs/agents/hermes.md +54 -0
- watch_skill-1.2.0/docs/agents/kilocode.md +67 -0
- watch_skill-1.2.0/docs/agents/kimi-code.md +64 -0
- watch_skill-1.2.0/docs/agents/openclaw.md +56 -0
- watch_skill-1.2.0/docs/agents/opencode.md +63 -0
- watch_skill-1.2.0/docs/agents/openhands.md +62 -0
- watch_skill-1.2.0/docs/agents/pi.md +57 -0
- watch_skill-1.2.0/docs/agents/qodo.md +72 -0
- watch_skill-1.2.0/docs/agents/qwen-code.md +65 -0
- watch_skill-1.2.0/docs/agents/roo-code.md +61 -0
- watch_skill-1.2.0/docs/agents/vscode.md +57 -0
- watch_skill-1.2.0/docs/agents/windsurf.md +55 -0
- watch_skill-1.2.0/docs/agents/zed.md +71 -0
- watch_skill-1.2.0/docs/architecture.md +273 -0
- watch_skill-1.2.0/docs/assets/agents/README.md +32 -0
- watch_skill-1.2.0/docs/assets/agents/agent-zero.webp +0 -0
- watch_skill-1.2.0/docs/assets/agents/claude-code.webp +0 -0
- watch_skill-1.2.0/docs/assets/agents/claude-desktop.webp +0 -0
- watch_skill-1.2.0/docs/assets/agents/cline.webp +0 -0
- watch_skill-1.2.0/docs/assets/agents/codex-cli.webp +0 -0
- watch_skill-1.2.0/docs/assets/agents/cursor.webp +0 -0
- watch_skill-1.2.0/docs/assets/agents/frameworks.webp +0 -0
- watch_skill-1.2.0/docs/assets/agents/gemini-cli.webp +0 -0
- watch_skill-1.2.0/docs/assets/agents/github-copilot-cli.webp +0 -0
- watch_skill-1.2.0/docs/assets/agents/goose.webp +0 -0
- watch_skill-1.2.0/docs/assets/agents/hermes.webp +0 -0
- watch_skill-1.2.0/docs/assets/agents/kilocode.webp +0 -0
- watch_skill-1.2.0/docs/assets/agents/kimi-code.webp +0 -0
- watch_skill-1.2.0/docs/assets/agents/openclaw.webp +0 -0
- watch_skill-1.2.0/docs/assets/agents/opencode.webp +0 -0
- watch_skill-1.2.0/docs/assets/agents/openhands.webp +0 -0
- watch_skill-1.2.0/docs/assets/agents/pi.webp +0 -0
- watch_skill-1.2.0/docs/assets/agents/qodo.webp +0 -0
- watch_skill-1.2.0/docs/assets/agents/qwen-code.webp +0 -0
- watch_skill-1.2.0/docs/assets/agents/vscode.webp +0 -0
- watch_skill-1.2.0/docs/assets/agents/windsurf.webp +0 -0
- watch_skill-1.2.0/docs/assets/lessons_loop.gif +0 -0
- watch_skill-1.2.0/docs/assets/loop_before_after.gif +0 -0
- watch_skill-1.2.0/docs/assets/savings_meter.png +0 -0
- watch_skill-1.2.0/docs/assets/watch-skill-hero.webp +0 -0
- watch_skill-1.2.0/docs/comparison.md +116 -0
- watch_skill-1.2.0/docs/configuration.md +386 -0
- watch_skill-1.2.0/docs/cost.md +71 -0
- watch_skill-1.2.0/docs/getting-started.md +135 -0
- watch_skill-1.2.0/docs/guides/arabic-in-arabic-out.md +84 -0
- watch_skill-1.2.0/docs/guides/how-it-improves-itself.md +75 -0
- watch_skill-1.2.0/docs/guides/lessons-and-savings.md +110 -0
- watch_skill-1.2.0/docs/guides/the-loop.md +104 -0
- watch_skill-1.2.0/docs/guides/youtube-analysis.md +100 -0
- watch_skill-1.2.0/docs/migrate-from-claude-video.md +117 -0
- watch_skill-1.2.0/docs/packs/README.md +19 -0
- watch_skill-1.2.0/docs/packs/agent-self-verification.md +46 -0
- watch_skill-1.2.0/docs/packs/browser-agent-verification.md +53 -0
- watch_skill-1.2.0/docs/packs/content-creators.md +47 -0
- watch_skill-1.2.0/docs/packs/learning-research.md +41 -0
- watch_skill-1.2.0/docs/packs/meetings-lectures.md +46 -0
- watch_skill-1.2.0/docs/packs/monitoring-ops.md +60 -0
- watch_skill-1.2.0/docs/packs/qa-bug-hunting.md +44 -0
- watch_skill-1.2.0/docs/tools/README.md +371 -0
- watch_skill-1.2.0/docs/troubleshooting.md +130 -0
- watch_skill-1.2.0/examples/01-watch-and-ask/README.md +80 -0
- watch_skill-1.2.0/examples/01-watch-and-ask/watch_and_ask.py +45 -0
- watch_skill-1.2.0/examples/02-focused-moment/README.md +49 -0
- watch_skill-1.2.0/examples/02-focused-moment/focused_moment.py +51 -0
- watch_skill-1.2.0/examples/03-cross-video-search/README.md +42 -0
- watch_skill-1.2.0/examples/03-cross-video-search/cross_video_search.py +54 -0
- watch_skill-1.2.0/examples/04-ui-loop/README.md +67 -0
- watch_skill-1.2.0/examples/04-ui-loop/page_broken.html +34 -0
- watch_skill-1.2.0/examples/04-ui-loop/page_fixed.html +33 -0
- watch_skill-1.2.0/examples/04-ui-loop/ui_loop.py +127 -0
- watch_skill-1.2.0/examples/05-multilingual-arabic/README.md +63 -0
- watch_skill-1.2.0/examples/05-multilingual-arabic/ask_in_arabic.py +41 -0
- watch_skill-1.2.0/examples/05-multilingual-arabic/index_arabic.py +47 -0
- watch_skill-1.2.0/examples/06-agent-integration/README.md +76 -0
- watch_skill-1.2.0/examples/06-agent-integration/agent_integration.py +50 -0
- watch_skill-1.2.0/examples/07-lessons-and-stats/README.md +68 -0
- watch_skill-1.2.0/examples/07-lessons-and-stats/lessons_and_stats.py +70 -0
- watch_skill-1.2.0/examples/08-loop-types/README.md +106 -0
- watch_skill-1.2.0/examples/08-loop-types/game_broken.html +34 -0
- watch_skill-1.2.0/examples/08-loop-types/game_fixed.html +34 -0
- watch_skill-1.2.0/examples/08-loop-types/game_loop.py +58 -0
- watch_skill-1.2.0/examples/08-loop-types/monitor_loop.py +86 -0
- watch_skill-1.2.0/examples/08-loop-types/render_title_card.py +47 -0
- watch_skill-1.2.0/examples/08-loop-types/video_gen_loop.py +59 -0
- watch_skill-1.2.0/examples/09-framework-adapters/README.md +48 -0
- watch_skill-1.2.0/examples/09-framework-adapters/_demo_clip.py +47 -0
- watch_skill-1.2.0/examples/09-framework-adapters/crewai_example.py +38 -0
- watch_skill-1.2.0/examples/09-framework-adapters/langchain_example.py +39 -0
- watch_skill-1.2.0/examples/09-framework-adapters/openai_agents_example.py +48 -0
- watch_skill-1.2.0/examples/10-structured-extraction/README.md +57 -0
- watch_skill-1.2.0/examples/10-structured-extraction/extraction_demo.py +102 -0
- watch_skill-1.2.0/examples/11-batch-mode/README.md +35 -0
- watch_skill-1.2.0/examples/11-batch-mode/batch_demo.py +94 -0
- watch_skill-1.2.0/examples/12-library-memory/README.md +70 -0
- watch_skill-1.2.0/examples/12-library-memory/library_demo.py +119 -0
- watch_skill-1.2.0/examples/13-self-improvement/README.md +56 -0
- watch_skill-1.2.0/examples/13-self-improvement/self_improvement_demo.py +151 -0
- watch_skill-1.2.0/examples/14-browser-verification/README.md +69 -0
- watch_skill-1.2.0/examples/14-browser-verification/browser_verification_demo.py +95 -0
- watch_skill-1.2.0/examples/14-browser-verification/checkout_buggy.html +50 -0
- watch_skill-1.2.0/examples/14-browser-verification/checkout_fixed.html +46 -0
- watch_skill-1.2.0/examples/15-private-offline-workflow/README.md +40 -0
- watch_skill-1.2.0/examples/15-private-offline-workflow/offline_workflow.py +98 -0
- watch_skill-1.2.0/examples/16-shareable-viewer/README.md +27 -0
- watch_skill-1.2.0/examples/16-shareable-viewer/export_viewer.py +33 -0
- watch_skill-1.2.0/examples/README.md +55 -0
- watch_skill-1.2.0/llms.txt +62 -0
- watch_skill-1.2.0/pyproject.toml +125 -0
- watch_skill-1.2.0/scripts/golden_path.py +229 -0
- watch_skill-1.2.0/scripts/install.ps1 +107 -0
- watch_skill-1.2.0/scripts/install.sh +107 -0
- watch_skill-1.2.0/skills/asking-with-evidence/SKILL.md +59 -0
- watch_skill-1.2.0/skills/configuring-vision/SKILL.md +53 -0
- watch_skill-1.2.0/skills/extracting-structure/SKILL.md +50 -0
- watch_skill-1.2.0/skills/learning-from-mistakes/SKILL.md +45 -0
- watch_skill-1.2.0/skills/recovering-from-errors/SKILL.md +41 -0
- watch_skill-1.2.0/skills/sharing-results/SKILL.md +41 -0
- watch_skill-1.2.0/skills/the-loop/SKILL.md +69 -0
- watch_skill-1.2.0/skills/video-memory/SKILL.md +65 -0
- watch_skill-1.2.0/skills/watch/SKILL.md +124 -0
- watch_skill-1.2.0/skills/watching-videos/SKILL.md +65 -0
- watch_skill-1.2.0/skills.sh.json +17 -0
- watch_skill-1.2.0/src/watch_skill/__init__.py +19 -0
- watch_skill-1.2.0/src/watch_skill/acquire/__init__.py +12 -0
- watch_skill-1.2.0/src/watch_skill/acquire/cache.py +150 -0
- watch_skill-1.2.0/src/watch_skill/acquire/cobalt.py +96 -0
- watch_skill-1.2.0/src/watch_skill/acquire/direct.py +50 -0
- watch_skill-1.2.0/src/watch_skill/acquire/resolver.py +176 -0
- watch_skill-1.2.0/src/watch_skill/acquire/sources.py +54 -0
- watch_skill-1.2.0/src/watch_skill/acquire/types.py +26 -0
- watch_skill-1.2.0/src/watch_skill/acquire/ytdlp.py +208 -0
- watch_skill-1.2.0/src/watch_skill/answer/__init__.py +11 -0
- watch_skill-1.2.0/src/watch_skill/answer/cache.py +172 -0
- watch_skill-1.2.0/src/watch_skill/answer/confidence.py +122 -0
- watch_skill-1.2.0/src/watch_skill/answer/crops.py +75 -0
- watch_skill-1.2.0/src/watch_skill/answer/engine.py +397 -0
- watch_skill-1.2.0/src/watch_skill/answer/ladder.py +182 -0
- watch_skill-1.2.0/src/watch_skill/answer/localize.py +218 -0
- watch_skill-1.2.0/src/watch_skill/answer/types.py +62 -0
- watch_skill-1.2.0/src/watch_skill/batch.py +158 -0
- watch_skill-1.2.0/src/watch_skill/bench/__init__.py +4 -0
- watch_skill-1.2.0/src/watch_skill/bench/perception.py +175 -0
- watch_skill-1.2.0/src/watch_skill/config.py +295 -0
- watch_skill-1.2.0/src/watch_skill/errors.py +98 -0
- watch_skill-1.2.0/src/watch_skill/extract/__init__.py +11 -0
- watch_skill-1.2.0/src/watch_skill/extract/bug_report.py +139 -0
- watch_skill-1.2.0/src/watch_skill/extract/chapters.py +123 -0
- watch_skill-1.2.0/src/watch_skill/extract/hook.py +152 -0
- watch_skill-1.2.0/src/watch_skill/health/__init__.py +6 -0
- watch_skill-1.2.0/src/watch_skill/health/agents_setup.py +151 -0
- watch_skill-1.2.0/src/watch_skill/health/binaries.py +277 -0
- watch_skill-1.2.0/src/watch_skill/health/clean.py +114 -0
- watch_skill-1.2.0/src/watch_skill/health/doctor.py +633 -0
- watch_skill-1.2.0/src/watch_skill/health/log.py +46 -0
- watch_skill-1.2.0/src/watch_skill/health/vision_setup.py +297 -0
- watch_skill-1.2.0/src/watch_skill/index/__init__.py +29 -0
- watch_skill-1.2.0/src/watch_skill/index/db.py +299 -0
- watch_skill-1.2.0/src/watch_skill/index/embeddings.py +74 -0
- watch_skill-1.2.0/src/watch_skill/index/retrieval.py +266 -0
- watch_skill-1.2.0/src/watch_skill/index/store.py +340 -0
- watch_skill-1.2.0/src/watch_skill/index/textnorm.py +208 -0
- watch_skill-1.2.0/src/watch_skill/integrations/__init__.py +13 -0
- watch_skill-1.2.0/src/watch_skill/integrations/_core.py +88 -0
- watch_skill-1.2.0/src/watch_skill/integrations/autogen.py +26 -0
- watch_skill-1.2.0/src/watch_skill/integrations/crewai.py +40 -0
- watch_skill-1.2.0/src/watch_skill/integrations/langchain.py +30 -0
- watch_skill-1.2.0/src/watch_skill/integrations/llamaindex.py +27 -0
- watch_skill-1.2.0/src/watch_skill/integrations/openai_agents.py +29 -0
- watch_skill-1.2.0/src/watch_skill/jobs.py +115 -0
- watch_skill-1.2.0/src/watch_skill/lessons/__init__.py +23 -0
- watch_skill-1.2.0/src/watch_skill/lessons/classify.py +105 -0
- watch_skill-1.2.0/src/watch_skill/lessons/evals.py +192 -0
- watch_skill-1.2.0/src/watch_skill/lessons/inject.py +81 -0
- watch_skill-1.2.0/src/watch_skill/lessons/profiles.py +94 -0
- watch_skill-1.2.0/src/watch_skill/lessons/report.py +96 -0
- watch_skill-1.2.0/src/watch_skill/lessons/store.py +184 -0
- watch_skill-1.2.0/src/watch_skill/library/__init__.py +23 -0
- watch_skill-1.2.0/src/watch_skill/library/notes.py +204 -0
- watch_skill-1.2.0/src/watch_skill/library/synthesize.py +349 -0
- watch_skill-1.2.0/src/watch_skill/loop/__init__.py +61 -0
- watch_skill-1.2.0/src/watch_skill/loop/artifact.py +82 -0
- watch_skill-1.2.0/src/watch_skill/loop/capture.py +226 -0
- watch_skill-1.2.0/src/watch_skill/loop/critic.py +331 -0
- watch_skill-1.2.0/src/watch_skill/loop/diff.py +125 -0
- watch_skill-1.2.0/src/watch_skill/loop/framework.py +215 -0
- watch_skill-1.2.0/src/watch_skill/loop/monitor.py +198 -0
- watch_skill-1.2.0/src/watch_skill/loop/reportfmt.py +42 -0
- watch_skill-1.2.0/src/watch_skill/loop/runner.py +293 -0
- watch_skill-1.2.0/src/watch_skill/loop/webhook.py +95 -0
- watch_skill-1.2.0/src/watch_skill/perceive/__init__.py +23 -0
- watch_skill-1.2.0/src/watch_skill/perceive/budget.py +97 -0
- watch_skill-1.2.0/src/watch_skill/perceive/engine.py +213 -0
- watch_skill-1.2.0/src/watch_skill/perceive/media.py +91 -0
- watch_skill-1.2.0/src/watch_skill/perceive/ocr.py +151 -0
- watch_skill-1.2.0/src/watch_skill/perceive/ocr_backends.py +315 -0
- watch_skill-1.2.0/src/watch_skill/perceive/scenes.py +87 -0
- watch_skill-1.2.0/src/watch_skill/perceive/types.py +88 -0
- watch_skill-1.2.0/src/watch_skill/report.py +108 -0
- watch_skill-1.2.0/src/watch_skill/surfaces/__init__.py +5 -0
- watch_skill-1.2.0/src/watch_skill/surfaces/api/__init__.py +9 -0
- watch_skill-1.2.0/src/watch_skill/surfaces/api/app.py +337 -0
- watch_skill-1.2.0/src/watch_skill/surfaces/cli/__init__.py +1 -0
- watch_skill-1.2.0/src/watch_skill/surfaces/cli/main.py +1035 -0
- watch_skill-1.2.0/src/watch_skill/surfaces/mcp/__init__.py +1 -0
- watch_skill-1.2.0/src/watch_skill/surfaces/mcp/server.py +619 -0
- watch_skill-1.2.0/src/watch_skill/transcribe/__init__.py +12 -0
- watch_skill-1.2.0/src/watch_skill/transcribe/audio.py +131 -0
- watch_skill-1.2.0/src/watch_skill/transcribe/cloud.py +190 -0
- watch_skill-1.2.0/src/watch_skill/transcribe/diarize.py +116 -0
- watch_skill-1.2.0/src/watch_skill/transcribe/ladder.py +71 -0
- watch_skill-1.2.0/src/watch_skill/transcribe/local.py +113 -0
- watch_skill-1.2.0/src/watch_skill/transcribe/types.py +66 -0
- watch_skill-1.2.0/src/watch_skill/transcribe/vtt.py +64 -0
- watch_skill-1.2.0/src/watch_skill/viewer.py +213 -0
- watch_skill-1.2.0/src/watch_skill/vision/__init__.py +22 -0
- watch_skill-1.2.0/src/watch_skill/vision/client.py +304 -0
- watch_skill-1.2.0/src/watch_skill/vision/cost.py +52 -0
- watch_skill-1.2.0/src/watch_skill/vision/local_health.py +119 -0
- watch_skill-1.2.0/src/watch_skill/vision/model.py +149 -0
- watch_skill-1.2.0/src/watch_skill/vision/prices.json +19 -0
- watch_skill-1.2.0/src/watch_skill/vision/registry.py +144 -0
- watch_skill-1.2.0/src/watch_skill/watch.py +177 -0
- watch_skill-1.2.0/templates/agent-adapter/README.md +45 -0
- watch_skill-1.2.0/templates/agent-adapter/docs-skeleton.md +48 -0
- watch_skill-1.2.0/templates/agent-adapter/validate.py +88 -0
- watch_skill-1.2.0/tests/acquire/test_acquire_chain.py +70 -0
- watch_skill-1.2.0/tests/acquire/test_cache.py +75 -0
- watch_skill-1.2.0/tests/acquire/test_sources.py +39 -0
- watch_skill-1.2.0/tests/acquire/test_ytdlp.py +133 -0
- watch_skill-1.2.0/tests/answer/test_answer.py +364 -0
- watch_skill-1.2.0/tests/answer/test_cost_meter.py +129 -0
- watch_skill-1.2.0/tests/answer/test_localize.py +142 -0
- watch_skill-1.2.0/tests/answer/test_token_economy.py +220 -0
- watch_skill-1.2.0/tests/conftest.py +77 -0
- watch_skill-1.2.0/tests/extract/test_extract.py +184 -0
- watch_skill-1.2.0/tests/health/test_binaries.py +91 -0
- watch_skill-1.2.0/tests/health/test_clean.py +67 -0
- watch_skill-1.2.0/tests/health/test_doctor.py +204 -0
- watch_skill-1.2.0/tests/health/test_doctor_repairs.py +157 -0
- watch_skill-1.2.0/tests/health/test_ffmpeg_bootstrap.py +135 -0
- watch_skill-1.2.0/tests/health/test_vision_setup.py +140 -0
- watch_skill-1.2.0/tests/index/test_arabic.py +117 -0
- watch_skill-1.2.0/tests/index/test_frame_persistence.py +110 -0
- watch_skill-1.2.0/tests/index/test_i18n.py +161 -0
- watch_skill-1.2.0/tests/index/test_i18n_v07.py +203 -0
- watch_skill-1.2.0/tests/index/test_index.py +303 -0
- watch_skill-1.2.0/tests/integrations/test_adapters.py +120 -0
- watch_skill-1.2.0/tests/lessons/test_eval_report.py +112 -0
- watch_skill-1.2.0/tests/lessons/test_lessons.py +261 -0
- watch_skill-1.2.0/tests/library/test_library.py +198 -0
- watch_skill-1.2.0/tests/loop/test_capture.py +56 -0
- watch_skill-1.2.0/tests/loop/test_describe_critic.py +269 -0
- watch_skill-1.2.0/tests/loop/test_framework.py +298 -0
- watch_skill-1.2.0/tests/loop/test_loop.py +246 -0
- watch_skill-1.2.0/tests/loop/test_webhook.py +113 -0
- watch_skill-1.2.0/tests/perceive/test_budget.py +58 -0
- watch_skill-1.2.0/tests/perceive/test_flow_cues.py +35 -0
- watch_skill-1.2.0/tests/perceive/test_ocr_backends.py +118 -0
- watch_skill-1.2.0/tests/perceive/test_perceive.py +176 -0
- watch_skill-1.2.0/tests/surfaces/test_api.py +100 -0
- watch_skill-1.2.0/tests/surfaces/test_cli_compat.py +77 -0
- watch_skill-1.2.0/tests/surfaces/test_cli_encoding.py +57 -0
- watch_skill-1.2.0/tests/surfaces/test_mcp_server.py +126 -0
- watch_skill-1.2.0/tests/test_agent_docs.py +102 -0
- watch_skill-1.2.0/tests/test_batch.py +127 -0
- watch_skill-1.2.0/tests/test_config.py +112 -0
- watch_skill-1.2.0/tests/test_error_fix_audit.py +160 -0
- watch_skill-1.2.0/tests/test_errors.py +43 -0
- watch_skill-1.2.0/tests/test_input_validation.py +53 -0
- watch_skill-1.2.0/tests/test_jobs.py +69 -0
- watch_skill-1.2.0/tests/test_plugin_packaging.py +155 -0
- watch_skill-1.2.0/tests/test_skills_library.py +100 -0
- watch_skill-1.2.0/tests/test_viewer.py +134 -0
- watch_skill-1.2.0/tests/test_watch_pipeline.py +61 -0
- watch_skill-1.2.0/tests/transcribe/test_diarize.py +106 -0
- watch_skill-1.2.0/tests/transcribe/test_privacy.py +65 -0
- watch_skill-1.2.0/tests/transcribe/test_transcribe.py +110 -0
- watch_skill-1.2.0/tests/vision/test_local_health.py +131 -0
- watch_skill-1.2.0/tests/vision/test_openai_compatible_providers.py +111 -0
- watch_skill-1.2.0/tests/vision/test_vision.py +321 -0
- watch_skill-1.2.0/uv.lock +7025 -0
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "watch-skill",
|
|
3
|
+
"owner": {
|
|
4
|
+
"name": "oxbshw",
|
|
5
|
+
"url": "https://github.com/oxbshw"
|
|
6
|
+
},
|
|
7
|
+
"metadata": {
|
|
8
|
+
"description": "Give any AI agent the ability to actually watch video.",
|
|
9
|
+
"version": "1.2.0"
|
|
10
|
+
},
|
|
11
|
+
"plugins": [
|
|
12
|
+
{
|
|
13
|
+
"name": "watch-skill",
|
|
14
|
+
"source": ".",
|
|
15
|
+
"description": "Watch, index, ask, and iterate on video (THE LOOP). Persistent index, self-healing answers, and a local lessons store — Windows/Linux/macOS.",
|
|
16
|
+
"version": "1.2.0",
|
|
17
|
+
"author": {
|
|
18
|
+
"name": "Watch Skill contributors",
|
|
19
|
+
"url": "https://github.com/oxbshw/watch-skill"
|
|
20
|
+
},
|
|
21
|
+
"homepage": "https://github.com/oxbshw/watch-skill",
|
|
22
|
+
"license": "MIT",
|
|
23
|
+
"keywords": [
|
|
24
|
+
"video",
|
|
25
|
+
"vision",
|
|
26
|
+
"mcp",
|
|
27
|
+
"skill",
|
|
28
|
+
"transcription",
|
|
29
|
+
"ocr",
|
|
30
|
+
"the-loop",
|
|
31
|
+
"youtube"
|
|
32
|
+
]
|
|
33
|
+
}
|
|
34
|
+
]
|
|
35
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json.schemastore.org/claude-code-plugin-manifest.json",
|
|
3
|
+
"name": "watch-skill",
|
|
4
|
+
"displayName": "Watch Skill",
|
|
5
|
+
"version": "1.2.0",
|
|
6
|
+
"description": "Give the agent a video input via Watch Skill: watch, index, ask, and iterate (THE LOOP).",
|
|
7
|
+
"author": {
|
|
8
|
+
"name": "Watch Skill contributors",
|
|
9
|
+
"url": "https://github.com/oxbshw/watch-skill"
|
|
10
|
+
},
|
|
11
|
+
"homepage": "https://github.com/oxbshw/watch-skill",
|
|
12
|
+
"repository": "https://github.com/oxbshw/watch-skill",
|
|
13
|
+
"license": "MIT",
|
|
14
|
+
"keywords": ["video", "vision", "mcp", "transcription", "ocr", "the-loop"],
|
|
15
|
+
"skills": "./skills/",
|
|
16
|
+
"commands": "./commands/",
|
|
17
|
+
"mcpServers": "./.mcp.json"
|
|
18
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
name: Bug report
|
|
2
|
+
description: Something broke
|
|
3
|
+
labels: [bug]
|
|
4
|
+
body:
|
|
5
|
+
- type: textarea
|
|
6
|
+
id: what
|
|
7
|
+
attributes:
|
|
8
|
+
label: What happened?
|
|
9
|
+
description: What did you run, what did you expect, what did you get?
|
|
10
|
+
placeholder: |
|
|
11
|
+
watch-skill watch "https://..." failed with ...
|
|
12
|
+
validations:
|
|
13
|
+
required: true
|
|
14
|
+
- type: textarea
|
|
15
|
+
id: doctor
|
|
16
|
+
attributes:
|
|
17
|
+
label: Doctor report
|
|
18
|
+
description: Paste the FULL output of `watch-skill doctor --json` (it contains no secrets — keys are reported as present/absent only).
|
|
19
|
+
render: json
|
|
20
|
+
validations:
|
|
21
|
+
required: true
|
|
22
|
+
- type: textarea
|
|
23
|
+
id: error
|
|
24
|
+
attributes:
|
|
25
|
+
label: Structured error
|
|
26
|
+
description: If a tool/CLI call failed, paste the `{error, message, fix, details}` JSON it printed.
|
|
27
|
+
render: json
|
|
28
|
+
- type: input
|
|
29
|
+
id: agent
|
|
30
|
+
attributes:
|
|
31
|
+
label: Agent / surface
|
|
32
|
+
placeholder: Claude Code MCP / Cursor / CLI / REST / Python
|
|
33
|
+
- type: input
|
|
34
|
+
id: source
|
|
35
|
+
attributes:
|
|
36
|
+
label: Video source (if shareable)
|
|
37
|
+
placeholder: URL or "local file, 34 min, mp4"
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
name: Feature request
|
|
2
|
+
description: Something Watch Skill should do
|
|
3
|
+
labels: [enhancement]
|
|
4
|
+
body:
|
|
5
|
+
- type: textarea
|
|
6
|
+
id: problem
|
|
7
|
+
attributes:
|
|
8
|
+
label: The problem
|
|
9
|
+
description: What can't your agent do today? Concrete scenario beats abstract capability.
|
|
10
|
+
validations:
|
|
11
|
+
required: true
|
|
12
|
+
- type: textarea
|
|
13
|
+
id: shape
|
|
14
|
+
attributes:
|
|
15
|
+
label: What would it look like?
|
|
16
|
+
description: A CLI/MCP call you wish existed, or expected output.
|
|
17
|
+
- type: dropdown
|
|
18
|
+
id: area
|
|
19
|
+
attributes:
|
|
20
|
+
label: Area
|
|
21
|
+
options: [acquire, perceive, transcribe, index/search, vision providers, the Loop, surfaces (MCP/CLI/REST), docs]
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
name: New agent support
|
|
2
|
+
description: Get Watch Skill working in another AI agent / IDE
|
|
3
|
+
labels: [agent-support]
|
|
4
|
+
body:
|
|
5
|
+
- type: input
|
|
6
|
+
id: agent
|
|
7
|
+
attributes:
|
|
8
|
+
label: Agent name + version
|
|
9
|
+
placeholder: e.g. Zed 0.160, Goose 1.2
|
|
10
|
+
validations:
|
|
11
|
+
required: true
|
|
12
|
+
- type: textarea
|
|
13
|
+
id: mcp
|
|
14
|
+
attributes:
|
|
15
|
+
label: How does it speak MCP (or HTTP)?
|
|
16
|
+
description: Link its MCP config docs. If it has no MCP, the REST API + OpenAPI spec is the path.
|
|
17
|
+
validations:
|
|
18
|
+
required: true
|
|
19
|
+
- type: textarea
|
|
20
|
+
id: tried
|
|
21
|
+
attributes:
|
|
22
|
+
label: What you tried
|
|
23
|
+
description: Config you wrote, what connected, what failed. If it worked — paste the working config and we'll add a docs/agents page crediting you.
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
## What
|
|
2
|
+
|
|
3
|
+
<!-- One paragraph: what changes and why. Link the issue if there is one. -->
|
|
4
|
+
|
|
5
|
+
## Checklist
|
|
6
|
+
|
|
7
|
+
- [ ] `uv run pytest -q -m "not network"` is green locally
|
|
8
|
+
- [ ] New behavior has a test (live bugs get a regression test *before* the fix counts)
|
|
9
|
+
- [ ] Errors raised are structured (`code` + `message` + actionable `fix`)
|
|
10
|
+
- [ ] `core/` does not import `surfaces/`
|
|
11
|
+
- [ ] Windows-safe paths (`pathlib`, spaces-in-paths tolerated)
|
|
12
|
+
- [ ] Privacy invariants untouched (video never leaves the machine; cloud STT stays opt-in) — or SECURITY.md updated with reviewer sign-off
|
|
13
|
+
- [ ] Non-obvious decisions logged in `docs/DECISIONS.md`
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
version: 2
|
|
2
|
+
updates:
|
|
3
|
+
# Actions pinned by major tag still move underneath you; a compromised or
|
|
4
|
+
# abandoned action is the shortest path into a release pipeline that can
|
|
5
|
+
# publish to PyPI.
|
|
6
|
+
- package-ecosystem: github-actions
|
|
7
|
+
directory: "/"
|
|
8
|
+
schedule:
|
|
9
|
+
interval: weekly
|
|
10
|
+
day: monday
|
|
11
|
+
commit-message:
|
|
12
|
+
prefix: "chore(ci)"
|
|
13
|
+
groups:
|
|
14
|
+
actions:
|
|
15
|
+
patterns: ["*"]
|
|
16
|
+
|
|
17
|
+
- package-ecosystem: pip
|
|
18
|
+
directory: "/"
|
|
19
|
+
schedule:
|
|
20
|
+
interval: weekly
|
|
21
|
+
day: monday
|
|
22
|
+
commit-message:
|
|
23
|
+
prefix: "chore(deps)"
|
|
24
|
+
# One PR per week for the routine bumps; anything that breaks an API gets
|
|
25
|
+
# its own so the failure is readable.
|
|
26
|
+
groups:
|
|
27
|
+
minor-and-patch:
|
|
28
|
+
patterns: ["*"]
|
|
29
|
+
update-types: ["minor", "patch"]
|
|
30
|
+
ignore:
|
|
31
|
+
# Pinned deliberately in pyproject with a reason next to each bound.
|
|
32
|
+
- dependency-name: rich
|
|
33
|
+
update-types: ["version-update:semver-major"]
|
|
34
|
+
|
|
35
|
+
- package-ecosystem: docker
|
|
36
|
+
directory: "/"
|
|
37
|
+
schedule:
|
|
38
|
+
interval: weekly
|
|
39
|
+
day: monday
|
|
40
|
+
commit-message:
|
|
41
|
+
prefix: "chore(docker)"
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
pull_request:
|
|
7
|
+
|
|
8
|
+
jobs:
|
|
9
|
+
lint:
|
|
10
|
+
runs-on: ubuntu-latest
|
|
11
|
+
steps:
|
|
12
|
+
- uses: actions/checkout@v4
|
|
13
|
+
- uses: astral-sh/setup-uv@v5
|
|
14
|
+
# the project-pinned ruff, not a floating uvx latest: local and CI
|
|
15
|
+
# must agree on import classification
|
|
16
|
+
- run: uv run --only-group dev ruff check .
|
|
17
|
+
|
|
18
|
+
test:
|
|
19
|
+
strategy:
|
|
20
|
+
fail-fast: false
|
|
21
|
+
matrix:
|
|
22
|
+
os: [ubuntu-latest, windows-latest]
|
|
23
|
+
python: ["3.11", "3.12"]
|
|
24
|
+
runs-on: ${{ matrix.os }}
|
|
25
|
+
steps:
|
|
26
|
+
- uses: actions/checkout@v4
|
|
27
|
+
- uses: astral-sh/setup-uv@v5
|
|
28
|
+
with:
|
|
29
|
+
python-version: ${{ matrix.python }}
|
|
30
|
+
- name: Install ffmpeg (Linux)
|
|
31
|
+
if: runner.os == 'Linux'
|
|
32
|
+
run: sudo apt-get update && sudo apt-get install -y ffmpeg
|
|
33
|
+
- name: Install ffmpeg (Windows)
|
|
34
|
+
if: runner.os == 'Windows'
|
|
35
|
+
run: choco install ffmpeg -y --no-progress
|
|
36
|
+
- name: Sync dependencies
|
|
37
|
+
run: uv sync --all-extras
|
|
38
|
+
- name: Cache Playwright browsers
|
|
39
|
+
uses: actions/cache@v4
|
|
40
|
+
with:
|
|
41
|
+
path: |
|
|
42
|
+
~/.cache/ms-playwright
|
|
43
|
+
~\AppData\Local\ms-playwright
|
|
44
|
+
key: playwright-${{ runner.os }}-${{ hashFiles('uv.lock') }}
|
|
45
|
+
- name: Cache embedding / whisper models
|
|
46
|
+
uses: actions/cache@v4
|
|
47
|
+
with:
|
|
48
|
+
path: |
|
|
49
|
+
~/.cache/huggingface
|
|
50
|
+
ci-model-cache
|
|
51
|
+
key: models-${{ runner.os }}-${{ hashFiles('uv.lock') }}
|
|
52
|
+
- name: Install Playwright browser + recording ffmpeg (Linux)
|
|
53
|
+
if: runner.os == 'Linux'
|
|
54
|
+
run: uv run playwright install --with-deps chromium ffmpeg
|
|
55
|
+
- name: Install Playwright browser + recording ffmpeg (Windows)
|
|
56
|
+
if: runner.os == 'Windows'
|
|
57
|
+
run: uv run playwright install chromium ffmpeg
|
|
58
|
+
- name: Run the offline suite
|
|
59
|
+
run: uv run pytest -q -m "not network" --timeout 300
|
|
60
|
+
env:
|
|
61
|
+
# keep CI hermetic: no live vision backends, no cloud keys
|
|
62
|
+
WATCHSKILL_VISION_CHEAP_PROVIDER: anthropic
|
|
63
|
+
WATCHSKILL_VISION_STRONG_PROVIDER: anthropic
|
|
64
|
+
FASTEMBED_CACHE_PATH: ci-model-cache
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
name: Docker
|
|
2
|
+
|
|
3
|
+
# Publishes ghcr.io/oxbshw/watch-skill. GitHub Container Registry needs no
|
|
4
|
+
# account setup and no secret — GITHUB_TOKEN is enough.
|
|
5
|
+
|
|
6
|
+
on:
|
|
7
|
+
push:
|
|
8
|
+
branches: [main]
|
|
9
|
+
paths:
|
|
10
|
+
- "Dockerfile"
|
|
11
|
+
- "pyproject.toml"
|
|
12
|
+
- "uv.lock"
|
|
13
|
+
- "src/**"
|
|
14
|
+
- ".github/workflows/docker.yml"
|
|
15
|
+
release:
|
|
16
|
+
types: [published]
|
|
17
|
+
pull_request:
|
|
18
|
+
paths:
|
|
19
|
+
- "Dockerfile"
|
|
20
|
+
- ".github/workflows/docker.yml"
|
|
21
|
+
workflow_dispatch:
|
|
22
|
+
|
|
23
|
+
env:
|
|
24
|
+
IMAGE: ghcr.io/${{ github.repository }}
|
|
25
|
+
|
|
26
|
+
jobs:
|
|
27
|
+
build:
|
|
28
|
+
runs-on: ubuntu-latest
|
|
29
|
+
permissions:
|
|
30
|
+
contents: read
|
|
31
|
+
packages: write
|
|
32
|
+
id-token: write # OIDC identity the provenance attestation is signed with
|
|
33
|
+
attestations: write
|
|
34
|
+
steps:
|
|
35
|
+
- uses: actions/checkout@v4
|
|
36
|
+
|
|
37
|
+
- uses: docker/setup-buildx-action@v3
|
|
38
|
+
|
|
39
|
+
# Pull requests from forks cannot push; build them anyway so a broken
|
|
40
|
+
# Dockerfile is caught in review rather than on main.
|
|
41
|
+
- name: Log in to ghcr.io
|
|
42
|
+
if: github.event_name != 'pull_request'
|
|
43
|
+
uses: docker/login-action@v3
|
|
44
|
+
with:
|
|
45
|
+
registry: ghcr.io
|
|
46
|
+
username: ${{ github.actor }}
|
|
47
|
+
password: ${{ secrets.GITHUB_TOKEN }}
|
|
48
|
+
|
|
49
|
+
- name: Tags and labels
|
|
50
|
+
id: meta
|
|
51
|
+
uses: docker/metadata-action@v5
|
|
52
|
+
with:
|
|
53
|
+
images: ${{ env.IMAGE }}
|
|
54
|
+
tags: |
|
|
55
|
+
type=ref,event=branch
|
|
56
|
+
type=semver,pattern={{version}}
|
|
57
|
+
type=semver,pattern={{major}}.{{minor}}
|
|
58
|
+
type=raw,value=latest,enable={{is_default_branch}}
|
|
59
|
+
|
|
60
|
+
- name: Build
|
|
61
|
+
uses: docker/build-push-action@v6
|
|
62
|
+
with:
|
|
63
|
+
context: .
|
|
64
|
+
load: true
|
|
65
|
+
tags: ${{ env.IMAGE }}:ci
|
|
66
|
+
cache-from: type=gha
|
|
67
|
+
cache-to: type=gha,mode=max
|
|
68
|
+
|
|
69
|
+
# A pushed image that cannot answer --version is worse than no image.
|
|
70
|
+
- name: The image must run
|
|
71
|
+
run: |
|
|
72
|
+
docker run --rm "${{ env.IMAGE }}:ci" --version
|
|
73
|
+
docker run --rm "${{ env.IMAGE }}:ci" doctor --no-fix --json > doctor.json || true
|
|
74
|
+
cat doctor.json
|
|
75
|
+
docker run --rm "${{ env.IMAGE }}:ci" --help > /dev/null
|
|
76
|
+
|
|
77
|
+
# arm64 matters: Apple Silicon is most of the macOS install base, and
|
|
78
|
+
# running an amd64 image under emulation makes ffmpeg work crawl.
|
|
79
|
+
- name: Set up QEMU for the arm64 build
|
|
80
|
+
if: github.event_name != 'pull_request'
|
|
81
|
+
uses: docker/setup-qemu-action@v3
|
|
82
|
+
|
|
83
|
+
- name: Push
|
|
84
|
+
if: github.event_name != 'pull_request'
|
|
85
|
+
id: push
|
|
86
|
+
uses: docker/build-push-action@v6
|
|
87
|
+
with:
|
|
88
|
+
context: .
|
|
89
|
+
push: true
|
|
90
|
+
platforms: linux/amd64,linux/arm64
|
|
91
|
+
tags: ${{ steps.meta.outputs.tags }}
|
|
92
|
+
labels: ${{ steps.meta.outputs.labels }}
|
|
93
|
+
cache-from: type=gha
|
|
94
|
+
cache-to: type=gha,mode=max
|
|
95
|
+
provenance: mode=max
|
|
96
|
+
sbom: true
|
|
97
|
+
|
|
98
|
+
# Signs the pushed digest with the workflow's own identity, so anyone
|
|
99
|
+
# can verify the image came from this repository and no other.
|
|
100
|
+
- name: Attest the build provenance
|
|
101
|
+
if: github.event_name != 'pull_request'
|
|
102
|
+
uses: actions/attest-build-provenance@v2
|
|
103
|
+
with:
|
|
104
|
+
subject-name: ${{ env.IMAGE }}
|
|
105
|
+
subject-digest: ${{ steps.push.outputs.digest }}
|
|
106
|
+
push-to-registry: true
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
name: Install
|
|
2
|
+
|
|
3
|
+
# The installers used to carry a "written on Windows, unverified elsewhere"
|
|
4
|
+
# disclaimer. This workflow is what replaced it: every push runs the real
|
|
5
|
+
# scripts on a clean runner for each OS we claim to support.
|
|
6
|
+
|
|
7
|
+
on:
|
|
8
|
+
# No path filter on main. A first attempt watched only scripts/ and
|
|
9
|
+
# pyproject.toml, and then the fix that made the installer work on Linux
|
|
10
|
+
# and macOS landed in src/watch_skill/health/ — so the run that would have
|
|
11
|
+
# proved it never fired. What the installer does depends on the engine it
|
|
12
|
+
# installs, so the whole tree is the trigger.
|
|
13
|
+
push:
|
|
14
|
+
branches: [main]
|
|
15
|
+
pull_request:
|
|
16
|
+
paths:
|
|
17
|
+
- "scripts/**"
|
|
18
|
+
- "src/watch_skill/health/**"
|
|
19
|
+
- "pyproject.toml"
|
|
20
|
+
- "uv.lock"
|
|
21
|
+
- ".github/workflows/install.yml"
|
|
22
|
+
schedule:
|
|
23
|
+
# Installers break from the outside — a new uv, a moved ffmpeg package.
|
|
24
|
+
# Catch that on a Monday, not from a bug report.
|
|
25
|
+
- cron: "0 6 * * 1"
|
|
26
|
+
workflow_dispatch:
|
|
27
|
+
|
|
28
|
+
jobs:
|
|
29
|
+
posix:
|
|
30
|
+
name: install.sh (${{ matrix.os }})
|
|
31
|
+
strategy:
|
|
32
|
+
fail-fast: false
|
|
33
|
+
matrix:
|
|
34
|
+
os: [ubuntu-latest, macos-latest]
|
|
35
|
+
runs-on: ${{ matrix.os }}
|
|
36
|
+
steps:
|
|
37
|
+
- uses: actions/checkout@v4
|
|
38
|
+
|
|
39
|
+
- name: Lint the script
|
|
40
|
+
if: runner.os == 'Linux'
|
|
41
|
+
run: shellcheck scripts/install.sh
|
|
42
|
+
|
|
43
|
+
# Run against the checked-out copy rather than curl-ing main, so a PR
|
|
44
|
+
# tests its own changes.
|
|
45
|
+
- name: Run the installer
|
|
46
|
+
run: sh scripts/install.sh
|
|
47
|
+
env:
|
|
48
|
+
WATCHSKILL_HOME: ${{ runner.temp }}/watch-skill
|
|
49
|
+
WATCHSKILL_INSTALL_REF: ${{ github.sha }}
|
|
50
|
+
WATCHSKILL_INSTALL_LOCAL: "1"
|
|
51
|
+
|
|
52
|
+
- name: The CLI must exist and report its version
|
|
53
|
+
run: |
|
|
54
|
+
cd "${{ runner.temp }}/watch-skill"
|
|
55
|
+
uv run watch-skill --version
|
|
56
|
+
uv run watch-skill --help > /dev/null
|
|
57
|
+
|
|
58
|
+
# `|| true` matters: the step runs under `bash -e`, so a failing doctor
|
|
59
|
+
# would abort before the report it just wrote could be printed — which
|
|
60
|
+
# hides the one thing worth seeing.
|
|
61
|
+
- name: Doctor must not fail
|
|
62
|
+
run: |
|
|
63
|
+
cd "${{ runner.temp }}/watch-skill"
|
|
64
|
+
uv run watch-skill doctor --json > doctor.json || true
|
|
65
|
+
cat doctor.json
|
|
66
|
+
python - <<'EOF'
|
|
67
|
+
import json, sys
|
|
68
|
+
report = json.load(open("doctor.json"))
|
|
69
|
+
for check in report["checks"]:
|
|
70
|
+
if check["status"] == "fail":
|
|
71
|
+
print(f"::error::doctor {check['name']}: {check['message']}")
|
|
72
|
+
sys.exit(0 if report["ok"] else 1)
|
|
73
|
+
EOF
|
|
74
|
+
|
|
75
|
+
# `timeout` is GNU coreutils and is not on macOS by default, so the
|
|
76
|
+
# bound is enforced in the shell instead of by an absent binary.
|
|
77
|
+
- name: MCP server must start and answer a handshake
|
|
78
|
+
run: |
|
|
79
|
+
cd "${{ runner.temp }}/watch-skill"
|
|
80
|
+
printf '%s\n' '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2024-11-05","capabilities":{},"clientInfo":{"name":"ci","version":"0"}}}' \
|
|
81
|
+
| uv run watch-skill serve > handshake.txt 2>server.log &
|
|
82
|
+
server=$!
|
|
83
|
+
for _ in $(seq 1 90); do
|
|
84
|
+
grep -q '"result"' handshake.txt && break
|
|
85
|
+
kill -0 "$server" 2>/dev/null || break
|
|
86
|
+
sleep 1
|
|
87
|
+
done
|
|
88
|
+
kill "$server" 2>/dev/null || true
|
|
89
|
+
wait "$server" 2>/dev/null || true
|
|
90
|
+
cat server.log
|
|
91
|
+
grep -q '"result"' handshake.txt
|
|
92
|
+
|
|
93
|
+
windows:
|
|
94
|
+
name: install.ps1 (windows-latest)
|
|
95
|
+
runs-on: windows-latest
|
|
96
|
+
steps:
|
|
97
|
+
- uses: actions/checkout@v4
|
|
98
|
+
|
|
99
|
+
- name: Run the installer
|
|
100
|
+
shell: pwsh
|
|
101
|
+
run: ./scripts/install.ps1
|
|
102
|
+
env:
|
|
103
|
+
WATCHSKILL_HOME: ${{ runner.temp }}\watch-skill
|
|
104
|
+
WATCHSKILL_INSTALL_REF: ${{ github.sha }}
|
|
105
|
+
WATCHSKILL_INSTALL_LOCAL: "1"
|
|
106
|
+
|
|
107
|
+
- name: The CLI must exist and report its version
|
|
108
|
+
shell: pwsh
|
|
109
|
+
run: |
|
|
110
|
+
Set-Location "${{ runner.temp }}\watch-skill"
|
|
111
|
+
uv run watch-skill --version
|
|
112
|
+
uv run watch-skill --help | Out-Null
|
|
113
|
+
|
|
114
|
+
- name: Doctor must not fail
|
|
115
|
+
shell: pwsh
|
|
116
|
+
run: |
|
|
117
|
+
Set-Location "${{ runner.temp }}\watch-skill"
|
|
118
|
+
uv run watch-skill doctor --json | Out-File -Encoding utf8 doctor.json
|
|
119
|
+
Get-Content doctor.json
|
|
120
|
+
$report = Get-Content doctor.json -Raw | ConvertFrom-Json
|
|
121
|
+
if (-not $report.ok) { exit 1 }
|
|
122
|
+
|
|
123
|
+
# Proves the claim the README makes about zero-install usage. Runs only
|
|
124
|
+
# after a release has actually landed on PyPI.
|
|
125
|
+
uvx:
|
|
126
|
+
name: uvx from PyPI
|
|
127
|
+
if: github.event_name == 'schedule' || github.event_name == 'workflow_dispatch'
|
|
128
|
+
strategy:
|
|
129
|
+
fail-fast: false
|
|
130
|
+
matrix:
|
|
131
|
+
os: [ubuntu-latest, macos-latest, windows-latest]
|
|
132
|
+
runs-on: ${{ matrix.os }}
|
|
133
|
+
steps:
|
|
134
|
+
- uses: astral-sh/setup-uv@v5
|
|
135
|
+
with:
|
|
136
|
+
python-version: "3.11"
|
|
137
|
+
- name: uvx must resolve and run the published package
|
|
138
|
+
run: uvx --from "watch-skill[standard]" watch-skill --version
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
name: Integration (network)
|
|
2
|
+
|
|
3
|
+
# Manual trigger only: hits YouTube/TikTok and (optionally) vision APIs.
|
|
4
|
+
# Run before releases; the offline CI suite is the merge gate.
|
|
5
|
+
on:
|
|
6
|
+
workflow_dispatch:
|
|
7
|
+
inputs:
|
|
8
|
+
skip_stages:
|
|
9
|
+
description: "Comma list for golden_path --skip (e.g. tiktok,loop)"
|
|
10
|
+
default: "loop"
|
|
11
|
+
|
|
12
|
+
jobs:
|
|
13
|
+
golden-path:
|
|
14
|
+
runs-on: ubuntu-latest
|
|
15
|
+
steps:
|
|
16
|
+
- uses: actions/checkout@v4
|
|
17
|
+
- uses: astral-sh/setup-uv@v5
|
|
18
|
+
with:
|
|
19
|
+
python-version: "3.11"
|
|
20
|
+
- run: sudo apt-get update && sudo apt-get install -y ffmpeg
|
|
21
|
+
- run: uv sync --all-extras
|
|
22
|
+
- name: Doctor
|
|
23
|
+
run: uv run watch-skill doctor
|
|
24
|
+
- name: Network-marked tests
|
|
25
|
+
run: uv run pytest -q -m network --timeout 600 || true
|
|
26
|
+
- name: Golden path (requires a vision key secret)
|
|
27
|
+
if: ${{ vars.WATCHSKILL_HAS_VISION_KEY == 'true' }}
|
|
28
|
+
env:
|
|
29
|
+
WATCHSKILL_OPENROUTER_API_KEY: ${{ secrets.WATCHSKILL_OPENROUTER_API_KEY }}
|
|
30
|
+
run: uv run python scripts/golden_path.py --skip "${{ inputs.skip_stages }}"
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
name: Release
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
tags: ["v*"]
|
|
6
|
+
|
|
7
|
+
permissions:
|
|
8
|
+
contents: write
|
|
9
|
+
|
|
10
|
+
jobs:
|
|
11
|
+
release:
|
|
12
|
+
runs-on: ubuntu-latest
|
|
13
|
+
steps:
|
|
14
|
+
- uses: actions/checkout@v4
|
|
15
|
+
- uses: astral-sh/setup-uv@v5
|
|
16
|
+
with:
|
|
17
|
+
python-version: "3.11"
|
|
18
|
+
- run: sudo apt-get update && sudo apt-get install -y ffmpeg
|
|
19
|
+
- run: uv sync --all-extras
|
|
20
|
+
- name: Install Playwright browser + recording ffmpeg
|
|
21
|
+
run: uv run playwright install --with-deps chromium ffmpeg
|
|
22
|
+
- name: Offline suite must be green
|
|
23
|
+
run: uv run pytest -q -m "not network" --timeout 300
|
|
24
|
+
env:
|
|
25
|
+
WATCHSKILL_VISION_CHEAP_PROVIDER: anthropic
|
|
26
|
+
WATCHSKILL_VISION_STRONG_PROVIDER: anthropic
|
|
27
|
+
- name: Build wheel + sdist
|
|
28
|
+
run: uv build
|
|
29
|
+
- name: Build the Claude Skill bundle
|
|
30
|
+
run: |
|
|
31
|
+
uv run python - <<'EOF'
|
|
32
|
+
import zipfile
|
|
33
|
+
from pathlib import Path
|
|
34
|
+
root = Path("skills/watch")
|
|
35
|
+
Path("dist").mkdir(exist_ok=True)
|
|
36
|
+
with zipfile.ZipFile("dist/watch-skill.skill", "w", zipfile.ZIP_DEFLATED) as z:
|
|
37
|
+
for f in root.rglob("*"):
|
|
38
|
+
if f.is_file():
|
|
39
|
+
z.write(f, f.relative_to(root))
|
|
40
|
+
EOF
|
|
41
|
+
- name: Extract changelog section
|
|
42
|
+
run: |
|
|
43
|
+
awk '/^## /{n++} n==1' CHANGELOG.md > release-notes.md || cp CHANGELOG.md release-notes.md
|
|
44
|
+
- name: GitHub Release
|
|
45
|
+
uses: softprops/action-gh-release@v2
|
|
46
|
+
with:
|
|
47
|
+
body_path: release-notes.md
|
|
48
|
+
files: |
|
|
49
|
+
dist/*.whl
|
|
50
|
+
dist/*.tar.gz
|
|
51
|
+
dist/watch-skill.skill
|
|
52
|
+
- name: Keep the built distributions for the publish job
|
|
53
|
+
uses: actions/upload-artifact@v4
|
|
54
|
+
with:
|
|
55
|
+
name: dist
|
|
56
|
+
path: |
|
|
57
|
+
dist/*.whl
|
|
58
|
+
dist/*.tar.gz
|
|
59
|
+
|
|
60
|
+
# Separate job: trusted publishing needs its own id-token, and a failed
|
|
61
|
+
# upload must not be able to take the GitHub Release down with it.
|
|
62
|
+
pypi:
|
|
63
|
+
needs: release
|
|
64
|
+
runs-on: ubuntu-latest
|
|
65
|
+
environment: pypi
|
|
66
|
+
permissions:
|
|
67
|
+
id-token: write
|
|
68
|
+
steps:
|
|
69
|
+
- uses: actions/download-artifact@v4
|
|
70
|
+
with:
|
|
71
|
+
name: dist
|
|
72
|
+
path: dist
|
|
73
|
+
# No token: PyPI verifies this workflow's OIDC identity. The publisher
|
|
74
|
+
# is configured once at
|
|
75
|
+
# https://pypi.org/manage/project/watch-skill/settings/publishing/
|
|
76
|
+
#
|
|
77
|
+
# The official action rather than `uv publish` because it also signs
|
|
78
|
+
# PEP 740 attestations, so an installer can verify a wheel was built
|
|
79
|
+
# by this workflow in this repository and not swapped en route.
|
|
80
|
+
- name: Publish to PyPI
|
|
81
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
|
82
|
+
with:
|
|
83
|
+
attestations: true
|
|
84
|
+
print-hash: true
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# Self-managed portable binaries (bootstrapped by `watch-skill doctor`)
|
|
2
|
+
bin/
|
|
3
|
+
|
|
4
|
+
# Python
|
|
5
|
+
__pycache__/
|
|
6
|
+
*.py[cod]
|
|
7
|
+
*.egg-info/
|
|
8
|
+
.venv/
|
|
9
|
+
dist/
|
|
10
|
+
build/
|
|
11
|
+
|
|
12
|
+
# Environment / secrets
|
|
13
|
+
.env
|
|
14
|
+
.env.*
|
|
15
|
+
|
|
16
|
+
# Runtime data (never commit user data)
|
|
17
|
+
*.db
|
|
18
|
+
*.sqlite3
|
|
19
|
+
.watch-skill/
|
|
20
|
+
|
|
21
|
+
# Test / tool caches
|
|
22
|
+
.pytest_cache/
|
|
23
|
+
.ruff_cache/
|
|
24
|
+
.coverage
|
|
25
|
+
# pytest's summary line does not print in this shell, so counts get read from
|
|
26
|
+
# a junit file — scratch output, never part of the tree
|
|
27
|
+
.pytest-junit.xml
|
|
28
|
+
*.junit.xml
|
|
29
|
+
|
|
30
|
+
# OS noise
|
|
31
|
+
Thumbs.db
|
|
32
|
+
Desktop.ini
|
|
33
|
+
.DS_Store
|
|
34
|
+
|
|
35
|
+
# Local assistant / workspace artifacts (never part of the product)
|
|
36
|
+
PROGRESS.md
|
|
37
|
+
CLAUDE.md
|
|
38
|
+
.claude/
|
|
39
|
+
AGENTS.local.md
|
|
40
|
+
*_REPORT.md
|
|
41
|
+
*.session.md
|
|
42
|
+
issues_export.local.json
|
|
43
|
+
|
|
44
|
+
# Internal notes (never tracked)
|
|
45
|
+
notes.local/
|
|
46
|
+
t.xml
|
|
47
|
+
.agents/
|