calvyn-code 0.14.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 (1718) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +217 -0
  3. package/README.zh-CN.md +180 -0
  4. package/acp_adapter/__init__.py +1 -0
  5. package/acp_adapter/__main__.py +5 -0
  6. package/acp_adapter/auth.py +68 -0
  7. package/acp_adapter/bootstrap/__init__.py +0 -0
  8. package/acp_adapter/bootstrap/bootstrap_browser_tools.ps1 +288 -0
  9. package/acp_adapter/bootstrap/bootstrap_browser_tools.sh +399 -0
  10. package/acp_adapter/entry.py +292 -0
  11. package/acp_adapter/events.py +265 -0
  12. package/acp_adapter/permissions.py +148 -0
  13. package/acp_adapter/server.py +1713 -0
  14. package/acp_adapter/session.py +629 -0
  15. package/acp_adapter/tools.py +1180 -0
  16. package/agent/__init__.py +6 -0
  17. package/agent/__pycache__/__init__.cpython-312.pyc +0 -0
  18. package/agent/__pycache__/account_usage.cpython-312.pyc +0 -0
  19. package/agent/__pycache__/anthropic_adapter.cpython-312.pyc +0 -0
  20. package/agent/__pycache__/async_utils.cpython-312.pyc +0 -0
  21. package/agent/__pycache__/auxiliary_client.cpython-312.pyc +0 -0
  22. package/agent/__pycache__/codex_responses_adapter.cpython-312.pyc +0 -0
  23. package/agent/__pycache__/context_compressor.cpython-312.pyc +0 -0
  24. package/agent/__pycache__/context_engine.cpython-312.pyc +0 -0
  25. package/agent/__pycache__/context_references.cpython-312.pyc +0 -0
  26. package/agent/__pycache__/credential_pool.cpython-312.pyc +0 -0
  27. package/agent/__pycache__/curator.cpython-312.pyc +0 -0
  28. package/agent/__pycache__/display.cpython-312.pyc +0 -0
  29. package/agent/__pycache__/error_classifier.cpython-312.pyc +0 -0
  30. package/agent/__pycache__/file_safety.cpython-312.pyc +0 -0
  31. package/agent/__pycache__/google_code_assist.cpython-312.pyc +0 -0
  32. package/agent/__pycache__/google_oauth.cpython-312.pyc +0 -0
  33. package/agent/__pycache__/i18n.cpython-312.pyc +0 -0
  34. package/agent/__pycache__/image_gen_provider.cpython-312.pyc +0 -0
  35. package/agent/__pycache__/image_gen_registry.cpython-312.pyc +0 -0
  36. package/agent/__pycache__/insights.cpython-312.pyc +0 -0
  37. package/agent/__pycache__/lmstudio_reasoning.cpython-312.pyc +0 -0
  38. package/agent/__pycache__/manual_compression_feedback.cpython-312.pyc +0 -0
  39. package/agent/__pycache__/markdown_tables.cpython-312.pyc +0 -0
  40. package/agent/__pycache__/memory_manager.cpython-312.pyc +0 -0
  41. package/agent/__pycache__/memory_provider.cpython-312.pyc +0 -0
  42. package/agent/__pycache__/model_metadata.cpython-312.pyc +0 -0
  43. package/agent/__pycache__/models_dev.cpython-312.pyc +0 -0
  44. package/agent/__pycache__/moonshot_schema.cpython-312.pyc +0 -0
  45. package/agent/__pycache__/onboarding.cpython-312.pyc +0 -0
  46. package/agent/__pycache__/portal_tags.cpython-312.pyc +0 -0
  47. package/agent/__pycache__/prompt_builder.cpython-312.pyc +0 -0
  48. package/agent/__pycache__/prompt_caching.cpython-312.pyc +0 -0
  49. package/agent/__pycache__/redact.cpython-312.pyc +0 -0
  50. package/agent/__pycache__/retry_utils.cpython-312.pyc +0 -0
  51. package/agent/__pycache__/shell_hooks.cpython-312.pyc +0 -0
  52. package/agent/__pycache__/skill_commands.cpython-312.pyc +0 -0
  53. package/agent/__pycache__/skill_preprocessing.cpython-312.pyc +0 -0
  54. package/agent/__pycache__/skill_utils.cpython-312.pyc +0 -0
  55. package/agent/__pycache__/subdirectory_hints.cpython-312.pyc +0 -0
  56. package/agent/__pycache__/think_scrubber.cpython-312.pyc +0 -0
  57. package/agent/__pycache__/title_generator.cpython-312.pyc +0 -0
  58. package/agent/__pycache__/tool_guardrails.cpython-312.pyc +0 -0
  59. package/agent/__pycache__/tool_result_classification.cpython-312.pyc +0 -0
  60. package/agent/__pycache__/trajectory.cpython-312.pyc +0 -0
  61. package/agent/__pycache__/usage_pricing.cpython-312.pyc +0 -0
  62. package/agent/__pycache__/video_gen_provider.cpython-312.pyc +0 -0
  63. package/agent/__pycache__/video_gen_registry.cpython-312.pyc +0 -0
  64. package/agent/__pycache__/web_search_provider.cpython-312.pyc +0 -0
  65. package/agent/__pycache__/web_search_registry.cpython-312.pyc +0 -0
  66. package/agent/account_usage.py +326 -0
  67. package/agent/anthropic_adapter.py +2087 -0
  68. package/agent/async_utils.py +68 -0
  69. package/agent/auxiliary_client.py +4893 -0
  70. package/agent/bedrock_adapter.py +1276 -0
  71. package/agent/codex_responses_adapter.py +1084 -0
  72. package/agent/context_compressor.py +1583 -0
  73. package/agent/context_engine.py +211 -0
  74. package/agent/context_references.py +519 -0
  75. package/agent/copilot_acp_client.py +684 -0
  76. package/agent/credential_pool.py +1780 -0
  77. package/agent/credential_sources.py +449 -0
  78. package/agent/curator.py +1782 -0
  79. package/agent/curator_backup.py +694 -0
  80. package/agent/display.py +987 -0
  81. package/agent/error_classifier.py +1058 -0
  82. package/agent/file_safety.py +112 -0
  83. package/agent/gemini_cloudcode_adapter.py +909 -0
  84. package/agent/gemini_native_adapter.py +971 -0
  85. package/agent/gemini_schema.py +99 -0
  86. package/agent/google_code_assist.py +452 -0
  87. package/agent/google_oauth.py +1062 -0
  88. package/agent/i18n.py +258 -0
  89. package/agent/image_gen_provider.py +243 -0
  90. package/agent/image_gen_registry.py +145 -0
  91. package/agent/image_routing.py +301 -0
  92. package/agent/insights.py +931 -0
  93. package/agent/lmstudio_reasoning.py +48 -0
  94. package/agent/lsp/__init__.py +106 -0
  95. package/agent/lsp/__pycache__/__init__.cpython-312.pyc +0 -0
  96. package/agent/lsp/__pycache__/cli.cpython-312.pyc +0 -0
  97. package/agent/lsp/__pycache__/client.cpython-312.pyc +0 -0
  98. package/agent/lsp/__pycache__/eventlog.cpython-312.pyc +0 -0
  99. package/agent/lsp/__pycache__/manager.cpython-312.pyc +0 -0
  100. package/agent/lsp/__pycache__/protocol.cpython-312.pyc +0 -0
  101. package/agent/lsp/__pycache__/servers.cpython-312.pyc +0 -0
  102. package/agent/lsp/__pycache__/workspace.cpython-312.pyc +0 -0
  103. package/agent/lsp/cli.py +308 -0
  104. package/agent/lsp/client.py +930 -0
  105. package/agent/lsp/eventlog.py +213 -0
  106. package/agent/lsp/install.py +376 -0
  107. package/agent/lsp/manager.py +644 -0
  108. package/agent/lsp/protocol.py +196 -0
  109. package/agent/lsp/range_shift.py +149 -0
  110. package/agent/lsp/reporter.py +78 -0
  111. package/agent/lsp/servers.py +1040 -0
  112. package/agent/lsp/workspace.py +223 -0
  113. package/agent/manual_compression_feedback.py +49 -0
  114. package/agent/markdown_tables.py +309 -0
  115. package/agent/memory_manager.py +556 -0
  116. package/agent/memory_provider.py +279 -0
  117. package/agent/model_metadata.py +1827 -0
  118. package/agent/models_dev.py +724 -0
  119. package/agent/moonshot_schema.py +231 -0
  120. package/agent/nous_rate_guard.py +326 -0
  121. package/agent/onboarding.py +193 -0
  122. package/agent/plugin_llm.py +1046 -0
  123. package/agent/portal_tags.py +64 -0
  124. package/agent/prompt_builder.py +1457 -0
  125. package/agent/prompt_caching.py +79 -0
  126. package/agent/rate_limit_tracker.py +246 -0
  127. package/agent/redact.py +403 -0
  128. package/agent/retry_utils.py +57 -0
  129. package/agent/shell_hooks.py +837 -0
  130. package/agent/skill_commands.py +502 -0
  131. package/agent/skill_preprocessing.py +131 -0
  132. package/agent/skill_utils.py +512 -0
  133. package/agent/subdirectory_hints.py +224 -0
  134. package/agent/think_scrubber.py +386 -0
  135. package/agent/title_generator.py +171 -0
  136. package/agent/tool_guardrails.py +458 -0
  137. package/agent/tool_result_classification.py +26 -0
  138. package/agent/trajectory.py +56 -0
  139. package/agent/transports/__init__.py +68 -0
  140. package/agent/transports/__pycache__/__init__.cpython-312.pyc +0 -0
  141. package/agent/transports/__pycache__/anthropic.cpython-312.pyc +0 -0
  142. package/agent/transports/__pycache__/base.cpython-312.pyc +0 -0
  143. package/agent/transports/__pycache__/bedrock.cpython-312.pyc +0 -0
  144. package/agent/transports/__pycache__/chat_completions.cpython-312.pyc +0 -0
  145. package/agent/transports/__pycache__/codex.cpython-312.pyc +0 -0
  146. package/agent/transports/__pycache__/types.cpython-312.pyc +0 -0
  147. package/agent/transports/anthropic.py +179 -0
  148. package/agent/transports/base.py +89 -0
  149. package/agent/transports/bedrock.py +154 -0
  150. package/agent/transports/chat_completions.py +614 -0
  151. package/agent/transports/codex.py +283 -0
  152. package/agent/transports/codex_app_server.py +368 -0
  153. package/agent/transports/codex_app_server_session.py +810 -0
  154. package/agent/transports/codex_event_projector.py +312 -0
  155. package/agent/transports/hermes_tools_mcp_server.py +233 -0
  156. package/agent/transports/types.py +162 -0
  157. package/agent/usage_pricing.py +877 -0
  158. package/agent/video_gen_provider.py +300 -0
  159. package/agent/video_gen_registry.py +117 -0
  160. package/agent/web_search_provider.py +221 -0
  161. package/agent/web_search_registry.py +262 -0
  162. package/assets/banner.png +0 -0
  163. package/batch_runner.py +1303 -0
  164. package/bin/calvyn.js +67 -0
  165. package/calvyn_bootstrap.py +130 -0
  166. package/calvyn_constants.py +346 -0
  167. package/calvyn_logging.py +390 -0
  168. package/calvyn_state.py +2967 -0
  169. package/calvyn_time.py +105 -0
  170. package/cli.py +14160 -0
  171. package/cron/__init__.py +42 -0
  172. package/cron/__pycache__/__init__.cpython-312.pyc +0 -0
  173. package/cron/__pycache__/jobs.cpython-312.pyc +0 -0
  174. package/cron/__pycache__/scheduler.cpython-312.pyc +0 -0
  175. package/cron/jobs.py +1160 -0
  176. package/cron/scheduler.py +1832 -0
  177. package/gateway/__init__.py +35 -0
  178. package/gateway/__pycache__/__init__.cpython-312.pyc +0 -0
  179. package/gateway/__pycache__/channel_directory.cpython-312.pyc +0 -0
  180. package/gateway/__pycache__/config.cpython-312.pyc +0 -0
  181. package/gateway/__pycache__/delivery.cpython-312.pyc +0 -0
  182. package/gateway/__pycache__/display_config.cpython-312.pyc +0 -0
  183. package/gateway/__pycache__/hooks.cpython-312.pyc +0 -0
  184. package/gateway/__pycache__/pairing.cpython-312.pyc +0 -0
  185. package/gateway/__pycache__/platform_registry.cpython-312.pyc +0 -0
  186. package/gateway/__pycache__/restart.cpython-312.pyc +0 -0
  187. package/gateway/__pycache__/run.cpython-312.pyc +0 -0
  188. package/gateway/__pycache__/runtime_footer.cpython-312.pyc +0 -0
  189. package/gateway/__pycache__/session.cpython-312.pyc +0 -0
  190. package/gateway/__pycache__/session_context.cpython-312.pyc +0 -0
  191. package/gateway/__pycache__/shutdown_forensics.cpython-312.pyc +0 -0
  192. package/gateway/__pycache__/slash_access.cpython-312.pyc +0 -0
  193. package/gateway/__pycache__/status.cpython-312.pyc +0 -0
  194. package/gateway/__pycache__/stream_consumer.cpython-312.pyc +0 -0
  195. package/gateway/__pycache__/whatsapp_identity.cpython-312.pyc +0 -0
  196. package/gateway/assets/telegram-botfather-threads-settings.jpg +0 -0
  197. package/gateway/builtin_hooks/__init__.py +1 -0
  198. package/gateway/channel_directory.py +357 -0
  199. package/gateway/config.py +1873 -0
  200. package/gateway/delivery.py +258 -0
  201. package/gateway/display_config.py +206 -0
  202. package/gateway/hooks.py +210 -0
  203. package/gateway/mirror.py +179 -0
  204. package/gateway/pairing.py +322 -0
  205. package/gateway/platform_registry.py +260 -0
  206. package/gateway/platforms/ADDING_A_PLATFORM.md +374 -0
  207. package/gateway/platforms/__init__.py +45 -0
  208. package/gateway/platforms/__pycache__/__init__.cpython-312.pyc +0 -0
  209. package/gateway/platforms/__pycache__/base.cpython-312.pyc +0 -0
  210. package/gateway/platforms/__pycache__/helpers.cpython-312.pyc +0 -0
  211. package/gateway/platforms/__pycache__/telegram.cpython-312.pyc +0 -0
  212. package/gateway/platforms/__pycache__/telegram_network.cpython-312.pyc +0 -0
  213. package/gateway/platforms/__pycache__/yuanbao.cpython-312.pyc +0 -0
  214. package/gateway/platforms/__pycache__/yuanbao_media.cpython-312.pyc +0 -0
  215. package/gateway/platforms/__pycache__/yuanbao_proto.cpython-312.pyc +0 -0
  216. package/gateway/platforms/_http_client_limits.py +84 -0
  217. package/gateway/platforms/api_server.py +3488 -0
  218. package/gateway/platforms/base.py +3747 -0
  219. package/gateway/platforms/bluebubbles.py +937 -0
  220. package/gateway/platforms/dingtalk.py +1473 -0
  221. package/gateway/platforms/discord.py +5584 -0
  222. package/gateway/platforms/email.py +773 -0
  223. package/gateway/platforms/feishu.py +5059 -0
  224. package/gateway/platforms/feishu_comment.py +1382 -0
  225. package/gateway/platforms/feishu_comment_rules.py +430 -0
  226. package/gateway/platforms/helpers.py +279 -0
  227. package/gateway/platforms/homeassistant.py +449 -0
  228. package/gateway/platforms/matrix.py +2777 -0
  229. package/gateway/platforms/mattermost.py +852 -0
  230. package/gateway/platforms/msgraph_webhook.py +397 -0
  231. package/gateway/platforms/qqbot/__init__.py +91 -0
  232. package/gateway/platforms/qqbot/adapter.py +3072 -0
  233. package/gateway/platforms/qqbot/chunked_upload.py +602 -0
  234. package/gateway/platforms/qqbot/constants.py +74 -0
  235. package/gateway/platforms/qqbot/crypto.py +45 -0
  236. package/gateway/platforms/qqbot/keyboards.py +473 -0
  237. package/gateway/platforms/qqbot/onboard.py +220 -0
  238. package/gateway/platforms/qqbot/utils.py +71 -0
  239. package/gateway/platforms/signal.py +1518 -0
  240. package/gateway/platforms/signal_rate_limit.py +369 -0
  241. package/gateway/platforms/slack.py +3028 -0
  242. package/gateway/platforms/sms.py +377 -0
  243. package/gateway/platforms/telegram.py +4836 -0
  244. package/gateway/platforms/telegram_network.py +249 -0
  245. package/gateway/platforms/webhook.py +806 -0
  246. package/gateway/platforms/wecom.py +1610 -0
  247. package/gateway/platforms/wecom_callback.py +403 -0
  248. package/gateway/platforms/wecom_crypto.py +142 -0
  249. package/gateway/platforms/weixin.py +2170 -0
  250. package/gateway/platforms/whatsapp.py +1283 -0
  251. package/gateway/platforms/yuanbao.py +4873 -0
  252. package/gateway/platforms/yuanbao_media.py +645 -0
  253. package/gateway/platforms/yuanbao_proto.py +1209 -0
  254. package/gateway/platforms/yuanbao_sticker.py +558 -0
  255. package/gateway/restart.py +20 -0
  256. package/gateway/run.py +17074 -0
  257. package/gateway/runtime_footer.py +150 -0
  258. package/gateway/session.py +1399 -0
  259. package/gateway/session_context.py +156 -0
  260. package/gateway/shutdown_forensics.py +462 -0
  261. package/gateway/slash_access.py +229 -0
  262. package/gateway/status.py +972 -0
  263. package/gateway/sticker_cache.py +111 -0
  264. package/gateway/stream_consumer.py +1286 -0
  265. package/gateway/whatsapp_identity.py +156 -0
  266. package/hermes_cli/__init__.py +47 -0
  267. package/hermes_cli/__pycache__/__init__.cpython-312.pyc +0 -0
  268. package/hermes_cli/__pycache__/_parser.cpython-312.pyc +0 -0
  269. package/hermes_cli/__pycache__/auth.cpython-312.pyc +0 -0
  270. package/hermes_cli/__pycache__/banner.cpython-312.pyc +0 -0
  271. package/hermes_cli/__pycache__/browser_connect.cpython-312.pyc +0 -0
  272. package/hermes_cli/__pycache__/callbacks.cpython-312.pyc +0 -0
  273. package/hermes_cli/__pycache__/checkpoints.cpython-312.pyc +0 -0
  274. package/hermes_cli/__pycache__/cli_output.cpython-312.pyc +0 -0
  275. package/hermes_cli/__pycache__/codex_models.cpython-312.pyc +0 -0
  276. package/hermes_cli/__pycache__/codex_runtime_switch.cpython-312.pyc +0 -0
  277. package/hermes_cli/__pycache__/colors.cpython-312.pyc +0 -0
  278. package/hermes_cli/__pycache__/commands.cpython-312.pyc +0 -0
  279. package/hermes_cli/__pycache__/config.cpython-312.pyc +0 -0
  280. package/hermes_cli/__pycache__/copilot_auth.cpython-312.pyc +0 -0
  281. package/hermes_cli/__pycache__/curator.cpython-312.pyc +0 -0
  282. package/hermes_cli/__pycache__/curses_ui.cpython-312.pyc +0 -0
  283. package/hermes_cli/__pycache__/debug.cpython-312.pyc +0 -0
  284. package/hermes_cli/__pycache__/default_soul.cpython-312.pyc +0 -0
  285. package/hermes_cli/__pycache__/env_loader.cpython-312.pyc +0 -0
  286. package/hermes_cli/__pycache__/fallback_cmd.cpython-312.pyc +0 -0
  287. package/hermes_cli/__pycache__/gateway.cpython-312.pyc +0 -0
  288. package/hermes_cli/__pycache__/gateway_windows.cpython-312.pyc +0 -0
  289. package/hermes_cli/__pycache__/goals.cpython-312.pyc +0 -0
  290. package/hermes_cli/__pycache__/inventory.cpython-312.pyc +0 -0
  291. package/hermes_cli/__pycache__/kanban.cpython-312.pyc +0 -0
  292. package/hermes_cli/__pycache__/kanban_db.cpython-312.pyc +0 -0
  293. package/hermes_cli/__pycache__/main.cpython-312.pyc +0 -0
  294. package/hermes_cli/__pycache__/model_catalog.cpython-312.pyc +0 -0
  295. package/hermes_cli/__pycache__/model_normalize.cpython-312.pyc +0 -0
  296. package/hermes_cli/__pycache__/model_switch.cpython-312.pyc +0 -0
  297. package/hermes_cli/__pycache__/models.cpython-312.pyc +0 -0
  298. package/hermes_cli/__pycache__/nous_subscription.cpython-312.pyc +0 -0
  299. package/hermes_cli/__pycache__/pairing.cpython-312.pyc +0 -0
  300. package/hermes_cli/__pycache__/platforms.cpython-312.pyc +0 -0
  301. package/hermes_cli/__pycache__/plugins.cpython-312.pyc +0 -0
  302. package/hermes_cli/__pycache__/profiles.cpython-312.pyc +0 -0
  303. package/hermes_cli/__pycache__/providers.cpython-312.pyc +0 -0
  304. package/hermes_cli/__pycache__/pt_input_extras.cpython-312.pyc +0 -0
  305. package/hermes_cli/__pycache__/runtime_provider.cpython-312.pyc +0 -0
  306. package/hermes_cli/__pycache__/security_advisories.cpython-312.pyc +0 -0
  307. package/hermes_cli/__pycache__/setup.cpython-312.pyc +0 -0
  308. package/hermes_cli/__pycache__/skills_hub.cpython-312.pyc +0 -0
  309. package/hermes_cli/__pycache__/skin_engine.cpython-312.pyc +0 -0
  310. package/hermes_cli/__pycache__/stdio.cpython-312.pyc +0 -0
  311. package/hermes_cli/__pycache__/timeouts.cpython-312.pyc +0 -0
  312. package/hermes_cli/__pycache__/tips.cpython-312.pyc +0 -0
  313. package/hermes_cli/__pycache__/tools_config.cpython-312.pyc +0 -0
  314. package/hermes_cli/__pycache__/voice.cpython-312.pyc +0 -0
  315. package/hermes_cli/_parser.py +365 -0
  316. package/hermes_cli/_subprocess_compat.py +175 -0
  317. package/hermes_cli/auth.py +6299 -0
  318. package/hermes_cli/auth_commands.py +749 -0
  319. package/hermes_cli/azure_detect.py +300 -0
  320. package/hermes_cli/backup.py +938 -0
  321. package/hermes_cli/banner.py +703 -0
  322. package/hermes_cli/browser_connect.py +139 -0
  323. package/hermes_cli/callbacks.py +243 -0
  324. package/hermes_cli/checkpoints.py +244 -0
  325. package/hermes_cli/claw.py +810 -0
  326. package/hermes_cli/cli_output.py +78 -0
  327. package/hermes_cli/clipboard.py +495 -0
  328. package/hermes_cli/codex_models.py +198 -0
  329. package/hermes_cli/codex_runtime_plugin_migration.py +757 -0
  330. package/hermes_cli/codex_runtime_switch.py +266 -0
  331. package/hermes_cli/colors.py +38 -0
  332. package/hermes_cli/commands.py +1728 -0
  333. package/hermes_cli/completion.py +315 -0
  334. package/hermes_cli/config.py +5382 -0
  335. package/hermes_cli/copilot_auth.py +392 -0
  336. package/hermes_cli/cron.py +313 -0
  337. package/hermes_cli/curator.py +598 -0
  338. package/hermes_cli/curses_ui.py +472 -0
  339. package/hermes_cli/debug.py +747 -0
  340. package/hermes_cli/default_soul.py +11 -0
  341. package/hermes_cli/dep_ensure.py +107 -0
  342. package/hermes_cli/dingtalk_auth.py +293 -0
  343. package/hermes_cli/doctor.py +1863 -0
  344. package/hermes_cli/dump.py +326 -0
  345. package/hermes_cli/env_loader.py +175 -0
  346. package/hermes_cli/fallback_cmd.py +361 -0
  347. package/hermes_cli/gateway.py +5422 -0
  348. package/hermes_cli/gateway_windows.py +692 -0
  349. package/hermes_cli/goals.py +757 -0
  350. package/hermes_cli/hooks.py +385 -0
  351. package/hermes_cli/inventory.py +240 -0
  352. package/hermes_cli/kanban.py +2252 -0
  353. package/hermes_cli/kanban_db.py +4840 -0
  354. package/hermes_cli/kanban_diagnostics.py +776 -0
  355. package/hermes_cli/kanban_specify.py +266 -0
  356. package/hermes_cli/logs.py +391 -0
  357. package/hermes_cli/main.py +12396 -0
  358. package/hermes_cli/mcp_config.py +781 -0
  359. package/hermes_cli/memory_setup.py +465 -0
  360. package/hermes_cli/model_catalog.py +330 -0
  361. package/hermes_cli/model_normalize.py +473 -0
  362. package/hermes_cli/model_switch.py +1777 -0
  363. package/hermes_cli/models.py +3789 -0
  364. package/hermes_cli/nous_subscription.py +799 -0
  365. package/hermes_cli/oneshot.py +351 -0
  366. package/hermes_cli/pairing.py +115 -0
  367. package/hermes_cli/platforms.py +83 -0
  368. package/hermes_cli/plugins.py +1562 -0
  369. package/hermes_cli/plugins_cmd.py +1587 -0
  370. package/hermes_cli/profile_distribution.py +703 -0
  371. package/hermes_cli/profiles.py +1319 -0
  372. package/hermes_cli/providers.py +720 -0
  373. package/hermes_cli/proxy/__init__.py +20 -0
  374. package/hermes_cli/proxy/adapters/__init__.py +35 -0
  375. package/hermes_cli/proxy/adapters/base.py +94 -0
  376. package/hermes_cli/proxy/adapters/nous_portal.py +137 -0
  377. package/hermes_cli/proxy/cli.py +141 -0
  378. package/hermes_cli/proxy/server.py +265 -0
  379. package/hermes_cli/pt_input_extras.py +83 -0
  380. package/hermes_cli/pty_bridge.py +237 -0
  381. package/hermes_cli/relaunch.py +205 -0
  382. package/hermes_cli/runtime_provider.py +1428 -0
  383. package/hermes_cli/security_advisories.py +452 -0
  384. package/hermes_cli/setup.py +3559 -0
  385. package/hermes_cli/skills_config.py +177 -0
  386. package/hermes_cli/skills_hub.py +1595 -0
  387. package/hermes_cli/skin_engine.py +929 -0
  388. package/hermes_cli/slack_cli.py +160 -0
  389. package/hermes_cli/status.py +550 -0
  390. package/hermes_cli/stdio.py +252 -0
  391. package/hermes_cli/timeouts.py +82 -0
  392. package/hermes_cli/tips.py +487 -0
  393. package/hermes_cli/tools_config.py +3151 -0
  394. package/hermes_cli/uninstall.py +681 -0
  395. package/hermes_cli/vercel_auth.py +70 -0
  396. package/hermes_cli/voice.py +846 -0
  397. package/hermes_cli/web_server.py +4438 -0
  398. package/hermes_cli/webhook.py +275 -0
  399. package/locales/af.yaml +350 -0
  400. package/locales/de.yaml +350 -0
  401. package/locales/en.yaml +365 -0
  402. package/locales/es.yaml +350 -0
  403. package/locales/fr.yaml +350 -0
  404. package/locales/ga.yaml +354 -0
  405. package/locales/hu.yaml +350 -0
  406. package/locales/it.yaml +350 -0
  407. package/locales/ja.yaml +350 -0
  408. package/locales/ko.yaml +350 -0
  409. package/locales/pt.yaml +350 -0
  410. package/locales/ru.yaml +350 -0
  411. package/locales/tr.yaml +350 -0
  412. package/locales/uk.yaml +350 -0
  413. package/locales/zh-hant.yaml +350 -0
  414. package/locales/zh.yaml +350 -0
  415. package/mcp_serve.py +898 -0
  416. package/model_tools.py +899 -0
  417. package/optional-skills/DESCRIPTION.md +24 -0
  418. package/optional-skills/autonomous-ai-agents/DESCRIPTION.md +2 -0
  419. package/optional-skills/autonomous-ai-agents/blackbox/SKILL.md +144 -0
  420. package/optional-skills/autonomous-ai-agents/honcho/SKILL.md +431 -0
  421. package/optional-skills/blockchain/evm/SKILL.md +211 -0
  422. package/optional-skills/blockchain/evm/scripts/evm_client.py +1508 -0
  423. package/optional-skills/blockchain/hyperliquid/SKILL.md +211 -0
  424. package/optional-skills/blockchain/hyperliquid/scripts/hyperliquid_client.py +1660 -0
  425. package/optional-skills/blockchain/solana/SKILL.md +208 -0
  426. package/optional-skills/blockchain/solana/scripts/solana_client.py +698 -0
  427. package/optional-skills/communication/DESCRIPTION.md +1 -0
  428. package/optional-skills/communication/one-three-one-rule/SKILL.md +104 -0
  429. package/optional-skills/creative/blender-mcp/SKILL.md +117 -0
  430. package/optional-skills/creative/concept-diagrams/SKILL.md +362 -0
  431. package/optional-skills/creative/concept-diagrams/examples/apartment-floor-plan-conversion.md +244 -0
  432. package/optional-skills/creative/concept-diagrams/examples/automated-password-reset-flow.md +276 -0
  433. package/optional-skills/creative/concept-diagrams/examples/autonomous-llm-research-agent-flow.md +240 -0
  434. package/optional-skills/creative/concept-diagrams/examples/banana-journey-tree-to-smoothie.md +161 -0
  435. package/optional-skills/creative/concept-diagrams/examples/commercial-aircraft-structure.md +209 -0
  436. package/optional-skills/creative/concept-diagrams/examples/cpu-ooo-microarchitecture.md +236 -0
  437. package/optional-skills/creative/concept-diagrams/examples/electricity-grid-flow.md +182 -0
  438. package/optional-skills/creative/concept-diagrams/examples/feature-film-production-pipeline.md +172 -0
  439. package/optional-skills/creative/concept-diagrams/examples/hospital-emergency-department-flow.md +165 -0
  440. package/optional-skills/creative/concept-diagrams/examples/ml-benchmark-grouped-bar-chart.md +114 -0
  441. package/optional-skills/creative/concept-diagrams/examples/place-order-uml-sequence.md +325 -0
  442. package/optional-skills/creative/concept-diagrams/examples/smart-city-infrastructure.md +173 -0
  443. package/optional-skills/creative/concept-diagrams/examples/smartphone-layer-anatomy.md +154 -0
  444. package/optional-skills/creative/concept-diagrams/examples/sn2-reaction-mechanism.md +247 -0
  445. package/optional-skills/creative/concept-diagrams/examples/wind-turbine-structure.md +338 -0
  446. package/optional-skills/creative/concept-diagrams/references/dashboard-patterns.md +43 -0
  447. package/optional-skills/creative/concept-diagrams/references/infrastructure-patterns.md +144 -0
  448. package/optional-skills/creative/concept-diagrams/references/physical-shape-cookbook.md +42 -0
  449. package/optional-skills/creative/concept-diagrams/templates/template.html +174 -0
  450. package/optional-skills/creative/hyperframes/SKILL.md +191 -0
  451. package/optional-skills/creative/hyperframes/references/cli.md +185 -0
  452. package/optional-skills/creative/hyperframes/references/composition.md +129 -0
  453. package/optional-skills/creative/hyperframes/references/features.md +289 -0
  454. package/optional-skills/creative/hyperframes/references/gsap.md +136 -0
  455. package/optional-skills/creative/hyperframes/references/troubleshooting.md +137 -0
  456. package/optional-skills/creative/hyperframes/references/website-to-video.md +145 -0
  457. package/optional-skills/creative/hyperframes/scripts/setup.sh +135 -0
  458. package/optional-skills/creative/kanban-video-orchestrator/SKILL.md +207 -0
  459. package/optional-skills/creative/kanban-video-orchestrator/assets/brief.md.tmpl +79 -0
  460. package/optional-skills/creative/kanban-video-orchestrator/assets/setup.sh.tmpl +185 -0
  461. package/optional-skills/creative/kanban-video-orchestrator/assets/soul.md.tmpl +38 -0
  462. package/optional-skills/creative/kanban-video-orchestrator/references/examples.md +227 -0
  463. package/optional-skills/creative/kanban-video-orchestrator/references/intake.md +166 -0
  464. package/optional-skills/creative/kanban-video-orchestrator/references/kanban-setup.md +276 -0
  465. package/optional-skills/creative/kanban-video-orchestrator/references/monitoring.md +180 -0
  466. package/optional-skills/creative/kanban-video-orchestrator/references/role-archetypes.md +298 -0
  467. package/optional-skills/creative/kanban-video-orchestrator/references/tool-matrix.md +317 -0
  468. package/optional-skills/creative/kanban-video-orchestrator/scripts/bootstrap_pipeline.py +501 -0
  469. package/optional-skills/creative/kanban-video-orchestrator/scripts/monitor.py +195 -0
  470. package/optional-skills/creative/meme-generation/EXAMPLES.md +46 -0
  471. package/optional-skills/creative/meme-generation/SKILL.md +130 -0
  472. package/optional-skills/creative/meme-generation/scripts/generate_meme.py +471 -0
  473. package/optional-skills/creative/meme-generation/scripts/templates.json +97 -0
  474. package/optional-skills/devops/cli/SKILL.md +156 -0
  475. package/optional-skills/devops/cli/references/app-discovery.md +112 -0
  476. package/optional-skills/devops/cli/references/authentication.md +59 -0
  477. package/optional-skills/devops/cli/references/cli-reference.md +104 -0
  478. package/optional-skills/devops/cli/references/running-apps.md +171 -0
  479. package/optional-skills/devops/docker-management/SKILL.md +281 -0
  480. package/optional-skills/devops/pinggy-tunnel/SKILL.md +309 -0
  481. package/optional-skills/devops/watchers/SKILL.md +112 -0
  482. package/optional-skills/devops/watchers/scripts/_watermark.py +148 -0
  483. package/optional-skills/devops/watchers/scripts/watch_github.py +168 -0
  484. package/optional-skills/devops/watchers/scripts/watch_http_json.py +131 -0
  485. package/optional-skills/devops/watchers/scripts/watch_rss.py +121 -0
  486. package/optional-skills/dogfood/DESCRIPTION.md +3 -0
  487. package/optional-skills/dogfood/adversarial-ux-test/SKILL.md +191 -0
  488. package/optional-skills/email/agentmail/SKILL.md +126 -0
  489. package/optional-skills/finance/3-statement-model/SKILL.md +433 -0
  490. package/optional-skills/finance/3-statement-model/references/formatting.md +118 -0
  491. package/optional-skills/finance/3-statement-model/references/formulas.md +292 -0
  492. package/optional-skills/finance/3-statement-model/references/sec-filings.md +125 -0
  493. package/optional-skills/finance/comps-analysis/SKILL.md +662 -0
  494. package/optional-skills/finance/dcf-model/SKILL.md +1270 -0
  495. package/optional-skills/finance/dcf-model/TROUBLESHOOTING.md +40 -0
  496. package/optional-skills/finance/dcf-model/requirements.txt +7 -0
  497. package/optional-skills/finance/dcf-model/scripts/validate_dcf.py +292 -0
  498. package/optional-skills/finance/excel-author/SKILL.md +244 -0
  499. package/optional-skills/finance/excel-author/scripts/recalc.py +88 -0
  500. package/optional-skills/finance/lbo-model/SKILL.md +291 -0
  501. package/optional-skills/finance/merger-model/SKILL.md +144 -0
  502. package/optional-skills/finance/pptx-author/SKILL.md +173 -0
  503. package/optional-skills/finance/stocks/SKILL.md +95 -0
  504. package/optional-skills/finance/stocks/scripts/stocks_client.py +755 -0
  505. package/optional-skills/health/DESCRIPTION.md +1 -0
  506. package/optional-skills/health/fitness-nutrition/SKILL.md +256 -0
  507. package/optional-skills/health/fitness-nutrition/references/FORMULAS.md +100 -0
  508. package/optional-skills/health/fitness-nutrition/scripts/body_calc.py +210 -0
  509. package/optional-skills/health/fitness-nutrition/scripts/nutrition_search.py +86 -0
  510. package/optional-skills/health/neuroskill-bci/SKILL.md +459 -0
  511. package/optional-skills/health/neuroskill-bci/references/api.md +286 -0
  512. package/optional-skills/health/neuroskill-bci/references/metrics.md +220 -0
  513. package/optional-skills/health/neuroskill-bci/references/protocols.md +452 -0
  514. package/optional-skills/mcp/DESCRIPTION.md +3 -0
  515. package/optional-skills/mcp/fastmcp/SKILL.md +300 -0
  516. package/optional-skills/mcp/fastmcp/references/fastmcp-cli.md +110 -0
  517. package/optional-skills/mcp/fastmcp/scripts/scaffold_fastmcp.py +56 -0
  518. package/optional-skills/mcp/fastmcp/templates/api_wrapper.py +54 -0
  519. package/optional-skills/mcp/fastmcp/templates/database_server.py +77 -0
  520. package/optional-skills/mcp/fastmcp/templates/file_processor.py +55 -0
  521. package/optional-skills/mcp/mcporter/SKILL.md +123 -0
  522. package/optional-skills/migration/DESCRIPTION.md +2 -0
  523. package/optional-skills/migration/openclaw-migration/SKILL.md +298 -0
  524. package/optional-skills/migration/openclaw-migration/scripts/openclaw_to_hermes.py +3136 -0
  525. package/optional-skills/mlops/accelerate/SKILL.md +336 -0
  526. package/optional-skills/mlops/accelerate/references/custom-plugins.md +453 -0
  527. package/optional-skills/mlops/accelerate/references/megatron-integration.md +489 -0
  528. package/optional-skills/mlops/accelerate/references/performance.md +525 -0
  529. package/optional-skills/mlops/chroma/SKILL.md +410 -0
  530. package/optional-skills/mlops/chroma/references/integration.md +38 -0
  531. package/optional-skills/mlops/clip/SKILL.md +257 -0
  532. package/optional-skills/mlops/clip/references/applications.md +207 -0
  533. package/optional-skills/mlops/faiss/SKILL.md +225 -0
  534. package/optional-skills/mlops/faiss/references/index_types.md +280 -0
  535. package/optional-skills/mlops/flash-attention/SKILL.md +367 -0
  536. package/optional-skills/mlops/flash-attention/references/benchmarks.md +215 -0
  537. package/optional-skills/mlops/flash-attention/references/transformers-integration.md +293 -0
  538. package/optional-skills/mlops/guidance/SKILL.md +576 -0
  539. package/optional-skills/mlops/guidance/references/backends.md +554 -0
  540. package/optional-skills/mlops/guidance/references/constraints.md +674 -0
  541. package/optional-skills/mlops/guidance/references/examples.md +767 -0
  542. package/optional-skills/mlops/huggingface-tokenizers/SKILL.md +520 -0
  543. package/optional-skills/mlops/huggingface-tokenizers/references/algorithms.md +653 -0
  544. package/optional-skills/mlops/huggingface-tokenizers/references/integration.md +637 -0
  545. package/optional-skills/mlops/huggingface-tokenizers/references/pipeline.md +723 -0
  546. package/optional-skills/mlops/huggingface-tokenizers/references/training.md +565 -0
  547. package/optional-skills/mlops/inference/outlines/SKILL.md +656 -0
  548. package/optional-skills/mlops/inference/outlines/references/backends.md +615 -0
  549. package/optional-skills/mlops/inference/outlines/references/examples.md +773 -0
  550. package/optional-skills/mlops/inference/outlines/references/json_generation.md +652 -0
  551. package/optional-skills/mlops/instructor/SKILL.md +744 -0
  552. package/optional-skills/mlops/instructor/references/examples.md +107 -0
  553. package/optional-skills/mlops/instructor/references/providers.md +70 -0
  554. package/optional-skills/mlops/instructor/references/validation.md +606 -0
  555. package/optional-skills/mlops/lambda-labs/SKILL.md +549 -0
  556. package/optional-skills/mlops/lambda-labs/references/advanced-usage.md +611 -0
  557. package/optional-skills/mlops/lambda-labs/references/troubleshooting.md +530 -0
  558. package/optional-skills/mlops/llava/SKILL.md +308 -0
  559. package/optional-skills/mlops/llava/references/training.md +197 -0
  560. package/optional-skills/mlops/modal/SKILL.md +345 -0
  561. package/optional-skills/mlops/modal/references/advanced-usage.md +503 -0
  562. package/optional-skills/mlops/modal/references/troubleshooting.md +494 -0
  563. package/optional-skills/mlops/nemo-curator/SKILL.md +387 -0
  564. package/optional-skills/mlops/nemo-curator/references/deduplication.md +87 -0
  565. package/optional-skills/mlops/nemo-curator/references/filtering.md +102 -0
  566. package/optional-skills/mlops/peft/SKILL.md +435 -0
  567. package/optional-skills/mlops/peft/references/advanced-usage.md +514 -0
  568. package/optional-skills/mlops/peft/references/troubleshooting.md +480 -0
  569. package/optional-skills/mlops/pinecone/SKILL.md +362 -0
  570. package/optional-skills/mlops/pinecone/references/deployment.md +181 -0
  571. package/optional-skills/mlops/pytorch-fsdp/SKILL.md +130 -0
  572. package/optional-skills/mlops/pytorch-fsdp/references/index.md +7 -0
  573. package/optional-skills/mlops/pytorch-fsdp/references/other.md +4261 -0
  574. package/optional-skills/mlops/pytorch-lightning/SKILL.md +350 -0
  575. package/optional-skills/mlops/pytorch-lightning/references/callbacks.md +436 -0
  576. package/optional-skills/mlops/pytorch-lightning/references/distributed.md +490 -0
  577. package/optional-skills/mlops/pytorch-lightning/references/hyperparameter-tuning.md +556 -0
  578. package/optional-skills/mlops/qdrant/SKILL.md +497 -0
  579. package/optional-skills/mlops/qdrant/references/advanced-usage.md +648 -0
  580. package/optional-skills/mlops/qdrant/references/troubleshooting.md +631 -0
  581. package/optional-skills/mlops/saelens/SKILL.md +390 -0
  582. package/optional-skills/mlops/saelens/references/README.md +69 -0
  583. package/optional-skills/mlops/saelens/references/api.md +333 -0
  584. package/optional-skills/mlops/saelens/references/tutorials.md +318 -0
  585. package/optional-skills/mlops/simpo/SKILL.md +223 -0
  586. package/optional-skills/mlops/simpo/references/datasets.md +478 -0
  587. package/optional-skills/mlops/simpo/references/hyperparameters.md +452 -0
  588. package/optional-skills/mlops/simpo/references/loss-functions.md +350 -0
  589. package/optional-skills/mlops/slime/SKILL.md +468 -0
  590. package/optional-skills/mlops/slime/references/api-reference.md +392 -0
  591. package/optional-skills/mlops/slime/references/troubleshooting.md +386 -0
  592. package/optional-skills/mlops/stable-diffusion/SKILL.md +523 -0
  593. package/optional-skills/mlops/stable-diffusion/references/advanced-usage.md +716 -0
  594. package/optional-skills/mlops/stable-diffusion/references/troubleshooting.md +555 -0
  595. package/optional-skills/mlops/tensorrt-llm/SKILL.md +191 -0
  596. package/optional-skills/mlops/tensorrt-llm/references/multi-gpu.md +298 -0
  597. package/optional-skills/mlops/tensorrt-llm/references/optimization.md +242 -0
  598. package/optional-skills/mlops/tensorrt-llm/references/serving.md +470 -0
  599. package/optional-skills/mlops/torchtitan/SKILL.md +362 -0
  600. package/optional-skills/mlops/torchtitan/references/checkpoint.md +181 -0
  601. package/optional-skills/mlops/torchtitan/references/custom-models.md +258 -0
  602. package/optional-skills/mlops/torchtitan/references/float8.md +133 -0
  603. package/optional-skills/mlops/torchtitan/references/fsdp.md +126 -0
  604. package/optional-skills/mlops/training/axolotl/SKILL.md +166 -0
  605. package/optional-skills/mlops/training/axolotl/references/api.md +5548 -0
  606. package/optional-skills/mlops/training/axolotl/references/dataset-formats.md +1029 -0
  607. package/optional-skills/mlops/training/axolotl/references/index.md +15 -0
  608. package/optional-skills/mlops/training/axolotl/references/other.md +3563 -0
  609. package/optional-skills/mlops/training/trl-fine-tuning/SKILL.md +463 -0
  610. package/optional-skills/mlops/training/trl-fine-tuning/references/dpo-variants.md +227 -0
  611. package/optional-skills/mlops/training/trl-fine-tuning/references/grpo-training.md +504 -0
  612. package/optional-skills/mlops/training/trl-fine-tuning/references/online-rl.md +82 -0
  613. package/optional-skills/mlops/training/trl-fine-tuning/references/reward-modeling.md +122 -0
  614. package/optional-skills/mlops/training/trl-fine-tuning/references/sft-training.md +168 -0
  615. package/optional-skills/mlops/training/trl-fine-tuning/templates/basic_grpo_training.py +228 -0
  616. package/optional-skills/mlops/training/unsloth/SKILL.md +84 -0
  617. package/optional-skills/mlops/training/unsloth/references/index.md +7 -0
  618. package/optional-skills/mlops/training/unsloth/references/llms-full.md +16799 -0
  619. package/optional-skills/mlops/training/unsloth/references/llms-txt.md +12044 -0
  620. package/optional-skills/mlops/training/unsloth/references/llms.md +82 -0
  621. package/optional-skills/mlops/whisper/SKILL.md +321 -0
  622. package/optional-skills/mlops/whisper/references/languages.md +189 -0
  623. package/optional-skills/productivity/canvas/SKILL.md +98 -0
  624. package/optional-skills/productivity/canvas/scripts/canvas_api.py +157 -0
  625. package/optional-skills/productivity/here-now/SKILL.md +217 -0
  626. package/optional-skills/productivity/here-now/scripts/drive.sh +406 -0
  627. package/optional-skills/productivity/here-now/scripts/publish.sh +445 -0
  628. package/optional-skills/productivity/memento-flashcards/SKILL.md +324 -0
  629. package/optional-skills/productivity/memento-flashcards/scripts/memento_cards.py +353 -0
  630. package/optional-skills/productivity/memento-flashcards/scripts/youtube_quiz.py +88 -0
  631. package/optional-skills/productivity/shop-app/SKILL.md +340 -0
  632. package/optional-skills/productivity/shopify/SKILL.md +373 -0
  633. package/optional-skills/productivity/siyuan/SKILL.md +298 -0
  634. package/optional-skills/productivity/telephony/SKILL.md +418 -0
  635. package/optional-skills/productivity/telephony/scripts/telephony.py +1343 -0
  636. package/optional-skills/research/bioinformatics/SKILL.md +235 -0
  637. package/optional-skills/research/darwinian-evolver/SKILL.md +199 -0
  638. package/optional-skills/research/darwinian-evolver/scripts/parrot_openrouter.py +218 -0
  639. package/optional-skills/research/darwinian-evolver/scripts/show_snapshot.py +69 -0
  640. package/optional-skills/research/darwinian-evolver/templates/custom_problem_template.py +240 -0
  641. package/optional-skills/research/domain-intel/SKILL.md +97 -0
  642. package/optional-skills/research/domain-intel/scripts/domain_intel.py +397 -0
  643. package/optional-skills/research/drug-discovery/SKILL.md +227 -0
  644. package/optional-skills/research/drug-discovery/references/ADMET_REFERENCE.md +66 -0
  645. package/optional-skills/research/drug-discovery/scripts/chembl_target.py +53 -0
  646. package/optional-skills/research/drug-discovery/scripts/ro5_screen.py +44 -0
  647. package/optional-skills/research/duckduckgo-search/SKILL.md +238 -0
  648. package/optional-skills/research/duckduckgo-search/scripts/duckduckgo.sh +28 -0
  649. package/optional-skills/research/gitnexus-explorer/SKILL.md +214 -0
  650. package/optional-skills/research/gitnexus-explorer/scripts/proxy.mjs +92 -0
  651. package/optional-skills/research/osint-investigation/SKILL.md +277 -0
  652. package/optional-skills/research/osint-investigation/references/sources/courtlistener.md +98 -0
  653. package/optional-skills/research/osint-investigation/references/sources/gdelt.md +104 -0
  654. package/optional-skills/research/osint-investigation/references/sources/icij-offshore.md +104 -0
  655. package/optional-skills/research/osint-investigation/references/sources/nyc-acris.md +90 -0
  656. package/optional-skills/research/osint-investigation/references/sources/ofac-sdn.md +92 -0
  657. package/optional-skills/research/osint-investigation/references/sources/opencorporates.md +103 -0
  658. package/optional-skills/research/osint-investigation/references/sources/sec-edgar.md +83 -0
  659. package/optional-skills/research/osint-investigation/references/sources/senate-ld.md +89 -0
  660. package/optional-skills/research/osint-investigation/references/sources/usaspending.md +97 -0
  661. package/optional-skills/research/osint-investigation/references/sources/wayback.md +93 -0
  662. package/optional-skills/research/osint-investigation/references/sources/wikipedia.md +107 -0
  663. package/optional-skills/research/osint-investigation/scripts/_http.py +82 -0
  664. package/optional-skills/research/osint-investigation/scripts/_normalize.py +67 -0
  665. package/optional-skills/research/osint-investigation/scripts/build_findings.py +221 -0
  666. package/optional-skills/research/osint-investigation/scripts/entity_resolution.py +228 -0
  667. package/optional-skills/research/osint-investigation/scripts/fetch_courtlistener.py +149 -0
  668. package/optional-skills/research/osint-investigation/scripts/fetch_gdelt.py +162 -0
  669. package/optional-skills/research/osint-investigation/scripts/fetch_icij_offshore.py +234 -0
  670. package/optional-skills/research/osint-investigation/scripts/fetch_nyc_acris.py +203 -0
  671. package/optional-skills/research/osint-investigation/scripts/fetch_ofac_sdn.py +175 -0
  672. package/optional-skills/research/osint-investigation/scripts/fetch_opencorporates.py +192 -0
  673. package/optional-skills/research/osint-investigation/scripts/fetch_sec_edgar.py +184 -0
  674. package/optional-skills/research/osint-investigation/scripts/fetch_senate_ld.py +146 -0
  675. package/optional-skills/research/osint-investigation/scripts/fetch_usaspending.py +170 -0
  676. package/optional-skills/research/osint-investigation/scripts/fetch_wayback.py +142 -0
  677. package/optional-skills/research/osint-investigation/scripts/fetch_wikipedia.py +267 -0
  678. package/optional-skills/research/osint-investigation/scripts/timing_analysis.py +253 -0
  679. package/optional-skills/research/osint-investigation/templates/source-template.md +59 -0
  680. package/optional-skills/research/parallel-cli/SKILL.md +391 -0
  681. package/optional-skills/research/qmd/SKILL.md +441 -0
  682. package/optional-skills/research/scrapling/SKILL.md +336 -0
  683. package/optional-skills/research/searxng-search/SKILL.md +212 -0
  684. package/optional-skills/research/searxng-search/scripts/searxng.sh +22 -0
  685. package/optional-skills/security/1password/SKILL.md +163 -0
  686. package/optional-skills/security/1password/references/cli-examples.md +31 -0
  687. package/optional-skills/security/1password/references/get-started.md +21 -0
  688. package/optional-skills/security/DESCRIPTION.md +3 -0
  689. package/optional-skills/security/oss-forensics/SKILL.md +423 -0
  690. package/optional-skills/security/oss-forensics/references/evidence-types.md +89 -0
  691. package/optional-skills/security/oss-forensics/references/github-archive-guide.md +184 -0
  692. package/optional-skills/security/oss-forensics/references/investigation-templates.md +131 -0
  693. package/optional-skills/security/oss-forensics/references/recovery-techniques.md +164 -0
  694. package/optional-skills/security/oss-forensics/scripts/evidence-store.py +313 -0
  695. package/optional-skills/security/oss-forensics/templates/forensic-report.md +151 -0
  696. package/optional-skills/security/oss-forensics/templates/malicious-package-report.md +43 -0
  697. package/optional-skills/security/sherlock/SKILL.md +193 -0
  698. package/optional-skills/software-development/rest-graphql-debug/SKILL.md +514 -0
  699. package/optional-skills/web-development/DESCRIPTION.md +5 -0
  700. package/optional-skills/web-development/page-agent/SKILL.md +190 -0
  701. package/package.json +78 -0
  702. package/plugins/__init__.py +1 -0
  703. package/plugins/__pycache__/__init__.cpython-312.pyc +0 -0
  704. package/plugins/context_engine/__init__.py +219 -0
  705. package/plugins/disk-cleanup/README.md +51 -0
  706. package/plugins/disk-cleanup/__init__.py +316 -0
  707. package/plugins/disk-cleanup/disk_cleanup.py +497 -0
  708. package/plugins/disk-cleanup/plugin.yaml +7 -0
  709. package/plugins/example-dashboard/dashboard/manifest.json +14 -0
  710. package/plugins/example-dashboard/dashboard/plugin_api.py +17 -0
  711. package/plugins/google_meet/README.md +131 -0
  712. package/plugins/google_meet/SKILL.md +148 -0
  713. package/plugins/google_meet/__init__.py +103 -0
  714. package/plugins/google_meet/audio_bridge.py +244 -0
  715. package/plugins/google_meet/cli.py +479 -0
  716. package/plugins/google_meet/meet_bot.py +852 -0
  717. package/plugins/google_meet/node/__init__.py +54 -0
  718. package/plugins/google_meet/node/cli.py +125 -0
  719. package/plugins/google_meet/node/client.py +107 -0
  720. package/plugins/google_meet/node/protocol.py +124 -0
  721. package/plugins/google_meet/node/registry.py +113 -0
  722. package/plugins/google_meet/node/server.py +201 -0
  723. package/plugins/google_meet/plugin.yaml +16 -0
  724. package/plugins/google_meet/process_manager.py +324 -0
  725. package/plugins/google_meet/realtime/__init__.py +10 -0
  726. package/plugins/google_meet/realtime/openai_client.py +332 -0
  727. package/plugins/google_meet/tools.py +348 -0
  728. package/plugins/hermes-achievements/LICENSE +21 -0
  729. package/plugins/hermes-achievements/README.md +150 -0
  730. package/plugins/hermes-achievements/dashboard/dist/index.js +732 -0
  731. package/plugins/hermes-achievements/dashboard/dist/style.css +146 -0
  732. package/plugins/hermes-achievements/dashboard/manifest.json +11 -0
  733. package/plugins/hermes-achievements/dashboard/plugin_api.py +1062 -0
  734. package/plugins/hermes-achievements/docs/achievements-performance-implementation-plan.md +157 -0
  735. package/plugins/hermes-achievements/docs/achievements-performance-implementation-spec.md +219 -0
  736. package/plugins/hermes-achievements/docs/achievements-performance-spec.md +174 -0
  737. package/plugins/hermes-achievements/docs/assets/achievements-dashboard-hd.png +0 -0
  738. package/plugins/hermes-achievements/docs/assets/achievements-tier-showcase-hd.png +0 -0
  739. package/plugins/hermes-achievements/tests/test_achievement_engine.py +156 -0
  740. package/plugins/image_gen/openai/__init__.py +303 -0
  741. package/plugins/image_gen/openai/__pycache__/__init__.cpython-312.pyc +0 -0
  742. package/plugins/image_gen/openai/plugin.yaml +7 -0
  743. package/plugins/image_gen/openai-codex/__init__.py +378 -0
  744. package/plugins/image_gen/openai-codex/__pycache__/__init__.cpython-312.pyc +0 -0
  745. package/plugins/image_gen/openai-codex/plugin.yaml +5 -0
  746. package/plugins/image_gen/xai/__init__.py +316 -0
  747. package/plugins/image_gen/xai/__pycache__/__init__.cpython-312.pyc +0 -0
  748. package/plugins/image_gen/xai/plugin.yaml +7 -0
  749. package/plugins/kanban/dashboard/dist/index.js +3143 -0
  750. package/plugins/kanban/dashboard/dist/style.css +1500 -0
  751. package/plugins/kanban/dashboard/manifest.json +14 -0
  752. package/plugins/kanban/dashboard/plugin_api.py +1612 -0
  753. package/plugins/kanban/systemd/hermes-kanban-dispatcher.service +32 -0
  754. package/plugins/memory/__init__.py +408 -0
  755. package/plugins/memory/byterover/README.md +41 -0
  756. package/plugins/memory/byterover/__init__.py +384 -0
  757. package/plugins/memory/byterover/plugin.yaml +9 -0
  758. package/plugins/memory/hindsight/README.md +138 -0
  759. package/plugins/memory/hindsight/__init__.py +1758 -0
  760. package/plugins/memory/hindsight/plugin.yaml +8 -0
  761. package/plugins/memory/holographic/README.md +36 -0
  762. package/plugins/memory/holographic/__init__.py +409 -0
  763. package/plugins/memory/holographic/holographic.py +203 -0
  764. package/plugins/memory/holographic/plugin.yaml +5 -0
  765. package/plugins/memory/holographic/retrieval.py +593 -0
  766. package/plugins/memory/holographic/store.py +579 -0
  767. package/plugins/memory/honcho/README.md +328 -0
  768. package/plugins/memory/honcho/__init__.py +1329 -0
  769. package/plugins/memory/honcho/cli.py +1452 -0
  770. package/plugins/memory/honcho/client.py +784 -0
  771. package/plugins/memory/honcho/plugin.yaml +7 -0
  772. package/plugins/memory/honcho/session.py +1255 -0
  773. package/plugins/memory/mem0/README.md +38 -0
  774. package/plugins/memory/mem0/__init__.py +374 -0
  775. package/plugins/memory/mem0/plugin.yaml +5 -0
  776. package/plugins/memory/openviking/README.md +40 -0
  777. package/plugins/memory/openviking/__init__.py +945 -0
  778. package/plugins/memory/openviking/plugin.yaml +9 -0
  779. package/plugins/memory/retaindb/README.md +40 -0
  780. package/plugins/memory/retaindb/__init__.py +767 -0
  781. package/plugins/memory/retaindb/plugin.yaml +7 -0
  782. package/plugins/memory/supermemory/README.md +99 -0
  783. package/plugins/memory/supermemory/__init__.py +792 -0
  784. package/plugins/memory/supermemory/plugin.yaml +5 -0
  785. package/plugins/model-providers/README.md +70 -0
  786. package/plugins/model-providers/ai-gateway/__init__.py +43 -0
  787. package/plugins/model-providers/ai-gateway/__pycache__/__init__.cpython-312.pyc +0 -0
  788. package/plugins/model-providers/ai-gateway/plugin.yaml +5 -0
  789. package/plugins/model-providers/alibaba/__init__.py +13 -0
  790. package/plugins/model-providers/alibaba/__pycache__/__init__.cpython-312.pyc +0 -0
  791. package/plugins/model-providers/alibaba/plugin.yaml +5 -0
  792. package/plugins/model-providers/alibaba-coding-plan/__init__.py +21 -0
  793. package/plugins/model-providers/alibaba-coding-plan/__pycache__/__init__.cpython-312.pyc +0 -0
  794. package/plugins/model-providers/alibaba-coding-plan/plugin.yaml +5 -0
  795. package/plugins/model-providers/anthropic/__init__.py +52 -0
  796. package/plugins/model-providers/anthropic/__pycache__/__init__.cpython-312.pyc +0 -0
  797. package/plugins/model-providers/anthropic/plugin.yaml +5 -0
  798. package/plugins/model-providers/arcee/__init__.py +13 -0
  799. package/plugins/model-providers/arcee/__pycache__/__init__.cpython-312.pyc +0 -0
  800. package/plugins/model-providers/arcee/plugin.yaml +5 -0
  801. package/plugins/model-providers/azure-foundry/__init__.py +21 -0
  802. package/plugins/model-providers/azure-foundry/__pycache__/__init__.cpython-312.pyc +0 -0
  803. package/plugins/model-providers/azure-foundry/plugin.yaml +5 -0
  804. package/plugins/model-providers/bedrock/__init__.py +29 -0
  805. package/plugins/model-providers/bedrock/__pycache__/__init__.cpython-312.pyc +0 -0
  806. package/plugins/model-providers/bedrock/plugin.yaml +5 -0
  807. package/plugins/model-providers/copilot/__init__.py +58 -0
  808. package/plugins/model-providers/copilot/__pycache__/__init__.cpython-312.pyc +0 -0
  809. package/plugins/model-providers/copilot/plugin.yaml +5 -0
  810. package/plugins/model-providers/copilot-acp/__init__.py +34 -0
  811. package/plugins/model-providers/copilot-acp/__pycache__/__init__.cpython-312.pyc +0 -0
  812. package/plugins/model-providers/copilot-acp/plugin.yaml +5 -0
  813. package/plugins/model-providers/custom/__init__.py +68 -0
  814. package/plugins/model-providers/custom/__pycache__/__init__.cpython-312.pyc +0 -0
  815. package/plugins/model-providers/custom/plugin.yaml +5 -0
  816. package/plugins/model-providers/deepseek/__init__.py +99 -0
  817. package/plugins/model-providers/deepseek/__pycache__/__init__.cpython-312.pyc +0 -0
  818. package/plugins/model-providers/deepseek/plugin.yaml +5 -0
  819. package/plugins/model-providers/gemini/__init__.py +72 -0
  820. package/plugins/model-providers/gemini/__pycache__/__init__.cpython-312.pyc +0 -0
  821. package/plugins/model-providers/gemini/plugin.yaml +5 -0
  822. package/plugins/model-providers/gmi/__init__.py +31 -0
  823. package/plugins/model-providers/gmi/__pycache__/__init__.cpython-312.pyc +0 -0
  824. package/plugins/model-providers/gmi/plugin.yaml +5 -0
  825. package/plugins/model-providers/huggingface/__init__.py +20 -0
  826. package/plugins/model-providers/huggingface/__pycache__/__init__.cpython-312.pyc +0 -0
  827. package/plugins/model-providers/huggingface/plugin.yaml +5 -0
  828. package/plugins/model-providers/kilocode/__init__.py +14 -0
  829. package/plugins/model-providers/kilocode/__pycache__/__init__.cpython-312.pyc +0 -0
  830. package/plugins/model-providers/kilocode/plugin.yaml +5 -0
  831. package/plugins/model-providers/kimi-coding/__init__.py +71 -0
  832. package/plugins/model-providers/kimi-coding/__pycache__/__init__.cpython-312.pyc +0 -0
  833. package/plugins/model-providers/kimi-coding/plugin.yaml +5 -0
  834. package/plugins/model-providers/minimax/__init__.py +45 -0
  835. package/plugins/model-providers/minimax/__pycache__/__init__.cpython-312.pyc +0 -0
  836. package/plugins/model-providers/minimax/plugin.yaml +5 -0
  837. package/plugins/model-providers/nous/__init__.py +54 -0
  838. package/plugins/model-providers/nous/__pycache__/__init__.cpython-312.pyc +0 -0
  839. package/plugins/model-providers/nous/plugin.yaml +5 -0
  840. package/plugins/model-providers/novita/__init__.py +27 -0
  841. package/plugins/model-providers/novita/__pycache__/__init__.cpython-312.pyc +0 -0
  842. package/plugins/model-providers/novita/plugin.yaml +5 -0
  843. package/plugins/model-providers/nvidia/__init__.py +21 -0
  844. package/plugins/model-providers/nvidia/__pycache__/__init__.cpython-312.pyc +0 -0
  845. package/plugins/model-providers/nvidia/plugin.yaml +5 -0
  846. package/plugins/model-providers/ollama-cloud/__init__.py +14 -0
  847. package/plugins/model-providers/ollama-cloud/__pycache__/__init__.cpython-312.pyc +0 -0
  848. package/plugins/model-providers/ollama-cloud/plugin.yaml +5 -0
  849. package/plugins/model-providers/openai-codex/__init__.py +15 -0
  850. package/plugins/model-providers/openai-codex/__pycache__/__init__.cpython-312.pyc +0 -0
  851. package/plugins/model-providers/openai-codex/plugin.yaml +5 -0
  852. package/plugins/model-providers/opencode-zen/__init__.py +30 -0
  853. package/plugins/model-providers/opencode-zen/__pycache__/__init__.cpython-312.pyc +0 -0
  854. package/plugins/model-providers/opencode-zen/plugin.yaml +5 -0
  855. package/plugins/model-providers/openrouter/__init__.py +115 -0
  856. package/plugins/model-providers/openrouter/__pycache__/__init__.cpython-312.pyc +0 -0
  857. package/plugins/model-providers/openrouter/plugin.yaml +5 -0
  858. package/plugins/model-providers/qwen-oauth/__init__.py +82 -0
  859. package/plugins/model-providers/qwen-oauth/__pycache__/__init__.cpython-312.pyc +0 -0
  860. package/plugins/model-providers/qwen-oauth/plugin.yaml +5 -0
  861. package/plugins/model-providers/stepfun/__init__.py +14 -0
  862. package/plugins/model-providers/stepfun/__pycache__/__init__.cpython-312.pyc +0 -0
  863. package/plugins/model-providers/stepfun/plugin.yaml +5 -0
  864. package/plugins/model-providers/xai/__init__.py +15 -0
  865. package/plugins/model-providers/xai/__pycache__/__init__.cpython-312.pyc +0 -0
  866. package/plugins/model-providers/xai/plugin.yaml +5 -0
  867. package/plugins/model-providers/xiaomi/__init__.py +14 -0
  868. package/plugins/model-providers/xiaomi/__pycache__/__init__.cpython-312.pyc +0 -0
  869. package/plugins/model-providers/xiaomi/plugin.yaml +5 -0
  870. package/plugins/model-providers/zai/__init__.py +21 -0
  871. package/plugins/model-providers/zai/__pycache__/__init__.cpython-312.pyc +0 -0
  872. package/plugins/model-providers/zai/plugin.yaml +5 -0
  873. package/plugins/observability/langfuse/README.md +53 -0
  874. package/plugins/observability/langfuse/__init__.py +1004 -0
  875. package/plugins/observability/langfuse/plugin.yaml +14 -0
  876. package/plugins/platforms/google_chat/__init__.py +3 -0
  877. package/plugins/platforms/google_chat/__pycache__/__init__.cpython-312.pyc +0 -0
  878. package/plugins/platforms/google_chat/__pycache__/adapter.cpython-312.pyc +0 -0
  879. package/plugins/platforms/google_chat/adapter.py +3343 -0
  880. package/plugins/platforms/google_chat/oauth.py +639 -0
  881. package/plugins/platforms/google_chat/plugin.yaml +39 -0
  882. package/plugins/platforms/irc/__init__.py +3 -0
  883. package/plugins/platforms/irc/__pycache__/__init__.cpython-312.pyc +0 -0
  884. package/plugins/platforms/irc/__pycache__/adapter.cpython-312.pyc +0 -0
  885. package/plugins/platforms/irc/adapter.py +969 -0
  886. package/plugins/platforms/irc/plugin.yaml +54 -0
  887. package/plugins/platforms/line/__init__.py +3 -0
  888. package/plugins/platforms/line/__pycache__/__init__.cpython-312.pyc +0 -0
  889. package/plugins/platforms/line/__pycache__/adapter.cpython-312.pyc +0 -0
  890. package/plugins/platforms/line/adapter.py +1639 -0
  891. package/plugins/platforms/line/plugin.yaml +65 -0
  892. package/plugins/platforms/simplex/__init__.py +3 -0
  893. package/plugins/platforms/simplex/__pycache__/__init__.cpython-312.pyc +0 -0
  894. package/plugins/platforms/simplex/__pycache__/adapter.cpython-312.pyc +0 -0
  895. package/plugins/platforms/simplex/adapter.py +746 -0
  896. package/plugins/platforms/simplex/plugin.yaml +37 -0
  897. package/plugins/platforms/teams/__init__.py +3 -0
  898. package/plugins/platforms/teams/__pycache__/__init__.cpython-312.pyc +0 -0
  899. package/plugins/platforms/teams/__pycache__/adapter.cpython-312.pyc +0 -0
  900. package/plugins/platforms/teams/adapter.py +1188 -0
  901. package/plugins/platforms/teams/plugin.yaml +48 -0
  902. package/plugins/spotify/__init__.py +66 -0
  903. package/plugins/spotify/__pycache__/__init__.cpython-312.pyc +0 -0
  904. package/plugins/spotify/__pycache__/client.cpython-312.pyc +0 -0
  905. package/plugins/spotify/__pycache__/tools.cpython-312.pyc +0 -0
  906. package/plugins/spotify/client.py +435 -0
  907. package/plugins/spotify/plugin.yaml +13 -0
  908. package/plugins/spotify/tools.py +454 -0
  909. package/plugins/teams_pipeline/__init__.py +23 -0
  910. package/plugins/teams_pipeline/cli.py +463 -0
  911. package/plugins/teams_pipeline/meetings.py +333 -0
  912. package/plugins/teams_pipeline/models.py +350 -0
  913. package/plugins/teams_pipeline/pipeline.py +692 -0
  914. package/plugins/teams_pipeline/plugin.yaml +9 -0
  915. package/plugins/teams_pipeline/runtime.py +135 -0
  916. package/plugins/teams_pipeline/store.py +194 -0
  917. package/plugins/teams_pipeline/subscriptions.py +249 -0
  918. package/plugins/video_gen/fal/__init__.py +523 -0
  919. package/plugins/video_gen/fal/__pycache__/__init__.cpython-312.pyc +0 -0
  920. package/plugins/video_gen/fal/plugin.yaml +7 -0
  921. package/plugins/video_gen/xai/__init__.py +441 -0
  922. package/plugins/video_gen/xai/__pycache__/__init__.cpython-312.pyc +0 -0
  923. package/plugins/video_gen/xai/plugin.yaml +7 -0
  924. package/plugins/web/__init__.py +7 -0
  925. package/plugins/web/__pycache__/__init__.cpython-312.pyc +0 -0
  926. package/plugins/web/brave_free/__init__.py +14 -0
  927. package/plugins/web/brave_free/__pycache__/__init__.cpython-312.pyc +0 -0
  928. package/plugins/web/brave_free/__pycache__/provider.cpython-312.pyc +0 -0
  929. package/plugins/web/brave_free/plugin.yaml +7 -0
  930. package/plugins/web/brave_free/provider.py +137 -0
  931. package/plugins/web/ddgs/__init__.py +15 -0
  932. package/plugins/web/ddgs/__pycache__/__init__.cpython-312.pyc +0 -0
  933. package/plugins/web/ddgs/__pycache__/provider.cpython-312.pyc +0 -0
  934. package/plugins/web/ddgs/plugin.yaml +7 -0
  935. package/plugins/web/ddgs/provider.py +104 -0
  936. package/plugins/web/exa/__init__.py +15 -0
  937. package/plugins/web/exa/__pycache__/__init__.cpython-312.pyc +0 -0
  938. package/plugins/web/exa/__pycache__/provider.cpython-312.pyc +0 -0
  939. package/plugins/web/exa/plugin.yaml +7 -0
  940. package/plugins/web/exa/provider.py +212 -0
  941. package/plugins/web/firecrawl/__init__.py +28 -0
  942. package/plugins/web/firecrawl/__pycache__/__init__.cpython-312.pyc +0 -0
  943. package/plugins/web/firecrawl/__pycache__/provider.cpython-312.pyc +0 -0
  944. package/plugins/web/firecrawl/plugin.yaml +7 -0
  945. package/plugins/web/firecrawl/provider.py +773 -0
  946. package/plugins/web/parallel/__init__.py +16 -0
  947. package/plugins/web/parallel/__pycache__/__init__.cpython-312.pyc +0 -0
  948. package/plugins/web/parallel/__pycache__/provider.cpython-312.pyc +0 -0
  949. package/plugins/web/parallel/plugin.yaml +7 -0
  950. package/plugins/web/parallel/provider.py +291 -0
  951. package/plugins/web/searxng/__init__.py +15 -0
  952. package/plugins/web/searxng/__pycache__/__init__.cpython-312.pyc +0 -0
  953. package/plugins/web/searxng/__pycache__/provider.cpython-312.pyc +0 -0
  954. package/plugins/web/searxng/plugin.yaml +7 -0
  955. package/plugins/web/searxng/provider.py +140 -0
  956. package/plugins/web/tavily/__init__.py +15 -0
  957. package/plugins/web/tavily/__pycache__/__init__.cpython-312.pyc +0 -0
  958. package/plugins/web/tavily/__pycache__/provider.cpython-312.pyc +0 -0
  959. package/plugins/web/tavily/plugin.yaml +7 -0
  960. package/plugins/web/tavily/provider.py +285 -0
  961. package/providers/README.md +78 -0
  962. package/providers/__init__.py +192 -0
  963. package/providers/__pycache__/__init__.cpython-312.pyc +0 -0
  964. package/providers/__pycache__/base.cpython-312.pyc +0 -0
  965. package/providers/base.py +184 -0
  966. package/pyproject.toml +255 -0
  967. package/run_agent.py +16409 -0
  968. package/scripts/benchmark_browser_eval.py +138 -0
  969. package/scripts/build_model_catalog.py +95 -0
  970. package/scripts/build_skills_index.py +325 -0
  971. package/scripts/check-windows-footguns.py +624 -0
  972. package/scripts/contributor_audit.py +473 -0
  973. package/scripts/discord-voice-doctor.py +396 -0
  974. package/scripts/hermes-gateway +416 -0
  975. package/scripts/install.cmd +28 -0
  976. package/scripts/install.ps1 +1611 -0
  977. package/scripts/install.sh +2007 -0
  978. package/scripts/install_psutil_android.py +117 -0
  979. package/scripts/keystroke_diagnostic.py +81 -0
  980. package/scripts/kill_modal.sh +34 -0
  981. package/scripts/lib/node-bootstrap.sh +238 -0
  982. package/scripts/lint_diff.py +207 -0
  983. package/scripts/postinstall.js +150 -0
  984. package/scripts/profile-tui.py +626 -0
  985. package/scripts/release.py +1680 -0
  986. package/scripts/run_tests.sh +129 -0
  987. package/scripts/sample_and_compress.py +409 -0
  988. package/scripts/setup_open_webui.sh +349 -0
  989. package/scripts/whatsapp-bridge/allowlist.js +88 -0
  990. package/scripts/whatsapp-bridge/allowlist.test.mjs +80 -0
  991. package/scripts/whatsapp-bridge/bridge.js +729 -0
  992. package/scripts/whatsapp-bridge/package-lock.json +2141 -0
  993. package/scripts/whatsapp-bridge/package.json +19 -0
  994. package/skills/apple/DESCRIPTION.md +2 -0
  995. package/skills/apple/apple-notes/SKILL.md +90 -0
  996. package/skills/apple/apple-reminders/SKILL.md +98 -0
  997. package/skills/apple/findmy/SKILL.md +131 -0
  998. package/skills/apple/imessage/SKILL.md +102 -0
  999. package/skills/apple/macos-computer-use/SKILL.md +201 -0
  1000. package/skills/autonomous-ai-agents/DESCRIPTION.md +3 -0
  1001. package/skills/autonomous-ai-agents/claude-code/SKILL.md +745 -0
  1002. package/skills/autonomous-ai-agents/codex/SKILL.md +130 -0
  1003. package/skills/autonomous-ai-agents/hermes-agent/SKILL.md +1014 -0
  1004. package/skills/autonomous-ai-agents/opencode/SKILL.md +219 -0
  1005. package/skills/creative/DESCRIPTION.md +3 -0
  1006. package/skills/creative/architecture-diagram/SKILL.md +148 -0
  1007. package/skills/creative/architecture-diagram/templates/template.html +319 -0
  1008. package/skills/creative/ascii-art/SKILL.md +322 -0
  1009. package/skills/creative/ascii-video/README.md +290 -0
  1010. package/skills/creative/ascii-video/SKILL.md +241 -0
  1011. package/skills/creative/ascii-video/references/architecture.md +802 -0
  1012. package/skills/creative/ascii-video/references/composition.md +892 -0
  1013. package/skills/creative/ascii-video/references/effects.md +1865 -0
  1014. package/skills/creative/ascii-video/references/inputs.md +685 -0
  1015. package/skills/creative/ascii-video/references/optimization.md +688 -0
  1016. package/skills/creative/ascii-video/references/scenes.md +1011 -0
  1017. package/skills/creative/ascii-video/references/shaders.md +1385 -0
  1018. package/skills/creative/ascii-video/references/troubleshooting.md +367 -0
  1019. package/skills/creative/baoyu-comic/PORT_NOTES.md +77 -0
  1020. package/skills/creative/baoyu-comic/SKILL.md +247 -0
  1021. package/skills/creative/baoyu-comic/references/analysis-framework.md +176 -0
  1022. package/skills/creative/baoyu-comic/references/art-styles/chalk.md +101 -0
  1023. package/skills/creative/baoyu-comic/references/art-styles/ink-brush.md +97 -0
  1024. package/skills/creative/baoyu-comic/references/art-styles/ligne-claire.md +75 -0
  1025. package/skills/creative/baoyu-comic/references/art-styles/manga.md +93 -0
  1026. package/skills/creative/baoyu-comic/references/art-styles/minimalist.md +84 -0
  1027. package/skills/creative/baoyu-comic/references/art-styles/realistic.md +89 -0
  1028. package/skills/creative/baoyu-comic/references/auto-selection.md +71 -0
  1029. package/skills/creative/baoyu-comic/references/base-prompt.md +98 -0
  1030. package/skills/creative/baoyu-comic/references/character-template.md +180 -0
  1031. package/skills/creative/baoyu-comic/references/layouts/cinematic.md +23 -0
  1032. package/skills/creative/baoyu-comic/references/layouts/dense.md +23 -0
  1033. package/skills/creative/baoyu-comic/references/layouts/four-panel.md +40 -0
  1034. package/skills/creative/baoyu-comic/references/layouts/mixed.md +23 -0
  1035. package/skills/creative/baoyu-comic/references/layouts/splash.md +23 -0
  1036. package/skills/creative/baoyu-comic/references/layouts/standard.md +23 -0
  1037. package/skills/creative/baoyu-comic/references/layouts/webtoon.md +30 -0
  1038. package/skills/creative/baoyu-comic/references/ohmsha-guide.md +85 -0
  1039. package/skills/creative/baoyu-comic/references/partial-workflows.md +106 -0
  1040. package/skills/creative/baoyu-comic/references/presets/concept-story.md +121 -0
  1041. package/skills/creative/baoyu-comic/references/presets/four-panel.md +107 -0
  1042. package/skills/creative/baoyu-comic/references/presets/ohmsha.md +114 -0
  1043. package/skills/creative/baoyu-comic/references/presets/shoujo.md +116 -0
  1044. package/skills/creative/baoyu-comic/references/presets/wuxia.md +110 -0
  1045. package/skills/creative/baoyu-comic/references/storyboard-template.md +143 -0
  1046. package/skills/creative/baoyu-comic/references/tones/action.md +110 -0
  1047. package/skills/creative/baoyu-comic/references/tones/dramatic.md +95 -0
  1048. package/skills/creative/baoyu-comic/references/tones/energetic.md +105 -0
  1049. package/skills/creative/baoyu-comic/references/tones/neutral.md +63 -0
  1050. package/skills/creative/baoyu-comic/references/tones/romantic.md +100 -0
  1051. package/skills/creative/baoyu-comic/references/tones/vintage.md +104 -0
  1052. package/skills/creative/baoyu-comic/references/tones/warm.md +94 -0
  1053. package/skills/creative/baoyu-comic/references/workflow.md +401 -0
  1054. package/skills/creative/baoyu-infographic/PORT_NOTES.md +43 -0
  1055. package/skills/creative/baoyu-infographic/SKILL.md +237 -0
  1056. package/skills/creative/baoyu-infographic/references/analysis-framework.md +182 -0
  1057. package/skills/creative/baoyu-infographic/references/base-prompt.md +43 -0
  1058. package/skills/creative/baoyu-infographic/references/layouts/bento-grid.md +41 -0
  1059. package/skills/creative/baoyu-infographic/references/layouts/binary-comparison.md +48 -0
  1060. package/skills/creative/baoyu-infographic/references/layouts/bridge.md +41 -0
  1061. package/skills/creative/baoyu-infographic/references/layouts/circular-flow.md +41 -0
  1062. package/skills/creative/baoyu-infographic/references/layouts/comic-strip.md +41 -0
  1063. package/skills/creative/baoyu-infographic/references/layouts/comparison-matrix.md +41 -0
  1064. package/skills/creative/baoyu-infographic/references/layouts/dashboard.md +41 -0
  1065. package/skills/creative/baoyu-infographic/references/layouts/dense-modules.md +72 -0
  1066. package/skills/creative/baoyu-infographic/references/layouts/funnel.md +41 -0
  1067. package/skills/creative/baoyu-infographic/references/layouts/hierarchical-layers.md +48 -0
  1068. package/skills/creative/baoyu-infographic/references/layouts/hub-spoke.md +41 -0
  1069. package/skills/creative/baoyu-infographic/references/layouts/iceberg.md +41 -0
  1070. package/skills/creative/baoyu-infographic/references/layouts/isometric-map.md +41 -0
  1071. package/skills/creative/baoyu-infographic/references/layouts/jigsaw.md +41 -0
  1072. package/skills/creative/baoyu-infographic/references/layouts/linear-progression.md +48 -0
  1073. package/skills/creative/baoyu-infographic/references/layouts/periodic-table.md +41 -0
  1074. package/skills/creative/baoyu-infographic/references/layouts/story-mountain.md +41 -0
  1075. package/skills/creative/baoyu-infographic/references/layouts/structural-breakdown.md +48 -0
  1076. package/skills/creative/baoyu-infographic/references/layouts/tree-branching.md +41 -0
  1077. package/skills/creative/baoyu-infographic/references/layouts/venn-diagram.md +41 -0
  1078. package/skills/creative/baoyu-infographic/references/layouts/winding-roadmap.md +41 -0
  1079. package/skills/creative/baoyu-infographic/references/structured-content-template.md +244 -0
  1080. package/skills/creative/baoyu-infographic/references/styles/aged-academia.md +36 -0
  1081. package/skills/creative/baoyu-infographic/references/styles/bold-graphic.md +36 -0
  1082. package/skills/creative/baoyu-infographic/references/styles/chalkboard.md +61 -0
  1083. package/skills/creative/baoyu-infographic/references/styles/claymation.md +29 -0
  1084. package/skills/creative/baoyu-infographic/references/styles/corporate-memphis.md +29 -0
  1085. package/skills/creative/baoyu-infographic/references/styles/craft-handmade.md +44 -0
  1086. package/skills/creative/baoyu-infographic/references/styles/cyberpunk-neon.md +29 -0
  1087. package/skills/creative/baoyu-infographic/references/styles/hand-drawn-edu.md +63 -0
  1088. package/skills/creative/baoyu-infographic/references/styles/ikea-manual.md +29 -0
  1089. package/skills/creative/baoyu-infographic/references/styles/kawaii.md +29 -0
  1090. package/skills/creative/baoyu-infographic/references/styles/knolling.md +29 -0
  1091. package/skills/creative/baoyu-infographic/references/styles/lego-brick.md +29 -0
  1092. package/skills/creative/baoyu-infographic/references/styles/morandi-journal.md +60 -0
  1093. package/skills/creative/baoyu-infographic/references/styles/origami.md +29 -0
  1094. package/skills/creative/baoyu-infographic/references/styles/pixel-art.md +29 -0
  1095. package/skills/creative/baoyu-infographic/references/styles/pop-laboratory.md +48 -0
  1096. package/skills/creative/baoyu-infographic/references/styles/retro-pop-grid.md +47 -0
  1097. package/skills/creative/baoyu-infographic/references/styles/storybook-watercolor.md +29 -0
  1098. package/skills/creative/baoyu-infographic/references/styles/subway-map.md +29 -0
  1099. package/skills/creative/baoyu-infographic/references/styles/technical-schematic.md +36 -0
  1100. package/skills/creative/baoyu-infographic/references/styles/ui-wireframe.md +29 -0
  1101. package/skills/creative/claude-design/SKILL.md +591 -0
  1102. package/skills/creative/comfyui/SKILL.md +612 -0
  1103. package/skills/creative/comfyui/references/official-cli.md +255 -0
  1104. package/skills/creative/comfyui/references/rest-api.md +312 -0
  1105. package/skills/creative/comfyui/references/template-integrity.md +243 -0
  1106. package/skills/creative/comfyui/references/workflow-format.md +226 -0
  1107. package/skills/creative/comfyui/scripts/_common.py +835 -0
  1108. package/skills/creative/comfyui/scripts/auto_fix_deps.py +225 -0
  1109. package/skills/creative/comfyui/scripts/check_deps.py +437 -0
  1110. package/skills/creative/comfyui/scripts/comfyui_setup.sh +286 -0
  1111. package/skills/creative/comfyui/scripts/extract_schema.py +315 -0
  1112. package/skills/creative/comfyui/scripts/fetch_logs.py +158 -0
  1113. package/skills/creative/comfyui/scripts/hardware_check.py +497 -0
  1114. package/skills/creative/comfyui/scripts/health_check.py +223 -0
  1115. package/skills/creative/comfyui/scripts/run_batch.py +243 -0
  1116. package/skills/creative/comfyui/scripts/run_workflow.py +796 -0
  1117. package/skills/creative/comfyui/scripts/ws_monitor.py +267 -0
  1118. package/skills/creative/comfyui/tests/README.md +50 -0
  1119. package/skills/creative/comfyui/tests/conftest.py +64 -0
  1120. package/skills/creative/comfyui/tests/pytest.ini +5 -0
  1121. package/skills/creative/comfyui/tests/test_check_deps.py +68 -0
  1122. package/skills/creative/comfyui/tests/test_cloud_integration.py +95 -0
  1123. package/skills/creative/comfyui/tests/test_common.py +447 -0
  1124. package/skills/creative/comfyui/tests/test_extract_schema.py +185 -0
  1125. package/skills/creative/comfyui/tests/test_run_workflow.py +213 -0
  1126. package/skills/creative/comfyui/workflows/README.md +86 -0
  1127. package/skills/creative/comfyui/workflows/animatediff_video.json +64 -0
  1128. package/skills/creative/comfyui/workflows/flux_dev_txt2img.json +78 -0
  1129. package/skills/creative/comfyui/workflows/sd15_txt2img.json +49 -0
  1130. package/skills/creative/comfyui/workflows/sdxl_img2img.json +54 -0
  1131. package/skills/creative/comfyui/workflows/sdxl_inpaint.json +59 -0
  1132. package/skills/creative/comfyui/workflows/sdxl_txt2img.json +49 -0
  1133. package/skills/creative/comfyui/workflows/upscale_4x.json +27 -0
  1134. package/skills/creative/comfyui/workflows/wan_video_t2v.json +69 -0
  1135. package/skills/creative/creative-ideation/SKILL.md +152 -0
  1136. package/skills/creative/creative-ideation/references/full-prompt-library.md +110 -0
  1137. package/skills/creative/design-md/SKILL.md +199 -0
  1138. package/skills/creative/design-md/templates/starter.md +99 -0
  1139. package/skills/creative/excalidraw/SKILL.md +199 -0
  1140. package/skills/creative/excalidraw/references/colors.md +44 -0
  1141. package/skills/creative/excalidraw/references/dark-mode.md +68 -0
  1142. package/skills/creative/excalidraw/references/examples.md +141 -0
  1143. package/skills/creative/excalidraw/scripts/upload.py +133 -0
  1144. package/skills/creative/humanizer/LICENSE +21 -0
  1145. package/skills/creative/humanizer/SKILL.md +578 -0
  1146. package/skills/creative/manim-video/README.md +23 -0
  1147. package/skills/creative/manim-video/SKILL.md +269 -0
  1148. package/skills/creative/manim-video/references/animation-design-thinking.md +161 -0
  1149. package/skills/creative/manim-video/references/animations.md +282 -0
  1150. package/skills/creative/manim-video/references/camera-and-3d.md +135 -0
  1151. package/skills/creative/manim-video/references/decorations.md +202 -0
  1152. package/skills/creative/manim-video/references/equations.md +216 -0
  1153. package/skills/creative/manim-video/references/graphs-and-data.md +163 -0
  1154. package/skills/creative/manim-video/references/mobjects.md +333 -0
  1155. package/skills/creative/manim-video/references/paper-explainer.md +255 -0
  1156. package/skills/creative/manim-video/references/production-quality.md +190 -0
  1157. package/skills/creative/manim-video/references/rendering.md +185 -0
  1158. package/skills/creative/manim-video/references/scene-planning.md +118 -0
  1159. package/skills/creative/manim-video/references/troubleshooting.md +135 -0
  1160. package/skills/creative/manim-video/references/updaters-and-trackers.md +260 -0
  1161. package/skills/creative/manim-video/references/visual-design.md +124 -0
  1162. package/skills/creative/manim-video/scripts/setup.sh +14 -0
  1163. package/skills/creative/p5js/README.md +64 -0
  1164. package/skills/creative/p5js/SKILL.md +556 -0
  1165. package/skills/creative/p5js/references/animation.md +439 -0
  1166. package/skills/creative/p5js/references/color-systems.md +352 -0
  1167. package/skills/creative/p5js/references/core-api.md +410 -0
  1168. package/skills/creative/p5js/references/export-pipeline.md +566 -0
  1169. package/skills/creative/p5js/references/interaction.md +398 -0
  1170. package/skills/creative/p5js/references/shapes-and-geometry.md +300 -0
  1171. package/skills/creative/p5js/references/troubleshooting.md +532 -0
  1172. package/skills/creative/p5js/references/typography.md +302 -0
  1173. package/skills/creative/p5js/references/visual-effects.md +895 -0
  1174. package/skills/creative/p5js/references/webgl-and-3d.md +423 -0
  1175. package/skills/creative/p5js/scripts/export-frames.js +179 -0
  1176. package/skills/creative/p5js/scripts/render.sh +108 -0
  1177. package/skills/creative/p5js/scripts/serve.sh +28 -0
  1178. package/skills/creative/p5js/scripts/setup.sh +87 -0
  1179. package/skills/creative/p5js/templates/viewer.html +395 -0
  1180. package/skills/creative/pixel-art/ATTRIBUTION.md +54 -0
  1181. package/skills/creative/pixel-art/SKILL.md +218 -0
  1182. package/skills/creative/pixel-art/references/palettes.md +49 -0
  1183. package/skills/creative/pixel-art/scripts/__init__.py +0 -0
  1184. package/skills/creative/pixel-art/scripts/palettes.py +167 -0
  1185. package/skills/creative/pixel-art/scripts/pixel_art.py +162 -0
  1186. package/skills/creative/pixel-art/scripts/pixel_art_video.py +345 -0
  1187. package/skills/creative/popular-web-designs/SKILL.md +214 -0
  1188. package/skills/creative/popular-web-designs/templates/airbnb.md +259 -0
  1189. package/skills/creative/popular-web-designs/templates/airtable.md +102 -0
  1190. package/skills/creative/popular-web-designs/templates/apple.md +326 -0
  1191. package/skills/creative/popular-web-designs/templates/bmw.md +193 -0
  1192. package/skills/creative/popular-web-designs/templates/cal.md +272 -0
  1193. package/skills/creative/popular-web-designs/templates/claude.md +325 -0
  1194. package/skills/creative/popular-web-designs/templates/clay.md +317 -0
  1195. package/skills/creative/popular-web-designs/templates/clickhouse.md +294 -0
  1196. package/skills/creative/popular-web-designs/templates/cohere.md +279 -0
  1197. package/skills/creative/popular-web-designs/templates/coinbase.md +142 -0
  1198. package/skills/creative/popular-web-designs/templates/composio.md +320 -0
  1199. package/skills/creative/popular-web-designs/templates/cursor.md +322 -0
  1200. package/skills/creative/popular-web-designs/templates/elevenlabs.md +278 -0
  1201. package/skills/creative/popular-web-designs/templates/expo.md +294 -0
  1202. package/skills/creative/popular-web-designs/templates/figma.md +233 -0
  1203. package/skills/creative/popular-web-designs/templates/framer.md +259 -0
  1204. package/skills/creative/popular-web-designs/templates/hashicorp.md +291 -0
  1205. package/skills/creative/popular-web-designs/templates/ibm.md +345 -0
  1206. package/skills/creative/popular-web-designs/templates/intercom.md +159 -0
  1207. package/skills/creative/popular-web-designs/templates/kraken.md +138 -0
  1208. package/skills/creative/popular-web-designs/templates/linear.app.md +380 -0
  1209. package/skills/creative/popular-web-designs/templates/lovable.md +311 -0
  1210. package/skills/creative/popular-web-designs/templates/minimax.md +270 -0
  1211. package/skills/creative/popular-web-designs/templates/mintlify.md +339 -0
  1212. package/skills/creative/popular-web-designs/templates/miro.md +121 -0
  1213. package/skills/creative/popular-web-designs/templates/mistral.ai.md +274 -0
  1214. package/skills/creative/popular-web-designs/templates/mongodb.md +279 -0
  1215. package/skills/creative/popular-web-designs/templates/notion.md +322 -0
  1216. package/skills/creative/popular-web-designs/templates/nvidia.md +306 -0
  1217. package/skills/creative/popular-web-designs/templates/ollama.md +280 -0
  1218. package/skills/creative/popular-web-designs/templates/opencode.ai.md +294 -0
  1219. package/skills/creative/popular-web-designs/templates/pinterest.md +243 -0
  1220. package/skills/creative/popular-web-designs/templates/posthog.md +269 -0
  1221. package/skills/creative/popular-web-designs/templates/raycast.md +281 -0
  1222. package/skills/creative/popular-web-designs/templates/replicate.md +274 -0
  1223. package/skills/creative/popular-web-designs/templates/resend.md +316 -0
  1224. package/skills/creative/popular-web-designs/templates/revolut.md +198 -0
  1225. package/skills/creative/popular-web-designs/templates/runwayml.md +257 -0
  1226. package/skills/creative/popular-web-designs/templates/sanity.md +370 -0
  1227. package/skills/creative/popular-web-designs/templates/sentry.md +275 -0
  1228. package/skills/creative/popular-web-designs/templates/spacex.md +207 -0
  1229. package/skills/creative/popular-web-designs/templates/spotify.md +259 -0
  1230. package/skills/creative/popular-web-designs/templates/stripe.md +335 -0
  1231. package/skills/creative/popular-web-designs/templates/supabase.md +268 -0
  1232. package/skills/creative/popular-web-designs/templates/superhuman.md +265 -0
  1233. package/skills/creative/popular-web-designs/templates/together.ai.md +276 -0
  1234. package/skills/creative/popular-web-designs/templates/uber.md +308 -0
  1235. package/skills/creative/popular-web-designs/templates/vercel.md +323 -0
  1236. package/skills/creative/popular-web-designs/templates/voltagent.md +336 -0
  1237. package/skills/creative/popular-web-designs/templates/warp.md +266 -0
  1238. package/skills/creative/popular-web-designs/templates/webflow.md +105 -0
  1239. package/skills/creative/popular-web-designs/templates/wise.md +186 -0
  1240. package/skills/creative/popular-web-designs/templates/x.ai.md +270 -0
  1241. package/skills/creative/popular-web-designs/templates/zapier.md +341 -0
  1242. package/skills/creative/pretext/SKILL.md +220 -0
  1243. package/skills/creative/pretext/references/patterns.md +258 -0
  1244. package/skills/creative/pretext/templates/donut-orbit.html +1468 -0
  1245. package/skills/creative/pretext/templates/hello-orb-flow.html +95 -0
  1246. package/skills/creative/sketch/SKILL.md +218 -0
  1247. package/skills/creative/songwriting-and-ai-music/SKILL.md +287 -0
  1248. package/skills/creative/touchdesigner-mcp/SKILL.md +356 -0
  1249. package/skills/creative/touchdesigner-mcp/references/3d-scene.md +275 -0
  1250. package/skills/creative/touchdesigner-mcp/references/animation.md +221 -0
  1251. package/skills/creative/touchdesigner-mcp/references/audio-reactive.md +175 -0
  1252. package/skills/creative/touchdesigner-mcp/references/dat-scripting.md +352 -0
  1253. package/skills/creative/touchdesigner-mcp/references/external-data.md +322 -0
  1254. package/skills/creative/touchdesigner-mcp/references/geometry-comp.md +121 -0
  1255. package/skills/creative/touchdesigner-mcp/references/glsl.md +151 -0
  1256. package/skills/creative/touchdesigner-mcp/references/layout-compositor.md +131 -0
  1257. package/skills/creative/touchdesigner-mcp/references/mcp-tools.md +382 -0
  1258. package/skills/creative/touchdesigner-mcp/references/midi-osc.md +211 -0
  1259. package/skills/creative/touchdesigner-mcp/references/network-patterns.md +966 -0
  1260. package/skills/creative/touchdesigner-mcp/references/operator-tips.md +106 -0
  1261. package/skills/creative/touchdesigner-mcp/references/operators.md +239 -0
  1262. package/skills/creative/touchdesigner-mcp/references/panel-ui.md +281 -0
  1263. package/skills/creative/touchdesigner-mcp/references/particles.md +245 -0
  1264. package/skills/creative/touchdesigner-mcp/references/pitfalls.md +704 -0
  1265. package/skills/creative/touchdesigner-mcp/references/postfx.md +183 -0
  1266. package/skills/creative/touchdesigner-mcp/references/projection-mapping.md +211 -0
  1267. package/skills/creative/touchdesigner-mcp/references/python-api.md +463 -0
  1268. package/skills/creative/touchdesigner-mcp/references/replicator.md +198 -0
  1269. package/skills/creative/touchdesigner-mcp/references/troubleshooting.md +244 -0
  1270. package/skills/creative/touchdesigner-mcp/scripts/setup.sh +115 -0
  1271. package/skills/data-science/DESCRIPTION.md +3 -0
  1272. package/skills/data-science/jupyter-live-kernel/SKILL.md +167 -0
  1273. package/skills/devops/kanban-orchestrator/SKILL.md +189 -0
  1274. package/skills/devops/kanban-worker/SKILL.md +184 -0
  1275. package/skills/devops/webhook-subscriptions/SKILL.md +204 -0
  1276. package/skills/diagramming/DESCRIPTION.md +3 -0
  1277. package/skills/dogfood/SKILL.md +162 -0
  1278. package/skills/dogfood/references/issue-taxonomy.md +109 -0
  1279. package/skills/dogfood/templates/dogfood-report-template.md +86 -0
  1280. package/skills/domain/DESCRIPTION.md +24 -0
  1281. package/skills/email/DESCRIPTION.md +3 -0
  1282. package/skills/email/himalaya/SKILL.md +299 -0
  1283. package/skills/email/himalaya/references/configuration.md +227 -0
  1284. package/skills/email/himalaya/references/message-composition.md +199 -0
  1285. package/skills/gaming/DESCRIPTION.md +3 -0
  1286. package/skills/gaming/minecraft-modpack-server/SKILL.md +187 -0
  1287. package/skills/gaming/pokemon-player/SKILL.md +216 -0
  1288. package/skills/gifs/DESCRIPTION.md +3 -0
  1289. package/skills/github/DESCRIPTION.md +3 -0
  1290. package/skills/github/codebase-inspection/SKILL.md +116 -0
  1291. package/skills/github/github-auth/SKILL.md +247 -0
  1292. package/skills/github/github-auth/scripts/gh-env.sh +66 -0
  1293. package/skills/github/github-code-review/SKILL.md +481 -0
  1294. package/skills/github/github-code-review/references/review-output-template.md +74 -0
  1295. package/skills/github/github-issues/SKILL.md +370 -0
  1296. package/skills/github/github-issues/templates/bug-report.md +35 -0
  1297. package/skills/github/github-issues/templates/feature-request.md +31 -0
  1298. package/skills/github/github-pr-workflow/SKILL.md +367 -0
  1299. package/skills/github/github-pr-workflow/references/ci-troubleshooting.md +183 -0
  1300. package/skills/github/github-pr-workflow/references/conventional-commits.md +71 -0
  1301. package/skills/github/github-pr-workflow/templates/pr-body-bugfix.md +35 -0
  1302. package/skills/github/github-pr-workflow/templates/pr-body-feature.md +33 -0
  1303. package/skills/github/github-repo-management/SKILL.md +516 -0
  1304. package/skills/github/github-repo-management/references/github-api-cheatsheet.md +161 -0
  1305. package/skills/index-cache/anthropics_skills_skills_.json +1 -0
  1306. package/skills/index-cache/claude_marketplace_anthropics_skills.json +1 -0
  1307. package/skills/index-cache/lobehub_index.json +1 -0
  1308. package/skills/index-cache/openai_skills_skills_.json +1 -0
  1309. package/skills/inference-sh/DESCRIPTION.md +19 -0
  1310. package/skills/mcp/DESCRIPTION.md +3 -0
  1311. package/skills/mcp/native-mcp/SKILL.md +357 -0
  1312. package/skills/media/DESCRIPTION.md +3 -0
  1313. package/skills/media/gif-search/SKILL.md +91 -0
  1314. package/skills/media/heartmula/SKILL.md +171 -0
  1315. package/skills/media/songsee/SKILL.md +83 -0
  1316. package/skills/media/spotify/SKILL.md +135 -0
  1317. package/skills/media/youtube-content/SKILL.md +73 -0
  1318. package/skills/media/youtube-content/references/output-formats.md +56 -0
  1319. package/skills/media/youtube-content/scripts/fetch_transcript.py +124 -0
  1320. package/skills/mlops/DESCRIPTION.md +3 -0
  1321. package/skills/mlops/evaluation/DESCRIPTION.md +3 -0
  1322. package/skills/mlops/evaluation/lm-evaluation-harness/SKILL.md +498 -0
  1323. package/skills/mlops/evaluation/lm-evaluation-harness/references/api-evaluation.md +490 -0
  1324. package/skills/mlops/evaluation/lm-evaluation-harness/references/benchmark-guide.md +488 -0
  1325. package/skills/mlops/evaluation/lm-evaluation-harness/references/custom-tasks.md +602 -0
  1326. package/skills/mlops/evaluation/lm-evaluation-harness/references/distributed-eval.md +519 -0
  1327. package/skills/mlops/evaluation/weights-and-biases/SKILL.md +594 -0
  1328. package/skills/mlops/evaluation/weights-and-biases/references/artifacts.md +584 -0
  1329. package/skills/mlops/evaluation/weights-and-biases/references/integrations.md +700 -0
  1330. package/skills/mlops/evaluation/weights-and-biases/references/sweeps.md +847 -0
  1331. package/skills/mlops/huggingface-hub/SKILL.md +81 -0
  1332. package/skills/mlops/inference/DESCRIPTION.md +3 -0
  1333. package/skills/mlops/inference/llama-cpp/SKILL.md +249 -0
  1334. package/skills/mlops/inference/llama-cpp/references/advanced-usage.md +504 -0
  1335. package/skills/mlops/inference/llama-cpp/references/hub-discovery.md +168 -0
  1336. package/skills/mlops/inference/llama-cpp/references/optimization.md +89 -0
  1337. package/skills/mlops/inference/llama-cpp/references/quantization.md +243 -0
  1338. package/skills/mlops/inference/llama-cpp/references/server.md +150 -0
  1339. package/skills/mlops/inference/llama-cpp/references/troubleshooting.md +442 -0
  1340. package/skills/mlops/inference/obliteratus/SKILL.md +342 -0
  1341. package/skills/mlops/inference/obliteratus/references/analysis-modules.md +166 -0
  1342. package/skills/mlops/inference/obliteratus/references/methods-guide.md +141 -0
  1343. package/skills/mlops/inference/obliteratus/templates/abliteration-config.yaml +33 -0
  1344. package/skills/mlops/inference/obliteratus/templates/analysis-study.yaml +40 -0
  1345. package/skills/mlops/inference/obliteratus/templates/batch-abliteration.yaml +41 -0
  1346. package/skills/mlops/inference/vllm/SKILL.md +372 -0
  1347. package/skills/mlops/inference/vllm/references/optimization.md +226 -0
  1348. package/skills/mlops/inference/vllm/references/quantization.md +284 -0
  1349. package/skills/mlops/inference/vllm/references/server-deployment.md +255 -0
  1350. package/skills/mlops/inference/vllm/references/troubleshooting.md +447 -0
  1351. package/skills/mlops/models/DESCRIPTION.md +3 -0
  1352. package/skills/mlops/models/audiocraft/SKILL.md +568 -0
  1353. package/skills/mlops/models/audiocraft/references/advanced-usage.md +666 -0
  1354. package/skills/mlops/models/audiocraft/references/troubleshooting.md +504 -0
  1355. package/skills/mlops/models/segment-anything/SKILL.md +506 -0
  1356. package/skills/mlops/models/segment-anything/references/advanced-usage.md +589 -0
  1357. package/skills/mlops/models/segment-anything/references/troubleshooting.md +484 -0
  1358. package/skills/mlops/research/DESCRIPTION.md +3 -0
  1359. package/skills/mlops/research/dspy/SKILL.md +594 -0
  1360. package/skills/mlops/research/dspy/references/examples.md +663 -0
  1361. package/skills/mlops/research/dspy/references/modules.md +475 -0
  1362. package/skills/mlops/research/dspy/references/optimizers.md +566 -0
  1363. package/skills/mlops/training/DESCRIPTION.md +3 -0
  1364. package/skills/mlops/vector-databases/DESCRIPTION.md +3 -0
  1365. package/skills/note-taking/DESCRIPTION.md +3 -0
  1366. package/skills/note-taking/obsidian/SKILL.md +61 -0
  1367. package/skills/productivity/DESCRIPTION.md +3 -0
  1368. package/skills/productivity/airtable/SKILL.md +229 -0
  1369. package/skills/productivity/google-workspace/SKILL.md +335 -0
  1370. package/skills/productivity/google-workspace/references/gmail-search-syntax.md +63 -0
  1371. package/skills/productivity/google-workspace/scripts/_hermes_home.py +43 -0
  1372. package/skills/productivity/google-workspace/scripts/google_api.py +1221 -0
  1373. package/skills/productivity/google-workspace/scripts/gws_bridge.py +108 -0
  1374. package/skills/productivity/google-workspace/scripts/setup.py +454 -0
  1375. package/skills/productivity/linear/SKILL.md +380 -0
  1376. package/skills/productivity/linear/scripts/linear_api.py +445 -0
  1377. package/skills/productivity/maps/SKILL.md +195 -0
  1378. package/skills/productivity/maps/scripts/maps_client.py +1298 -0
  1379. package/skills/productivity/nano-pdf/SKILL.md +52 -0
  1380. package/skills/productivity/notion/SKILL.md +448 -0
  1381. package/skills/productivity/notion/references/block-types.md +112 -0
  1382. package/skills/productivity/ocr-and-documents/DESCRIPTION.md +3 -0
  1383. package/skills/productivity/ocr-and-documents/SKILL.md +172 -0
  1384. package/skills/productivity/ocr-and-documents/scripts/extract_marker.py +87 -0
  1385. package/skills/productivity/ocr-and-documents/scripts/extract_pymupdf.py +98 -0
  1386. package/skills/productivity/powerpoint/LICENSE.txt +30 -0
  1387. package/skills/productivity/powerpoint/SKILL.md +237 -0
  1388. package/skills/productivity/powerpoint/editing.md +205 -0
  1389. package/skills/productivity/powerpoint/pptxgenjs.md +420 -0
  1390. package/skills/productivity/powerpoint/scripts/__init__.py +0 -0
  1391. package/skills/productivity/powerpoint/scripts/add_slide.py +195 -0
  1392. package/skills/productivity/powerpoint/scripts/clean.py +286 -0
  1393. package/skills/productivity/powerpoint/scripts/office/helpers/__init__.py +0 -0
  1394. package/skills/productivity/powerpoint/scripts/office/helpers/merge_runs.py +199 -0
  1395. package/skills/productivity/powerpoint/scripts/office/helpers/simplify_redlines.py +197 -0
  1396. package/skills/productivity/powerpoint/scripts/office/pack.py +159 -0
  1397. package/skills/productivity/powerpoint/scripts/office/schemas/ISO-IEC29500-4_2016/dml-chart.xsd +1499 -0
  1398. package/skills/productivity/powerpoint/scripts/office/schemas/ISO-IEC29500-4_2016/dml-chartDrawing.xsd +146 -0
  1399. package/skills/productivity/powerpoint/scripts/office/schemas/ISO-IEC29500-4_2016/dml-diagram.xsd +1085 -0
  1400. package/skills/productivity/powerpoint/scripts/office/schemas/ISO-IEC29500-4_2016/dml-lockedCanvas.xsd +11 -0
  1401. package/skills/productivity/powerpoint/scripts/office/schemas/ISO-IEC29500-4_2016/dml-main.xsd +3081 -0
  1402. package/skills/productivity/powerpoint/scripts/office/schemas/ISO-IEC29500-4_2016/dml-picture.xsd +23 -0
  1403. package/skills/productivity/powerpoint/scripts/office/schemas/ISO-IEC29500-4_2016/dml-spreadsheetDrawing.xsd +185 -0
  1404. package/skills/productivity/powerpoint/scripts/office/schemas/ISO-IEC29500-4_2016/dml-wordprocessingDrawing.xsd +287 -0
  1405. package/skills/productivity/powerpoint/scripts/office/schemas/ISO-IEC29500-4_2016/pml.xsd +1676 -0
  1406. package/skills/productivity/powerpoint/scripts/office/schemas/ISO-IEC29500-4_2016/shared-additionalCharacteristics.xsd +28 -0
  1407. package/skills/productivity/powerpoint/scripts/office/schemas/ISO-IEC29500-4_2016/shared-bibliography.xsd +144 -0
  1408. package/skills/productivity/powerpoint/scripts/office/schemas/ISO-IEC29500-4_2016/shared-commonSimpleTypes.xsd +174 -0
  1409. package/skills/productivity/powerpoint/scripts/office/schemas/ISO-IEC29500-4_2016/shared-customXmlDataProperties.xsd +25 -0
  1410. package/skills/productivity/powerpoint/scripts/office/schemas/ISO-IEC29500-4_2016/shared-customXmlSchemaProperties.xsd +18 -0
  1411. package/skills/productivity/powerpoint/scripts/office/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesCustom.xsd +59 -0
  1412. package/skills/productivity/powerpoint/scripts/office/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesExtended.xsd +56 -0
  1413. package/skills/productivity/powerpoint/scripts/office/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesVariantTypes.xsd +195 -0
  1414. package/skills/productivity/powerpoint/scripts/office/schemas/ISO-IEC29500-4_2016/shared-math.xsd +582 -0
  1415. package/skills/productivity/powerpoint/scripts/office/schemas/ISO-IEC29500-4_2016/shared-relationshipReference.xsd +25 -0
  1416. package/skills/productivity/powerpoint/scripts/office/schemas/ISO-IEC29500-4_2016/sml.xsd +4439 -0
  1417. package/skills/productivity/powerpoint/scripts/office/schemas/ISO-IEC29500-4_2016/vml-main.xsd +570 -0
  1418. package/skills/productivity/powerpoint/scripts/office/schemas/ISO-IEC29500-4_2016/vml-officeDrawing.xsd +509 -0
  1419. package/skills/productivity/powerpoint/scripts/office/schemas/ISO-IEC29500-4_2016/vml-presentationDrawing.xsd +12 -0
  1420. package/skills/productivity/powerpoint/scripts/office/schemas/ISO-IEC29500-4_2016/vml-spreadsheetDrawing.xsd +108 -0
  1421. package/skills/productivity/powerpoint/scripts/office/schemas/ISO-IEC29500-4_2016/vml-wordprocessingDrawing.xsd +96 -0
  1422. package/skills/productivity/powerpoint/scripts/office/schemas/ISO-IEC29500-4_2016/wml.xsd +3646 -0
  1423. package/skills/productivity/powerpoint/scripts/office/schemas/ISO-IEC29500-4_2016/xml.xsd +116 -0
  1424. package/skills/productivity/powerpoint/scripts/office/schemas/ecma/fourth-edition/opc-contentTypes.xsd +42 -0
  1425. package/skills/productivity/powerpoint/scripts/office/schemas/ecma/fourth-edition/opc-coreProperties.xsd +50 -0
  1426. package/skills/productivity/powerpoint/scripts/office/schemas/ecma/fourth-edition/opc-digSig.xsd +49 -0
  1427. package/skills/productivity/powerpoint/scripts/office/schemas/ecma/fourth-edition/opc-relationships.xsd +33 -0
  1428. package/skills/productivity/powerpoint/scripts/office/schemas/mce/mc.xsd +75 -0
  1429. package/skills/productivity/powerpoint/scripts/office/schemas/microsoft/wml-2010.xsd +560 -0
  1430. package/skills/productivity/powerpoint/scripts/office/schemas/microsoft/wml-2012.xsd +67 -0
  1431. package/skills/productivity/powerpoint/scripts/office/schemas/microsoft/wml-2018.xsd +14 -0
  1432. package/skills/productivity/powerpoint/scripts/office/schemas/microsoft/wml-cex-2018.xsd +20 -0
  1433. package/skills/productivity/powerpoint/scripts/office/schemas/microsoft/wml-cid-2016.xsd +13 -0
  1434. package/skills/productivity/powerpoint/scripts/office/schemas/microsoft/wml-sdtdatahash-2020.xsd +4 -0
  1435. package/skills/productivity/powerpoint/scripts/office/schemas/microsoft/wml-symex-2015.xsd +8 -0
  1436. package/skills/productivity/teams-meeting-pipeline/SKILL.md +116 -0
  1437. package/skills/red-teaming/godmode/SKILL.md +404 -0
  1438. package/skills/red-teaming/godmode/references/jailbreak-templates.md +128 -0
  1439. package/skills/red-teaming/godmode/references/refusal-detection.md +142 -0
  1440. package/skills/red-teaming/godmode/scripts/auto_jailbreak.py +769 -0
  1441. package/skills/red-teaming/godmode/scripts/godmode_race.py +530 -0
  1442. package/skills/red-teaming/godmode/scripts/load_godmode.py +45 -0
  1443. package/skills/red-teaming/godmode/scripts/parseltongue.py +550 -0
  1444. package/skills/red-teaming/godmode/templates/prefill-subtle.json +10 -0
  1445. package/skills/red-teaming/godmode/templates/prefill.json +18 -0
  1446. package/skills/research/DESCRIPTION.md +3 -0
  1447. package/skills/research/arxiv/SKILL.md +282 -0
  1448. package/skills/research/arxiv/scripts/search_arxiv.py +114 -0
  1449. package/skills/research/blogwatcher/SKILL.md +137 -0
  1450. package/skills/research/llm-wiki/SKILL.md +507 -0
  1451. package/skills/research/polymarket/SKILL.md +77 -0
  1452. package/skills/research/polymarket/references/api-endpoints.md +220 -0
  1453. package/skills/research/polymarket/scripts/polymarket.py +284 -0
  1454. package/skills/research/research-paper-writing/SKILL.md +2377 -0
  1455. package/skills/research/research-paper-writing/references/autoreason-methodology.md +394 -0
  1456. package/skills/research/research-paper-writing/references/checklists.md +434 -0
  1457. package/skills/research/research-paper-writing/references/citation-workflow.md +564 -0
  1458. package/skills/research/research-paper-writing/references/experiment-patterns.md +728 -0
  1459. package/skills/research/research-paper-writing/references/human-evaluation.md +476 -0
  1460. package/skills/research/research-paper-writing/references/paper-types.md +481 -0
  1461. package/skills/research/research-paper-writing/references/reviewer-guidelines.md +433 -0
  1462. package/skills/research/research-paper-writing/references/sources.md +191 -0
  1463. package/skills/research/research-paper-writing/references/writing-guide.md +474 -0
  1464. package/skills/research/research-paper-writing/templates/README.md +251 -0
  1465. package/skills/research/research-paper-writing/templates/aaai2026/README.md +534 -0
  1466. package/skills/research/research-paper-writing/templates/aaai2026/aaai2026-unified-supp.tex +144 -0
  1467. package/skills/research/research-paper-writing/templates/aaai2026/aaai2026-unified-template.tex +952 -0
  1468. package/skills/research/research-paper-writing/templates/aaai2026/aaai2026.bib +111 -0
  1469. package/skills/research/research-paper-writing/templates/aaai2026/aaai2026.bst +1493 -0
  1470. package/skills/research/research-paper-writing/templates/aaai2026/aaai2026.sty +315 -0
  1471. package/skills/research/research-paper-writing/templates/acl/README.md +50 -0
  1472. package/skills/research/research-paper-writing/templates/acl/acl.sty +312 -0
  1473. package/skills/research/research-paper-writing/templates/acl/acl_latex.tex +377 -0
  1474. package/skills/research/research-paper-writing/templates/acl/acl_lualatex.tex +101 -0
  1475. package/skills/research/research-paper-writing/templates/acl/acl_natbib.bst +1940 -0
  1476. package/skills/research/research-paper-writing/templates/acl/anthology.bib.txt +26 -0
  1477. package/skills/research/research-paper-writing/templates/acl/custom.bib +70 -0
  1478. package/skills/research/research-paper-writing/templates/acl/formatting.md +326 -0
  1479. package/skills/research/research-paper-writing/templates/colm2025/README.md +3 -0
  1480. package/skills/research/research-paper-writing/templates/colm2025/colm2025_conference.bib +11 -0
  1481. package/skills/research/research-paper-writing/templates/colm2025/colm2025_conference.bst +1440 -0
  1482. package/skills/research/research-paper-writing/templates/colm2025/colm2025_conference.pdf +0 -0
  1483. package/skills/research/research-paper-writing/templates/colm2025/colm2025_conference.sty +218 -0
  1484. package/skills/research/research-paper-writing/templates/colm2025/colm2025_conference.tex +305 -0
  1485. package/skills/research/research-paper-writing/templates/colm2025/fancyhdr.sty +485 -0
  1486. package/skills/research/research-paper-writing/templates/colm2025/math_commands.tex +508 -0
  1487. package/skills/research/research-paper-writing/templates/colm2025/natbib.sty +1246 -0
  1488. package/skills/research/research-paper-writing/templates/iclr2026/fancyhdr.sty +485 -0
  1489. package/skills/research/research-paper-writing/templates/iclr2026/iclr2026_conference.bib +24 -0
  1490. package/skills/research/research-paper-writing/templates/iclr2026/iclr2026_conference.bst +1440 -0
  1491. package/skills/research/research-paper-writing/templates/iclr2026/iclr2026_conference.pdf +0 -0
  1492. package/skills/research/research-paper-writing/templates/iclr2026/iclr2026_conference.sty +246 -0
  1493. package/skills/research/research-paper-writing/templates/iclr2026/iclr2026_conference.tex +414 -0
  1494. package/skills/research/research-paper-writing/templates/iclr2026/math_commands.tex +508 -0
  1495. package/skills/research/research-paper-writing/templates/iclr2026/natbib.sty +1246 -0
  1496. package/skills/research/research-paper-writing/templates/icml2026/algorithm.sty +79 -0
  1497. package/skills/research/research-paper-writing/templates/icml2026/algorithmic.sty +201 -0
  1498. package/skills/research/research-paper-writing/templates/icml2026/example_paper.bib +75 -0
  1499. package/skills/research/research-paper-writing/templates/icml2026/example_paper.pdf +0 -0
  1500. package/skills/research/research-paper-writing/templates/icml2026/example_paper.tex +662 -0
  1501. package/skills/research/research-paper-writing/templates/icml2026/fancyhdr.sty +864 -0
  1502. package/skills/research/research-paper-writing/templates/icml2026/icml2026.bst +1443 -0
  1503. package/skills/research/research-paper-writing/templates/icml2026/icml2026.sty +767 -0
  1504. package/skills/research/research-paper-writing/templates/icml2026/icml_numpapers.pdf +0 -0
  1505. package/skills/research/research-paper-writing/templates/neurips2025/Makefile +36 -0
  1506. package/skills/research/research-paper-writing/templates/neurips2025/extra_pkgs.tex +53 -0
  1507. package/skills/research/research-paper-writing/templates/neurips2025/main.tex +38 -0
  1508. package/skills/research/research-paper-writing/templates/neurips2025/neurips.sty +382 -0
  1509. package/skills/smart-home/DESCRIPTION.md +3 -0
  1510. package/skills/smart-home/openhue/SKILL.md +109 -0
  1511. package/skills/social-media/DESCRIPTION.md +3 -0
  1512. package/skills/social-media/xurl/SKILL.md +414 -0
  1513. package/skills/software-development/debugging-hermes-tui-commands/SKILL.md +152 -0
  1514. package/skills/software-development/hermes-agent-skill-authoring/SKILL.md +165 -0
  1515. package/skills/software-development/node-inspect-debugger/SKILL.md +319 -0
  1516. package/skills/software-development/plan/SKILL.md +58 -0
  1517. package/skills/software-development/python-debugpy/SKILL.md +375 -0
  1518. package/skills/software-development/requesting-code-review/SKILL.md +280 -0
  1519. package/skills/software-development/spike/SKILL.md +197 -0
  1520. package/skills/software-development/subagent-driven-development/SKILL.md +352 -0
  1521. package/skills/software-development/subagent-driven-development/references/context-budget-discipline.md +53 -0
  1522. package/skills/software-development/subagent-driven-development/references/gates-taxonomy.md +93 -0
  1523. package/skills/software-development/systematic-debugging/SKILL.md +367 -0
  1524. package/skills/software-development/test-driven-development/SKILL.md +343 -0
  1525. package/skills/software-development/writing-plans/SKILL.md +297 -0
  1526. package/skills/yuanbao/SKILL.md +108 -0
  1527. package/tools/__init__.py +25 -0
  1528. package/tools/__pycache__/__init__.cpython-312.pyc +0 -0
  1529. package/tools/__pycache__/approval.cpython-312.pyc +0 -0
  1530. package/tools/__pycache__/binary_extensions.cpython-312.pyc +0 -0
  1531. package/tools/__pycache__/browser_camofox.cpython-312.pyc +0 -0
  1532. package/tools/__pycache__/browser_camofox_state.cpython-312.pyc +0 -0
  1533. package/tools/__pycache__/browser_cdp_tool.cpython-312.pyc +0 -0
  1534. package/tools/__pycache__/browser_dialog_tool.cpython-312.pyc +0 -0
  1535. package/tools/__pycache__/browser_supervisor.cpython-312.pyc +0 -0
  1536. package/tools/__pycache__/browser_tool.cpython-312.pyc +0 -0
  1537. package/tools/__pycache__/budget_config.cpython-312.pyc +0 -0
  1538. package/tools/__pycache__/checkpoint_manager.cpython-312.pyc +0 -0
  1539. package/tools/__pycache__/clarify_gateway.cpython-312.pyc +0 -0
  1540. package/tools/__pycache__/clarify_tool.cpython-312.pyc +0 -0
  1541. package/tools/__pycache__/code_execution_tool.cpython-312.pyc +0 -0
  1542. package/tools/__pycache__/computer_use_tool.cpython-312.pyc +0 -0
  1543. package/tools/__pycache__/cronjob_tools.cpython-312.pyc +0 -0
  1544. package/tools/__pycache__/debug_helpers.cpython-312.pyc +0 -0
  1545. package/tools/__pycache__/delegate_tool.cpython-312.pyc +0 -0
  1546. package/tools/__pycache__/discord_tool.cpython-312.pyc +0 -0
  1547. package/tools/__pycache__/feishu_doc_tool.cpython-312.pyc +0 -0
  1548. package/tools/__pycache__/feishu_drive_tool.cpython-312.pyc +0 -0
  1549. package/tools/__pycache__/file_operations.cpython-312.pyc +0 -0
  1550. package/tools/__pycache__/file_state.cpython-312.pyc +0 -0
  1551. package/tools/__pycache__/file_tools.cpython-312.pyc +0 -0
  1552. package/tools/__pycache__/homeassistant_tool.cpython-312.pyc +0 -0
  1553. package/tools/__pycache__/image_generation_tool.cpython-312.pyc +0 -0
  1554. package/tools/__pycache__/interrupt.cpython-312.pyc +0 -0
  1555. package/tools/__pycache__/kanban_tools.cpython-312.pyc +0 -0
  1556. package/tools/__pycache__/lazy_deps.cpython-312.pyc +0 -0
  1557. package/tools/__pycache__/managed_tool_gateway.cpython-312.pyc +0 -0
  1558. package/tools/__pycache__/mcp_tool.cpython-312.pyc +0 -0
  1559. package/tools/__pycache__/memory_tool.cpython-312.pyc +0 -0
  1560. package/tools/__pycache__/mixture_of_agents_tool.cpython-312.pyc +0 -0
  1561. package/tools/__pycache__/openrouter_client.cpython-312.pyc +0 -0
  1562. package/tools/__pycache__/process_registry.cpython-312.pyc +0 -0
  1563. package/tools/__pycache__/registry.cpython-312.pyc +0 -0
  1564. package/tools/__pycache__/schema_sanitizer.cpython-312.pyc +0 -0
  1565. package/tools/__pycache__/send_message_tool.cpython-312.pyc +0 -0
  1566. package/tools/__pycache__/session_search_tool.cpython-312.pyc +0 -0
  1567. package/tools/__pycache__/skill_manager_tool.cpython-312.pyc +0 -0
  1568. package/tools/__pycache__/skill_provenance.cpython-312.pyc +0 -0
  1569. package/tools/__pycache__/skill_usage.cpython-312.pyc +0 -0
  1570. package/tools/__pycache__/skills_guard.cpython-312.pyc +0 -0
  1571. package/tools/__pycache__/skills_sync.cpython-312.pyc +0 -0
  1572. package/tools/__pycache__/skills_tool.cpython-312.pyc +0 -0
  1573. package/tools/__pycache__/slash_confirm.cpython-312.pyc +0 -0
  1574. package/tools/__pycache__/terminal_tool.cpython-312.pyc +0 -0
  1575. package/tools/__pycache__/tirith_security.cpython-312.pyc +0 -0
  1576. package/tools/__pycache__/todo_tool.cpython-312.pyc +0 -0
  1577. package/tools/__pycache__/tool_backend_helpers.cpython-312.pyc +0 -0
  1578. package/tools/__pycache__/tool_result_storage.cpython-312.pyc +0 -0
  1579. package/tools/__pycache__/tts_tool.cpython-312.pyc +0 -0
  1580. package/tools/__pycache__/url_safety.cpython-312.pyc +0 -0
  1581. package/tools/__pycache__/video_generation_tool.cpython-312.pyc +0 -0
  1582. package/tools/__pycache__/vision_tools.cpython-312.pyc +0 -0
  1583. package/tools/__pycache__/voice_mode.cpython-312.pyc +0 -0
  1584. package/tools/__pycache__/web_tools.cpython-312.pyc +0 -0
  1585. package/tools/__pycache__/website_policy.cpython-312.pyc +0 -0
  1586. package/tools/__pycache__/x_search_tool.cpython-312.pyc +0 -0
  1587. package/tools/__pycache__/xai_http.cpython-312.pyc +0 -0
  1588. package/tools/__pycache__/yuanbao_tools.cpython-312.pyc +0 -0
  1589. package/tools/ansi_strip.py +44 -0
  1590. package/tools/approval.py +1392 -0
  1591. package/tools/binary_extensions.py +42 -0
  1592. package/tools/browser_camofox.py +700 -0
  1593. package/tools/browser_camofox_state.py +48 -0
  1594. package/tools/browser_cdp_tool.py +569 -0
  1595. package/tools/browser_dialog_tool.py +148 -0
  1596. package/tools/browser_providers/__init__.py +10 -0
  1597. package/tools/browser_providers/__pycache__/__init__.cpython-312.pyc +0 -0
  1598. package/tools/browser_providers/__pycache__/base.cpython-312.pyc +0 -0
  1599. package/tools/browser_providers/__pycache__/browser_use.cpython-312.pyc +0 -0
  1600. package/tools/browser_providers/__pycache__/browserbase.cpython-312.pyc +0 -0
  1601. package/tools/browser_providers/__pycache__/firecrawl.cpython-312.pyc +0 -0
  1602. package/tools/browser_providers/base.py +59 -0
  1603. package/tools/browser_providers/browser_use.py +225 -0
  1604. package/tools/browser_providers/browserbase.py +222 -0
  1605. package/tools/browser_providers/firecrawl.py +112 -0
  1606. package/tools/browser_supervisor.py +1457 -0
  1607. package/tools/browser_tool.py +3676 -0
  1608. package/tools/budget_config.py +51 -0
  1609. package/tools/checkpoint_manager.py +1639 -0
  1610. package/tools/clarify_gateway.py +278 -0
  1611. package/tools/clarify_tool.py +141 -0
  1612. package/tools/code_execution_tool.py +1782 -0
  1613. package/tools/computer_use/__init__.py +43 -0
  1614. package/tools/computer_use/__pycache__/__init__.cpython-312.pyc +0 -0
  1615. package/tools/computer_use/__pycache__/backend.cpython-312.pyc +0 -0
  1616. package/tools/computer_use/__pycache__/schema.cpython-312.pyc +0 -0
  1617. package/tools/computer_use/__pycache__/tool.cpython-312.pyc +0 -0
  1618. package/tools/computer_use/backend.py +150 -0
  1619. package/tools/computer_use/cua_backend.py +682 -0
  1620. package/tools/computer_use/schema.py +191 -0
  1621. package/tools/computer_use/tool.py +521 -0
  1622. package/tools/computer_use_tool.py +39 -0
  1623. package/tools/credential_files.py +437 -0
  1624. package/tools/cronjob_tools.py +719 -0
  1625. package/tools/debug_helpers.py +106 -0
  1626. package/tools/delegate_tool.py +2797 -0
  1627. package/tools/discord_tool.py +959 -0
  1628. package/tools/env_passthrough.py +145 -0
  1629. package/tools/environments/__init__.py +14 -0
  1630. package/tools/environments/__pycache__/__init__.cpython-312.pyc +0 -0
  1631. package/tools/environments/__pycache__/base.cpython-312.pyc +0 -0
  1632. package/tools/environments/__pycache__/docker.cpython-312.pyc +0 -0
  1633. package/tools/environments/__pycache__/file_sync.cpython-312.pyc +0 -0
  1634. package/tools/environments/__pycache__/local.cpython-312.pyc +0 -0
  1635. package/tools/environments/__pycache__/managed_modal.cpython-312.pyc +0 -0
  1636. package/tools/environments/__pycache__/modal.cpython-312.pyc +0 -0
  1637. package/tools/environments/__pycache__/modal_utils.cpython-312.pyc +0 -0
  1638. package/tools/environments/__pycache__/singularity.cpython-312.pyc +0 -0
  1639. package/tools/environments/__pycache__/ssh.cpython-312.pyc +0 -0
  1640. package/tools/environments/base.py +844 -0
  1641. package/tools/environments/daytona.py +270 -0
  1642. package/tools/environments/docker.py +656 -0
  1643. package/tools/environments/file_sync.py +400 -0
  1644. package/tools/environments/local.py +658 -0
  1645. package/tools/environments/managed_modal.py +282 -0
  1646. package/tools/environments/modal.py +479 -0
  1647. package/tools/environments/modal_utils.py +199 -0
  1648. package/tools/environments/singularity.py +263 -0
  1649. package/tools/environments/ssh.py +295 -0
  1650. package/tools/environments/vercel_sandbox.py +655 -0
  1651. package/tools/feishu_doc_tool.py +138 -0
  1652. package/tools/feishu_drive_tool.py +431 -0
  1653. package/tools/file_operations.py +1825 -0
  1654. package/tools/file_state.py +332 -0
  1655. package/tools/file_tools.py +1172 -0
  1656. package/tools/fuzzy_match.py +703 -0
  1657. package/tools/homeassistant_tool.py +513 -0
  1658. package/tools/image_generation_tool.py +1098 -0
  1659. package/tools/interrupt.py +98 -0
  1660. package/tools/kanban_tools.py +1139 -0
  1661. package/tools/lazy_deps.py +608 -0
  1662. package/tools/managed_tool_gateway.py +168 -0
  1663. package/tools/mcp_oauth.py +633 -0
  1664. package/tools/mcp_oauth_manager.py +607 -0
  1665. package/tools/mcp_tool.py +3483 -0
  1666. package/tools/memory_tool.py +584 -0
  1667. package/tools/microsoft_graph_auth.py +245 -0
  1668. package/tools/microsoft_graph_client.py +408 -0
  1669. package/tools/mixture_of_agents_tool.py +542 -0
  1670. package/tools/neutts_samples/jo.txt +1 -0
  1671. package/tools/neutts_samples/jo.wav +0 -0
  1672. package/tools/neutts_synth.py +104 -0
  1673. package/tools/openrouter_client.py +33 -0
  1674. package/tools/osv_check.py +155 -0
  1675. package/tools/patch_parser.py +592 -0
  1676. package/tools/path_security.py +43 -0
  1677. package/tools/process_registry.py +1534 -0
  1678. package/tools/registry.py +589 -0
  1679. package/tools/schema_sanitizer.py +370 -0
  1680. package/tools/send_message_tool.py +1900 -0
  1681. package/tools/session_search_tool.py +613 -0
  1682. package/tools/skill_manager_tool.py +932 -0
  1683. package/tools/skill_provenance.py +78 -0
  1684. package/tools/skill_usage.py +610 -0
  1685. package/tools/skills_guard.py +932 -0
  1686. package/tools/skills_hub.py +3263 -0
  1687. package/tools/skills_sync.py +432 -0
  1688. package/tools/skills_tool.py +1569 -0
  1689. package/tools/slash_confirm.py +167 -0
  1690. package/tools/terminal_tool.py +2376 -0
  1691. package/tools/tirith_security.py +775 -0
  1692. package/tools/todo_tool.py +277 -0
  1693. package/tools/tool_backend_helpers.py +144 -0
  1694. package/tools/tool_output_limits.py +92 -0
  1695. package/tools/tool_result_storage.py +232 -0
  1696. package/tools/transcription_tools.py +936 -0
  1697. package/tools/tts_tool.py +2285 -0
  1698. package/tools/url_safety.py +330 -0
  1699. package/tools/video_generation_tool.py +561 -0
  1700. package/tools/vision_tools.py +1422 -0
  1701. package/tools/voice_mode.py +1019 -0
  1702. package/tools/web_tools.py +1551 -0
  1703. package/tools/website_policy.py +283 -0
  1704. package/tools/x_search_tool.py +424 -0
  1705. package/tools/xai_http.py +83 -0
  1706. package/tools/yuanbao_tools.py +736 -0
  1707. package/toolset_distributions.py +364 -0
  1708. package/toolsets.py +866 -0
  1709. package/trajectory_compressor.py +1509 -0
  1710. package/tui_gateway/__init__.py +0 -0
  1711. package/tui_gateway/entry.py +251 -0
  1712. package/tui_gateway/event_publisher.py +126 -0
  1713. package/tui_gateway/render.py +49 -0
  1714. package/tui_gateway/server.py +6623 -0
  1715. package/tui_gateway/slash_worker.py +76 -0
  1716. package/tui_gateway/transport.py +219 -0
  1717. package/tui_gateway/ws.py +178 -0
  1718. package/utils.py +361 -0
@@ -0,0 +1,1612 @@
1
+ """Kanban dashboard plugin — backend API routes.
2
+
3
+ Mounted at /api/plugins/kanban/ by the dashboard plugin system.
4
+
5
+ This layer is intentionally thin: every handler is a small wrapper around
6
+ ``hermes_cli.kanban_db`` or a direct SQL query. Writes use the same code
7
+ paths the CLI and gateway ``/kanban`` command use, so the three surfaces
8
+ cannot drift.
9
+
10
+ Live updates arrive via the ``/events`` WebSocket, which tails the
11
+ append-only ``task_events`` table on a short poll interval (WAL mode lets
12
+ reads run alongside the dispatcher's IMMEDIATE write transactions).
13
+
14
+ Security note
15
+ -------------
16
+ Plugin HTTP routes go through the dashboard's session-token auth middleware
17
+ (``web_server.auth_middleware``) just like core API routes — every
18
+ ``/api/plugins/...`` request must present the session bearer token (or the
19
+ session cookie set when you load the dashboard HTML). The token is the
20
+ random per-process ``_SESSION_TOKEN`` printed at startup; the dashboard's
21
+ own pages inject it via ``window.__HERMES_SESSION_TOKEN__`` so logged-in
22
+ browsers don't have to handle it manually.
23
+
24
+ For the ``/events`` WebSocket we still require the session token as a
25
+ ``?token=`` query parameter (browsers cannot set the ``Authorization``
26
+ header on an upgrade request), matching the established pattern used by
27
+ the in-browser PTY bridge in ``hermes_cli/web_server.py``.
28
+
29
+ This means ``hermes dashboard --host 0.0.0.0`` is safe to run on a LAN:
30
+ plugin routes are no longer an unauthenticated exception. The auth still
31
+ isn't multi-user — anyone who can read the printed URL+token gets full
32
+ dashboard access — but they can't ride along just because they can reach
33
+ the port.
34
+ """
35
+
36
+ from __future__ import annotations
37
+
38
+ import asyncio
39
+ import hmac
40
+ import json
41
+ import logging
42
+ import os
43
+ import sqlite3
44
+ import time
45
+ from dataclasses import asdict
46
+ from typing import Any, Optional
47
+
48
+ from fastapi import APIRouter, HTTPException, Query, WebSocket, WebSocketDisconnect, status as http_status
49
+ from pydantic import BaseModel, Field
50
+
51
+ from hermes_cli import kanban_db
52
+
53
+ log = logging.getLogger(__name__)
54
+
55
+ router = APIRouter()
56
+
57
+
58
+ # ---------------------------------------------------------------------------
59
+ # Auth helper — WebSocket only (HTTP routes live behind the dashboard's
60
+ # existing plugin-bypass; this is documented above).
61
+ # ---------------------------------------------------------------------------
62
+
63
+ def _check_ws_token(provided: Optional[str]) -> bool:
64
+ """Constant-time compare against the dashboard session token.
65
+
66
+ Imported lazily so the plugin still loads in test contexts where the
67
+ dashboard web_server module isn't importable (e.g. the bare-FastAPI
68
+ test harness).
69
+ """
70
+ if not provided:
71
+ return False
72
+ try:
73
+ from hermes_cli import web_server as _ws
74
+ except Exception:
75
+ # No dashboard context (tests). Accept so the tail loop is still
76
+ # testable; in production the dashboard module always imports
77
+ # cleanly because it's the caller.
78
+ return True
79
+ expected = getattr(_ws, "_SESSION_TOKEN", None)
80
+ if not expected:
81
+ return True
82
+ return hmac.compare_digest(str(provided), str(expected))
83
+
84
+
85
+ def _resolve_board(board: Optional[str]) -> Optional[str]:
86
+ """Validate and normalise a board slug from a query param.
87
+
88
+ Raises :class:`HTTPException` 400 on malformed slugs so the browser
89
+ sees a clean error instead of a 500. Returns the normalised slug,
90
+ or ``None`` when the caller omitted the param (which then falls
91
+ through to the active board inside ``kb.connect()``).
92
+ """
93
+ if board is None or board == "":
94
+ return None
95
+ try:
96
+ normed = kanban_db._normalize_board_slug(board)
97
+ except ValueError as exc:
98
+ raise HTTPException(status_code=400, detail=str(exc))
99
+ if normed and normed != kanban_db.DEFAULT_BOARD and not kanban_db.board_exists(normed):
100
+ raise HTTPException(
101
+ status_code=404,
102
+ detail=f"board {normed!r} does not exist",
103
+ )
104
+ return normed
105
+
106
+
107
+ def _conn(board: Optional[str] = None):
108
+ """Open a kanban_db connection, creating the schema on first use.
109
+
110
+ Every handler that mutates the DB goes through this so the plugin
111
+ self-heals on a fresh install (no user-visible "no such table"
112
+ error if somebody hits POST /tasks before GET /board).
113
+ ``init_db`` is idempotent.
114
+
115
+ ``board`` is the query-param slug (already normalised by
116
+ :func:`_resolve_board`). When ``None`` the active board is used
117
+ via the resolution chain (env var → ``current`` file → ``default``).
118
+ """
119
+ try:
120
+ kanban_db.init_db(board=board)
121
+ except Exception as exc:
122
+ log.warning("kanban init_db failed: %s", exc)
123
+ return kanban_db.connect(board=board)
124
+
125
+
126
+ # ---------------------------------------------------------------------------
127
+ # Serialization helpers
128
+ # ---------------------------------------------------------------------------
129
+
130
+ # Columns shown by the dashboard, in left-to-right order. "archived" is
131
+ # available via a filter toggle rather than a visible column.
132
+ BOARD_COLUMNS: list[str] = [
133
+ "triage", "todo", "ready", "running", "blocked", "done",
134
+ ]
135
+
136
+
137
+ _CARD_SUMMARY_PREVIEW_CHARS = 200
138
+
139
+
140
+ def _task_dict(
141
+ task: kanban_db.Task,
142
+ *,
143
+ latest_summary: Optional[str] = None,
144
+ ) -> dict[str, Any]:
145
+ d = asdict(task)
146
+ # Add derived age metrics so the UI can colour stale cards without
147
+ # computing deltas client-side.
148
+ try:
149
+ d["age"] = kanban_db.task_age(task)
150
+ except Exception:
151
+ d["age"] = {"created_age_seconds": None, "started_age_seconds": None, "time_to_complete_seconds": None}
152
+ # Surface the latest non-null run summary so dashboards don't show
153
+ # blank cards/drawers for tasks where the worker handed off via
154
+ # ``task_runs.summary`` (the kanban-worker pattern) instead of
155
+ # ``tasks.result``. ``None`` when no run has produced a summary yet.
156
+ d["latest_summary"] = latest_summary
157
+ # Keep body short on list endpoints; full body comes from /tasks/:id.
158
+ return d
159
+
160
+
161
+ def _event_dict(event: kanban_db.Event) -> dict[str, Any]:
162
+ return {
163
+ "id": event.id,
164
+ "task_id": event.task_id,
165
+ "kind": event.kind,
166
+ "payload": event.payload,
167
+ "created_at": event.created_at,
168
+ "run_id": event.run_id,
169
+ }
170
+
171
+
172
+ def _comment_dict(c: kanban_db.Comment) -> dict[str, Any]:
173
+ return {
174
+ "id": c.id,
175
+ "task_id": c.task_id,
176
+ "author": c.author,
177
+ "body": c.body,
178
+ "created_at": c.created_at,
179
+ }
180
+
181
+
182
+ def _run_dict(r: kanban_db.Run) -> dict[str, Any]:
183
+ """Serialise a Run for the drawer's Run history section."""
184
+ return {
185
+ "id": r.id,
186
+ "task_id": r.task_id,
187
+ "profile": r.profile,
188
+ "step_key": r.step_key,
189
+ "status": r.status,
190
+ "claim_lock": r.claim_lock,
191
+ "claim_expires": r.claim_expires,
192
+ "worker_pid": r.worker_pid,
193
+ "max_runtime_seconds": r.max_runtime_seconds,
194
+ "last_heartbeat_at": r.last_heartbeat_at,
195
+ "started_at": r.started_at,
196
+ "ended_at": r.ended_at,
197
+ "outcome": r.outcome,
198
+ "summary": r.summary,
199
+ "metadata": r.metadata,
200
+ "error": r.error,
201
+ }
202
+
203
+
204
+ # Hallucination-warning event kinds — see complete_task() in kanban_db.py.
205
+ # completion_blocked_hallucination: kernel rejected created_cards with
206
+ # phantom ids; task stays in prior state.
207
+ # suspected_hallucinated_references: prose scan found t_<hex> in summary
208
+ # that doesn't resolve; completion succeeded, advisory only.
209
+ _WARNING_EVENT_KINDS = (
210
+ "completion_blocked_hallucination",
211
+ "suspected_hallucinated_references",
212
+ )
213
+
214
+
215
+ def _compute_task_diagnostics(
216
+ conn: sqlite3.Connection,
217
+ task_ids: Optional[list[str]] = None,
218
+ ) -> dict[str, list[dict]]:
219
+ """Run the diagnostic rule engine against every task (or a subset)
220
+ and return ``{task_id: [diagnostic_dict, ...]}``.
221
+
222
+ Tasks with no active diagnostics are omitted from the result.
223
+ Uses ``hermes_cli.kanban_diagnostics`` — see that module for the
224
+ rule definitions.
225
+ """
226
+ from hermes_cli import kanban_diagnostics as kd
227
+
228
+ # Build the candidate task list. We need each task's row + its
229
+ # events + its runs. Doing N separate queries works but scales
230
+ # poorly; do three aggregate queries instead.
231
+ if task_ids is not None:
232
+ if not task_ids:
233
+ return {}
234
+ placeholders = ",".join(["?"] * len(task_ids))
235
+ rows = conn.execute(
236
+ f"SELECT * FROM tasks WHERE id IN ({placeholders})",
237
+ tuple(task_ids),
238
+ ).fetchall()
239
+ else:
240
+ rows = conn.execute(
241
+ "SELECT * FROM tasks WHERE status != 'archived'",
242
+ ).fetchall()
243
+
244
+ if not rows:
245
+ return {}
246
+
247
+ # Index events + runs by task id. For very large boards this will
248
+ # slurp a lot — acceptable on the dashboard's typical working set
249
+ # (hundreds of tasks), but we can add pagination / filtering later
250
+ # if profiling shows it's a hotspot.
251
+ row_ids = [r["id"] for r in rows]
252
+ placeholders = ",".join(["?"] * len(row_ids))
253
+ events_by_task: dict[str, list] = {tid: [] for tid in row_ids}
254
+ for ev_row in conn.execute(
255
+ f"SELECT * FROM task_events WHERE task_id IN ({placeholders}) ORDER BY id",
256
+ tuple(row_ids),
257
+ ).fetchall():
258
+ events_by_task.setdefault(ev_row["task_id"], []).append(ev_row)
259
+ runs_by_task: dict[str, list] = {tid: [] for tid in row_ids}
260
+ for run_row in conn.execute(
261
+ f"SELECT * FROM task_runs WHERE task_id IN ({placeholders}) ORDER BY id",
262
+ tuple(row_ids),
263
+ ).fetchall():
264
+ runs_by_task.setdefault(run_row["task_id"], []).append(run_row)
265
+
266
+ out: dict[str, list[dict]] = {}
267
+ for r in rows:
268
+ tid = r["id"]
269
+ diags = kd.compute_task_diagnostics(
270
+ r,
271
+ events_by_task.get(tid, []),
272
+ runs_by_task.get(tid, []),
273
+ )
274
+ if diags:
275
+ out[tid] = [d.to_dict() for d in diags]
276
+ return out
277
+
278
+
279
+ def _warnings_summary_from_diagnostics(
280
+ diagnostics: list[dict],
281
+ ) -> Optional[dict]:
282
+ """Compact summary for cards: {count, highest_severity, kinds,
283
+ latest_at}. Replaces the old hallucination-only ``warnings`` object
284
+ — same shape additions plus ``highest_severity`` so the UI can color
285
+ badges per diagnostic severity.
286
+
287
+ Returns None when ``diagnostics`` is empty.
288
+ """
289
+ if not diagnostics:
290
+ return None
291
+ from hermes_cli.kanban_diagnostics import SEVERITY_ORDER
292
+
293
+ kinds: dict[str, int] = {}
294
+ latest = 0
295
+ highest_idx = -1
296
+ highest_sev: Optional[str] = None
297
+ count = 0
298
+ for d in diagnostics:
299
+ kinds[d["kind"]] = kinds.get(d["kind"], 0) + d.get("count", 1)
300
+ count += d.get("count", 1)
301
+ la = d.get("last_seen_at") or 0
302
+ if la > latest:
303
+ latest = la
304
+ sev = d.get("severity")
305
+ if sev in SEVERITY_ORDER:
306
+ idx = SEVERITY_ORDER.index(sev)
307
+ if idx > highest_idx:
308
+ highest_idx = idx
309
+ highest_sev = sev
310
+ return {
311
+ "count": count,
312
+ "kinds": kinds,
313
+ "latest_at": latest,
314
+ "highest_severity": highest_sev,
315
+ }
316
+
317
+
318
+ def _links_for(conn: sqlite3.Connection, task_id: str) -> dict[str, list[str]]:
319
+ """Return {'parents': [...], 'children': [...]} for a task."""
320
+ parents = [
321
+ r["parent_id"]
322
+ for r in conn.execute(
323
+ "SELECT parent_id FROM task_links WHERE child_id = ? ORDER BY parent_id",
324
+ (task_id,),
325
+ )
326
+ ]
327
+ children = [
328
+ r["child_id"]
329
+ for r in conn.execute(
330
+ "SELECT child_id FROM task_links WHERE parent_id = ? ORDER BY child_id",
331
+ (task_id,),
332
+ )
333
+ ]
334
+ return {"parents": parents, "children": children}
335
+
336
+
337
+ # ---------------------------------------------------------------------------
338
+ # GET /board
339
+ # ---------------------------------------------------------------------------
340
+
341
+ @router.get("/board")
342
+ def get_board(
343
+ tenant: Optional[str] = Query(None, description="Filter to a single tenant"),
344
+ include_archived: bool = Query(False),
345
+ board: Optional[str] = Query(None, description="Kanban board slug (omit for current)"),
346
+ ):
347
+ """Return the full board grouped by status column.
348
+
349
+ ``_conn()`` auto-initializes ``kanban.db`` on first call so a fresh
350
+ install doesn't surface a "failed to load" error on the plugin tab.
351
+
352
+ ``board`` selects which board to read from. Omitting it falls
353
+ through to the active board (``HERMES_KANBAN_BOARD`` env → on-disk
354
+ ``current`` pointer → ``default``).
355
+ """
356
+ board = _resolve_board(board)
357
+ conn = _conn(board=board)
358
+ try:
359
+ tasks = kanban_db.list_tasks(
360
+ conn, tenant=tenant, include_archived=include_archived
361
+ )
362
+ # Pre-fetch link counts per task (cheap: one query).
363
+ link_counts: dict[str, dict[str, int]] = {}
364
+ for row in conn.execute(
365
+ "SELECT parent_id, child_id FROM task_links"
366
+ ).fetchall():
367
+ link_counts.setdefault(row["parent_id"], {"parents": 0, "children": 0})[
368
+ "children"
369
+ ] += 1
370
+ link_counts.setdefault(row["child_id"], {"parents": 0, "children": 0})[
371
+ "parents"
372
+ ] += 1
373
+
374
+ # Comment + event counts (both cheap aggregates).
375
+ comment_counts: dict[str, int] = {
376
+ r["task_id"]: r["n"]
377
+ for r in conn.execute(
378
+ "SELECT task_id, COUNT(*) AS n FROM task_comments GROUP BY task_id"
379
+ )
380
+ }
381
+
382
+ # Progress rollup: for each parent, how many children are done / total.
383
+ # One pass over task_links joined with child status — cheaper than
384
+ # N per-task queries and the plugin uses it to render "N/M".
385
+ progress: dict[str, dict[str, int]] = {}
386
+ for row in conn.execute(
387
+ "SELECT l.parent_id AS pid, t.status AS cstatus "
388
+ "FROM task_links l JOIN tasks t ON t.id = l.child_id"
389
+ ).fetchall():
390
+ p = progress.setdefault(row["pid"], {"done": 0, "total": 0})
391
+ p["total"] += 1
392
+ if row["cstatus"] == "done":
393
+ p["done"] += 1
394
+
395
+ # Diagnostics rollup for this board — see kanban_diagnostics.
396
+ # We get the full structured list per task AND a compact
397
+ # summary for the card badge (so cards don't carry the detail
398
+ # text; the drawer fetches that via /tasks/:id or /diagnostics).
399
+ diagnostics_per_task = _compute_task_diagnostics(conn, task_ids=None)
400
+
401
+ latest_event_id = conn.execute(
402
+ "SELECT COALESCE(MAX(id), 0) AS m FROM task_events"
403
+ ).fetchone()["m"]
404
+
405
+ columns: dict[str, list[dict]] = {c: [] for c in BOARD_COLUMNS}
406
+ if include_archived:
407
+ columns["archived"] = []
408
+
409
+ # Batch-fetch the latest non-null run summary per task in one
410
+ # window-function query (avoids N+1 ``latest_summary`` calls
411
+ # for boards with hundreds of tasks). Truncated to a card-size
412
+ # preview here — the full text is available via /tasks/:id.
413
+ summary_map = kanban_db.latest_summaries(conn, [t.id for t in tasks])
414
+
415
+ for t in tasks:
416
+ full = summary_map.get(t.id)
417
+ preview = (
418
+ full[:_CARD_SUMMARY_PREVIEW_CHARS] if full else None
419
+ )
420
+ d = _task_dict(t, latest_summary=preview)
421
+ d["link_counts"] = link_counts.get(t.id, {"parents": 0, "children": 0})
422
+ d["comment_count"] = comment_counts.get(t.id, 0)
423
+ d["progress"] = progress.get(t.id) # None when the task has no children
424
+ diags = diagnostics_per_task.get(t.id)
425
+ if diags:
426
+ # Full list goes into the payload so the drawer can render
427
+ # without a second round-trip. The board-level badge only
428
+ # needs the summary.
429
+ d["diagnostics"] = diags
430
+ d["warnings"] = _warnings_summary_from_diagnostics(diags)
431
+ col = t.status if t.status in columns else "todo"
432
+ columns[col].append(d)
433
+
434
+ # Stable per-column ordering already applied by list_tasks
435
+ # (priority DESC, created_at ASC), keep as-is.
436
+
437
+ # List of known tenants for the UI filter dropdown.
438
+ tenants = [
439
+ r["tenant"]
440
+ for r in conn.execute(
441
+ "SELECT DISTINCT tenant FROM tasks WHERE tenant IS NOT NULL ORDER BY tenant"
442
+ )
443
+ ]
444
+ # List of distinct assignees for the lane-by-profile sub-grouping.
445
+ assignees = [
446
+ r["assignee"]
447
+ for r in conn.execute(
448
+ "SELECT DISTINCT assignee FROM tasks WHERE assignee IS NOT NULL "
449
+ "AND status != 'archived' ORDER BY assignee"
450
+ )
451
+ ]
452
+
453
+ return {
454
+ "columns": [
455
+ {"name": name, "tasks": columns[name]} for name in columns.keys()
456
+ ],
457
+ "tenants": tenants,
458
+ "assignees": assignees,
459
+ "latest_event_id": int(latest_event_id),
460
+ "now": int(time.time()),
461
+ }
462
+ finally:
463
+ conn.close()
464
+
465
+
466
+ # ---------------------------------------------------------------------------
467
+ # GET /tasks/:id
468
+ # ---------------------------------------------------------------------------
469
+
470
+ @router.get("/tasks/{task_id}")
471
+ def get_task(task_id: str, board: Optional[str] = Query(None)):
472
+ board = _resolve_board(board)
473
+ conn = _conn(board=board)
474
+ try:
475
+ task = kanban_db.get_task(conn, task_id)
476
+ if task is None:
477
+ raise HTTPException(status_code=404, detail=f"task {task_id} not found")
478
+ # Drawer/detail view returns the FULL summary (no truncation) so
479
+ # operators can read the complete worker handoff without making
480
+ # a second round-trip. Cards on /board carry a 200-char preview.
481
+ full_summary = kanban_db.latest_summary(conn, task_id)
482
+ task_d = _task_dict(task, latest_summary=full_summary)
483
+ # Attach diagnostics so the drawer's Diagnostics section can
484
+ # render recovery actions without a second round-trip.
485
+ diags = _compute_task_diagnostics(conn, task_ids=[task_id])
486
+ diag_list = diags.get(task_id) or []
487
+ if diag_list:
488
+ task_d["diagnostics"] = diag_list
489
+ task_d["warnings"] = _warnings_summary_from_diagnostics(diag_list)
490
+ return {
491
+ "task": task_d,
492
+ "comments": [_comment_dict(c) for c in kanban_db.list_comments(conn, task_id)],
493
+ "events": [_event_dict(e) for e in kanban_db.list_events(conn, task_id)],
494
+ "links": _links_for(conn, task_id),
495
+ "runs": [_run_dict(r) for r in kanban_db.list_runs(conn, task_id)],
496
+ }
497
+ finally:
498
+ conn.close()
499
+
500
+
501
+ # ---------------------------------------------------------------------------
502
+ # POST /tasks
503
+ # ---------------------------------------------------------------------------
504
+
505
+ class CreateTaskBody(BaseModel):
506
+ title: str
507
+ body: Optional[str] = None
508
+ assignee: Optional[str] = None
509
+ tenant: Optional[str] = None
510
+ priority: int = 0
511
+ workspace_kind: str = "scratch"
512
+ workspace_path: Optional[str] = None
513
+ parents: list[str] = Field(default_factory=list)
514
+ triage: bool = False
515
+ idempotency_key: Optional[str] = None
516
+ max_runtime_seconds: Optional[int] = None
517
+ skills: Optional[list[str]] = None
518
+
519
+
520
+ @router.post("/tasks")
521
+ def create_task(payload: CreateTaskBody, board: Optional[str] = Query(None)):
522
+ board = _resolve_board(board)
523
+ conn = _conn(board=board)
524
+ try:
525
+ task_id = kanban_db.create_task(
526
+ conn,
527
+ title=payload.title,
528
+ body=payload.body,
529
+ assignee=payload.assignee,
530
+ created_by="dashboard",
531
+ workspace_kind=payload.workspace_kind,
532
+ workspace_path=payload.workspace_path,
533
+ tenant=payload.tenant,
534
+ priority=payload.priority,
535
+ parents=payload.parents,
536
+ triage=payload.triage,
537
+ idempotency_key=payload.idempotency_key,
538
+ max_runtime_seconds=payload.max_runtime_seconds,
539
+ skills=payload.skills,
540
+ )
541
+ task = kanban_db.get_task(conn, task_id)
542
+ body: dict[str, Any] = {"task": _task_dict(task) if task else None}
543
+ # Surface a dispatcher-presence warning so the UI can show a
544
+ # banner when a `ready` task would otherwise sit idle because no
545
+ # gateway is running (or dispatch_in_gateway=false). Only emit
546
+ # for ready+assigned tasks; triage/todo are expected to wait,
547
+ # and unassigned tasks can't be dispatched regardless.
548
+ if task and task.status == "ready" and task.assignee:
549
+ try:
550
+ from hermes_cli.kanban import _check_dispatcher_presence
551
+ running, message = _check_dispatcher_presence()
552
+ if not running and message:
553
+ body["warning"] = message
554
+ except Exception:
555
+ # Probe failure must never block the create itself.
556
+ pass
557
+ return body
558
+ except ValueError as e:
559
+ raise HTTPException(status_code=400, detail=str(e))
560
+ finally:
561
+ conn.close()
562
+
563
+
564
+ # ---------------------------------------------------------------------------
565
+ # PATCH /tasks/:id (status / assignee / priority / title / body)
566
+ # ---------------------------------------------------------------------------
567
+
568
+ class UpdateTaskBody(BaseModel):
569
+ status: Optional[str] = None
570
+ assignee: Optional[str] = None
571
+ priority: Optional[int] = None
572
+ title: Optional[str] = None
573
+ body: Optional[str] = None
574
+ result: Optional[str] = None
575
+ block_reason: Optional[str] = None
576
+ # Structured handoff fields — forwarded to complete_task when status
577
+ # transitions to 'done'. Dashboard parity with ``hermes kanban
578
+ # complete --summary ... --metadata ...``.
579
+ summary: Optional[str] = None
580
+ metadata: Optional[dict] = None
581
+
582
+
583
+ @router.patch("/tasks/{task_id}")
584
+ def update_task(task_id: str, payload: UpdateTaskBody, board: Optional[str] = Query(None)):
585
+ board = _resolve_board(board)
586
+ conn = _conn(board=board)
587
+ try:
588
+ task = kanban_db.get_task(conn, task_id)
589
+ if task is None:
590
+ raise HTTPException(status_code=404, detail=f"task {task_id} not found")
591
+
592
+ # --- assignee ----------------------------------------------------
593
+ if payload.assignee is not None:
594
+ try:
595
+ ok = kanban_db.assign_task(
596
+ conn, task_id, payload.assignee or None,
597
+ )
598
+ except RuntimeError as e:
599
+ raise HTTPException(status_code=409, detail=str(e))
600
+ if not ok:
601
+ raise HTTPException(status_code=404, detail="task not found")
602
+
603
+ # --- status -------------------------------------------------------
604
+ if payload.status is not None:
605
+ s = payload.status
606
+ ok = True
607
+ if s == "done":
608
+ ok = kanban_db.complete_task(
609
+ conn, task_id,
610
+ result=payload.result,
611
+ summary=payload.summary,
612
+ metadata=payload.metadata,
613
+ )
614
+ elif s == "blocked":
615
+ ok = kanban_db.block_task(conn, task_id, reason=payload.block_reason)
616
+ elif s == "ready":
617
+ # Re-open a blocked task, or just an explicit status set.
618
+ current = kanban_db.get_task(conn, task_id)
619
+ if current and current.status == "blocked":
620
+ ok = kanban_db.unblock_task(conn, task_id)
621
+ else:
622
+ # Direct status write for drag-drop (todo -> ready etc).
623
+ ok = _set_status_direct(conn, task_id, "ready")
624
+ elif s == "archived":
625
+ ok = kanban_db.archive_task(conn, task_id)
626
+ elif s == "running":
627
+ raise HTTPException(
628
+ status_code=400,
629
+ detail="Cannot set status to 'running' directly; use the dispatcher/claim path",
630
+ )
631
+ elif s in ("todo", "triage"):
632
+ ok = _set_status_direct(conn, task_id, s)
633
+ else:
634
+ raise HTTPException(status_code=400, detail=f"unknown status: {s}")
635
+ if not ok:
636
+ raise HTTPException(
637
+ status_code=409,
638
+ detail=f"status transition to {s!r} not valid from current state",
639
+ )
640
+
641
+ # --- priority -----------------------------------------------------
642
+ if payload.priority is not None:
643
+ with kanban_db.write_txn(conn):
644
+ conn.execute(
645
+ "UPDATE tasks SET priority = ? WHERE id = ?",
646
+ (int(payload.priority), task_id),
647
+ )
648
+ conn.execute(
649
+ "INSERT INTO task_events (task_id, kind, payload, created_at) "
650
+ "VALUES (?, 'reprioritized', ?, ?)",
651
+ (task_id, json.dumps({"priority": int(payload.priority)}),
652
+ int(time.time())),
653
+ )
654
+
655
+ # --- title / body -------------------------------------------------
656
+ if payload.title is not None or payload.body is not None:
657
+ with kanban_db.write_txn(conn):
658
+ sets, vals = [], []
659
+ if payload.title is not None:
660
+ if not payload.title.strip():
661
+ raise HTTPException(status_code=400, detail="title cannot be empty")
662
+ sets.append("title = ?")
663
+ vals.append(payload.title.strip())
664
+ if payload.body is not None:
665
+ sets.append("body = ?")
666
+ vals.append(payload.body)
667
+ vals.append(task_id)
668
+ conn.execute(
669
+ f"UPDATE tasks SET {', '.join(sets)} WHERE id = ?", vals,
670
+ )
671
+ conn.execute(
672
+ "INSERT INTO task_events (task_id, kind, payload, created_at) "
673
+ "VALUES (?, 'edited', NULL, ?)",
674
+ (task_id, int(time.time())),
675
+ )
676
+
677
+ updated = kanban_db.get_task(conn, task_id)
678
+ return {"task": _task_dict(updated) if updated else None}
679
+ finally:
680
+ conn.close()
681
+
682
+
683
+ def _set_status_direct(
684
+ conn: sqlite3.Connection, task_id: str, new_status: str,
685
+ ) -> bool:
686
+ """Direct status write for drag-drop moves that aren't covered by the
687
+ structured complete/block/unblock/archive verbs (e.g. todo<->ready,
688
+ running<->ready). Appends a ``status`` event row for the live feed.
689
+
690
+ When this transitions OFF ``running`` to anything other than the
691
+ terminal verbs above (which own their own run closing), we close the
692
+ active run with outcome='reclaimed' so attempt history isn't
693
+ orphaned. ``running -> ready`` via drag-drop is the common case
694
+ (user yanking a stuck worker back to the queue).
695
+ """
696
+ with kanban_db.write_txn(conn):
697
+ # Snapshot current state so we know whether to close a run.
698
+ prev = conn.execute(
699
+ "SELECT status, current_run_id FROM tasks WHERE id = ?",
700
+ (task_id,),
701
+ ).fetchone()
702
+ if prev is None:
703
+ return False
704
+
705
+ # Guard: don't allow promoting to 'ready' unless all parents are done.
706
+ # Prevents the dispatcher from spawning a child whose upstream work
707
+ # hasn't completed (e.g. T4 dispatched while T3 is still blocked).
708
+ if new_status == "ready":
709
+ parent_statuses = conn.execute(
710
+ "SELECT t.status FROM tasks t "
711
+ "JOIN task_links l ON l.parent_id = t.id "
712
+ "WHERE l.child_id = ?",
713
+ (task_id,),
714
+ ).fetchall()
715
+ if parent_statuses and not all(
716
+ p["status"] == "done" for p in parent_statuses
717
+ ):
718
+ return False
719
+
720
+ was_running = prev["status"] == "running"
721
+
722
+ cur = conn.execute(
723
+ "UPDATE tasks SET status = ?, "
724
+ " claim_lock = CASE WHEN ? = 'running' THEN claim_lock ELSE NULL END, "
725
+ " claim_expires = CASE WHEN ? = 'running' THEN claim_expires ELSE NULL END, "
726
+ " worker_pid = CASE WHEN ? = 'running' THEN worker_pid ELSE NULL END "
727
+ "WHERE id = ?",
728
+ (new_status, new_status, new_status, new_status, task_id),
729
+ )
730
+ if cur.rowcount != 1:
731
+ return False
732
+ run_id = None
733
+ if was_running and new_status != "running" and prev["current_run_id"]:
734
+ run_id = kanban_db._end_run(
735
+ conn, task_id,
736
+ outcome="reclaimed", status="reclaimed",
737
+ summary=f"status changed to {new_status} (dashboard/direct)",
738
+ )
739
+ conn.execute(
740
+ "INSERT INTO task_events (task_id, run_id, kind, payload, created_at) "
741
+ "VALUES (?, ?, 'status', ?, ?)",
742
+ (task_id, run_id, json.dumps({"status": new_status}), int(time.time())),
743
+ )
744
+ # If we re-opened something, children may have gone stale.
745
+ if new_status in ("done", "ready"):
746
+ kanban_db.recompute_ready(conn)
747
+ return True
748
+
749
+
750
+ # ---------------------------------------------------------------------------
751
+ # Comments
752
+ # ---------------------------------------------------------------------------
753
+
754
+ class CommentBody(BaseModel):
755
+ body: str
756
+ author: Optional[str] = "dashboard"
757
+
758
+
759
+ @router.post("/tasks/{task_id}/comments")
760
+ def add_comment(task_id: str, payload: CommentBody, board: Optional[str] = Query(None)):
761
+ if not payload.body.strip():
762
+ raise HTTPException(status_code=400, detail="body is required")
763
+ board = _resolve_board(board)
764
+ conn = _conn(board=board)
765
+ try:
766
+ if kanban_db.get_task(conn, task_id) is None:
767
+ raise HTTPException(status_code=404, detail=f"task {task_id} not found")
768
+ kanban_db.add_comment(
769
+ conn, task_id, author=payload.author or "dashboard", body=payload.body,
770
+ )
771
+ return {"ok": True}
772
+ finally:
773
+ conn.close()
774
+
775
+
776
+ # ---------------------------------------------------------------------------
777
+ # Links
778
+ # ---------------------------------------------------------------------------
779
+
780
+ class LinkBody(BaseModel):
781
+ parent_id: str
782
+ child_id: str
783
+
784
+
785
+ @router.post("/links")
786
+ def add_link(payload: LinkBody, board: Optional[str] = Query(None)):
787
+ board = _resolve_board(board)
788
+ conn = _conn(board=board)
789
+ try:
790
+ kanban_db.link_tasks(conn, payload.parent_id, payload.child_id)
791
+ return {"ok": True}
792
+ except ValueError as e:
793
+ raise HTTPException(status_code=400, detail=str(e))
794
+ finally:
795
+ conn.close()
796
+
797
+
798
+ @router.delete("/links")
799
+ def delete_link(
800
+ parent_id: str = Query(...),
801
+ child_id: str = Query(...),
802
+ board: Optional[str] = Query(None),
803
+ ):
804
+ board = _resolve_board(board)
805
+ conn = _conn(board=board)
806
+ try:
807
+ ok = kanban_db.unlink_tasks(conn, parent_id, child_id)
808
+ return {"ok": bool(ok)}
809
+ finally:
810
+ conn.close()
811
+
812
+
813
+ # ---------------------------------------------------------------------------
814
+ # Bulk actions (multi-select on the board)
815
+ # ---------------------------------------------------------------------------
816
+
817
+ class BulkTaskBody(BaseModel):
818
+ ids: list[str]
819
+ status: Optional[str] = None
820
+ assignee: Optional[str] = None # "" or None = unassign
821
+ priority: Optional[int] = None
822
+ archive: bool = False
823
+ result: Optional[str] = None
824
+ summary: Optional[str] = None
825
+ metadata: Optional[dict] = None
826
+ reclaim_first: bool = False
827
+
828
+
829
+ @router.post("/tasks/bulk")
830
+ def bulk_update(payload: BulkTaskBody, board: Optional[str] = Query(None)):
831
+ """Apply the same patch to every id in ``payload.ids``.
832
+
833
+ This is an *independent* iteration — per-task failures don't abort
834
+ siblings. Returns per-id outcome so the UI can surface partials.
835
+ """
836
+ ids = [i for i in (payload.ids or []) if i]
837
+ if not ids:
838
+ raise HTTPException(status_code=400, detail="ids is required")
839
+ results: list[dict] = []
840
+ board = _resolve_board(board)
841
+ conn = _conn(board=board)
842
+ try:
843
+ for tid in ids:
844
+ entry: dict[str, Any] = {"id": tid, "ok": True}
845
+ try:
846
+ task = kanban_db.get_task(conn, tid)
847
+ if task is None:
848
+ entry.update(ok=False, error="not found")
849
+ results.append(entry)
850
+ continue
851
+ if payload.archive:
852
+ if not kanban_db.archive_task(conn, tid):
853
+ entry.update(ok=False, error="archive refused")
854
+ if payload.status is not None and not payload.archive:
855
+ s = payload.status
856
+ if s == "done":
857
+ ok = kanban_db.complete_task(
858
+ conn, tid,
859
+ result=payload.result,
860
+ summary=payload.summary,
861
+ metadata=payload.metadata,
862
+ )
863
+ elif s == "blocked":
864
+ ok = kanban_db.block_task(conn, tid)
865
+ elif s == "ready":
866
+ cur = kanban_db.get_task(conn, tid)
867
+ if cur and cur.status == "blocked":
868
+ ok = kanban_db.unblock_task(conn, tid)
869
+ else:
870
+ ok = _set_status_direct(conn, tid, "ready")
871
+ elif s in ("todo", "running", "triage"):
872
+ ok = _set_status_direct(conn, tid, s)
873
+ else:
874
+ entry.update(ok=False, error=f"unknown status {s!r}")
875
+ results.append(entry)
876
+ continue
877
+ if not ok:
878
+ entry.update(ok=False, error=f"transition to {s!r} refused")
879
+ if payload.assignee is not None:
880
+ try:
881
+ if payload.reclaim_first:
882
+ ok = kanban_db.reassign_task(
883
+ conn, tid, payload.assignee or None,
884
+ reclaim_first=True,
885
+ )
886
+ else:
887
+ ok = kanban_db.assign_task(
888
+ conn, tid, payload.assignee or None,
889
+ )
890
+ if not ok:
891
+ entry.update(ok=False, error="assign refused")
892
+ except RuntimeError as e:
893
+ entry.update(ok=False, error=str(e))
894
+ if payload.priority is not None:
895
+ with kanban_db.write_txn(conn):
896
+ conn.execute(
897
+ "UPDATE tasks SET priority = ? WHERE id = ?",
898
+ (int(payload.priority), tid),
899
+ )
900
+ conn.execute(
901
+ "INSERT INTO task_events (task_id, kind, payload, created_at) "
902
+ "VALUES (?, 'reprioritized', ?, ?)",
903
+ (tid, json.dumps({"priority": int(payload.priority)}),
904
+ int(time.time())),
905
+ )
906
+ except Exception as e: # defensive — one bad id shouldn't kill the batch
907
+ entry.update(ok=False, error=str(e))
908
+ results.append(entry)
909
+ return {"results": results}
910
+ finally:
911
+ conn.close()
912
+
913
+
914
+ # ---------------------------------------------------------------------------
915
+ # Diagnostics — fleet-wide distress signals (hallucinations, crashes,
916
+ # spawn failures, stuck-blocked). See hermes_cli.kanban_diagnostics for
917
+ # the rule engine.
918
+ # ---------------------------------------------------------------------------
919
+
920
+ @router.get("/diagnostics")
921
+ def list_diagnostics(
922
+ board: Optional[str] = Query(None, description="Kanban board slug (omit for current)"),
923
+ severity: Optional[str] = Query(
924
+ None,
925
+ description="Filter by severity: warning|error|critical",
926
+ ),
927
+ ):
928
+ """Return ``[{task_id, task_title, task_status, task_assignee,
929
+ diagnostics: [...]}, ...]`` for every task on the board with at
930
+ least one active diagnostic.
931
+
932
+ Severity-filterable so the UI can render "just the critical ones"
933
+ or the CLI can grep. Useful for the board-header attention strip
934
+ AND for ``hermes kanban diagnostics`` which shells to this
935
+ endpoint when the dashboard's running, or invokes the engine
936
+ directly when it isn't.
937
+ """
938
+ board = _resolve_board(board)
939
+ conn = _conn(board=board)
940
+ try:
941
+ diags_by_task = _compute_task_diagnostics(conn, task_ids=None)
942
+ if not diags_by_task:
943
+ return {"diagnostics": [], "count": 0}
944
+
945
+ # Narrow by severity if asked.
946
+ if severity:
947
+ filtered: dict[str, list[dict]] = {}
948
+ for tid, dl in diags_by_task.items():
949
+ keep = [d for d in dl if d.get("severity") == severity]
950
+ if keep:
951
+ filtered[tid] = keep
952
+ diags_by_task = filtered
953
+ if not diags_by_task:
954
+ return {"diagnostics": [], "count": 0}
955
+
956
+ # Pull the task rows we need in one query so we can include
957
+ # titles/statuses without a per-task lookup.
958
+ ids = list(diags_by_task.keys())
959
+ placeholders = ",".join(["?"] * len(ids))
960
+ rows = {
961
+ r["id"]: r
962
+ for r in conn.execute(
963
+ f"SELECT id, title, status, assignee FROM tasks WHERE id IN ({placeholders})",
964
+ tuple(ids),
965
+ ).fetchall()
966
+ }
967
+
968
+ out = []
969
+ for tid, dl in diags_by_task.items():
970
+ r = rows.get(tid)
971
+ out.append({
972
+ "task_id": tid,
973
+ "task_title": r["title"] if r else None,
974
+ "task_status": r["status"] if r else None,
975
+ "task_assignee": r["assignee"] if r else None,
976
+ "diagnostics": dl,
977
+ })
978
+ # Sort: highest severity first, then most recent.
979
+ from hermes_cli.kanban_diagnostics import SEVERITY_ORDER
980
+ sev_idx = {s: i for i, s in enumerate(SEVERITY_ORDER)}
981
+ def _sort_key(row):
982
+ top = row["diagnostics"][0]
983
+ return (
984
+ -sev_idx.get(top.get("severity"), -1),
985
+ -(top.get("last_seen_at") or 0),
986
+ )
987
+ out.sort(key=_sort_key)
988
+
989
+ return {
990
+ "diagnostics": out,
991
+ "count": sum(len(d["diagnostics"]) for d in out),
992
+ }
993
+ finally:
994
+ conn.close()
995
+
996
+
997
+ # ---------------------------------------------------------------------------
998
+ # Recovery actions — reclaim a running claim, reassign to a new profile
999
+ # ---------------------------------------------------------------------------
1000
+
1001
+ class ReclaimBody(BaseModel):
1002
+ reason: Optional[str] = None
1003
+
1004
+
1005
+ @router.post("/tasks/{task_id}/reclaim")
1006
+ def reclaim_task_endpoint(
1007
+ task_id: str,
1008
+ payload: ReclaimBody,
1009
+ board: Optional[str] = Query(None),
1010
+ ):
1011
+ """Release an active worker claim on a running task.
1012
+
1013
+ Used by the dashboard recovery popover when an operator wants to
1014
+ abort a stuck worker (e.g. one that keeps hallucinating card ids)
1015
+ without waiting for the claim TTL. Maps 1:1 to
1016
+ ``hermes kanban reclaim <task_id> --reason ...``.
1017
+ """
1018
+ board = _resolve_board(board)
1019
+ conn = _conn(board=board)
1020
+ try:
1021
+ ok = kanban_db.reclaim_task(conn, task_id, reason=payload.reason)
1022
+ if not ok:
1023
+ raise HTTPException(
1024
+ status_code=409,
1025
+ detail=(
1026
+ f"cannot reclaim {task_id}: not in a claimable state "
1027
+ "(not running, or unknown id)"
1028
+ ),
1029
+ )
1030
+ return {"ok": True, "task_id": task_id}
1031
+ finally:
1032
+ conn.close()
1033
+
1034
+
1035
+ class SpecifyBody(BaseModel):
1036
+ """Optional author override. Nothing else is configurable from the
1037
+ dashboard — model + prompt come from ``auxiliary.triage_specifier``
1038
+ in config.yaml, same as the CLI."""
1039
+
1040
+ author: Optional[str] = None
1041
+
1042
+
1043
+ @router.post("/tasks/{task_id}/specify")
1044
+ def specify_task_endpoint(
1045
+ task_id: str,
1046
+ payload: SpecifyBody,
1047
+ board: Optional[str] = Query(None),
1048
+ ):
1049
+ """Flesh out a triage-column task via the auxiliary LLM and promote
1050
+ it to ``todo``. Maps 1:1 to ``hermes kanban specify <task_id>``.
1051
+
1052
+ Returns the outcome shape used by the CLI: ``{ok, task_id, reason,
1053
+ new_title}``. A non-OK outcome is NOT an HTTP error — the UI renders
1054
+ the reason inline (e.g. "no auxiliary client configured") so the
1055
+ operator knows what to fix, and retries without a page reload.
1056
+
1057
+ This endpoint runs in FastAPI's threadpool (sync ``def``) because
1058
+ the underlying LLM call can take tens of seconds to minutes on
1059
+ reasoning models, which would block the event loop if we used
1060
+ ``async def`` without an explicit ``run_in_executor``.
1061
+ """
1062
+ board = _resolve_board(board)
1063
+ # Pin the board for the duration of this call so the specifier module
1064
+ # (which calls ``kb.connect()`` with no args) hits the right DB.
1065
+ prev_env = os.environ.get("HERMES_KANBAN_BOARD")
1066
+ try:
1067
+ os.environ["HERMES_KANBAN_BOARD"] = board or kanban_db.DEFAULT_BOARD
1068
+ # Import lazily so a missing auxiliary client at import time
1069
+ # doesn't break plugin load.
1070
+ from hermes_cli import kanban_specify # noqa: WPS433 (intentional)
1071
+
1072
+ outcome = kanban_specify.specify_task(
1073
+ task_id,
1074
+ author=(payload.author or None),
1075
+ )
1076
+ finally:
1077
+ if prev_env is None:
1078
+ os.environ.pop("HERMES_KANBAN_BOARD", None)
1079
+ else:
1080
+ os.environ["HERMES_KANBAN_BOARD"] = prev_env
1081
+
1082
+ return {
1083
+ "ok": bool(outcome.ok),
1084
+ "task_id": outcome.task_id,
1085
+ "reason": outcome.reason,
1086
+ "new_title": outcome.new_title,
1087
+ }
1088
+
1089
+
1090
+ class ReassignBody(BaseModel):
1091
+ profile: Optional[str] = None # "" or None = unassign
1092
+ reclaim_first: bool = False
1093
+ reason: Optional[str] = None
1094
+
1095
+
1096
+ @router.post("/tasks/{task_id}/reassign")
1097
+ def reassign_task_endpoint(
1098
+ task_id: str,
1099
+ payload: ReassignBody,
1100
+ board: Optional[str] = Query(None),
1101
+ ):
1102
+ """Reassign a task to a different profile, optionally reclaiming first.
1103
+
1104
+ Used by the dashboard recovery popover when an operator wants to
1105
+ retry a task with a different worker profile (e.g. switch to a
1106
+ smarter model after the assigned profile keeps hallucinating).
1107
+ Maps 1:1 to ``hermes kanban reassign <task_id> <profile> [--reclaim]``.
1108
+ """
1109
+ board = _resolve_board(board)
1110
+ conn = _conn(board=board)
1111
+ try:
1112
+ ok = kanban_db.reassign_task(
1113
+ conn, task_id,
1114
+ payload.profile or None,
1115
+ reclaim_first=bool(payload.reclaim_first),
1116
+ reason=payload.reason,
1117
+ )
1118
+ if not ok:
1119
+ raise HTTPException(
1120
+ status_code=409,
1121
+ detail=(
1122
+ f"cannot reassign {task_id}: unknown id, or still "
1123
+ "running (pass reclaim_first=true to release the claim first)"
1124
+ ),
1125
+ )
1126
+ return {"ok": True, "task_id": task_id, "assignee": payload.profile or None}
1127
+ finally:
1128
+ conn.close()
1129
+
1130
+
1131
+ # ---------------------------------------------------------------------------
1132
+ # Plugin config (read dashboard.kanban.* defaults from config.yaml)
1133
+ # ---------------------------------------------------------------------------
1134
+
1135
+ @router.get("/config")
1136
+ def get_config():
1137
+ """Return kanban dashboard preferences from ~/.hermes/config.yaml.
1138
+
1139
+ Reads the ``dashboard.kanban`` section if present; defaults otherwise.
1140
+ Used by the UI to pre-select tenant filters, toggle markdown rendering,
1141
+ or set column-width preferences without a round-trip per page load.
1142
+ """
1143
+ try:
1144
+ from hermes_cli.config import load_config
1145
+ cfg = load_config() or {}
1146
+ except Exception:
1147
+ cfg = {}
1148
+ dash_cfg = (cfg.get("dashboard") or {})
1149
+ # dashboard.kanban may itself be a dict; fall back to {}.
1150
+ k_cfg = dash_cfg.get("kanban") or {}
1151
+ return {
1152
+ "default_tenant": k_cfg.get("default_tenant") or "",
1153
+ "lane_by_profile": bool(k_cfg.get("lane_by_profile", True)),
1154
+ "include_archived_by_default": bool(k_cfg.get("include_archived_by_default", False)),
1155
+ "render_markdown": bool(k_cfg.get("render_markdown", True)),
1156
+ }
1157
+
1158
+
1159
+ # ---------------------------------------------------------------------------
1160
+ # Home-channel subscriptions (per-task, per-platform toggles)
1161
+ # ---------------------------------------------------------------------------
1162
+ #
1163
+ # Home channels are a first-class gateway concept — each configured platform
1164
+ # can have exactly one (chat_id, thread_id, name) it considers "home". The
1165
+ # dashboard surfaces these as per-task toggles so a user can opt a specific
1166
+ # task into receiving terminal notifications (completed / blocked / gave_up)
1167
+ # at their telegram/discord/slack home, without touching the CLI.
1168
+ #
1169
+ # The wire format mirrors kanban_db.add_notify_sub — (task_id, platform,
1170
+ # chat_id, thread_id) — so toggle-on creates exactly the same row the
1171
+ # `/kanban create` slash command would, and the existing gateway notifier
1172
+ # watcher delivers events without any additional plumbing.
1173
+
1174
+
1175
+ def _configured_home_channels() -> list[dict]:
1176
+ """Return every platform that has a home_channel set, fully hydrated.
1177
+
1178
+ Reads the live GatewayConfig so env-var overlays (``TELEGRAM_HOME_CHANNEL``
1179
+ etc.) are honored alongside config.yaml. Returns platforms in a stable
1180
+ order and drops platforms without a home.
1181
+ """
1182
+ try:
1183
+ from gateway.config import load_gateway_config
1184
+ except Exception:
1185
+ return []
1186
+ try:
1187
+ gw_cfg = load_gateway_config()
1188
+ except Exception:
1189
+ return []
1190
+ result: list[dict] = []
1191
+ for platform, pcfg in gw_cfg.platforms.items():
1192
+ if not pcfg or not pcfg.home_channel:
1193
+ continue
1194
+ hc = pcfg.home_channel
1195
+ result.append({
1196
+ "platform": platform.value,
1197
+ "chat_id": hc.chat_id,
1198
+ "thread_id": hc.thread_id or "",
1199
+ "name": hc.name or "Home",
1200
+ })
1201
+ # Stable order for deterministic UI — platform name alphabetical.
1202
+ result.sort(key=lambda r: r["platform"])
1203
+ return result
1204
+
1205
+
1206
+ def _home_sub_matches(sub: dict, home: dict) -> bool:
1207
+ """True if a notify_subs row corresponds to the given home channel."""
1208
+ return (
1209
+ sub.get("platform") == home["platform"]
1210
+ and str(sub.get("chat_id", "")) == str(home["chat_id"])
1211
+ and str(sub.get("thread_id") or "") == str(home["thread_id"] or "")
1212
+ )
1213
+
1214
+
1215
+ @router.get("/home-channels")
1216
+ def get_home_channels(
1217
+ task_id: Optional[str] = Query(None),
1218
+ board: Optional[str] = Query(None),
1219
+ ):
1220
+ """List every platform with a home channel, plus whether *task_id*
1221
+ (if given) is currently subscribed to that home.
1222
+
1223
+ When ``task_id`` is omitted, every entry's ``subscribed`` is ``false``
1224
+ — useful for the "no task selected" state of the UI.
1225
+ """
1226
+ homes = _configured_home_channels()
1227
+ subscribed_homes: set[tuple[str, str, str]] = set()
1228
+ if task_id:
1229
+ board = _resolve_board(board)
1230
+ conn = _conn(board=board)
1231
+ try:
1232
+ subs = kanban_db.list_notify_subs(conn, task_id)
1233
+ finally:
1234
+ conn.close()
1235
+ for sub in subs:
1236
+ key = (
1237
+ str(sub.get("platform") or ""),
1238
+ str(sub.get("chat_id") or ""),
1239
+ str(sub.get("thread_id") or ""),
1240
+ )
1241
+ subscribed_homes.add(key)
1242
+ result = []
1243
+ for home in homes:
1244
+ key = (home["platform"], home["chat_id"], home["thread_id"])
1245
+ result.append({**home, "subscribed": key in subscribed_homes})
1246
+ return {"home_channels": result}
1247
+
1248
+
1249
+ @router.post("/tasks/{task_id}/home-subscribe/{platform}")
1250
+ def subscribe_home(task_id: str, platform: str, board: Optional[str] = Query(None)):
1251
+ """Subscribe *task_id* to notifications routed to *platform*'s home channel.
1252
+
1253
+ Idempotent — re-subscribing is a no-op at the DB layer. 404 if the
1254
+ platform has no home channel configured. 404 if the task doesn't exist.
1255
+ """
1256
+ homes = _configured_home_channels()
1257
+ home = next((h for h in homes if h["platform"] == platform), None)
1258
+ if not home:
1259
+ raise HTTPException(
1260
+ status_code=404,
1261
+ detail=f"No home channel configured for platform {platform!r}. "
1262
+ f"Set one from the messenger via /sethome, or configure "
1263
+ f"gateway.platforms.{platform}.home_channel in config.yaml.",
1264
+ )
1265
+ board = _resolve_board(board)
1266
+ conn = _conn(board=board)
1267
+ try:
1268
+ task = kanban_db.get_task(conn, task_id)
1269
+ if task is None:
1270
+ raise HTTPException(status_code=404, detail=f"task {task_id} not found")
1271
+ kanban_db.add_notify_sub(
1272
+ conn,
1273
+ task_id=task_id,
1274
+ platform=platform,
1275
+ chat_id=home["chat_id"],
1276
+ thread_id=home["thread_id"] or None,
1277
+ )
1278
+ return {"ok": True, "task_id": task_id, "home_channel": home}
1279
+ finally:
1280
+ conn.close()
1281
+
1282
+
1283
+ @router.delete("/tasks/{task_id}/home-subscribe/{platform}")
1284
+ def unsubscribe_home(task_id: str, platform: str, board: Optional[str] = Query(None)):
1285
+ """Remove any notify subscription on *task_id* that matches *platform*'s home."""
1286
+ homes = _configured_home_channels()
1287
+ home = next((h for h in homes if h["platform"] == platform), None)
1288
+ if not home:
1289
+ raise HTTPException(
1290
+ status_code=404,
1291
+ detail=f"No home channel configured for platform {platform!r}.",
1292
+ )
1293
+ board = _resolve_board(board)
1294
+ conn = _conn(board=board)
1295
+ try:
1296
+ kanban_db.remove_notify_sub(
1297
+ conn,
1298
+ task_id=task_id,
1299
+ platform=platform,
1300
+ chat_id=home["chat_id"],
1301
+ thread_id=home["thread_id"] or None,
1302
+ )
1303
+ return {"ok": True, "task_id": task_id, "home_channel": home}
1304
+ finally:
1305
+ conn.close()
1306
+
1307
+
1308
+ # ---------------------------------------------------------------------------
1309
+ # Stats (per-profile / per-status counts + oldest-ready age)
1310
+ # ---------------------------------------------------------------------------
1311
+
1312
+ @router.get("/stats")
1313
+ def get_stats(board: Optional[str] = Query(None)):
1314
+ """Per-status + per-assignee counts + oldest-ready age.
1315
+
1316
+ Designed for the dashboard HUD and for router profiles that need to
1317
+ answer "is this specialist overloaded?" without scanning the whole
1318
+ board themselves.
1319
+ """
1320
+ board = _resolve_board(board)
1321
+ conn = _conn(board=board)
1322
+ try:
1323
+ return kanban_db.board_stats(conn)
1324
+ finally:
1325
+ conn.close()
1326
+
1327
+
1328
+ @router.get("/assignees")
1329
+ def get_assignees(board: Optional[str] = Query(None)):
1330
+ """Known profiles + per-profile task counts.
1331
+
1332
+ Returns the union of ``~/.hermes/profiles/*`` on disk and every
1333
+ distinct assignee currently used on the board. The dashboard uses
1334
+ this to populate its assignee dropdown so a freshly-created profile
1335
+ appears in the picker before it's been given any task.
1336
+ """
1337
+ board = _resolve_board(board)
1338
+ conn = _conn(board=board)
1339
+ try:
1340
+ return {"assignees": kanban_db.known_assignees(conn)}
1341
+ finally:
1342
+ conn.close()
1343
+
1344
+
1345
+ # ---------------------------------------------------------------------------
1346
+ # Worker log (read-only; file written by _default_spawn)
1347
+ # ---------------------------------------------------------------------------
1348
+
1349
+ @router.get("/tasks/{task_id}/log")
1350
+ def get_task_log(
1351
+ task_id: str,
1352
+ tail: Optional[int] = Query(None, ge=1, le=2_000_000),
1353
+ board: Optional[str] = Query(None),
1354
+ ):
1355
+ """Return the worker's stdout/stderr log.
1356
+
1357
+ ``tail`` caps the response size (bytes) so the dashboard drawer
1358
+ doesn't paginate megabytes into the browser. Returns 404 if the task
1359
+ has never spawned. The on-disk log is rotated at 2 MiB per
1360
+ ``_rotate_worker_log`` — a single ``.log.1`` is kept, no further
1361
+ generations, so disk usage per task is bounded at ~4 MiB.
1362
+ """
1363
+ board = _resolve_board(board)
1364
+ conn = _conn(board=board)
1365
+ try:
1366
+ task = kanban_db.get_task(conn, task_id)
1367
+ finally:
1368
+ conn.close()
1369
+ if task is None:
1370
+ raise HTTPException(status_code=404, detail=f"task {task_id} not found")
1371
+ content = kanban_db.read_worker_log(task_id, tail_bytes=tail, board=board)
1372
+ log_path = kanban_db.worker_log_path(task_id, board=board)
1373
+ size = log_path.stat().st_size if log_path.exists() else 0
1374
+ return {
1375
+ "task_id": task_id,
1376
+ "path": str(log_path),
1377
+ "exists": content is not None,
1378
+ "size_bytes": size,
1379
+ "content": content or "",
1380
+ # Truncated when the on-disk file was larger than the tail cap.
1381
+ "truncated": bool(tail and size > tail),
1382
+ }
1383
+
1384
+
1385
+ # ---------------------------------------------------------------------------
1386
+ # Dispatch nudge (optional quick-path so the UI doesn't wait 60 s)
1387
+ # ---------------------------------------------------------------------------
1388
+
1389
+ @router.post("/dispatch")
1390
+ def dispatch(
1391
+ dry_run: bool = Query(False),
1392
+ max_n: int = Query(8, alias="max"),
1393
+ board: Optional[str] = Query(None),
1394
+ ):
1395
+ board = _resolve_board(board)
1396
+ conn = _conn(board=board)
1397
+ try:
1398
+ result = kanban_db.dispatch_once(
1399
+ conn, dry_run=dry_run, max_spawn=max_n, board=board,
1400
+ )
1401
+ # DispatchResult is a dataclass.
1402
+ try:
1403
+ return asdict(result)
1404
+ except TypeError:
1405
+ return {"result": str(result)}
1406
+ finally:
1407
+ conn.close()
1408
+
1409
+
1410
+ # ---------------------------------------------------------------------------
1411
+ # Boards CRUD (multi-project support)
1412
+ # ---------------------------------------------------------------------------
1413
+
1414
+ class CreateBoardBody(BaseModel):
1415
+ slug: str
1416
+ name: Optional[str] = None
1417
+ description: Optional[str] = None
1418
+ icon: Optional[str] = None
1419
+ color: Optional[str] = None
1420
+ switch: bool = False
1421
+
1422
+
1423
+ class RenameBoardBody(BaseModel):
1424
+ name: Optional[str] = None
1425
+ description: Optional[str] = None
1426
+ icon: Optional[str] = None
1427
+ color: Optional[str] = None
1428
+
1429
+
1430
+ def _board_counts(slug: str) -> dict[str, int]:
1431
+ """Return ``{status: count}`` for a board. Safe on an empty DB."""
1432
+ try:
1433
+ path = kanban_db.kanban_db_path(board=slug)
1434
+ if not path.exists():
1435
+ return {}
1436
+ conn = kanban_db.connect(board=slug)
1437
+ try:
1438
+ rows = conn.execute(
1439
+ "SELECT status, COUNT(*) AS n FROM tasks GROUP BY status"
1440
+ ).fetchall()
1441
+ return {r["status"]: int(r["n"]) for r in rows}
1442
+ finally:
1443
+ conn.close()
1444
+ except Exception:
1445
+ return {}
1446
+
1447
+
1448
+ @router.get("/boards")
1449
+ def list_boards(include_archived: bool = Query(False)):
1450
+ """Return every board on disk with task counts and the active slug."""
1451
+ boards = kanban_db.list_boards(include_archived=include_archived)
1452
+ current = kanban_db.get_current_board()
1453
+ for b in boards:
1454
+ b["is_current"] = (b["slug"] == current)
1455
+ b["counts"] = _board_counts(b["slug"])
1456
+ b["total"] = sum(b["counts"].values())
1457
+ return {"boards": boards, "current": current}
1458
+
1459
+
1460
+ @router.post("/boards")
1461
+ def create_board_endpoint(payload: CreateBoardBody):
1462
+ """Create a new board. Idempotent — ``slug`` collision returns existing."""
1463
+ try:
1464
+ meta = kanban_db.create_board(
1465
+ payload.slug,
1466
+ name=payload.name,
1467
+ description=payload.description,
1468
+ icon=payload.icon,
1469
+ color=payload.color,
1470
+ )
1471
+ except ValueError as exc:
1472
+ raise HTTPException(status_code=400, detail=str(exc))
1473
+ if payload.switch:
1474
+ try:
1475
+ kanban_db.set_current_board(meta["slug"])
1476
+ except ValueError as exc:
1477
+ raise HTTPException(status_code=400, detail=str(exc))
1478
+ return {"board": meta, "current": kanban_db.get_current_board()}
1479
+
1480
+
1481
+ @router.patch("/boards/{slug}")
1482
+ def rename_board(slug: str, payload: RenameBoardBody):
1483
+ """Update a board's display metadata (slug is immutable — create a new one to rename the directory)."""
1484
+ try:
1485
+ normed = kanban_db._normalize_board_slug(slug)
1486
+ except ValueError as exc:
1487
+ raise HTTPException(status_code=400, detail=str(exc))
1488
+ if not normed or not kanban_db.board_exists(normed):
1489
+ raise HTTPException(status_code=404, detail=f"board {slug!r} does not exist")
1490
+ meta = kanban_db.write_board_metadata(
1491
+ normed,
1492
+ name=payload.name,
1493
+ description=payload.description,
1494
+ icon=payload.icon,
1495
+ color=payload.color,
1496
+ )
1497
+ return {"board": meta}
1498
+
1499
+
1500
+ @router.delete("/boards/{slug}")
1501
+ def delete_board(slug: str, delete: bool = Query(False, description="Hard-delete instead of archive")):
1502
+ """Archive (default) or hard-delete a board."""
1503
+ try:
1504
+ res = kanban_db.remove_board(slug, archive=not delete)
1505
+ except ValueError as exc:
1506
+ raise HTTPException(status_code=400, detail=str(exc))
1507
+ return {"result": res, "current": kanban_db.get_current_board()}
1508
+
1509
+
1510
+ @router.post("/boards/{slug}/switch")
1511
+ def switch_board(slug: str):
1512
+ """Persist ``slug`` as the active board for subsequent CLI / slash calls.
1513
+
1514
+ Dashboard users pick boards via a client-side ``localStorage`` — this
1515
+ endpoint is for ``/kanban boards switch`` parity so gateway slash
1516
+ commands and the CLI share the same current-board pointer.
1517
+ """
1518
+ try:
1519
+ normed = kanban_db._normalize_board_slug(slug)
1520
+ except ValueError as exc:
1521
+ raise HTTPException(status_code=400, detail=str(exc))
1522
+ if not normed or not kanban_db.board_exists(normed):
1523
+ raise HTTPException(status_code=404, detail=f"board {slug!r} does not exist")
1524
+ kanban_db.set_current_board(normed)
1525
+ return {"current": normed}
1526
+
1527
+
1528
+ # ---------------------------------------------------------------------------
1529
+ # WebSocket: /events?since=<event_id>
1530
+ # ---------------------------------------------------------------------------
1531
+
1532
+ # Poll interval for the event tail loop. SQLite WAL + 300 ms polling is
1533
+ # the simplest and most robust approach; it adds a fraction of a percent
1534
+ # of CPU and has no shared state to synchronize across workers.
1535
+ _EVENT_POLL_SECONDS = 0.3
1536
+
1537
+
1538
+ @router.websocket("/events")
1539
+ async def stream_events(ws: WebSocket):
1540
+ # Enforce the dashboard session token as a query param — browsers can't
1541
+ # set Authorization on a WS upgrade. This matches how the PTY bridge
1542
+ # authenticates in hermes_cli/web_server.py.
1543
+ token = ws.query_params.get("token")
1544
+ if not _check_ws_token(token):
1545
+ await ws.close(code=http_status.WS_1008_POLICY_VIOLATION)
1546
+ return
1547
+ await ws.accept()
1548
+ try:
1549
+ since_raw = ws.query_params.get("since", "0")
1550
+ try:
1551
+ cursor = int(since_raw)
1552
+ except ValueError:
1553
+ cursor = 0
1554
+
1555
+ # Board selection — pinned at the WS handshake; re-subscribe to
1556
+ # switch boards. Changing boards mid-stream would require
1557
+ # reconciling two cursors, so the UI just opens a new WS on
1558
+ # board change.
1559
+ ws_board_raw = ws.query_params.get("board")
1560
+ try:
1561
+ ws_board = kanban_db._normalize_board_slug(ws_board_raw) if ws_board_raw else None
1562
+ except ValueError:
1563
+ ws_board = None
1564
+
1565
+ def _fetch_new(cursor_val: int) -> tuple[int, list[dict]]:
1566
+ conn = kanban_db.connect(board=ws_board)
1567
+ try:
1568
+ rows = conn.execute(
1569
+ "SELECT id, task_id, run_id, kind, payload, created_at "
1570
+ "FROM task_events WHERE id > ? ORDER BY id ASC LIMIT 200",
1571
+ (cursor_val,),
1572
+ ).fetchall()
1573
+ out: list[dict] = []
1574
+ new_cursor = cursor_val
1575
+ for r in rows:
1576
+ try:
1577
+ payload = json.loads(r["payload"]) if r["payload"] else None
1578
+ except Exception:
1579
+ payload = None
1580
+ out.append({
1581
+ "id": r["id"],
1582
+ "task_id": r["task_id"],
1583
+ "run_id": r["run_id"],
1584
+ "kind": r["kind"],
1585
+ "payload": payload,
1586
+ "created_at": r["created_at"],
1587
+ })
1588
+ new_cursor = r["id"]
1589
+ return new_cursor, out
1590
+ finally:
1591
+ conn.close()
1592
+
1593
+ while True:
1594
+ cursor, events = await asyncio.to_thread(_fetch_new, cursor)
1595
+ if events:
1596
+ await ws.send_json({"events": events, "cursor": cursor})
1597
+ await asyncio.sleep(_EVENT_POLL_SECONDS)
1598
+ except WebSocketDisconnect:
1599
+ return
1600
+ except asyncio.CancelledError:
1601
+ # Normal shutdown path: dashboard process exit (Ctrl-C) cancels the
1602
+ # websocket task while it is sleeping in the poll loop.
1603
+ # CancelledError is a BaseException in 3.8+ so the bare Exception
1604
+ # handler below would not catch it; without this clause Uvicorn
1605
+ # surfaces the cancellation as an application traceback. Quiet it.
1606
+ return
1607
+ except Exception as exc: # defensive: never crash the dashboard worker
1608
+ log.warning("Kanban event stream error: %s", exc)
1609
+ try:
1610
+ await ws.close()
1611
+ except Exception:
1612
+ pass