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.
Files changed (337) hide show
  1. watch_skill-1.2.0/.claude-plugin/marketplace.json +35 -0
  2. watch_skill-1.2.0/.claude-plugin/plugin.json +18 -0
  3. watch_skill-1.2.0/.gitattributes +5 -0
  4. watch_skill-1.2.0/.github/CODEOWNERS +3 -0
  5. watch_skill-1.2.0/.github/FUNDING.yml +4 -0
  6. watch_skill-1.2.0/.github/ISSUE_TEMPLATE/bug_report.yml +37 -0
  7. watch_skill-1.2.0/.github/ISSUE_TEMPLATE/feature_request.yml +21 -0
  8. watch_skill-1.2.0/.github/ISSUE_TEMPLATE/new_agent_support.yml +23 -0
  9. watch_skill-1.2.0/.github/PULL_REQUEST_TEMPLATE.md +13 -0
  10. watch_skill-1.2.0/.github/dependabot.yml +41 -0
  11. watch_skill-1.2.0/.github/workflows/ci.yml +64 -0
  12. watch_skill-1.2.0/.github/workflows/docker.yml +106 -0
  13. watch_skill-1.2.0/.github/workflows/install.yml +138 -0
  14. watch_skill-1.2.0/.github/workflows/integration.yml +30 -0
  15. watch_skill-1.2.0/.github/workflows/release.yml +84 -0
  16. watch_skill-1.2.0/.gitignore +47 -0
  17. watch_skill-1.2.0/.mcp.json +9 -0
  18. watch_skill-1.2.0/.python-version +1 -0
  19. watch_skill-1.2.0/AGENTS.md +76 -0
  20. watch_skill-1.2.0/CHANGELOG.md +478 -0
  21. watch_skill-1.2.0/CONTRIBUTING.md +97 -0
  22. watch_skill-1.2.0/Dockerfile +49 -0
  23. watch_skill-1.2.0/LICENSE +21 -0
  24. watch_skill-1.2.0/PKG-INFO +410 -0
  25. watch_skill-1.2.0/README.md +323 -0
  26. watch_skill-1.2.0/SECURITY.md +65 -0
  27. watch_skill-1.2.0/adapters/agents-md/AGENTS.md +84 -0
  28. watch_skill-1.2.0/benchmarks/cost/README.md +23 -0
  29. watch_skill-1.2.0/benchmarks/cost/RESULTS.md +16 -0
  30. watch_skill-1.2.0/benchmarks/cost/run_cost_benchmark.py +214 -0
  31. watch_skill-1.2.0/benchmarks/perception/README.md +40 -0
  32. watch_skill-1.2.0/benchmarks/perception/RESULTS.md +29 -0
  33. watch_skill-1.2.0/benchmarks/perception/fixtures/arabic_rtl.png +0 -0
  34. watch_skill-1.2.0/benchmarks/perception/fixtures/cjk.png +0 -0
  35. watch_skill-1.2.0/benchmarks/perception/fixtures/fixtures.json +26 -0
  36. watch_skill-1.2.0/benchmarks/perception/fixtures/mixed_script.png +0 -0
  37. watch_skill-1.2.0/benchmarks/perception/fixtures/screen_text.png +0 -0
  38. watch_skill-1.2.0/benchmarks/perception/fixtures/sea_lao.png +0 -0
  39. watch_skill-1.2.0/benchmarks/perception/fixtures/subtitles.png +0 -0
  40. watch_skill-1.2.0/benchmarks/perception/make_fixtures.py +117 -0
  41. watch_skill-1.2.0/commands/setup-watch-skill.md +100 -0
  42. watch_skill-1.2.0/docs/DECISIONS.md +211 -0
  43. watch_skill-1.2.0/docs/README.md +63 -0
  44. watch_skill-1.2.0/docs/ROADMAP.md +69 -0
  45. watch_skill-1.2.0/docs/agents/README.md +99 -0
  46. watch_skill-1.2.0/docs/agents/agent-zero.md +66 -0
  47. watch_skill-1.2.0/docs/agents/claude-code.md +61 -0
  48. watch_skill-1.2.0/docs/agents/claude-desktop.md +57 -0
  49. watch_skill-1.2.0/docs/agents/cline.md +58 -0
  50. watch_skill-1.2.0/docs/agents/codex-cli.md +56 -0
  51. watch_skill-1.2.0/docs/agents/continue.md +60 -0
  52. watch_skill-1.2.0/docs/agents/cursor.md +63 -0
  53. watch_skill-1.2.0/docs/agents/frameworks.md +134 -0
  54. watch_skill-1.2.0/docs/agents/gemini-cli.md +56 -0
  55. watch_skill-1.2.0/docs/agents/github-copilot-cli.md +66 -0
  56. watch_skill-1.2.0/docs/agents/goose.md +67 -0
  57. watch_skill-1.2.0/docs/agents/hermes.md +54 -0
  58. watch_skill-1.2.0/docs/agents/kilocode.md +67 -0
  59. watch_skill-1.2.0/docs/agents/kimi-code.md +64 -0
  60. watch_skill-1.2.0/docs/agents/openclaw.md +56 -0
  61. watch_skill-1.2.0/docs/agents/opencode.md +63 -0
  62. watch_skill-1.2.0/docs/agents/openhands.md +62 -0
  63. watch_skill-1.2.0/docs/agents/pi.md +57 -0
  64. watch_skill-1.2.0/docs/agents/qodo.md +72 -0
  65. watch_skill-1.2.0/docs/agents/qwen-code.md +65 -0
  66. watch_skill-1.2.0/docs/agents/roo-code.md +61 -0
  67. watch_skill-1.2.0/docs/agents/vscode.md +57 -0
  68. watch_skill-1.2.0/docs/agents/windsurf.md +55 -0
  69. watch_skill-1.2.0/docs/agents/zed.md +71 -0
  70. watch_skill-1.2.0/docs/architecture.md +273 -0
  71. watch_skill-1.2.0/docs/assets/agents/README.md +32 -0
  72. watch_skill-1.2.0/docs/assets/agents/agent-zero.webp +0 -0
  73. watch_skill-1.2.0/docs/assets/agents/claude-code.webp +0 -0
  74. watch_skill-1.2.0/docs/assets/agents/claude-desktop.webp +0 -0
  75. watch_skill-1.2.0/docs/assets/agents/cline.webp +0 -0
  76. watch_skill-1.2.0/docs/assets/agents/codex-cli.webp +0 -0
  77. watch_skill-1.2.0/docs/assets/agents/cursor.webp +0 -0
  78. watch_skill-1.2.0/docs/assets/agents/frameworks.webp +0 -0
  79. watch_skill-1.2.0/docs/assets/agents/gemini-cli.webp +0 -0
  80. watch_skill-1.2.0/docs/assets/agents/github-copilot-cli.webp +0 -0
  81. watch_skill-1.2.0/docs/assets/agents/goose.webp +0 -0
  82. watch_skill-1.2.0/docs/assets/agents/hermes.webp +0 -0
  83. watch_skill-1.2.0/docs/assets/agents/kilocode.webp +0 -0
  84. watch_skill-1.2.0/docs/assets/agents/kimi-code.webp +0 -0
  85. watch_skill-1.2.0/docs/assets/agents/openclaw.webp +0 -0
  86. watch_skill-1.2.0/docs/assets/agents/opencode.webp +0 -0
  87. watch_skill-1.2.0/docs/assets/agents/openhands.webp +0 -0
  88. watch_skill-1.2.0/docs/assets/agents/pi.webp +0 -0
  89. watch_skill-1.2.0/docs/assets/agents/qodo.webp +0 -0
  90. watch_skill-1.2.0/docs/assets/agents/qwen-code.webp +0 -0
  91. watch_skill-1.2.0/docs/assets/agents/vscode.webp +0 -0
  92. watch_skill-1.2.0/docs/assets/agents/windsurf.webp +0 -0
  93. watch_skill-1.2.0/docs/assets/lessons_loop.gif +0 -0
  94. watch_skill-1.2.0/docs/assets/loop_before_after.gif +0 -0
  95. watch_skill-1.2.0/docs/assets/savings_meter.png +0 -0
  96. watch_skill-1.2.0/docs/assets/watch-skill-hero.webp +0 -0
  97. watch_skill-1.2.0/docs/comparison.md +116 -0
  98. watch_skill-1.2.0/docs/configuration.md +386 -0
  99. watch_skill-1.2.0/docs/cost.md +71 -0
  100. watch_skill-1.2.0/docs/getting-started.md +135 -0
  101. watch_skill-1.2.0/docs/guides/arabic-in-arabic-out.md +84 -0
  102. watch_skill-1.2.0/docs/guides/how-it-improves-itself.md +75 -0
  103. watch_skill-1.2.0/docs/guides/lessons-and-savings.md +110 -0
  104. watch_skill-1.2.0/docs/guides/the-loop.md +104 -0
  105. watch_skill-1.2.0/docs/guides/youtube-analysis.md +100 -0
  106. watch_skill-1.2.0/docs/migrate-from-claude-video.md +117 -0
  107. watch_skill-1.2.0/docs/packs/README.md +19 -0
  108. watch_skill-1.2.0/docs/packs/agent-self-verification.md +46 -0
  109. watch_skill-1.2.0/docs/packs/browser-agent-verification.md +53 -0
  110. watch_skill-1.2.0/docs/packs/content-creators.md +47 -0
  111. watch_skill-1.2.0/docs/packs/learning-research.md +41 -0
  112. watch_skill-1.2.0/docs/packs/meetings-lectures.md +46 -0
  113. watch_skill-1.2.0/docs/packs/monitoring-ops.md +60 -0
  114. watch_skill-1.2.0/docs/packs/qa-bug-hunting.md +44 -0
  115. watch_skill-1.2.0/docs/tools/README.md +371 -0
  116. watch_skill-1.2.0/docs/troubleshooting.md +130 -0
  117. watch_skill-1.2.0/examples/01-watch-and-ask/README.md +80 -0
  118. watch_skill-1.2.0/examples/01-watch-and-ask/watch_and_ask.py +45 -0
  119. watch_skill-1.2.0/examples/02-focused-moment/README.md +49 -0
  120. watch_skill-1.2.0/examples/02-focused-moment/focused_moment.py +51 -0
  121. watch_skill-1.2.0/examples/03-cross-video-search/README.md +42 -0
  122. watch_skill-1.2.0/examples/03-cross-video-search/cross_video_search.py +54 -0
  123. watch_skill-1.2.0/examples/04-ui-loop/README.md +67 -0
  124. watch_skill-1.2.0/examples/04-ui-loop/page_broken.html +34 -0
  125. watch_skill-1.2.0/examples/04-ui-loop/page_fixed.html +33 -0
  126. watch_skill-1.2.0/examples/04-ui-loop/ui_loop.py +127 -0
  127. watch_skill-1.2.0/examples/05-multilingual-arabic/README.md +63 -0
  128. watch_skill-1.2.0/examples/05-multilingual-arabic/ask_in_arabic.py +41 -0
  129. watch_skill-1.2.0/examples/05-multilingual-arabic/index_arabic.py +47 -0
  130. watch_skill-1.2.0/examples/06-agent-integration/README.md +76 -0
  131. watch_skill-1.2.0/examples/06-agent-integration/agent_integration.py +50 -0
  132. watch_skill-1.2.0/examples/07-lessons-and-stats/README.md +68 -0
  133. watch_skill-1.2.0/examples/07-lessons-and-stats/lessons_and_stats.py +70 -0
  134. watch_skill-1.2.0/examples/08-loop-types/README.md +106 -0
  135. watch_skill-1.2.0/examples/08-loop-types/game_broken.html +34 -0
  136. watch_skill-1.2.0/examples/08-loop-types/game_fixed.html +34 -0
  137. watch_skill-1.2.0/examples/08-loop-types/game_loop.py +58 -0
  138. watch_skill-1.2.0/examples/08-loop-types/monitor_loop.py +86 -0
  139. watch_skill-1.2.0/examples/08-loop-types/render_title_card.py +47 -0
  140. watch_skill-1.2.0/examples/08-loop-types/video_gen_loop.py +59 -0
  141. watch_skill-1.2.0/examples/09-framework-adapters/README.md +48 -0
  142. watch_skill-1.2.0/examples/09-framework-adapters/_demo_clip.py +47 -0
  143. watch_skill-1.2.0/examples/09-framework-adapters/crewai_example.py +38 -0
  144. watch_skill-1.2.0/examples/09-framework-adapters/langchain_example.py +39 -0
  145. watch_skill-1.2.0/examples/09-framework-adapters/openai_agents_example.py +48 -0
  146. watch_skill-1.2.0/examples/10-structured-extraction/README.md +57 -0
  147. watch_skill-1.2.0/examples/10-structured-extraction/extraction_demo.py +102 -0
  148. watch_skill-1.2.0/examples/11-batch-mode/README.md +35 -0
  149. watch_skill-1.2.0/examples/11-batch-mode/batch_demo.py +94 -0
  150. watch_skill-1.2.0/examples/12-library-memory/README.md +70 -0
  151. watch_skill-1.2.0/examples/12-library-memory/library_demo.py +119 -0
  152. watch_skill-1.2.0/examples/13-self-improvement/README.md +56 -0
  153. watch_skill-1.2.0/examples/13-self-improvement/self_improvement_demo.py +151 -0
  154. watch_skill-1.2.0/examples/14-browser-verification/README.md +69 -0
  155. watch_skill-1.2.0/examples/14-browser-verification/browser_verification_demo.py +95 -0
  156. watch_skill-1.2.0/examples/14-browser-verification/checkout_buggy.html +50 -0
  157. watch_skill-1.2.0/examples/14-browser-verification/checkout_fixed.html +46 -0
  158. watch_skill-1.2.0/examples/15-private-offline-workflow/README.md +40 -0
  159. watch_skill-1.2.0/examples/15-private-offline-workflow/offline_workflow.py +98 -0
  160. watch_skill-1.2.0/examples/16-shareable-viewer/README.md +27 -0
  161. watch_skill-1.2.0/examples/16-shareable-viewer/export_viewer.py +33 -0
  162. watch_skill-1.2.0/examples/README.md +55 -0
  163. watch_skill-1.2.0/llms.txt +62 -0
  164. watch_skill-1.2.0/pyproject.toml +125 -0
  165. watch_skill-1.2.0/scripts/golden_path.py +229 -0
  166. watch_skill-1.2.0/scripts/install.ps1 +107 -0
  167. watch_skill-1.2.0/scripts/install.sh +107 -0
  168. watch_skill-1.2.0/skills/asking-with-evidence/SKILL.md +59 -0
  169. watch_skill-1.2.0/skills/configuring-vision/SKILL.md +53 -0
  170. watch_skill-1.2.0/skills/extracting-structure/SKILL.md +50 -0
  171. watch_skill-1.2.0/skills/learning-from-mistakes/SKILL.md +45 -0
  172. watch_skill-1.2.0/skills/recovering-from-errors/SKILL.md +41 -0
  173. watch_skill-1.2.0/skills/sharing-results/SKILL.md +41 -0
  174. watch_skill-1.2.0/skills/the-loop/SKILL.md +69 -0
  175. watch_skill-1.2.0/skills/video-memory/SKILL.md +65 -0
  176. watch_skill-1.2.0/skills/watch/SKILL.md +124 -0
  177. watch_skill-1.2.0/skills/watching-videos/SKILL.md +65 -0
  178. watch_skill-1.2.0/skills.sh.json +17 -0
  179. watch_skill-1.2.0/src/watch_skill/__init__.py +19 -0
  180. watch_skill-1.2.0/src/watch_skill/acquire/__init__.py +12 -0
  181. watch_skill-1.2.0/src/watch_skill/acquire/cache.py +150 -0
  182. watch_skill-1.2.0/src/watch_skill/acquire/cobalt.py +96 -0
  183. watch_skill-1.2.0/src/watch_skill/acquire/direct.py +50 -0
  184. watch_skill-1.2.0/src/watch_skill/acquire/resolver.py +176 -0
  185. watch_skill-1.2.0/src/watch_skill/acquire/sources.py +54 -0
  186. watch_skill-1.2.0/src/watch_skill/acquire/types.py +26 -0
  187. watch_skill-1.2.0/src/watch_skill/acquire/ytdlp.py +208 -0
  188. watch_skill-1.2.0/src/watch_skill/answer/__init__.py +11 -0
  189. watch_skill-1.2.0/src/watch_skill/answer/cache.py +172 -0
  190. watch_skill-1.2.0/src/watch_skill/answer/confidence.py +122 -0
  191. watch_skill-1.2.0/src/watch_skill/answer/crops.py +75 -0
  192. watch_skill-1.2.0/src/watch_skill/answer/engine.py +397 -0
  193. watch_skill-1.2.0/src/watch_skill/answer/ladder.py +182 -0
  194. watch_skill-1.2.0/src/watch_skill/answer/localize.py +218 -0
  195. watch_skill-1.2.0/src/watch_skill/answer/types.py +62 -0
  196. watch_skill-1.2.0/src/watch_skill/batch.py +158 -0
  197. watch_skill-1.2.0/src/watch_skill/bench/__init__.py +4 -0
  198. watch_skill-1.2.0/src/watch_skill/bench/perception.py +175 -0
  199. watch_skill-1.2.0/src/watch_skill/config.py +295 -0
  200. watch_skill-1.2.0/src/watch_skill/errors.py +98 -0
  201. watch_skill-1.2.0/src/watch_skill/extract/__init__.py +11 -0
  202. watch_skill-1.2.0/src/watch_skill/extract/bug_report.py +139 -0
  203. watch_skill-1.2.0/src/watch_skill/extract/chapters.py +123 -0
  204. watch_skill-1.2.0/src/watch_skill/extract/hook.py +152 -0
  205. watch_skill-1.2.0/src/watch_skill/health/__init__.py +6 -0
  206. watch_skill-1.2.0/src/watch_skill/health/agents_setup.py +151 -0
  207. watch_skill-1.2.0/src/watch_skill/health/binaries.py +277 -0
  208. watch_skill-1.2.0/src/watch_skill/health/clean.py +114 -0
  209. watch_skill-1.2.0/src/watch_skill/health/doctor.py +633 -0
  210. watch_skill-1.2.0/src/watch_skill/health/log.py +46 -0
  211. watch_skill-1.2.0/src/watch_skill/health/vision_setup.py +297 -0
  212. watch_skill-1.2.0/src/watch_skill/index/__init__.py +29 -0
  213. watch_skill-1.2.0/src/watch_skill/index/db.py +299 -0
  214. watch_skill-1.2.0/src/watch_skill/index/embeddings.py +74 -0
  215. watch_skill-1.2.0/src/watch_skill/index/retrieval.py +266 -0
  216. watch_skill-1.2.0/src/watch_skill/index/store.py +340 -0
  217. watch_skill-1.2.0/src/watch_skill/index/textnorm.py +208 -0
  218. watch_skill-1.2.0/src/watch_skill/integrations/__init__.py +13 -0
  219. watch_skill-1.2.0/src/watch_skill/integrations/_core.py +88 -0
  220. watch_skill-1.2.0/src/watch_skill/integrations/autogen.py +26 -0
  221. watch_skill-1.2.0/src/watch_skill/integrations/crewai.py +40 -0
  222. watch_skill-1.2.0/src/watch_skill/integrations/langchain.py +30 -0
  223. watch_skill-1.2.0/src/watch_skill/integrations/llamaindex.py +27 -0
  224. watch_skill-1.2.0/src/watch_skill/integrations/openai_agents.py +29 -0
  225. watch_skill-1.2.0/src/watch_skill/jobs.py +115 -0
  226. watch_skill-1.2.0/src/watch_skill/lessons/__init__.py +23 -0
  227. watch_skill-1.2.0/src/watch_skill/lessons/classify.py +105 -0
  228. watch_skill-1.2.0/src/watch_skill/lessons/evals.py +192 -0
  229. watch_skill-1.2.0/src/watch_skill/lessons/inject.py +81 -0
  230. watch_skill-1.2.0/src/watch_skill/lessons/profiles.py +94 -0
  231. watch_skill-1.2.0/src/watch_skill/lessons/report.py +96 -0
  232. watch_skill-1.2.0/src/watch_skill/lessons/store.py +184 -0
  233. watch_skill-1.2.0/src/watch_skill/library/__init__.py +23 -0
  234. watch_skill-1.2.0/src/watch_skill/library/notes.py +204 -0
  235. watch_skill-1.2.0/src/watch_skill/library/synthesize.py +349 -0
  236. watch_skill-1.2.0/src/watch_skill/loop/__init__.py +61 -0
  237. watch_skill-1.2.0/src/watch_skill/loop/artifact.py +82 -0
  238. watch_skill-1.2.0/src/watch_skill/loop/capture.py +226 -0
  239. watch_skill-1.2.0/src/watch_skill/loop/critic.py +331 -0
  240. watch_skill-1.2.0/src/watch_skill/loop/diff.py +125 -0
  241. watch_skill-1.2.0/src/watch_skill/loop/framework.py +215 -0
  242. watch_skill-1.2.0/src/watch_skill/loop/monitor.py +198 -0
  243. watch_skill-1.2.0/src/watch_skill/loop/reportfmt.py +42 -0
  244. watch_skill-1.2.0/src/watch_skill/loop/runner.py +293 -0
  245. watch_skill-1.2.0/src/watch_skill/loop/webhook.py +95 -0
  246. watch_skill-1.2.0/src/watch_skill/perceive/__init__.py +23 -0
  247. watch_skill-1.2.0/src/watch_skill/perceive/budget.py +97 -0
  248. watch_skill-1.2.0/src/watch_skill/perceive/engine.py +213 -0
  249. watch_skill-1.2.0/src/watch_skill/perceive/media.py +91 -0
  250. watch_skill-1.2.0/src/watch_skill/perceive/ocr.py +151 -0
  251. watch_skill-1.2.0/src/watch_skill/perceive/ocr_backends.py +315 -0
  252. watch_skill-1.2.0/src/watch_skill/perceive/scenes.py +87 -0
  253. watch_skill-1.2.0/src/watch_skill/perceive/types.py +88 -0
  254. watch_skill-1.2.0/src/watch_skill/report.py +108 -0
  255. watch_skill-1.2.0/src/watch_skill/surfaces/__init__.py +5 -0
  256. watch_skill-1.2.0/src/watch_skill/surfaces/api/__init__.py +9 -0
  257. watch_skill-1.2.0/src/watch_skill/surfaces/api/app.py +337 -0
  258. watch_skill-1.2.0/src/watch_skill/surfaces/cli/__init__.py +1 -0
  259. watch_skill-1.2.0/src/watch_skill/surfaces/cli/main.py +1035 -0
  260. watch_skill-1.2.0/src/watch_skill/surfaces/mcp/__init__.py +1 -0
  261. watch_skill-1.2.0/src/watch_skill/surfaces/mcp/server.py +619 -0
  262. watch_skill-1.2.0/src/watch_skill/transcribe/__init__.py +12 -0
  263. watch_skill-1.2.0/src/watch_skill/transcribe/audio.py +131 -0
  264. watch_skill-1.2.0/src/watch_skill/transcribe/cloud.py +190 -0
  265. watch_skill-1.2.0/src/watch_skill/transcribe/diarize.py +116 -0
  266. watch_skill-1.2.0/src/watch_skill/transcribe/ladder.py +71 -0
  267. watch_skill-1.2.0/src/watch_skill/transcribe/local.py +113 -0
  268. watch_skill-1.2.0/src/watch_skill/transcribe/types.py +66 -0
  269. watch_skill-1.2.0/src/watch_skill/transcribe/vtt.py +64 -0
  270. watch_skill-1.2.0/src/watch_skill/viewer.py +213 -0
  271. watch_skill-1.2.0/src/watch_skill/vision/__init__.py +22 -0
  272. watch_skill-1.2.0/src/watch_skill/vision/client.py +304 -0
  273. watch_skill-1.2.0/src/watch_skill/vision/cost.py +52 -0
  274. watch_skill-1.2.0/src/watch_skill/vision/local_health.py +119 -0
  275. watch_skill-1.2.0/src/watch_skill/vision/model.py +149 -0
  276. watch_skill-1.2.0/src/watch_skill/vision/prices.json +19 -0
  277. watch_skill-1.2.0/src/watch_skill/vision/registry.py +144 -0
  278. watch_skill-1.2.0/src/watch_skill/watch.py +177 -0
  279. watch_skill-1.2.0/templates/agent-adapter/README.md +45 -0
  280. watch_skill-1.2.0/templates/agent-adapter/docs-skeleton.md +48 -0
  281. watch_skill-1.2.0/templates/agent-adapter/validate.py +88 -0
  282. watch_skill-1.2.0/tests/acquire/test_acquire_chain.py +70 -0
  283. watch_skill-1.2.0/tests/acquire/test_cache.py +75 -0
  284. watch_skill-1.2.0/tests/acquire/test_sources.py +39 -0
  285. watch_skill-1.2.0/tests/acquire/test_ytdlp.py +133 -0
  286. watch_skill-1.2.0/tests/answer/test_answer.py +364 -0
  287. watch_skill-1.2.0/tests/answer/test_cost_meter.py +129 -0
  288. watch_skill-1.2.0/tests/answer/test_localize.py +142 -0
  289. watch_skill-1.2.0/tests/answer/test_token_economy.py +220 -0
  290. watch_skill-1.2.0/tests/conftest.py +77 -0
  291. watch_skill-1.2.0/tests/extract/test_extract.py +184 -0
  292. watch_skill-1.2.0/tests/health/test_binaries.py +91 -0
  293. watch_skill-1.2.0/tests/health/test_clean.py +67 -0
  294. watch_skill-1.2.0/tests/health/test_doctor.py +204 -0
  295. watch_skill-1.2.0/tests/health/test_doctor_repairs.py +157 -0
  296. watch_skill-1.2.0/tests/health/test_ffmpeg_bootstrap.py +135 -0
  297. watch_skill-1.2.0/tests/health/test_vision_setup.py +140 -0
  298. watch_skill-1.2.0/tests/index/test_arabic.py +117 -0
  299. watch_skill-1.2.0/tests/index/test_frame_persistence.py +110 -0
  300. watch_skill-1.2.0/tests/index/test_i18n.py +161 -0
  301. watch_skill-1.2.0/tests/index/test_i18n_v07.py +203 -0
  302. watch_skill-1.2.0/tests/index/test_index.py +303 -0
  303. watch_skill-1.2.0/tests/integrations/test_adapters.py +120 -0
  304. watch_skill-1.2.0/tests/lessons/test_eval_report.py +112 -0
  305. watch_skill-1.2.0/tests/lessons/test_lessons.py +261 -0
  306. watch_skill-1.2.0/tests/library/test_library.py +198 -0
  307. watch_skill-1.2.0/tests/loop/test_capture.py +56 -0
  308. watch_skill-1.2.0/tests/loop/test_describe_critic.py +269 -0
  309. watch_skill-1.2.0/tests/loop/test_framework.py +298 -0
  310. watch_skill-1.2.0/tests/loop/test_loop.py +246 -0
  311. watch_skill-1.2.0/tests/loop/test_webhook.py +113 -0
  312. watch_skill-1.2.0/tests/perceive/test_budget.py +58 -0
  313. watch_skill-1.2.0/tests/perceive/test_flow_cues.py +35 -0
  314. watch_skill-1.2.0/tests/perceive/test_ocr_backends.py +118 -0
  315. watch_skill-1.2.0/tests/perceive/test_perceive.py +176 -0
  316. watch_skill-1.2.0/tests/surfaces/test_api.py +100 -0
  317. watch_skill-1.2.0/tests/surfaces/test_cli_compat.py +77 -0
  318. watch_skill-1.2.0/tests/surfaces/test_cli_encoding.py +57 -0
  319. watch_skill-1.2.0/tests/surfaces/test_mcp_server.py +126 -0
  320. watch_skill-1.2.0/tests/test_agent_docs.py +102 -0
  321. watch_skill-1.2.0/tests/test_batch.py +127 -0
  322. watch_skill-1.2.0/tests/test_config.py +112 -0
  323. watch_skill-1.2.0/tests/test_error_fix_audit.py +160 -0
  324. watch_skill-1.2.0/tests/test_errors.py +43 -0
  325. watch_skill-1.2.0/tests/test_input_validation.py +53 -0
  326. watch_skill-1.2.0/tests/test_jobs.py +69 -0
  327. watch_skill-1.2.0/tests/test_plugin_packaging.py +155 -0
  328. watch_skill-1.2.0/tests/test_skills_library.py +100 -0
  329. watch_skill-1.2.0/tests/test_viewer.py +134 -0
  330. watch_skill-1.2.0/tests/test_watch_pipeline.py +61 -0
  331. watch_skill-1.2.0/tests/transcribe/test_diarize.py +106 -0
  332. watch_skill-1.2.0/tests/transcribe/test_privacy.py +65 -0
  333. watch_skill-1.2.0/tests/transcribe/test_transcribe.py +110 -0
  334. watch_skill-1.2.0/tests/vision/test_local_health.py +131 -0
  335. watch_skill-1.2.0/tests/vision/test_openai_compatible_providers.py +111 -0
  336. watch_skill-1.2.0/tests/vision/test_vision.py +321 -0
  337. 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,5 @@
1
+ * text=auto eol=lf
2
+ *.png binary
3
+ *.jpg binary
4
+ *.gif binary
5
+ *.mp4 binary
@@ -0,0 +1,3 @@
1
+ # Every pull request gets a review request automatically, so nothing sits in
2
+ # the queue unnoticed. See CONTRIBUTING.md for the review turnaround.
3
+ * @oxbshw
@@ -0,0 +1,4 @@
1
+ # Enabling the Sponsor button takes two steps and this file is only one of
2
+ # them: GitHub Sponsors must also be switched on for the oxbshw account at
3
+ # https://github.com/sponsors — until then the button stays hidden.
4
+ github: [oxbshw]
@@ -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/