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,2252 @@
1
+ """CLI for the Hermes Kanban board — ``hermes kanban …`` subcommand.
2
+
3
+ Exposes the full 15-verb surface documented in the design spec
4
+ (``docs/hermes-kanban-v1-spec.pdf``). All DB work is delegated to
5
+ ``kanban_db``. This module adds:
6
+
7
+ * Argparse subcommand construction (``build_parser``).
8
+ * Argument dispatch (``kanban_command``).
9
+ * Output formatting (plain text + ``--json``).
10
+ * A short shared helper that parses a single slash-style string
11
+ (used by ``/kanban …`` in CLI and gateway) and forwards it to the
12
+ argparse surface.
13
+ """
14
+
15
+ from __future__ import annotations
16
+
17
+ import argparse
18
+ import json
19
+ import os
20
+ import shlex
21
+ import sys
22
+ import time
23
+ from pathlib import Path
24
+ from typing import Any, Optional
25
+
26
+ from hermes_cli import kanban_db as kb
27
+
28
+
29
+ # ---------------------------------------------------------------------------
30
+ # Small formatting helpers
31
+ # ---------------------------------------------------------------------------
32
+
33
+ _STATUS_ICONS = {
34
+ "todo": "◻",
35
+ "ready": "▶",
36
+ "running": "●",
37
+ "blocked": "⊘",
38
+ "done": "✓",
39
+ "archived": "—",
40
+ }
41
+
42
+
43
+ def _fmt_ts(ts: Optional[int]) -> str:
44
+ if not ts:
45
+ return ""
46
+ return time.strftime("%Y-%m-%d %H:%M", time.localtime(ts))
47
+
48
+
49
+ def _fmt_task_line(t: kb.Task) -> str:
50
+ icon = _STATUS_ICONS.get(t.status, "?")
51
+ assignee = t.assignee or "(unassigned)"
52
+ tenant = f" [{t.tenant}]" if t.tenant else ""
53
+ return f"{icon} {t.id} {t.status:8s} {assignee:20s}{tenant} {t.title}"
54
+
55
+
56
+ def _task_to_dict(t: kb.Task) -> dict[str, Any]:
57
+ return {
58
+ "id": t.id,
59
+ "title": t.title,
60
+ "body": t.body,
61
+ "assignee": t.assignee,
62
+ "status": t.status,
63
+ "priority": t.priority,
64
+ "tenant": t.tenant,
65
+ "workspace_kind": t.workspace_kind,
66
+ "workspace_path": t.workspace_path,
67
+ "created_by": t.created_by,
68
+ "created_at": t.created_at,
69
+ "started_at": t.started_at,
70
+ "completed_at": t.completed_at,
71
+ "result": t.result,
72
+ "skills": list(t.skills) if t.skills else [],
73
+ "max_retries": t.max_retries,
74
+ }
75
+
76
+
77
+ def _parse_workspace_flag(value: str) -> tuple[str, Optional[str]]:
78
+ """Parse ``--workspace`` into ``(kind, path|None)``.
79
+
80
+ Accepts: ``scratch``, ``worktree``, ``dir:<path>``.
81
+ """
82
+ if not value:
83
+ return ("scratch", None)
84
+ v = value.strip()
85
+ if v in {"scratch", "worktree"}:
86
+ return (v, None)
87
+ if v.startswith("dir:"):
88
+ path = v[len("dir:"):].strip()
89
+ if not path:
90
+ raise argparse.ArgumentTypeError(
91
+ "--workspace dir: requires a path after the colon"
92
+ )
93
+ return ("dir", os.path.expanduser(path))
94
+ raise argparse.ArgumentTypeError(
95
+ f"unknown --workspace value {value!r}: use scratch, worktree, or dir:<path>"
96
+ )
97
+
98
+
99
+ def _check_dispatcher_presence() -> tuple[bool, str]:
100
+ """Return ``(running, message)``.
101
+
102
+ - ``running=True``: a gateway is alive for this HERMES_HOME and its
103
+ config has ``kanban.dispatch_in_gateway`` on (default). Message
104
+ is a short status line.
105
+ - ``running=False``: either no gateway is running, or the gateway
106
+ is running but the config flag is off. Message is human guidance
107
+ explaining the next step.
108
+
109
+ Used by ``hermes kanban create`` (and callers) to warn when a task
110
+ will sit in ``ready`` because nothing is there to pick it up.
111
+ Defensive against import failures and config-read errors — if the
112
+ probe itself errors, we return ``(True, "")`` so we don't spam
113
+ false warnings (better to miss a warning than to cry wolf).
114
+ """
115
+ try:
116
+ from gateway.status import get_running_pid # type: ignore
117
+ except Exception:
118
+ return (True, "") # can't probe — silent
119
+ try:
120
+ pid = get_running_pid()
121
+ except Exception:
122
+ return (True, "") # probe errored — silent
123
+
124
+ # Even if the gateway is up, dispatch_in_gateway may be off.
125
+ try:
126
+ from hermes_cli.config import load_config
127
+ cfg = load_config()
128
+ dispatch_on = bool(cfg.get("kanban", {}).get("dispatch_in_gateway", True))
129
+ except Exception:
130
+ dispatch_on = True # can't tell — assume default
131
+
132
+ if pid and dispatch_on:
133
+ return (True, f"gateway pid={pid}, dispatch enabled")
134
+ if pid and not dispatch_on:
135
+ return (
136
+ False,
137
+ "Gateway is running but kanban.dispatch_in_gateway=false in "
138
+ "config.yaml — the task will sit in 'ready' until you flip it "
139
+ "back on and restart the gateway, OR run the legacy "
140
+ "standalone daemon (`hermes kanban daemon --force`)."
141
+ )
142
+ return (
143
+ False,
144
+ "No gateway is running — the task will sit in 'ready' until you "
145
+ "start it. Run:\n"
146
+ " hermes gateway start\n"
147
+ "The gateway hosts an embedded dispatcher (tick interval 60s by "
148
+ "default); your task will be picked up on the next tick after "
149
+ "the gateway comes up."
150
+ )
151
+
152
+
153
+ # ---------------------------------------------------------------------------
154
+ # Argparse builder
155
+ # ---------------------------------------------------------------------------
156
+
157
+ def build_parser(parent_subparsers: argparse._SubParsersAction) -> argparse.ArgumentParser:
158
+ """Attach the ``kanban`` subcommand tree under an existing subparsers.
159
+
160
+ Returns the top-level ``kanban`` parser so caller can ``set_defaults``.
161
+ """
162
+ kanban_parser = parent_subparsers.add_parser(
163
+ "kanban",
164
+ help="Multi-profile collaboration board (tasks, links, comments)",
165
+ description=(
166
+ "Durable SQLite-backed task board shared across Hermes profiles. "
167
+ "Tasks are claimed atomically, can depend on other tasks, and "
168
+ "are executed by a named profile in an isolated workspace. "
169
+ "See https://hermes-agent.nousresearch.com/docs/user-guide/features/kanban "
170
+ "or docs/hermes-kanban-v1-spec.pdf for the full design."
171
+ ),
172
+ )
173
+ # --- global --board flag ---
174
+ # Applies to every subcommand below. When set, scopes all reads and
175
+ # writes to that board's DB. When omitted, resolves via the
176
+ # HERMES_KANBAN_BOARD env var, then the persisted current-board
177
+ # file, then "default". See kanban_db.get_current_board().
178
+ kanban_parser.add_argument(
179
+ "--board",
180
+ default=None,
181
+ metavar="<slug>",
182
+ help=(
183
+ "Board slug to operate on. Defaults to the current board "
184
+ "(set via `hermes kanban boards switch <slug>` or the "
185
+ "HERMES_KANBAN_BOARD env var). Use `hermes kanban boards list` "
186
+ "to see all boards."
187
+ ),
188
+ )
189
+ sub = kanban_parser.add_subparsers(dest="kanban_action")
190
+
191
+ # --- init ---
192
+ sub.add_parser("init", help="Create kanban.db if missing (idempotent)")
193
+
194
+ # --- boards (new in v2: multi-project support) ---
195
+ p_boards = sub.add_parser(
196
+ "boards",
197
+ help="Manage kanban boards (one board per project / workstream)",
198
+ description=(
199
+ "Boards let you separate unrelated streams of work "
200
+ "(projects, repos, domains) into isolated queues. Each "
201
+ "board has its own DB, workspaces directory, and dispatcher "
202
+ "loop — tasks on one board cannot collide with tasks on "
203
+ "another. The first board is 'default' and always exists."
204
+ ),
205
+ )
206
+ boards_sub = p_boards.add_subparsers(dest="boards_action")
207
+
208
+ b_list = boards_sub.add_parser(
209
+ "list", aliases=["ls"],
210
+ help="List all boards with task counts",
211
+ )
212
+ b_list.add_argument("--json", action="store_true")
213
+ b_list.add_argument("--all", action="store_true",
214
+ help="Include archived boards too")
215
+
216
+ b_create = boards_sub.add_parser(
217
+ "create", aliases=["new"],
218
+ help="Create a new board",
219
+ )
220
+ b_create.add_argument("slug",
221
+ help="Board slug (kebab-case, e.g. atm10-server)")
222
+ b_create.add_argument("--name", default=None,
223
+ help="Human-readable display name (defaults to Title Case of slug)")
224
+ b_create.add_argument("--description", default=None,
225
+ help="Optional description")
226
+ b_create.add_argument("--icon", default=None,
227
+ help="Optional emoji or single-character icon for the dashboard")
228
+ b_create.add_argument("--color", default=None,
229
+ help="Optional hex color (e.g. '#8b5cf6') for the dashboard")
230
+ b_create.add_argument("--switch", action="store_true",
231
+ help="Switch to the new board after creating it")
232
+
233
+ b_rm = boards_sub.add_parser(
234
+ "rm", aliases=["remove", "delete"],
235
+ help="Archive (default) or delete a board",
236
+ )
237
+ b_rm.add_argument("slug")
238
+ b_rm.add_argument("--delete", action="store_true",
239
+ help="Hard-delete the board directory instead of archiving it. "
240
+ "Default is to move it to boards/_archived/ so it's recoverable.")
241
+
242
+ b_switch = boards_sub.add_parser(
243
+ "switch", aliases=["use"],
244
+ help="Set the active board for subsequent CLI calls",
245
+ )
246
+ b_switch.add_argument("slug")
247
+
248
+ boards_sub.add_parser(
249
+ "show", aliases=["current"],
250
+ help="Print the currently-active board slug",
251
+ )
252
+
253
+ b_rename = boards_sub.add_parser(
254
+ "rename",
255
+ help="Change a board's human-readable display name (slug is immutable)",
256
+ )
257
+ b_rename.add_argument("slug")
258
+ b_rename.add_argument("name", help="New display name")
259
+
260
+ # --- create ---
261
+ p_create = sub.add_parser("create", help="Create a new task")
262
+ p_create.add_argument("title", help="Task title")
263
+ p_create.add_argument("--body", default=None, help="Optional opening post")
264
+ p_create.add_argument("--assignee", default=None, help="Profile name to assign")
265
+ p_create.add_argument("--parent", action="append", default=[],
266
+ help="Parent task id (repeatable)")
267
+ p_create.add_argument("--workspace", default="scratch",
268
+ help="scratch | worktree | dir:<path> (default: scratch)")
269
+ p_create.add_argument("--tenant", default=None, help="Tenant namespace")
270
+ p_create.add_argument("--priority", type=int, default=0, help="Priority tiebreaker")
271
+ p_create.add_argument("--triage", action="store_true",
272
+ help="Park in triage — a specifier will flesh out the spec and promote to todo")
273
+ p_create.add_argument("--idempotency-key", default=None,
274
+ help="Dedup key. If a non-archived task with this key exists, "
275
+ "its id is returned instead of creating a duplicate.")
276
+ p_create.add_argument("--max-runtime", default=None,
277
+ help="Per-task runtime cap. Accepts seconds (300) or "
278
+ "durations (90s, 30m, 2h, 1d). When exceeded, "
279
+ "the dispatcher SIGTERMs (then SIGKILLs) the worker "
280
+ "and re-queues the task.")
281
+ p_create.add_argument("--created-by", default="user",
282
+ help="Author name recorded on the task (default: user)")
283
+ p_create.add_argument("--skill", action="append", default=[], dest="skills",
284
+ help="Skill to force-load into the worker "
285
+ "(repeatable). Appended to the built-in "
286
+ "kanban-worker skill. Example: "
287
+ "--skill translation --skill github-code-review")
288
+ p_create.add_argument("--max-retries", type=int, default=None,
289
+ metavar="N",
290
+ help="Per-task override for the consecutive-failure "
291
+ "circuit breaker. Trip on the Nth failure — "
292
+ "e.g. --max-retries 1 blocks on the first "
293
+ "failure (no retries), --max-retries 3 allows "
294
+ "two retries. Omit to use the dispatcher's "
295
+ "kanban.failure_limit config "
296
+ f"(default {kb.DEFAULT_FAILURE_LIMIT}).")
297
+ p_create.add_argument("--json", action="store_true", help="Emit JSON output")
298
+
299
+ # --- list ---
300
+ p_list = sub.add_parser("list", aliases=["ls"], help="List tasks")
301
+ p_list.add_argument("--mine", action="store_true",
302
+ help="Filter by $HERMES_PROFILE as assignee")
303
+ p_list.add_argument("--assignee", default=None)
304
+ p_list.add_argument("--status", default=None,
305
+ choices=sorted(kb.VALID_STATUSES))
306
+ p_list.add_argument("--tenant", default=None)
307
+ p_list.add_argument("--archived", action="store_true",
308
+ help="Include archived tasks")
309
+ p_list.add_argument("--json", action="store_true")
310
+
311
+ # --- show ---
312
+ p_show = sub.add_parser("show", help="Show a task with comments + events")
313
+ p_show.add_argument("task_id")
314
+ p_show.add_argument("--json", action="store_true")
315
+
316
+ # --- assign ---
317
+ p_assign = sub.add_parser("assign", help="Assign or reassign a task")
318
+ p_assign.add_argument("task_id")
319
+ p_assign.add_argument("profile", help="Profile name (or 'none' to unassign)")
320
+
321
+ # --- reclaim / reassign (recovery) ---
322
+ p_reclaim = sub.add_parser(
323
+ "reclaim",
324
+ help="Release an active worker claim on a running task",
325
+ )
326
+ p_reclaim.add_argument("task_id")
327
+ p_reclaim.add_argument(
328
+ "--reason", default=None,
329
+ help="Human-readable reason (recorded on the reclaimed event)",
330
+ )
331
+
332
+ p_reassign = sub.add_parser(
333
+ "reassign",
334
+ help="Reassign a task to a different profile, optionally reclaiming first",
335
+ )
336
+ p_reassign.add_argument("task_id")
337
+ p_reassign.add_argument(
338
+ "profile",
339
+ help="New profile name (or 'none' to unassign)",
340
+ )
341
+ p_reassign.add_argument(
342
+ "--reclaim", action="store_true",
343
+ help="Release any active claim before reassigning (required if task is running)",
344
+ )
345
+ p_reassign.add_argument(
346
+ "--reason", default=None,
347
+ help="Human-readable reason (recorded on the reclaimed event)",
348
+ )
349
+
350
+ # --- diagnostics (board-wide health) ---
351
+ p_diag = sub.add_parser(
352
+ "diagnostics",
353
+ aliases=["diag"],
354
+ help="List active diagnostics on the current board",
355
+ )
356
+ p_diag.add_argument(
357
+ "--severity",
358
+ choices=["warning", "error", "critical"],
359
+ default=None,
360
+ help="Only show diagnostics at or above this severity",
361
+ )
362
+ p_diag.add_argument(
363
+ "--task",
364
+ default=None,
365
+ help="Only show diagnostics for one task id",
366
+ )
367
+ p_diag.add_argument(
368
+ "--json", action="store_true",
369
+ help="Emit JSON (structured) instead of the default human table",
370
+ )
371
+
372
+ # --- link / unlink ---
373
+ p_link = sub.add_parser("link", help="Add a parent->child dependency")
374
+ p_link.add_argument("parent_id")
375
+ p_link.add_argument("child_id")
376
+ p_unlink = sub.add_parser("unlink", help="Remove a parent->child dependency")
377
+ p_unlink.add_argument("parent_id")
378
+ p_unlink.add_argument("child_id")
379
+
380
+ # --- claim ---
381
+ p_claim = sub.add_parser(
382
+ "claim",
383
+ help="Atomically claim a ready task (prints resolved workspace path)",
384
+ )
385
+ p_claim.add_argument("task_id")
386
+ p_claim.add_argument("--ttl", type=int, default=kb.DEFAULT_CLAIM_TTL_SECONDS,
387
+ help="Claim TTL in seconds (default: 900)")
388
+
389
+ # --- comment / complete / block / unblock / archive ---
390
+ p_comment = sub.add_parser("comment", help="Append a comment")
391
+ p_comment.add_argument("task_id")
392
+ p_comment.add_argument("text", nargs="+", help="Comment body")
393
+ p_comment.add_argument("--author", default=None,
394
+ help="Author name (default: $HERMES_PROFILE or 'user')")
395
+
396
+ p_complete = sub.add_parser("complete", help="Mark one or more tasks done")
397
+ p_complete.add_argument("task_ids", nargs="+",
398
+ help="One or more task ids (only --result applies to all of them)")
399
+ p_complete.add_argument("--result", default=None, help="Result summary")
400
+ p_complete.add_argument("--summary", default=None,
401
+ help="Structured handoff summary for downstream tasks. "
402
+ "Falls back to --result if omitted.")
403
+ p_complete.add_argument("--metadata", default=None,
404
+ help='JSON dict of structured facts (e.g. \'{"changed_files": [...], '
405
+ '"tests_run": 12}\'). Stored on the closing run.')
406
+
407
+ p_edit = sub.add_parser(
408
+ "edit",
409
+ help="Edit recovery fields on an already-completed task",
410
+ )
411
+ p_edit.add_argument("task_id")
412
+ p_edit.add_argument(
413
+ "--result",
414
+ required=True,
415
+ help="Backfilled task result text for a done task",
416
+ )
417
+ p_edit.add_argument(
418
+ "--summary",
419
+ default=None,
420
+ help="Structured handoff summary. Falls back to --result if omitted.",
421
+ )
422
+ p_edit.add_argument(
423
+ "--metadata",
424
+ default=None,
425
+ help="JSON dict of structured facts to store on the latest completed run.",
426
+ )
427
+
428
+ p_block = sub.add_parser("block", help="Mark one or more tasks blocked")
429
+ p_block.add_argument("task_id")
430
+ p_block.add_argument("reason", nargs="*", help="Reason (also appended as a comment)")
431
+ p_block.add_argument("--ids", nargs="+", default=None,
432
+ help="Additional task ids to block with the same reason (bulk mode)")
433
+
434
+ p_unblock = sub.add_parser("unblock", help="Return one or more blocked tasks to ready")
435
+ p_unblock.add_argument("task_ids", nargs="+")
436
+
437
+ p_archive = sub.add_parser("archive", help="Archive one or more tasks")
438
+ p_archive.add_argument("task_ids", nargs="+")
439
+
440
+ # --- tail ---
441
+ p_tail = sub.add_parser("tail", help="Follow a task's event stream")
442
+ p_tail.add_argument("task_id")
443
+ p_tail.add_argument("--interval", type=float, default=1.0)
444
+
445
+ # --- dispatch ---
446
+ p_disp = sub.add_parser(
447
+ "dispatch",
448
+ help="One dispatcher pass: reclaim stale, promote ready, spawn workers",
449
+ )
450
+ p_disp.add_argument("--dry-run", action="store_true",
451
+ help="Don't actually spawn processes; just print what would happen")
452
+ p_disp.add_argument("--max", type=int, default=None,
453
+ help="Cap number of spawns this pass")
454
+ p_disp.add_argument("--failure-limit", type=int,
455
+ default=kb.DEFAULT_SPAWN_FAILURE_LIMIT,
456
+ help=f"Auto-block a task after this many consecutive non-success attempts "
457
+ f"(spawn_failed, timed_out, or crashed; default: {kb.DEFAULT_SPAWN_FAILURE_LIMIT})")
458
+ p_disp.add_argument("--json", action="store_true")
459
+
460
+ # --- daemon (deprecated) ---
461
+ p_daemon = sub.add_parser(
462
+ "daemon",
463
+ help="DEPRECATED — dispatcher now runs in the gateway. Use `hermes gateway start`.",
464
+ )
465
+ p_daemon.add_argument("--interval", type=float, default=60.0,
466
+ help="Seconds between dispatch ticks (default: 60)")
467
+ p_daemon.add_argument("--max", type=int, default=None,
468
+ help="Cap number of spawns per tick")
469
+ p_daemon.add_argument("--failure-limit", type=int,
470
+ default=kb.DEFAULT_SPAWN_FAILURE_LIMIT)
471
+ p_daemon.add_argument("--pidfile", default=None,
472
+ help="Write the daemon's PID to this file on start")
473
+ p_daemon.add_argument("--verbose", "-v", action="store_true",
474
+ help="Log each tick's outcome to stdout")
475
+ # Undocumented escape hatch for users who truly cannot run the gateway.
476
+ # Intentionally excluded from --help so nobody discovers it casually and
477
+ # keeps the old double-dispatcher pattern alive.
478
+ p_daemon.add_argument("--force", action="store_true",
479
+ help=argparse.SUPPRESS)
480
+
481
+ # --- watch ---
482
+ p_watch = sub.add_parser(
483
+ "watch",
484
+ help="Live-stream task_events to the terminal (Ctrl+C to exit)",
485
+ )
486
+ p_watch.add_argument("--assignee", default=None,
487
+ help="Only show events for tasks assigned to this profile")
488
+ p_watch.add_argument("--tenant", default=None,
489
+ help="Only show events from tasks in this tenant")
490
+ p_watch.add_argument("--kinds", default=None,
491
+ help="Comma-separated event kinds to include "
492
+ "(e.g. 'completed,blocked,gave_up,crashed,timed_out')")
493
+ p_watch.add_argument("--interval", type=float, default=0.5,
494
+ help="Poll interval in seconds (default: 0.5)")
495
+
496
+ # --- stats ---
497
+ p_stats = sub.add_parser(
498
+ "stats", help="Per-status + per-assignee counts + oldest-ready age",
499
+ )
500
+ p_stats.add_argument("--json", action="store_true")
501
+
502
+ # --- notify subscribe / list / remove ---
503
+ p_nsub = sub.add_parser(
504
+ "notify-subscribe",
505
+ help="Subscribe a gateway source to a task's terminal events "
506
+ "(used by /kanban subscribe in the gateway adapter)",
507
+ )
508
+ p_nsub.add_argument("task_id")
509
+ p_nsub.add_argument("--platform", required=True)
510
+ p_nsub.add_argument("--chat-id", required=True)
511
+ p_nsub.add_argument("--thread-id", default=None)
512
+ p_nsub.add_argument("--user-id", default=None)
513
+ p_nsub.add_argument(
514
+ "--notifier-profile", default=None,
515
+ help="Profile gateway that owns/delivers this subscription (default: active profile)",
516
+ )
517
+
518
+ p_nlist = sub.add_parser(
519
+ "notify-list",
520
+ help="List notification subscriptions (optionally for a single task)",
521
+ )
522
+ p_nlist.add_argument("task_id", nargs="?", default=None)
523
+ p_nlist.add_argument("--json", action="store_true")
524
+
525
+ p_nrm = sub.add_parser(
526
+ "notify-unsubscribe",
527
+ help="Remove a gateway subscription from a task",
528
+ )
529
+ p_nrm.add_argument("task_id")
530
+ p_nrm.add_argument("--platform", required=True)
531
+ p_nrm.add_argument("--chat-id", required=True)
532
+ p_nrm.add_argument("--thread-id", default=None)
533
+
534
+ # --- log ---
535
+ p_log = sub.add_parser(
536
+ "log",
537
+ help="Print the worker log for a task (from <kanban-root>/kanban/logs/)",
538
+ )
539
+ p_log.add_argument("task_id")
540
+ p_log.add_argument("--tail", type=int, default=None,
541
+ help="Only print the last N bytes")
542
+
543
+ # --- runs (per-attempt history for a task) ---
544
+ p_runs = sub.add_parser(
545
+ "runs",
546
+ help="Show attempt history for a task (one row per run: profile, "
547
+ "outcome, elapsed, summary)",
548
+ )
549
+ p_runs.add_argument("task_id")
550
+ p_runs.add_argument("--json", action="store_true")
551
+
552
+ # --- heartbeat (worker liveness signal) ---
553
+ p_hb = sub.add_parser(
554
+ "heartbeat",
555
+ help="Emit a heartbeat event for a running task (worker liveness signal)",
556
+ )
557
+ p_hb.add_argument("task_id")
558
+ p_hb.add_argument("--note", default=None,
559
+ help="Optional short note attached to the heartbeat event")
560
+
561
+ # --- assignees ---
562
+ p_asg = sub.add_parser(
563
+ "assignees",
564
+ help="List known profiles + per-profile task counts "
565
+ "(union of ~/.hermes/profiles/ and current assignees on the board)",
566
+ )
567
+ p_asg.add_argument("--json", action="store_true")
568
+
569
+ # --- context --- (for spawned workers)
570
+ p_ctx = sub.add_parser(
571
+ "context",
572
+ help="Print the full context a worker sees for a task "
573
+ "(title + body + parent results + comments).",
574
+ )
575
+ p_ctx.add_argument("task_id")
576
+
577
+ # --- specify --- (triage → todo via auxiliary LLM)
578
+ p_specify = sub.add_parser(
579
+ "specify",
580
+ help="Flesh out a triage-column task into a concrete spec "
581
+ "(title + body) and promote it to todo. Uses the auxiliary "
582
+ "LLM configured under auxiliary.triage_specifier.",
583
+ )
584
+ p_specify.add_argument(
585
+ "task_id",
586
+ nargs="?",
587
+ default=None,
588
+ help="Task id to specify (required unless --all is given)",
589
+ )
590
+ p_specify.add_argument(
591
+ "--all",
592
+ dest="all_triage",
593
+ action="store_true",
594
+ help="Specify every task currently in the triage column",
595
+ )
596
+ p_specify.add_argument(
597
+ "--tenant",
598
+ default=None,
599
+ help="When used with --all, restrict the sweep to this tenant",
600
+ )
601
+ p_specify.add_argument(
602
+ "--author",
603
+ default=None,
604
+ help="Author name recorded on the audit comment "
605
+ "(default: $HERMES_PROFILE or 'specifier')",
606
+ )
607
+ p_specify.add_argument(
608
+ "--json",
609
+ action="store_true",
610
+ help="Emit one JSON object per task on stdout",
611
+ )
612
+
613
+ # --- gc ---
614
+ p_gc = sub.add_parser(
615
+ "gc", help="Garbage-collect archived-task workspaces, old events, and old logs",
616
+ )
617
+ p_gc.add_argument("--event-retention-days", type=int, default=30,
618
+ help="Delete task_events older than N days for terminal tasks (default: 30)")
619
+ p_gc.add_argument("--log-retention-days", type=int, default=30,
620
+ help="Delete worker log files older than N days (default: 30)")
621
+
622
+ kanban_parser.set_defaults(_kanban_parser=kanban_parser)
623
+ return kanban_parser
624
+
625
+
626
+ # ---------------------------------------------------------------------------
627
+ # Command dispatch
628
+ # ---------------------------------------------------------------------------
629
+
630
+ def kanban_command(args: argparse.Namespace) -> int:
631
+ """Entry point from ``hermes kanban …`` argparse dispatch.
632
+
633
+ Returns a shell-style exit code (0 on success, non-zero on error).
634
+ """
635
+ action = getattr(args, "kanban_action", None)
636
+ if not action:
637
+ # No subaction given: print help via the stored parser reference.
638
+ parser = getattr(args, "_kanban_parser", None)
639
+ if parser is not None:
640
+ parser.print_help()
641
+ else:
642
+ print(
643
+ "usage: hermes kanban <action> [options]\n"
644
+ "Run 'hermes kanban --help' for the full list of actions.",
645
+ file=sys.stderr,
646
+ )
647
+ return 0
648
+
649
+ # `--board <slug>` applies to every subcommand below by way of an
650
+ # env-var pin for the duration of this call. Using HERMES_KANBAN_BOARD
651
+ # (rather than threading `board=` through 50+ kb.connect() sites)
652
+ # keeps the patch small and inherits the exact same resolution the
653
+ # dispatcher uses for workers — consistency is a feature here.
654
+ board_override = getattr(args, "board", None)
655
+ prev_board_env = os.environ.get("HERMES_KANBAN_BOARD")
656
+ restore_board_env = False
657
+
658
+ def _restore_board_env() -> None:
659
+ if not restore_board_env:
660
+ return
661
+ if prev_board_env is None:
662
+ os.environ.pop("HERMES_KANBAN_BOARD", None)
663
+ else:
664
+ os.environ["HERMES_KANBAN_BOARD"] = prev_board_env
665
+ if board_override:
666
+ try:
667
+ normed = kb._normalize_board_slug(board_override)
668
+ except ValueError as exc:
669
+ print(f"kanban: {exc}", file=sys.stderr)
670
+ return 2
671
+ if not normed:
672
+ print("kanban: --board requires a slug", file=sys.stderr)
673
+ return 2
674
+ # Boards other than 'default' must already exist — typoed slugs
675
+ # would otherwise silently create an empty board.
676
+ if normed != kb.DEFAULT_BOARD and not kb.board_exists(normed):
677
+ print(
678
+ f"kanban: board {normed!r} does not exist. "
679
+ f"Create it with `hermes kanban boards create {normed}`.",
680
+ file=sys.stderr,
681
+ )
682
+ return 1
683
+ os.environ["HERMES_KANBAN_BOARD"] = normed
684
+ restore_board_env = True
685
+
686
+ # Boards management doesn't touch the DB at all — dispatch early so
687
+ # fresh installs that haven't initialized any DB can still use
688
+ # `hermes kanban boards create …`.
689
+ if action == "boards":
690
+ try:
691
+ return _dispatch_boards(args)
692
+ finally:
693
+ _restore_board_env()
694
+
695
+ # Auto-initialize the DB before dispatching any subcommand. init_db
696
+ # is idempotent, so running it every invocation is cheap (one
697
+ # SELECT against sqlite_master when tables already exist) and
698
+ # prevents "no such table: tasks" on first use from a fresh
699
+ # HERMES_HOME. Previously only `init` and `daemon` triggered
700
+ # schema creation; `create` / `list` / every other command would
701
+ # error out on a fresh install.
702
+ try:
703
+ kb.init_db()
704
+ except Exception as exc:
705
+ print(f"kanban: could not initialize database: {exc}", file=sys.stderr)
706
+ _restore_board_env()
707
+ return 1
708
+
709
+ handlers = {
710
+ "init": _cmd_init,
711
+ "create": _cmd_create,
712
+ "list": _cmd_list,
713
+ "ls": _cmd_list,
714
+ "show": _cmd_show,
715
+ "assign": _cmd_assign,
716
+ "reclaim": _cmd_reclaim,
717
+ "reassign": _cmd_reassign,
718
+ "diagnostics": _cmd_diagnostics,
719
+ "diag": _cmd_diagnostics,
720
+ "link": _cmd_link,
721
+ "unlink": _cmd_unlink,
722
+ "claim": _cmd_claim,
723
+ "comment": _cmd_comment,
724
+ "complete": _cmd_complete,
725
+ "edit": _cmd_edit,
726
+ "block": _cmd_block,
727
+ "unblock": _cmd_unblock,
728
+ "archive": _cmd_archive,
729
+ "tail": _cmd_tail,
730
+ "dispatch": _cmd_dispatch,
731
+ "daemon": _cmd_daemon,
732
+ "watch": _cmd_watch,
733
+ "stats": _cmd_stats,
734
+ "log": _cmd_log,
735
+ "runs": _cmd_runs,
736
+ "heartbeat": _cmd_heartbeat,
737
+ "assignees": _cmd_assignees,
738
+ "notify-subscribe": _cmd_notify_subscribe,
739
+ "notify-list": _cmd_notify_list,
740
+ "notify-unsubscribe": _cmd_notify_unsubscribe,
741
+ "context": _cmd_context,
742
+ "specify": _cmd_specify,
743
+ "gc": _cmd_gc,
744
+ }
745
+ handler = handlers.get(action)
746
+ if not handler:
747
+ print(f"kanban: unknown action {action!r}", file=sys.stderr)
748
+ _restore_board_env()
749
+ return 2
750
+ try:
751
+ return int(handler(args) or 0)
752
+ except (ValueError, RuntimeError) as exc:
753
+ print(f"kanban: {exc}", file=sys.stderr)
754
+ _restore_board_env()
755
+ return 1
756
+ finally:
757
+ _restore_board_env()
758
+
759
+
760
+ # ---------------------------------------------------------------------------
761
+ # Handlers
762
+ # ---------------------------------------------------------------------------
763
+
764
+ def _profile_author() -> str:
765
+ """Best-effort author name for an interactive CLI call."""
766
+ for env in ("HERMES_PROFILE_NAME", "HERMES_PROFILE"):
767
+ v = os.environ.get(env)
768
+ if v:
769
+ return v
770
+ try:
771
+ from hermes_cli.profiles import get_active_profile_name
772
+ return get_active_profile_name() or "user"
773
+ except Exception:
774
+ return "user"
775
+
776
+
777
+ # ---------------------------------------------------------------------------
778
+ # Boards management (hermes kanban boards …)
779
+ # ---------------------------------------------------------------------------
780
+
781
+ def _dispatch_boards(args: argparse.Namespace) -> int:
782
+ """Handle ``hermes kanban boards <action>``.
783
+
784
+ Boards management is deliberately separate from the task-level
785
+ commands: it operates on the filesystem (board directories,
786
+ ``current`` pointer, ``board.json``), not on the per-board SQLite
787
+ DB, so a fresh HERMES_HOME that has never called ``kanban init``
788
+ can still run ``boards create`` / ``boards list``.
789
+ """
790
+ sub = getattr(args, "boards_action", None) or "list"
791
+ if sub in {"list", "ls"}:
792
+ return _cmd_boards_list(args)
793
+ if sub in {"create", "new"}:
794
+ return _cmd_boards_create(args)
795
+ if sub in {"rm", "remove", "delete"}:
796
+ return _cmd_boards_rm(args)
797
+ if sub in {"switch", "use"}:
798
+ return _cmd_boards_switch(args)
799
+ if sub in {"show", "current"}:
800
+ return _cmd_boards_show(args)
801
+ if sub == "rename":
802
+ return _cmd_boards_rename(args)
803
+ print(f"kanban boards: unknown action {sub!r}", file=sys.stderr)
804
+ return 2
805
+
806
+
807
+ def _board_task_counts(slug: str) -> dict[str, int]:
808
+ """Return ``{status: count}`` for a board. Safe to call on an empty DB."""
809
+ try:
810
+ path = kb.kanban_db_path(board=slug)
811
+ if not path.exists():
812
+ return {}
813
+ with kb.connect(board=slug) as conn:
814
+ rows = conn.execute(
815
+ "SELECT status, COUNT(*) AS n FROM tasks GROUP BY status"
816
+ ).fetchall()
817
+ return {r["status"]: int(r["n"]) for r in rows}
818
+ except Exception:
819
+ return {}
820
+
821
+
822
+ def _cmd_boards_list(args: argparse.Namespace) -> int:
823
+ include_archived = bool(getattr(args, "all", False))
824
+ boards = kb.list_boards(include_archived=include_archived)
825
+ # Enrich each entry with task counts + whether it's the current board.
826
+ current = kb.get_current_board()
827
+ for b in boards:
828
+ b["is_current"] = (b["slug"] == current)
829
+ b["counts"] = _board_task_counts(b["slug"])
830
+ b["total"] = sum(b["counts"].values())
831
+ if getattr(args, "json", False):
832
+ print(json.dumps(boards, indent=2, ensure_ascii=False))
833
+ return 0
834
+ # Human table: marker (•) for current, slug, display name, counts.
835
+ if not boards:
836
+ print("(no boards — create one with `hermes kanban boards create <slug>`)")
837
+ return 0
838
+ print(f"{'':2s} {'SLUG':24s} {'NAME':28s} COUNTS")
839
+ for b in boards:
840
+ marker = "●" if b["is_current"] else " "
841
+ counts = b["counts"] or {}
842
+ counts_str = (
843
+ ", ".join(f"{k}={v}" for k, v in sorted(counts.items()))
844
+ or "(empty)"
845
+ )
846
+ name = b.get("name") or ""
847
+ if b.get("archived"):
848
+ name += " [archived]"
849
+ print(f"{marker:2s} {b['slug']:24s} {name:28s} {counts_str}")
850
+ print()
851
+ print(f"Current board: {current}")
852
+ if len(boards) > 1:
853
+ print("Switch boards with `hermes kanban boards switch <slug>`.")
854
+ return 0
855
+
856
+
857
+ def _cmd_boards_create(args: argparse.Namespace) -> int:
858
+ try:
859
+ normed = kb._normalize_board_slug(args.slug)
860
+ except ValueError as exc:
861
+ print(f"kanban boards create: {exc}", file=sys.stderr)
862
+ return 2
863
+ if not normed:
864
+ print("kanban boards create: slug is required", file=sys.stderr)
865
+ return 2
866
+ already = kb.board_exists(normed) and normed != kb.DEFAULT_BOARD
867
+ meta = kb.create_board(
868
+ normed,
869
+ name=args.name,
870
+ description=args.description,
871
+ icon=args.icon,
872
+ color=args.color,
873
+ )
874
+ verb = "already exists" if already else "created"
875
+ print(f"Board {meta['slug']!r} {verb}.")
876
+ print(f" Display name: {meta.get('name', '')}")
877
+ print(f" DB path: {meta['db_path']}")
878
+ if getattr(args, "switch", False):
879
+ kb.set_current_board(meta["slug"])
880
+ print(f" Switched to {meta['slug']!r}.")
881
+ else:
882
+ print(f" Use `hermes kanban boards switch {meta['slug']}` to make it current.")
883
+ return 0
884
+
885
+
886
+ def _cmd_boards_rm(args: argparse.Namespace) -> int:
887
+ try:
888
+ res = kb.remove_board(args.slug, archive=not getattr(args, "delete", False))
889
+ except ValueError as exc:
890
+ print(f"kanban boards rm: {exc}", file=sys.stderr)
891
+ return 1
892
+ if res["action"] == "archived":
893
+ print(f"Board {res['slug']!r} archived → {res['new_path']}")
894
+ print("Recover by moving the directory back to "
895
+ "<root>/kanban/boards/<slug>/.")
896
+ else:
897
+ print(f"Board {res['slug']!r} deleted.")
898
+ return 0
899
+
900
+
901
+ def _cmd_boards_switch(args: argparse.Namespace) -> int:
902
+ try:
903
+ normed = kb._normalize_board_slug(args.slug)
904
+ except ValueError as exc:
905
+ print(f"kanban boards switch: {exc}", file=sys.stderr)
906
+ return 2
907
+ if not normed:
908
+ print("kanban boards switch: slug is required", file=sys.stderr)
909
+ return 2
910
+ if not kb.board_exists(normed):
911
+ print(
912
+ f"kanban boards switch: board {normed!r} does not exist. "
913
+ f"Create it with `hermes kanban boards create {normed}`.",
914
+ file=sys.stderr,
915
+ )
916
+ return 1
917
+ kb.set_current_board(normed)
918
+ print(f"Active board is now {normed!r}.")
919
+ return 0
920
+
921
+
922
+ def _cmd_boards_show(args: argparse.Namespace) -> int:
923
+ current = kb.get_current_board()
924
+ meta = kb.read_board_metadata(current)
925
+ counts = _board_task_counts(current)
926
+ total = sum(counts.values())
927
+ print(f"Current board: {current}")
928
+ print(f" Display name: {meta.get('name', '')}")
929
+ if meta.get("description"):
930
+ print(f" Description: {meta['description']}")
931
+ print(f" DB path: {meta['db_path']}")
932
+ print(f" Tasks: {total} total"
933
+ + (f" ({', '.join(f'{k}={v}' for k, v in sorted(counts.items()))})"
934
+ if counts else ""))
935
+ return 0
936
+
937
+
938
+ def _cmd_boards_rename(args: argparse.Namespace) -> int:
939
+ try:
940
+ normed = kb._normalize_board_slug(args.slug)
941
+ except ValueError as exc:
942
+ print(f"kanban boards rename: {exc}", file=sys.stderr)
943
+ return 2
944
+ if not normed or not kb.board_exists(normed):
945
+ print(f"kanban boards rename: board {args.slug!r} does not exist",
946
+ file=sys.stderr)
947
+ return 1
948
+ meta = kb.write_board_metadata(normed, name=args.name)
949
+ print(f"Board {normed!r} renamed to {meta['name']!r}.")
950
+ return 0
951
+
952
+
953
+ # ---------------------------------------------------------------------------
954
+
955
+
956
+ def _parse_duration(val) -> Optional[int]:
957
+ """Parse ``30s`` / ``5m`` / ``2h`` / ``1d`` or a raw integer → seconds.
958
+
959
+ Returns None for empty input. Raises ValueError on malformed input so
960
+ the CLI can surface a usage error cleanly.
961
+ """
962
+ if val is None or val == "":
963
+ return None
964
+ s = str(val).strip().lower()
965
+ # Bare integer → seconds.
966
+ try:
967
+ return int(s)
968
+ except ValueError:
969
+ pass
970
+ # Suffixed form.
971
+ units = {"s": 1, "m": 60, "h": 3600, "d": 86400}
972
+ if s and s[-1] in units:
973
+ try:
974
+ n = float(s[:-1])
975
+ except ValueError as exc:
976
+ raise ValueError(f"malformed duration {val!r}") from exc
977
+ return int(n * units[s[-1]])
978
+ raise ValueError(f"malformed duration {val!r} (expected 30s, 5m, 2h, 1d, or a number)")
979
+
980
+
981
+ def _cmd_init(args: argparse.Namespace) -> int:
982
+ path = kb.init_db()
983
+ print(f"Kanban DB initialized at {path}")
984
+ print()
985
+ # Enumerate profiles on disk so the user knows what assignees are
986
+ # already addressable. Multica does this auto-detection on its
987
+ # daemon start; we do it here at init time instead because our
988
+ # dispatcher doesn't need to enumerate — we just pass the name
989
+ # through to `hermes -p <name>`.
990
+ try:
991
+ profiles = kb.list_profiles_on_disk()
992
+ except Exception:
993
+ profiles = []
994
+ if profiles:
995
+ print(f"Discovered {len(profiles)} profile(s) on disk; any of these can "
996
+ f"be an --assignee:")
997
+ for name in profiles:
998
+ print(f" {name}")
999
+ else:
1000
+ print("No profiles found under ~/.hermes/profiles/.")
1001
+ print("Create one with `hermes -p <name> setup` before assigning tasks.")
1002
+ print()
1003
+ print("Next step: start the gateway so ready tasks actually get picked up.")
1004
+ print(" hermes gateway start")
1005
+ print()
1006
+ print(
1007
+ "The gateway hosts an embedded dispatcher that ticks every 60 seconds\n"
1008
+ "by default (config: kanban.dispatch_interval_seconds). Without a\n"
1009
+ "running gateway, tasks stay in 'ready' forever."
1010
+ )
1011
+ return 0
1012
+
1013
+
1014
+ def _cmd_heartbeat(args: argparse.Namespace) -> int:
1015
+ with kb.connect() as conn:
1016
+ ok = kb.heartbeat_worker(
1017
+ conn,
1018
+ args.task_id,
1019
+ note=getattr(args, "note", None),
1020
+ expected_run_id=_worker_run_id_for(args.task_id),
1021
+ )
1022
+ if not ok:
1023
+ print(f"cannot heartbeat {args.task_id} (not running?)", file=sys.stderr)
1024
+ return 1
1025
+ print(f"Heartbeat recorded for {args.task_id}")
1026
+ return 0
1027
+
1028
+
1029
+ def _cmd_assignees(args: argparse.Namespace) -> int:
1030
+ with kb.connect() as conn:
1031
+ data = kb.known_assignees(conn)
1032
+ if getattr(args, "json", False):
1033
+ print(json.dumps(data, indent=2, ensure_ascii=False))
1034
+ return 0
1035
+ if not data:
1036
+ print("(no assignees — create a profile with `hermes -p <name> setup`)")
1037
+ return 0
1038
+ # Header
1039
+ print(f"{'NAME':20s} {'ON DISK':8s} COUNTS")
1040
+ for entry in data:
1041
+ on_disk = "yes" if entry["on_disk"] else "no"
1042
+ counts = entry["counts"] or {}
1043
+ count_str = ", ".join(f"{k}={v}" for k, v in sorted(counts.items())) or "(idle)"
1044
+ print(f"{entry['name']:20s} {on_disk:8s} {count_str}")
1045
+ return 0
1046
+
1047
+
1048
+ def _cmd_create(args: argparse.Namespace) -> int:
1049
+ ws_kind, ws_path = _parse_workspace_flag(args.workspace)
1050
+ try:
1051
+ max_runtime = _parse_duration(getattr(args, "max_runtime", None))
1052
+ except ValueError as exc:
1053
+ print(f"kanban: --max-runtime: {exc}", file=sys.stderr)
1054
+ return 2
1055
+ max_retries = getattr(args, "max_retries", None)
1056
+ if max_retries is not None and max_retries < 1:
1057
+ print(
1058
+ f"kanban: --max-retries must be >= 1 (got {max_retries}); "
1059
+ "use 1 to trip on the first failure.",
1060
+ file=sys.stderr,
1061
+ )
1062
+ return 2
1063
+ with kb.connect() as conn:
1064
+ task_id = kb.create_task(
1065
+ conn,
1066
+ title=args.title,
1067
+ body=args.body,
1068
+ assignee=args.assignee,
1069
+ created_by=args.created_by or _profile_author(),
1070
+ workspace_kind=ws_kind,
1071
+ workspace_path=ws_path,
1072
+ tenant=args.tenant,
1073
+ priority=args.priority,
1074
+ parents=tuple(args.parent or ()),
1075
+ triage=bool(getattr(args, "triage", False)),
1076
+ idempotency_key=getattr(args, "idempotency_key", None),
1077
+ max_runtime_seconds=max_runtime,
1078
+ skills=getattr(args, "skills", None) or None,
1079
+ max_retries=max_retries,
1080
+ )
1081
+ task = kb.get_task(conn, task_id)
1082
+ if getattr(args, "json", False):
1083
+ print(json.dumps(_task_to_dict(task), indent=2, ensure_ascii=False))
1084
+ else:
1085
+ print(f"Created {task_id} ({task.status}, assignee={task.assignee or '-'})")
1086
+
1087
+ # Warn when the task would sit in `ready` because no dispatcher is
1088
+ # present. Only warn on ready+assigned tasks — triage/todo are
1089
+ # expected to sit idle until promoted, and unassigned tasks
1090
+ # can't be dispatched. Skipped in --json mode so the stdout
1091
+ # stream stays strictly machine-parseable for callers (the JSON
1092
+ # response itself carries enough info for them to decide if
1093
+ # they want to check dispatcher presence separately).
1094
+ if task.status == "ready" and task.assignee:
1095
+ running, message = _check_dispatcher_presence()
1096
+ if not running and message:
1097
+ print(f"\n⚠ {message}", file=sys.stderr)
1098
+ return 0
1099
+
1100
+
1101
+ def _cmd_list(args: argparse.Namespace) -> int:
1102
+ assignee = args.assignee
1103
+ if args.mine and not assignee:
1104
+ assignee = _profile_author()
1105
+ with kb.connect() as conn:
1106
+ # Cheap "mini-dispatch": recompute ready so list output reflects
1107
+ # dependencies that may have cleared since the last dispatcher tick.
1108
+ kb.recompute_ready(conn)
1109
+ tasks = kb.list_tasks(
1110
+ conn,
1111
+ assignee=assignee,
1112
+ status=args.status,
1113
+ tenant=args.tenant,
1114
+ include_archived=args.archived,
1115
+ )
1116
+ if getattr(args, "json", False):
1117
+ print(json.dumps([_task_to_dict(t) for t in tasks], indent=2, ensure_ascii=False))
1118
+ return 0
1119
+ # Passive discoverability: when the user has multiple boards, surface
1120
+ # which one they're looking at in the list header. Single-board users
1121
+ # never see this — the feature stays invisible until you opt in.
1122
+ try:
1123
+ all_boards = kb.list_boards(include_archived=False)
1124
+ except Exception:
1125
+ all_boards = []
1126
+ if len(all_boards) > 1:
1127
+ current = kb.get_current_board()
1128
+ other_count = len(all_boards) - 1
1129
+ print(
1130
+ f"Board: {current} "
1131
+ f"({other_count} other board{'s' if other_count != 1 else ''} — "
1132
+ f"`hermes kanban boards list`)\n"
1133
+ )
1134
+ if not tasks:
1135
+ print("(no matching tasks)")
1136
+ return 0
1137
+ for t in tasks:
1138
+ print(_fmt_task_line(t))
1139
+ return 0
1140
+
1141
+
1142
+ def _cmd_show(args: argparse.Namespace) -> int:
1143
+ with kb.connect() as conn:
1144
+ task = kb.get_task(conn, args.task_id)
1145
+ if not task:
1146
+ print(f"no such task: {args.task_id}", file=sys.stderr)
1147
+ return 1
1148
+ comments = kb.list_comments(conn, args.task_id)
1149
+ events = kb.list_events(conn, args.task_id)
1150
+ parents = kb.parent_ids(conn, args.task_id)
1151
+ children = kb.child_ids(conn, args.task_id)
1152
+ runs = kb.list_runs(conn, args.task_id)
1153
+ # Workers hand off via ``task_runs.summary`` (kanban-worker skill);
1154
+ # ``tasks.result`` is left NULL unless the caller explicitly passed
1155
+ # ``result=``. Surfacing the latest summary here keeps ``show`` from
1156
+ # looking like a no-op when the worker actually did real work.
1157
+ latest_summary = kb.latest_summary(conn, args.task_id)
1158
+
1159
+ if getattr(args, "json", False):
1160
+ payload = {
1161
+ "task": _task_to_dict(task),
1162
+ "latest_summary": latest_summary,
1163
+ "parents": parents,
1164
+ "children": children,
1165
+ "comments": [
1166
+ {"author": c.author, "body": c.body, "created_at": c.created_at}
1167
+ for c in comments
1168
+ ],
1169
+ "events": [
1170
+ {
1171
+ "kind": e.kind,
1172
+ "payload": e.payload,
1173
+ "created_at": e.created_at,
1174
+ "run_id": e.run_id,
1175
+ }
1176
+ for e in events
1177
+ ],
1178
+ "runs": [
1179
+ {
1180
+ "id": r.id,
1181
+ "profile": r.profile,
1182
+ "step_key": r.step_key,
1183
+ "status": r.status,
1184
+ "outcome": r.outcome,
1185
+ "summary": r.summary,
1186
+ "error": r.error,
1187
+ "metadata": r.metadata,
1188
+ "worker_pid": r.worker_pid,
1189
+ "started_at": r.started_at,
1190
+ "ended_at": r.ended_at,
1191
+ }
1192
+ for r in runs
1193
+ ],
1194
+ }
1195
+ print(json.dumps(payload, indent=2, ensure_ascii=False))
1196
+ return 0
1197
+
1198
+ print(f"Task {task.id}: {task.title}")
1199
+ print(f" status: {task.status}")
1200
+ print(f" assignee: {task.assignee or '-'}")
1201
+ if task.tenant:
1202
+ print(f" tenant: {task.tenant}")
1203
+ print(f" workspace: {task.workspace_kind}" +
1204
+ (f" @ {task.workspace_path}" if task.workspace_path else ""))
1205
+ if task.skills:
1206
+ print(f" skills: {', '.join(task.skills)}")
1207
+ # Effective retry threshold. Show the per-task override if set,
1208
+ # otherwise the dispatcher's resolved value from config (or the
1209
+ # default if config doesn't set it either). Helps operators see
1210
+ # why a task auto-blocked earlier/later than they expected.
1211
+ if task.max_retries is not None:
1212
+ print(f" max-retries: {task.max_retries} (task)")
1213
+ else:
1214
+ try:
1215
+ from hermes_cli.config import load_config
1216
+ cfg = load_config()
1217
+ cfg_val = (cfg.get("kanban", {}) or {}).get("failure_limit")
1218
+ except Exception:
1219
+ cfg_val = None
1220
+ if cfg_val is not None and int(cfg_val) != kb.DEFAULT_FAILURE_LIMIT:
1221
+ print(f" max-retries: {int(cfg_val)} (config kanban.failure_limit)")
1222
+ else:
1223
+ print(f" max-retries: {kb.DEFAULT_FAILURE_LIMIT} (default)")
1224
+ print(f" created: {_fmt_ts(task.created_at)} by {task.created_by or '-'}")
1225
+
1226
+ # Diagnostics section — surface active distress signals at the top
1227
+ # of show output so CLI users see them before scrolling through
1228
+ # comments / runs.
1229
+ from hermes_cli import kanban_diagnostics as kd
1230
+ diags = kd.compute_task_diagnostics(task, events, runs)
1231
+ if diags:
1232
+ sev_marker = {"warning": "⚠", "error": "!!", "critical": "!!!"}
1233
+ print(f"\n Diagnostics ({len(diags)}):")
1234
+ for d in diags:
1235
+ print(f" {sev_marker.get(d.severity, '?')} [{d.severity}] {d.title}")
1236
+ if d.data:
1237
+ bits = []
1238
+ for k, v in d.data.items():
1239
+ if isinstance(v, list):
1240
+ bits.append(f"{k}={','.join(str(x) for x in v)}")
1241
+ else:
1242
+ bits.append(f"{k}={v}")
1243
+ if bits:
1244
+ print(f" data: {' | '.join(bits)}")
1245
+ # Only show suggested actions in show output to keep it tight;
1246
+ # full list is available via `kanban diagnostics --task <id>`.
1247
+ for a in d.actions:
1248
+ if a.suggested:
1249
+ print(f" → {a.label}")
1250
+ if task.started_at:
1251
+ print(f" started: {_fmt_ts(task.started_at)}")
1252
+ if task.completed_at:
1253
+ print(f" completed: {_fmt_ts(task.completed_at)}")
1254
+ if parents:
1255
+ print(f" parents: {', '.join(parents)}")
1256
+ if children:
1257
+ print(f" children: {', '.join(children)}")
1258
+ if task.body:
1259
+ print()
1260
+ print("Body:")
1261
+ print(task.body)
1262
+ if task.result:
1263
+ print()
1264
+ print("Result:")
1265
+ print(task.result)
1266
+ elif latest_summary:
1267
+ # Worker handoff lives on the latest run, not on tasks.result.
1268
+ # Surface it at top-level so a glance at ``hermes kanban show <id>``
1269
+ # tells you what the worker did even if tasks.result is empty.
1270
+ print()
1271
+ print("Latest summary:")
1272
+ print(latest_summary)
1273
+ if comments:
1274
+ print()
1275
+ print(f"Comments ({len(comments)}):")
1276
+ for c in comments:
1277
+ print(f" [{_fmt_ts(c.created_at)}] {c.author}: {c.body}")
1278
+ if events:
1279
+ print()
1280
+ print(f"Events ({len(events)}):")
1281
+ for e in events[-20:]:
1282
+ pl = f" {e.payload}" if e.payload else ""
1283
+ run_tag = f" [run {e.run_id}]" if e.run_id else ""
1284
+ print(f" [{_fmt_ts(e.created_at)}]{run_tag} {e.kind}{pl}")
1285
+ if runs:
1286
+ print()
1287
+ print(f"Runs ({len(runs)}):")
1288
+ for r in runs:
1289
+ # Clamp to 0 so NTP backward-jumps don't print negative seconds.
1290
+ elapsed = (max(0, r.ended_at - r.started_at)
1291
+ if r.ended_at else None)
1292
+ el = f"{elapsed}s" if elapsed is not None else "active"
1293
+ outcome = r.outcome or r.status or "active"
1294
+ print(f" #{r.id:<3} {outcome:<12} @{r.profile or '-'} {el} "
1295
+ f"{_fmt_ts(r.started_at)}")
1296
+ if r.summary:
1297
+ print(f" → {r.summary.splitlines()[0][:160]}")
1298
+ if r.error:
1299
+ print(f" ! {r.error.splitlines()[0][:160]}")
1300
+ return 0
1301
+
1302
+
1303
+ def _cmd_assign(args: argparse.Namespace) -> int:
1304
+ profile = None if args.profile.lower() in {"none", "-", "null"} else args.profile
1305
+ with kb.connect() as conn:
1306
+ ok = kb.assign_task(conn, args.task_id, profile)
1307
+ if not ok:
1308
+ print(f"no such task: {args.task_id}", file=sys.stderr)
1309
+ return 1
1310
+ print(f"Assigned {args.task_id} to {profile or '(unassigned)'}")
1311
+ return 0
1312
+
1313
+
1314
+ def _cmd_reclaim(args: argparse.Namespace) -> int:
1315
+ with kb.connect() as conn:
1316
+ ok = kb.reclaim_task(
1317
+ conn, args.task_id,
1318
+ reason=getattr(args, "reason", None),
1319
+ )
1320
+ if not ok:
1321
+ print(
1322
+ f"cannot reclaim {args.task_id} (not running or unknown id)",
1323
+ file=sys.stderr,
1324
+ )
1325
+ return 1
1326
+ print(f"Reclaimed {args.task_id}")
1327
+ return 0
1328
+
1329
+
1330
+ def _cmd_reassign(args: argparse.Namespace) -> int:
1331
+ profile = None if args.profile.lower() in {"none", "-", "null"} else args.profile
1332
+ with kb.connect() as conn:
1333
+ ok = kb.reassign_task(
1334
+ conn, args.task_id, profile,
1335
+ reclaim_first=bool(getattr(args, "reclaim", False)),
1336
+ reason=getattr(args, "reason", None),
1337
+ )
1338
+ if not ok:
1339
+ print(
1340
+ f"cannot reassign {args.task_id} "
1341
+ f"(unknown id, or still running — pass --reclaim to release first)",
1342
+ file=sys.stderr,
1343
+ )
1344
+ return 1
1345
+ print(
1346
+ f"Reassigned {args.task_id} to "
1347
+ f"{profile or '(unassigned)'}"
1348
+ + (" (claim reclaimed)" if getattr(args, "reclaim", False) else "")
1349
+ )
1350
+ return 0
1351
+
1352
+
1353
+ def _cmd_diagnostics(args: argparse.Namespace) -> int:
1354
+ """List active diagnostics on the board. Wraps the same rule engine
1355
+ the dashboard uses, so CLI output matches what the UI shows.
1356
+ """
1357
+ from hermes_cli import kanban_diagnostics as kd
1358
+
1359
+ with kb.connect() as conn:
1360
+ # Either one-task mode or fleet mode.
1361
+ if getattr(args, "task", None):
1362
+ task = kb.get_task(conn, args.task)
1363
+ if task is None:
1364
+ print(f"no such task: {args.task}", file=sys.stderr)
1365
+ return 1
1366
+ diags_by_task = {
1367
+ args.task: kd.compute_task_diagnostics(
1368
+ task,
1369
+ kb.list_events(conn, args.task),
1370
+ kb.list_runs(conn, args.task),
1371
+ )
1372
+ }
1373
+ else:
1374
+ # Fleet mode: pull all non-archived tasks + their events/runs.
1375
+ rows = list(conn.execute(
1376
+ "SELECT * FROM tasks WHERE status != 'archived'"
1377
+ ).fetchall())
1378
+ ids = [r["id"] for r in rows]
1379
+ if not ids:
1380
+ diags_by_task = {}
1381
+ else:
1382
+ placeholders = ",".join(["?"] * len(ids))
1383
+ ev_by = {i: [] for i in ids}
1384
+ for row in conn.execute(
1385
+ f"SELECT * FROM task_events WHERE task_id IN ({placeholders}) ORDER BY id",
1386
+ tuple(ids),
1387
+ ):
1388
+ ev_by.setdefault(row["task_id"], []).append(row)
1389
+ run_by = {i: [] for i in ids}
1390
+ for row in conn.execute(
1391
+ f"SELECT * FROM task_runs WHERE task_id IN ({placeholders}) ORDER BY id",
1392
+ tuple(ids),
1393
+ ):
1394
+ run_by.setdefault(row["task_id"], []).append(row)
1395
+ diags_by_task = {}
1396
+ for r in rows:
1397
+ tid = r["id"]
1398
+ dl = kd.compute_task_diagnostics(r, ev_by.get(tid, []), run_by.get(tid, []))
1399
+ if dl:
1400
+ diags_by_task[tid] = dl
1401
+
1402
+ # Severity filter.
1403
+ sev = getattr(args, "severity", None)
1404
+ if sev:
1405
+ for tid in list(diags_by_task.keys()):
1406
+ kept = [d for d in diags_by_task[tid] if d.severity == sev]
1407
+ if kept:
1408
+ diags_by_task[tid] = kept
1409
+ else:
1410
+ del diags_by_task[tid]
1411
+
1412
+ # Map task_id → title/status/assignee for the table output.
1413
+ meta: dict[str, dict] = {}
1414
+ if diags_by_task:
1415
+ placeholders = ",".join(["?"] * len(diags_by_task))
1416
+ for r in conn.execute(
1417
+ f"SELECT id, title, status, assignee FROM tasks WHERE id IN ({placeholders})",
1418
+ tuple(diags_by_task.keys()),
1419
+ ):
1420
+ meta[r["id"]] = {
1421
+ "title": r["title"], "status": r["status"],
1422
+ "assignee": r["assignee"],
1423
+ }
1424
+
1425
+ if getattr(args, "json", False):
1426
+ out_json = [
1427
+ {
1428
+ "task_id": tid,
1429
+ **meta.get(tid, {}),
1430
+ "diagnostics": [d.to_dict() for d in dl],
1431
+ }
1432
+ for tid, dl in diags_by_task.items()
1433
+ ]
1434
+ print(json.dumps(out_json, indent=2, ensure_ascii=False))
1435
+ return 0
1436
+
1437
+ if not diags_by_task:
1438
+ print("No active diagnostics on this board.")
1439
+ return 0
1440
+
1441
+ # Human-readable summary: grouped by task, severity-marked, with
1442
+ # suggested actions inline.
1443
+ sev_marker = {"warning": "⚠", "error": "!!", "critical": "!!!"}
1444
+ total = sum(len(dl) for dl in diags_by_task.values())
1445
+ print(
1446
+ f"{total} active diagnostic(s) across "
1447
+ f"{len(diags_by_task)} task(s):\n"
1448
+ )
1449
+ for tid, dl in diags_by_task.items():
1450
+ m = meta.get(tid, {})
1451
+ title = m.get("title") or "(untitled)"
1452
+ status = m.get("status") or "?"
1453
+ assignee = m.get("assignee") or "(unassigned)"
1454
+ print(f" {tid} {status:8s} @{assignee:18s} {title}")
1455
+ for d in dl:
1456
+ print(f" {sev_marker.get(d.severity, '?')} [{d.severity}] {d.kind}: {d.title}")
1457
+ if d.data:
1458
+ # Compact key:value pairs on one line.
1459
+ bits = []
1460
+ for k, v in d.data.items():
1461
+ if isinstance(v, list):
1462
+ bits.append(f"{k}={','.join(str(x) for x in v)}")
1463
+ else:
1464
+ bits.append(f"{k}={v}")
1465
+ if bits:
1466
+ print(f" data: {' | '.join(bits)}")
1467
+ # Suggested actions first.
1468
+ for a in d.actions:
1469
+ if a.suggested:
1470
+ print(f" → {a.label}")
1471
+ print()
1472
+ return 0
1473
+
1474
+
1475
+ def _cmd_link(args: argparse.Namespace) -> int:
1476
+ with kb.connect() as conn:
1477
+ kb.link_tasks(conn, args.parent_id, args.child_id)
1478
+ print(f"Linked {args.parent_id} -> {args.child_id}")
1479
+ return 0
1480
+
1481
+
1482
+ def _cmd_unlink(args: argparse.Namespace) -> int:
1483
+ with kb.connect() as conn:
1484
+ ok = kb.unlink_tasks(conn, args.parent_id, args.child_id)
1485
+ if not ok:
1486
+ print(f"No such link: {args.parent_id} -> {args.child_id}", file=sys.stderr)
1487
+ return 1
1488
+ print(f"Unlinked {args.parent_id} -> {args.child_id}")
1489
+ return 0
1490
+
1491
+
1492
+ def _cmd_claim(args: argparse.Namespace) -> int:
1493
+ with kb.connect() as conn:
1494
+ task = kb.claim_task(conn, args.task_id, ttl_seconds=args.ttl)
1495
+ if task is None:
1496
+ # Report why
1497
+ existing = kb.get_task(conn, args.task_id)
1498
+ if existing is None:
1499
+ print(f"no such task: {args.task_id}", file=sys.stderr)
1500
+ return 1
1501
+ print(
1502
+ f"cannot claim {args.task_id}: status={existing.status} "
1503
+ f"lock={existing.claim_lock or '(none)'}",
1504
+ file=sys.stderr,
1505
+ )
1506
+ return 1
1507
+ workspace = kb.resolve_workspace(task)
1508
+ kb.set_workspace_path(conn, task.id, str(workspace))
1509
+ print(f"Claimed {task.id}")
1510
+ print(f"Workspace: {workspace}")
1511
+ return 0
1512
+
1513
+
1514
+ def _cmd_comment(args: argparse.Namespace) -> int:
1515
+ body = " ".join(args.text).strip()
1516
+ author = args.author or _profile_author()
1517
+ with kb.connect() as conn:
1518
+ kb.add_comment(conn, args.task_id, author, body)
1519
+ print(f"Comment added to {args.task_id}")
1520
+ return 0
1521
+
1522
+
1523
+ def _worker_run_id_for(task_id: str) -> Optional[int]:
1524
+ if os.environ.get("HERMES_KANBAN_TASK") != task_id:
1525
+ return None
1526
+ raw = os.environ.get("HERMES_KANBAN_RUN_ID")
1527
+ if not raw:
1528
+ return None
1529
+ try:
1530
+ return int(raw)
1531
+ except ValueError:
1532
+ return None
1533
+
1534
+
1535
+ def _cmd_complete(args: argparse.Namespace) -> int:
1536
+ """Mark one or more tasks done. Supports a single id or a list."""
1537
+ ids = list(args.task_ids or [])
1538
+ if not ids:
1539
+ print("at least one task_id is required", file=sys.stderr)
1540
+ return 1
1541
+ summary = getattr(args, "summary", None)
1542
+ raw_meta = getattr(args, "metadata", None)
1543
+ # Guard: structured handoff fields are per-run, so they'd be
1544
+ # copy-pasted identically across N runs — almost always a footgun.
1545
+ # Refuse instead of silently doing the wrong thing.
1546
+ if len(ids) > 1 and (summary or raw_meta):
1547
+ print(
1548
+ "kanban: --summary / --metadata are per-task and can't be used "
1549
+ "with multiple ids (would apply the same handoff to every task). "
1550
+ "Complete tasks one at a time, or drop the flags for the bulk close.",
1551
+ file=sys.stderr,
1552
+ )
1553
+ return 2
1554
+ metadata = None
1555
+ if raw_meta:
1556
+ try:
1557
+ metadata = json.loads(raw_meta)
1558
+ if not isinstance(metadata, dict):
1559
+ raise ValueError("must be a JSON object")
1560
+ except (ValueError, json.JSONDecodeError) as exc:
1561
+ print(f"kanban: --metadata: {exc}", file=sys.stderr)
1562
+ return 2
1563
+ failed: list[str] = []
1564
+ with kb.connect() as conn:
1565
+ for tid in ids:
1566
+ if not kb.complete_task(
1567
+ conn, tid,
1568
+ result=args.result,
1569
+ summary=summary,
1570
+ metadata=metadata,
1571
+ expected_run_id=_worker_run_id_for(tid),
1572
+ ):
1573
+ failed.append(tid)
1574
+ print(f"cannot complete {tid} (unknown id or terminal state)", file=sys.stderr)
1575
+ else:
1576
+ print(f"Completed {tid}")
1577
+ return 0 if not failed else 1
1578
+
1579
+
1580
+ def _cmd_edit(args: argparse.Namespace) -> int:
1581
+ raw_meta = getattr(args, "metadata", None)
1582
+ metadata = None
1583
+ if raw_meta:
1584
+ try:
1585
+ metadata = json.loads(raw_meta)
1586
+ if not isinstance(metadata, dict):
1587
+ raise ValueError("must be a JSON object")
1588
+ except (ValueError, json.JSONDecodeError) as exc:
1589
+ print(f"kanban: --metadata: {exc}", file=sys.stderr)
1590
+ return 2
1591
+ with kb.connect() as conn:
1592
+ if not kb.edit_completed_task_result(
1593
+ conn,
1594
+ args.task_id,
1595
+ result=args.result,
1596
+ summary=getattr(args, "summary", None),
1597
+ metadata=metadata,
1598
+ ):
1599
+ print(
1600
+ f"cannot edit {args.task_id} (unknown id or task is not done)",
1601
+ file=sys.stderr,
1602
+ )
1603
+ return 1
1604
+ print(f"Edited {args.task_id}")
1605
+ return 0
1606
+
1607
+
1608
+ def _cmd_block(args: argparse.Namespace) -> int:
1609
+ reason = " ".join(args.reason).strip() if args.reason else None
1610
+ author = _profile_author()
1611
+ ids = [args.task_id] + list(getattr(args, "ids", None) or [])
1612
+ failed: list[str] = []
1613
+ with kb.connect() as conn:
1614
+ for tid in ids:
1615
+ if reason:
1616
+ kb.add_comment(conn, tid, author, f"BLOCKED: {reason}")
1617
+ if not kb.block_task(
1618
+ conn,
1619
+ tid,
1620
+ reason=reason,
1621
+ expected_run_id=_worker_run_id_for(tid),
1622
+ ):
1623
+ failed.append(tid)
1624
+ print(f"cannot block {tid}", file=sys.stderr)
1625
+ else:
1626
+ print(f"Blocked {tid}" + (f": {reason}" if reason else ""))
1627
+ return 0 if not failed else 1
1628
+
1629
+
1630
+ def _cmd_unblock(args: argparse.Namespace) -> int:
1631
+ ids = list(args.task_ids or [])
1632
+ if not ids:
1633
+ print("at least one task_id is required", file=sys.stderr)
1634
+ return 1
1635
+ failed: list[str] = []
1636
+ with kb.connect() as conn:
1637
+ for tid in ids:
1638
+ if not kb.unblock_task(conn, tid):
1639
+ failed.append(tid)
1640
+ print(f"cannot unblock {tid} (not blocked?)", file=sys.stderr)
1641
+ else:
1642
+ print(f"Unblocked {tid}")
1643
+ return 0 if not failed else 1
1644
+
1645
+
1646
+ def _cmd_archive(args: argparse.Namespace) -> int:
1647
+ ids = list(args.task_ids or [])
1648
+ if not ids:
1649
+ print("at least one task_id is required", file=sys.stderr)
1650
+ return 1
1651
+ failed: list[str] = []
1652
+ with kb.connect() as conn:
1653
+ for tid in ids:
1654
+ if not kb.archive_task(conn, tid):
1655
+ failed.append(tid)
1656
+ print(f"cannot archive {tid}", file=sys.stderr)
1657
+ else:
1658
+ print(f"Archived {tid}")
1659
+ return 0 if not failed else 1
1660
+
1661
+
1662
+ def _cmd_tail(args: argparse.Namespace) -> int:
1663
+ last_id = 0
1664
+ print(f"Tailing events for {args.task_id}. Ctrl-C to stop.")
1665
+ try:
1666
+ while True:
1667
+ with kb.connect() as conn:
1668
+ events = kb.list_events(conn, args.task_id)
1669
+ for e in events:
1670
+ if e.id > last_id:
1671
+ pl = f" {e.payload}" if e.payload else ""
1672
+ print(f"[{_fmt_ts(e.created_at)}] {e.kind}{pl}", flush=True)
1673
+ last_id = e.id
1674
+ time.sleep(max(0.1, args.interval))
1675
+ except KeyboardInterrupt:
1676
+ print("\n(stopped)")
1677
+ return 0
1678
+
1679
+
1680
+ def _cmd_dispatch(args: argparse.Namespace) -> int:
1681
+ with kb.connect() as conn:
1682
+ res = kb.dispatch_once(
1683
+ conn,
1684
+ dry_run=args.dry_run,
1685
+ max_spawn=args.max,
1686
+ failure_limit=getattr(args, "failure_limit", kb.DEFAULT_SPAWN_FAILURE_LIMIT),
1687
+ )
1688
+ if getattr(args, "json", False):
1689
+ print(json.dumps({
1690
+ "reclaimed": res.reclaimed,
1691
+ "crashed": res.crashed,
1692
+ "timed_out": res.timed_out,
1693
+ "auto_blocked": res.auto_blocked,
1694
+ "promoted": res.promoted,
1695
+ "spawned": [
1696
+ {"task_id": tid, "assignee": who, "workspace": ws}
1697
+ for (tid, who, ws) in res.spawned
1698
+ ],
1699
+ "skipped_unassigned": res.skipped_unassigned,
1700
+ "skipped_nonspawnable": res.skipped_nonspawnable,
1701
+ }, indent=2))
1702
+ return 0
1703
+ print(f"Reclaimed: {res.reclaimed}")
1704
+ print(f"Crashed: {len(res.crashed)}")
1705
+ if res.crashed:
1706
+ print(f" {', '.join(res.crashed)}")
1707
+ print(f"Timed out: {len(res.timed_out)}")
1708
+ if res.timed_out:
1709
+ print(f" {', '.join(res.timed_out)}")
1710
+ print(f"Auto-blocked: {len(res.auto_blocked)}")
1711
+ if res.auto_blocked:
1712
+ print(f" {', '.join(res.auto_blocked)}")
1713
+ print(f"Promoted: {res.promoted}")
1714
+ print(f"Spawned: {len(res.spawned)}")
1715
+ for tid, who, ws in res.spawned:
1716
+ tag = " (dry)" if args.dry_run else ""
1717
+ print(f" - {tid} -> {who} @ {ws or '-'}{tag}")
1718
+ if res.skipped_unassigned:
1719
+ print(f"Skipped (unassigned): {', '.join(res.skipped_unassigned)}")
1720
+ if res.skipped_nonspawnable:
1721
+ print(
1722
+ f"Skipped (non-spawnable assignee — terminal lane, OK): "
1723
+ f"{', '.join(res.skipped_nonspawnable)}"
1724
+ )
1725
+ return 0
1726
+
1727
+
1728
+ def _cmd_daemon(args: argparse.Namespace) -> int:
1729
+ """Deprecated — the dispatcher now runs inside the gateway.
1730
+
1731
+ Left in as a stub so users with the old command in scripts/systemd
1732
+ units get a clear migration message instead of a cryptic
1733
+ "no such command" error. A ``--force`` escape hatch keeps the old
1734
+ standalone daemon alive for the rare edge case where someone truly
1735
+ cannot run the gateway (e.g. running on a host that forbids
1736
+ long-lived background services), but the default path exits 2
1737
+ with guidance so nobody accidentally keeps running two dispatchers
1738
+ against the same kanban.db.
1739
+ """
1740
+ # --force lets power users keep the standalone loop for one more
1741
+ # release cycle. Undocumented in `--help` so nobody discovers it
1742
+ # casually — intentional.
1743
+ if not getattr(args, "force", False):
1744
+ print(
1745
+ "hermes kanban daemon: DEPRECATED — the dispatcher now runs\n"
1746
+ "inside the gateway. To use kanban:\n"
1747
+ "\n"
1748
+ " hermes gateway start # starts the gateway + embedded dispatcher\n"
1749
+ "\n"
1750
+ "Ready tasks will be picked up on the next dispatcher tick\n"
1751
+ "(default: every 60 seconds). Configure via config.yaml:\n"
1752
+ "\n"
1753
+ " kanban:\n"
1754
+ " dispatch_in_gateway: true # default\n"
1755
+ " dispatch_interval_seconds: 60\n"
1756
+ " failure_limit: 2 # consecutive non-success attempts before auto-block\n"
1757
+ "\n"
1758
+ "Running both the gateway AND this standalone daemon will\n"
1759
+ "race for claims. If you truly need the old standalone\n"
1760
+ "daemon (no gateway available), rerun with --force.",
1761
+ file=sys.stderr,
1762
+ )
1763
+ return 2
1764
+
1765
+ # Legacy path — same logic as before, kept behind --force.
1766
+ # Make sure the DB exists before printing "started" so the user sees the
1767
+ # correct DB path and any init error surfaces immediately.
1768
+ kb.init_db()
1769
+
1770
+ pidfile = getattr(args, "pidfile", None)
1771
+ if pidfile:
1772
+ try:
1773
+ Path(pidfile).parent.mkdir(parents=True, exist_ok=True)
1774
+ Path(pidfile).write_text(str(os.getpid()), encoding="utf-8")
1775
+ except OSError as exc:
1776
+ print(f"warning: could not write pidfile {pidfile}: {exc}", file=sys.stderr)
1777
+
1778
+ verbose = bool(getattr(args, "verbose", False))
1779
+ print(
1780
+ f"Kanban dispatcher running STANDALONE via --force "
1781
+ f"(interval={args.interval}s, pid={os.getpid()}). "
1782
+ f"Ctrl-C to stop. NOTE: if a gateway is also running with "
1783
+ f"dispatch_in_gateway=true (default), you have two dispatchers "
1784
+ f"racing for claims.",
1785
+ file=sys.stderr,
1786
+ )
1787
+
1788
+ # Health telemetry: warn when every tick finds ready work but fails to
1789
+ # spawn any worker. Catches broken profiles, PATH drift, missing venv,
1790
+ # credential loss — cases where the per-task circuit breaker auto-blocks
1791
+ # each task quietly but the operator has no signal that the dispatcher
1792
+ # itself is dysfunctional.
1793
+ HEALTH_WINDOW = 6 # ticks (default 30s at interval=5)
1794
+ health_state = {"bad_ticks": 0, "last_warn_at": 0}
1795
+
1796
+ def _on_tick(res):
1797
+ ready_pending = bool(res.skipped_unassigned) or _ready_queue_nonempty()
1798
+ spawned_any = bool(res.spawned)
1799
+ if ready_pending and not spawned_any:
1800
+ health_state["bad_ticks"] += 1
1801
+ else:
1802
+ health_state["bad_ticks"] = 0
1803
+ # Emit a warning once per HEALTH_WINDOW bad ticks (not every tick)
1804
+ # so log volume stays bounded while the problem persists.
1805
+ if health_state["bad_ticks"] >= HEALTH_WINDOW:
1806
+ now = int(time.time())
1807
+ # Rate-limit repeats: at most one warning per 5 minutes.
1808
+ if now - health_state["last_warn_at"] >= 300:
1809
+ print(
1810
+ f"[{_fmt_ts(now)}] WARN dispatcher stuck: "
1811
+ f"ready queue non-empty for {health_state['bad_ticks']} "
1812
+ f"consecutive ticks but 0 workers spawned successfully. "
1813
+ f"Check profile health (venv, PATH, credentials) and "
1814
+ f"`hermes kanban list --status ready` / "
1815
+ f"`hermes kanban list --status blocked` for recent "
1816
+ f"spawn_failed tasks.",
1817
+ file=sys.stderr, flush=True,
1818
+ )
1819
+ health_state["last_warn_at"] = now
1820
+ if not verbose:
1821
+ return
1822
+ did_work = (
1823
+ res.reclaimed or res.crashed or res.timed_out or res.promoted
1824
+ or res.spawned or res.auto_blocked
1825
+ )
1826
+ if did_work:
1827
+ print(
1828
+ f"[{_fmt_ts(int(time.time()))}] "
1829
+ f"reclaimed={res.reclaimed} crashed={len(res.crashed)} "
1830
+ f"timed_out={len(res.timed_out)} "
1831
+ f"promoted={res.promoted} spawned={len(res.spawned)} "
1832
+ f"auto_blocked={len(res.auto_blocked)}",
1833
+ flush=True,
1834
+ )
1835
+
1836
+ def _ready_queue_nonempty() -> bool:
1837
+ """Cheap probe — is there at least one ready+assigned+unclaimed
1838
+ task whose assignee maps to a real Hermes profile (i.e. one the
1839
+ dispatcher would actually try to spawn for)?
1840
+
1841
+ Filters out tasks assigned to control-plane lanes
1842
+ (e.g. ``orion-cc``, ``orion-research``) that are pulled by
1843
+ terminals via ``claim_task`` directly — those are correctly idle
1844
+ from the dispatcher's perspective, not stuck.
1845
+ """
1846
+ try:
1847
+ with kb.connect() as conn:
1848
+ return kb.has_spawnable_ready(conn)
1849
+ except Exception:
1850
+ return False
1851
+
1852
+ try:
1853
+ kb.run_daemon(
1854
+ interval=args.interval,
1855
+ max_spawn=args.max,
1856
+ failure_limit=getattr(args, "failure_limit", kb.DEFAULT_SPAWN_FAILURE_LIMIT),
1857
+ on_tick=_on_tick,
1858
+ )
1859
+ finally:
1860
+ if pidfile:
1861
+ try:
1862
+ Path(pidfile).unlink()
1863
+ except OSError:
1864
+ pass
1865
+ print("(dispatcher stopped)")
1866
+ return 0
1867
+
1868
+
1869
+ def _cmd_watch(args: argparse.Namespace) -> int:
1870
+ """Live-stream task_events to the terminal."""
1871
+ kinds = (
1872
+ {k.strip() for k in args.kinds.split(",") if k.strip()}
1873
+ if args.kinds else None
1874
+ )
1875
+ cursor = 0
1876
+ print("Watching kanban events. Ctrl-C to stop.", flush=True)
1877
+ # Seed cursor at the latest id so we don't replay history.
1878
+ with kb.connect() as conn:
1879
+ row = conn.execute(
1880
+ "SELECT COALESCE(MAX(id), 0) AS m FROM task_events"
1881
+ ).fetchone()
1882
+ cursor = int(row["m"])
1883
+
1884
+ try:
1885
+ while True:
1886
+ with kb.connect() as conn:
1887
+ rows = conn.execute(
1888
+ "SELECT e.id, e.task_id, e.kind, e.payload, e.created_at, "
1889
+ " t.assignee, t.tenant "
1890
+ "FROM task_events e LEFT JOIN tasks t ON t.id = e.task_id "
1891
+ "WHERE e.id > ? ORDER BY e.id ASC LIMIT 200",
1892
+ (cursor,),
1893
+ ).fetchall()
1894
+ for r in rows:
1895
+ cursor = max(cursor, int(r["id"]))
1896
+ if kinds and r["kind"] not in kinds:
1897
+ continue
1898
+ if args.assignee and r["assignee"] != args.assignee:
1899
+ continue
1900
+ if args.tenant and r["tenant"] != args.tenant:
1901
+ continue
1902
+ try:
1903
+ payload = json.loads(r["payload"]) if r["payload"] else None
1904
+ except Exception:
1905
+ payload = None
1906
+ pl = f" {payload}" if payload else ""
1907
+ print(
1908
+ f"[{_fmt_ts(r['created_at'])}] {r['task_id']:10s} "
1909
+ f"{r['kind']:18s} (@{r['assignee'] or '-'}){pl}",
1910
+ flush=True,
1911
+ )
1912
+ time.sleep(max(0.1, args.interval))
1913
+ except KeyboardInterrupt:
1914
+ print("\n(stopped)")
1915
+ return 0
1916
+
1917
+
1918
+ def _cmd_stats(args: argparse.Namespace) -> int:
1919
+ with kb.connect() as conn:
1920
+ stats = kb.board_stats(conn)
1921
+ if getattr(args, "json", False):
1922
+ print(json.dumps(stats, indent=2, ensure_ascii=False))
1923
+ return 0
1924
+ print("By status:")
1925
+ for k in ("triage", "todo", "ready", "running", "blocked", "done"):
1926
+ print(f" {k:8s} {stats['by_status'].get(k, 0)}")
1927
+ if stats["by_assignee"]:
1928
+ print("\nBy assignee:")
1929
+ for who, counts in sorted(stats["by_assignee"].items()):
1930
+ parts = ", ".join(f"{k}={v}" for k, v in sorted(counts.items()))
1931
+ print(f" {who:20s} {parts}")
1932
+ age = stats["oldest_ready_age_seconds"]
1933
+ if age is not None:
1934
+ print(f"\nOldest ready task age: {int(age)}s")
1935
+ return 0
1936
+
1937
+
1938
+ def _cmd_notify_subscribe(args: argparse.Namespace) -> int:
1939
+ with kb.connect() as conn:
1940
+ if kb.get_task(conn, args.task_id) is None:
1941
+ print(f"no such task: {args.task_id}", file=sys.stderr)
1942
+ return 1
1943
+ kb.add_notify_sub(
1944
+ conn, task_id=args.task_id,
1945
+ platform=args.platform, chat_id=args.chat_id,
1946
+ thread_id=args.thread_id, user_id=args.user_id,
1947
+ notifier_profile=args.notifier_profile or _profile_author(),
1948
+ )
1949
+ print(f"Subscribed {args.platform}:{args.chat_id}"
1950
+ + (f":{args.thread_id}" if args.thread_id else "")
1951
+ + f" to {args.task_id}")
1952
+ return 0
1953
+
1954
+
1955
+ def _cmd_notify_list(args: argparse.Namespace) -> int:
1956
+ with kb.connect() as conn:
1957
+ subs = kb.list_notify_subs(conn, args.task_id)
1958
+ if getattr(args, "json", False):
1959
+ print(json.dumps(subs, indent=2, ensure_ascii=False))
1960
+ return 0
1961
+ if not subs:
1962
+ print("(no subscriptions)")
1963
+ return 0
1964
+ for s in subs:
1965
+ thr = f":{s['thread_id']}" if s.get("thread_id") else ""
1966
+ owner = f" owner={s['notifier_profile']}" if s.get("notifier_profile") else ""
1967
+ print(f" {s['task_id']:10s} {s['platform']}:{s['chat_id']}{thr}"
1968
+ f" (since event {s['last_event_id']}){owner}")
1969
+ return 0
1970
+
1971
+
1972
+ def _cmd_notify_unsubscribe(args: argparse.Namespace) -> int:
1973
+ with kb.connect() as conn:
1974
+ ok = kb.remove_notify_sub(
1975
+ conn, task_id=args.task_id,
1976
+ platform=args.platform, chat_id=args.chat_id,
1977
+ thread_id=args.thread_id,
1978
+ )
1979
+ if not ok:
1980
+ print("(no such subscription)", file=sys.stderr)
1981
+ return 1
1982
+ print(f"Unsubscribed from {args.task_id}")
1983
+ return 0
1984
+
1985
+
1986
+ def _cmd_log(args: argparse.Namespace) -> int:
1987
+ content = kb.read_worker_log(args.task_id, tail_bytes=args.tail)
1988
+ if content is None:
1989
+ print(f"(no log for {args.task_id} — task may not have spawned yet)",
1990
+ file=sys.stderr)
1991
+ return 1
1992
+ sys.stdout.write(content)
1993
+ if not content.endswith("\n"):
1994
+ sys.stdout.write("\n")
1995
+ return 0
1996
+
1997
+
1998
+ def _cmd_runs(args: argparse.Namespace) -> int:
1999
+ """Show attempt history for a task."""
2000
+ with kb.connect() as conn:
2001
+ runs = kb.list_runs(conn, args.task_id)
2002
+ if getattr(args, "json", False):
2003
+ print(json.dumps([
2004
+ {
2005
+ "id": r.id, "profile": r.profile, "status": r.status,
2006
+ "outcome": r.outcome, "started_at": r.started_at,
2007
+ "ended_at": r.ended_at, "summary": r.summary,
2008
+ "error": r.error, "metadata": r.metadata,
2009
+ "worker_pid": r.worker_pid, "step_key": r.step_key,
2010
+ } for r in runs
2011
+ ], indent=2, ensure_ascii=False))
2012
+ return 0
2013
+ if not runs:
2014
+ print(f"(no runs yet for {args.task_id})")
2015
+ return 0
2016
+ print(f"{'#':3s} {'OUTCOME':12s} {'PROFILE':16s} {'ELAPSED':>8s} STARTED")
2017
+ for i, r in enumerate(runs, 1):
2018
+ end = r.ended_at or int(time.time())
2019
+ # Clamp to 0 so NTP backward-jumps don't print negative durations.
2020
+ elapsed = max(0, end - r.started_at)
2021
+ if elapsed < 60:
2022
+ el = f"{elapsed}s"
2023
+ elif elapsed < 3600:
2024
+ el = f"{elapsed // 60}m"
2025
+ else:
2026
+ el = f"{elapsed / 3600:.1f}h"
2027
+ outcome = r.outcome or ("(running)" if not r.ended_at else r.status)
2028
+ print(f"{i:3d} {outcome:12s} {(r.profile or '-'):16s} {el:>8s} {_fmt_ts(r.started_at)}")
2029
+ if r.summary:
2030
+ # Indent and truncate long summaries to keep the table readable.
2031
+ summary = r.summary.splitlines()[0][:100]
2032
+ print(f" → {summary}")
2033
+ if r.error:
2034
+ print(f" ✖ {r.error[:100]}")
2035
+ return 0
2036
+
2037
+
2038
+ def _cmd_context(args: argparse.Namespace) -> int:
2039
+ with kb.connect() as conn:
2040
+ text = kb.build_worker_context(conn, args.task_id)
2041
+ print(text)
2042
+ return 0
2043
+
2044
+
2045
+ def _cmd_specify(args: argparse.Namespace) -> int:
2046
+ """Flesh out a triage task (or all of them) via auxiliary LLM,
2047
+ then promote to todo. Thin wrapper over ``kanban_specify``."""
2048
+ from hermes_cli import kanban_specify as spec
2049
+
2050
+ all_flag = bool(getattr(args, "all_triage", False))
2051
+ tenant = getattr(args, "tenant", None)
2052
+ author = getattr(args, "author", None) or _profile_author()
2053
+ want_json = bool(getattr(args, "json", False))
2054
+
2055
+ if args.task_id and all_flag:
2056
+ print(
2057
+ "kanban: pass either a task id OR --all, not both",
2058
+ file=sys.stderr,
2059
+ )
2060
+ return 2
2061
+
2062
+ if all_flag:
2063
+ ids = spec.list_triage_ids(tenant=tenant)
2064
+ if not ids:
2065
+ msg = (
2066
+ "No triage tasks"
2067
+ + (f" for tenant {tenant!r}" if tenant else "")
2068
+ + "."
2069
+ )
2070
+ if want_json:
2071
+ print(json.dumps({"specified": 0, "total": 0}))
2072
+ else:
2073
+ print(msg)
2074
+ return 0
2075
+ elif args.task_id:
2076
+ ids = [args.task_id]
2077
+ else:
2078
+ print(
2079
+ "kanban: specify requires a task id or --all",
2080
+ file=sys.stderr,
2081
+ )
2082
+ return 2
2083
+
2084
+ ok_count = 0
2085
+ fail_count = 0
2086
+ for tid in ids:
2087
+ outcome = spec.specify_task(tid, author=author)
2088
+ if outcome.ok:
2089
+ ok_count += 1
2090
+ else:
2091
+ fail_count += 1
2092
+ if want_json:
2093
+ print(json.dumps({
2094
+ "task_id": outcome.task_id,
2095
+ "ok": outcome.ok,
2096
+ "reason": outcome.reason,
2097
+ "new_title": outcome.new_title,
2098
+ }))
2099
+ elif outcome.ok:
2100
+ title_suffix = (
2101
+ f" — retitled: {outcome.new_title!r}"
2102
+ if outcome.new_title
2103
+ else ""
2104
+ )
2105
+ print(f"Specified {outcome.task_id} → todo{title_suffix}")
2106
+ else:
2107
+ print(
2108
+ f"kanban: specify {outcome.task_id}: {outcome.reason}",
2109
+ file=sys.stderr,
2110
+ )
2111
+ if not all_flag:
2112
+ return 0 if ok_count == 1 else 1
2113
+ # --all: succeed if at least one promotion landed; exit 1 only when
2114
+ # every candidate failed (honest signal for scripts).
2115
+ return 0 if (ok_count > 0 or not ids) else 1
2116
+
2117
+
2118
+ def _cmd_gc(args: argparse.Namespace) -> int:
2119
+ """Remove scratch workspaces of archived tasks, prune old events, and
2120
+ delete old worker logs."""
2121
+ import shutil
2122
+ scratch_root = kb.workspaces_root()
2123
+ removed_ws = 0
2124
+ with kb.connect() as conn:
2125
+ rows = conn.execute(
2126
+ "SELECT id, workspace_kind, workspace_path FROM tasks WHERE status = 'archived'"
2127
+ ).fetchall()
2128
+ for row in rows:
2129
+ if row["workspace_kind"] != "scratch":
2130
+ continue
2131
+ path = Path(row["workspace_path"] or (scratch_root / row["id"]))
2132
+ try:
2133
+ path = path.resolve()
2134
+ except OSError:
2135
+ continue
2136
+ try:
2137
+ path.relative_to(scratch_root.resolve())
2138
+ except ValueError:
2139
+ # Safety: never delete outside the scratch root.
2140
+ continue
2141
+ if path.exists() and path.is_dir():
2142
+ shutil.rmtree(path, ignore_errors=True)
2143
+ removed_ws += 1
2144
+
2145
+ event_days = getattr(args, "event_retention_days", 30)
2146
+ log_days = getattr(args, "log_retention_days", 30)
2147
+ with kb.connect() as conn:
2148
+ removed_events = kb.gc_events(
2149
+ conn, older_than_seconds=event_days * 24 * 3600,
2150
+ )
2151
+ removed_logs = kb.gc_worker_logs(
2152
+ older_than_seconds=log_days * 24 * 3600,
2153
+ )
2154
+ print(f"GC complete: {removed_ws} workspace(s), "
2155
+ f"{removed_events} event row(s), {removed_logs} log file(s) removed")
2156
+ return 0
2157
+
2158
+
2159
+ # ---------------------------------------------------------------------------
2160
+ # Slash-command entry point (used by /kanban from CLI and gateway)
2161
+ # ---------------------------------------------------------------------------
2162
+
2163
+ _SLASH_KANBAN_HELP = """\
2164
+ **/kanban** — manage the shared task board.
2165
+
2166
+ Common subcommands:
2167
+ `list` (alias `ls`) List tasks on the current board
2168
+ `show <id>` Task details + comments + events
2169
+ `stats` Per-status / per-assignee counts
2170
+ `create <title>…` Create a task (auto-subscribes you to events)
2171
+ `comment <id> <msg>` Append a comment
2172
+ `complete <id>…` Mark task(s) done
2173
+ `block <id> [reason]` Mark blocked; `unblock <id>` to revive
2174
+ `assign <id> <profile>` Reassign
2175
+ `boards list` Show all boards
2176
+ `assignees` Known profiles + counts
2177
+ `context <id>` Full worker-context dump
2178
+ `runs <id>` Attempt history
2179
+ `log <id>` Worker log
2180
+
2181
+ Run `/kanban <subcommand> -h` for arguments. \
2182
+ Read-only commands are safe while an agent is running.\
2183
+ """
2184
+
2185
+
2186
+ def run_slash(rest: str) -> str:
2187
+ """Execute a ``/kanban …`` string and return captured stdout/stderr.
2188
+
2189
+ ``rest`` is everything after ``/kanban`` (may be empty). Used from
2190
+ both the interactive CLI (``self._handle_kanban_command``) and the
2191
+ gateway (``_handle_kanban_command``) so formatting is identical.
2192
+ """
2193
+ import io
2194
+ import contextlib
2195
+
2196
+ tokens = shlex.split(rest) if rest and rest.strip() else []
2197
+
2198
+ # Bare ``/kanban`` or ``/kanban help`` / ``--help`` / ``-h`` / ``?``:
2199
+ # show the curated short-help block instead of dumping argparse's full
2200
+ # usage tree (which is enormous and reads as garbage in a chat
2201
+ # bubble). Per-subcommand help still works via ``/kanban foo -h``.
2202
+ if not tokens or tokens[0] in {"help", "--help", "-h", "?"}:
2203
+ return _SLASH_KANBAN_HELP
2204
+
2205
+ # Single argparse tree rooted at "/kanban". build_parser() expects a
2206
+ # subparsers action to attach to, so build a throwaway one and pull
2207
+ # the kanban_parser back out — then drive it directly so usage/error
2208
+ # text reads as ``/kanban`` (not ``/kanban-wrap kanban``).
2209
+ _wrap = argparse.ArgumentParser(prog="/kanban-wrap", add_help=False)
2210
+ _wrap.exit_on_error = False # type: ignore[attr-defined]
2211
+ _top_sub = _wrap.add_subparsers(dest="_top")
2212
+ kanban_parser = build_parser(_top_sub)
2213
+ kanban_parser.prog = "/kanban"
2214
+ kanban_parser.exit_on_error = False # type: ignore[attr-defined]
2215
+ for _action in kanban_parser._actions:
2216
+ if isinstance(_action, argparse._SubParsersAction):
2217
+ for _name, _choice in _action.choices.items():
2218
+ _choice.prog = f"/kanban {_name}"
2219
+ _choice.exit_on_error = False # type: ignore[attr-defined]
2220
+
2221
+ buf_out = io.StringIO()
2222
+ buf_err = io.StringIO()
2223
+ # ``-h`` / ``--help`` makes argparse print to stdout and SystemExit(0).
2224
+ # Capture both streams so neither the help text nor the error text
2225
+ # bypasses our buffer.
2226
+ try:
2227
+ with contextlib.redirect_stdout(buf_out), contextlib.redirect_stderr(buf_err):
2228
+ args = kanban_parser.parse_args(tokens)
2229
+ except SystemExit as exc:
2230
+ out = buf_out.getvalue().rstrip()
2231
+ err = buf_err.getvalue().rstrip()
2232
+ # Help dump (exit 0) → return the captured help text directly.
2233
+ if exc.code in {0, None} and out:
2234
+ return out
2235
+ body = err or out
2236
+ return f"⚠ /kanban usage error\n{body}" if body else "⚠ /kanban usage error"
2237
+ except argparse.ArgumentError as exc:
2238
+ return f"⚠ /kanban usage error: {exc}"
2239
+
2240
+ with contextlib.redirect_stdout(buf_out), contextlib.redirect_stderr(buf_err):
2241
+ try:
2242
+ kanban_command(args)
2243
+ except SystemExit:
2244
+ pass
2245
+ except Exception as exc:
2246
+ print(f"error: {exc}", file=sys.stderr)
2247
+
2248
+ out = buf_out.getvalue().rstrip()
2249
+ err = buf_err.getvalue().rstrip()
2250
+ if err and out:
2251
+ return f"{out}\n{err}"
2252
+ return err if err else (out or "(no output)")