opencode-skills-collection 3.1.0 → 3.1.2

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 (291) hide show
  1. package/bundled-skills/.antigravity-install-manifest.json +84 -1
  2. package/bundled-skills/2slides-ppt-generator/SKILL.md +8 -7
  3. package/bundled-skills/android-cli/SKILL.md +19 -7
  4. package/bundled-skills/android-ui-journey-testing/SKILL.md +191 -0
  5. package/bundled-skills/apple-notes-search/SKILL.md +12 -2
  6. package/bundled-skills/ask-matt/SKILL.md +92 -0
  7. package/bundled-skills/atlas-ledger/SKILL.md +8 -0
  8. package/bundled-skills/bugs-are-annoying/SKILL.md +137 -0
  9. package/bundled-skills/codebase-design/DEEPENING.md +37 -0
  10. package/bundled-skills/codebase-design/DESIGN-IT-TWICE.md +44 -0
  11. package/bundled-skills/codebase-design/SKILL.md +145 -0
  12. package/bundled-skills/codex-fable5/SKILL.md +10 -2
  13. package/bundled-skills/competitor-analysis/LICENSE.txt +21 -0
  14. package/bundled-skills/competitor-analysis/SKILL.md +434 -0
  15. package/bundled-skills/competitor-analysis/references/battle-card-subagent.md +127 -0
  16. package/bundled-skills/competitor-analysis/references/battle-card.md +91 -0
  17. package/bundled-skills/competitor-analysis/references/example-research.md +130 -0
  18. package/bundled-skills/competitor-analysis/references/report-template.html +127 -0
  19. package/bundled-skills/competitor-analysis/references/research-patterns.md +217 -0
  20. package/bundled-skills/competitor-analysis/references/workflow.md +434 -0
  21. package/bundled-skills/competitor-analysis/scripts/capture_screenshots.mjs +142 -0
  22. package/bundled-skills/competitor-analysis/scripts/compile_report.mjs +929 -0
  23. package/bundled-skills/competitor-analysis/scripts/extract_vs_names.mjs +140 -0
  24. package/bundled-skills/competitor-analysis/scripts/gate_candidates.mjs +224 -0
  25. package/bundled-skills/competitor-analysis/scripts/list_urls.mjs +90 -0
  26. package/bundled-skills/competitor-analysis/scripts/md_utils.mjs +50 -0
  27. package/bundled-skills/competitor-analysis/scripts/merge_partials.mjs +291 -0
  28. package/bundled-skills/competitor-analysis/scripts/package.json +6 -0
  29. package/bundled-skills/design-it/3d-ui/SKILL.md +259 -0
  30. package/bundled-skills/design-it/SKILL.md +170 -0
  31. package/bundled-skills/design-it/ai-native-ui/SKILL.md +295 -0
  32. package/bundled-skills/design-it/aurora-ui/SKILL.md +307 -0
  33. package/bundled-skills/design-it/bento-ui/SKILL.md +314 -0
  34. package/bundled-skills/design-it/brutalism/SKILL.md +270 -0
  35. package/bundled-skills/design-it/brutalist-typography/SKILL.md +287 -0
  36. package/bundled-skills/design-it/card-based-design/SKILL.md +262 -0
  37. package/bundled-skills/design-it/claymorphism/SKILL.md +287 -0
  38. package/bundled-skills/design-it/color-blocking/SKILL.md +278 -0
  39. package/bundled-skills/design-it/command-center-ui/SKILL.md +345 -0
  40. package/bundled-skills/design-it/cyber-y2k/SKILL.md +312 -0
  41. package/bundled-skills/design-it/cyberpunk-ui/SKILL.md +262 -0
  42. package/bundled-skills/design-it/dark-mode/SKILL.md +289 -0
  43. package/bundled-skills/design-it/dashboard-design/SKILL.md +331 -0
  44. package/bundled-skills/design-it/data-dense-design/SKILL.md +322 -0
  45. package/bundled-skills/design-it/duotone-design/SKILL.md +248 -0
  46. package/bundled-skills/design-it/editorial-design/SKILL.md +328 -0
  47. package/bundled-skills/design-it/flat-design/SKILL.md +221 -0
  48. package/bundled-skills/design-it/flat-design-2/SKILL.md +240 -0
  49. package/bundled-skills/design-it/floating-ui/SKILL.md +299 -0
  50. package/bundled-skills/design-it/frutiger-aero/SKILL.md +274 -0
  51. package/bundled-skills/design-it/glassmorphism/SKILL.md +272 -0
  52. package/bundled-skills/design-it/gradient-design/SKILL.md +309 -0
  53. package/bundled-skills/design-it/high-contrast/SKILL.md +288 -0
  54. package/bundled-skills/design-it/holographic-ui/SKILL.md +310 -0
  55. package/bundled-skills/design-it/isometric-design/SKILL.md +228 -0
  56. package/bundled-skills/design-it/layered-design/SKILL.md +247 -0
  57. package/bundled-skills/design-it/material-design/SKILL.md +275 -0
  58. package/bundled-skills/design-it/maximalism/SKILL.md +297 -0
  59. package/bundled-skills/design-it/minimalism/SKILL.md +267 -0
  60. package/bundled-skills/design-it/monochromatic-ui/SKILL.md +296 -0
  61. package/bundled-skills/design-it/neo-brutalism/SKILL.md +270 -0
  62. package/bundled-skills/design-it/neumorphism/SKILL.md +248 -0
  63. package/bundled-skills/design-it/retro-design/SKILL.md +283 -0
  64. package/bundled-skills/design-it/retro-futurism/SKILL.md +259 -0
  65. package/bundled-skills/design-it/sci-fi-interface/SKILL.md +309 -0
  66. package/bundled-skills/design-it/skeuomorphism/SKILL.md +280 -0
  67. package/bundled-skills/design-it/soft-pastel/SKILL.md +307 -0
  68. package/bundled-skills/design-it/spatial-computing-ui/SKILL.md +300 -0
  69. package/bundled-skills/design-it/spatial-design/SKILL.md +268 -0
  70. package/bundled-skills/design-it/swiss-design/SKILL.md +293 -0
  71. package/bundled-skills/design-it/synthwave/SKILL.md +257 -0
  72. package/bundled-skills/design-it/tile-design/SKILL.md +297 -0
  73. package/bundled-skills/design-it/typography-first/SKILL.md +247 -0
  74. package/bundled-skills/design-it/vaporwave/SKILL.md +331 -0
  75. package/bundled-skills/design-it/vibrant-maximalism/SKILL.md +291 -0
  76. package/bundled-skills/design-it/widget-based-design/SKILL.md +274 -0
  77. package/bundled-skills/design-it/y2k-design/SKILL.md +268 -0
  78. package/bundled-skills/diagnosing-bugs/SKILL.md +165 -0
  79. package/bundled-skills/diagnosing-bugs/scripts/hitl-loop.template.sh +41 -0
  80. package/bundled-skills/docs/contributors/skill-scoring.md +235 -0
  81. package/bundled-skills/docs/integrations/jetski-cortex.md +3 -3
  82. package/bundled-skills/docs/integrations/jetski-gemini-loader/README.md +1 -1
  83. package/bundled-skills/docs/maintainers/repo-growth-seo.md +3 -3
  84. package/bundled-skills/docs/maintainers/skills-update-guide.md +1 -1
  85. package/bundled-skills/docs/users/bundles.md +145 -1
  86. package/bundled-skills/docs/users/claude-code-skills.md +1 -1
  87. package/bundled-skills/docs/users/gemini-cli-skills.md +1 -1
  88. package/bundled-skills/docs/users/getting-started.md +1 -1
  89. package/bundled-skills/docs/users/kiro-integration.md +1 -1
  90. package/bundled-skills/docs/users/specialized-plugin-roadmap.md +11 -4
  91. package/bundled-skills/docs/users/usage.md +4 -4
  92. package/bundled-skills/docs/users/visual-guide.md +4 -4
  93. package/bundled-skills/domain-modeling/ADR-FORMAT.md +47 -0
  94. package/bundled-skills/domain-modeling/CONTEXT-FORMAT.md +60 -0
  95. package/bundled-skills/domain-modeling/SKILL.md +105 -0
  96. package/bundled-skills/dos-verify-done-claims/SKILL.md +16 -4
  97. package/bundled-skills/ecl-harness-engineer/agents/creator-config.md +1 -1
  98. package/bundled-skills/ecl-harness-engineer/references/environment-config-guide.md +2 -2
  99. package/bundled-skills/ecl-harness-engineer/references/environment-detection-guide.md +4 -4
  100. package/bundled-skills/event-staffing-ordering/SKILL.md +4 -0
  101. package/bundled-skills/grill-me/SKILL.md +36 -0
  102. package/bundled-skills/grill-with-docs/SKILL.md +36 -0
  103. package/bundled-skills/grilling/SKILL.md +39 -0
  104. package/bundled-skills/handoff/SKILL.md +45 -0
  105. package/bundled-skills/image-generator/.env.example +7 -0
  106. package/bundled-skills/image-generator/SKILL.md +509 -0
  107. package/bundled-skills/improve-codebase-architecture/HTML-REPORT.md +123 -0
  108. package/bundled-skills/improve-codebase-architecture/SKILL.md +97 -0
  109. package/bundled-skills/learn/SKILL.md +156 -0
  110. package/bundled-skills/lesson-generator/SKILL.md +90 -0
  111. package/bundled-skills/llm-council/.env.example +7 -0
  112. package/bundled-skills/llm-council/SKILL.md +602 -0
  113. package/bundled-skills/loop-library/SKILL.md +208 -0
  114. package/bundled-skills/loop-library/agents/openai.yaml +4 -0
  115. package/bundled-skills/loop-library/references/catalog.md +270 -0
  116. package/bundled-skills/lovable-cleanup/SKILL.md +9 -7
  117. package/bundled-skills/macos-screen-recorder/SKILL.md +9 -1
  118. package/bundled-skills/mailtrap-managing-contacts/SKILL.md +112 -0
  119. package/bundled-skills/mailtrap-sending-emails/SKILL.md +167 -0
  120. package/bundled-skills/mailtrap-setting-up-sending-domain/SKILL.md +77 -0
  121. package/bundled-skills/mailtrap-testing-with-sandbox/SKILL.md +110 -0
  122. package/bundled-skills/prototype/LOGIC.md +79 -0
  123. package/bundled-skills/prototype/SKILL.md +62 -0
  124. package/bundled-skills/prototype/UI.md +112 -0
  125. package/bundled-skills/screenstudio-alt/SKILL.md +9 -1
  126. package/bundled-skills/setup-matt-pocock-skills/SKILL.md +158 -0
  127. package/bundled-skills/setup-matt-pocock-skills/domain.md +51 -0
  128. package/bundled-skills/setup-matt-pocock-skills/issue-tracker-github.md +34 -0
  129. package/bundled-skills/setup-matt-pocock-skills/issue-tracker-gitlab.md +35 -0
  130. package/bundled-skills/setup-matt-pocock-skills/issue-tracker-local.md +19 -0
  131. package/bundled-skills/setup-matt-pocock-skills/triage-labels.md +15 -0
  132. package/bundled-skills/survey-generator/LICENSE +21 -0
  133. package/bundled-skills/survey-generator/SKILL.md +143 -0
  134. package/bundled-skills/survey-generator/build_artifact.py +208 -0
  135. package/bundled-skills/survey-generator/examples/agentic-engineering/research_bundle.json +1196 -0
  136. package/bundled-skills/survey-generator/examples/agentic-engineering/survey.html +706 -0
  137. package/bundled-skills/survey-generator/style_spec.json +85 -0
  138. package/bundled-skills/survey-generator/templates/research_bundle_template.json +69 -0
  139. package/bundled-skills/tdd/SKILL.md +139 -0
  140. package/bundled-skills/tdd/mocking.md +59 -0
  141. package/bundled-skills/tdd/refactoring.md +10 -0
  142. package/bundled-skills/tdd/tests.md +61 -0
  143. package/bundled-skills/teach/GLOSSARY-FORMAT.md +35 -0
  144. package/bundled-skills/teach/LEARNING-RECORD-FORMAT.md +46 -0
  145. package/bundled-skills/teach/MISSION-FORMAT.md +31 -0
  146. package/bundled-skills/teach/RESOURCES-FORMAT.md +32 -0
  147. package/bundled-skills/teach/SKILL.md +169 -0
  148. package/bundled-skills/to-issues/SKILL.md +115 -0
  149. package/bundled-skills/to-prd/SKILL.md +104 -0
  150. package/bundled-skills/tools-page-seo-optimizer/SKILL.md +616 -0
  151. package/bundled-skills/triage/AGENT-BRIEF.md +207 -0
  152. package/bundled-skills/triage/OUT-OF-SCOPE.md +105 -0
  153. package/bundled-skills/triage/SKILL.md +143 -0
  154. package/bundled-skills/vibecode-production-qa-validator/SKILL.md +371 -141
  155. package/bundled-skills/wiki-builder/SKILL.md +157 -0
  156. package/bundled-skills/wiki-builder/agents/openai.yaml +5 -0
  157. package/bundled-skills/wiki-builder/references/wiki-flavors.md +98 -0
  158. package/bundled-skills/wiki-builder/scripts/init_wiki.sh +105 -0
  159. package/bundled-skills/wiki-builder/templates/index.md +20 -0
  160. package/bundled-skills/wiki-builder/templates/maintenance-log.md +7 -0
  161. package/bundled-skills/wiki-builder/templates/prompts/compile-concept-page.md +12 -0
  162. package/bundled-skills/wiki-builder/templates/prompts/compile-index.md +11 -0
  163. package/bundled-skills/wiki-builder/templates/prompts/compile-source-page.md +12 -0
  164. package/bundled-skills/wiki-builder/templates/prompts/lint-wiki.md +10 -0
  165. package/bundled-skills/wiki-builder/templates/prompts/query-and-file.md +11 -0
  166. package/bundled-skills/wiki-builder/templates/sources.md +9 -0
  167. package/bundled-skills/wiki-builder/templates/wiki.config.md +53 -0
  168. package/bundled-skills/writing-great-skills/GLOSSARY.md +181 -0
  169. package/bundled-skills/writing-great-skills/SKILL.md +111 -0
  170. package/bundled-skills/yao-meta-skill/SKILL.md +86 -0
  171. package/bundled-skills/yao-meta-skill/agents/interface.yaml +26 -0
  172. package/bundled-skills/yao-meta-skill/manifest.json +24 -0
  173. package/bundled-skills/yao-meta-skill/references/artifact-design-doctrine.md +49 -0
  174. package/bundled-skills/yao-meta-skill/references/authoring-discipline.md +78 -0
  175. package/bundled-skills/yao-meta-skill/references/autonomous-adaptation.md +65 -0
  176. package/bundled-skills/yao-meta-skill/references/distribution-registry-method.md +60 -0
  177. package/bundled-skills/yao-meta-skill/references/eval-playbook.md +69 -0
  178. package/bundled-skills/yao-meta-skill/references/gate-selection.md +68 -0
  179. package/bundled-skills/yao-meta-skill/references/governance.md +134 -0
  180. package/bundled-skills/yao-meta-skill/references/human-review-template.md +54 -0
  181. package/bundled-skills/yao-meta-skill/references/intent-dialogue.md +138 -0
  182. package/bundled-skills/yao-meta-skill/references/iteration-philosophy.md +30 -0
  183. package/bundled-skills/yao-meta-skill/references/non-skill-decision-tree.md +39 -0
  184. package/bundled-skills/yao-meta-skill/references/operating-modes.md +107 -0
  185. package/bundled-skills/yao-meta-skill/references/output-eval-method.md +113 -0
  186. package/bundled-skills/yao-meta-skill/references/output-quality-risk.md +41 -0
  187. package/bundled-skills/yao-meta-skill/references/output-visual-quality.md +53 -0
  188. package/bundled-skills/yao-meta-skill/references/packaging-contracts.md +70 -0
  189. package/bundled-skills/yao-meta-skill/references/pattern-extraction-doctrine.md +76 -0
  190. package/bundled-skills/yao-meta-skill/references/platform-capability-matrix.md +49 -0
  191. package/bundled-skills/yao-meta-skill/references/prompt-engineering-doctrine.md +76 -0
  192. package/bundled-skills/yao-meta-skill/references/qa-ladder.md +57 -0
  193. package/bundled-skills/yao-meta-skill/references/reference-scan.md +126 -0
  194. package/bundled-skills/yao-meta-skill/references/regression-cause-taxonomy.md +80 -0
  195. package/bundled-skills/yao-meta-skill/references/resource-boundaries.md +120 -0
  196. package/bundled-skills/yao-meta-skill/references/review-studio-method.md +87 -0
  197. package/bundled-skills/yao-meta-skill/references/review-waiver-method.md +76 -0
  198. package/bundled-skills/yao-meta-skill/references/runtime-conformance-method.md +21 -0
  199. package/bundled-skills/yao-meta-skill/references/skill-archetypes.md +86 -0
  200. package/bundled-skills/yao-meta-skill/references/skill-atlas-method.md +35 -0
  201. package/bundled-skills/yao-meta-skill/references/skill-engineering-method.md +210 -0
  202. package/bundled-skills/yao-meta-skill/references/skill-ir-method.md +41 -0
  203. package/bundled-skills/yao-meta-skill/references/skillops-decision-policy.md +53 -0
  204. package/bundled-skills/yao-meta-skill/references/systems-thinking-doctrine.md +75 -0
  205. package/bundled-skills/yao-meta-skill/references/telemetry-drift-method.md +182 -0
  206. package/bundled-skills/yao-meta-skill/references/trust-security-method.md +79 -0
  207. package/bundled-skills/yao-meta-skill/references/user-memory-policy.md +35 -0
  208. package/bundled-skills/youtube-notetaker/SKILL.md +209 -0
  209. package/bundled-skills/youtube-notetaker/reference/artifact.html +269 -0
  210. package/bundled-skills/youtube-notetaker/scripts/contact_sheet.py +53 -0
  211. package/bundled-skills/youtube-notetaker/scripts/detect_slides.sh +19 -0
  212. package/bundled-skills/youtube-notetaker/scripts/download.sh +24 -0
  213. package/bundled-skills/youtube-notetaker/scripts/extract_slides.py +43 -0
  214. package/bundled-skills/youtube-notetaker/scripts/serve.py +222 -0
  215. package/bundled-skills/youtube-notetaker/scripts/setup.sh +27 -0
  216. package/bundled-skills/youtube-notetaker/scripts/verify.sh +31 -0
  217. package/bundled-skills/youtube-notetaker/scripts/vtt_to_transcript.py +59 -0
  218. package/bundled-skills/youtube-notetaker/scripts/write_library_item.py +69 -0
  219. package/package.json +1 -1
  220. package/skills_index.json +2013 -330
  221. package/bundled-skills/ai-md/SKILL.md +0 -523
  222. package/bundled-skills/atlas-contract/SKILL.md +0 -650
  223. package/bundled-skills/busybox-on-windows/SKILL.md +0 -40
  224. package/bundled-skills/monte-carlo-prevent/SKILL.md +0 -257
  225. package/bundled-skills/monte-carlo-prevent/references/TROUBLESHOOTING.md +0 -23
  226. package/bundled-skills/monte-carlo-prevent/references/parameters.md +0 -32
  227. package/bundled-skills/monte-carlo-prevent/references/workflows.md +0 -478
  228. package/bundled-skills/monte-carlo-push-ingestion/SKILL.md +0 -372
  229. package/bundled-skills/monte-carlo-push-ingestion/references/anomaly-detection.md +0 -87
  230. package/bundled-skills/monte-carlo-push-ingestion/references/custom-lineage.md +0 -203
  231. package/bundled-skills/monte-carlo-push-ingestion/references/direct-http-api.md +0 -207
  232. package/bundled-skills/monte-carlo-push-ingestion/references/prerequisites.md +0 -150
  233. package/bundled-skills/monte-carlo-push-ingestion/references/push-lineage.md +0 -160
  234. package/bundled-skills/monte-carlo-push-ingestion/references/push-metadata.md +0 -158
  235. package/bundled-skills/monte-carlo-push-ingestion/references/push-query-logs.md +0 -219
  236. package/bundled-skills/monte-carlo-push-ingestion/references/validation.md +0 -257
  237. package/bundled-skills/monte-carlo-push-ingestion/scripts/sample_verify.py +0 -357
  238. package/bundled-skills/monte-carlo-push-ingestion/scripts/templates/bigquery/collect_and_push_lineage.py +0 -70
  239. package/bundled-skills/monte-carlo-push-ingestion/scripts/templates/bigquery/collect_and_push_metadata.py +0 -65
  240. package/bundled-skills/monte-carlo-push-ingestion/scripts/templates/bigquery/collect_and_push_query_logs.py +0 -70
  241. package/bundled-skills/monte-carlo-push-ingestion/scripts/templates/bigquery/collect_lineage.py +0 -214
  242. package/bundled-skills/monte-carlo-push-ingestion/scripts/templates/bigquery/collect_metadata.py +0 -160
  243. package/bundled-skills/monte-carlo-push-ingestion/scripts/templates/bigquery/collect_query_logs.py +0 -164
  244. package/bundled-skills/monte-carlo-push-ingestion/scripts/templates/bigquery/push_lineage.py +0 -198
  245. package/bundled-skills/monte-carlo-push-ingestion/scripts/templates/bigquery/push_metadata.py +0 -193
  246. package/bundled-skills/monte-carlo-push-ingestion/scripts/templates/bigquery/push_query_logs.py +0 -207
  247. package/bundled-skills/monte-carlo-push-ingestion/scripts/templates/bigquery-iceberg/collect_and_push_metadata.py +0 -71
  248. package/bundled-skills/monte-carlo-push-ingestion/scripts/templates/bigquery-iceberg/collect_and_push_query_logs.py +0 -64
  249. package/bundled-skills/monte-carlo-push-ingestion/scripts/templates/bigquery-iceberg/collect_metadata.py +0 -253
  250. package/bundled-skills/monte-carlo-push-ingestion/scripts/templates/bigquery-iceberg/collect_query_logs.py +0 -149
  251. package/bundled-skills/monte-carlo-push-ingestion/scripts/templates/bigquery-iceberg/push_metadata.py +0 -190
  252. package/bundled-skills/monte-carlo-push-ingestion/scripts/templates/bigquery-iceberg/push_query_logs.py +0 -208
  253. package/bundled-skills/monte-carlo-push-ingestion/scripts/templates/databricks/collect_and_push_lineage.py +0 -83
  254. package/bundled-skills/monte-carlo-push-ingestion/scripts/templates/databricks/collect_and_push_metadata.py +0 -77
  255. package/bundled-skills/monte-carlo-push-ingestion/scripts/templates/databricks/collect_and_push_query_logs.py +0 -83
  256. package/bundled-skills/monte-carlo-push-ingestion/scripts/templates/databricks/collect_lineage.py +0 -240
  257. package/bundled-skills/monte-carlo-push-ingestion/scripts/templates/databricks/collect_metadata.py +0 -212
  258. package/bundled-skills/monte-carlo-push-ingestion/scripts/templates/databricks/collect_query_logs.py +0 -204
  259. package/bundled-skills/monte-carlo-push-ingestion/scripts/templates/databricks/push_lineage.py +0 -192
  260. package/bundled-skills/monte-carlo-push-ingestion/scripts/templates/databricks/push_metadata.py +0 -178
  261. package/bundled-skills/monte-carlo-push-ingestion/scripts/templates/databricks/push_query_logs.py +0 -200
  262. package/bundled-skills/monte-carlo-push-ingestion/scripts/templates/hive/collect_and_push_lineage.py +0 -119
  263. package/bundled-skills/monte-carlo-push-ingestion/scripts/templates/hive/collect_and_push_metadata.py +0 -119
  264. package/bundled-skills/monte-carlo-push-ingestion/scripts/templates/hive/collect_and_push_query_logs.py +0 -117
  265. package/bundled-skills/monte-carlo-push-ingestion/scripts/templates/hive/collect_lineage.py +0 -265
  266. package/bundled-skills/monte-carlo-push-ingestion/scripts/templates/hive/collect_metadata.py +0 -313
  267. package/bundled-skills/monte-carlo-push-ingestion/scripts/templates/hive/collect_query_logs.py +0 -284
  268. package/bundled-skills/monte-carlo-push-ingestion/scripts/templates/hive/push_lineage.py +0 -309
  269. package/bundled-skills/monte-carlo-push-ingestion/scripts/templates/hive/push_metadata.py +0 -245
  270. package/bundled-skills/monte-carlo-push-ingestion/scripts/templates/hive/push_query_logs.py +0 -255
  271. package/bundled-skills/monte-carlo-push-ingestion/scripts/templates/redshift/collect_and_push_lineage.py +0 -78
  272. package/bundled-skills/monte-carlo-push-ingestion/scripts/templates/redshift/collect_and_push_metadata.py +0 -80
  273. package/bundled-skills/monte-carlo-push-ingestion/scripts/templates/redshift/collect_and_push_query_logs.py +0 -88
  274. package/bundled-skills/monte-carlo-push-ingestion/scripts/templates/redshift/collect_lineage.py +0 -235
  275. package/bundled-skills/monte-carlo-push-ingestion/scripts/templates/redshift/collect_metadata.py +0 -219
  276. package/bundled-skills/monte-carlo-push-ingestion/scripts/templates/redshift/collect_query_logs.py +0 -239
  277. package/bundled-skills/monte-carlo-push-ingestion/scripts/templates/redshift/push_lineage.py +0 -178
  278. package/bundled-skills/monte-carlo-push-ingestion/scripts/templates/redshift/push_metadata.py +0 -178
  279. package/bundled-skills/monte-carlo-push-ingestion/scripts/templates/redshift/push_query_logs.py +0 -196
  280. package/bundled-skills/monte-carlo-push-ingestion/scripts/templates/snowflake/collect_and_push_lineage.py +0 -154
  281. package/bundled-skills/monte-carlo-push-ingestion/scripts/templates/snowflake/collect_and_push_metadata.py +0 -137
  282. package/bundled-skills/monte-carlo-push-ingestion/scripts/templates/snowflake/collect_and_push_query_logs.py +0 -137
  283. package/bundled-skills/monte-carlo-push-ingestion/scripts/templates/snowflake/collect_lineage.py +0 -349
  284. package/bundled-skills/monte-carlo-push-ingestion/scripts/templates/snowflake/collect_metadata.py +0 -329
  285. package/bundled-skills/monte-carlo-push-ingestion/scripts/templates/snowflake/collect_query_logs.py +0 -254
  286. package/bundled-skills/monte-carlo-push-ingestion/scripts/templates/snowflake/push_lineage.py +0 -307
  287. package/bundled-skills/monte-carlo-push-ingestion/scripts/templates/snowflake/push_metadata.py +0 -228
  288. package/bundled-skills/monte-carlo-push-ingestion/scripts/templates/snowflake/push_query_logs.py +0 -248
  289. package/bundled-skills/monte-carlo-push-ingestion/scripts/test_template_sdk_usage.py +0 -340
  290. package/bundled-skills/skill-optimizer/SKILL.md +0 -271
  291. package/bundled-skills/using-superpowers/SKILL.md +0 -98
@@ -0,0 +1,209 @@
1
+ ---
2
+ name: youtube-notetaker
3
+ description: "Turn YouTube talks into local study notes with slides, transcripts, editable annotations, and a markdown-backed viewer."
4
+ category: "video"
5
+ risk: "safe"
6
+ source: "official"
7
+ source_repo: "dair-ai/dair-academy-plugins"
8
+ source_type: "official"
9
+ date_added: "2026-06-19"
10
+ author: "DAIR.AI"
11
+ license: "MIT"
12
+ license_source: "https://github.com/dair-ai/dair-academy-plugins/blob/main/README.md#license"
13
+ tags:
14
+ - dair-academy
15
+ - ai
16
+ - workflow
17
+ tools:
18
+ - claude-code
19
+ - codex-cli
20
+ - cursor
21
+ ---
22
+
23
+ # YouTube Notetaker
24
+
25
+ ## When to Use
26
+
27
+ Use when this workflow matches the user request: >
28
+
29
+
30
+ _Source: [dair-ai/dair-academy-plugins](https://github.com/dair-ai/dair-academy-plugins) (MIT)._
31
+
32
+ Build a personal library of YouTube talks you study with. Each video becomes one **plain
33
+ markdown file**: slide snapshots at their timestamps, a full timestamped transcript, and
34
+ editable notes. A small bundled server renders the library as an interactive deep-dive in the
35
+ browser. No database, no cloud service. Everything is files on disk you fully own.
36
+
37
+ ## Architecture (read this first)
38
+
39
+ The **markdown library is the single source of truth**. The artifact is a thin HTML shell that
40
+ fetches from the server and writes notes back. Never hardcode video data into the HTML.
41
+
42
+ - **Library:** a plain folder, set by `VIDEO_LIBRARY_DIR` (default `~/video-deepdives/`).
43
+ - One markdown file per video, **filename slug = YouTube id** (e.g. `RtywqDFBYnQ.md`).
44
+ - Frontmatter holds video metadata + a `slides` array.
45
+ - Body holds the full transcript as `[HH:MM:SS] text` lines.
46
+ - `_media/` holds slide images, **namespaced per video** as `<youtube_id>-slide-NN.jpg`
47
+ to avoid collisions between videos.
48
+ - **Server:** `scripts/serve.py`, a single stdlib + PyYAML file. Start it with:
49
+ ```
50
+ python3 scripts/serve.py --dir ~/video-deepdives --port 8000
51
+ ```
52
+ It serves the artifact at `/` and a small API the artifact talks to:
53
+ - `GET /api/video-deepdives` (front page fetches this) lists every video.
54
+ - `GET /api/video-deepdives/<id>` returns one video `{meta, body}`.
55
+ - `GET /api/video-deepdives/_media/<file>` serves a slide image.
56
+ - `PATCH /api/video-deepdives/<id>` with `{fields:{slides:[...]}}` writes notes back.
57
+ - **It picks up new videos automatically** the moment a markdown file exists. Adding a video
58
+ means writing a markdown file + media; you almost never touch the HTML.
59
+ - The `/api/video-deepdives` URL namespace is local to the bundled server.
60
+ - **Artifact:** `reference/artifact.html`, served by `serve.py` at `/`. A clean reference copy;
61
+ only rewrite it if the user wants a UI change. For new videos, leave it alone.
62
+
63
+ ## Requirements
64
+
65
+ - `yt-dlp` and `ffmpeg` on PATH (download + frame/scene extraction).
66
+ - Python 3 with `Pillow` (contact sheet) and `PyYAML` (markdown file + server).
67
+ ```
68
+ pip install yt-dlp pillow pyyaml # ffmpeg via your package manager
69
+ ```
70
+
71
+ ## Adding a video — the pipeline
72
+
73
+ All helper scripts are in `scripts/`. Work in a scratch dir (e.g. `/tmp/ytnote-<id>/`), then
74
+ copy final assets into the library. Set `VIDEO_LIBRARY_DIR` once per shell if you don't want the
75
+ default. **Do not use em dashes (—) or arrows (→) in notes/titles.**
76
+
77
+ ### 1. Resolve the id and check embeddability
78
+ ```
79
+ scripts/setup.sh "<youtube_url_or_id>"
80
+ ```
81
+ Prints the 11-char `YTID`, the scratch dir, the target library path, and whether YouTube
82
+ **embedding is allowed** (oembed 200) or **blocked** (oembed 401, e.g. some university talks).
83
+ If blocked, inline playback won't work but the artifact degrades gracefully to an "open at this
84
+ moment on YouTube" link, so proceed normally.
85
+
86
+ ### 2. Download video + subtitles
87
+ ```
88
+ scripts/download.sh "<YTID>" /tmp/ytnote-<YTID>
89
+ ```
90
+ Uses `yt-dlp` to grab the video (≤720p is plenty for slide frames) and the best available
91
+ subtitles (manual if present, else auto-captions) as `.vtt`. Also fetches title/uploader.
92
+
93
+ ### 3. Detect candidate slide timestamps
94
+ ```
95
+ scripts/detect_slides.sh /tmp/ytnote-<YTID>/video.mp4 /tmp/ytnote-<YTID>
96
+ ```
97
+ Runs ffmpeg scene detection (`select='gt(scene,0.3)'`) and writes `scene_times.txt` (seconds).
98
+ 0.3 is a good default; lower it (0.2) for subtle slide decks, raise it (0.4) for busy video.
99
+
100
+ ### 4. Build a contact sheet and CURATE
101
+ ```
102
+ python3 scripts/contact_sheet.py /tmp/ytnote-<YTID>/video.mp4 /tmp/ytnote-<YTID>/scene_times.txt /tmp/ytnote-<YTID>/contact.jpg
103
+ ```
104
+ Read `contact.jpg` (labeled with index + timestamp). **This is the human-judgment step:** keep
105
+ frames that are real content slides; **drop talking-head shots, transitions, duplicates, and
106
+ blurry mid-animation frames.** Save the kept timestamps (seconds) to `/tmp/ytnote-<YTID>/keep.txt`,
107
+ one per line. Typical talk yields 15-25 slides.
108
+
109
+ ### 5. Extract the curated slides at full quality and install to _media
110
+ ```
111
+ python3 scripts/extract_slides.py <YTID> /tmp/ytnote-<YTID>/video.mp4 /tmp/ytnote-<YTID>/keep.txt > /tmp/ytnote-<YTID>/slides.json
112
+ ```
113
+ Extracts each kept timestamp at 1280px wide, JPEG, and copies them into
114
+ `$VIDEO_LIBRARY_DIR/_media/` as `<YTID>-slide-01.jpg`, `-02.jpg`, … (numbered in time order).
115
+ Progress goes to stderr; a clean `slides.json` scaffold prints to **stdout**, so redirect it to a
116
+ file as shown, then fill in `title` and `note`.
117
+
118
+ Tip: talks are often a slide + speaker-cam composite, and speakers flip back and forth, so the
119
+ same slide appears at several timestamps. Keep the cleanest instance of each, and re-anchor each
120
+ slide's `t` to where it is actually discussed in the transcript (better "play from here" UX).
121
+
122
+ ### 6. Build the transcript
123
+ ```
124
+ python3 scripts/vtt_to_transcript.py /tmp/ytnote-<YTID>/*.vtt /tmp/ytnote-<YTID>/transcript.txt
125
+ ```
126
+ Parses the VTT into clean, de-duplicated `[HH:MM:SS] text` lines (YouTube auto-captions repeat
127
+ rolling text; the script collapses it). This becomes the markdown body.
128
+
129
+ ### 7. Write notes and assemble the markdown file
130
+ For each kept slide, write a 1-3 sentence `note` grounded in the transcript around that timestamp
131
+ (don't invent claims). Then assemble:
132
+ ```
133
+ python3 scripts/write_library_item.py \
134
+ --id <YTID> \
135
+ --title "Talk title" \
136
+ --speaker "Name, Role, Org" \
137
+ --tags tag1,tag2,tag3 \
138
+ --slides /tmp/ytnote-<YTID>/slides.json \
139
+ --transcript /tmp/ytnote-<YTID>/transcript.txt
140
+ ```
141
+ Writes `$VIDEO_LIBRARY_DIR/<YTID>.md` with correct frontmatter + body.
142
+
143
+ ### 8. Serve and verify (always do this)
144
+ ```
145
+ python3 scripts/serve.py --dir "$VIDEO_LIBRARY_DIR" --port 8000 &
146
+ scripts/verify.sh <YTID> # defaults to http://127.0.0.1:8000
147
+ ```
148
+ `verify.sh` curls the collection list, the item, the first slide image, and the artifact,
149
+ asserting HTTP 200 and that the new id appears in the index. Then open
150
+ `http://127.0.0.1:8000/#/<YTID>` in a browser to confirm slides + transcript + notes render.
151
+
152
+ ## Markdown file shape (reference)
153
+
154
+ ```markdown
155
+ ---
156
+ id: RtywqDFBYnQ
157
+ title: Memory and dreaming for self-learning agents
158
+ youtube_id: RtywqDFBYnQ
159
+ speaker: Mahesh, Product Manager, Platform team at Anthropic
160
+ source_url: https://www.youtube.com/watch?v=RtywqDFBYnQ
161
+ slide_count: 19
162
+ created: '2026-05-25'
163
+ tags: [anthropic, memory, agents]
164
+ slides:
165
+ - idx: 1
166
+ t: 55.7 # seconds (float ok), used for seeking
167
+ mmss: 00:55 # display label
168
+ title: Agent primitives have evolved
169
+ note: One to three sentences grounded in the transcript at this timestamp.
170
+ img: /api/video-deepdives/_media/RtywqDFBYnQ-slide-01.jpg
171
+ # ... more slides
172
+ ---
173
+ ## Transcript
174
+ [00:00:08] Hello, everyone...
175
+ [00:00:11] ...
176
+ ```
177
+
178
+ Notes:
179
+ - `idx` can be sparse/non-contiguous; the artifact sorts slides by `t`, so ordering is by
180
+ timestamp, not idx.
181
+ - `img` is always a `/api/video-deepdives/_media/<file>` URL (served by serve.py),
182
+ never base64.
183
+ - Slide `note` is what the user edits in the UI; PATCH writes the whole `slides` array back.
184
+
185
+ ## Gotchas
186
+ - **Embedding disabled** (oembed 401): inline player is blocked by the video owner. Not a bug;
187
+ the artifact shows an "open at this moment on YouTube" link instead. Mention it to the user.
188
+ - **Image collisions:** always namespace media `<YTID>-slide-NN.jpg`. Never reuse bare
189
+ `slide-NN.jpg` for a new video.
190
+ - **Auto-caption noise:** rolling YouTube captions duplicate text across cues; use the provided
191
+ VTT parser, don't dump raw VTT into the body.
192
+ - **Don't touch existing videos** when adding a new one. Each video is an independent file.
193
+ - **Server not picking up a video:** confirm the `.md` file is directly inside `--dir` (not a
194
+ subfolder) and the filename is `<YTID>.md`.
195
+
196
+ ## What makes this portable
197
+ - **No orchestrator / no database.** Storage is a plain folder of markdown + images.
198
+ - **One env var** (`VIDEO_LIBRARY_DIR`) controls where the library lives.
199
+ - **One small server file** (`serve.py`, stdlib + PyYAML) renders everything and handles
200
+ note write-back. Drop it anywhere Python runs.
201
+ - The markdown files are portable: readable in Obsidian or any editor, and the frontmatter is
202
+ standard YAML.
203
+
204
+
205
+ ## Limitations
206
+
207
+ - Requires the upstream tool, account, API key, or local setup when the workflow names one.
208
+ - Does not authorize destructive, production, paid, or external-message actions without explicit user approval.
209
+ - Validate generated artifacts or recommendations against the user's real sources before treating them as final.
@@ -0,0 +1,269 @@
1
+ <!DOCTYPE html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1">
2
+ <title>Video Deep-Dives</title>
3
+ <link href="https://fonts.googleapis.com/css2?family=Playfair+Display:wght@700;900&family=DM+Sans:wght@300;400;500;600&display=swap" rel="stylesheet">
4
+ <style>
5
+ *{box-sizing:border-box;margin:0;padding:0}
6
+ html,body{height:100%}
7
+ body{font-family:'DM Sans',system-ui,sans-serif;background:#f4f1eb;color:#1c1a17;line-height:1.55;display:flex;flex-direction:column;height:100vh;overflow:hidden}
8
+ :root{--slide-max:560px;--vid-max:100%}
9
+ header{padding:11px 18px;border-bottom:1px solid #e2dccf;background:#fbf9f4;flex:0 0 auto}
10
+ .eyebrow{font-size:10px;letter-spacing:2px;text-transform:uppercase;color:#8a7e6e;font-weight:600}
11
+ .backlink{display:none;font-size:11.5px;color:#2a5cbf;text-decoration:none;font-weight:600;cursor:pointer}
12
+ .backlink:hover{text-decoration:underline}
13
+ h1{font-family:'Playfair Display',serif;font-size:clamp(17px,2.3vw,25px);font-weight:900;color:#111;line-height:1.05;margin:2px 0}
14
+ .speaker{color:#4a4236;font-size:12.5px}.speaker a{color:#2a5cbf;text-decoration:none}
15
+ /* ---------- HOME (index) ---------- */
16
+ #home{flex:1 1 auto;overflow:auto;padding:22px 26px}
17
+ .home-intro{max-width:760px;margin:0 auto 22px}
18
+ .home-intro .lede{font-family:'Playfair Display',serif;font-size:clamp(22px,3vw,34px);font-weight:900;color:#111;line-height:1.08}
19
+ .home-intro p{color:#5a5145;font-size:13.5px;margin-top:8px;max-width:640px}
20
+ .grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(300px,1fr));gap:18px;max-width:1100px;margin:0 auto}
21
+ .card{background:#fff;border:1px solid #e2dccf;border-radius:14px;overflow:hidden;cursor:pointer;text-decoration:none;color:inherit;display:flex;flex-direction:column;transition:transform .14s,box-shadow .14s,border-color .14s}
22
+ .card:hover{transform:translateY(-3px);box-shadow:0 10px 26px rgba(0,0,0,.10);border-color:#c8920a}
23
+ .card .thumb{position:relative;aspect-ratio:16/9;background:#000;overflow:hidden}
24
+ .card .thumb img{width:100%;height:100%;object-fit:cover;display:block}
25
+ .card .badge{position:absolute;bottom:8px;right:8px;background:rgba(0,0,0,.74);color:#fff;font-size:11px;font-weight:600;padding:2px 8px;border-radius:6px}
26
+ .card .play{position:absolute;inset:0;margin:auto;width:54px;height:54px;display:flex;align-items:center;justify-content:center;background:rgba(0,0,0,.5);color:#fff;border-radius:50%;font-size:20px;opacity:0;transition:.2s}
27
+ .card:hover .play{opacity:1}
28
+ .card .body{padding:13px 15px 15px}
29
+ .card .ct{font-family:'Playfair Display',serif;font-size:16.5px;font-weight:700;color:#111;line-height:1.22}
30
+ .card .cs{color:#7a6f5d;font-size:12px;margin-top:6px}
31
+ .card .tags{margin-top:10px;display:flex;flex-wrap:wrap;gap:6px}
32
+ .card .tag{font-size:10px;letter-spacing:.6px;text-transform:uppercase;color:#7a5010;background:rgba(180,130,20,.14);border-radius:5px;padding:2px 7px;font-weight:600}
33
+ #homeErr{display:none;max-width:760px;margin:0 auto;padding:10px 14px;background:#fff0ec;border:1px solid #c87060;border-radius:8px;color:#8a1a1a;font-size:13px}
34
+ /* ---------- DEEP-DIVE ---------- */
35
+ #deepdive{flex:1 1 auto;display:none;min-height:0;overflow:hidden}
36
+ #split{flex:1 1 auto;display:flex;min-height:0;overflow:hidden;width:100%}
37
+ #left{flex:0 0 58%;min-width:240px;overflow:auto;padding:14px 18px;display:flex;flex-direction:column}
38
+ #divider{flex:0 0 8px;cursor:col-resize;background:linear-gradient(#ddd6c8,#cfc7b6);position:relative}
39
+ #divider::after{content:"⋮⋮";position:absolute;top:50%;left:50%;transform:translate(-50%,-50%) rotate(90deg);color:#8a7e6e;font-size:11px;letter-spacing:-2px}
40
+ #divider:hover,#divider.drag{background:#c8920a}
41
+ /* RIGHT pane: fixed video region + independently scrolling transcript */
42
+ #right{flex:1 1 0;min-width:280px;display:flex;flex-direction:column;overflow:hidden;padding:14px 16px;background:#fbf9f4;border-left:1px solid #e2dccf}
43
+ .rtop{flex:0 0 auto}
44
+ .toolbar{display:flex;align-items:center;gap:14px;flex-wrap:wrap;background:#fff;border:1px solid #ddd8d0;border-radius:10px;padding:8px 12px;margin-bottom:12px;font-size:12px;color:#4a4236}
45
+ .toolbar label{display:flex;align-items:center;gap:7px}.toolbar input[type=range]{accent-color:#c8920a}
46
+ .deck-h{font-family:'Playfair Display',serif;font-size:18px;margin-bottom:3px}
47
+ .deck-sub{color:#8a7e6e;font-size:12px;margin-bottom:12px}
48
+ .slide{background:#fff;border:1px solid #ddd8d0;border-radius:12px;box-shadow:0 2px 8px rgba(0,0,0,.05);padding:11px;margin:0 auto 16px;width:100%;max-width:var(--slide-max);transition:border-color .15s,box-shadow .15s}
49
+ .slide.active{border-color:#b0357a;box-shadow:0 0 0 3px rgba(176,53,122,.16)}
50
+ .slide-img{position:relative;cursor:pointer;border-radius:9px;overflow:hidden;background:#000}
51
+ .slide-img img{width:100%;display:block;aspect-ratio:16/9;object-fit:contain;background:#000}
52
+ .slide-img:hover img{opacity:.85}
53
+ .play-badge{position:absolute;inset:0;margin:auto;width:54px;height:54px;display:flex;align-items:center;justify-content:center;background:rgba(0,0,0,.55);color:#fff;border-radius:50%;font-size:20px;opacity:0;transition:.2s}
54
+ .slide-img:hover .play-badge{opacity:1}
55
+ .slide-t{position:absolute;bottom:8px;right:8px;background:rgba(0,0,0,.72);color:#fff;font-size:11px;font-weight:600;padding:2px 7px;border-radius:5px}
56
+ .slide-meta{display:flex;align-items:center;justify-content:space-between;gap:10px;margin:10px 2px 8px}
57
+ .slide-meta h3{font-family:'Playfair Display',serif;font-size:15.5px;color:#111;font-weight:700;line-height:1.25}
58
+ .btn{cursor:pointer;border:0;background:#2a5cbf;color:#fff;font-size:11.5px;font-weight:600;padding:5px 11px;border-radius:6px;white-space:nowrap;font-family:'DM Sans'}
59
+ .btn:hover{background:#1d4699}
60
+ .note-lbl{display:block;font-size:10px;letter-spacing:1.4px;text-transform:uppercase;color:#8a7e6e;font-weight:600;margin:2px 2px 4px}
61
+ .saved{color:#2e8b57;letter-spacing:0;text-transform:none;font-weight:500;margin-left:6px}
62
+ .note-area{width:100%;min-height:70px;resize:vertical;border:1px solid #e3dccb;border-radius:8px;background:#fdfcf8;padding:9px 11px;font-family:'DM Sans';font-size:13px;color:#3a342b;line-height:1.55}
63
+ .note-area:focus{outline:none;border-color:#c8920a;background:#fffdf6}
64
+ .vidwrap{max-width:var(--vid-max);margin:0 auto}
65
+ .vid{position:relative;width:100%;aspect-ratio:16/9;border-radius:10px;overflow:hidden;background:#000}
66
+ .vid iframe{position:absolute;inset:0;width:100%;height:100%;border:0}
67
+ .now{margin-top:10px;background:#fdf9f0;border:1px solid #e6dcc4;border-radius:9px;padding:10px 13px}
68
+ .now .lbl{font-size:10px;letter-spacing:1.5px;text-transform:uppercase;color:#7a5010;font-weight:600;display:flex;gap:8px;align-items:center}
69
+ .now .lbl b{background:rgba(180,130,20,.14);color:#7a5010;border-radius:5px;padding:2px 7px;font-size:11px}
70
+ .now p{margin-top:6px;font-size:13px;color:#3a342b;max-height:96px;overflow:auto}
71
+ .tr-h{font-family:'Playfair Display',serif;font-size:15px;margin:12px 2px 6px}
72
+ .tsearch{width:100%;padding:8px 11px;border:1px solid #ddd8d0;border-radius:8px;font-size:12.5px;margin-bottom:8px;font-family:'DM Sans'}
73
+ #transcript{flex:1 1 0;overflow:auto;border:1px solid #eee4d4;border-radius:9px;background:#fffdf8}
74
+ .trow{display:flex;gap:9px;padding:6px 11px;cursor:pointer;border-bottom:1px solid #f1ead9;font-size:12.5px}
75
+ .trow:hover{background:#fdf3df}.trow.hl{background:#fce9bd}
76
+ .tt{color:#2a5cbf;font-weight:600;font-size:11.5px;min-width:54px;flex-shrink:0}
77
+ #err{display:none;padding:10px 14px;background:#fff0ec;border:1px solid #c87060;border-radius:8px;color:#8a1a1a;font-size:13px;margin:10px 0}
78
+ @media(max-width:760px){#split{flex-direction:column}#left,#right{flex:1 1 auto}#divider{display:none}}
79
+ </style></head><body>
80
+ <header>
81
+ <a class="backlink" id="backlink" href="#/">← All videos</a>
82
+ <div class="eyebrow">Video deep-dives · markdown-backed</div>
83
+ <h1 id="title">Video Deep-Dives</h1>
84
+ <div class="speaker" id="speaker">A growing library of talks I'm studying. Slides, transcripts, and editable notes, all backed by markdown files.</div>
85
+ </header>
86
+
87
+ <!-- ===================== HOME / INDEX ===================== -->
88
+ <div id="home">
89
+ <div id="homeErr"></div>
90
+ <div class="grid" id="grid"></div>
91
+ </div>
92
+
93
+ <!-- ===================== DEEP-DIVE ===================== -->
94
+ <div id="deepdive">
95
+ <div id="split">
96
+ <div id="left">
97
+ <div class="toolbar">
98
+ <label>Slide size <input type="range" id="slideSize" min="320" max="900" value="560" oninput="document.documentElement.style.setProperty('--slide-max',this.value+'px')"></label>
99
+ <span style="color:#cfc7b6">|</span><span class="deck-sub" style="margin:0" id="deckcount"></span>
100
+ </div>
101
+ <div class="deck-h">Slide deck</div>
102
+ <div class="deck-sub">Click a slide (or ▶) to play the video from that moment. Notes are editable and saved to markdown.</div>
103
+ <div id="err"></div>
104
+ <div id="deck"></div>
105
+ </div>
106
+ <div id="divider" title="Drag to resize"></div>
107
+ <div id="right">
108
+ <div class="rtop">
109
+ <div class="toolbar"><label>Video size <input type="range" id="vidSize" min="40" max="100" value="100" oninput="document.documentElement.style.setProperty('--vid-max',this.value+'%')"></label></div>
110
+ <div class="vidwrap"><div class="vid"><iframe id="ytplayer" src="" allow="autoplay; encrypted-media; fullscreen" allowfullscreen></iframe></div></div>
111
+ <div class="now"><div class="lbl">Now playing <b id="now-t">--:--</b> <a id="yt-jump" target="_blank" rel="noopener" style="margin-left:auto;color:#2a5cbf;text-decoration:none;font-weight:600;letter-spacing:0;text-transform:none;display:none">open at this moment on YouTube ↗</a></div><p id="now-tx">Click any slide to play the video from that point.</p></div>
112
+ <div class="tr-h">Full transcript</div>
113
+ <input class="tsearch" id="tsearch" placeholder="Search transcript…" oninput="filt(this.value)">
114
+ </div>
115
+ <div id="transcript"></div>
116
+ </div>
117
+ </div>
118
+ </div>
119
+
120
+ <script src="https://www.youtube.com/iframe_api"></script>
121
+ <script>
122
+ var API_URL='/api/video-deepdives';
123
+ var player,ready=false,pending=null,DATA=null,SLIDES=[],SEGS=[];
124
+ var CURRENT_ID=null, YTID=null, INDEX=null;
125
+
126
+ function onYouTubeIframeAPIReady(){player=new YT.Player('ytplayer',{events:{'onReady':function(){ready=true;if(pending!=null){doPlay(pending);pending=null;}}}});}
127
+ function fmt(t){t=Math.floor(t);return String(Math.floor(t/60)).padStart(2,'0')+':'+String(t%60).padStart(2,'0');}
128
+ function esc(s){var d=document.createElement('div');d.textContent=s==null?'':s;return d.innerHTML;}
129
+
130
+ /* ---------------- Router ---------------- */
131
+ function route(){
132
+ var h=(location.hash||'').replace(/^#\/?/,'').trim();
133
+ if(h){showVideo(h);} else {showHome();}
134
+ }
135
+ function showHome(){
136
+ document.getElementById('deepdive').style.display='none';
137
+ document.getElementById('home').style.display='block';
138
+ document.getElementById('backlink').style.display='none';
139
+ document.getElementById('title').textContent='Video Deep-Dives';
140
+ document.getElementById('speaker').textContent="A growing library of talks I'm studying. Slides, transcripts, and editable notes, all backed by markdown files.";
141
+ document.title='Video Deep-Dives';
142
+ if(INDEX===null) loadIndex();
143
+ }
144
+ function showVideo(id){
145
+ document.getElementById('home').style.display='none';
146
+ document.getElementById('deepdive').style.display='flex';
147
+ document.getElementById('backlink').style.display='inline-block';
148
+ loadVideo(id);
149
+ }
150
+
151
+ /* ---------------- Home / index ---------------- */
152
+ async function loadIndex(){
153
+ try{
154
+ var r=await fetch(API_URL); if(!r.ok) throw new Error('HTTP '+r.status);
155
+ var d=await r.json();
156
+ INDEX=(d.items||[]).filter(function(it){return it.youtube_id;});
157
+ var g=document.getElementById('grid'); g.innerHTML='';
158
+ if(!INDEX.length){g.innerHTML='<p style="color:#7a6f5d">No videos in the library yet.</p>';return;}
159
+ INDEX.forEach(function(it){
160
+ var slides=it.slides||[]; var thumb=(slides[0]&&slides[0].img)||'';
161
+ var tags=(it.tags||[]).slice(0,3).map(function(t){return '<span class="tag">'+esc(t)+'</span>';}).join('');
162
+ var a=document.createElement('a'); a.className='card'; a.href='#/'+encodeURIComponent(it.id);
163
+ a.innerHTML='<div class="thumb">'+(thumb?'<img src="'+esc(thumb)+'" alt="">':'')+'<span class="play">▶</span><span class="badge">'+(it.slide_count||slides.length)+' slides</span></div>'
164
+ +'<div class="body"><div class="ct">'+esc(it.title||it.id)+'</div>'
165
+ +'<div class="cs">'+esc(it.speaker||'')+'</div>'
166
+ +(tags?'<div class="tags">'+tags+'</div>':'')+'</div>';
167
+ g.appendChild(a);
168
+ });
169
+ }catch(e){var el=document.getElementById('homeErr');el.style.display='block';el.textContent='Could not load the video library: '+e.message+'. Is the backend running?';}
170
+ }
171
+
172
+ /* ---------------- Deep-dive ---------------- */
173
+ async function loadVideo(id){
174
+ if(CURRENT_ID===id && DATA){return;} // already loaded
175
+ CURRENT_ID=id;
176
+ document.getElementById('err').style.display='none';
177
+ document.getElementById('deck').innerHTML='';
178
+ document.getElementById('transcript').innerHTML='';
179
+ try{
180
+ var r=await fetch(API_URL+'/'+encodeURIComponent(id)); if(!r.ok) throw new Error('HTTP '+r.status);
181
+ DATA=await r.json(); var m=DATA.meta||{};
182
+ YTID=m.youtube_id||id;
183
+ SLIDES=(m.slides||[]).slice().sort(function(a,b){return a.t-b.t;});
184
+ document.title=m.title||'Video deep-dive';
185
+ document.getElementById('title').textContent=m.title||'';
186
+ document.getElementById('speaker').innerHTML=esc(m.speaker||'')+' · <a target="_blank" href="'+(m.source_url||'#')+'">watch on YouTube ↗</a>';
187
+ document.getElementById('deckcount').textContent=SLIDES.length+' slides · drag the divider ⋮⋮ to resize';
188
+ document.getElementById('now-t').textContent='--:--';
189
+ document.getElementById('now-tx').textContent='Click any slide to play the video from that point.';
190
+ document.getElementById('ytplayer').src='https://www.youtube.com/embed/'+YTID+'?enablejsapi=1&rel=0&playsinline=1';
191
+ SEGS=parseTranscript(DATA.body||'');
192
+ renderDeck(); renderTranscript();
193
+ }catch(e){var el=document.getElementById('err');el.style.display='block';el.textContent='Could not load library data: '+e.message+'. Is the server running?';}
194
+ }
195
+ function parseTranscript(body){
196
+ var out=[],re=/^\[(\d{2}):(\d{2}):(\d{2})\]\s*(.*)$/;
197
+ body.split('\n').forEach(function(line){var mm=line.match(re);if(mm){var sec=(+mm[1])*3600+(+mm[2])*60+(+mm[3]);out.push({t:sec,text:mm[4]});}});
198
+ return out;
199
+ }
200
+ function renderDeck(){
201
+ var deck=document.getElementById('deck');deck.innerHTML='';
202
+ SLIDES.forEach(function(s,i){
203
+ var d=document.createElement('div');d.className='slide';d.id='slide-'+i;d.dataset.t=s.t;
204
+ d.innerHTML='<div class="slide-img"><img src="'+esc(s.img)+'" alt="'+esc(s.title)+'"><span class="play-badge">▶</span><span class="slide-t">'+esc(s.mmss||fmt(s.t))+'</span></div>'
205
+ +'<div class="slide-meta"><h3>'+esc(s.title)+'</h3><button class="btn">▶ Play '+esc(s.mmss||fmt(s.t))+'</button></div>'
206
+ +'<label class="note-lbl">Notes <span class="saved" id="saved-'+i+'"></span></label>'
207
+ +'<textarea class="note-area" id="note-'+i+'"></textarea>';
208
+ deck.appendChild(d);
209
+ d.querySelector('textarea').value=s.note||'';
210
+ d.querySelector('.slide-img').onclick=function(){play(i);};
211
+ d.querySelector('.btn').onclick=function(){play(i);};
212
+ d.querySelector('textarea').addEventListener('input',function(){onNote(i,this.value);});
213
+ });
214
+ }
215
+ function renderTranscript(){
216
+ var c=document.getElementById('transcript');c.innerHTML='';
217
+ SEGS.forEach(function(seg){
218
+ var r=document.createElement('div');r.className='trow';r.dataset.t=seg.t;r.dataset.text=seg.text.toLowerCase();
219
+ r.innerHTML='<span class="tt">'+fmt(seg.t)+'</span><span class="tx">'+esc(seg.text)+'</span>';
220
+ r.onclick=function(){seekOnly(seg.t);};c.appendChild(r);
221
+ });
222
+ }
223
+ function loadAt(t){
224
+ // Robust across video switches: use the JS API to load the right video at t.
225
+ var vd=player.getVideoData?player.getVideoData():null;
226
+ if(vd && vd.video_id===YTID){player.seekTo(t,true);player.playVideo();}
227
+ else{player.loadVideoById({videoId:YTID,startSeconds:Math.floor(t)});}
228
+ }
229
+ function doPlay(t){loadAt(t);}
230
+ function srcFallback(t){document.getElementById('ytplayer').src='https://www.youtube.com/embed/'+YTID+'?enablejsapi=1&rel=0&playsinline=1&autoplay=1&start='+Math.floor(t);}
231
+ function setJump(t){var a=document.getElementById('yt-jump');if(a){a.href='https://www.youtube.com/watch?v='+YTID+'&t='+Math.floor(t)+'s';a.style.display='inline';}}
232
+ function play(i){
233
+ var s=SLIDES[i];
234
+ document.querySelectorAll('.slide.active').forEach(function(x){x.classList.remove('active')});
235
+ var card=document.getElementById('slide-'+i);if(card)card.classList.add('active');
236
+ document.getElementById('now-t').textContent=s.mmss||fmt(s.t);
237
+ document.getElementById('now-tx').textContent=transcriptAt(s.t)||'(no transcript here)';
238
+ setJump(s.t);
239
+ if(ready&&player&&player.loadVideoById)doPlay(s.t);else{pending=s.t;srcFallback(s.t);}
240
+ hlRow(s.t);
241
+ }
242
+ function seekOnly(t){document.getElementById('now-t').textContent=fmt(t);document.getElementById('now-tx').textContent=transcriptAt(t);setJump(t);if(ready&&player&&player.loadVideoById)doPlay(t);else{pending=t;srcFallback(t);}hlRow(t);}
243
+ function transcriptAt(t){var out=[];SEGS.forEach(function(s){if(s.t>=t-1&&s.t<=t+10)out.push(s.text);});return out.join(' ');}
244
+ function hlRow(t){var rows=document.querySelectorAll('.trow'),best=null;rows.forEach(function(r){if(parseFloat(r.dataset.t)<=t+0.5)best=r;});document.querySelectorAll('.trow.hl').forEach(function(r){r.classList.remove('hl')});if(best){best.classList.add('hl');best.scrollIntoView({block:'nearest'});}}
245
+ function filt(q){q=q.toLowerCase().trim();document.querySelectorAll('.trow').forEach(function(r){r.style.display=(!q||r.dataset.text.indexOf(q)>-1)?'flex':'none';});}
246
+ // note write-back to markdown via PATCH
247
+ var timers={};
248
+ function onNote(i,val){
249
+ SLIDES[i].note=val;
250
+ var s=document.getElementById('saved-'+i);s.textContent='saving…';
251
+ clearTimeout(timers[i]);
252
+ timers[i]=setTimeout(function(){saveNotes(i,s);},700);
253
+ }
254
+ async function saveNotes(i,badge){
255
+ try{
256
+ var payload={fields:{slides:SLIDES.map(function(s){return {idx:s.idx,t:s.t,mmss:s.mmss,title:s.title,note:s.note,img:s.img};})}};
257
+ var r=await fetch(API_URL+'/'+encodeURIComponent(CURRENT_ID),{method:'PATCH',headers:{'Content-Type':'application/json'},body:JSON.stringify(payload)});
258
+ badge.textContent=r.ok?'✓ saved':'save failed';
259
+ }catch(e){badge.textContent='save failed';}
260
+ setTimeout(function(){badge.textContent='';},1500);
261
+ }
262
+ (function(){var dv=document.getElementById('divider'),sp=document.getElementById('split'),lf=document.getElementById('left'),drag=false;
263
+ dv.addEventListener('mousedown',function(e){drag=true;dv.classList.add('drag');e.preventDefault();});
264
+ window.addEventListener('mousemove',function(e){if(!drag)return;var r=sp.getBoundingClientRect();var pct=(e.clientX-r.left)/r.width*100;pct=Math.max(25,Math.min(80,pct));lf.style.flexBasis=pct+'%';});
265
+ window.addEventListener('mouseup',function(){drag=false;dv.classList.remove('drag');});})();
266
+
267
+ window.addEventListener('hashchange',route);
268
+ route();
269
+ </script></body></html>
@@ -0,0 +1,53 @@
1
+ #!/usr/bin/env python3
2
+ """Build a labeled contact sheet of candidate slide frames for human curation.
3
+
4
+ Usage: contact_sheet.py <video.mp4> <scene_times.txt> <out.jpg> [--cols 5] [--thumb 360]
5
+
6
+ Reads timestamps (seconds, one per line), grabs a frame at each, lays them out in a
7
+ grid labeled "<index> | <mm:ss>". Read the output image, then write the timestamps you
8
+ want to KEEP (real content slides, not talking-head/transition frames) to a keep.txt,
9
+ one per line. The index labels make it easy to call out which to drop.
10
+ """
11
+ import subprocess, sys, tempfile, os, argparse
12
+ from PIL import Image, ImageDraw, ImageFont
13
+
14
+ def grab(video, t, path, w=360):
15
+ subprocess.run(["ffmpeg","-hide_banner","-loglevel","error","-ss",str(t),
16
+ "-i",video,"-frames:v","1","-vf",f"scale={w}:-1","-y",path], check=True)
17
+
18
+ def mmss(t):
19
+ t=int(float(t)); return f"{t//60:02d}:{t%60:02d}"
20
+
21
+ def main():
22
+ ap=argparse.ArgumentParser()
23
+ ap.add_argument("video"); ap.add_argument("times"); ap.add_argument("out")
24
+ ap.add_argument("--cols",type=int,default=5); ap.add_argument("--thumb",type=int,default=360)
25
+ a=ap.parse_args()
26
+ times=[l.strip() for l in open(a.times) if l.strip()]
27
+ if not times: sys.exit("no timestamps")
28
+ tmp=tempfile.mkdtemp()
29
+ thumbs=[]
30
+ for i,t in enumerate(times):
31
+ p=os.path.join(tmp,f"f{i:03d}.jpg")
32
+ try:
33
+ grab(a.video,t,p,a.thumb); thumbs.append((i,t,p))
34
+ except subprocess.CalledProcessError:
35
+ pass
36
+ if not thumbs: sys.exit("could not grab any frames")
37
+ tw=a.thumb; th=int(tw*9/16); lab=22; pad=6
38
+ cols=a.cols; rows=(len(thumbs)+cols-1)//cols
39
+ cw=tw+pad*2; ch=th+lab+pad*2
40
+ sheet=Image.new("RGB",(cols*cw,rows*ch),(20,20,20))
41
+ d=ImageDraw.Draw(sheet)
42
+ try: font=ImageFont.truetype("/System/Library/Fonts/Supplemental/Arial Bold.ttf",15)
43
+ except Exception: font=ImageFont.load_default()
44
+ for n,(idx,t,p) in enumerate(thumbs):
45
+ r,c=divmod(n,cols); x=c*cw+pad; y=r*ch+pad
46
+ im=Image.open(p).convert("RGB").resize((tw,th))
47
+ sheet.paste(im,(x,y+lab))
48
+ d.text((x+2,y+2),f"{idx} | {mmss(t)} ({float(t):.1f}s)",fill=(255,210,90),font=font)
49
+ sheet.save(a.out,quality=85)
50
+ print(f"contact sheet: {a.out} ({len(thumbs)} frames, {cols}x{rows})")
51
+ print("Read it, then write the timestamps (seconds) to keep -> keep.txt (one per line).")
52
+
53
+ if __name__=="__main__": main()
@@ -0,0 +1,19 @@
1
+ #!/usr/bin/env bash
2
+ # Scene-detect candidate slide-change timestamps with ffmpeg.
3
+ # Usage: detect_slides.sh <video.mp4> <out_dir> [threshold]
4
+ # threshold default 0.3 (lower=more frames for subtle decks, higher=fewer for busy video).
5
+ set -euo pipefail
6
+ VIDEO="${1:?usage: detect_slides.sh <video.mp4> <out_dir> [threshold]}"
7
+ OUT="${2:?usage: detect_slides.sh <video.mp4> <out_dir> [threshold]}"
8
+ THRESH="${3:-0.3}"
9
+ mkdir -p "$OUT"
10
+
11
+ # showinfo on the scene-selected frames prints pts_time per cut.
12
+ ffmpeg -hide_banner -i "$VIDEO" \
13
+ -vf "select='gt(scene,$THRESH)',showinfo" -vsync vfr -f null - 2>"$OUT/ffinfo.log" || true
14
+
15
+ grep -oE 'pts_time:[0-9.]+' "$OUT/ffinfo.log" | sed 's/pts_time://' | sort -n -u > "$OUT/scene_times.txt"
16
+
17
+ N=$(wc -l < "$OUT/scene_times.txt" | tr -d ' ')
18
+ echo "Detected $N candidate scene changes (threshold=$THRESH) -> $OUT/scene_times.txt"
19
+ echo "Next: build a contact sheet and curate which are real content slides."
@@ -0,0 +1,24 @@
1
+ #!/usr/bin/env bash
2
+ # Download video (<=720p) + best subtitles for slide/transcript extraction.
3
+ # Usage: download.sh "<YTID>" "<scratch_dir>"
4
+ set -euo pipefail
5
+ YTID="${1:?usage: download.sh <YTID> <scratch_dir>}"
6
+ OUT="${2:?usage: download.sh <YTID> <scratch_dir>}"
7
+ mkdir -p "$OUT"
8
+ URL="https://www.youtube.com/watch?v=$YTID"
9
+
10
+ # Video: 720p mp4 is plenty for 1280px slide frames; merge to a single file.
11
+ yt-dlp -f "bestvideo[height<=720][ext=mp4]+bestaudio[ext=m4a]/best[height<=720]" \
12
+ --merge-output-format mp4 -o "$OUT/video.%(ext)s" "$URL"
13
+
14
+ # Subtitles: prefer human captions, fall back to auto. English variants.
15
+ yt-dlp --skip-download --write-subs --write-auto-subs \
16
+ --sub-langs "en.*,en" --sub-format vtt -o "$OUT/subs.%(ext)s" "$URL" || true
17
+
18
+ # Metadata for title/uploader.
19
+ yt-dlp --skip-download --print "%(title)s\n%(uploader)s\n%(duration)s" "$URL" \
20
+ > "$OUT/meta.txt" 2>/dev/null || true
21
+
22
+ echo "--- downloaded to $OUT ---"
23
+ ls -la "$OUT"
24
+ echo "title/uploader/duration:"; cat "$OUT/meta.txt" 2>/dev/null || true
@@ -0,0 +1,43 @@
1
+ #!/usr/bin/env python3
2
+ """Extract curated slide frames at full quality and install them into the library _media dir.
3
+
4
+ Usage: extract_slides.py <YTID> <video.mp4> <keep.txt>
5
+
6
+ keep.txt: one timestamp (seconds) per line, the frames you chose from the contact sheet.
7
+ Frames are extracted at 1280px wide, JPEG, numbered in time order, and copied to
8
+ $VIDEO_LIBRARY_DIR/_media/<YTID>-slide-NN.jpg (default ~/video-deepdives/_media)
9
+
10
+ Prints a slides scaffold (idx,t,mmss,img) you can paste into slides.json and then fill
11
+ in title + note for each. idx here is just the sequence number; ordering is by time.
12
+
13
+ The img URL is served by serve.py at /api/video-deepdives/_media/<file>.
14
+ """
15
+ import subprocess, sys, os, json
16
+
17
+ LIB = os.path.expanduser(os.environ.get("VIDEO_LIBRARY_DIR", "~/video-deepdives"))
18
+ MEDIA = os.path.join(LIB, "_media")
19
+ IMG_PREFIX = "/api/video-deepdives/_media" # served by serve.py
20
+
21
+ def mmss(t):
22
+ t=int(round(float(t))); return f"{t//60:02d}:{t%60:02d}"
23
+
24
+ def main():
25
+ if len(sys.argv)!=4: sys.exit("usage: extract_slides.py <YTID> <video.mp4> <keep.txt>")
26
+ ytid,video,keep=sys.argv[1],sys.argv[2],sys.argv[3]
27
+ times=sorted({float(l.strip()) for l in open(keep) if l.strip()})
28
+ if not times: sys.exit("keep.txt is empty")
29
+ os.makedirs(MEDIA,exist_ok=True)
30
+ scaffold=[]
31
+ for i,t in enumerate(times,1):
32
+ fn=f"{ytid}-slide-{i:02d}.jpg"
33
+ out=os.path.join(MEDIA,fn)
34
+ subprocess.run(["ffmpeg","-hide_banner","-loglevel","error","-ss",f"{t}",
35
+ "-i",video,"-frames:v","1","-vf","scale=1280:-1","-q:v","3","-y",out],check=True)
36
+ scaffold.append({"idx":i,"t":round(t,1),"mmss":mmss(t),"title":"","note":"",
37
+ "img":f"{IMG_PREFIX}/{fn}"})
38
+ print(f" wrote {fn} @ {mmss(t)}",file=sys.stderr)
39
+ print(f"\nInstalled {len(scaffold)} slides to {MEDIA}",file=sys.stderr)
40
+ print("--- slides.json scaffold on stdout; redirect to a file, then fill in title + note ---",file=sys.stderr)
41
+ print(json.dumps(scaffold,indent=2))
42
+
43
+ if __name__=="__main__": main()