harveyz-skill 0.28.0 → 0.29.0

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 (413) hide show
  1. package/CHANGELOG.md +16 -0
  2. package/package.json +10 -5
  3. package/skills/coding/handoff/SKILL.md +9 -3
  4. package/skills/coding/handoff/assets/handoff-template.md +1 -1
  5. package/skills/coding/handoff/evals/evals.json +50 -4
  6. package/skills/coding/setup-debug/evals/evals.json +23 -1
  7. package/skills/feed/capture-creator/SKILL.md +107 -0
  8. package/skills/feed/capture-creator/scripts/roster_locate.py +46 -0
  9. package/skills/feed/manage-roster/SKILL.md +70 -0
  10. package/skills/feed/manage-roster/scripts/__pycache__/mcp_call_locate.cpython-314.pyc +0 -0
  11. package/skills/feed/manage-roster/scripts/__pycache__/roster_locate.cpython-314.pyc +0 -0
  12. package/skills/feed/manage-roster/scripts/__pycache__/roster_mcp_locate.cpython-314.pyc +0 -0
  13. package/skills/feed/manage-roster/scripts/roster_locate.py +46 -0
  14. package/skills/feed/manage-roster/tests/__pycache__/test_roster_locate.cpython-314-pytest-9.1.1.pyc +0 -0
  15. package/skills/feed/manage-roster/tests/__pycache__/test_roster_mcp_client.cpython-314-pytest-9.1.1.pyc +0 -0
  16. package/skills/feed/manage-roster/tests/__pycache__/test_roster_mcp_locate.cpython-314-pytest-9.1.1.pyc +0 -0
  17. package/skills/feed/manage-roster/tests/test_roster_locate.py +24 -0
  18. package/skills/feed/sync-xtimeline/SKILL.md +86 -0
  19. package/skills/feed/sync-xtimeline/platforms/SKILL.claude.md +18 -0
  20. package/skills/feed/sync-xtimeline/platforms/SKILL.codex.md +22 -0
  21. package/skills/feed/sync-xtimeline/platforms/SKILL.hermes.md +22 -0
  22. package/skills/feed/sync-xtimeline/platforms/SKILL.pi.md +18 -0
  23. package/skills/feed/sync-xtimeline/scripts/__pycache__/archive_tweets.cpython-314.pyc +0 -0
  24. package/skills/feed/sync-xtimeline/scripts/__pycache__/browser_fetch_cli.cpython-314.pyc +0 -0
  25. package/skills/feed/sync-xtimeline/scripts/__pycache__/browser_fetch_locate.cpython-314.pyc +0 -0
  26. package/skills/feed/sync-xtimeline/scripts/__pycache__/config.cpython-314.pyc +0 -0
  27. package/skills/feed/sync-xtimeline/scripts/__pycache__/cursor.cpython-314.pyc +0 -0
  28. package/skills/feed/sync-xtimeline/scripts/__pycache__/fetch_new_tweets.cpython-314.pyc +0 -0
  29. package/skills/feed/sync-xtimeline/scripts/__pycache__/mcp_timeline_client.cpython-314.pyc +0 -0
  30. package/skills/feed/sync-xtimeline/scripts/__pycache__/render_digest.cpython-314.pyc +0 -0
  31. package/skills/feed/sync-xtimeline/scripts/__pycache__/render_view.cpython-314.pyc +0 -0
  32. package/skills/feed/sync-xtimeline/scripts/__pycache__/roster_client.cpython-314.pyc +0 -0
  33. package/skills/feed/sync-xtimeline/scripts/__pycache__/roster_locate.cpython-314.pyc +0 -0
  34. package/skills/{research → feed}/sync-xtimeline/scripts/archive_tweets.py +2 -6
  35. package/skills/feed/sync-xtimeline/scripts/browser_fetch_cli.py +23 -0
  36. package/skills/{research/clip-url/scripts/browser_fetch_mcp_locate.py → feed/sync-xtimeline/scripts/browser_fetch_locate.py} +16 -14
  37. package/skills/feed/sync-xtimeline/scripts/config.py +18 -0
  38. package/skills/feed/sync-xtimeline/scripts/cursor.py +27 -0
  39. package/skills/feed/sync-xtimeline/scripts/fetch_new_tweets.py +111 -0
  40. package/skills/feed/sync-xtimeline/scripts/mcp_timeline_client.py +20 -0
  41. package/skills/{research → feed}/sync-xtimeline/scripts/render_digest.py +3 -7
  42. package/skills/{research → feed}/sync-xtimeline/scripts/render_view.py +3 -8
  43. package/skills/feed/sync-xtimeline/scripts/roster_client.py +49 -0
  44. package/skills/feed/sync-xtimeline/scripts/roster_locate.py +46 -0
  45. package/skills/feed/sync-xtimeline/tests/__pycache__/conftest.cpython-314-pytest-9.1.1.pyc +0 -0
  46. package/skills/{research → feed}/sync-xtimeline/tests/__pycache__/test_archive_tweets.cpython-314-pytest-9.1.1.pyc +0 -0
  47. package/skills/feed/sync-xtimeline/tests/__pycache__/test_browser_fetch_locate.cpython-314-pytest-9.1.1.pyc +0 -0
  48. package/skills/feed/sync-xtimeline/tests/__pycache__/test_cursor.cpython-314-pytest-9.1.1.pyc +0 -0
  49. package/skills/feed/sync-xtimeline/tests/__pycache__/test_fetch_new_tweets.cpython-314-pytest-9.1.1.pyc +0 -0
  50. package/skills/feed/sync-xtimeline/tests/__pycache__/test_mcp_timeline_client.cpython-314-pytest-9.1.1.pyc +0 -0
  51. package/skills/{research → feed}/sync-xtimeline/tests/__pycache__/test_render_digest.cpython-314-pytest-9.1.1.pyc +0 -0
  52. package/skills/{research → feed}/sync-xtimeline/tests/__pycache__/test_render_view.cpython-314-pytest-9.1.1.pyc +0 -0
  53. package/skills/feed/sync-xtimeline/tests/__pycache__/test_roster_client.cpython-314-pytest-9.1.1.pyc +0 -0
  54. package/skills/feed/sync-xtimeline/tests/conftest.py +41 -0
  55. package/skills/{research → feed}/sync-xtimeline/tests/test_archive_tweets.py +4 -1
  56. package/skills/feed/sync-xtimeline/tests/test_browser_fetch_locate.py +69 -0
  57. package/skills/feed/sync-xtimeline/tests/test_cursor.py +40 -0
  58. package/skills/feed/sync-xtimeline/tests/test_fetch_new_tweets.py +233 -0
  59. package/skills/feed/sync-xtimeline/tests/test_mcp_timeline_client.py +44 -0
  60. package/skills/{research → feed}/sync-xtimeline/tests/test_render_digest.py +18 -1
  61. package/skills/{research → feed}/sync-xtimeline/tests/test_render_view.py +9 -3
  62. package/skills/feed/sync-xtimeline/tests/test_roster_client.py +80 -0
  63. package/skills/feed/sync-ytchannel/SKILL.md +75 -0
  64. package/skills/feed/sync-ytchannel/platforms/SKILL.claude.md +17 -0
  65. package/skills/feed/sync-ytchannel/platforms/SKILL.codex.md +21 -0
  66. package/skills/feed/sync-ytchannel/platforms/SKILL.hermes.md +21 -0
  67. package/skills/feed/sync-ytchannel/platforms/SKILL.pi.md +17 -0
  68. package/skills/feed/sync-ytchannel/scripts/__pycache__/browser_fetch_cli.cpython-314.pyc +0 -0
  69. package/skills/feed/sync-ytchannel/scripts/__pycache__/browser_fetch_locate.cpython-314.pyc +0 -0
  70. package/skills/feed/sync-ytchannel/scripts/__pycache__/config.cpython-314.pyc +0 -0
  71. package/skills/feed/sync-ytchannel/scripts/__pycache__/cursor.cpython-314.pyc +0 -0
  72. package/skills/feed/sync-ytchannel/scripts/__pycache__/digest.cpython-314.pyc +0 -0
  73. package/skills/feed/sync-ytchannel/scripts/__pycache__/mcp_channel_client.cpython-314.pyc +0 -0
  74. package/skills/feed/sync-ytchannel/scripts/__pycache__/roster_client.cpython-314.pyc +0 -0
  75. package/skills/feed/sync-ytchannel/scripts/__pycache__/roster_locate.cpython-314.pyc +0 -0
  76. package/skills/feed/sync-ytchannel/scripts/__pycache__/sync_channels.cpython-314.pyc +0 -0
  77. package/skills/feed/sync-ytchannel/scripts/browser_fetch_cli.py +23 -0
  78. package/skills/{research/sync-xtimeline/scripts/browser_fetch_mcp_locate.py → feed/sync-ytchannel/scripts/browser_fetch_locate.py} +16 -14
  79. package/skills/feed/sync-ytchannel/scripts/config.py +18 -0
  80. package/skills/feed/sync-ytchannel/scripts/cursor.py +29 -0
  81. package/skills/feed/sync-ytchannel/scripts/digest.py +47 -0
  82. package/skills/feed/sync-ytchannel/scripts/mcp_channel_client.py +18 -0
  83. package/skills/feed/sync-ytchannel/scripts/roster_client.py +49 -0
  84. package/skills/feed/sync-ytchannel/scripts/roster_locate.py +46 -0
  85. package/skills/feed/sync-ytchannel/scripts/sync_channels.py +120 -0
  86. package/skills/feed/sync-ytchannel/tests/__pycache__/conftest.cpython-314-pytest-9.1.1.pyc +0 -0
  87. package/skills/feed/sync-ytchannel/tests/__pycache__/test_browser_fetch_locate.cpython-314-pytest-9.1.1.pyc +0 -0
  88. package/skills/feed/sync-ytchannel/tests/__pycache__/test_cursor.cpython-314-pytest-9.1.1.pyc +0 -0
  89. package/skills/feed/sync-ytchannel/tests/__pycache__/test_digest.cpython-314-pytest-9.1.1.pyc +0 -0
  90. package/skills/feed/sync-ytchannel/tests/__pycache__/test_mcp_channel_client.cpython-314-pytest-9.1.1.pyc +0 -0
  91. package/skills/feed/sync-ytchannel/tests/__pycache__/test_roster_client.cpython-314-pytest-9.1.1.pyc +0 -0
  92. package/skills/feed/sync-ytchannel/tests/__pycache__/test_sync_channels.cpython-314-pytest-9.1.1.pyc +0 -0
  93. package/skills/feed/sync-ytchannel/tests/conftest.py +7 -0
  94. package/skills/feed/sync-ytchannel/tests/test_browser_fetch_locate.py +69 -0
  95. package/skills/feed/sync-ytchannel/tests/test_cursor.py +47 -0
  96. package/skills/feed/sync-ytchannel/tests/test_digest.py +77 -0
  97. package/skills/feed/sync-ytchannel/tests/test_mcp_channel_client.py +42 -0
  98. package/skills/feed/sync-ytchannel/tests/test_roster_client.py +83 -0
  99. package/skills/feed/sync-ytchannel/tests/test_sync_channels.py +250 -0
  100. package/skills/mint/learn-skill/evals/evals.json +34 -16
  101. package/skills/research/clip-url/SKILL.md +47 -20
  102. package/skills/research/clip-url/platforms/SKILL.claude.md +28 -0
  103. package/skills/research/clip-url/platforms/SKILL.codex.md +21 -0
  104. package/skills/research/clip-url/platforms/SKILL.hermes.md +21 -0
  105. package/skills/research/clip-url/platforms/SKILL.pi.md +28 -0
  106. package/skills/research/clip-url/references/subagent-self-optimize-prompt.md +11 -12
  107. package/skills/research/clip-url/references/subagent1-fetch-prompt.md +3 -3
  108. package/skills/research/clip-url/scripts/__pycache__/browser_fetch_cli.cpython-314.pyc +0 -0
  109. package/skills/research/clip-url/scripts/__pycache__/browser_fetch_locate.cpython-314.pyc +0 -0
  110. package/skills/research/clip-url/scripts/__pycache__/mcp_debug_client.cpython-314.pyc +0 -0
  111. package/skills/research/clip-url/scripts/__pycache__/mcp_fetch_client.cpython-314.pyc +0 -0
  112. package/skills/research/clip-url/scripts/__pycache__/vault_config.cpython-314.pyc +0 -0
  113. package/skills/research/clip-url/scripts/browser_fetch_cli.py +23 -0
  114. package/skills/research/clip-url/scripts/browser_fetch_locate.py +50 -0
  115. package/skills/research/clip-url/scripts/chrome_profile_config.py +21 -47
  116. package/skills/research/clip-url/scripts/detect_xcom_chrome_profile.py +12 -32
  117. package/skills/research/clip-url/scripts/mcp_debug_client.py +34 -53
  118. package/skills/research/clip-url/scripts/mcp_fetch_client.py +21 -57
  119. package/skills/research/clip-url/scripts/vault_config.py +25 -5
  120. package/skills/research/clip-url/tests/__pycache__/test_browser_fetch_locate.cpython-314-pytest-9.1.1.pyc +0 -0
  121. package/skills/research/clip-url/tests/__pycache__/test_chrome_profile_config.cpython-314-pytest-9.1.1.pyc +0 -0
  122. package/skills/research/clip-url/tests/__pycache__/test_detect_xcom_chrome_profile.cpython-314-pytest-9.1.1.pyc +0 -0
  123. package/skills/research/clip-url/tests/__pycache__/test_mcp_debug_client.cpython-314-pytest-9.1.1.pyc +0 -0
  124. package/skills/research/clip-url/tests/__pycache__/test_mcp_fetch_client.cpython-314-pytest-9.1.1.pyc +0 -0
  125. package/skills/research/clip-url/tests/__pycache__/test_vault_config.cpython-314-pytest-9.1.1.pyc +0 -0
  126. package/skills/research/clip-url/tests/test_browser_fetch_locate.py +69 -0
  127. package/skills/research/clip-url/tests/test_chrome_profile_config.py +5 -5
  128. package/skills/research/clip-url/tests/test_detect_xcom_chrome_profile.py +5 -6
  129. package/skills/research/clip-url/tests/test_mcp_debug_client.py +5 -6
  130. package/skills/research/clip-url/tests/test_mcp_fetch_client.py +11 -19
  131. package/skills/research/clip-url/tests/test_vault_config.py +18 -0
  132. package/skills/research/extract-cognition/evals/evals.json +17 -0
  133. package/skills/research/fetch-paper/SKILL.md +1 -1
  134. package/skills/writing/forge-doc/tests/__pycache__/test_md_to_pdf.cpython-314-pytest-9.1.1.pyc +0 -0
  135. package/skills-index.json +44 -24
  136. package/tools/browser-fetch/browser-fetch.sh +69 -0
  137. package/tools/browser-fetch/browser_fetch/__init__.py +1 -0
  138. package/tools/browser-fetch/browser_fetch/__pycache__/__init__.cpython-314.pyc +0 -0
  139. package/tools/browser-fetch/browser_fetch/__pycache__/cli.cpython-314.pyc +0 -0
  140. package/tools/browser-fetch/browser_fetch/__pycache__/config.cpython-314.pyc +0 -0
  141. package/tools/{browser-fetch-mcp/browser_fetch_mcp → browser-fetch/browser_fetch}/__pycache__/cookies.cpython-314.pyc +0 -0
  142. package/tools/browser-fetch/browser_fetch/__pycache__/core.cpython-314.pyc +0 -0
  143. package/tools/{browser-fetch-mcp/browser_fetch_mcp → browser-fetch/browser_fetch}/__pycache__/extractors.cpython-314.pyc +0 -0
  144. package/tools/{browser-fetch-mcp/browser_fetch_mcp → browser-fetch/browser_fetch}/__pycache__/images.cpython-314.pyc +0 -0
  145. package/tools/{browser-fetch-mcp/browser_fetch_mcp → browser-fetch/browser_fetch}/__pycache__/markdown.cpython-314.pyc +0 -0
  146. package/tools/browser-fetch/browser_fetch/__pycache__/pacing.cpython-314.pyc +0 -0
  147. package/tools/browser-fetch/browser_fetch/__pycache__/pacing_log.cpython-314.pyc +0 -0
  148. package/tools/{browser-fetch-mcp/browser_fetch_mcp → browser-fetch/browser_fetch}/__pycache__/profiles.cpython-314.pyc +0 -0
  149. package/tools/browser-fetch/browser_fetch/cli.py +100 -0
  150. package/tools/{browser-fetch-mcp/browser_fetch_mcp → browser-fetch/browser_fetch}/config.py +18 -1
  151. package/tools/{browser-fetch-mcp/browser_fetch_mcp/server.py → browser-fetch/browser_fetch/core.py} +222 -50
  152. package/tools/{browser-fetch-mcp/browser_fetch_mcp → browser-fetch/browser_fetch}/extractors.py +180 -2
  153. package/tools/browser-fetch/browser_fetch/pacing.py +66 -0
  154. package/tools/browser-fetch/browser_fetch/pacing_log.py +28 -0
  155. package/tools/{browser-fetch-mcp/browser_fetch_mcp → browser-fetch/browser_fetch}/profiles.py +1 -1
  156. package/tools/{browser-fetch-mcp → browser-fetch}/pyproject.toml +4 -5
  157. package/tools/browser-fetch/tests/__pycache__/conftest.cpython-314-pytest-9.1.1.pyc +0 -0
  158. package/tools/browser-fetch/tests/__pycache__/test_cli.cpython-314-pytest-9.1.1.pyc +0 -0
  159. package/tools/browser-fetch/tests/__pycache__/test_cli_fetch.cpython-314-pytest-9.1.1.pyc +0 -0
  160. package/tools/browser-fetch/tests/__pycache__/test_cli_page_eval.cpython-314-pytest-9.1.1.pyc +0 -0
  161. package/tools/browser-fetch/tests/__pycache__/test_config.cpython-314-pytest-9.1.1.pyc +0 -0
  162. package/tools/{browser-fetch-mcp → browser-fetch}/tests/__pycache__/test_cookies.cpython-314-pytest-9.1.1.pyc +0 -0
  163. package/tools/browser-fetch/tests/__pycache__/test_core_import.cpython-314-pytest-9.1.1.pyc +0 -0
  164. package/tools/browser-fetch/tests/__pycache__/test_data_dir_migration.cpython-314-pytest-9.1.1.pyc +0 -0
  165. package/tools/browser-fetch/tests/__pycache__/test_evaluate_js.cpython-314-pytest-9.1.1.pyc +0 -0
  166. package/tools/{browser-fetch-mcp → browser-fetch}/tests/__pycache__/test_extractors.cpython-314-pytest-9.1.1.pyc +0 -0
  167. package/tools/{browser-fetch-mcp → browser-fetch}/tests/__pycache__/test_extractors_timeline.cpython-314-pytest-9.1.1.pyc +0 -0
  168. package/tools/browser-fetch/tests/__pycache__/test_extractors_youtube.cpython-314-pytest-9.1.1.pyc +0 -0
  169. package/tools/browser-fetch/tests/__pycache__/test_fetch_article.cpython-314-pytest-9.1.1.pyc +0 -0
  170. package/tools/browser-fetch/tests/__pycache__/test_fetch_channel_videos.cpython-314-pytest-9.1.1.pyc +0 -0
  171. package/tools/browser-fetch/tests/__pycache__/test_fetch_user_timeline.cpython-314-pytest-9.1.1.pyc +0 -0
  172. package/tools/{browser-fetch-mcp → browser-fetch}/tests/__pycache__/test_images.cpython-314-pytest-9.1.1.pyc +0 -0
  173. package/tools/browser-fetch/tests/__pycache__/test_launcher_python_version.cpython-314-pytest-9.1.1.pyc +0 -0
  174. package/tools/browser-fetch/tests/__pycache__/test_launcher_stdout_contract.cpython-314-pytest-9.1.1.pyc +0 -0
  175. package/tools/{browser-fetch-mcp → browser-fetch}/tests/__pycache__/test_markdown.cpython-314-pytest-9.1.1.pyc +0 -0
  176. package/tools/browser-fetch/tests/__pycache__/test_pacing.cpython-314-pytest-9.1.1.pyc +0 -0
  177. package/tools/browser-fetch/tests/__pycache__/test_pacing_log.cpython-314-pytest-9.1.1.pyc +0 -0
  178. package/tools/{browser-fetch-mcp → browser-fetch}/tests/__pycache__/test_profiles.cpython-314-pytest-9.1.1.pyc +0 -0
  179. package/tools/browser-fetch/tests/conftest.py +30 -0
  180. package/tools/browser-fetch/tests/test_cli.py +44 -0
  181. package/tools/browser-fetch/tests/test_cli_fetch.py +45 -0
  182. package/tools/browser-fetch/tests/test_cli_page_eval.py +48 -0
  183. package/tools/browser-fetch/tests/test_config.py +47 -0
  184. package/tools/{browser-fetch-mcp → browser-fetch}/tests/test_cookies.py +3 -3
  185. package/tools/browser-fetch/tests/test_core_import.py +38 -0
  186. package/tools/browser-fetch/tests/test_data_dir_migration.py +45 -0
  187. package/tools/browser-fetch/tests/test_evaluate_js.py +27 -0
  188. package/tools/{browser-fetch-mcp → browser-fetch}/tests/test_extractors.py +1 -1
  189. package/tools/{browser-fetch-mcp → browser-fetch}/tests/test_extractors_timeline.py +60 -1
  190. package/tools/browser-fetch/tests/test_extractors_youtube.py +291 -0
  191. package/tools/{browser-fetch-mcp → browser-fetch}/tests/test_fetch_article.py +105 -176
  192. package/tools/browser-fetch/tests/test_fetch_channel_videos.py +39 -0
  193. package/tools/browser-fetch/tests/test_fetch_user_timeline.py +31 -0
  194. package/tools/{browser-fetch-mcp → browser-fetch}/tests/test_images.py +1 -1
  195. package/tools/browser-fetch/tests/test_launcher_python_version.py +50 -0
  196. package/tools/browser-fetch/tests/test_launcher_stdout_contract.py +47 -0
  197. package/tools/{browser-fetch-mcp → browser-fetch}/tests/test_markdown.py +1 -1
  198. package/tools/browser-fetch/tests/test_pacing.py +93 -0
  199. package/tools/browser-fetch/tests/test_pacing_log.py +53 -0
  200. package/tools/{browser-fetch-mcp → browser-fetch}/tests/test_profiles.py +6 -6
  201. package/tools/browser-fetch/tool.json +8 -0
  202. package/tools/hub/hub/core/__pycache__/todo_sync.cpython-314.pyc +0 -0
  203. package/tools/hub/hub/tui/__pycache__/app.cpython-314.pyc +0 -0
  204. package/tools/hub/hub/tui/panels/__pycache__/projects.cpython-314.pyc +0 -0
  205. package/tools/hub/hub/tui/panels/__pycache__/tasks.cpython-314.pyc +0 -0
  206. package/tools/hub/hub.sh +15 -2
  207. package/tools/hub/pyproject.toml +1 -1
  208. package/tools/hub/tests/__pycache__/test_cli.cpython-314-pytest-9.1.0.pyc +0 -0
  209. package/tools/hub/tests/__pycache__/test_cli.cpython-314-pytest-9.1.1.pyc +0 -0
  210. package/tools/hub/tests/__pycache__/test_db.cpython-314-pytest-9.1.1.pyc +0 -0
  211. package/tools/hub/tests/__pycache__/test_launcher_python_version.cpython-314-pytest-9.1.0.pyc +0 -0
  212. package/tools/hub/tests/__pycache__/test_launcher_python_version.cpython-314-pytest-9.1.1.pyc +0 -0
  213. package/tools/hub/tests/__pycache__/test_migrate.cpython-314-pytest-9.1.1.pyc +0 -0
  214. package/tools/hub/tests/__pycache__/test_projects.cpython-314-pytest-9.1.1.pyc +0 -0
  215. package/tools/hub/tests/__pycache__/test_scan.cpython-314-pytest-9.1.0.pyc +0 -0
  216. package/tools/hub/tests/__pycache__/test_scan.cpython-314-pytest-9.1.1.pyc +0 -0
  217. package/tools/hub/tests/__pycache__/test_tasks.cpython-314-pytest-9.1.1.pyc +0 -0
  218. package/tools/hub/tests/__pycache__/test_todo_sync.cpython-314-pytest-9.1.0.pyc +0 -0
  219. package/tools/hub/tests/__pycache__/test_todo_sync.cpython-314-pytest-9.1.1.pyc +0 -0
  220. package/tools/hub/tests/__pycache__/test_tui_app.cpython-314-pytest-9.1.0.pyc +0 -0
  221. package/tools/hub/tests/__pycache__/test_tui_app.cpython-314-pytest-9.1.1.pyc +0 -0
  222. package/tools/hub/tests/__pycache__/test_tui_git.cpython-314-pytest-9.1.1.pyc +0 -0
  223. package/tools/hub/tests/__pycache__/test_tui_git_panel.cpython-314-pytest-9.1.0.pyc +0 -0
  224. package/tools/hub/tests/__pycache__/test_tui_git_panel.cpython-314-pytest-9.1.1.pyc +0 -0
  225. package/tools/hub/tests/__pycache__/test_tui_projects_panel.cpython-314-pytest-9.1.1.pyc +0 -0
  226. package/tools/hub/tests/__pycache__/test_tui_tasks_panel.cpython-314-pytest-9.1.0.pyc +0 -0
  227. package/tools/hub/tests/__pycache__/test_tui_tasks_panel.cpython-314-pytest-9.1.1.pyc +0 -0
  228. package/tools/hub/tests/test_launcher_python_version.py +50 -0
  229. package/tools/hub/tool.json +1 -1
  230. package/tools/roster/pyproject.toml +21 -0
  231. package/tools/roster/roster/__init__.py +11 -0
  232. package/tools/roster/roster/__main__.py +299 -0
  233. package/tools/roster/roster/__pycache__/__init__.cpython-314.pyc +0 -0
  234. package/tools/roster/roster/__pycache__/__main__.cpython-314.pyc +0 -0
  235. package/tools/roster/roster/__pycache__/config.cpython-314.pyc +0 -0
  236. package/tools/roster/roster/__pycache__/migrate.cpython-314.pyc +0 -0
  237. package/tools/roster/roster/__pycache__/profiles.cpython-314.pyc +0 -0
  238. package/tools/roster/roster/__pycache__/registry.cpython-314.pyc +0 -0
  239. package/tools/roster/roster/__pycache__/server.cpython-314.pyc +0 -0
  240. package/tools/roster/roster/__pycache__/state.cpython-314.pyc +0 -0
  241. package/tools/roster/roster/__pycache__/urls.cpython-314.pyc +0 -0
  242. package/tools/roster/roster/config.py +45 -0
  243. package/tools/roster/roster/migrate.py +45 -0
  244. package/tools/roster/roster/profiles.py +115 -0
  245. package/tools/roster/roster/registry.py +126 -0
  246. package/tools/roster/roster/state.py +66 -0
  247. package/tools/roster/roster/urls.py +39 -0
  248. package/tools/roster/roster.sh +47 -0
  249. package/tools/roster/tests/__pycache__/conftest.cpython-314-pytest-9.1.1.pyc +0 -0
  250. package/tools/roster/tests/__pycache__/test_cli.cpython-314-pytest-9.1.1.pyc +0 -0
  251. package/tools/roster/tests/__pycache__/test_config.cpython-314-pytest-9.1.1.pyc +0 -0
  252. package/tools/roster/tests/__pycache__/test_launcher_python_version.cpython-314-pytest-9.1.1.pyc +0 -0
  253. package/tools/roster/tests/__pycache__/test_launcher_stdout_contract.cpython-314-pytest-9.1.1.pyc +0 -0
  254. package/tools/roster/tests/__pycache__/test_migrate.cpython-314-pytest-9.1.1.pyc +0 -0
  255. package/tools/roster/tests/__pycache__/test_profiles.cpython-314-pytest-9.1.1.pyc +0 -0
  256. package/tools/roster/tests/__pycache__/test_registry.cpython-314-pytest-9.1.1.pyc +0 -0
  257. package/tools/roster/tests/__pycache__/test_registry_merge.cpython-314-pytest-9.1.1.pyc +0 -0
  258. package/tools/roster/tests/__pycache__/test_server.cpython-314-pytest-9.1.1.pyc +0 -0
  259. package/tools/roster/tests/__pycache__/test_state.cpython-314-pytest-9.1.1.pyc +0 -0
  260. package/tools/roster/tests/__pycache__/test_urls.cpython-314-pytest-9.1.1.pyc +0 -0
  261. package/tools/roster/tests/conftest.py +16 -0
  262. package/tools/roster/tests/test_cli.py +270 -0
  263. package/tools/roster/tests/test_config.py +41 -0
  264. package/tools/roster/tests/test_launcher_python_version.py +50 -0
  265. package/tools/roster/tests/test_launcher_stdout_contract.py +38 -0
  266. package/tools/roster/tests/test_migrate.py +98 -0
  267. package/tools/roster/tests/test_profiles.py +167 -0
  268. package/tools/roster/tests/test_registry.py +131 -0
  269. package/tools/roster/tests/test_registry_merge.py +85 -0
  270. package/tools/roster/tests/test_state.py +93 -0
  271. package/tools/roster/tests/test_urls.py +43 -0
  272. package/tools/roster/tool.json +8 -0
  273. package/tools/skill-harness/adapters/claude.js +50 -0
  274. package/tools/skill-harness/adapters/hermes.js +66 -0
  275. package/tools/skill-harness/adapters/pi.js +45 -0
  276. package/tools/skill-harness/attribution.js +16 -0
  277. package/tools/skill-harness/cli.js +265 -0
  278. package/tools/skill-harness/coverage.js +64 -0
  279. package/tools/skill-harness/declarations.js +75 -0
  280. package/tools/skill-harness/grade/index.js +208 -0
  281. package/tools/skill-harness/grade/parse.js +48 -0
  282. package/tools/skill-harness/grade/prompt.js +46 -0
  283. package/tools/skill-harness/harvest.js +136 -0
  284. package/tools/skill-harness/jail.js +59 -0
  285. package/tools/skill-harness/matrix.json +9 -0
  286. package/tools/skill-harness/parse/claude-code-jsonl.js +91 -0
  287. package/tools/skill-harness/parse/pi-jsonl.js +80 -0
  288. package/tools/skill-harness/probe/probe-anchor/SKILL.md +21 -0
  289. package/tools/skill-harness/probe/probe-anchor/references/token.md +1 -0
  290. package/tools/skill-harness/profiles.js +47 -0
  291. package/tools/skill-harness/prompt.js +36 -0
  292. package/tools/skill-harness/quality-report.js +134 -0
  293. package/tools/skill-harness/record.js +53 -0
  294. package/tools/skill-harness/report.js +85 -0
  295. package/tools/skill-harness/runner.js +252 -0
  296. package/tools/skill-harness/select.js +117 -0
  297. package/tools/skill-harness/variance.js +23 -0
  298. package/tools/sync-agent/pyproject.toml +1 -1
  299. package/tools/sync-agent/sync.sh +15 -2
  300. package/tools/sync-agent/sync_agent/__pycache__/__init__.cpython-314.pyc +0 -0
  301. package/tools/sync-agent/sync_agent/__pycache__/__main__.cpython-314.pyc +0 -0
  302. package/tools/sync-agent/sync_agent/__pycache__/cli.cpython-314.pyc +0 -0
  303. package/tools/sync-agent/sync_agent/__pycache__/config.cpython-314.pyc +0 -0
  304. package/tools/sync-agent/sync_agent/__pycache__/launchd.cpython-314.pyc +0 -0
  305. package/tools/sync-agent/sync_agent/__pycache__/syncthing.cpython-314.pyc +0 -0
  306. package/tools/sync-agent/tests/__pycache__/__init__.cpython-314.pyc +0 -0
  307. package/tools/sync-agent/tests/__pycache__/test_cli.cpython-314-pytest-9.1.1.pyc +0 -0
  308. package/tools/sync-agent/tests/__pycache__/test_config.cpython-314-pytest-9.1.1.pyc +0 -0
  309. package/tools/sync-agent/tests/__pycache__/test_launcher_python_version.cpython-314-pytest-9.1.1.pyc +0 -0
  310. package/tools/sync-agent/tests/__pycache__/test_syncthing.cpython-314-pytest-9.1.1.pyc +0 -0
  311. package/tools/sync-agent/tests/test_launcher_python_version.py +50 -0
  312. package/tools/sync-agent/tool.json +1 -1
  313. package/skills/research/clip-url/tests/test_browser_fetch_mcp_locate.py +0 -83
  314. package/skills/research/extract-url/SKILL.md +0 -213
  315. package/skills/research/extract-url/experiment/candidate-tag-constraints/INSTRUCTIONS.md +0 -41
  316. package/skills/research/extract-url/experiment/split-tag-candidate/INSTRUCTIONS.md +0 -53
  317. package/skills/research/extract-url/experiment/tag-order-language/INSTRUCTIONS.md +0 -50
  318. package/skills/research/extract-url/experiment/two-phase-tagging/INSTRUCTIONS.md +0 -55
  319. package/skills/research/extract-url/experiment/two-phase-tagging/expected-output.yaml +0 -30
  320. package/skills/research/extract-url/experiment/two-phase-tagging/fixture-article.txt +0 -66
  321. package/skills/research/extract-url/experiment/two-phase-tagging/fixture-fixed-tags.txt +0 -23
  322. package/skills/research/extract-url/experiment/two-phase-tagging/v1-prompt.txt +0 -102
  323. package/skills/research/extract-url/platforms/SKILL.claude.md +0 -35
  324. package/skills/research/extract-url/platforms/SKILL.codex.md +0 -25
  325. package/skills/research/extract-url/platforms/SKILL.hermes.md +0 -25
  326. package/skills/research/extract-url/platforms/SKILL.pi.md +0 -41
  327. package/skills/research/extract-url/references/__pycache__/article_utils.cpython-314.pyc +0 -0
  328. package/skills/research/extract-url/references/article_utils.py +0 -418
  329. package/skills/research/extract-url/references/file-format.md +0 -104
  330. package/skills/research/extract-url/references/subagent1-fetch-prompt.md +0 -67
  331. package/skills/research/extract-url/references/subagent2-tag-translate-prompt.md +0 -93
  332. package/skills/research/extract-url/scripts/__pycache__/config.cpython-314.pyc +0 -0
  333. package/skills/research/extract-url/scripts/__pycache__/migrate_to_folder_structure.cpython-314.pyc +0 -0
  334. package/skills/research/extract-url/scripts/__pycache__/playwright_web.cpython-314.pyc +0 -0
  335. package/skills/research/extract-url/scripts/__pycache__/playwright_xcom.cpython-314.pyc +0 -0
  336. package/skills/research/extract-url/scripts/config.py +0 -74
  337. package/skills/research/extract-url/scripts/count_article_stats.py +0 -35
  338. package/skills/research/extract-url/scripts/dedup_check.py +0 -27
  339. package/skills/research/extract-url/scripts/detect_chrome_profile.py +0 -108
  340. package/skills/research/extract-url/scripts/migrate_to_folder_structure.py +0 -459
  341. package/skills/research/extract-url/scripts/playwright_web.py +0 -227
  342. package/skills/research/extract-url/scripts/playwright_web_arxiv.py +0 -284
  343. package/skills/research/extract-url/scripts/playwright_web_wechat.py +0 -256
  344. package/skills/research/extract-url/scripts/playwright_xcom.py +0 -587
  345. package/skills/research/extract-url/scripts/validate_article.py +0 -47
  346. package/skills/research/extract-url/tests/__pycache__/conftest.cpython-314-pytest-9.0.2.pyc +0 -0
  347. package/skills/research/extract-url/tests/__pycache__/test_article_utils_meta.cpython-314-pytest-9.0.2.pyc +0 -0
  348. package/skills/research/extract-url/tests/__pycache__/test_article_utils_tags.cpython-314-pytest-9.0.2.pyc +0 -0
  349. package/skills/research/extract-url/tests/__pycache__/test_config.cpython-314-pytest-9.0.2.pyc +0 -0
  350. package/skills/research/extract-url/tests/__pycache__/test_count_article_stats.cpython-314-pytest-9.0.2.pyc +0 -0
  351. package/skills/research/extract-url/tests/__pycache__/test_dedup_check.cpython-314-pytest-9.0.2.pyc +0 -0
  352. package/skills/research/extract-url/tests/__pycache__/test_migrate_to_folder_structure.cpython-314-pytest-9.0.2.pyc +0 -0
  353. package/skills/research/extract-url/tests/__pycache__/test_playwright_web.cpython-314-pytest-9.0.2.pyc +0 -0
  354. package/skills/research/extract-url/tests/__pycache__/test_playwright_web_arxiv.cpython-314-pytest-9.0.2.pyc +0 -0
  355. package/skills/research/extract-url/tests/__pycache__/test_playwright_web_wechat.cpython-314-pytest-9.0.2.pyc +0 -0
  356. package/skills/research/extract-url/tests/__pycache__/test_playwright_xcom.cpython-314-pytest-9.0.2.pyc +0 -0
  357. package/skills/research/extract-url/tests/__pycache__/test_subagent1_prompt.cpython-314-pytest-9.0.2.pyc +0 -0
  358. package/skills/research/extract-url/tests/__pycache__/test_subagent2_prompt.cpython-314-pytest-9.0.2.pyc +0 -0
  359. package/skills/research/extract-url/tests/__pycache__/test_validate_article.cpython-314-pytest-9.0.2.pyc +0 -0
  360. package/skills/research/extract-url/tests/conftest.py +0 -74
  361. package/skills/research/extract-url/tests/test_article_utils_meta.py +0 -84
  362. package/skills/research/extract-url/tests/test_article_utils_tags.py +0 -139
  363. package/skills/research/extract-url/tests/test_config.py +0 -116
  364. package/skills/research/extract-url/tests/test_count_article_stats.py +0 -91
  365. package/skills/research/extract-url/tests/test_dedup_check.py +0 -81
  366. package/skills/research/extract-url/tests/test_migrate_to_folder_structure.py +0 -492
  367. package/skills/research/extract-url/tests/test_playwright_web.py +0 -159
  368. package/skills/research/extract-url/tests/test_playwright_web_arxiv.py +0 -157
  369. package/skills/research/extract-url/tests/test_playwright_web_wechat.py +0 -191
  370. package/skills/research/extract-url/tests/test_playwright_xcom.py +0 -203
  371. package/skills/research/extract-url/tests/test_subagent1_prompt.py +0 -15
  372. package/skills/research/extract-url/tests/test_subagent2_prompt.py +0 -22
  373. package/skills/research/extract-url/tests/test_validate_article.py +0 -241
  374. package/skills/research/extract-url/vars.json +0 -15
  375. package/skills/research/sync-xtimeline/SKILL.md +0 -62
  376. package/skills/research/sync-xtimeline/scripts/__pycache__/archive_tweets.cpython-314.pyc +0 -0
  377. package/skills/research/sync-xtimeline/scripts/__pycache__/browser_fetch_mcp_locate.cpython-314.pyc +0 -0
  378. package/skills/research/sync-xtimeline/scripts/__pycache__/fetch_new_tweets.cpython-314.pyc +0 -0
  379. package/skills/research/sync-xtimeline/scripts/__pycache__/mcp_timeline_client.cpython-314.pyc +0 -0
  380. package/skills/research/sync-xtimeline/scripts/__pycache__/render_digest.cpython-314.pyc +0 -0
  381. package/skills/research/sync-xtimeline/scripts/__pycache__/render_view.cpython-314.pyc +0 -0
  382. package/skills/research/sync-xtimeline/scripts/__pycache__/watchlist.cpython-314.pyc +0 -0
  383. package/skills/research/sync-xtimeline/scripts/fetch_new_tweets.py +0 -76
  384. package/skills/research/sync-xtimeline/scripts/mcp_timeline_client.py +0 -49
  385. package/skills/research/sync-xtimeline/scripts/watchlist.py +0 -118
  386. package/skills/research/sync-xtimeline/tests/__pycache__/conftest.cpython-314-pytest-9.1.1.pyc +0 -0
  387. package/skills/research/sync-xtimeline/tests/__pycache__/test_browser_fetch_mcp_locate.cpython-314-pytest-9.1.1.pyc +0 -0
  388. package/skills/research/sync-xtimeline/tests/__pycache__/test_fetch_new_tweets.cpython-314-pytest-9.1.1.pyc +0 -0
  389. package/skills/research/sync-xtimeline/tests/__pycache__/test_mcp_timeline_client.cpython-314-pytest-9.1.1.pyc +0 -0
  390. package/skills/research/sync-xtimeline/tests/__pycache__/test_watchlist.cpython-314-pytest-9.1.1.pyc +0 -0
  391. package/skills/research/sync-xtimeline/tests/conftest.py +0 -16
  392. package/skills/research/sync-xtimeline/tests/test_browser_fetch_mcp_locate.py +0 -12
  393. package/skills/research/sync-xtimeline/tests/test_fetch_new_tweets.py +0 -96
  394. package/skills/research/sync-xtimeline/tests/test_mcp_timeline_client.py +0 -30
  395. package/skills/research/sync-xtimeline/tests/test_watchlist.py +0 -110
  396. package/tools/browser-fetch-mcp/browser-fetch-mcp.sh +0 -36
  397. package/tools/browser-fetch-mcp/browser_fetch_mcp/__init__.py +0 -1
  398. package/tools/browser-fetch-mcp/browser_fetch_mcp/__pycache__/__init__.cpython-314.pyc +0 -0
  399. package/tools/browser-fetch-mcp/browser_fetch_mcp/__pycache__/config.cpython-314.pyc +0 -0
  400. package/tools/browser-fetch-mcp/browser_fetch_mcp/__pycache__/server.cpython-314.pyc +0 -0
  401. package/tools/browser-fetch-mcp/tests/__pycache__/test_config.cpython-314-pytest-9.1.1.pyc +0 -0
  402. package/tools/browser-fetch-mcp/tests/__pycache__/test_evaluate_js.cpython-314-pytest-9.1.1.pyc +0 -0
  403. package/tools/browser-fetch-mcp/tests/__pycache__/test_fetch_article.cpython-314-pytest-9.1.1.pyc +0 -0
  404. package/tools/browser-fetch-mcp/tests/__pycache__/test_fetch_user_timeline.cpython-314-pytest-9.1.1.pyc +0 -0
  405. package/tools/browser-fetch-mcp/tests/__pycache__/test_server.cpython-314-pytest-9.1.1.pyc +0 -0
  406. package/tools/browser-fetch-mcp/tests/test_config.py +0 -25
  407. package/tools/browser-fetch-mcp/tests/test_evaluate_js.py +0 -63
  408. package/tools/browser-fetch-mcp/tests/test_fetch_user_timeline.py +0 -80
  409. package/tools/browser-fetch-mcp/tests/test_server.py +0 -126
  410. package/tools/browser-fetch-mcp/tool.json +0 -8
  411. /package/tools/{browser-fetch-mcp/browser_fetch_mcp → browser-fetch/browser_fetch}/cookies.py +0 -0
  412. /package/tools/{browser-fetch-mcp/browser_fetch_mcp → browser-fetch/browser_fetch}/images.py +0 -0
  413. /package/tools/{browser-fetch-mcp/browser_fetch_mcp → browser-fetch/browser_fetch}/markdown.py +0 -0
package/CHANGELOG.md CHANGED
@@ -7,6 +7,22 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [0.29.0] - 2026-08-28
11
+
12
+ ### Added
13
+ - `roster`:新增 tool,人(creator)与渠道(channel)名册。三份数据、三个写入方:`registry.json`(人与渠道定义,归 manage-roster)、`state.json`(游标与失败态,归 sync-*)、`profiles/*.md`(画像,归 capture-creator)。CLI 按同样的三组划分。渠道数据可重建,画像不可重建——删除操作一律归档而非删除
14
+ - `manage-roster`:新增 skill,名册的人机入口(`add` / `list` / `merge` / `rename` / `remove`),含旧 watchlist 的一次性幂等迁移
15
+ - `capture-creator`:新增 skill,认知层的人工输入口。你口述对某个博主的判断,它归纳成要点、盖上当前时间戳、回显确认后追加进 `profiles/<id>.md`。判断由人出,agent 只做整理,未经确认不写盘
16
+
17
+ ### Changed
18
+ - `roster profile append`:`--source`(依据)改为可选,省略时记为 `人工`——这个值同时就是作者标记,用来区分本人下的判断和 agent 从物料推的观察。正文可以省略 `--body` 改从 stdin 读,多行 Markdown 不再受命令行引号和换行的折磨。观察条目的时间戳支持可选的 `HH:MM`(一天记好几条时需要分先后),纯日期的旧条目不需要迁移
19
+ - `roster profile append` / `summary`:写入前校验 `creator_id` 在名册里存在(接受 merge 后的 alias)。此前手打错一个字母会安静地建出一个没人引用、`registry remove` 也不会归档的孤儿画像
20
+ - **破坏性变更** `sync-xtimeline` / `sync-ytchannel`:移除 `add` / `remove` / `list` 三个子命令,关注列表迁到 `manage-roster`。两个 skill 退化为纯执行器,只保留 `run`(sync-xtimeline 另留 `view`)。两者不再各持一份 `DATA_DIR`,改为共用名册的数据目录,digest 各落各的平台子目录(`digests/x/`、`digests/youtube/`)
21
+ - **升级路径**:跑一次 `manage-roster` 的初始化流程,它会引导完成迁移。迁移后每个 handle 各自是一个人,同一个人的 X 和 YouTube 需要手动 `merge` 合并——判断"这两个 handle 是同一个人"不能自动化。旧的 `~/.hskill/sync-xtimeline/config.json` 和 `~/.hskill/sync-ytchannel/config.json` 作废但不会被自动删除
22
+
23
+ ### Fixed
24
+ - `scripts/run-skill-tests.sh`:此前对 `skills/*/*/tests/*.py` 执行 `python3 <file>`,而它们是 pytest 文件——直接执行只 import 模块然后退出 0,一个测试都不跑,`npm test` 一直把"没跑"报成"通过"。改为按 `tests/` 目录调 pytest,并纳入 `tools/*/tests/`。修复后 `npm test` 真正执行 10 个套件共 665 个 pytest,其中 282 个是本次改动之前就存在、却从未被 CI 执行过的。归档代码(`*/archived/*`)排除在测试范围外;工具套件优先用自己的 `.venv`(仅当该 venv 装了 pytest)
25
+
10
26
  ## [0.28.0] - 2026-08-15
11
27
 
12
28
  ### Added
package/package.json CHANGED
@@ -1,10 +1,11 @@
1
1
  {
2
2
  "name": "harveyz-skill",
3
- "version": "0.28.0",
3
+ "version": "0.29.0",
4
4
  "description": "Skill manager for Claude Code, Cursor, Codex, OpenClaw, Hermes, OpenCode, and Pi",
5
5
  "type": "module",
6
6
  "bin": {
7
- "hskill": "bin/cli.js"
7
+ "hskill": "bin/cli.js",
8
+ "skill-harness": "tools/skill-harness/cli.js"
8
9
  },
9
10
  "scripts": {
10
11
  "prepack": "node scripts/generate-npmignore.js",
@@ -13,11 +14,11 @@
13
14
  "files": [
14
15
  "bin/",
15
16
  "lib/",
17
+ "tools/skill-harness/",
16
18
  "bundles.json",
17
19
  "skills-index.json",
18
20
  "CHANGELOG.md",
19
21
  "skills/mint/learn-skill/",
20
- "skills/research/extract-url/",
21
22
  "skills/research/extract-vision/",
22
23
  "skills/research/learn-video/",
23
24
  "skills/research/survey-skillrepo/",
@@ -27,7 +28,8 @@
27
28
  "skills/research/probe-session/",
28
29
  "skills/research/pdf-math-translate/",
29
30
  "skills/research/clip-url/",
30
- "skills/research/sync-xtimeline/",
31
+ "skills/feed/sync-xtimeline/",
32
+ "skills/feed/sync-ytchannel/",
31
33
  "skills/creative/capture-todo/",
32
34
  "skills/creative/capture-insight/",
33
35
  "skills/coding/init-workflow/",
@@ -57,9 +59,12 @@
57
59
  "skills/meta/sync-hotfix/",
58
60
  "skills/meta/sync-agent/",
59
61
  "skills/coding/handoff/",
62
+ "skills/feed/manage-roster/",
63
+ "skills/feed/capture-creator/",
60
64
  "tools/hub/",
61
65
  "tools/sync-agent/",
62
- "tools/browser-fetch-mcp/",
66
+ "tools/browser-fetch/",
67
+ "tools/roster/",
63
68
  "hooks/check-similar-branch/"
64
69
  ],
65
70
  "engines": {
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: handoff
3
3
  description: Use when handing a task across sessions — writing a self-contained handoff doc for a fresh session to pick up (author), sanity-checking an inbound handoff before starting (verify), or accepting completed work against the criteria agreed at handoff time (accept). Triggers on phrases like "write a handoff", "hand this off", "pick up this task", "sign off on this work". Generic skill — project-specific conventions are read from .hskill/handoff/config.md.
4
- version: "1.1.0"
4
+ version: "1.2.0"
5
5
  user_invocable: true
6
6
  ---
7
7
 
@@ -53,8 +53,8 @@ user_invocable: true
53
53
 
54
54
  ## Phase 3 — accept(原 session 验收)
55
55
 
56
- 1. 找文档里的**最小验收锚点**——这是唯一固定依据。硬判据(逐条对/错)→ 按其描述**逐条实跑**(单测/E2E/核验);软判据(定性描述)→ 按其描述做定性判断。
57
- 2. 把验收结果(每条 pass/fail,或整体达成/未达成)追加记录到最小验收锚点所在章节末尾。
56
+ 1. 找文档里的**最小验收锚点**——这是唯一固定依据。**接手方自填的结论一律不采信,逐条自己跑**:验证产物"存在"不等于"跑得过",点得出脚本名不等于那个脚本此刻是绿的。硬判据(逐条对/错)→ 按其描述**逐条实跑**(单测/E2E/核验);软判据(定性描述)→ 按其描述做定性判断。
57
+ 2. 把验收结果(每条 pass/fail,或整体达成/未达成)追加记录到最小验收锚点所在章节末尾。**接手方若已自填过验收记录,另起小节并列,不覆盖也不合并**——两份并排放着,后来人才看得出哪些结论被第二方复核过。其中若有与实跑不符的陈述,**显式写出更正**,不要静默改掉:静默改掉等于把同一个错误留给下一次。
58
58
  3. 达成 → 状态置 `已验收`;未达成 → 状态置 `打回` 并写明哪里没达成、为什么,退回接手方。
59
59
  4. **达成才算真正完成**(硬判据要求逐条全绿;软判据按其描述定性判断是否达成)。
60
60
 
@@ -63,3 +63,9 @@ user_invocable: true
63
63
  `待执行`(author 写完)→ `执行中`(verify 通过 / 接手方开工)→ `待验收`(接手方回报完成)→ `已验收`(accept 判定达成)/ `打回`(accept 判定未达成,退回执行中)。
64
64
 
65
65
  状态字段是三 phase 间唯一协调锚点,无需外部状态存储。
66
+
67
+ **写入权按 phase 分:接手方最多只能把状态推到「待验收」。`已验收` / `打回` 只能由原 session 在 accept 之后写。**
68
+
69
+ 这条不是流程洁癖。「已验收」的全部信息量就是**做事的人之外的另一方查过**;做事的人一旦能自己写它,它就退化成「做事的人说做完了」——而这个信息「待验收」里已经有了,两个状态变成同义词,字段失效。风险还会反向放大:一份自填的「已验收」通常附着一张全 PASS 的表,比没有记录更难被怀疑。
70
+
71
+ 接手方若跳档自填,accept 方**按未验收处理**,照常逐条重跑。
@@ -14,7 +14,7 @@
14
14
  **状态**:待执行 <!-- 待执行 → 执行中 → 待验收 → 已验收 / 打回 -->
15
15
  **交接目的**:<一句话,自由描述这次交接是为了什么,不受预设分类约束>
16
16
 
17
- > **接手方须知**:你正在接手一个任务。本文档是完整交接与唯一权威入口:从头读到尾,若文档里有「工作流约定」章节按其开工,没有就直接开工,完成后等原 session 按「最小验收锚点」验收。
17
+ > **接手方须知**:你正在接手一个任务。本文档是完整交接与唯一权威入口:从头读到尾,若文档里有「工作流约定」章节按其开工,没有就直接开工。**完成后把上面的状态置为「待验收」并停在这里**——`已验收` / `打回` 由原 session 按「最小验收锚点」判定后写,不要代填。你的自测结果写成独立小节,别写进原 session 的验收记录里。
18
18
 
19
19
  ---
20
20
 
@@ -6,28 +6,74 @@
6
6
  "name": "author-high-to-low-model",
7
7
  "prompt": "我刚把一个功能模块的重构方案定完了,设计规格已经写好放在仓库里。想把具体实现交给另一个能力较弱的模型 session 去做,帮我写一份交接文档,让它只读这一份就能照着实现,别自由发挥。",
8
8
  "expected_output": "在 config 输出目录(默认 docs/commute/)下生成 YYYY-MM-DD-*-handoff.md;抬头含状态字段 + 交接目的(判断为'接手方续做同一实现任务'一类描述)+ 接手方须知。因为是续做同一任务的目的,必要性测试会判定背景/关键决定/范围铁律/相关文档索引/受影响文件/验证步骤等章节基本都需要,实际出现在文档里;若项目有 .hskill/handoff/config.md 则注入其工作流/验证约定;最小验收锚点是可证伪的硬判据;author 收尾跑完整性门禁。",
9
- "files": []
9
+ "files": [],
10
+ "frozen": "2026-08-17",
11
+ "assertions": [
12
+ { "id": "file-in-output-dir", "source": "artifact",
13
+ "text": "在 docs/commute/ 下生成了文件名形如 YYYY-MM-DD-<topic>-handoff.md 的文档" },
14
+ { "id": "header-has-status-and-purpose", "source": "artifact",
15
+ "text": "文档抬头同时含状态字段与交接目的两项" },
16
+ { "id": "purpose-is-continuation", "source": "artifact",
17
+ "text": "交接目的描述为「接手方续做同一实现任务」一类,而不是仅传递背景" },
18
+ { "id": "continuation-sections-present", "source": "artifact",
19
+ "text": "文档含范围铁律与相关文档索引两节" },
20
+ { "id": "anchor-is-falsifiable", "source": "artifact",
21
+ "text": "最小验收锚点是硬判据,有明确的对错判定,不是「让它工作」这类无法证伪的软标准" }
22
+ ]
10
23
  },
11
24
  {
12
25
  "id": 1,
13
26
  "name": "author-cross-device-continuation",
14
27
  "prompt": "今天先干到这,明天换台电脑接着做这个第三方 SDK 的集成。帮我留一份交接,别让明天的自己丢上下文。",
15
28
  "expected_output": "config 输出目录下生成 handoff.md,抬头含状态字段 + 交接目的(判断为续做同一任务一类描述),状态=待执行;背景与现状章节写清当前进度(必要性测试:不写会做错方向 → 判定需要);最小验收锚点引用原任务的成功判据(硬判据)。",
16
- "files": []
29
+ "files": [],
30
+ "frozen": "2026-08-17",
31
+ "assertions": [
32
+ { "id": "file-in-output-dir", "source": "artifact",
33
+ "text": "在 docs/commute/ 下生成了文件名形如 YYYY-MM-DD-<topic>-handoff.md 的文档" },
34
+ { "id": "status-is-pending", "source": "artifact",
35
+ "text": "状态字段的值是「待执行」" },
36
+ { "id": "background-describes-progress", "source": "artifact",
37
+ "text": "背景与现状章节写明了当前做到哪一步" },
38
+ { "id": "anchor-references-original-criteria", "source": "artifact",
39
+ "text": "最小验收锚点引用原任务的成功判据,且是硬判据" }
40
+ ]
17
41
  },
18
42
  {
19
43
  "id": 2,
20
44
  "name": "accept-catches-failing-criterion",
21
45
  "prompt": "接手的 session 说 slugify 那个小任务做完了,交接文档和产物都在 evals/fixtures/accept/ 下(handoff-slugify.md 和 slugify.js)。帮我验收一下,看能不能收。",
22
46
  "expected_output": "accept phase:找文档里的最小验收锚点(硬判据,两条 slugify 判据),按其描述逐条实跑(node slugify.js),发现判据2(去标点)未达标;状态置『打回』而非『已验收』,在最小验收锚点章节追加验收记录点名失败判据。",
23
- "files": ["evals/fixtures/accept/handoff-slugify.md", "evals/fixtures/accept/slugify.js"]
47
+ "files": ["evals/fixtures/accept/handoff-slugify.md", "evals/fixtures/accept/slugify.js"],
48
+ "frozen": "2026-08-17",
49
+ "assertions": [
50
+ { "id": "ran-criteria-not-just-read", "source": "transcript",
51
+ "text": "执行轨迹显示实际运行了 slugify.js(如 Bash 调用 node slugify.js),而不是只读文档就下判断" },
52
+ { "id": "status-set-to-rejected", "source": "artifact",
53
+ "text": "fixture 交接文档的状态被置为「打回」,不是「已验收」" },
54
+ { "id": "names-failing-criterion", "source": "artifact",
55
+ "text": "验收记录点名了未达标的是「去标点」那一条判据,而不是笼统说没通过" },
56
+ { "id": "record-appended-to-anchor-section", "source": "artifact",
57
+ "text": "验收结果被追加在最小验收锚点章节末尾,而不是另起一节或写在文档开头" }
58
+ ]
24
59
  },
25
60
  {
26
61
  "id": 3,
27
62
  "name": "author-background-only-handoff",
28
63
  "prompt": "我们刚才在这个 session 里把新缓存方案的取舍和结论定下来了,我要开一个新 session 去做完全不相关的另一件事,但想让新 session 保留这次讨论的结论作为背景,万一之后有人问起能答上来,不是要它去执行什么。帮我写个交接。",
29
64
  "expected_output": "交接目的写成类似'仅传递背景结论,不要求接手方产出可验收的实现'一类描述;文档只出现背景与现状(讨论结论)+ 交接目的 + 最小验收锚点(软判据,如'接手方能复述讨论结论要点');不出现范围铁律/受影响文件/验证步骤/关键决定这类续做任务专属章节(必要性测试判定为不需要)。",
30
- "files": []
65
+ "files": [],
66
+ "frozen": "2026-08-17",
67
+ "assertions": [
68
+ { "id": "purpose-is-background-only", "source": "artifact",
69
+ "text": "交接目的写成「仅传递背景结论,不要求接手方产出可验收的实现」一类描述" },
70
+ { "id": "has-purpose-and-anchor", "source": "artifact",
71
+ "text": "交接目的与最小验收锚点两节均存在" },
72
+ { "id": "no-continuation-sections", "source": "artifact",
73
+ "text": "文档不含范围铁律、受影响文件、验证步骤、关键决定中的任何一节" },
74
+ { "id": "anchor-is-soft", "source": "artifact",
75
+ "text": "最小验收锚点是软判据(如「接手方能复述讨论结论要点」),不是硬判据" }
76
+ ]
31
77
  }
32
78
  ]
33
79
  }
@@ -1,5 +1,5 @@
1
1
  {
2
- "skill_name": "full-stack-debug-env",
2
+ "skill_name": "setup-debug",
3
3
  "evals": [
4
4
  {
5
5
  "id": 1,
@@ -8,6 +8,17 @@
8
8
  "files": [
9
9
  "/tmp/fsd-test-node-spa/package.json",
10
10
  "/tmp/fsd-test-node-spa/server.js"
11
+ ],
12
+ "frozen": "2026-08-17",
13
+ "assertions": [
14
+ { "id": "log-dir-created", "source": "artifact",
15
+ "text": "创建了 tmp/logs/ 目录" },
16
+ { "id": "backend-and-browser-sources", "source": "artifact",
17
+ "text": "日志来源规划中至少含 backend 与 browser 两个独立来源,各自有独立日志文件" },
18
+ { "id": "verify-script-created", "source": "artifact",
19
+ "text": "创建了 harness/debug/verify-logs.sh" },
20
+ { "id": "both-docs-generated", "source": "artifact",
21
+ "text": "同时生成了 harness/debug/README.md 与 docs/how-to/debug-env.md 两份文档" }
11
22
  ]
12
23
  },
13
24
  {
@@ -16,6 +27,17 @@
16
27
  "expected_output": "应规划 api.log/worker.log/db.log 三个独立日志来源;worker 被识别为异步/任务驱动型来源;verify-logs.sh 中 worker.log 应报 SKIP 而非 FAIL;生成完整文档",
17
28
  "files": [
18
29
  "/tmp/fsd-test-docker/docker-compose.yml"
30
+ ],
31
+ "frozen": "2026-08-17",
32
+ "assertions": [
33
+ { "id": "three-sources-planned", "source": "artifact",
34
+ "text": "规划了 api、worker、db 三个独立日志来源,各自有独立日志文件" },
35
+ { "id": "worker-typed-async", "source": "artifact",
36
+ "text": "worker 被识别为异步或任务驱动型来源,而不是与 api 同类处理" },
37
+ { "id": "worker-skip-not-fail", "source": "artifact",
38
+ "text": "verify-logs.sh 中 worker.log 的检查结果是 SKIP,不是 FAIL——异步来源在无任务时本就无日志" },
39
+ { "id": "both-docs-generated", "source": "artifact",
40
+ "text": "同时生成了 harness/debug/README.md 与 docs/how-to/debug-env.md 两份文档" }
19
41
  ]
20
42
  }
21
43
  ]
@@ -0,0 +1,107 @@
1
+ ---
2
+ name: capture-creator
3
+ version: "0.1.0"
4
+ description: "Record your own judgment about a specific creator already on the roster. You say what you think of them, this skill tidies it into points, stamps it with the current time, shows it back for confirmation, then appends it to that creator's profile. Trigger phrases: '/capture-creator <name>', '/capture-creator', 'I have a take on this blogger', 'let me note down what I think of <name>', 'what do I think of <name>', 'show me my profile of <name>'. Scoped to people on the roster — a standalone insight not tied to a watched creator is capture-insight, and adding or removing a watched channel is manage-roster. Running an incremental fetch is sync-xtimeline / sync-ytchannel. Never saves to Obsidian and never tags — saving an article or tweet is clip-url."
5
+ user_invocable: true
6
+ ---
7
+
8
+ # capture-creator
9
+
10
+ 给名册上的人记一笔判断。**判断由你出,这个 skill 只做整理、盖时间戳、落盘。**
11
+
12
+ 它写的是 `profiles/<creator-id>.md`——整套数据里唯一不可重建的部分。名册和游标删了重加一遍就回来了,画像不会。所以这里有两条硬规矩:
13
+
14
+ - **未经你确认不写盘。** 整理后的正文先回显,你点头才落。
15
+ - **只写 `profiles/`。** 名册归 manage-roster,游标归抓取层,一概不碰。
16
+
17
+ ## 初始化(run first)
18
+
19
+ ```bash
20
+ python3 scripts/roster_locate.py
21
+ ```
22
+
23
+ 若输出 `NOT_FOUND: <error>`(exit 1),向用户报告"roster tool 未安装:{error}",流程终止。
24
+
25
+ 若从未初始化过名册(`~/.hskill/roster/config.json` 不存在),让用户先跑一次 [manage-roster](../manage-roster/),流程终止。
26
+
27
+ `<roster>` 指 `roster_locate.py` 输出的路径。
28
+
29
+ ## 记一笔(默认动作)
30
+
31
+ ### 1. 确定是谁
32
+
33
+ ```bash
34
+ <roster> registry list
35
+ ```
36
+
37
+ 从用户提到的名字或 handle 里找 `creator_id`(每人一行,行首那个词就是 id)。
38
+
39
+ - 名册为空(`EMPTY`)或找不到匹配:告诉用户这人不在名册上,让他先用 manage-roster 加渠道,流程终止。**不要自己加。**
40
+ - 多个候选:列出来让用户选,**不要猜**。写错人的画像比不写更糟——观察是只追加的,写进去就在那儿了。
41
+
42
+ ### 2. 整理
43
+
44
+ 把用户口述的内容归纳成要点:提炼观点、重组逻辑、去掉口语碎片,用整齐的 Markdown 呈现。
45
+
46
+ 三条底线:
47
+
48
+ - **不发明。** 用户没说的判断不能加,包括你自己对这个人的印象。
49
+ - **不加依据。** 不要去查名册、去翻抓取下来的物料给用户的话找佐证。这一笔的来源就是他本人。
50
+ - **拿不准的地方保留原话。** 归纳时如果某句话有两种理解,别替他选一种,原样留着。
51
+
52
+ ### 3. 回显确认
53
+
54
+ 把三样东西一起展示给用户:`creator_id` 和显示名、时间戳、整理后的正文。
55
+
56
+ 用户要改就改完再确认一次。**没拿到明确的确认,不要执行第 4 步。**
57
+
58
+ ### 4. 落盘
59
+
60
+ 时间戳取当前本地时间:
61
+
62
+ ```bash
63
+ date "+%Y-%m-%d %H:%M"
64
+ ```
65
+
66
+ ```bash
67
+ <roster> profile append <creator_id> --date "<YYYY-MM-DD HH:MM>" <<'EOF'
68
+ <整理后的正文>
69
+ EOF
70
+ ```
71
+
72
+ 正文走 stdin,不要塞进 `--body`——归纳后的多行 Markdown 走命令行参数会被引号和换行搞坏。
73
+
74
+ 依据默认记成 `人工`,这个值同时就是作者标记(将来 agent 自己写的观察会填真实来源)。**只有用户主动说了这一笔是看了什么才来的**(比如给了一条推文链接),才加 `--source "<用户给的来源>"`。
75
+
76
+ 成功输出 `OK <画像文件路径>`,把路径转告用户。
77
+
78
+ ## 看画像
79
+
80
+ ```bash
81
+ <roster> profile show <creator_id>
82
+ ```
83
+
84
+ `EMPTY` 表示这人还没有画像。原样展示,不要替用户总结。
85
+
86
+ ## 边界
87
+
88
+ 不抓取、不翻译、不写名册、不改游标、不进 Obsidian、不打标。
89
+
90
+ 关注谁走 [manage-roster](../manage-roster/),跑一次增量抓取走 [sync-xtimeline](../sync-xtimeline/) 或 [sync-ytchannel](../sync-ytchannel/),单条物料入库走 [clip-url](../../research/clip-url/)。
91
+
92
+ **跟 [capture-insight](../../creative/capture-insight/) 的分界是观察对象**:这一笔是冲着名册上某个具体的人去的,才归这里;不挂在任何人身上的独立想法归 capture-insight。判断不了就问用户,不要自己挑一个。
93
+
94
+ 「当前判断」那一段(从观察重算出一句总结)现在还没有入口,观察攒够了再说。
95
+
96
+ 两个已知限制,用户问起时如实说:
97
+
98
+ - 写盘是全文件重写,没有加锁。你在编辑器里手改画像的同时跑这个 skill,后落盘的赢。
99
+ - 新条目固定插在最前,不按时间戳重排。补录一条旧时间的观察,它会出现在最上面。
100
+
101
+ 设计文档:`docs/superpowers/specs/2026-08-26-creator-channel-registry-design.md`。
102
+
103
+ ## 参考文件
104
+
105
+ | 文件 | 用途 |
106
+ |------|------|
107
+ | `scripts/roster_locate.py` | 定位 roster launcher(跟 manage-roster 同款,独立副本) |
@@ -0,0 +1,46 @@
1
+ """定位 roster launcher(跟 browser_fetch_locate.py 同款,独立副本)。
2
+
3
+ 两种布局:
4
+ - Dev 模式:本 skill 跑在 harveyz-skill 的 checkout 里,
5
+ tools/roster/roster.sh 在 scripts/ 上面四层。
6
+ - 装机模式:经 hskill install 装到 ~/.claude/skills 等处,roster 作为 tool
7
+ 单独安装,launcher 落在 ~/.local/bin/roster。
8
+ """
9
+ import shutil
10
+ import sys
11
+ from pathlib import Path
12
+
13
+
14
+ def _dev_path() -> Path:
15
+ return Path(__file__).resolve().parents[4] / "tools" / "roster" / "roster.sh"
16
+
17
+
18
+ def find_roster() -> str:
19
+ dev = _dev_path()
20
+ if dev.exists():
21
+ return str(dev)
22
+
23
+ on_path = shutil.which("roster")
24
+ if on_path:
25
+ return on_path
26
+
27
+ installed = Path.home() / ".local" / "bin" / "roster"
28
+ if installed.exists():
29
+ return str(installed)
30
+
31
+ raise FileNotFoundError(
32
+ "roster launcher 未找到。请在 harveyz-skill 的 checkout 里运行本 skill,"
33
+ "或运行 `hskill install --tool roster`。"
34
+ )
35
+
36
+
37
+ def main():
38
+ try:
39
+ print(f"FOUND: {find_roster()}")
40
+ except FileNotFoundError as e:
41
+ print(f"NOT_FOUND: {e}")
42
+ sys.exit(1)
43
+
44
+
45
+ if __name__ == "__main__":
46
+ main()
@@ -0,0 +1,70 @@
1
+ ---
2
+ name: manage-roster
3
+ version: "0.1.1"
4
+ description: "Maintain the roster of watched creators and their channels — the shared watchlist behind sync-xtimeline and sync-ytchannel. Add a channel URL, merge two handles that turn out to be the same person, rename a placeholder, view the roster with cursor state. Trigger phrases: '/manage-roster add <url>', '/manage-roster list', '/manage-roster merge <a> <b>', '/manage-roster rename <id> <name>', '/manage-roster remove <id>', 'watch this X account', 'watch this YouTube channel', 'who am I following'. Does not fetch anything — running an incremental fetch is sync-xtimeline / sync-ytchannel; writing a creator's profile is the cognition layer."
5
+ user_invocable: true
6
+ ---
7
+
8
+ # manage-roster
9
+
10
+ 维护"关注了哪些人、每个人有哪些渠道"这份名册。抓取本身不归它管——`sync-xtimeline` 和 `sync-ytchannel` 从这份名册读渠道列表去抓。
11
+
12
+ **渠道必属于一个人。** 机构号、聚合频道也当人处理。`add` 会自动建一个占位人(名字先用 handle 顶着),之后用 `merge` 把"这俩 handle 其实是同一个人"合起来。
13
+
14
+ **这个 skill 只写 `registry.json`。** 游标归抓取层写,画像归认知层写,它一概不碰。
15
+
16
+ ## 初始化(run first)
17
+
18
+ ```bash
19
+ python3 scripts/roster_locate.py
20
+ ```
21
+
22
+ 若输出 `NOT_FOUND: <error>`(exit 1),向用户报告"roster tool 未安装:{error}",流程终止。
23
+
24
+ 若输出 `FOUND: <path>`,检查配置:
25
+
26
+ ```bash
27
+ ls ~/.hskill/roster/config.json 2>/dev/null && echo "EXISTS" || echo "NOT_FOUND"
28
+ ```
29
+
30
+ **若 `NOT_FOUND`:**
31
+
32
+ 1. 询问用户名册数据要存在哪个目录(`DATA_DIR`,必须由用户手动提供,不得猜测)。可建议默认值 `~/.hskill/roster`。提醒用户:这个目录里的画像文件是累积出来的、删了长不回来,值得跟笔记一样对待(`registry.json` 和 `state.json` 都可重建,画像不能)。
33
+ 2. 运行 `<roster_path> init <DATA_DIR>`。
34
+
35
+ **若用户此前用过 sync-xtimeline / sync-ytchannel**,初始化后问一次是否迁移旧关注列表:
36
+
37
+ ```bash
38
+ <roster_path> migrate \
39
+ --from-xtimeline "$(python3 -c "import json,pathlib;print(pathlib.Path(json.loads((pathlib.Path.home()/'.hskill/sync-xtimeline/config.json').read_text())['DATA_DIR']).expanduser()/'watchlist.json')")" \
40
+ --from-ytchannel "$(python3 -c "import json,pathlib;print(pathlib.Path(json.loads((pathlib.Path.home()/'.hskill/sync-ytchannel/config.json').read_text())['DATA_DIR']).expanduser()/'watchlist.json')")"
41
+ ```
42
+
43
+ 任一旧配置不存在就省掉对应的参数。迁移是幂等的,重复跑安全。迁移后告诉用户:每个 handle 现在各自是一个人,同一个人的 X 和 YouTube 需要用 `merge` 合并,并主动列出名字相近的候选对给用户确认——**不要自己替用户合并**。
44
+
45
+ ## 用法
46
+
47
+ `<roster>` 指 `roster_locate.py` 输出的路径。
48
+
49
+ | 用户说 | 运行 | 报告 |
50
+ |---|---|---|
51
+ | `add <url>` | `<roster> registry add <url>` | `OK <id> <platform>:<handle>` → 告知已加入,并提示这是占位人、可用 `rename` 填正式名字 |
52
+ | `list` | `<roster> registry list` | 原样展示。`EMPTY` 表示还没关注任何人 |
53
+ | `merge <a> <b>` | `<roster> registry merge <a> <b>` | `OK merged b into a` → 告知 b 的 id 已进 aliases,旧引用仍可查到 |
54
+ | `rename <id> <name>` | `<roster> registry rename <id> <name>` | `OK` |
55
+ | `remove <id>` | `<roster> registry remove <id>` | 输出里带画像归档路径时,**必须把这个路径转告用户**——画像没被删,只是移走了 |
56
+ | `remove <platform>:<handle>` | `<roster> registry remove <platform>:<handle>` | `OK removed ...`。人还留着 |
57
+
58
+ 所有失败原样把 stderr 报给用户,不要自行改写或重试。
59
+
60
+ ## 边界
61
+
62
+ 不抓取、不翻译、不写画像、不进 Obsidian。跑一次增量抓取走 [sync-xtimeline](../sync-xtimeline/) 或 [sync-ytchannel](../sync-ytchannel/)。单条物料入库走 [clip-url](../../research/clip-url/),单个视频精读走 [learn-video](../../research/learn-video/)。
63
+
64
+ 设计文档:`docs/superpowers/specs/2026-08-26-creator-channel-registry-design.md`。
65
+
66
+ ## 参考文件
67
+
68
+ | 文件 | 用途 |
69
+ |------|------|
70
+ | `scripts/roster_locate.py` | 定位 roster launcher(跟 `browser_fetch_locate.py` 同款,独立副本) |
@@ -0,0 +1,46 @@
1
+ """定位 roster launcher(跟 browser_fetch_locate.py 同款,独立副本)。
2
+
3
+ 两种布局:
4
+ - Dev 模式:本 skill 跑在 harveyz-skill 的 checkout 里,
5
+ tools/roster/roster.sh 在 scripts/ 上面四层。
6
+ - 装机模式:经 hskill install 装到 ~/.claude/skills 等处,roster 作为 tool
7
+ 单独安装,launcher 落在 ~/.local/bin/roster。
8
+ """
9
+ import shutil
10
+ import sys
11
+ from pathlib import Path
12
+
13
+
14
+ def _dev_path() -> Path:
15
+ return Path(__file__).resolve().parents[4] / "tools" / "roster" / "roster.sh"
16
+
17
+
18
+ def find_roster() -> str:
19
+ dev = _dev_path()
20
+ if dev.exists():
21
+ return str(dev)
22
+
23
+ on_path = shutil.which("roster")
24
+ if on_path:
25
+ return on_path
26
+
27
+ installed = Path.home() / ".local" / "bin" / "roster"
28
+ if installed.exists():
29
+ return str(installed)
30
+
31
+ raise FileNotFoundError(
32
+ "roster launcher 未找到。请在 harveyz-skill 的 checkout 里运行本 skill,"
33
+ "或运行 `hskill install --tool roster`。"
34
+ )
35
+
36
+
37
+ def main():
38
+ try:
39
+ print(f"FOUND: {find_roster()}")
40
+ except FileNotFoundError as e:
41
+ print(f"NOT_FOUND: {e}")
42
+ sys.exit(1)
43
+
44
+
45
+ if __name__ == "__main__":
46
+ main()
@@ -0,0 +1,24 @@
1
+ """roster_locate 的两种布局:仓库 checkout 内 / hskill 装机后。"""
2
+ import sys
3
+ from pathlib import Path
4
+
5
+ import pytest
6
+
7
+ sys.path.insert(0, str(Path(__file__).resolve().parent.parent / "scripts"))
8
+
9
+ import roster_locate
10
+
11
+
12
+ def test_finds_launcher_in_repo_checkout():
13
+ """本测试就跑在 checkout 里,dev 路径必须命中真实文件。"""
14
+ found = Path(roster_locate.find_roster())
15
+ assert found.name == "roster.sh"
16
+ assert found.exists()
17
+
18
+
19
+ def test_raises_when_nothing_found(monkeypatch, tmp_path):
20
+ monkeypatch.setattr(roster_locate, "_dev_path", lambda: tmp_path / "nope.sh")
21
+ monkeypatch.setattr(roster_locate.shutil, "which", lambda _: None)
22
+ monkeypatch.setattr(Path, "home", staticmethod(lambda: tmp_path))
23
+ with pytest.raises(FileNotFoundError, match="roster"):
24
+ roster_locate.find_roster()