harveyz-skill 0.27.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 (402) hide show
  1. package/CHANGELOG.md +30 -0
  2. package/package.json +12 -5
  3. package/skills/coding/explain-pm/SKILL.md +39 -13
  4. package/skills/coding/handoff/SKILL.md +9 -3
  5. package/skills/coding/handoff/assets/handoff-template.md +1 -1
  6. package/skills/coding/handoff/evals/evals.json +50 -4
  7. package/skills/coding/setup-debug/evals/evals.json +23 -1
  8. package/skills/feed/capture-creator/SKILL.md +107 -0
  9. package/skills/feed/capture-creator/scripts/roster_locate.py +46 -0
  10. package/skills/feed/manage-roster/SKILL.md +70 -0
  11. package/skills/feed/manage-roster/scripts/__pycache__/mcp_call_locate.cpython-314.pyc +0 -0
  12. package/skills/feed/manage-roster/scripts/__pycache__/roster_locate.cpython-314.pyc +0 -0
  13. package/skills/feed/manage-roster/scripts/__pycache__/roster_mcp_locate.cpython-314.pyc +0 -0
  14. package/skills/feed/manage-roster/scripts/roster_locate.py +46 -0
  15. package/skills/feed/manage-roster/tests/__pycache__/test_roster_locate.cpython-314-pytest-9.1.1.pyc +0 -0
  16. package/skills/feed/manage-roster/tests/__pycache__/test_roster_mcp_client.cpython-314-pytest-9.1.1.pyc +0 -0
  17. package/skills/feed/manage-roster/tests/__pycache__/test_roster_mcp_locate.cpython-314-pytest-9.1.1.pyc +0 -0
  18. package/skills/feed/manage-roster/tests/test_roster_locate.py +24 -0
  19. package/skills/feed/sync-xtimeline/SKILL.md +86 -0
  20. package/skills/feed/sync-xtimeline/platforms/SKILL.claude.md +18 -0
  21. package/skills/feed/sync-xtimeline/platforms/SKILL.codex.md +22 -0
  22. package/skills/feed/sync-xtimeline/platforms/SKILL.hermes.md +22 -0
  23. package/skills/feed/sync-xtimeline/platforms/SKILL.pi.md +18 -0
  24. package/skills/feed/sync-xtimeline/scripts/__pycache__/archive_tweets.cpython-314.pyc +0 -0
  25. package/skills/feed/sync-xtimeline/scripts/__pycache__/browser_fetch_cli.cpython-314.pyc +0 -0
  26. package/skills/feed/sync-xtimeline/scripts/__pycache__/browser_fetch_locate.cpython-314.pyc +0 -0
  27. package/skills/feed/sync-xtimeline/scripts/__pycache__/config.cpython-314.pyc +0 -0
  28. package/skills/feed/sync-xtimeline/scripts/__pycache__/cursor.cpython-314.pyc +0 -0
  29. package/skills/feed/sync-xtimeline/scripts/__pycache__/fetch_new_tweets.cpython-314.pyc +0 -0
  30. package/skills/feed/sync-xtimeline/scripts/__pycache__/mcp_timeline_client.cpython-314.pyc +0 -0
  31. package/skills/feed/sync-xtimeline/scripts/__pycache__/render_digest.cpython-314.pyc +0 -0
  32. package/skills/feed/sync-xtimeline/scripts/__pycache__/render_view.cpython-314.pyc +0 -0
  33. package/skills/feed/sync-xtimeline/scripts/__pycache__/roster_client.cpython-314.pyc +0 -0
  34. package/skills/feed/sync-xtimeline/scripts/__pycache__/roster_locate.cpython-314.pyc +0 -0
  35. package/skills/feed/sync-xtimeline/scripts/archive_tweets.py +41 -0
  36. package/skills/feed/sync-xtimeline/scripts/browser_fetch_cli.py +23 -0
  37. package/skills/{research/clip-url/scripts/browser_fetch_mcp_locate.py → feed/sync-xtimeline/scripts/browser_fetch_locate.py} +16 -14
  38. package/skills/feed/sync-xtimeline/scripts/config.py +18 -0
  39. package/skills/feed/sync-xtimeline/scripts/cursor.py +27 -0
  40. package/skills/feed/sync-xtimeline/scripts/fetch_new_tweets.py +111 -0
  41. package/skills/feed/sync-xtimeline/scripts/mcp_timeline_client.py +20 -0
  42. package/skills/feed/sync-xtimeline/scripts/render_digest.py +102 -0
  43. package/skills/feed/sync-xtimeline/scripts/render_view.py +264 -0
  44. package/skills/feed/sync-xtimeline/scripts/roster_client.py +49 -0
  45. package/skills/feed/sync-xtimeline/scripts/roster_locate.py +46 -0
  46. package/skills/feed/sync-xtimeline/tests/__pycache__/conftest.cpython-314-pytest-9.1.1.pyc +0 -0
  47. package/skills/feed/sync-xtimeline/tests/__pycache__/test_archive_tweets.cpython-314-pytest-9.1.1.pyc +0 -0
  48. package/skills/feed/sync-xtimeline/tests/__pycache__/test_browser_fetch_locate.cpython-314-pytest-9.1.1.pyc +0 -0
  49. package/skills/feed/sync-xtimeline/tests/__pycache__/test_cursor.cpython-314-pytest-9.1.1.pyc +0 -0
  50. package/skills/feed/sync-xtimeline/tests/__pycache__/test_fetch_new_tweets.cpython-314-pytest-9.1.1.pyc +0 -0
  51. package/skills/feed/sync-xtimeline/tests/__pycache__/test_mcp_timeline_client.cpython-314-pytest-9.1.1.pyc +0 -0
  52. package/skills/feed/sync-xtimeline/tests/__pycache__/test_render_digest.cpython-314-pytest-9.1.1.pyc +0 -0
  53. package/skills/feed/sync-xtimeline/tests/__pycache__/test_render_view.cpython-314-pytest-9.1.1.pyc +0 -0
  54. package/skills/feed/sync-xtimeline/tests/__pycache__/test_roster_client.cpython-314-pytest-9.1.1.pyc +0 -0
  55. package/skills/feed/sync-xtimeline/tests/conftest.py +41 -0
  56. package/skills/feed/sync-xtimeline/tests/test_archive_tweets.py +78 -0
  57. package/skills/feed/sync-xtimeline/tests/test_browser_fetch_locate.py +69 -0
  58. package/skills/feed/sync-xtimeline/tests/test_cursor.py +40 -0
  59. package/skills/feed/sync-xtimeline/tests/test_fetch_new_tweets.py +233 -0
  60. package/skills/feed/sync-xtimeline/tests/test_mcp_timeline_client.py +44 -0
  61. package/skills/feed/sync-xtimeline/tests/test_render_digest.py +239 -0
  62. package/skills/feed/sync-xtimeline/tests/test_render_view.py +107 -0
  63. package/skills/feed/sync-xtimeline/tests/test_roster_client.py +80 -0
  64. package/skills/feed/sync-ytchannel/SKILL.md +75 -0
  65. package/skills/feed/sync-ytchannel/platforms/SKILL.claude.md +17 -0
  66. package/skills/feed/sync-ytchannel/platforms/SKILL.codex.md +21 -0
  67. package/skills/feed/sync-ytchannel/platforms/SKILL.hermes.md +21 -0
  68. package/skills/feed/sync-ytchannel/platforms/SKILL.pi.md +17 -0
  69. package/skills/feed/sync-ytchannel/scripts/__pycache__/browser_fetch_cli.cpython-314.pyc +0 -0
  70. package/skills/feed/sync-ytchannel/scripts/__pycache__/browser_fetch_locate.cpython-314.pyc +0 -0
  71. package/skills/feed/sync-ytchannel/scripts/__pycache__/config.cpython-314.pyc +0 -0
  72. package/skills/feed/sync-ytchannel/scripts/__pycache__/cursor.cpython-314.pyc +0 -0
  73. package/skills/feed/sync-ytchannel/scripts/__pycache__/digest.cpython-314.pyc +0 -0
  74. package/skills/feed/sync-ytchannel/scripts/__pycache__/mcp_channel_client.cpython-314.pyc +0 -0
  75. package/skills/feed/sync-ytchannel/scripts/__pycache__/roster_client.cpython-314.pyc +0 -0
  76. package/skills/feed/sync-ytchannel/scripts/__pycache__/roster_locate.cpython-314.pyc +0 -0
  77. package/skills/feed/sync-ytchannel/scripts/__pycache__/sync_channels.cpython-314.pyc +0 -0
  78. package/skills/feed/sync-ytchannel/scripts/browser_fetch_cli.py +23 -0
  79. package/skills/feed/sync-ytchannel/scripts/browser_fetch_locate.py +50 -0
  80. package/skills/feed/sync-ytchannel/scripts/config.py +18 -0
  81. package/skills/feed/sync-ytchannel/scripts/cursor.py +29 -0
  82. package/skills/feed/sync-ytchannel/scripts/digest.py +47 -0
  83. package/skills/feed/sync-ytchannel/scripts/mcp_channel_client.py +18 -0
  84. package/skills/feed/sync-ytchannel/scripts/roster_client.py +49 -0
  85. package/skills/feed/sync-ytchannel/scripts/roster_locate.py +46 -0
  86. package/skills/feed/sync-ytchannel/scripts/sync_channels.py +120 -0
  87. package/skills/feed/sync-ytchannel/tests/__pycache__/conftest.cpython-314-pytest-9.1.1.pyc +0 -0
  88. package/skills/feed/sync-ytchannel/tests/__pycache__/test_browser_fetch_locate.cpython-314-pytest-9.1.1.pyc +0 -0
  89. package/skills/feed/sync-ytchannel/tests/__pycache__/test_cursor.cpython-314-pytest-9.1.1.pyc +0 -0
  90. package/skills/feed/sync-ytchannel/tests/__pycache__/test_digest.cpython-314-pytest-9.1.1.pyc +0 -0
  91. package/skills/feed/sync-ytchannel/tests/__pycache__/test_mcp_channel_client.cpython-314-pytest-9.1.1.pyc +0 -0
  92. package/skills/feed/sync-ytchannel/tests/__pycache__/test_roster_client.cpython-314-pytest-9.1.1.pyc +0 -0
  93. package/skills/feed/sync-ytchannel/tests/__pycache__/test_sync_channels.cpython-314-pytest-9.1.1.pyc +0 -0
  94. package/skills/feed/sync-ytchannel/tests/conftest.py +7 -0
  95. package/skills/feed/sync-ytchannel/tests/test_browser_fetch_locate.py +69 -0
  96. package/skills/feed/sync-ytchannel/tests/test_cursor.py +47 -0
  97. package/skills/feed/sync-ytchannel/tests/test_digest.py +77 -0
  98. package/skills/feed/sync-ytchannel/tests/test_mcp_channel_client.py +42 -0
  99. package/skills/feed/sync-ytchannel/tests/test_roster_client.py +83 -0
  100. package/skills/feed/sync-ytchannel/tests/test_sync_channels.py +250 -0
  101. package/skills/mint/learn-skill/evals/evals.json +34 -16
  102. package/skills/mint/publish-skill/SKILL.md +36 -15
  103. package/skills/research/clip-url/SKILL.md +54 -31
  104. package/skills/research/clip-url/platforms/SKILL.claude.md +28 -0
  105. package/skills/research/clip-url/platforms/SKILL.codex.md +21 -0
  106. package/skills/research/clip-url/platforms/SKILL.hermes.md +21 -0
  107. package/skills/research/clip-url/platforms/SKILL.pi.md +28 -0
  108. package/skills/research/clip-url/references/subagent-self-optimize-prompt.md +11 -12
  109. package/skills/research/clip-url/references/subagent1-fetch-prompt.md +3 -3
  110. package/skills/research/clip-url/scripts/__pycache__/article_meta.cpython-314.pyc +0 -0
  111. package/skills/research/clip-url/scripts/__pycache__/browser_fetch_cli.cpython-314.pyc +0 -0
  112. package/skills/research/clip-url/scripts/__pycache__/browser_fetch_locate.cpython-314.pyc +0 -0
  113. package/skills/research/clip-url/scripts/__pycache__/browser_fetch_mcp_locate.cpython-314.pyc +0 -0
  114. package/skills/research/clip-url/scripts/__pycache__/dedup_check.cpython-314.pyc +0 -0
  115. package/skills/research/clip-url/scripts/__pycache__/mcp_debug_client.cpython-314.pyc +0 -0
  116. package/skills/research/clip-url/scripts/__pycache__/mcp_fetch_client.cpython-314.pyc +0 -0
  117. package/skills/research/clip-url/scripts/__pycache__/vault_config.cpython-314.pyc +0 -0
  118. package/skills/research/clip-url/scripts/__pycache__/write_meta_and_separate.cpython-314.pyc +0 -0
  119. package/skills/research/clip-url/scripts/browser_fetch_cli.py +23 -0
  120. package/skills/research/clip-url/scripts/browser_fetch_locate.py +50 -0
  121. package/skills/research/clip-url/scripts/chrome_profile_config.py +21 -47
  122. package/skills/research/clip-url/scripts/detect_xcom_chrome_profile.py +12 -32
  123. package/skills/research/clip-url/scripts/mcp_debug_client.py +34 -53
  124. package/skills/research/clip-url/scripts/mcp_fetch_client.py +21 -57
  125. package/skills/research/clip-url/scripts/vault_config.py +25 -5
  126. package/skills/research/clip-url/tests/__pycache__/conftest.cpython-314-pytest-9.1.1.pyc +0 -0
  127. package/skills/research/clip-url/tests/__pycache__/test_article_meta.cpython-314-pytest-9.1.1.pyc +0 -0
  128. package/skills/research/clip-url/tests/__pycache__/test_browser_fetch_locate.cpython-314-pytest-9.1.1.pyc +0 -0
  129. package/skills/research/clip-url/tests/__pycache__/test_browser_fetch_mcp_locate.cpython-314-pytest-9.1.1.pyc +0 -0
  130. package/skills/research/clip-url/tests/__pycache__/test_chrome_profile_config.cpython-314-pytest-9.1.1.pyc +0 -0
  131. package/skills/research/clip-url/tests/__pycache__/test_dedup_check.cpython-314-pytest-9.1.1.pyc +0 -0
  132. package/skills/research/clip-url/tests/__pycache__/test_detect_xcom_chrome_profile.cpython-314-pytest-9.1.1.pyc +0 -0
  133. package/skills/research/clip-url/tests/__pycache__/test_mcp_debug_client.cpython-314-pytest-9.1.1.pyc +0 -0
  134. package/skills/research/clip-url/tests/__pycache__/test_mcp_fetch_client.cpython-314-pytest-9.1.1.pyc +0 -0
  135. package/skills/research/clip-url/tests/__pycache__/test_vault_config.cpython-314-pytest-9.1.1.pyc +0 -0
  136. package/skills/research/clip-url/tests/__pycache__/test_write_meta_and_separate.cpython-314-pytest-9.1.1.pyc +0 -0
  137. package/skills/research/clip-url/tests/test_browser_fetch_locate.py +69 -0
  138. package/skills/research/clip-url/tests/test_chrome_profile_config.py +5 -5
  139. package/skills/research/clip-url/tests/test_detect_xcom_chrome_profile.py +5 -6
  140. package/skills/research/clip-url/tests/test_mcp_debug_client.py +5 -6
  141. package/skills/research/clip-url/tests/test_mcp_fetch_client.py +11 -19
  142. package/skills/research/clip-url/tests/test_vault_config.py +18 -0
  143. package/skills/research/extract-cognition/evals/evals.json +17 -0
  144. package/skills/research/fetch-paper/SKILL.md +239 -0
  145. package/skills/research/learn-video/SKILL.md +58 -22
  146. package/skills/writing/forge-doc/tests/__pycache__/test_md_to_pdf.cpython-314-pytest-9.1.1.pyc +0 -0
  147. package/skills-index.json +58 -24
  148. package/tools/browser-fetch/browser-fetch.sh +69 -0
  149. package/tools/browser-fetch/browser_fetch/__init__.py +1 -0
  150. package/tools/browser-fetch/browser_fetch/__pycache__/__init__.cpython-314.pyc +0 -0
  151. package/tools/browser-fetch/browser_fetch/__pycache__/cli.cpython-314.pyc +0 -0
  152. package/tools/browser-fetch/browser_fetch/__pycache__/config.cpython-314.pyc +0 -0
  153. package/tools/{browser-fetch-mcp/browser_fetch_mcp → browser-fetch/browser_fetch}/__pycache__/cookies.cpython-314.pyc +0 -0
  154. package/tools/browser-fetch/browser_fetch/__pycache__/core.cpython-314.pyc +0 -0
  155. package/tools/{browser-fetch-mcp/browser_fetch_mcp → browser-fetch/browser_fetch}/__pycache__/extractors.cpython-314.pyc +0 -0
  156. package/tools/{browser-fetch-mcp/browser_fetch_mcp → browser-fetch/browser_fetch}/__pycache__/images.cpython-314.pyc +0 -0
  157. package/tools/{browser-fetch-mcp/browser_fetch_mcp → browser-fetch/browser_fetch}/__pycache__/markdown.cpython-314.pyc +0 -0
  158. package/tools/browser-fetch/browser_fetch/__pycache__/pacing.cpython-314.pyc +0 -0
  159. package/tools/browser-fetch/browser_fetch/__pycache__/pacing_log.cpython-314.pyc +0 -0
  160. package/tools/{browser-fetch-mcp/browser_fetch_mcp → browser-fetch/browser_fetch}/__pycache__/profiles.cpython-314.pyc +0 -0
  161. package/tools/browser-fetch/browser_fetch/cli.py +100 -0
  162. package/tools/{browser-fetch-mcp/browser_fetch_mcp → browser-fetch/browser_fetch}/config.py +18 -1
  163. package/tools/{browser-fetch-mcp/browser_fetch_mcp/server.py → browser-fetch/browser_fetch/core.py} +352 -27
  164. package/tools/{browser-fetch-mcp/browser_fetch_mcp → browser-fetch/browser_fetch}/extractors.py +278 -0
  165. package/tools/browser-fetch/browser_fetch/pacing.py +66 -0
  166. package/tools/browser-fetch/browser_fetch/pacing_log.py +28 -0
  167. package/tools/{browser-fetch-mcp/browser_fetch_mcp → browser-fetch/browser_fetch}/profiles.py +1 -1
  168. package/tools/{browser-fetch-mcp → browser-fetch}/pyproject.toml +4 -5
  169. package/tools/browser-fetch/tests/__pycache__/conftest.cpython-314-pytest-9.1.1.pyc +0 -0
  170. package/tools/browser-fetch/tests/__pycache__/test_cli.cpython-314-pytest-9.1.1.pyc +0 -0
  171. package/tools/browser-fetch/tests/__pycache__/test_cli_fetch.cpython-314-pytest-9.1.1.pyc +0 -0
  172. package/tools/browser-fetch/tests/__pycache__/test_cli_page_eval.cpython-314-pytest-9.1.1.pyc +0 -0
  173. package/tools/browser-fetch/tests/__pycache__/test_config.cpython-314-pytest-9.1.1.pyc +0 -0
  174. package/tools/{browser-fetch-mcp → browser-fetch}/tests/__pycache__/test_cookies.cpython-314-pytest-9.1.1.pyc +0 -0
  175. package/tools/browser-fetch/tests/__pycache__/test_core_import.cpython-314-pytest-9.1.1.pyc +0 -0
  176. package/tools/browser-fetch/tests/__pycache__/test_data_dir_migration.cpython-314-pytest-9.1.1.pyc +0 -0
  177. package/tools/browser-fetch/tests/__pycache__/test_evaluate_js.cpython-314-pytest-9.1.1.pyc +0 -0
  178. package/tools/{browser-fetch-mcp → browser-fetch}/tests/__pycache__/test_extractors.cpython-314-pytest-9.1.1.pyc +0 -0
  179. package/tools/browser-fetch/tests/__pycache__/test_extractors_timeline.cpython-314-pytest-9.1.1.pyc +0 -0
  180. package/tools/browser-fetch/tests/__pycache__/test_extractors_youtube.cpython-314-pytest-9.1.1.pyc +0 -0
  181. package/tools/browser-fetch/tests/__pycache__/test_fetch_article.cpython-314-pytest-9.1.1.pyc +0 -0
  182. package/tools/browser-fetch/tests/__pycache__/test_fetch_channel_videos.cpython-314-pytest-9.1.1.pyc +0 -0
  183. package/tools/browser-fetch/tests/__pycache__/test_fetch_user_timeline.cpython-314-pytest-9.1.1.pyc +0 -0
  184. package/tools/{browser-fetch-mcp → browser-fetch}/tests/__pycache__/test_images.cpython-314-pytest-9.1.1.pyc +0 -0
  185. package/tools/browser-fetch/tests/__pycache__/test_launcher_python_version.cpython-314-pytest-9.1.1.pyc +0 -0
  186. package/tools/browser-fetch/tests/__pycache__/test_launcher_stdout_contract.cpython-314-pytest-9.1.1.pyc +0 -0
  187. package/tools/{browser-fetch-mcp → browser-fetch}/tests/__pycache__/test_markdown.cpython-314-pytest-9.1.1.pyc +0 -0
  188. package/tools/browser-fetch/tests/__pycache__/test_pacing.cpython-314-pytest-9.1.1.pyc +0 -0
  189. package/tools/browser-fetch/tests/__pycache__/test_pacing_log.cpython-314-pytest-9.1.1.pyc +0 -0
  190. package/tools/{browser-fetch-mcp → browser-fetch}/tests/__pycache__/test_profiles.cpython-314-pytest-9.1.1.pyc +0 -0
  191. package/tools/browser-fetch/tests/conftest.py +30 -0
  192. package/tools/browser-fetch/tests/test_cli.py +44 -0
  193. package/tools/browser-fetch/tests/test_cli_fetch.py +45 -0
  194. package/tools/browser-fetch/tests/test_cli_page_eval.py +48 -0
  195. package/tools/browser-fetch/tests/test_config.py +47 -0
  196. package/tools/{browser-fetch-mcp → browser-fetch}/tests/test_cookies.py +3 -3
  197. package/tools/browser-fetch/tests/test_core_import.py +38 -0
  198. package/tools/browser-fetch/tests/test_data_dir_migration.py +45 -0
  199. package/tools/browser-fetch/tests/test_evaluate_js.py +27 -0
  200. package/tools/{browser-fetch-mcp → browser-fetch}/tests/test_extractors.py +1 -1
  201. package/tools/browser-fetch/tests/test_extractors_timeline.py +229 -0
  202. package/tools/browser-fetch/tests/test_extractors_youtube.py +291 -0
  203. package/tools/{browser-fetch-mcp → browser-fetch}/tests/test_fetch_article.py +105 -176
  204. package/tools/browser-fetch/tests/test_fetch_channel_videos.py +39 -0
  205. package/tools/browser-fetch/tests/test_fetch_user_timeline.py +31 -0
  206. package/tools/{browser-fetch-mcp → browser-fetch}/tests/test_images.py +1 -1
  207. package/tools/browser-fetch/tests/test_launcher_python_version.py +50 -0
  208. package/tools/browser-fetch/tests/test_launcher_stdout_contract.py +47 -0
  209. package/tools/{browser-fetch-mcp → browser-fetch}/tests/test_markdown.py +1 -1
  210. package/tools/browser-fetch/tests/test_pacing.py +93 -0
  211. package/tools/browser-fetch/tests/test_pacing_log.py +53 -0
  212. package/tools/{browser-fetch-mcp → browser-fetch}/tests/test_profiles.py +6 -6
  213. package/tools/browser-fetch/tool.json +8 -0
  214. package/tools/hub/hub/core/__pycache__/todo_sync.cpython-314.pyc +0 -0
  215. package/tools/hub/hub/tui/__pycache__/app.cpython-314.pyc +0 -0
  216. package/tools/hub/hub/tui/panels/__pycache__/projects.cpython-314.pyc +0 -0
  217. package/tools/hub/hub/tui/panels/__pycache__/tasks.cpython-314.pyc +0 -0
  218. package/tools/hub/hub.sh +15 -2
  219. package/tools/hub/pyproject.toml +1 -1
  220. package/tools/hub/tests/__pycache__/test_cli.cpython-314-pytest-9.1.0.pyc +0 -0
  221. package/tools/hub/tests/__pycache__/test_cli.cpython-314-pytest-9.1.1.pyc +0 -0
  222. package/tools/hub/tests/__pycache__/test_db.cpython-314-pytest-9.1.1.pyc +0 -0
  223. package/tools/hub/tests/__pycache__/test_launcher_python_version.cpython-314-pytest-9.1.0.pyc +0 -0
  224. package/tools/hub/tests/__pycache__/test_launcher_python_version.cpython-314-pytest-9.1.1.pyc +0 -0
  225. package/tools/hub/tests/__pycache__/test_migrate.cpython-314-pytest-9.1.1.pyc +0 -0
  226. package/tools/hub/tests/__pycache__/test_projects.cpython-314-pytest-9.1.1.pyc +0 -0
  227. package/tools/hub/tests/__pycache__/test_scan.cpython-314-pytest-9.1.0.pyc +0 -0
  228. package/tools/hub/tests/__pycache__/test_scan.cpython-314-pytest-9.1.1.pyc +0 -0
  229. package/tools/hub/tests/__pycache__/test_tasks.cpython-314-pytest-9.1.1.pyc +0 -0
  230. package/tools/hub/tests/__pycache__/test_todo_sync.cpython-314-pytest-9.1.0.pyc +0 -0
  231. package/tools/hub/tests/__pycache__/test_todo_sync.cpython-314-pytest-9.1.1.pyc +0 -0
  232. package/tools/hub/tests/__pycache__/test_tui_app.cpython-314-pytest-9.1.0.pyc +0 -0
  233. package/tools/hub/tests/__pycache__/test_tui_app.cpython-314-pytest-9.1.1.pyc +0 -0
  234. package/tools/hub/tests/__pycache__/test_tui_git.cpython-314-pytest-9.1.1.pyc +0 -0
  235. package/tools/hub/tests/__pycache__/test_tui_git_panel.cpython-314-pytest-9.1.0.pyc +0 -0
  236. package/tools/hub/tests/__pycache__/test_tui_git_panel.cpython-314-pytest-9.1.1.pyc +0 -0
  237. package/tools/hub/tests/__pycache__/test_tui_projects_panel.cpython-314-pytest-9.1.1.pyc +0 -0
  238. package/tools/hub/tests/__pycache__/test_tui_tasks_panel.cpython-314-pytest-9.1.0.pyc +0 -0
  239. package/tools/hub/tests/__pycache__/test_tui_tasks_panel.cpython-314-pytest-9.1.1.pyc +0 -0
  240. package/tools/hub/tests/test_launcher_python_version.py +50 -0
  241. package/tools/hub/tool.json +1 -1
  242. package/tools/roster/pyproject.toml +21 -0
  243. package/tools/roster/roster/__init__.py +11 -0
  244. package/tools/roster/roster/__main__.py +299 -0
  245. package/tools/roster/roster/__pycache__/__init__.cpython-314.pyc +0 -0
  246. package/tools/roster/roster/__pycache__/__main__.cpython-314.pyc +0 -0
  247. package/tools/roster/roster/__pycache__/config.cpython-314.pyc +0 -0
  248. package/tools/roster/roster/__pycache__/migrate.cpython-314.pyc +0 -0
  249. package/tools/roster/roster/__pycache__/profiles.cpython-314.pyc +0 -0
  250. package/tools/roster/roster/__pycache__/registry.cpython-314.pyc +0 -0
  251. package/tools/roster/roster/__pycache__/server.cpython-314.pyc +0 -0
  252. package/tools/roster/roster/__pycache__/state.cpython-314.pyc +0 -0
  253. package/tools/roster/roster/__pycache__/urls.cpython-314.pyc +0 -0
  254. package/tools/roster/roster/config.py +45 -0
  255. package/tools/roster/roster/migrate.py +45 -0
  256. package/tools/roster/roster/profiles.py +115 -0
  257. package/tools/roster/roster/registry.py +126 -0
  258. package/tools/roster/roster/state.py +66 -0
  259. package/tools/roster/roster/urls.py +39 -0
  260. package/tools/roster/roster.sh +47 -0
  261. package/tools/roster/tests/__pycache__/conftest.cpython-314-pytest-9.1.1.pyc +0 -0
  262. package/tools/roster/tests/__pycache__/test_cli.cpython-314-pytest-9.1.1.pyc +0 -0
  263. package/tools/roster/tests/__pycache__/test_config.cpython-314-pytest-9.1.1.pyc +0 -0
  264. package/tools/roster/tests/__pycache__/test_launcher_python_version.cpython-314-pytest-9.1.1.pyc +0 -0
  265. package/tools/roster/tests/__pycache__/test_launcher_stdout_contract.cpython-314-pytest-9.1.1.pyc +0 -0
  266. package/tools/roster/tests/__pycache__/test_migrate.cpython-314-pytest-9.1.1.pyc +0 -0
  267. package/tools/roster/tests/__pycache__/test_profiles.cpython-314-pytest-9.1.1.pyc +0 -0
  268. package/tools/roster/tests/__pycache__/test_registry.cpython-314-pytest-9.1.1.pyc +0 -0
  269. package/tools/roster/tests/__pycache__/test_registry_merge.cpython-314-pytest-9.1.1.pyc +0 -0
  270. package/tools/roster/tests/__pycache__/test_server.cpython-314-pytest-9.1.1.pyc +0 -0
  271. package/tools/roster/tests/__pycache__/test_state.cpython-314-pytest-9.1.1.pyc +0 -0
  272. package/tools/roster/tests/__pycache__/test_urls.cpython-314-pytest-9.1.1.pyc +0 -0
  273. package/tools/roster/tests/conftest.py +16 -0
  274. package/tools/roster/tests/test_cli.py +270 -0
  275. package/tools/roster/tests/test_config.py +41 -0
  276. package/tools/roster/tests/test_launcher_python_version.py +50 -0
  277. package/tools/roster/tests/test_launcher_stdout_contract.py +38 -0
  278. package/tools/roster/tests/test_migrate.py +98 -0
  279. package/tools/roster/tests/test_profiles.py +167 -0
  280. package/tools/roster/tests/test_registry.py +131 -0
  281. package/tools/roster/tests/test_registry_merge.py +85 -0
  282. package/tools/roster/tests/test_state.py +93 -0
  283. package/tools/roster/tests/test_urls.py +43 -0
  284. package/tools/roster/tool.json +8 -0
  285. package/tools/skill-harness/adapters/claude.js +50 -0
  286. package/tools/skill-harness/adapters/hermes.js +66 -0
  287. package/tools/skill-harness/adapters/pi.js +45 -0
  288. package/tools/skill-harness/attribution.js +16 -0
  289. package/tools/skill-harness/cli.js +265 -0
  290. package/tools/skill-harness/coverage.js +64 -0
  291. package/tools/skill-harness/declarations.js +75 -0
  292. package/tools/skill-harness/grade/index.js +208 -0
  293. package/tools/skill-harness/grade/parse.js +48 -0
  294. package/tools/skill-harness/grade/prompt.js +46 -0
  295. package/tools/skill-harness/harvest.js +136 -0
  296. package/tools/skill-harness/jail.js +59 -0
  297. package/tools/skill-harness/matrix.json +9 -0
  298. package/tools/skill-harness/parse/claude-code-jsonl.js +91 -0
  299. package/tools/skill-harness/parse/pi-jsonl.js +80 -0
  300. package/tools/skill-harness/probe/probe-anchor/SKILL.md +21 -0
  301. package/tools/skill-harness/probe/probe-anchor/references/token.md +1 -0
  302. package/tools/skill-harness/profiles.js +47 -0
  303. package/tools/skill-harness/prompt.js +36 -0
  304. package/tools/skill-harness/quality-report.js +134 -0
  305. package/tools/skill-harness/record.js +53 -0
  306. package/tools/skill-harness/report.js +85 -0
  307. package/tools/skill-harness/runner.js +252 -0
  308. package/tools/skill-harness/select.js +117 -0
  309. package/tools/skill-harness/variance.js +23 -0
  310. package/tools/sync-agent/pyproject.toml +1 -1
  311. package/tools/sync-agent/sync.sh +15 -2
  312. package/tools/sync-agent/sync_agent/__pycache__/__init__.cpython-314.pyc +0 -0
  313. package/tools/sync-agent/sync_agent/__pycache__/__main__.cpython-314.pyc +0 -0
  314. package/tools/sync-agent/sync_agent/__pycache__/cli.cpython-314.pyc +0 -0
  315. package/tools/sync-agent/sync_agent/__pycache__/config.cpython-314.pyc +0 -0
  316. package/tools/sync-agent/sync_agent/__pycache__/launchd.cpython-314.pyc +0 -0
  317. package/tools/sync-agent/sync_agent/__pycache__/syncthing.cpython-314.pyc +0 -0
  318. package/tools/sync-agent/tests/__pycache__/__init__.cpython-314.pyc +0 -0
  319. package/tools/sync-agent/tests/__pycache__/test_cli.cpython-314-pytest-9.1.1.pyc +0 -0
  320. package/tools/sync-agent/tests/__pycache__/test_config.cpython-314-pytest-9.1.1.pyc +0 -0
  321. package/tools/sync-agent/tests/__pycache__/test_launcher_python_version.cpython-314-pytest-9.1.1.pyc +0 -0
  322. package/tools/sync-agent/tests/__pycache__/test_syncthing.cpython-314-pytest-9.1.1.pyc +0 -0
  323. package/tools/sync-agent/tests/test_launcher_python_version.py +50 -0
  324. package/tools/sync-agent/tool.json +1 -1
  325. package/skills/research/clip-url/tests/test_browser_fetch_mcp_locate.py +0 -83
  326. package/skills/research/extract-url/SKILL.md +0 -213
  327. package/skills/research/extract-url/experiment/candidate-tag-constraints/INSTRUCTIONS.md +0 -41
  328. package/skills/research/extract-url/experiment/split-tag-candidate/INSTRUCTIONS.md +0 -53
  329. package/skills/research/extract-url/experiment/tag-order-language/INSTRUCTIONS.md +0 -50
  330. package/skills/research/extract-url/experiment/two-phase-tagging/INSTRUCTIONS.md +0 -55
  331. package/skills/research/extract-url/experiment/two-phase-tagging/expected-output.yaml +0 -30
  332. package/skills/research/extract-url/experiment/two-phase-tagging/fixture-article.txt +0 -66
  333. package/skills/research/extract-url/experiment/two-phase-tagging/fixture-fixed-tags.txt +0 -23
  334. package/skills/research/extract-url/experiment/two-phase-tagging/v1-prompt.txt +0 -102
  335. package/skills/research/extract-url/platforms/SKILL.claude.md +0 -35
  336. package/skills/research/extract-url/platforms/SKILL.codex.md +0 -25
  337. package/skills/research/extract-url/platforms/SKILL.hermes.md +0 -25
  338. package/skills/research/extract-url/platforms/SKILL.pi.md +0 -41
  339. package/skills/research/extract-url/references/__pycache__/article_utils.cpython-314.pyc +0 -0
  340. package/skills/research/extract-url/references/article_utils.py +0 -418
  341. package/skills/research/extract-url/references/file-format.md +0 -104
  342. package/skills/research/extract-url/references/subagent1-fetch-prompt.md +0 -67
  343. package/skills/research/extract-url/references/subagent2-tag-translate-prompt.md +0 -93
  344. package/skills/research/extract-url/scripts/__pycache__/config.cpython-314.pyc +0 -0
  345. package/skills/research/extract-url/scripts/__pycache__/migrate_to_folder_structure.cpython-314.pyc +0 -0
  346. package/skills/research/extract-url/scripts/__pycache__/playwright_web.cpython-314.pyc +0 -0
  347. package/skills/research/extract-url/scripts/__pycache__/playwright_xcom.cpython-314.pyc +0 -0
  348. package/skills/research/extract-url/scripts/config.py +0 -74
  349. package/skills/research/extract-url/scripts/count_article_stats.py +0 -35
  350. package/skills/research/extract-url/scripts/dedup_check.py +0 -27
  351. package/skills/research/extract-url/scripts/detect_chrome_profile.py +0 -108
  352. package/skills/research/extract-url/scripts/migrate_to_folder_structure.py +0 -459
  353. package/skills/research/extract-url/scripts/playwright_web.py +0 -227
  354. package/skills/research/extract-url/scripts/playwright_web_arxiv.py +0 -284
  355. package/skills/research/extract-url/scripts/playwright_web_wechat.py +0 -256
  356. package/skills/research/extract-url/scripts/playwright_xcom.py +0 -587
  357. package/skills/research/extract-url/scripts/validate_article.py +0 -47
  358. package/skills/research/extract-url/tests/__pycache__/conftest.cpython-314-pytest-9.0.2.pyc +0 -0
  359. package/skills/research/extract-url/tests/__pycache__/test_article_utils_meta.cpython-314-pytest-9.0.2.pyc +0 -0
  360. package/skills/research/extract-url/tests/__pycache__/test_article_utils_tags.cpython-314-pytest-9.0.2.pyc +0 -0
  361. package/skills/research/extract-url/tests/__pycache__/test_config.cpython-314-pytest-9.0.2.pyc +0 -0
  362. package/skills/research/extract-url/tests/__pycache__/test_count_article_stats.cpython-314-pytest-9.0.2.pyc +0 -0
  363. package/skills/research/extract-url/tests/__pycache__/test_dedup_check.cpython-314-pytest-9.0.2.pyc +0 -0
  364. package/skills/research/extract-url/tests/__pycache__/test_migrate_to_folder_structure.cpython-314-pytest-9.0.2.pyc +0 -0
  365. package/skills/research/extract-url/tests/__pycache__/test_playwright_web.cpython-314-pytest-9.0.2.pyc +0 -0
  366. package/skills/research/extract-url/tests/__pycache__/test_playwright_web_arxiv.cpython-314-pytest-9.0.2.pyc +0 -0
  367. package/skills/research/extract-url/tests/__pycache__/test_playwright_web_wechat.cpython-314-pytest-9.0.2.pyc +0 -0
  368. package/skills/research/extract-url/tests/__pycache__/test_playwright_xcom.cpython-314-pytest-9.0.2.pyc +0 -0
  369. package/skills/research/extract-url/tests/__pycache__/test_subagent1_prompt.cpython-314-pytest-9.0.2.pyc +0 -0
  370. package/skills/research/extract-url/tests/__pycache__/test_subagent2_prompt.cpython-314-pytest-9.0.2.pyc +0 -0
  371. package/skills/research/extract-url/tests/__pycache__/test_validate_article.cpython-314-pytest-9.0.2.pyc +0 -0
  372. package/skills/research/extract-url/tests/conftest.py +0 -74
  373. package/skills/research/extract-url/tests/test_article_utils_meta.py +0 -84
  374. package/skills/research/extract-url/tests/test_article_utils_tags.py +0 -139
  375. package/skills/research/extract-url/tests/test_config.py +0 -116
  376. package/skills/research/extract-url/tests/test_count_article_stats.py +0 -91
  377. package/skills/research/extract-url/tests/test_dedup_check.py +0 -81
  378. package/skills/research/extract-url/tests/test_migrate_to_folder_structure.py +0 -492
  379. package/skills/research/extract-url/tests/test_playwright_web.py +0 -159
  380. package/skills/research/extract-url/tests/test_playwright_web_arxiv.py +0 -157
  381. package/skills/research/extract-url/tests/test_playwright_web_wechat.py +0 -191
  382. package/skills/research/extract-url/tests/test_playwright_xcom.py +0 -203
  383. package/skills/research/extract-url/tests/test_subagent1_prompt.py +0 -15
  384. package/skills/research/extract-url/tests/test_subagent2_prompt.py +0 -22
  385. package/skills/research/extract-url/tests/test_validate_article.py +0 -241
  386. package/skills/research/extract-url/vars.json +0 -15
  387. package/tools/browser-fetch-mcp/browser-fetch-mcp.sh +0 -36
  388. package/tools/browser-fetch-mcp/browser_fetch_mcp/__init__.py +0 -1
  389. package/tools/browser-fetch-mcp/browser_fetch_mcp/__pycache__/__init__.cpython-314.pyc +0 -0
  390. package/tools/browser-fetch-mcp/browser_fetch_mcp/__pycache__/config.cpython-314.pyc +0 -0
  391. package/tools/browser-fetch-mcp/browser_fetch_mcp/__pycache__/server.cpython-314.pyc +0 -0
  392. package/tools/browser-fetch-mcp/tests/__pycache__/test_config.cpython-314-pytest-9.1.1.pyc +0 -0
  393. package/tools/browser-fetch-mcp/tests/__pycache__/test_evaluate_js.cpython-314-pytest-9.1.1.pyc +0 -0
  394. package/tools/browser-fetch-mcp/tests/__pycache__/test_fetch_article.cpython-314-pytest-9.1.1.pyc +0 -0
  395. package/tools/browser-fetch-mcp/tests/__pycache__/test_server.cpython-314-pytest-9.1.1.pyc +0 -0
  396. package/tools/browser-fetch-mcp/tests/test_config.py +0 -25
  397. package/tools/browser-fetch-mcp/tests/test_evaluate_js.py +0 -63
  398. package/tools/browser-fetch-mcp/tests/test_server.py +0 -126
  399. package/tools/browser-fetch-mcp/tool.json +0 -8
  400. /package/tools/{browser-fetch-mcp/browser_fetch_mcp → browser-fetch/browser_fetch}/cookies.py +0 -0
  401. /package/tools/{browser-fetch-mcp/browser_fetch_mcp → browser-fetch/browser_fetch}/images.py +0 -0
  402. /package/tools/{browser-fetch-mcp/browser_fetch_mcp → browser-fetch/browser_fetch}/markdown.py +0 -0
package/CHANGELOG.md CHANGED
@@ -7,6 +7,36 @@ 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
+
26
+ ## [0.28.0] - 2026-08-15
27
+
28
+ ### Added
29
+ - `sync-xtimeline`:新增 skill(原名 watch-x),批量追更一批固定的 X 博主推文,抓取时自动识别推文类型(post/repost/quote/reply),产出翻译摘要 + 累计静态 HTML 时间线视图(X 风格深色主题)
30
+ - `browser-fetch-mcp`:新增 `fetch_user_timeline` MCP 工具,支持抓取 X 用户时间线
31
+ - `fetch-paper`:新增 skill,贡献到 research bundle
32
+
33
+ ### Changed
34
+ - `explain-pm`:输出改为分层,界定技术语言边界,允许调研代码
35
+
36
+ ### Fixed
37
+ - `fetch-paper`:修复 PMC/OA 页面被反爬拦截无法抓取、PDF 二进制内容误用 Write 工具写盘、SKILL.md F3 违规
38
+ - `learn-video`:硬编码用户路径改用 `$HOME` + task_id 读取重试;改为后台执行+日志判定,避免阻塞调用
39
+
10
40
  ## [0.27.0] - 2026-08-13
11
41
 
12
42
  ### Added
package/package.json CHANGED
@@ -1,31 +1,35 @@
1
1
  {
2
2
  "name": "harveyz-skill",
3
- "version": "0.27.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",
11
- "test": "bats tests/ && bash scripts/run-skill-tests.sh && node --test tests/mcp.test.mjs"
12
+ "test": "bats tests/ && bash scripts/run-skill-tests.sh && node --test tests/mcp.test.mjs tests/harness/*.test.mjs"
12
13
  },
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/",
24
25
  "skills/research/learn-paper/",
26
+ "skills/research/fetch-paper/",
25
27
  "skills/research/extract-cognition/",
26
28
  "skills/research/probe-session/",
27
29
  "skills/research/pdf-math-translate/",
28
30
  "skills/research/clip-url/",
31
+ "skills/feed/sync-xtimeline/",
32
+ "skills/feed/sync-ytchannel/",
29
33
  "skills/creative/capture-todo/",
30
34
  "skills/creative/capture-insight/",
31
35
  "skills/coding/init-workflow/",
@@ -55,9 +59,12 @@
55
59
  "skills/meta/sync-hotfix/",
56
60
  "skills/meta/sync-agent/",
57
61
  "skills/coding/handoff/",
62
+ "skills/feed/manage-roster/",
63
+ "skills/feed/capture-creator/",
58
64
  "tools/hub/",
59
65
  "tools/sync-agent/",
60
- "tools/browser-fetch-mcp/",
66
+ "tools/browser-fetch/",
67
+ "tools/roster/",
61
68
  "hooks/check-similar-branch/"
62
69
  ],
63
70
  "engines": {
@@ -1,34 +1,60 @@
1
1
  ---
2
2
  name: explain-pm
3
- description: "Restate a recent technical explanation from a product-manager's analytical lensuser experience, product philosophy, execution mechanics, and architectural healthrather than translating jargon, and flag product-perspective concerns like scope, over-engineering, priority, or drift from user need. Triggers: '/explain-pm', '/explain-pm <topic or file>', 'explain this like a PM', 'restate from a PM perspective'."
3
+ description: "Restate a technical explanation in language a product manager can act on what changes for users, which components are involved, and the call/timing dependencies between them keeping technical terms that carry architecture and dropping pure implementation mechanics. May read related code to fill in facts the original left out. Product-angle observations, if any, go in a separate optional section, never mixed into the restatement. Triggers: '/explain-pm', '/explain-pm <topic or file>', 'explain this like a PM', 'restate from a PM perspective'."
4
4
  user_invocable: true
5
- version: "1.1.1"
5
+ version: "1.2.0"
6
6
  ---
7
7
 
8
8
  # explain-pm — PM 视角复述
9
9
 
10
- 把一段技术性表述从 PM 视角复述:换成 PM 关注的分析维度,再视情况指出值得从产品角度重新考虑的地方。
10
+ 把一段技术性表述复述成 PM(产品经理)能听懂、能据以决策的版本。
11
+
12
+ 输出分两层,边界必须清晰:
13
+
14
+ 1. **核心复述**(必出)——严格不超出原材料的范围。
15
+ 2. **产品角度的延伸**(可选)——有才写,没有就整节不出现。
11
16
 
12
17
  ## 触发
13
18
 
14
19
  仅手动调用:`/explain-pm` 或 `/explain-pm <主题或文件路径>`。不自动检测触发,不主动插话。
15
20
 
16
- ## PM 视角的关注维度
21
+ ## 技术语言的保留边界
22
+
23
+ 不是把技术语言全部剥离——PM 本身熟悉基础技术语言,剥干净反而看不懂全貌。判据是:
24
+
25
+ > PM 依赖的是「什么在什么之后发生、谁依赖谁」,不依赖「这段代码是怎么写的」。
26
+
27
+ **保留**(撑起架构轮廓和调用关系的技术表述):
28
+
29
+ - 组件、服务、模块的名字
30
+ - 调用方向:谁调用谁,同步还是异步
31
+ - 依赖与时序:谁必须先就绪,哪一步是阻塞的
32
+ - 失败传播:某一环挂了,影响面到哪里为止
33
+
34
+ **剥离**(纯实现机制,对理解全貌没有帮助):
35
+
36
+ - 函数签名、参数、返回值类型
37
+ - 数据结构、字段名、序列化格式
38
+ - 算法实现、设计模式名称
39
+ - 具体代码怎么写的、用了哪个库的哪个 API
40
+
41
+ ## 核心复述的组织维度
17
42
 
18
- 不是语言转译——PM 本身熟悉基础技术语言。要转换的是关注点:
43
+ 分析时的参考,不必在回复里逐条列出,也不套固定小标题:
19
44
 
20
- - **用户体验**:这个决定最终会让用户感受到什么变化?
21
- - **产品哲学**:是否符合产品一贯的取舍和原则?
22
- - **执行机制与流程**:具体怎么落地——谁来做、分几步、影响什么协作节奏?
23
- - **架构健康度(次要)**:是否有明显的技术债或长期维护风险,只做提醒,不展开实现细节本身。
45
+ - **用户侧变化**:这件事最终让用户感受到什么?
46
+ - **参与者**:涉及哪些组件/角色,各自负责什么?
47
+ - **调用关系与时序依赖**:谁先谁后、谁依赖谁、哪一环可能卡住?
48
+ - **执行机制与影响面**:谁来做、分几步、影响什么协作节奏、边界在哪?
24
49
 
25
50
  ## 执行
26
51
 
27
52
  1. **取材料**:带参数用参数指定的主题或文件;无参数则取调用前的最后一条 assistant 消息。
28
- 2. **换视角**:对照上面的关注维度重新审视材料——不必在回复里逐条列出,只是分析时的参考。
29
- 3. **提出关注点(视情况)**:判断原表述从 PM 角度是否有值得重新审视的地方,比如是否偏离用户实际需求、范围是否合理、是否过度工程化、优先级是否搞错了。如果原表述本身已经很贴近产品目标,挑不出问题,就只做视角复述,不硬造关注点。关注点如果成立,放在回复最后,不要打断前面的视角复述。
30
- 4. **输出**:在对话中直接回复,简短(几句话量级)。默认不写文件;仅当用户明确要求存档时,才把这段评论写入用户指定的文件。
53
+ 2. **按需调研**:如果原表述没交代清楚 PM 需要知道的调用关系或依赖顺序,去读相关代码/文件把事实补齐。调研只为补核心复述的事实,不用于给延伸小节攒素材。
54
+ 3. **写核心复述**:对照上面的维度和保留边界重述,严格限定在原材料(含第 2 步补齐的事实)范围内,不做评价、不做建议。
55
+ 4. **写延伸(视情况)**:判断是否真有值得从产品角度说的——产品哲学上是否符合一贯取舍、有无明显技术债或长期维护风险、范围是否合理、优先级是否搞错、是否过度工程化。有就单独成节,与核心复述之间保持明确分隔;**挑不出就整节不写,不硬造**。
56
+ 5. **输出**:在对话中直接回复,简短(核心复述几句话量级)。默认不写文件;仅当用户明确要求存档时,才写入用户指定的文件。
31
57
 
32
58
  ## 不做
33
59
 
34
- 多轮追问式澄清、自动检测技术语言并主动触发、默认生成或保存文档、固定的输出小标题模板。
60
+ 多轮追问式澄清、自动检测技术语言并主动触发、默认生成或保存文档、在核心复述里夹带评价或改进建议、为了凑出延伸小节而硬造关注点。
@@ -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()