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,3143 @@
1
+ /**
2
+ * Hermes Kanban — Dashboard Plugin
3
+ *
4
+ * Board view for the multi-agent collaboration board backed by
5
+ * ~/.hermes/kanban.db. Calls the plugin's backend at /api/plugins/kanban/
6
+ * and tails task_events over a WebSocket for live updates.
7
+ *
8
+ * Plain IIFE, no build step. Uses window.__HERMES_PLUGIN_SDK__ for React +
9
+ * shadcn primitives; HTML5 drag-and-drop for card movement on desktop and
10
+ * a pointer-based fallback for touch.
11
+ */
12
+ (function () {
13
+ "use strict";
14
+
15
+ const SDK = window.__HERMES_PLUGIN_SDK__;
16
+ if (!SDK) return;
17
+
18
+ const { React } = SDK;
19
+ const h = React.createElement;
20
+ const {
21
+ Card, CardContent,
22
+ Badge, Button, Input, Label, Select, SelectOption,
23
+ } = SDK.components;
24
+ const { useState, useEffect, useCallback, useMemo, useRef } = SDK.hooks;
25
+ const { cn, timeAgo } = SDK.utils;
26
+
27
+ // useI18n is a hook each component calls locally. Older host dashboards
28
+ // may not expose it yet; fall back to a shim so the bundle still renders
29
+ // English against an older host SDK. English fallback strings live
30
+ // alongside each call site (passed as the third arg of tx()).
31
+ const useI18n = SDK.useI18n || function () { return { t: { kanban: null }, locale: "en" }; };
32
+
33
+ // Resolve a translation by dotted path under the kanban namespace
34
+ // (e.g. "columnLabels.triage"); fall back to the English string passed in.
35
+ function tx(t, path, fallback, vars) {
36
+ let node = t && t.kanban;
37
+ if (node) {
38
+ const parts = path.split(".");
39
+ for (let i = 0; i < parts.length; i++) {
40
+ if (node && typeof node === "object" && parts[i] in node) {
41
+ node = node[parts[i]];
42
+ } else { node = null; break; }
43
+ }
44
+ }
45
+ let str = (typeof node === "string") ? node : fallback;
46
+ if (vars) {
47
+ for (const k in vars) {
48
+ str = str.replace(new RegExp("\\{" + k + "\\}", "g"), vars[k]);
49
+ }
50
+ }
51
+ return str;
52
+ }
53
+
54
+ // Order matches BOARD_COLUMNS in plugin_api.py.
55
+ const COLUMN_ORDER = ["triage", "todo", "ready", "running", "blocked", "done"];
56
+ // English fallback dictionaries — used when the i18n catalog is missing
57
+ // a key, and as defaults for the get*() helpers below so callers running
58
+ // outside any React component (where there's no `t`) still get sane text.
59
+ const FALLBACK_COLUMN_LABEL = {
60
+ triage: "Triage",
61
+ todo: "Todo",
62
+ ready: "Ready",
63
+ running: "In Progress",
64
+ blocked: "Blocked",
65
+ done: "Done",
66
+ archived: "Archived",
67
+ };
68
+ const FALLBACK_COLUMN_HELP = {
69
+ triage: "Raw ideas — a specifier will flesh out the spec",
70
+ todo: "Waiting on dependencies or unassigned",
71
+ ready: "Dependencies satisfied; assign a profile to dispatch",
72
+ running: "Claimed by a worker — in-flight",
73
+ blocked: "Worker asked for human input",
74
+ done: "Completed",
75
+ archived: "Archived",
76
+ };
77
+ const FALLBACK_DESTRUCTIVE = {
78
+ done: "Mark this task as done? The worker's claim is released and dependent children become ready.",
79
+ archived: "Archive this task? It disappears from the default board view.",
80
+ blocked: "Mark this task as blocked? The worker's claim is released.",
81
+ };
82
+ const FALLBACK_DIAGNOSTIC_EVENT_LABELS = {
83
+ completion_blocked_hallucination: "⚠ Completion blocked — phantom card ids",
84
+ suspected_hallucinated_references: "⚠ Prose referenced phantom card ids",
85
+ };
86
+ const DIAGNOSTIC_EVENT_KIND_KEYS = {
87
+ completion_blocked_hallucination: "completionBlockedHallucination",
88
+ suspected_hallucinated_references: "suspectedHallucinatedReferences",
89
+ };
90
+ const DESTRUCTIVE_KEYS = {
91
+ done: "confirmDone",
92
+ archived: "confirmArchive",
93
+ blocked: "confirmBlocked",
94
+ };
95
+
96
+ function getColumnLabel(t, status) {
97
+ return tx(t, "columnLabels." + status, FALLBACK_COLUMN_LABEL[status] || status);
98
+ }
99
+ function getColumnHelp(t, status) {
100
+ return tx(t, "columnHelp." + status, FALLBACK_COLUMN_HELP[status] || "");
101
+ }
102
+ function getDestructiveConfirm(t, status) {
103
+ const key = DESTRUCTIVE_KEYS[status];
104
+ if (!key) return null;
105
+ return tx(t, key, FALLBACK_DESTRUCTIVE[status]);
106
+ }
107
+ function getDiagnosticEventLabel(t, kind) {
108
+ const key = DIAGNOSTIC_EVENT_KIND_KEYS[kind];
109
+ if (!key) return null;
110
+ return tx(t, key, FALLBACK_DIAGNOSTIC_EVENT_LABELS[kind]);
111
+ }
112
+
113
+ const COLUMN_DOT = {
114
+ triage: "hermes-kanban-dot-triage",
115
+ todo: "hermes-kanban-dot-todo",
116
+ ready: "hermes-kanban-dot-ready",
117
+ running: "hermes-kanban-dot-running",
118
+ blocked: "hermes-kanban-dot-blocked",
119
+ done: "hermes-kanban-dot-done",
120
+ archived: "hermes-kanban-dot-archived",
121
+ };
122
+
123
+ function isDiagnosticEvent(kind) {
124
+ return Object.prototype.hasOwnProperty.call(FALLBACK_DIAGNOSTIC_EVENT_LABELS, kind);
125
+ }
126
+
127
+ function phantomIdsFromEvent(ev) {
128
+ if (!ev || !ev.payload) return [];
129
+ const p = ev.payload;
130
+ return p.phantom_cards || p.phantom_refs || [];
131
+ }
132
+
133
+ // Takes an optional `t` so the prompt/alert text is localised. Callers
134
+ // outside React components can pass null and fall through to English.
135
+ function withCompletionSummary(patch, count, t) {
136
+ if (!patch || patch.status !== "done") return patch;
137
+ const label = count && count > 1 ? `${count} selected task(s)` : "this task";
138
+ const value = window.prompt(
139
+ tx(t, "completionSummary",
140
+ "Completion summary for {label}. This is stored as the task result.",
141
+ { label: label }),
142
+ "",
143
+ );
144
+ if (value === null) return null;
145
+ const summary = value.trim();
146
+ if (!summary) {
147
+ window.alert(tx(t, "completionSummaryRequired",
148
+ "Completion summary is required before marking a task done."));
149
+ return null;
150
+ }
151
+ return Object.assign({}, patch, { result: summary, summary });
152
+ }
153
+
154
+ const API = "/api/plugins/kanban";
155
+ const MIME_TASK = "text/x-hermes-task";
156
+
157
+ // Docs link — surfaced as a `?` icon next to the board switcher and as
158
+ // `title=` hints on unlabelled controls. Kept in one place so rebrands or
159
+ // path changes are a single edit.
160
+ const DOCS_URL = "https://hermes-agent.nousresearch.com/docs/user-guide/features/kanban";
161
+ const DOCS_TUTORIAL_URL = "https://hermes-agent.nousresearch.com/docs/user-guide/features/kanban-tutorial";
162
+
163
+ // localStorage key for the user's selected board. Independent of the
164
+ // CLI's on-disk ``<root>/kanban/current`` pointer so browser users
165
+ // can inspect any board without shifting the CLI's active board out
166
+ // from under a terminal they left open.
167
+ const LS_BOARD_KEY = "hermes.kanban.selectedBoard";
168
+
169
+ function readSelectedBoard() {
170
+ try {
171
+ const v = window.localStorage.getItem(LS_BOARD_KEY);
172
+ return (v || "").trim() || null;
173
+ } catch (_e) { return null; }
174
+ }
175
+
176
+ function writeSelectedBoard(slug) {
177
+ try {
178
+ // Persist the user's dashboard-side board pin even for "default".
179
+ // Previously this stripped "default" to keep localStorage empty,
180
+ // but the fetch layer read that absence as "no opinion" and fell
181
+ // through to the server-side ``current`` file — which the board
182
+ // switcher also writes. Result: selecting the default tab after
183
+ // creating a new board with "switch" checked showed the new
184
+ // board's (wrong) data because the URL omitted ``?board=`` and
185
+ // the backend happily returned whichever board was "current".
186
+ // Persisting every selection keeps the dashboard's board opinion
187
+ // independent of the CLI's active board, which was the original
188
+ // design intent. Regression: #20879.
189
+ if (slug) window.localStorage.setItem(LS_BOARD_KEY, slug);
190
+ else window.localStorage.removeItem(LS_BOARD_KEY);
191
+ } catch (_e) { /* ignore quota / private mode */ }
192
+ }
193
+
194
+ function withBoard(url, board) {
195
+ // Always append ?board=<slug> when we have one picked — including
196
+ // "default". Omitting the param would fall through to the backend's
197
+ // resolution chain (env var → ``current`` file → default), which
198
+ // means the dashboard's tab selection gets silently overridden by
199
+ // whatever board the CLI or "switch" checkbox last activated.
200
+ // Regression: #20879.
201
+ if (!board) return url;
202
+ const sep = url.indexOf("?") >= 0 ? "&" : "?";
203
+ return `${url}${sep}board=${encodeURIComponent(board)}`;
204
+ }
205
+
206
+ // The SDK's Select component fires ``onValueChange(value)`` directly
207
+ // (it's a shadcn-style popup, not a native <select>). Older plugin
208
+ // code calls ``onChange({target: {value}})`` which silently never
209
+ // fires. This helper wires both signatures so a setter works with
210
+ // either API — use it as:
211
+ //
212
+ // h(Select, {..., ...selectChangeHandler(setState), ...})
213
+ function selectChangeHandler(setter) {
214
+ return {
215
+ onValueChange: function (v) { setter(v == null ? "" : v); },
216
+ onChange: function (e) {
217
+ const v = e && e.target ? e.target.value : e;
218
+ setter(v == null ? "" : v);
219
+ },
220
+ };
221
+ }
222
+
223
+ // -------------------------------------------------------------------------
224
+ // Minimal safe markdown renderer.
225
+ //
226
+ // Recognises a small subset (headings, bold, italic, inline code, fenced
227
+ // code, links, bullet lists, paragraphs). HTML escaping first, then
228
+ // inline replacements against the escaped string — no raw HTML from the
229
+ // user is ever executed.
230
+ // -------------------------------------------------------------------------
231
+
232
+ function escapeHtml(s) {
233
+ return String(s)
234
+ .replace(/&/g, "&amp;")
235
+ .replace(/</g, "&lt;")
236
+ .replace(/>/g, "&gt;")
237
+ .replace(/"/g, "&quot;")
238
+ .replace(/'/g, "&#39;");
239
+ }
240
+ function renderInline(esc) {
241
+ // Fenced code has already been extracted before this runs; process
242
+ // inline replacements on the escaped string.
243
+ return esc
244
+ // inline code
245
+ .replace(/`([^`\n]+)`/g, (_m, c) => `<code>${c}</code>`)
246
+ // bold
247
+ .replace(/\*\*([^*\n]+)\*\*/g, "<strong>$1</strong>")
248
+ // italic
249
+ .replace(/(^|[^*])\*([^*\n]+)\*/g, "$1<em>$2</em>")
250
+ // safe links — only http(s) and mailto
251
+ .replace(
252
+ /\[([^\]\n]+)\]\((https?:\/\/[^\s)]+|mailto:[^\s)]+)\)/g,
253
+ (_m, text, href) =>
254
+ `<a href="${href}" target="_blank" rel="noopener noreferrer">${text}</a>`,
255
+ );
256
+ }
257
+ function renderMarkdown(src) {
258
+ if (!src) return "";
259
+ // Split out fenced code blocks first so their contents aren't mangled.
260
+ const blocks = [];
261
+ let working = String(src).replace(/```([\s\S]*?)```/g, (_m, code) => {
262
+ blocks.push(code);
263
+ return `\u0000CODE${blocks.length - 1}\u0000`;
264
+ });
265
+ const escaped = escapeHtml(working);
266
+ const lines = escaped.split(/\r?\n/);
267
+ const out = [];
268
+ let inList = false;
269
+ for (const raw of lines) {
270
+ const line = raw;
271
+ const bullet = /^\s*[-*]\s+(.*)$/.exec(line);
272
+ const heading = /^(#{1,4})\s+(.*)$/.exec(line);
273
+ if (bullet) {
274
+ if (!inList) { out.push("<ul>"); inList = true; }
275
+ out.push(`<li>${renderInline(bullet[1])}</li>`);
276
+ continue;
277
+ }
278
+ if (inList) { out.push("</ul>"); inList = false; }
279
+ if (heading) {
280
+ const level = heading[1].length;
281
+ out.push(`<h${level}>${renderInline(heading[2])}</h${level}>`);
282
+ } else if (line.trim() === "") {
283
+ out.push("");
284
+ } else {
285
+ out.push(`<p>${renderInline(line)}</p>`);
286
+ }
287
+ }
288
+ if (inList) out.push("</ul>");
289
+ let html = out.join("\n");
290
+ // Re-insert fenced code blocks.
291
+ html = html.replace(/\u0000CODE(\d+)\u0000/g, (_m, i) =>
292
+ `<pre class="hermes-kanban-md-code"><code>${escapeHtml(blocks[Number(i)])}</code></pre>`,
293
+ );
294
+ return html;
295
+ }
296
+
297
+ function MarkdownBlock(props) {
298
+ const enabled = props.enabled !== false;
299
+ if (!enabled) {
300
+ return h("pre", { className: "hermes-kanban-pre" }, props.source || "");
301
+ }
302
+ return h("div", {
303
+ className: "hermes-kanban-md",
304
+ dangerouslySetInnerHTML: { __html: renderMarkdown(props.source || "") },
305
+ });
306
+ }
307
+
308
+ // -------------------------------------------------------------------------
309
+ // Touch drag-drop helper.
310
+ //
311
+ // HTML5 DnD is desktop-only. On touch devices we attach a pointerdown
312
+ // handler that simulates a drag proxy and fires a custom event on the
313
+ // column under the finger when released. Columns listen for both the
314
+ // standard `drop` event and our `hermes-kanban:drop` event.
315
+ // -------------------------------------------------------------------------
316
+
317
+ function attachTouchDrag(el, taskId) {
318
+ if (!el) return;
319
+ function onDown(e) {
320
+ if (e.pointerType !== "touch") return;
321
+ e.preventDefault();
322
+ const proxy = el.cloneNode(true);
323
+ proxy.classList.add("hermes-kanban-touch-proxy");
324
+ document.body.appendChild(proxy);
325
+ let lastTarget = null;
326
+
327
+ function move(ev) {
328
+ proxy.style.left = `${ev.clientX - proxy.offsetWidth / 2}px`;
329
+ proxy.style.top = `${ev.clientY - 24}px`;
330
+ proxy.style.display = "none";
331
+ const under = document.elementFromPoint(ev.clientX, ev.clientY);
332
+ proxy.style.display = "";
333
+ const col = under && under.closest && under.closest("[data-kanban-column]");
334
+ if (col !== lastTarget) {
335
+ if (lastTarget) lastTarget.classList.remove("hermes-kanban-column--drop");
336
+ if (col) col.classList.add("hermes-kanban-column--drop");
337
+ lastTarget = col;
338
+ }
339
+ }
340
+ function up() {
341
+ document.removeEventListener("pointermove", move);
342
+ document.removeEventListener("pointerup", up);
343
+ document.removeEventListener("pointercancel", up);
344
+ if (lastTarget) {
345
+ lastTarget.classList.remove("hermes-kanban-column--drop");
346
+ const status = lastTarget.getAttribute("data-kanban-column");
347
+ lastTarget.dispatchEvent(new CustomEvent("hermes-kanban:drop", {
348
+ detail: { taskId, status },
349
+ bubbles: true,
350
+ }));
351
+ }
352
+ proxy.remove();
353
+ }
354
+ // Kick off proxy at the pointer origin.
355
+ proxy.style.position = "fixed";
356
+ proxy.style.pointerEvents = "none";
357
+ proxy.style.opacity = "0.85";
358
+ proxy.style.zIndex = "9999";
359
+ proxy.style.width = `${el.offsetWidth}px`;
360
+ proxy.style.left = `${e.clientX - el.offsetWidth / 2}px`;
361
+ proxy.style.top = `${e.clientY - 24}px`;
362
+ document.addEventListener("pointermove", move);
363
+ document.addEventListener("pointerup", up);
364
+ document.addEventListener("pointercancel", up);
365
+ }
366
+ el.addEventListener("pointerdown", onDown);
367
+ return function () { el.removeEventListener("pointerdown", onDown); };
368
+ }
369
+
370
+ // -------------------------------------------------------------------------
371
+ // Error boundary
372
+ // -------------------------------------------------------------------------
373
+
374
+ // Wrap the boundary's fallback in a tiny function component so we can
375
+ // call useI18n() — class components can't use hooks directly.
376
+ function ErrorBoundaryFallback(props) {
377
+ const { t } = useI18n();
378
+ return h(Card, null,
379
+ h(CardContent, { className: "p-6 text-sm" },
380
+ h("div", { className: "text-destructive font-semibold mb-1" },
381
+ tx(t, "renderingError", "Kanban tab hit a rendering error")),
382
+ h("div", { className: "text-muted-foreground text-xs mb-3" },
383
+ props.message),
384
+ h(Button, {
385
+ onClick: props.onReset,
386
+ size: "sm",
387
+ }, tx(t, "reloadView", "Reload view")),
388
+ ),
389
+ );
390
+ }
391
+
392
+ class ErrorBoundary extends React.Component {
393
+ constructor(props) { super(props); this.state = { error: null }; }
394
+ static getDerivedStateFromError(error) { return { error }; }
395
+ componentDidCatch(error, info) {
396
+ // eslint-disable-next-line no-console
397
+ console.error("Kanban plugin crashed:", error, info);
398
+ }
399
+ render() {
400
+ if (this.state.error) {
401
+ return h(ErrorBoundaryFallback, {
402
+ message: String(this.state.error && this.state.error.message || this.state.error),
403
+ onReset: () => this.setState({ error: null }),
404
+ });
405
+ }
406
+ return this.props.children;
407
+ }
408
+ }
409
+
410
+ // -------------------------------------------------------------------------
411
+ // Root page
412
+ // -------------------------------------------------------------------------
413
+
414
+ function KanbanPage() {
415
+ const { t } = useI18n();
416
+ const [board, setBoard] = useState(() => readSelectedBoard() || "default");
417
+ const [boardList, setBoardList] = useState([]); // [{slug, name, counts, ...}]
418
+ const [showNewBoard, setShowNewBoard] = useState(false);
419
+
420
+ const [kanbanBoard, setKanbanBoard] = useState(null); // the grid data
421
+ // Alias so the rest of the function can keep using `board` semantically
422
+ // for the grid data (card columns + tenants + assignees) without
423
+ // colliding with the selected-board slug above. History: the old
424
+ // component had `const [board, setBoard]` for the grid data. We
425
+ // renamed the grid data to `kanbanBoard` so the more useful name
426
+ // (`board`) belongs to the selected slug.
427
+ const boardData = kanbanBoard;
428
+ const setBoardData = setKanbanBoard;
429
+ const [config, setConfig] = useState(null);
430
+ const [loading, setLoading] = useState(true);
431
+ const [error, setError] = useState(null);
432
+
433
+ const [tenantFilter, setTenantFilter] = useState("");
434
+ const [assigneeFilter, setAssigneeFilter] = useState("");
435
+ const [includeArchived, setIncludeArchived] = useState(false);
436
+ const [search, setSearch] = useState("");
437
+ const [laneByProfile, setLaneByProfile] = useState(true);
438
+ const [configApplied, setConfigApplied] = useState(false);
439
+
440
+ const [selectedTaskId, setSelectedTaskId] = useState(null);
441
+ const [selectedIds, setSelectedIds] = useState(() => new Set());
442
+ const [lastSelectedId, setLastSelectedId] = useState(null);
443
+ const [failedIds, setFailedIds] = useState(() => new Set());
444
+ const [draggingTaskId, setDraggingTaskId] = useState(null);
445
+ const handleDragStart = useCallback(function (taskId) { setDraggingTaskId(taskId); }, []);
446
+ const handleDragEnd = useCallback(function () { setDraggingTaskId(null); }, []);
447
+ // Per-task event counter incremented whenever the WS stream reports
448
+ // a new event for that task id. TaskDrawer useEffect-depends on its
449
+ // own task's counter so it reloads itself on live events instead of
450
+ // showing stale data.
451
+ const [taskEventTick, setTaskEventTick] = useState({});
452
+
453
+ const cursorRef = useRef(0);
454
+ const reloadTimerRef = useRef(null);
455
+ const wsRef = useRef(null);
456
+ const wsBackoffRef = useRef(1000);
457
+ const wsClosedRef = useRef(false);
458
+
459
+ // --- load config once ---------------------------------------------------
460
+ useEffect(function () {
461
+ SDK.fetchJSON(withBoard(`${API}/config`, board))
462
+ .then(function (c) {
463
+ setConfig(c);
464
+ if (!configApplied) {
465
+ if (c.default_tenant) setTenantFilter(c.default_tenant);
466
+ if (typeof c.lane_by_profile === "boolean") setLaneByProfile(c.lane_by_profile);
467
+ if (typeof c.include_archived_by_default === "boolean") setIncludeArchived(c.include_archived_by_default);
468
+ setConfigApplied(true);
469
+ }
470
+ })
471
+ .catch(function () { setConfig({ render_markdown: true }); });
472
+ }, []); // eslint-disable-line react-hooks/exhaustive-deps
473
+
474
+ // --- fetch full board ---------------------------------------------------
475
+ const loadBoard = useCallback(() => {
476
+ const qs = new URLSearchParams();
477
+ if (tenantFilter) qs.set("tenant", tenantFilter);
478
+ if (includeArchived) qs.set("include_archived", "true");
479
+ const url = qs.toString() ? `${API}/board?${qs}` : `${API}/board`;
480
+ return SDK.fetchJSON(withBoard(url, board))
481
+ .then(function (data) {
482
+ setBoardData(data);
483
+ cursorRef.current = data.latest_event_id || 0;
484
+ setError(null);
485
+ })
486
+ .catch(function (err) {
487
+ setError(String(err && err.message ? err.message : err));
488
+ })
489
+ .finally(function () { setLoading(false); });
490
+ }, [tenantFilter, includeArchived, board]);
491
+
492
+ // --- load list of boards for the switcher ------------------------------
493
+ const loadBoardList = useCallback(function () {
494
+ return SDK.fetchJSON(withBoard(`${API}/boards`, board))
495
+ .then(function (data) {
496
+ const boards = (data && data.boards) || [];
497
+ setBoardList(boards);
498
+ // If the stored slug isn't in the list any longer (board was
499
+ // deleted in the CLI while dashboard was open), fall back to
500
+ // default so the UI doesn't hang on a 404.
501
+ if (board !== "default" && !boards.find(function (b) { return b.slug === board; })) {
502
+ setBoard("default");
503
+ writeSelectedBoard("default");
504
+ }
505
+ })
506
+ .catch(function () { /* non-fatal */ });
507
+ }, [board]);
508
+
509
+ useEffect(function () { loadBoardList(); }, [loadBoardList]);
510
+
511
+ const scheduleReload = useCallback(function () {
512
+ if (reloadTimerRef.current) return;
513
+ reloadTimerRef.current = setTimeout(function () {
514
+ reloadTimerRef.current = null;
515
+ loadBoard();
516
+ }, 250);
517
+ }, [loadBoard]);
518
+
519
+ useEffect(function () {
520
+ loadBoard();
521
+ return function () {
522
+ if (reloadTimerRef.current) {
523
+ clearTimeout(reloadTimerRef.current);
524
+ reloadTimerRef.current = null;
525
+ }
526
+ };
527
+ }, [loadBoard]);
528
+
529
+ // --- WebSocket ---------------------------------------------------------
530
+ useEffect(function () {
531
+ if (!boardData) return undefined;
532
+ wsClosedRef.current = false;
533
+ function openWs() {
534
+ if (wsClosedRef.current) return;
535
+ const token = window.__HERMES_SESSION_TOKEN__ || "";
536
+ const proto = window.location.protocol === "https:" ? "wss:" : "ws:";
537
+ const qsParams = {
538
+ since: String(cursorRef.current || 0),
539
+ token: token,
540
+ };
541
+ // Pin the WS stream to the currently-selected board so events
542
+ // from other boards don't bleed in. Includes "default" so the
543
+ // dashboard's own board pin always wins over the server-side
544
+ // ``current`` file — same rationale as ``withBoard()`` above.
545
+ // Regression: #20879.
546
+ if (board) qsParams.board = board;
547
+ const qs = new URLSearchParams(qsParams);
548
+ const url = `${proto}//${window.location.host}${API}/events?${qs}`;
549
+ let ws;
550
+ try { ws = new WebSocket(url); } catch (_e) { return; }
551
+ wsRef.current = ws;
552
+ ws.onopen = function () { wsBackoffRef.current = 1000; };
553
+ ws.onmessage = function (ev) {
554
+ try {
555
+ const msg = JSON.parse(ev.data);
556
+ if (msg && Array.isArray(msg.events) && msg.events.length > 0) {
557
+ cursorRef.current = msg.cursor || cursorRef.current;
558
+ // Stamp per-task signal so the TaskDrawer can reload itself.
559
+ setTaskEventTick(function (prev) {
560
+ const next = Object.assign({}, prev);
561
+ for (const e of msg.events) {
562
+ if (e && e.task_id) next[e.task_id] = (next[e.task_id] || 0) + 1;
563
+ }
564
+ return next;
565
+ });
566
+ scheduleReload();
567
+ }
568
+ } catch (_e) { /* ignore */ }
569
+ };
570
+ ws.onclose = function (ev) {
571
+ if (wsClosedRef.current) return;
572
+ if (ev && ev.code === 1008) {
573
+ setError(tx(t, "wsAuthFailed",
574
+ "WebSocket auth failed — reload the page to refresh the session token."));
575
+ return;
576
+ }
577
+ const delay = Math.min(wsBackoffRef.current, 30000);
578
+ wsBackoffRef.current = Math.min(wsBackoffRef.current * 2, 30000);
579
+ setTimeout(openWs, delay);
580
+ };
581
+ }
582
+ openWs();
583
+ return function () {
584
+ wsClosedRef.current = true;
585
+ try { wsRef.current && wsRef.current.close(); } catch (_e) { /* noop */ }
586
+ };
587
+ }, [!!boardData, board, scheduleReload]);
588
+
589
+ // --- filtering ----------------------------------------------------------
590
+ const filteredBoard = useMemo(function () {
591
+ if (!boardData) return null;
592
+ const q = search.trim().toLowerCase();
593
+ const filterTask = function (t) {
594
+ if (tenantFilter && t.tenant !== tenantFilter) return false;
595
+ if (assigneeFilter && t.assignee !== assigneeFilter) return false;
596
+ if (q) {
597
+ const hay = `${t.id} ${t.title || ""} ${t.body || ""} ${t.result || ""} ${t.latest_summary || ""} ${t.assignee || ""} ${t.tenant || ""}`.toLowerCase();
598
+ if (hay.indexOf(q) === -1) return false;
599
+ }
600
+ return true;
601
+ };
602
+ return Object.assign({}, boardData, {
603
+ columns: boardData.columns.map(function (col) {
604
+ return Object.assign({}, col, { tasks: col.tasks.filter(filterTask) });
605
+ }),
606
+ });
607
+ }, [boardData, tenantFilter, assigneeFilter, search]);
608
+
609
+ // --- actions ------------------------------------------------------------
610
+ const moveTask = useCallback(function (taskId, newStatus) {
611
+ const confirmMsg = getDestructiveConfirm(t, newStatus);
612
+ if (confirmMsg && !window.confirm(confirmMsg)) return;
613
+ const patch = withCompletionSummary({ status: newStatus }, 1, t);
614
+ if (!patch) return;
615
+ setBoardData(function (b) {
616
+ if (!b) return b;
617
+ let moved = null;
618
+ const columns = b.columns.map(function (col) {
619
+ const next = col.tasks.filter(function (t) {
620
+ if (t.id === taskId) { moved = Object.assign({}, t, { status: newStatus }); return false; }
621
+ return true;
622
+ });
623
+ return Object.assign({}, col, { tasks: next });
624
+ });
625
+ if (moved) {
626
+ const dest = columns.find(function (c) { return c.name === newStatus; });
627
+ if (dest) dest.tasks = [moved].concat(dest.tasks);
628
+ }
629
+ return Object.assign({}, b, { columns });
630
+ });
631
+ SDK.fetchJSON(withBoard(`${API}/tasks/${encodeURIComponent(taskId)}`, board), {
632
+ method: "PATCH",
633
+ headers: { "Content-Type": "application/json" },
634
+ body: JSON.stringify(patch),
635
+ }).catch(function (err) {
636
+ setError(tx(t, "moveFailed", "Move failed: ") + (err.message || err));
637
+ loadBoard();
638
+ });
639
+ }, [loadBoard, board, t]);
640
+
641
+ const clearSelected = useCallback(function () {
642
+ setSelectedIds(new Set());
643
+ setLastSelectedId(null);
644
+ setFailedIds(new Set());
645
+ }, []);
646
+ const moveSelected = useCallback(function (newStatus) {
647
+ const confirmMsg = DESTRUCTIVE_TRANSITIONS[newStatus];
648
+ if (confirmMsg && !window.confirm(confirmMsg)) return;
649
+ if (selectedIds.size === 0) return;
650
+ const patch = withCompletionSummary({ status: newStatus }, selectedIds.size);
651
+ if (!patch) return;
652
+ const ids = Array.from(selectedIds);
653
+ // Optimistic UI: remove selected from all columns and prepend to target.
654
+ setBoardData(function (b) {
655
+ if (!b) return b;
656
+ const moved = [];
657
+ const columns = b.columns.map(function (col) {
658
+ const kept = [];
659
+ for (const t of col.tasks) {
660
+ if (selectedIds.has(t.id)) moved.push(Object.assign({}, t, { status: newStatus }));
661
+ else kept.push(t);
662
+ }
663
+ return Object.assign({}, col, { tasks: kept });
664
+ });
665
+ const dest = columns.find(function (c) { return c.name === newStatus; });
666
+ if (dest) dest.tasks = moved.concat(dest.tasks);
667
+ return Object.assign({}, b, { columns });
668
+ });
669
+ SDK.fetchJSON(withBoard(`${API}/tasks/bulk`, board), {
670
+ method: "POST",
671
+ headers: { "Content-Type": "application/json" },
672
+ body: JSON.stringify(Object.assign({ ids }, patch)),
673
+ }).then(function (res) {
674
+ const failed = (res.results || []).filter(function (r) { return !r.ok; });
675
+ if (failed.length > 0) {
676
+ setError(`Bulk move: ${failed.length} of ${res.results.length} failed`);
677
+ setFailedIds(new Set(failed.map(function (f) { return f.id; })));
678
+ } else {
679
+ setFailedIds(new Set());
680
+ }
681
+ setSelectedIds(new Set());
682
+ setLastSelectedId(null);
683
+ loadBoard();
684
+ }).catch(function (err) {
685
+ setError(`Move failed: ${err.message || err}`);
686
+ setFailedIds(new Set(selectedIds));
687
+ loadBoard();
688
+ });
689
+ }, [selectedIds, loadBoard, board]);
690
+
691
+ const createTask = useCallback(function (body) {
692
+ return SDK.fetchJSON(withBoard(`${API}/tasks`, board), {
693
+ method: "POST",
694
+ headers: { "Content-Type": "application/json" },
695
+ body: JSON.stringify(body),
696
+ }).then(function (res) {
697
+ // Surface dispatcher-presence warnings (e.g. "no gateway is
698
+ // running") via the existing error banner channel. Not fatal —
699
+ // the task was created successfully — but the user should know
700
+ // their ready task will sit idle until the gateway is up.
701
+ if (res && res.warning) {
702
+ setError(tx(t, "taskCreatedWarning", "Task created, but: ") + res.warning);
703
+ }
704
+ loadBoard();
705
+ loadBoardList(); // refresh counts in the switcher
706
+ return res;
707
+ });
708
+ }, [loadBoard, loadBoardList, board, t]);
709
+
710
+ const toggleSelected = useCallback(function (id, additive) {
711
+ setSelectedIds(function (prev) {
712
+ const next = new Set(additive ? prev : []);
713
+ if (prev.has(id)) next.delete(id);
714
+ else next.add(id);
715
+ return next;
716
+ });
717
+ setLastSelectedId(id);
718
+ setFailedIds(function (prev) {
719
+ if (prev.has(id)) {
720
+ const next = new Set(prev);
721
+ next.delete(id);
722
+ return next;
723
+ }
724
+ return prev;
725
+ });
726
+ }, []);
727
+
728
+ const toggleRange = useCallback(function (toId) {
729
+ // Build flat visible task order from filteredBoard columns.
730
+ setSelectedIds(function (prev) {
731
+ const next = new Set(prev);
732
+ if (!filteredBoard || !filteredBoard.columns) return next;
733
+ const order = [];
734
+ for (const col of filteredBoard.columns) {
735
+ for (const t of col.tasks || []) order.push(t.id);
736
+ }
737
+ const anchor = lastSelectedId;
738
+ if (!anchor || anchor === toId) {
739
+ next.add(toId);
740
+ return next;
741
+ }
742
+ const aIdx = order.indexOf(anchor);
743
+ const bIdx = order.indexOf(toId);
744
+ if (aIdx === -1 || bIdx === -1) {
745
+ next.add(toId);
746
+ return next;
747
+ }
748
+ const lo = Math.min(aIdx, bIdx);
749
+ const hi = Math.max(aIdx, bIdx);
750
+ for (let i = lo; i <= hi; i++) next.add(order[i]);
751
+ return next;
752
+ });
753
+ setLastSelectedId(toId);
754
+ }, [filteredBoard, lastSelectedId]);
755
+
756
+ const selectAllVisible = useCallback(function () {
757
+ if (!filteredBoard || !filteredBoard.columns) return;
758
+ const next = new Set();
759
+ for (const col of filteredBoard.columns) {
760
+ for (const t of col.tasks || []) next.add(t.id);
761
+ }
762
+ setSelectedIds(next);
763
+ if (next.size > 0) {
764
+ const first = Array.from(next)[0];
765
+ setLastSelectedId(first);
766
+ }
767
+ }, [filteredBoard]);
768
+
769
+ const selectAllInColumn = useCallback(function (columnName) {
770
+ if (!filteredBoard || !filteredBoard.columns) return;
771
+ const col = filteredBoard.columns.find(function (c) { return c.name === columnName; });
772
+ if (!col) return;
773
+ const allSelected = col.tasks && col.tasks.length > 0 && col.tasks.every(function (t) { return selectedIds.has(t.id); });
774
+ const next = new Set(selectedIds);
775
+ if (allSelected) {
776
+ for (const t of col.tasks || []) next.delete(t.id);
777
+ } else {
778
+ for (const t of col.tasks || []) next.add(t.id);
779
+ }
780
+ setSelectedIds(next);
781
+ if (col.tasks && col.tasks.length > 0) setLastSelectedId(col.tasks[0].id);
782
+ }, [filteredBoard, selectedIds]);
783
+
784
+ const applyBulk = useCallback(function (patch, confirmMsg) {
785
+ if (selectedIds.size === 0) return;
786
+ if (confirmMsg && !window.confirm(confirmMsg)) return;
787
+ const finalPatch = withCompletionSummary(patch, selectedIds.size, t);
788
+ if (!finalPatch) return;
789
+ const body = Object.assign({ ids: Array.from(selectedIds) }, finalPatch);
790
+ // Optimistic UI for status moves (same pattern as moveSelected).
791
+ if (finalPatch.status) {
792
+ setBoardData(function (b) {
793
+ if (!b) return b;
794
+ const moved = [];
795
+ const columns = b.columns.map(function (col) {
796
+ const kept = [];
797
+ for (const t of col.tasks) {
798
+ if (selectedIds.has(t.id)) moved.push(Object.assign({}, t, { status: finalPatch.status }));
799
+ else kept.push(t);
800
+ }
801
+ return Object.assign({}, col, { tasks: kept });
802
+ });
803
+ const dest = columns.find(function (c) { return c.name === finalPatch.status; });
804
+ if (dest) dest.tasks = moved.concat(dest.tasks);
805
+ return Object.assign({}, b, { columns });
806
+ });
807
+ }
808
+ SDK.fetchJSON(withBoard(`${API}/tasks/bulk`, board), {
809
+ method: "POST",
810
+ headers: { "Content-Type": "application/json" },
811
+ body: JSON.stringify(body),
812
+ })
813
+ .then(function (res) {
814
+ const failed = (res.results || []).filter(function (r) { return !r.ok; });
815
+ if (failed.length > 0) {
816
+ setError(tx(t, "bulkFailed", "Bulk: ") +
817
+ `${failed.length} of ${res.results.length} failed: ` +
818
+ failed.slice(0, 3).map(function (f) { return `${f.id} (${f.error})`; }).join("; "));
819
+ setFailedIds(new Set(failed.map(function (f) { return f.id; })));
820
+ } else {
821
+ setFailedIds(new Set());
822
+ }
823
+ setSelectedIds(new Set());
824
+ setLastSelectedId(null);
825
+ loadBoard();
826
+ })
827
+ .catch(function (e) {
828
+ setError(String(e.message || e));
829
+ setFailedIds(new Set(selectedIds));
830
+ loadBoard();
831
+ });
832
+ }, [selectedIds, loadBoard, board, t]);
833
+
834
+ // --- board switching ----------------------------------------------------
835
+ const switchBoard = useCallback(function (nextSlug) {
836
+ if (!nextSlug || nextSlug === board) return;
837
+ // Optimistic UI: clear the current grid + show loading, reset the
838
+ // event cursor so the WS reopens aligned to the new board's
839
+ // latest_event_id on the next loadBoard.
840
+ setBoardData(null);
841
+ cursorRef.current = 0;
842
+ setLoading(true);
843
+ setBoard(nextSlug);
844
+ writeSelectedBoard(nextSlug);
845
+ // Reset filters so stale search/tenant/assignee don't persist across boards.
846
+ setSearch("");
847
+ setTenantFilter("");
848
+ setAssigneeFilter("");
849
+ setIncludeArchived(false);
850
+ clearSelected();
851
+ }, [board, clearSelected]);
852
+
853
+ const createNewBoard = useCallback(function (payload) {
854
+ return SDK.fetchJSON(`${API}/boards`, {
855
+ method: "POST",
856
+ headers: { "Content-Type": "application/json" },
857
+ body: JSON.stringify(payload),
858
+ }).then(function (res) {
859
+ loadBoardList();
860
+ const slug = res && res.board && res.board.slug;
861
+ if (slug && payload.switch) switchBoard(slug);
862
+ return res;
863
+ });
864
+ }, [loadBoardList, switchBoard, board]);
865
+
866
+ const deleteBoard = useCallback(function (slug) {
867
+ if (!slug || slug === "default") return Promise.resolve();
868
+ return SDK.fetchJSON(`${API}/boards/${encodeURIComponent(slug)}`, {
869
+ method: "DELETE",
870
+ }).then(function () {
871
+ loadBoardList();
872
+ if (board === slug) switchBoard("default");
873
+ });
874
+ }, [board, loadBoardList, switchBoard]);
875
+
876
+ // --- render -------------------------------------------------------------
877
+ if (loading && !boardData) {
878
+ return h("div", { className: "p-8 text-sm text-muted-foreground" },
879
+ tx(t, "loading", "Loading Kanban board…"));
880
+ }
881
+ if (error && !boardData) {
882
+ return h(Card, null,
883
+ h(CardContent, { className: "p-6" },
884
+ h("div", { className: "text-sm text-destructive" },
885
+ tx(t, "loadFailed", "Failed to load Kanban board: "), error),
886
+ h("div", { className: "text-xs text-muted-foreground mt-2" },
887
+ tx(t, "loadFailedHint",
888
+ "The backend auto-creates kanban.db on first read. If this persists, check the dashboard logs.")),
889
+ ),
890
+ );
891
+ }
892
+ if (!filteredBoard) return null;
893
+
894
+ const renderMd = !config || config.render_markdown !== false;
895
+
896
+ return h(ErrorBoundary, null,
897
+ h("div", { className: "hermes-kanban flex flex-col gap-4" },
898
+ h(BoardSwitcher, {
899
+ board: board,
900
+ boardList: boardList,
901
+ onSwitch: switchBoard,
902
+ onNewClick: function () { setShowNewBoard(true); },
903
+ onDeleteBoard: deleteBoard,
904
+ }),
905
+ showNewBoard ? h(NewBoardDialog, {
906
+ onCancel: function () { setShowNewBoard(false); },
907
+ onCreate: function (payload) {
908
+ return createNewBoard(payload).then(function () { setShowNewBoard(false); });
909
+ },
910
+ }) : null,
911
+ h(AttentionStrip, {
912
+ boardData,
913
+ onOpen: setSelectedTaskId,
914
+ }),
915
+ h(BoardToolbar, {
916
+ board: boardData,
917
+ tenantFilter, setTenantFilter,
918
+ assigneeFilter, setAssigneeFilter,
919
+ includeArchived, setIncludeArchived,
920
+ laneByProfile, setLaneByProfile,
921
+ search, setSearch,
922
+ onNudgeDispatch: function () {
923
+ SDK.fetchJSON(withBoard(`${API}/dispatch?max=8`, board), { method: "POST" })
924
+ .then(loadBoard)
925
+ .catch(function (e) { setError(String(e.message || e)); });
926
+ },
927
+ onRefresh: loadBoard,
928
+ }),
929
+ selectedIds.size > 0 ? h(BulkActionBar, {
930
+ count: selectedIds.size,
931
+ assignees: (boardData && boardData.assignees) || [],
932
+ onApply: applyBulk,
933
+ onClear: clearSelected,
934
+ onSelectAllVisible: selectAllVisible,
935
+ }) : null,
936
+ error ? h("div", { className: "text-xs text-destructive px-2" }, error) : null,
937
+ h(BoardColumns, {
938
+ board: filteredBoard,
939
+ laneByProfile,
940
+ selectedIds,
941
+ failedIds,
942
+ draggingTaskId,
943
+ onDragStart: handleDragStart,
944
+ onDragEnd: handleDragEnd,
945
+ toggleSelected,
946
+ toggleRange,
947
+ selectAllInColumn,
948
+ onMove: moveTask,
949
+ onMoveSelected: moveSelected,
950
+ onOpen: setSelectedTaskId,
951
+ onCreate: createTask,
952
+ allTasks: boardData.columns.reduce(function (acc, c) { return acc.concat(c.tasks); }, []),
953
+ }),
954
+ selectedTaskId ? h(TaskDrawer, {
955
+ taskId: selectedTaskId,
956
+ boardSlug: board,
957
+ onClose: function () { setSelectedTaskId(null); },
958
+ onRefresh: loadBoard,
959
+ renderMarkdown: renderMd,
960
+ allTasks: boardData.columns.reduce(function (acc, c) { return acc.concat(c.tasks); }, []),
961
+ assignees: (boardData && boardData.assignees) || [],
962
+ eventTick: taskEventTick[selectedTaskId] || 0,
963
+ }) : null,
964
+ ),
965
+ );
966
+ }
967
+
968
+ // -------------------------------------------------------------------------
969
+ // Attention strip — surfaces every task with active diagnostics,
970
+ // severity-marked (warning/error/critical). Collapsed by default; click
971
+ // Show to expand into per-task rows with Open buttons. Dismissible
972
+ // per session via state flag.
973
+ // -------------------------------------------------------------------------
974
+
975
+ function collectDiagTasks(boardData) {
976
+ if (!boardData || !boardData.columns) return [];
977
+ const out = [];
978
+ for (const col of boardData.columns) {
979
+ for (const t of col.tasks || []) {
980
+ if (t.diagnostics && t.diagnostics.length > 0) out.push(t);
981
+ else if (t.warnings && t.warnings.count > 0) out.push(t);
982
+ }
983
+ }
984
+ // Sort: highest severity first (critical > error > warning), then by
985
+ // most recent latest_at.
986
+ const sevIdx = function (s) {
987
+ if (s === "critical") return 3;
988
+ if (s === "error") return 2;
989
+ if (s === "warning") return 1;
990
+ return 0;
991
+ };
992
+ out.sort(function (a, b) {
993
+ const aSev = sevIdx((a.warnings && a.warnings.highest_severity) || "warning");
994
+ const bSev = sevIdx((b.warnings && b.warnings.highest_severity) || "warning");
995
+ if (aSev !== bSev) return bSev - aSev;
996
+ const aLa = (a.warnings && a.warnings.latest_at) || 0;
997
+ const bLa = (b.warnings && b.warnings.latest_at) || 0;
998
+ return bLa - aLa;
999
+ });
1000
+ return out;
1001
+ }
1002
+
1003
+ function AttentionStrip(props) {
1004
+ const { t } = useI18n();
1005
+ const [expanded, setExpanded] = useState(false);
1006
+ const [dismissed, setDismissed] = useState(false);
1007
+ const diagTasks = useMemo(
1008
+ function () { return collectDiagTasks(props.boardData); },
1009
+ [props.boardData]
1010
+ );
1011
+ if (dismissed || diagTasks.length === 0) return null;
1012
+ // Pick the highest severity present so we can colour the strip.
1013
+ let topSev = "warning";
1014
+ for (const td of diagTasks) {
1015
+ const s = (td.warnings && td.warnings.highest_severity) || "warning";
1016
+ if (s === "critical") { topSev = "critical"; break; }
1017
+ if (s === "error" && topSev !== "critical") topSev = "error";
1018
+ }
1019
+ return h("div", {
1020
+ className: cn(
1021
+ "hermes-kanban-attention",
1022
+ "hermes-kanban-attention--" + topSev,
1023
+ ),
1024
+ },
1025
+ h("div", { className: "hermes-kanban-attention-bar" },
1026
+ h("span", { className: "hermes-kanban-attention-icon" },
1027
+ topSev === "critical" ? "!!!" : topSev === "error" ? "!!" : "⚠"),
1028
+ h("span", { className: "hermes-kanban-attention-text" },
1029
+ diagTasks.length === 1
1030
+ ? tx(t, "taskNeedsAttention", "1 task needs attention")
1031
+ : tx(t, "tasksNeedAttention", "{n} tasks need attention",
1032
+ { n: diagTasks.length }),
1033
+ ),
1034
+ h("button", {
1035
+ className: "hermes-kanban-attention-toggle",
1036
+ onClick: function () { setExpanded(function (x) { return !x; }); },
1037
+ type: "button",
1038
+ }, expanded ? tx(t, "hide", "Hide") : tx(t, "show", "Show")),
1039
+ h("button", {
1040
+ className: "hermes-kanban-attention-dismiss",
1041
+ onClick: function () { setDismissed(true); },
1042
+ title: "Hide until next page reload",
1043
+ type: "button",
1044
+ }, "\u2715"),
1045
+ ),
1046
+ expanded
1047
+ ? h("div", { className: "hermes-kanban-attention-list" },
1048
+ diagTasks.map(function (task) {
1049
+ const sev = (task.warnings && task.warnings.highest_severity) || "warning";
1050
+ const kinds = task.warnings && task.warnings.kinds ? Object.keys(task.warnings.kinds) : [];
1051
+ return h("div", {
1052
+ key: task.id,
1053
+ className: cn(
1054
+ "hermes-kanban-attention-row",
1055
+ "hermes-kanban-attention-row--" + sev,
1056
+ ),
1057
+ },
1058
+ h("span", { className: "hermes-kanban-attention-row-sev" },
1059
+ sev === "critical" ? "!!!" : sev === "error" ? "!!" : "⚠"),
1060
+ h("span", { className: "hermes-kanban-attention-row-id" }, task.id),
1061
+ h("span", { className: "hermes-kanban-attention-row-title" },
1062
+ task.title || tx(t, "untitled", "(untitled)")),
1063
+ h("span", { className: "hermes-kanban-attention-row-meta" },
1064
+ task.assignee ? "@" + task.assignee : tx(t, "unassigned", "unassigned"),
1065
+ " \u00b7 ",
1066
+ kinds.length > 0 ? kinds.join(", ") : tx(t, "diagnostic", "diagnostic"),
1067
+ ),
1068
+ h("button", {
1069
+ className: "hermes-kanban-attention-row-btn",
1070
+ onClick: function () { props.onOpen(task.id); },
1071
+ type: "button",
1072
+ }, tx(t, "open", "Open")),
1073
+ );
1074
+ }),
1075
+ )
1076
+ : null,
1077
+ );
1078
+ }
1079
+
1080
+ // -------------------------------------------------------------------------
1081
+ // Diagnostics section — generic renderer for a task's active distress
1082
+ // signals. Each diagnostic carries its own title, detail, data payload,
1083
+ // and a list of structured actions; the section renders them uniformly
1084
+ // regardless of kind. Replaces the hallucination-specific
1085
+ // ``RecoveryPopover`` from the previous iteration.
1086
+ //
1087
+ // Action kinds supported today:
1088
+ // reclaim → POST /tasks/:id/reclaim
1089
+ // reassign → POST /tasks/:id/reassign (with profile picker)
1090
+ // unblock → PATCH /tasks/:id body: {status: "ready"}
1091
+ // comment → scroll to the comment input at the bottom of the drawer
1092
+ // cli_hint → copy payload.command to clipboard
1093
+ // open_docs → open payload.url in a new tab
1094
+ // Unknown kinds are rendered as a disabled informational row so the
1095
+ // server can add new action kinds without breaking the UI.
1096
+ // -------------------------------------------------------------------------
1097
+
1098
+ function DiagnosticActionButton(props) {
1099
+ const { t } = useI18n();
1100
+ const { action, onExec, busy, extra } = props;
1101
+ const label = (action.suggested ? "\u2606 " : "") + action.label;
1102
+ const cls = cn(
1103
+ "hermes-kanban-diag-action-btn",
1104
+ action.suggested ? "hermes-kanban-diag-action-btn--suggested" : "",
1105
+ );
1106
+ if (action.kind === "reclaim" || action.kind === "reassign" ||
1107
+ action.kind === "unblock") {
1108
+ return h("button", {
1109
+ className: cls,
1110
+ disabled: busy || (extra && extra.disabled),
1111
+ onClick: function () { onExec(action); },
1112
+ type: "button",
1113
+ }, label);
1114
+ }
1115
+ if (action.kind === "cli_hint") {
1116
+ return h("button", {
1117
+ className: cls,
1118
+ disabled: busy,
1119
+ onClick: function () { onExec(action); },
1120
+ type: "button",
1121
+ title: tx(t, "copyCommand", "Copy command to clipboard"),
1122
+ }, (extra && extra.copied) ? tx(t, "copied", "Copied") : label);
1123
+ }
1124
+ if (action.kind === "comment") {
1125
+ return h("button", {
1126
+ className: cls,
1127
+ onClick: function () { onExec(action); },
1128
+ type: "button",
1129
+ }, label);
1130
+ }
1131
+ if (action.kind === "open_docs") {
1132
+ return h("a", {
1133
+ className: cls,
1134
+ href: (action.payload && action.payload.url) || "#",
1135
+ target: "_blank",
1136
+ rel: "noreferrer",
1137
+ }, label);
1138
+ }
1139
+ // Unknown kind — render informational, non-interactive.
1140
+ return h("span", { className: cls + " hermes-kanban-diag-action-btn--unknown" },
1141
+ label);
1142
+ }
1143
+
1144
+ function DiagnosticCard(props) {
1145
+ const { t } = useI18n();
1146
+ const { diag, task, boardSlug, assignees, onRefresh } = props;
1147
+ const [busy, setBusy] = useState(false);
1148
+ const [msg, setMsg] = useState(null);
1149
+ const [copiedKey, setCopiedKey] = useState(null);
1150
+ const [reassignProfile, setReassignProfile] = useState(task.assignee || "");
1151
+
1152
+ const execAction = function (action) {
1153
+ if (busy) return;
1154
+ if (action.kind === "cli_hint") {
1155
+ const cmd = (action.payload && action.payload.command) || action.label;
1156
+ const fallback = function () { window.prompt("Copy this command:", cmd); };
1157
+ try {
1158
+ const p = navigator.clipboard && navigator.clipboard.writeText(cmd);
1159
+ if (p && p.then) {
1160
+ p.then(function () {
1161
+ setCopiedKey(action.label);
1162
+ setTimeout(function () { setCopiedKey(null); }, 2000);
1163
+ }).catch(fallback);
1164
+ } else {
1165
+ fallback();
1166
+ }
1167
+ } catch (_) {
1168
+ fallback();
1169
+ }
1170
+ return;
1171
+ }
1172
+ if (action.kind === "comment") {
1173
+ // Scroll the comment input into view; the drawer already has one
1174
+ // at the bottom. Focus it so the operator can start typing.
1175
+ const ta = document.querySelector(".hermes-kanban-drawer-comment-row input, .hermes-kanban-drawer-comment-row textarea");
1176
+ if (ta) {
1177
+ ta.scrollIntoView({ behavior: "smooth", block: "nearest" });
1178
+ ta.focus();
1179
+ }
1180
+ return;
1181
+ }
1182
+ if (action.kind === "unblock") {
1183
+ setBusy(true); setMsg(null);
1184
+ const url = withBoard(`${API}/tasks/${encodeURIComponent(task.id)}`, boardSlug);
1185
+ SDK.fetchJSON(url, {
1186
+ method: "PATCH",
1187
+ headers: { "Content-Type": "application/json" },
1188
+ body: JSON.stringify({ status: "ready" }),
1189
+ }).then(function () {
1190
+ setMsg({ ok: true, text: tx(t, "unblockedMessage",
1191
+ "Unblocked {id}. Task is ready for the next tick.", { id: task.id }) });
1192
+ if (onRefresh) onRefresh();
1193
+ }).catch(function (err) {
1194
+ setMsg({ ok: false, text: tx(t, "unblockFailed", "Unblock failed: ") + (err.message || err) });
1195
+ }).then(function () { setBusy(false); });
1196
+ return;
1197
+ }
1198
+ if (action.kind === "reclaim") {
1199
+ setBusy(true); setMsg(null);
1200
+ const url = withBoard(`${API}/tasks/${encodeURIComponent(task.id)}/reclaim`, boardSlug);
1201
+ SDK.fetchJSON(url, {
1202
+ method: "POST",
1203
+ headers: { "Content-Type": "application/json" },
1204
+ body: JSON.stringify({ reason: `recovery action for ${diag.kind}` }),
1205
+ }).then(function () {
1206
+ setMsg({ ok: true, text: tx(t, "reclaimedMessage",
1207
+ "Reclaimed {id}. Task is back to ready.", { id: task.id }) });
1208
+ if (onRefresh) onRefresh();
1209
+ }).catch(function (err) {
1210
+ setMsg({ ok: false, text: tx(t, "reclaimFailed", "Reclaim failed: ") + (err.message || err) });
1211
+ }).then(function () { setBusy(false); });
1212
+ return;
1213
+ }
1214
+ if (action.kind === "reassign") {
1215
+ if (!reassignProfile) {
1216
+ setMsg({ ok: false, text: tx(t, "pickProfileFirst", "Pick a profile first.") });
1217
+ return;
1218
+ }
1219
+ setBusy(true); setMsg(null);
1220
+ const url = withBoard(`${API}/tasks/${encodeURIComponent(task.id)}/reassign`, boardSlug);
1221
+ const body = {
1222
+ profile: reassignProfile || null,
1223
+ reclaim_first: !!(action.payload && action.payload.reclaim_first),
1224
+ reason: `recovery action for ${diag.kind}`,
1225
+ };
1226
+ SDK.fetchJSON(url, {
1227
+ method: "POST",
1228
+ headers: { "Content-Type": "application/json" },
1229
+ body: JSON.stringify(body),
1230
+ }).then(function () {
1231
+ setMsg({
1232
+ ok: true,
1233
+ text: tx(t, "reassignedMessage", "Reassigned {id} to {profile}.",
1234
+ { id: task.id, profile: reassignProfile }),
1235
+ });
1236
+ if (onRefresh) onRefresh();
1237
+ }).catch(function (err) {
1238
+ setMsg({ ok: false, text: tx(t, "reassignFailed", "Reassign failed: ") + (err.message || err) });
1239
+ }).then(function () { setBusy(false); });
1240
+ return;
1241
+ }
1242
+ };
1243
+
1244
+ // Pull out the reassign action so we can render its picker inline.
1245
+ const reassignAction = (diag.actions || []).find(function (a) {
1246
+ return a.kind === "reassign";
1247
+ });
1248
+
1249
+ const sevClass = "hermes-kanban-diag--" + (diag.severity || "warning");
1250
+ return h("div", { className: cn("hermes-kanban-diag", sevClass) },
1251
+ h("div", { className: "hermes-kanban-diag-header" },
1252
+ h("span", { className: "hermes-kanban-diag-sev" },
1253
+ diag.severity === "critical" ? "!!!" :
1254
+ diag.severity === "error" ? "!!" : "\u26a0"),
1255
+ h("span", { className: "hermes-kanban-diag-title" },
1256
+ diag.title),
1257
+ ),
1258
+ h("div", { className: "hermes-kanban-diag-detail" },
1259
+ diag.detail),
1260
+ diag.data && Object.keys(diag.data).length > 0
1261
+ ? h("div", { className: "hermes-kanban-diag-data" },
1262
+ Object.keys(diag.data).map(function (k) {
1263
+ const v = diag.data[k];
1264
+ if (Array.isArray(v) && v.length > 0 && typeof v[0] === "string" &&
1265
+ v[0].indexOf("t_") === 0) {
1266
+ // Task-id list — render as chips.
1267
+ return h("div", { key: k, className: "hermes-kanban-diag-data-row" },
1268
+ h("span", { className: "hermes-kanban-diag-data-key" }, k + ":"),
1269
+ v.map(function (x) {
1270
+ return h("code", {
1271
+ key: x, className: "hermes-kanban-event-phantom-chip",
1272
+ }, x);
1273
+ }),
1274
+ );
1275
+ }
1276
+ return h("div", { key: k, className: "hermes-kanban-diag-data-row" },
1277
+ h("span", { className: "hermes-kanban-diag-data-key" }, k + ":"),
1278
+ h("span", { className: "hermes-kanban-diag-data-val" },
1279
+ Array.isArray(v) ? v.join(", ") : String(v)),
1280
+ );
1281
+ }),
1282
+ )
1283
+ : null,
1284
+ // Inline reassign picker — only shown when the diagnostic offers
1285
+ // a reassign action. Profile list comes from the board payload.
1286
+ reassignAction
1287
+ ? h("div", { className: "hermes-kanban-diag-reassign-row" },
1288
+ h("span", { className: "hermes-kanban-diag-reassign-label" },
1289
+ tx(t, "reassignTo", "Reassign to:")),
1290
+ h("select", {
1291
+ className: "hermes-kanban-recovery-select",
1292
+ value: reassignProfile,
1293
+ onChange: function (e) { setReassignProfile(e.target.value); },
1294
+ },
1295
+ h("option", { value: "" }, "(unassigned)"),
1296
+ (assignees || []).map(function (a) {
1297
+ return h("option", { key: a, value: a }, a);
1298
+ }),
1299
+ ),
1300
+ )
1301
+ : null,
1302
+ h("div", { className: "hermes-kanban-diag-actions" },
1303
+ (diag.actions || []).map(function (a, i) {
1304
+ return h(DiagnosticActionButton, {
1305
+ key: a.kind + i,
1306
+ action: a,
1307
+ onExec: execAction,
1308
+ busy: busy,
1309
+ extra: {
1310
+ copied: copiedKey === a.label,
1311
+ disabled: (a.kind === "reassign" && !reassignProfile),
1312
+ },
1313
+ });
1314
+ }),
1315
+ ),
1316
+ msg
1317
+ ? h("div", {
1318
+ className: cn(
1319
+ "hermes-kanban-diag-msg",
1320
+ msg.ok ? "hermes-kanban-diag-msg--ok" : "hermes-kanban-diag-msg--err",
1321
+ ),
1322
+ }, msg.text)
1323
+ : null,
1324
+ );
1325
+ }
1326
+
1327
+ function DiagnosticsSection(props) {
1328
+ const { t } = useI18n();
1329
+ const diags = props.diagnostics || [];
1330
+ const hasOpenDiags = diags.length > 0;
1331
+ const [open, setOpen] = useState(hasOpenDiags);
1332
+ useEffect(function () {
1333
+ if (hasOpenDiags) setOpen(true);
1334
+ }, [hasOpenDiags]);
1335
+ if (!hasOpenDiags && !props.alwaysVisible) {
1336
+ // Nothing active. Collapse the section entirely rather than showing
1337
+ // an empty "Recovery" header — keeps clean tasks visually clean.
1338
+ return null;
1339
+ }
1340
+ return h("div", { className: "hermes-kanban-section" },
1341
+ h("div", { className: "hermes-kanban-section-head-row" },
1342
+ h("span", { className: "hermes-kanban-section-head" },
1343
+ hasOpenDiags
1344
+ ? h("span", { className: "hermes-kanban-section-head-warning" },
1345
+ `\u26a0 ${tx(t, "diagnostics", "Diagnostics")} (${diags.length})`)
1346
+ : tx(t, "diagnostics", "Diagnostics"),
1347
+ ),
1348
+ h("button", {
1349
+ className: "hermes-kanban-section-toggle",
1350
+ onClick: function () { setOpen(function (x) { return !x; }); },
1351
+ type: "button",
1352
+ }, open ? tx(t, "hide", "Hide") : tx(t, "show", "Show")),
1353
+ ),
1354
+ open
1355
+ ? h("div", { className: "hermes-kanban-diag-list" },
1356
+ diags.map(function (d, i) {
1357
+ return h(DiagnosticCard, {
1358
+ key: props.task.id + ":" + d.kind + i,
1359
+ diag: d,
1360
+ task: props.task,
1361
+ boardSlug: props.boardSlug,
1362
+ assignees: props.assignees,
1363
+ onRefresh: props.onRefresh,
1364
+ });
1365
+ }),
1366
+ )
1367
+ : null,
1368
+ );
1369
+ }
1370
+
1371
+ // -------------------------------------------------------------------------
1372
+ // Board switcher (multi-project)
1373
+ // -------------------------------------------------------------------------
1374
+
1375
+ // Small `?` affordance next to the board controls. Opens the kanban docs
1376
+ // page in a new tab so users can look up what any of the widgets mean
1377
+ // without losing the current board view.
1378
+ function DocsLink() {
1379
+ return h("a", {
1380
+ href: DOCS_URL,
1381
+ target: "_blank",
1382
+ rel: "noopener noreferrer",
1383
+ className: "hermes-kanban-docs-link",
1384
+ title: "Open Hermes Kanban docs in a new tab",
1385
+ "aria-label": "Hermes Kanban documentation",
1386
+ }, "?");
1387
+ }
1388
+
1389
+ function BoardSwitcher(props) {
1390
+ const { t } = useI18n();
1391
+ const list = props.boardList || [];
1392
+ const current = list.find(function (b) { return b.slug === props.board; });
1393
+ const currentName = current && current.name ? current.name : props.board;
1394
+ const currentTotal = current ? current.total : 0;
1395
+ const hasMultipleBoards = list.length > 1;
1396
+
1397
+ // Hide entirely when only the default board exists AND it's empty —
1398
+ // single-project users never see boards UI unless they ask for it.
1399
+ // We show the [+ New board] affordance as soon as any board has a
1400
+ // task (so the user can discover multi-project before they need it)
1401
+ // OR when any non-default board exists.
1402
+ const totalAcrossAllBoards = list.reduce(function (n, b) { return n + (b.total || 0); }, 0);
1403
+ const shouldShow = hasMultipleBoards || totalAcrossAllBoards > 0;
1404
+ if (!shouldShow) {
1405
+ return h("div", {
1406
+ className: "hermes-kanban-boardswitcher-compact",
1407
+ title: tx(t, "boardSwitcherHint", "Boards let you separate unrelated streams of work"),
1408
+ },
1409
+ h(Button, {
1410
+ onClick: props.onNewClick,
1411
+ size: "sm",
1412
+ className: "h-7 text-xs",
1413
+ }, tx(t, "newBoard", "+ New board")),
1414
+ h(DocsLink, null),
1415
+ );
1416
+ }
1417
+
1418
+ return h("div", { className: "hermes-kanban-boardswitcher" },
1419
+ h("div", { className: "hermes-kanban-boardswitcher-inner" },
1420
+ h("div", { className: "flex flex-col gap-0.5" },
1421
+ h("div", { className: "text-[11px] uppercase tracking-wider text-muted-foreground" },
1422
+ tx(t, "board", "Board")),
1423
+ h("div", { className: "flex items-center gap-2" },
1424
+ h(Select, Object.assign({
1425
+ value: props.board,
1426
+ className: "h-8 min-w-[220px]",
1427
+ "aria-label": "Switch kanban board",
1428
+ title: "Boards are independent work streams. Each board has its own tasks, tenants, and assignees.",
1429
+ }, selectChangeHandler(function (v) { if (v) props.onSwitch(v); })),
1430
+ list.map(function (b) {
1431
+ const label = b.total > 0
1432
+ ? `${b.name || b.slug} · ${b.total}`
1433
+ : (b.name || b.slug);
1434
+ return h(SelectOption, { key: b.slug, value: b.slug }, label);
1435
+ }),
1436
+ ),
1437
+ h("span", { className: "text-xs text-muted-foreground" },
1438
+ `${currentTotal || 0} task${currentTotal === 1 ? "" : "s"}`),
1439
+ ),
1440
+ ),
1441
+ h("div", { className: "flex-1" }),
1442
+ h(DocsLink, null),
1443
+ h(Button, {
1444
+ onClick: props.onNewClick,
1445
+ size: "sm",
1446
+ className: "h-8",
1447
+ title: "Create a new board. Useful when you want an unrelated work stream (different project, different team, isolated scratch area).",
1448
+ }, tx(t, "newBoard", "+ New board")),
1449
+ props.board !== "default"
1450
+ ? h(Button, {
1451
+ onClick: function () {
1452
+ const msg = tx(t, "archiveBoardConfirm",
1453
+ "Archive board '{name}'? It will be moved to boards/_archived/ so you can recover it later. Tasks on this board will no longer appear anywhere in the UI.",
1454
+ { name: currentName });
1455
+ if (window.confirm(msg)) props.onDeleteBoard(props.board);
1456
+ },
1457
+ size: "sm",
1458
+ className: "h-8",
1459
+ title: tx(t, "archiveBoardTitle", "Archive this board"),
1460
+ }, tx(t, "archive", "Archive"))
1461
+ : null,
1462
+ ),
1463
+ );
1464
+ }
1465
+
1466
+ function NewBoardDialog(props) {
1467
+ const { t } = useI18n();
1468
+ const [slug, setSlug] = useState("");
1469
+ const [name, setName] = useState("");
1470
+ const [description, setDescription] = useState("");
1471
+ const [icon, setIcon] = useState("");
1472
+ const [switchTo, setSwitchTo] = useState(true);
1473
+ const [submitting, setSubmitting] = useState(false);
1474
+ const [err, setErr] = useState(null);
1475
+
1476
+ // Auto-derive a name from the slug if the user hasn't typed one.
1477
+ const autoName = useMemo(function () {
1478
+ if (!slug) return "";
1479
+ return slug.replace(/[-_]+/g, " ")
1480
+ .split(" ")
1481
+ .filter(Boolean)
1482
+ .map(function (w) { return w[0].toUpperCase() + w.slice(1); })
1483
+ .join(" ");
1484
+ }, [slug]);
1485
+
1486
+ function onSubmit(ev) {
1487
+ if (ev) ev.preventDefault();
1488
+ if (!slug.trim()) { setErr("slug is required"); return; }
1489
+ setSubmitting(true);
1490
+ setErr(null);
1491
+ props.onCreate({
1492
+ slug: slug.trim(),
1493
+ name: name.trim() || autoName || undefined,
1494
+ description: description.trim() || undefined,
1495
+ icon: icon.trim() || undefined,
1496
+ switch: switchTo,
1497
+ }).catch(function (e) {
1498
+ setErr(String(e && e.message ? e.message : e));
1499
+ setSubmitting(false);
1500
+ });
1501
+ }
1502
+
1503
+ return h("div", {
1504
+ className: "hermes-kanban-dialog-backdrop",
1505
+ onClick: function (e) { if (e.target === e.currentTarget) props.onCancel(); },
1506
+ },
1507
+ h("form", {
1508
+ className: "hermes-kanban-dialog",
1509
+ onSubmit: onSubmit,
1510
+ },
1511
+ h("div", { className: "hermes-kanban-dialog-title" },
1512
+ tx(t, "newBoardTitle", "New board")),
1513
+ h("div", { className: "text-xs text-muted-foreground mb-2" },
1514
+ tx(t, "newBoardDescription",
1515
+ "Boards let you separate unrelated streams of work — one per project, repo, or domain. Workers on one board never see another board's tasks.")),
1516
+ h("div", { className: "flex flex-col gap-3" },
1517
+ h("div", { className: "flex flex-col gap-1" },
1518
+ h(Label, { className: "text-xs" }, tx(t, "slug", "Slug"), " ",
1519
+ h("span", { className: "text-muted-foreground" },
1520
+ tx(t, "slugHint", "— lowercase, hyphens, e.g. atm10-server"))),
1521
+ h(Input, {
1522
+ value: slug,
1523
+ onChange: function (e) { setSlug(e.target.value.toLowerCase().replace(/[^a-z0-9\-_]/g, "-")); },
1524
+ placeholder: "atm10-server",
1525
+ autoFocus: true,
1526
+ className: "h-8",
1527
+ }),
1528
+ ),
1529
+ h("div", { className: "flex flex-col gap-1" },
1530
+ h(Label, { className: "text-xs" }, tx(t, "displayName", "Display name"), " ",
1531
+ h("span", { className: "text-muted-foreground" },
1532
+ tx(t, "displayNameHint", "(optional)"))),
1533
+ h(Input, {
1534
+ value: name,
1535
+ onChange: function (e) { setName(e.target.value); },
1536
+ placeholder: autoName || tx(t, "displayName", "Display name"),
1537
+ className: "h-8",
1538
+ }),
1539
+ ),
1540
+ h("div", { className: "flex flex-col gap-1" },
1541
+ h(Label, { className: "text-xs" }, tx(t, "description", "Description"), " ",
1542
+ h("span", { className: "text-muted-foreground" },
1543
+ tx(t, "descriptionHint", "(optional)"))),
1544
+ h(Input, {
1545
+ value: description,
1546
+ onChange: function (e) { setDescription(e.target.value); },
1547
+ placeholder: "What goes on this board?",
1548
+ className: "h-8",
1549
+ }),
1550
+ ),
1551
+ h("div", { className: "flex flex-col gap-1" },
1552
+ h(Label, { className: "text-xs" }, tx(t, "icon", "Icon"), " ",
1553
+ h("span", { className: "text-muted-foreground" },
1554
+ tx(t, "iconHint", "(single character or emoji)"))),
1555
+ h(Input, {
1556
+ value: icon,
1557
+ onChange: function (e) { setIcon(e.target.value.slice(0, 4)); },
1558
+ placeholder: "📦",
1559
+ className: "h-8 w-24",
1560
+ }),
1561
+ ),
1562
+ h("label", { className: "flex items-center gap-2 text-xs" },
1563
+ h("input", {
1564
+ type: "checkbox",
1565
+ checked: switchTo,
1566
+ onChange: function (e) { setSwitchTo(e.target.checked); },
1567
+ }),
1568
+ tx(t, "switchAfterCreate", "Switch to this board after creating it"),
1569
+ ),
1570
+ ),
1571
+ err ? h("div", { className: "text-xs text-destructive mt-2" }, err) : null,
1572
+ h("div", { className: "hermes-kanban-dialog-actions" },
1573
+ h(Button, {
1574
+ type: "button",
1575
+ onClick: props.onCancel,
1576
+ size: "sm",
1577
+ disabled: submitting,
1578
+ }, tx(t, "cancel", "Cancel")),
1579
+ h(Button, {
1580
+ type: "submit",
1581
+ size: "sm",
1582
+ disabled: submitting || !slug.trim(),
1583
+ }, submitting ? tx(t, "creating", "Creating…") : tx(t, "createBoard", "Create board")),
1584
+ ),
1585
+ ),
1586
+ );
1587
+ }
1588
+
1589
+ // -------------------------------------------------------------------------
1590
+ // Toolbar
1591
+ // -------------------------------------------------------------------------
1592
+
1593
+ function BoardToolbar(props) {
1594
+ const { t } = useI18n();
1595
+ const tenants = (props.board && props.board.tenants) || [];
1596
+ const assignees = (props.board && props.board.assignees) || [];
1597
+ return h("div", { className: "flex flex-wrap items-end gap-3" },
1598
+ h("div", { className: "flex flex-col gap-1",
1599
+ title: "Fuzzy-match tasks by id, title, or description. Matches across all columns." },
1600
+ h(Label, { className: "text-xs text-muted-foreground" }, tx(t, "search", "Search")),
1601
+ h(Input, {
1602
+ placeholder: tx(t, "filterCards", "Filter cards…"),
1603
+ value: props.search,
1604
+ onChange: function (e) { props.setSearch(e.target.value); },
1605
+ className: "w-56 h-8",
1606
+ }),
1607
+ ),
1608
+ h("div", { className: "flex flex-col gap-1",
1609
+ title: "Tenants are free-form tags on a task (e.g. customer, project, team). Set them via the task drawer or kanban_create." },
1610
+ h(Label, { className: "text-xs text-muted-foreground" }, tx(t, "tenant", "Tenant")),
1611
+ h(Select, Object.assign({
1612
+ value: props.tenantFilter,
1613
+ className: "h-8",
1614
+ }, selectChangeHandler(props.setTenantFilter)),
1615
+ h(SelectOption, { value: "" }, tx(t, "allTenants", "All tenants")),
1616
+ tenants.map(function (tn) {
1617
+ return h(SelectOption, { key: tn, value: tn }, tn);
1618
+ }),
1619
+ ),
1620
+ ),
1621
+ h("div", { className: "flex flex-col gap-1",
1622
+ title: "Filter by assigned Hermes profile. Profiles are the named agent identities that claim and work on tasks." },
1623
+ h(Label, { className: "text-xs text-muted-foreground" }, tx(t, "assignee", "Assignee")),
1624
+ h(Select, Object.assign({
1625
+ value: props.assigneeFilter,
1626
+ className: "h-8",
1627
+ }, selectChangeHandler(props.setAssigneeFilter)),
1628
+ h(SelectOption, { value: "" }, tx(t, "allProfiles", "All profiles")),
1629
+ assignees.map(function (a) {
1630
+ return h(SelectOption, { key: a, value: a }, a);
1631
+ }),
1632
+ ),
1633
+ ),
1634
+ h("label", { className: "flex items-center gap-2 text-xs",
1635
+ title: "Include archived tasks in the board view. Archived tasks are hidden by default." },
1636
+ h("input", {
1637
+ type: "checkbox",
1638
+ checked: props.includeArchived,
1639
+ onChange: function (e) { props.setIncludeArchived(e.target.checked); },
1640
+ }),
1641
+ tx(t, "showArchived", "Show archived"),
1642
+ ),
1643
+ h("label", { className: "flex items-center gap-2 text-xs",
1644
+ title: "Group the Running column by assigned profile" },
1645
+ h("input", {
1646
+ type: "checkbox",
1647
+ checked: props.laneByProfile,
1648
+ onChange: function (e) { props.setLaneByProfile(e.target.checked); },
1649
+ }),
1650
+ tx(t, "lanesByProfile", "Lanes by profile"),
1651
+ ),
1652
+ h("div", { className: "flex-1" }),
1653
+ h(Button, {
1654
+ onClick: props.onNudgeDispatch,
1655
+ size: "sm",
1656
+ title: "Wake the dispatcher to claim ready tasks now instead of waiting for the next tick. Use this after adding tasks if you want them picked up immediately.",
1657
+ }, tx(t, "nudgeDispatcher", "Nudge dispatcher")),
1658
+ h(Button, {
1659
+ onClick: props.onRefresh,
1660
+ size: "sm",
1661
+ title: "Reload the board from the database. The board auto-refreshes on task events; this is for forcing a re-read.",
1662
+ }, tx(t, "refresh", "Refresh")),
1663
+ h(Button, {
1664
+ onClick: function () {
1665
+ props.setSearch("");
1666
+ props.setTenantFilter("");
1667
+ props.setAssigneeFilter("");
1668
+ props.setIncludeArchived(false);
1669
+ },
1670
+ size: "sm",
1671
+ title: "Clear all active filters (search, tenant, assignee, archived).",
1672
+ }, tx(t, "clearFilters", "Clear filters")),
1673
+ );
1674
+ }
1675
+
1676
+ // -------------------------------------------------------------------------
1677
+ // Bulk action bar (appears when >= 1 card is selected)
1678
+ // -------------------------------------------------------------------------
1679
+
1680
+ function BulkActionBar(props) {
1681
+ const { t } = useI18n();
1682
+ const [assignee, setAssignee] = useState("");
1683
+ const [reclaimFirst, setReclaimFirst] = useState(false);
1684
+ const [priority, setPriority] = useState("");
1685
+ return h("div", { className: "hermes-kanban-bulk" },
1686
+ h("span", { className: "hermes-kanban-bulk-count" },
1687
+ `${props.count} ${tx(t, "selected", "selected")}`),
1688
+ h(Button, {
1689
+ onClick: function () { props.onApply({ status: "todo" }); },
1690
+ size: "sm",
1691
+ title: "Move selected tasks to Todo.",
1692
+ }, "→ todo"),
1693
+ h(Button, {
1694
+ onClick: function () { props.onApply({ status: "ready" }); },
1695
+ size: "sm",
1696
+ title: "Move selected tasks to Ready. Ready tasks are picked up by the dispatcher on the next tick.",
1697
+ }, "→ ready"),
1698
+ h(Button, {
1699
+ onClick: function () { props.onApply({ status: "blocked" },
1700
+ `Block ${props.count} task(s)?`); },
1701
+ size: "sm",
1702
+ title: "Block selected tasks. Releases any active claims.",
1703
+ }, "Block"),
1704
+ h(Button, {
1705
+ onClick: function () { props.onApply({ status: "ready" },
1706
+ `Unblock ${props.count} task(s)?`); },
1707
+ size: "sm",
1708
+ title: "Unblock selected tasks (promote to Ready).",
1709
+ }, "Unblock"),
1710
+ h(Button, {
1711
+ onClick: function () {
1712
+ props.onApply({ status: "done" },
1713
+ tx(t, "markDone", "Mark {n} task(s) as done?", { n: props.count }));
1714
+ },
1715
+ size: "sm",
1716
+ title: "Mark selected tasks as done. Releases any claims and unblocks dependent children. You'll be asked for a completion summary.",
1717
+ }, tx(t, "complete", "Complete")),
1718
+ h(Button, {
1719
+ onClick: function () {
1720
+ props.onApply({ archive: true },
1721
+ tx(t, "markArchived", "Archive {n} task(s)?", { n: props.count }));
1722
+ },
1723
+ size: "sm",
1724
+ title: "Archive selected tasks. They disappear from the default board view but remain in the database.",
1725
+ }, tx(t, "archive", "Archive")),
1726
+ h("div", { className: "hermes-kanban-bulk-priority",
1727
+ title: "Set priority on selected tasks. Higher = claimed first." },
1728
+ h(Input, {
1729
+ type: "number",
1730
+ value: priority,
1731
+ onChange: function (e) { setPriority(e.target.value); },
1732
+ placeholder: tx(t, "priority", "pri"),
1733
+ className: "h-7 text-xs w-16",
1734
+ }),
1735
+ h(Button, {
1736
+ onClick: function () {
1737
+ if (priority === "") return;
1738
+ props.onApply({ priority: Number(priority) });
1739
+ setPriority("");
1740
+ },
1741
+ disabled: priority === "",
1742
+ size: "sm",
1743
+ }, tx(t, "setPriority", "Set priority")),
1744
+ ),
1745
+ h("div", { className: "hermes-kanban-bulk-reassign",
1746
+ title: "Reassign selected tasks to a different Hermes profile. Pick a profile (or unassign) and click Apply." },
1747
+ h(Select, {
1748
+ value: assignee,
1749
+ onChange: function (e) { setAssignee(e.target.value); },
1750
+ className: "h-7 text-xs",
1751
+ },
1752
+ h(SelectOption, { value: "" }, "— reassign —"),
1753
+ h(SelectOption, { value: "__none__" }, "(unassign)"),
1754
+ props.assignees.map(function (a) {
1755
+ return h(SelectOption, { key: a, value: a }, a);
1756
+ }),
1757
+ ),
1758
+ h(Button, {
1759
+ onClick: function () {
1760
+ if (!assignee) return;
1761
+ props.onApply({ assignee: assignee === "__none__" ? "" : assignee, reclaim_first: reclaimFirst });
1762
+ setAssignee("");
1763
+ },
1764
+ disabled: !assignee,
1765
+ size: "sm",
1766
+ title: "Apply the selected assignee to all selected tasks.",
1767
+ }, tx(t, "apply", "Apply")),
1768
+ ),
1769
+ h("label", { className: "hermes-kanban-bulk-reclaim-first", title: "Reclaim any active claims before reassigning" },
1770
+ h("input", {
1771
+ type: "checkbox",
1772
+ checked: reclaimFirst,
1773
+ onChange: function (e) { setReclaimFirst(e.target.checked); },
1774
+ }),
1775
+ "Reclaim first",
1776
+ ),
1777
+ h("div", { className: "flex-1" }),
1778
+ h(Button, {
1779
+ onClick: props.onSelectAllVisible,
1780
+ size: "sm",
1781
+ title: "Select all visible cards across columns.",
1782
+ }, "Select all visible"),
1783
+ h(Button, {
1784
+ onClick: props.onClear,
1785
+ size: "sm",
1786
+ title: "Deselect all tasks and hide this bar.",
1787
+ }, tx(t, "clear", "Clear")),
1788
+ );
1789
+ }
1790
+
1791
+ // -------------------------------------------------------------------------
1792
+ // Columns
1793
+ // -------------------------------------------------------------------------
1794
+
1795
+ function BoardColumns(props) {
1796
+ const handleDragStart = useCallback(function (e) {
1797
+ const card = e.target.closest && e.target.closest(".hermes-kanban-card");
1798
+ if (!card) return;
1799
+ const taskId = card.getAttribute("data-task-id");
1800
+ if (taskId && props.onDragStart) props.onDragStart(taskId);
1801
+ }, [props.onDragStart]);
1802
+ const handleDragEnd = useCallback(function () {
1803
+ if (props.onDragEnd) props.onDragEnd();
1804
+ }, [props.onDragEnd]);
1805
+ return h("div", { className: "hermes-kanban-columns", onDragStart: handleDragStart, onDragEnd: handleDragEnd },
1806
+ props.board.columns.map(function (col) {
1807
+ return h(Column, {
1808
+ key: col.name,
1809
+ column: col,
1810
+ laneByProfile: props.laneByProfile,
1811
+ selectedIds: props.selectedIds,
1812
+ failedIds: props.failedIds,
1813
+ draggingTaskId: props.draggingTaskId,
1814
+ toggleSelected: props.toggleSelected,
1815
+ toggleRange: props.toggleRange,
1816
+ selectAllInColumn: props.selectAllInColumn,
1817
+ onMove: props.onMove,
1818
+ onMoveSelected: props.onMoveSelected,
1819
+ onOpen: props.onOpen,
1820
+ onCreate: props.onCreate,
1821
+ allTasks: props.allTasks,
1822
+ });
1823
+ }),
1824
+ );
1825
+ }
1826
+
1827
+ function Column(props) {
1828
+ const { t } = useI18n();
1829
+ const [dragOver, setDragOver] = useState(false);
1830
+ const [showCreate, setShowCreate] = useState(false);
1831
+ const colRef = useRef(null);
1832
+
1833
+ // Listen for our synthetic touch-drop events from attachTouchDrag().
1834
+ useEffect(function () {
1835
+ if (!colRef.current) return undefined;
1836
+ const el = colRef.current;
1837
+ function onTouchDrop(e) {
1838
+ if (e.detail && e.detail.status === props.column.name) {
1839
+ const taskId = e.detail.taskId;
1840
+ if (props.selectedIds && props.selectedIds.has(taskId) && props.selectedIds.size > 1 && props.onMoveSelected) {
1841
+ props.onMoveSelected(props.column.name);
1842
+ } else {
1843
+ props.onMove(taskId, props.column.name);
1844
+ }
1845
+ }
1846
+ }
1847
+ el.addEventListener("hermes-kanban:drop", onTouchDrop);
1848
+ return function () { el.removeEventListener("hermes-kanban:drop", onTouchDrop); };
1849
+ }, [props.column.name, props.onMove, props.selectedIds, props.onMoveSelected]);
1850
+
1851
+ const handleDragOver = function (e) {
1852
+ e.preventDefault();
1853
+ e.dataTransfer.dropEffect = "move";
1854
+ if (!dragOver) setDragOver(true);
1855
+ };
1856
+ const handleDragLeave = function () { setDragOver(false); };
1857
+ const handleDrop = function (e) {
1858
+ e.preventDefault();
1859
+ setDragOver(false);
1860
+ const taskId = e.dataTransfer.getData(MIME_TASK);
1861
+ if (!taskId) return;
1862
+ if (props.selectedIds && props.selectedIds.has(taskId) && props.selectedIds.size > 1) {
1863
+ if (props.onMoveSelected) props.onMoveSelected(props.column.name);
1864
+ } else {
1865
+ props.onMove(taskId, props.column.name);
1866
+ }
1867
+ };
1868
+
1869
+ const lanes = useMemo(function () {
1870
+ if (!props.laneByProfile || props.column.name !== "running") return null;
1871
+ const byProfile = {};
1872
+ for (const tk of props.column.tasks) {
1873
+ const key = tk.assignee || "(unassigned)";
1874
+ (byProfile[key] = byProfile[key] || []).push(tk);
1875
+ }
1876
+ return Object.keys(byProfile).sort().map(function (k) {
1877
+ return { assignee: k, tasks: byProfile[k] };
1878
+ });
1879
+ }, [props.column, props.laneByProfile]);
1880
+
1881
+ const colHelp = getColumnHelp(t, props.column.name);
1882
+ const colLabel = getColumnLabel(t, props.column.name);
1883
+
1884
+ return h("div", {
1885
+ ref: colRef,
1886
+ "data-kanban-column": props.column.name,
1887
+ className: cn(
1888
+ "hermes-kanban-column",
1889
+ dragOver ? "hermes-kanban-column--drop" : "",
1890
+ ),
1891
+ onDragOver: handleDragOver,
1892
+ onDragLeave: handleDragLeave,
1893
+ onDrop: handleDrop,
1894
+ },
1895
+ h("div", { className: "hermes-kanban-column-header",
1896
+ title: colHelp || "" },
1897
+ h("input", {
1898
+ type: "checkbox",
1899
+ className: "hermes-kanban-col-check",
1900
+ title: "Select all tasks in this column",
1901
+ "aria-label": `Select all tasks in ${colLabel || props.column.name}`,
1902
+ checked: props.column.tasks.length > 0 && props.column.tasks.every(function (t) { return props.selectedIds.has(t.id); }),
1903
+ onChange: function (e) {
1904
+ e.stopPropagation();
1905
+ if (props.selectAllInColumn) props.selectAllInColumn(props.column.name);
1906
+ },
1907
+ onClick: function (e) { e.stopPropagation(); },
1908
+ }),
1909
+ h("span", { className: cn("hermes-kanban-dot", COLUMN_DOT[props.column.name]) }),
1910
+ h("span", { className: "hermes-kanban-column-label" },
1911
+ colLabel || props.column.name),
1912
+ h("span", { className: "hermes-kanban-column-count",
1913
+ title: `${props.column.tasks.length} task${props.column.tasks.length === 1 ? "" : "s"} in this column` },
1914
+ props.column.tasks.length),
1915
+ h("button", {
1916
+ type: "button",
1917
+ className: "hermes-kanban-column-add",
1918
+ title: tx(t, "createTask", "Create task in this column"),
1919
+ onClick: function () { setShowCreate(function (v) { return !v; }); },
1920
+ }, showCreate ? "×" : "+"),
1921
+ ),
1922
+ h("div", { className: "hermes-kanban-column-sub" },
1923
+ colHelp || ""),
1924
+ showCreate ? h(InlineCreate, {
1925
+ columnName: props.column.name,
1926
+ allTasks: props.allTasks,
1927
+ onSubmit: function (body) {
1928
+ props.onCreate(body).then(function () { setShowCreate(false); });
1929
+ },
1930
+ onCancel: function () { setShowCreate(false); },
1931
+ }) : null,
1932
+ h("div", { className: "hermes-kanban-column-body" },
1933
+ props.column.tasks.length === 0
1934
+ ? h("div", { className: "hermes-kanban-empty" }, tx(t, "noTasks", "— no tasks —"))
1935
+ : lanes
1936
+ ? lanes.map(function (lane) {
1937
+ return h("div", { key: lane.assignee, className: "hermes-kanban-lane" },
1938
+ h("div", { className: "hermes-kanban-lane-head" },
1939
+ h("span", { className: "hermes-kanban-lane-name" }, lane.assignee),
1940
+ h("span", { className: "hermes-kanban-lane-count" }, lane.tasks.length),
1941
+ ),
1942
+ lane.tasks.map(function (tk) {
1943
+ return h(TaskCard, {
1944
+ key: tk.id, task: tk,
1945
+ selected: props.selectedIds.has(tk.id),
1946
+ failed: props.failedIds && props.failedIds.has(tk.id),
1947
+ draggingTaskId: props.draggingTaskId,
1948
+ draggingSource: props.draggingTaskId && props.selectedIds.has(props.draggingTaskId) && props.selectedIds.size > 1 && props.selectedIds.has(tk.id),
1949
+ toggleSelected: props.toggleSelected,
1950
+ toggleRange: props.toggleRange,
1951
+ onOpen: props.onOpen,
1952
+ });
1953
+ }),
1954
+ );
1955
+ })
1956
+ : props.column.tasks.map(function (tk) {
1957
+ return h(TaskCard, {
1958
+ key: tk.id, task: tk,
1959
+ selected: props.selectedIds.has(tk.id),
1960
+ failed: props.failedIds && props.failedIds.has(tk.id),
1961
+ draggingTaskId: props.draggingTaskId,
1962
+ draggingSource: props.draggingTaskId && props.selectedIds.has(props.draggingTaskId) && props.selectedIds.size > 1 && props.selectedIds.has(tk.id),
1963
+ toggleSelected: props.toggleSelected,
1964
+ toggleRange: props.toggleRange,
1965
+ onOpen: props.onOpen,
1966
+ });
1967
+ }),
1968
+ ),
1969
+ );
1970
+ }
1971
+
1972
+ // -------------------------------------------------------------------------
1973
+ // Card
1974
+ // -------------------------------------------------------------------------
1975
+
1976
+ // Staleness tiers — amber after a grace window, red when clearly stuck.
1977
+ // Values below are seconds.
1978
+ const STALENESS = {
1979
+ ready: { amber: 1 * 60 * 60, red: 24 * 60 * 60 },
1980
+ running: { amber: 10 * 60, red: 60 * 60 },
1981
+ blocked: { amber: 1 * 60 * 60, red: 24 * 60 * 60 },
1982
+ todo: { amber: 7 * 24 * 60 * 60, red: 30 * 24 * 60 * 60 },
1983
+ };
1984
+
1985
+ function stalenessClass(task) {
1986
+ if (!task || !task.age) return "";
1987
+ const age = task.status === "running"
1988
+ ? task.age.started_age_seconds
1989
+ : task.age.created_age_seconds;
1990
+ const tier = STALENESS[task.status];
1991
+ if (!tier || age == null) return "";
1992
+ if (age >= tier.red) return "hermes-kanban-card--stale-red";
1993
+ if (age >= tier.amber) return "hermes-kanban-card--stale-amber";
1994
+ return "";
1995
+ }
1996
+
1997
+ function TaskCard(props) {
1998
+ const { t: i18n } = useI18n();
1999
+ const t = props.task;
2000
+ const cardRef = useRef(null);
2001
+
2002
+ useEffect(function () {
2003
+ return attachTouchDrag(cardRef.current, t.id);
2004
+ }, [t.id]);
2005
+
2006
+ const handleDragStart = function (e) {
2007
+ e.dataTransfer.setData(MIME_TASK, t.id);
2008
+ e.dataTransfer.effectAllowed = "move";
2009
+ const selectedCards = document.querySelectorAll(".hermes-kanban-card--selected");
2010
+ if (selectedCards.length > 1 && props.selected) {
2011
+ const ghost = document.createElement("div");
2012
+ ghost.className = "hermes-kanban-drag-ghost";
2013
+ ghost.textContent = selectedCards.length + " cards";
2014
+ document.body.appendChild(ghost);
2015
+ e.dataTransfer.setDragImage(ghost, 0, 0);
2016
+ requestAnimationFrame(function () {
2017
+ if (ghost.parentNode) document.body.removeChild(ghost);
2018
+ });
2019
+ }
2020
+ };
2021
+ const handleClick = function (e) {
2022
+ if (e.shiftKey) {
2023
+ e.preventDefault();
2024
+ e.stopPropagation();
2025
+ if (props.toggleRange) props.toggleRange(t.id);
2026
+ return;
2027
+ }
2028
+ if (e.ctrlKey || e.metaKey) {
2029
+ e.preventDefault();
2030
+ e.stopPropagation();
2031
+ props.toggleSelected(t.id, true);
2032
+ return;
2033
+ }
2034
+ props.onOpen(t.id);
2035
+ };
2036
+ const handleKeyDown = function (e) {
2037
+ if (e.key === "Enter" || e.key === " ") {
2038
+ e.preventDefault();
2039
+ props.onOpen(t.id);
2040
+ }
2041
+ if (e.key === "Escape") {
2042
+ if (props.toggleSelected) props.toggleSelected(t.id, false);
2043
+ }
2044
+ };
2045
+ const handleCheckbox = function (e) {
2046
+ e.stopPropagation();
2047
+ props.toggleSelected(t.id, true);
2048
+ };
2049
+
2050
+ const progress = t.progress;
2051
+ const needsAssignee = t.status === "ready" && !t.assignee;
2052
+
2053
+ return h("div", {
2054
+ ref: cardRef,
2055
+ "data-task-id": t.id,
2056
+ className: cn(
2057
+ "hermes-kanban-card",
2058
+ props.selected ? "hermes-kanban-card--selected" : "",
2059
+ props.failed ? "hermes-kanban-card--failed" : "",
2060
+ props.draggingSource ? "hermes-kanban-card--dragging-source" : "",
2061
+ stalenessClass(t),
2062
+ ),
2063
+ draggable: true,
2064
+ tabIndex: 0,
2065
+ role: "button",
2066
+ "aria-label": `${t.title || "untitled"} — ${t.id} — ${t.status}`,
2067
+ onDragStart: handleDragStart,
2068
+ onClick: handleClick,
2069
+ onKeyDown: handleKeyDown,
2070
+ },
2071
+ h(Card, null,
2072
+ h(CardContent, { className: "hermes-kanban-card-content" },
2073
+ h("div", { className: "hermes-kanban-card-row" },
2074
+ h("label", {
2075
+ className: "hermes-kanban-card-check-wrap",
2076
+ title: tx(i18n, "selectForBulk", "Select for bulk actions"),
2077
+ onClick: function (e) { e.stopPropagation(); },
2078
+ },
2079
+ h("input", {
2080
+ type: "checkbox",
2081
+ className: "hermes-kanban-card-check",
2082
+ checked: props.selected,
2083
+ onChange: handleCheckbox,
2084
+ onClick: function (e) { e.stopPropagation(); },
2085
+ "aria-label": `Select task ${t.id}`,
2086
+ }),
2087
+ ),
2088
+ h("span", { className: "hermes-kanban-card-id",
2089
+ title: `Task id: ${t.id}. Use this id with kanban_show, /kanban show, or hermes kanban show.` }, t.id),
2090
+ t.warnings && t.warnings.count > 0
2091
+ ? h("span", {
2092
+ className: cn(
2093
+ "hermes-kanban-warning-badge",
2094
+ "hermes-kanban-warning-badge--" + (t.warnings.highest_severity || "warning"),
2095
+ ),
2096
+ title: (
2097
+ `${t.warnings.count} active diagnostic` +
2098
+ (t.warnings.count === 1 ? "" : "s") +
2099
+ ` (severity: ${t.warnings.highest_severity || "warning"}). ` +
2100
+ `Click to open for details.`
2101
+ ),
2102
+ }, t.warnings.highest_severity === "critical" ? "!!!" :
2103
+ t.warnings.highest_severity === "error" ? "!!" : "⚠")
2104
+ : null,
2105
+ t.priority > 0
2106
+ ? h(Badge, { className: "hermes-kanban-priority",
2107
+ title: `Priority ${t.priority}. Higher-priority tasks are claimed first by the dispatcher.` }, `P${t.priority}`)
2108
+ : null,
2109
+ t.tenant
2110
+ ? h(Badge, { variant: "outline", className: "hermes-kanban-tag",
2111
+ title: `Tenant: ${t.tenant}. Free-form tag for grouping tasks (customer, project, team).` }, t.tenant)
2112
+ : null,
2113
+ progress
2114
+ ? h("span", {
2115
+ className: cn(
2116
+ "hermes-kanban-progress",
2117
+ progress.done === progress.total ? "hermes-kanban-progress--full" : "",
2118
+ ),
2119
+ title: `${progress.done} of ${progress.total} child tasks done`,
2120
+ }, `${progress.done}/${progress.total}`)
2121
+ : null,
2122
+ needsAssignee
2123
+ ? h(Badge, {
2124
+ variant: "outline",
2125
+ className: "hermes-kanban-needs-assignee",
2126
+ title: tx(i18n, "needsAssigneeHint", "Dependencies are satisfied, but the dispatcher skips this task until you assign a profile."),
2127
+ }, tx(i18n, "needsAssignee", "Needs assignee"))
2128
+ : null,
2129
+ ),
2130
+ h("div", { className: "hermes-kanban-card-title" },
2131
+ t.title || tx(i18n, "untitled", "(untitled)")),
2132
+ h("div", { className: "hermes-kanban-card-row hermes-kanban-card-meta" },
2133
+ t.assignee
2134
+ ? h("span", { className: "hermes-kanban-assignee",
2135
+ title: `Assigned to Hermes profile @${t.assignee}` }, "@", t.assignee)
2136
+ : h("span", { className: "hermes-kanban-unassigned",
2137
+ title: needsAssignee
2138
+ ? tx(i18n, "needsAssigneeHint", "Dependencies are satisfied, but the dispatcher skips this task until you assign a profile.")
2139
+ : "No profile assigned." },
2140
+ tx(i18n, "unassigned", "unassigned")),
2141
+ t.comment_count > 0
2142
+ ? h("span", { className: "hermes-kanban-count",
2143
+ title: `${t.comment_count} comment${t.comment_count === 1 ? "" : "s"} on this task` }, "💬 ", t.comment_count)
2144
+ : null,
2145
+ t.link_counts && (t.link_counts.parents + t.link_counts.children) > 0
2146
+ ? h("span", { className: "hermes-kanban-count",
2147
+ title: `${t.link_counts.parents} parent${t.link_counts.parents === 1 ? "" : "s"}, ${t.link_counts.children} child${t.link_counts.children === 1 ? "" : "ren"}. Children stay blocked until their parent is done.` },
2148
+ "↔ ", t.link_counts.parents + t.link_counts.children)
2149
+ : null,
2150
+ h("span", { className: "hermes-kanban-ago",
2151
+ title: t.created_at ? `Created ${t.created_at}` : "" },
2152
+ timeAgo ? timeAgo(t.created_at) : ""),
2153
+ ),
2154
+ ),
2155
+ ),
2156
+ );
2157
+ }
2158
+
2159
+ // -------------------------------------------------------------------------
2160
+ // Inline create (with parent selector)
2161
+ // -------------------------------------------------------------------------
2162
+
2163
+ function InlineCreate(props) {
2164
+ const { t } = useI18n();
2165
+ const [title, setTitle] = useState("");
2166
+ const [assignee, setAssignee] = useState("");
2167
+ const [priority, setPriority] = useState(0);
2168
+ const [parent, setParent] = useState("");
2169
+ const [skills, setSkills] = useState("");
2170
+ // Workspace controls. `scratch` (default) ignores path; `worktree` optionally
2171
+ // takes a path (dispatcher derives one from the assignee profile otherwise);
2172
+ // `dir` requires a path. Backend enforces the rule — we only hide/show the
2173
+ // input here to save vertical space in the common `scratch` case.
2174
+ const [workspaceKind, setWorkspaceKind] = useState("scratch");
2175
+ const [workspacePath, setWorkspacePath] = useState("");
2176
+
2177
+ const submit = function () {
2178
+ const trimmed = title.trim();
2179
+ if (!trimmed) return;
2180
+ const body = {
2181
+ title: trimmed,
2182
+ assignee: assignee.trim() || null,
2183
+ priority: Number(priority) || 0,
2184
+ triage: props.columnName === "triage",
2185
+ };
2186
+ if (parent) body.parents = [parent];
2187
+ // Parse comma-separated skills into a clean list. Blank = no
2188
+ // extras (omit key so backend leaves it null). The dispatcher
2189
+ // always auto-loads kanban-worker; these are extras on top.
2190
+ const skillList = skills
2191
+ .split(",")
2192
+ .map(function (s) { return s.trim(); })
2193
+ .filter(function (s) { return s.length > 0; });
2194
+ if (skillList.length > 0) body.skills = skillList;
2195
+ // Only send workspace_kind when it's non-default. Keeps the request
2196
+ // shape small and interoperable with older dispatcher versions.
2197
+ if (workspaceKind && workspaceKind !== "scratch") {
2198
+ body.workspace_kind = workspaceKind;
2199
+ }
2200
+ const wpTrim = workspacePath.trim();
2201
+ if (wpTrim) body.workspace_path = wpTrim;
2202
+ props.onSubmit(body);
2203
+ setTitle(""); setAssignee(""); setPriority(0); setParent(""); setSkills("");
2204
+ setWorkspaceKind("scratch"); setWorkspacePath("");
2205
+ };
2206
+
2207
+ const showPathInput = workspaceKind !== "scratch";
2208
+ const pathPlaceholder = workspaceKind === "dir"
2209
+ ? tx(t, "workspacePathDir", "workspace path (required, e.g. ~/projects/my-app)")
2210
+ : tx(t, "workspacePathOptional",
2211
+ "workspace path (optional, derived from assignee if blank)");
2212
+
2213
+ return h("div", { className: "hermes-kanban-inline-create" },
2214
+ h("textarea", {
2215
+ value: title,
2216
+ onChange: function (e) { setTitle(e.target.value); },
2217
+ onKeyDown: function (e) {
2218
+ if (e.key === "Enter" && !e.shiftKey) { e.preventDefault(); submit(); }
2219
+ if (e.key === "Escape") props.onCancel();
2220
+ },
2221
+ placeholder: props.columnName === "triage"
2222
+ ? tx(t, "triagePlaceholder", "Rough idea — AI will spec it…")
2223
+ : tx(t, "taskTitlePlaceholder", "New task title…"),
2224
+ autoFocus: true,
2225
+ className: "text-sm min-h-[2rem] max-h-32 resize-y w-full border border-input bg-transparent px-2 py-1 rounded-md focus:outline-none focus:ring-2 focus:ring-ring",
2226
+ rows: 2,
2227
+ }),
2228
+ h("div", { className: "flex gap-2" },
2229
+ h(Input, {
2230
+ value: assignee,
2231
+ onChange: function (e) { setAssignee(e.target.value); },
2232
+ placeholder: props.columnName === "triage"
2233
+ ? tx(t, "specifier", "specifier")
2234
+ : tx(t, "assigneePlaceholder", "assignee"),
2235
+ className: "h-7 text-xs flex-1",
2236
+ title: props.columnName === "triage"
2237
+ ? "Hermes profile that will spec this task (default: the dispatcher's configured specifier). Leave blank to let the dispatcher pick."
2238
+ : "Hermes profile to assign. Leave blank and the dispatcher will pick from available profiles when the task is Ready.",
2239
+ style: { textTransform: "none" },
2240
+ autoCapitalize: "none",
2241
+ autoCorrect: "off",
2242
+ spellCheck: false,
2243
+ }),
2244
+ h(Input, {
2245
+ type: "number",
2246
+ value: priority,
2247
+ onChange: function (e) { setPriority(e.target.value); },
2248
+ placeholder: "pri",
2249
+ className: "h-7 text-xs w-16",
2250
+ title: "Priority. Higher-priority tasks are claimed first by the dispatcher. 0 = default.",
2251
+ }),
2252
+ ),
2253
+ h(Input, {
2254
+ value: skills,
2255
+ onChange: function (e) { setSkills(e.target.value); },
2256
+ placeholder: tx(t, "skillsPlaceholder",
2257
+ "skills (optional, comma-separated): translation, github-code-review"),
2258
+ title: "Force-load these skills into the worker (in addition to the built-in kanban-worker).",
2259
+ className: "h-7 text-xs",
2260
+ }),
2261
+ h("div", { className: "flex gap-2" },
2262
+ h(Select, {
2263
+ value: workspaceKind,
2264
+ onChange: function (e) { setWorkspaceKind(e.target.value); },
2265
+ title: "scratch: isolated temp dir (default). worktree: git worktree on the assignee profile. dir: exact path (required below).",
2266
+ className: "h-7 text-xs w-28",
2267
+ },
2268
+ h(SelectOption, { value: "scratch" }, "scratch"),
2269
+ h(SelectOption, { value: "worktree" }, "worktree"),
2270
+ h(SelectOption, { value: "dir" }, "dir"),
2271
+ ),
2272
+ showPathInput ? h(Input, {
2273
+ value: workspacePath,
2274
+ onChange: function (e) { setWorkspacePath(e.target.value); },
2275
+ placeholder: pathPlaceholder,
2276
+ className: "h-7 text-xs flex-1",
2277
+ }) : null,
2278
+ ),
2279
+ h(Select, {
2280
+ value: parent,
2281
+ onChange: function (e) { setParent(e.target.value); },
2282
+ className: "h-7 text-xs",
2283
+ title: "Optional parent task. A child stays blocked in its current column until the parent is marked done.",
2284
+ },
2285
+ h(SelectOption, { value: "" }, tx(t, "noParent", "— no parent —")),
2286
+ (props.allTasks || []).map(function (task) {
2287
+ return h(SelectOption, { key: task.id, value: task.id },
2288
+ `${task.id} — ${(task.title || "").slice(0, 50)}`);
2289
+ }),
2290
+ ),
2291
+ h("div", { className: "flex gap-2" },
2292
+ h(Button, {
2293
+ onClick: submit,
2294
+ size: "sm",
2295
+ }, "Create"),
2296
+ h(Button, {
2297
+ onClick: props.onCancel,
2298
+ size: "sm",
2299
+ }, tx(t, "cancel", "Cancel")),
2300
+ ),
2301
+ );
2302
+ }
2303
+
2304
+ // -------------------------------------------------------------------------
2305
+ // Task drawer
2306
+ // -------------------------------------------------------------------------
2307
+
2308
+ function TaskDrawer(props) {
2309
+ const { t } = useI18n();
2310
+ const [data, setData] = useState(null);
2311
+ const [loading, setLoading] = useState(true);
2312
+ const [err, setErr] = useState(null);
2313
+ const [newComment, setNewComment] = useState("");
2314
+ const [editing, setEditing] = useState(false);
2315
+ // Home-channel notification toggles. homeChannels is the list of platforms
2316
+ // the user has a /sethome on; each entry has a `subscribed` bool telling
2317
+ // us whether this task is currently subscribed via that platform's home.
2318
+ const [homeChannels, setHomeChannels] = useState([]);
2319
+ const [homeBusy, setHomeBusy] = useState({});
2320
+ const boardSlug = props.boardSlug;
2321
+
2322
+ const load = useCallback(function () {
2323
+ return SDK.fetchJSON(withBoard(`${API}/tasks/${encodeURIComponent(props.taskId)}`, boardSlug))
2324
+ .then(function (d) { setData(d); setErr(null); })
2325
+ .catch(function (e) { setErr(String(e.message || e)); })
2326
+ .finally(function () { setLoading(false); });
2327
+ }, [props.taskId, boardSlug]);
2328
+
2329
+ const loadHomeChannels = useCallback(function () {
2330
+ const qs = new URLSearchParams({ task_id: props.taskId });
2331
+ const url = withBoard(`${API}/home-channels?${qs}`, boardSlug);
2332
+ return SDK.fetchJSON(url)
2333
+ .then(function (d) { setHomeChannels(d.home_channels || []); })
2334
+ .catch(function () { /* silent — endpoint optional on older gateways */ });
2335
+ }, [props.taskId, boardSlug]);
2336
+
2337
+ // Reload when the WS stream reports new events for this task id
2338
+ // (completion, block, crash, etc. — anything that'd make the drawer
2339
+ // show stale data if we only loaded on mount).
2340
+ useEffect(function () { load(); }, [load, props.eventTick]);
2341
+ useEffect(function () { loadHomeChannels(); }, [loadHomeChannels]);
2342
+ useEffect(function () {
2343
+ function onKey(e) { if (e.key === "Escape" && !editing) props.onClose(); }
2344
+ window.addEventListener("keydown", onKey);
2345
+ return function () { window.removeEventListener("keydown", onKey); };
2346
+ }, [props.onClose, editing]);
2347
+
2348
+ const handleComment = function () {
2349
+ const body = newComment.trim();
2350
+ if (!body) return;
2351
+ SDK.fetchJSON(withBoard(`${API}/tasks/${encodeURIComponent(props.taskId)}/comments`, boardSlug), {
2352
+ method: "POST",
2353
+ headers: { "Content-Type": "application/json" },
2354
+ body: JSON.stringify({ body }),
2355
+ }).then(function () {
2356
+ setNewComment("");
2357
+ load();
2358
+ props.onRefresh();
2359
+ }).catch(function (e) { setErr(String(e.message || e)); });
2360
+ };
2361
+
2362
+ const doPatch = function (patch, opts) {
2363
+ if (opts && opts.confirm && !window.confirm(opts.confirm)) {
2364
+ return Promise.resolve();
2365
+ }
2366
+ const finalPatch = withCompletionSummary(patch, 1);
2367
+ if (!finalPatch) return Promise.resolve();
2368
+ return SDK.fetchJSON(withBoard(`${API}/tasks/${encodeURIComponent(props.taskId)}`, boardSlug), {
2369
+ method: "PATCH",
2370
+ headers: { "Content-Type": "application/json" },
2371
+ body: JSON.stringify(finalPatch),
2372
+ }).then(function () { load(); props.onRefresh(); });
2373
+ };
2374
+
2375
+ // Triage specifier — calls the auxiliary LLM to flesh out a rough
2376
+ // idea in the Triage column into a concrete spec (title + body with
2377
+ // goal, approach, acceptance criteria) and promotes it to todo.
2378
+ // Not a PATCH: runs through a dedicated POST endpoint because the
2379
+ // LLM call can take tens of seconds, and its outcome is richer than
2380
+ // a status flip (may update title AND body AND emit an audit
2381
+ // comment — or fail with a human-readable reason that the UI
2382
+ // surfaces inline without treating it as an HTTP error).
2383
+ const doSpecify = function () {
2384
+ return SDK.fetchJSON(
2385
+ withBoard(`${API}/tasks/${encodeURIComponent(props.taskId)}/specify`, boardSlug),
2386
+ {
2387
+ method: "POST",
2388
+ headers: { "Content-Type": "application/json" },
2389
+ body: JSON.stringify({}),
2390
+ }
2391
+ ).then(function (res) {
2392
+ load();
2393
+ props.onRefresh();
2394
+ return res;
2395
+ });
2396
+ };
2397
+
2398
+ const addLink = function (parentId) {
2399
+ return SDK.fetchJSON(withBoard(`${API}/links`, boardSlug), {
2400
+ method: "POST",
2401
+ headers: { "Content-Type": "application/json" },
2402
+ body: JSON.stringify({ parent_id: parentId, child_id: props.taskId }),
2403
+ }).then(function () { load(); props.onRefresh(); })
2404
+ .catch(function (e) { setErr(String(e.message || e)); });
2405
+ };
2406
+ const removeLink = function (parentId) {
2407
+ const qs = new URLSearchParams({ parent_id: parentId, child_id: props.taskId });
2408
+ return SDK.fetchJSON(withBoard(`${API}/links?${qs}`, boardSlug), { method: "DELETE" })
2409
+ .then(function () { load(); props.onRefresh(); })
2410
+ .catch(function (e) { setErr(String(e.message || e)); });
2411
+ };
2412
+ const addChild = function (childId) {
2413
+ return SDK.fetchJSON(withBoard(`${API}/links`, boardSlug), {
2414
+ method: "POST",
2415
+ headers: { "Content-Type": "application/json" },
2416
+ body: JSON.stringify({ parent_id: props.taskId, child_id: childId }),
2417
+ }).then(function () { load(); props.onRefresh(); })
2418
+ .catch(function (e) { setErr(String(e.message || e)); });
2419
+ };
2420
+ const removeChild = function (childId) {
2421
+ const qs = new URLSearchParams({ parent_id: props.taskId, child_id: childId });
2422
+ return SDK.fetchJSON(withBoard(`${API}/links?${qs}`, boardSlug), { method: "DELETE" })
2423
+ .then(function () { load(); props.onRefresh(); })
2424
+ .catch(function (e) { setErr(String(e.message || e)); });
2425
+ };
2426
+
2427
+ const toggleHomeSubscription = function (platform, currentlySubscribed) {
2428
+ // Optimistic flip + busy flag to keep double-clicks idempotent.
2429
+ setHomeBusy(function (b) { return Object.assign({}, b, { [platform]: true }); });
2430
+ setHomeChannels(function (list) {
2431
+ return list.map(function (h) {
2432
+ return h.platform === platform
2433
+ ? Object.assign({}, h, { subscribed: !currentlySubscribed })
2434
+ : h;
2435
+ });
2436
+ });
2437
+ const method = currentlySubscribed ? "DELETE" : "POST";
2438
+ const url = withBoard(
2439
+ `${API}/tasks/${encodeURIComponent(props.taskId)}/home-subscribe/${encodeURIComponent(platform)}`,
2440
+ boardSlug,
2441
+ );
2442
+ return SDK.fetchJSON(url, { method: method })
2443
+ .then(function () { return loadHomeChannels(); })
2444
+ .catch(function (e) {
2445
+ // Revert optimistic flip on failure.
2446
+ setHomeChannels(function (list) {
2447
+ return list.map(function (h) {
2448
+ return h.platform === platform
2449
+ ? Object.assign({}, h, { subscribed: currentlySubscribed })
2450
+ : h;
2451
+ });
2452
+ });
2453
+ setErr(String(e.message || e));
2454
+ })
2455
+ .finally(function () {
2456
+ setHomeBusy(function (b) {
2457
+ const next = Object.assign({}, b);
2458
+ delete next[platform];
2459
+ return next;
2460
+ });
2461
+ });
2462
+ };
2463
+
2464
+ return h("div", { className: "hermes-kanban-drawer-shade", onClick: props.onClose },
2465
+ h("div", {
2466
+ className: "hermes-kanban-drawer",
2467
+ onClick: function (e) { e.stopPropagation(); },
2468
+ },
2469
+ h("div", { className: "hermes-kanban-drawer-head" },
2470
+ h("span", { className: "text-xs text-muted-foreground" }, props.taskId),
2471
+ h("button", {
2472
+ type: "button",
2473
+ onClick: props.onClose,
2474
+ className: "hermes-kanban-drawer-close",
2475
+ title: tx(t, "close", "Close (Esc)"),
2476
+ }, "×"),
2477
+ ),
2478
+ loading ? h("div", { className: "p-4 text-sm text-muted-foreground" },
2479
+ tx(t, "loadingDetail", "Loading…")) :
2480
+ err ? h("div", { className: "p-4 text-sm text-destructive" }, err) :
2481
+ data ? h(TaskDetail, {
2482
+ data, editing, setEditing,
2483
+ renderMarkdown: props.renderMarkdown,
2484
+ allTasks: props.allTasks,
2485
+ assignees: props.assignees || [],
2486
+ boardSlug: boardSlug,
2487
+ onPatch: doPatch,
2488
+ onSpecify: doSpecify,
2489
+ onAddParent: addLink,
2490
+ onRemoveParent: removeLink,
2491
+ onAddChild: addChild,
2492
+ onRemoveChild: removeChild,
2493
+ homeChannels: homeChannels,
2494
+ homeBusy: homeBusy,
2495
+ onToggleHomeSub: toggleHomeSubscription,
2496
+ onRefresh: props.onRefresh,
2497
+ }) : null,
2498
+ data ? h("div", { className: "hermes-kanban-drawer-comment-row" },
2499
+ h(Input, {
2500
+ value: newComment,
2501
+ onChange: function (e) { setNewComment(e.target.value); },
2502
+ onKeyDown: function (e) {
2503
+ if (e.key === "Enter" && !e.shiftKey) {
2504
+ e.preventDefault(); handleComment();
2505
+ }
2506
+ },
2507
+ placeholder: tx(t, "addComment", "Add a comment… (Enter to submit)"),
2508
+ className: "h-8 text-sm flex-1",
2509
+ }),
2510
+ h(Button, {
2511
+ onClick: handleComment,
2512
+ size: "sm",
2513
+ }, tx(t, "comment", "Comment")),
2514
+ ) : null,
2515
+ ),
2516
+ );
2517
+ }
2518
+
2519
+ function TaskDetail(props) {
2520
+ const { t: i18n } = useI18n();
2521
+ const t = props.data.task;
2522
+ const comments = props.data.comments || [];
2523
+ const events = props.data.events || [];
2524
+ const links = props.data.links || { parents: [], children: [] };
2525
+
2526
+ return h("div", { className: "hermes-kanban-drawer-body" },
2527
+ h("div", { className: "hermes-kanban-drawer-title" },
2528
+ h("span", { className: cn("hermes-kanban-dot", COLUMN_DOT[t.status]) }),
2529
+ props.editing
2530
+ ? h(TitleEditor, {
2531
+ initial: t.title || "",
2532
+ onSave: function (newTitle) {
2533
+ return props.onPatch({ title: newTitle }).then(function () { props.setEditing(false); });
2534
+ },
2535
+ onCancel: function () { props.setEditing(false); },
2536
+ })
2537
+ : h("span", {
2538
+ className: "hermes-kanban-drawer-title-text",
2539
+ title: tx(i18n, "clickToEdit", "Click to edit"),
2540
+ onClick: function () { props.setEditing(true); },
2541
+ }, t.title || tx(i18n, "untitled", "(untitled)")),
2542
+ ),
2543
+ h("div", { className: "hermes-kanban-drawer-meta" },
2544
+ h(MetaRow, { label: tx(i18n, "status", "Status"), value: t.status }),
2545
+ h(AssigneeEditor, { task: t, onPatch: props.onPatch }),
2546
+ h(PriorityEditor, { task: t, onPatch: props.onPatch }),
2547
+ t.tenant ? h(MetaRow, { label: tx(i18n, "tenant", "Tenant"), value: t.tenant }) : null,
2548
+ h(MetaRow, {
2549
+ label: tx(i18n, "workspace", "Workspace"),
2550
+ value: `${t.workspace_kind}${t.workspace_path ? ": " + t.workspace_path : ""}`,
2551
+ }),
2552
+ (t.skills && t.skills.length > 0) ? h(MetaRow, {
2553
+ label: tx(i18n, "skills", "Skills"),
2554
+ value: t.skills.join(", "),
2555
+ }) : null,
2556
+ t.created_by ? h(MetaRow, { label: tx(i18n, "createdBy", "Created by"), value: t.created_by }) : null,
2557
+ ),
2558
+ h(StatusActions, {
2559
+ task: t,
2560
+ onPatch: props.onPatch,
2561
+ onSpecify: props.onSpecify,
2562
+ }),
2563
+ h(DiagnosticsSection, {
2564
+ task: t,
2565
+ boardSlug: props.boardSlug,
2566
+ assignees: props.assignees,
2567
+ diagnostics: t.diagnostics || [],
2568
+ onRefresh: props.onRefresh,
2569
+ }),
2570
+ h(HomeSubsSection, {
2571
+ homeChannels: props.homeChannels || [],
2572
+ homeBusy: props.homeBusy || {},
2573
+ onToggle: props.onToggleHomeSub,
2574
+ }),
2575
+ h(BodyEditor, {
2576
+ task: t,
2577
+ renderMarkdown: props.renderMarkdown,
2578
+ onPatch: props.onPatch,
2579
+ }),
2580
+ h(DependencyEditor, {
2581
+ task: t,
2582
+ links, allTasks: props.allTasks,
2583
+ onAddParent: props.onAddParent,
2584
+ onRemoveParent: props.onRemoveParent,
2585
+ onAddChild: props.onAddChild,
2586
+ onRemoveChild: props.onRemoveChild,
2587
+ }),
2588
+ t.result ? h("div", { className: "hermes-kanban-section" },
2589
+ h("div", { className: "hermes-kanban-section-head" }, tx(i18n, "result", "Result")),
2590
+ h(MarkdownBlock, { source: t.result, enabled: props.renderMarkdown }),
2591
+ ) : null,
2592
+ h("div", { className: "hermes-kanban-section" },
2593
+ h("div", { className: "hermes-kanban-section-head" },
2594
+ `${tx(i18n, "comments", "Comments")} (${comments.length})`),
2595
+ comments.length === 0
2596
+ ? h("div", { className: "text-xs text-muted-foreground" },
2597
+ tx(i18n, "noComments", "— no comments —"))
2598
+ : comments.map(function (c) {
2599
+ return h("div", { key: c.id, className: "hermes-kanban-comment" },
2600
+ h("div", { className: "hermes-kanban-comment-head" },
2601
+ h("span", { className: "hermes-kanban-comment-author" }, c.author || "anon"),
2602
+ h("span", { className: "hermes-kanban-comment-ago" },
2603
+ timeAgo ? timeAgo(c.created_at) : ""),
2604
+ ),
2605
+ h(MarkdownBlock, { source: c.body, enabled: props.renderMarkdown }),
2606
+ );
2607
+ }),
2608
+ ),
2609
+ h("div", { className: "hermes-kanban-section" },
2610
+ h("div", { className: "hermes-kanban-section-head" },
2611
+ `${tx(i18n, "events", "Events")} (${events.length})`),
2612
+ events.slice().reverse().slice(0, 20).map(function (e) {
2613
+ const isDiag = isDiagnosticEvent(e.kind);
2614
+ const phantoms = isDiag ? phantomIdsFromEvent(e) : [];
2615
+ return h("div", {
2616
+ key: e.id,
2617
+ className: cn(
2618
+ "hermes-kanban-event",
2619
+ isDiag ? "hermes-kanban-event--hallucination" : "",
2620
+ ),
2621
+ },
2622
+ isDiag
2623
+ ? h("div", { className: "hermes-kanban-event-header" },
2624
+ h("span", { className: "hermes-kanban-event-warning-icon" }, "⚠"),
2625
+ h("span", { className: "hermes-kanban-event-warning-label" },
2626
+ getDiagnosticEventLabel(i18n, e.kind) || e.kind),
2627
+ h("span", { className: "hermes-kanban-event-ago" },
2628
+ timeAgo ? timeAgo(e.created_at) : ""),
2629
+ )
2630
+ : h("div", { className: "hermes-kanban-event-header-plain" },
2631
+ h("span", { className: "hermes-kanban-event-kind" }, e.kind),
2632
+ h("span", { className: "hermes-kanban-event-ago" },
2633
+ timeAgo ? timeAgo(e.created_at) : ""),
2634
+ ),
2635
+ isDiag && phantoms.length > 0
2636
+ ? h("div", { className: "hermes-kanban-event-phantom-row" },
2637
+ h("span", { className: "hermes-kanban-event-phantom-label" },
2638
+ tx(i18n, "phantomIds", "Phantom ids:")),
2639
+ phantoms.map(function (pid) {
2640
+ return h("code", {
2641
+ key: pid,
2642
+ className: "hermes-kanban-event-phantom-chip",
2643
+ }, pid);
2644
+ }),
2645
+ )
2646
+ : null,
2647
+ e.payload && !isDiag
2648
+ ? h("code", { className: "hermes-kanban-event-payload" },
2649
+ JSON.stringify(e.payload))
2650
+ : null,
2651
+ );
2652
+ }),
2653
+ ),
2654
+ h(WorkerLogSection, { taskId: t.id, boardSlug: props.boardSlug }),
2655
+ h(RunHistorySection, { runs: props.data.runs || [] }),
2656
+ );
2657
+ }
2658
+
2659
+ // Per-attempt history. Closed runs first (most recent last), then the
2660
+ // active run if any. Each row shows profile / outcome / elapsed /
2661
+ // summary. Collapsed by default when there are more than three runs.
2662
+ function RunHistorySection(props) {
2663
+ const { t } = useI18n();
2664
+ const runs = props.runs || [];
2665
+ const [expanded, setExpanded] = useState(false);
2666
+ if (runs.length === 0) return null;
2667
+ const showAll = expanded || runs.length <= 3;
2668
+ const visible = showAll ? runs : runs.slice(-3);
2669
+
2670
+ const fmtElapsed = function (run) {
2671
+ if (!run || !run.started_at) return "";
2672
+ const end = run.ended_at || Math.floor(Date.now() / 1000);
2673
+ const secs = Math.max(0, end - run.started_at);
2674
+ if (secs < 60) return `${secs}s`;
2675
+ if (secs < 3600) return `${Math.round(secs / 60)}m`;
2676
+ return `${(secs / 3600).toFixed(1)}h`;
2677
+ };
2678
+
2679
+ return h("div", { className: "hermes-kanban-section" },
2680
+ h("div", { className: "hermes-kanban-section-head-row" },
2681
+ h("span", { className: "hermes-kanban-section-head" },
2682
+ `${tx(t, "runHistory", "Run history")} (${runs.length})`),
2683
+ !showAll
2684
+ ? h("button", {
2685
+ type: "button",
2686
+ onClick: function () { setExpanded(true); },
2687
+ className: "hermes-kanban-edit-link",
2688
+ title: tx(t, "showAllAttempts", "Show all attempts"),
2689
+ }, `+${runs.length - 3} earlier`)
2690
+ : null,
2691
+ ),
2692
+ visible.map(function (r) {
2693
+ const outcomeClass = r.ended_at
2694
+ ? `hermes-kanban-run--${r.outcome || r.status || "ended"}`
2695
+ : "hermes-kanban-run--active";
2696
+ return h("div", { key: r.id, className: cn("hermes-kanban-run", outcomeClass) },
2697
+ h("div", { className: "hermes-kanban-run-head" },
2698
+ h("span", { className: "hermes-kanban-run-outcome" },
2699
+ r.ended_at ? (r.outcome || r.status || tx(t, "ended", "ended")) : tx(t, "active", "active")),
2700
+ h("span", { className: "hermes-kanban-run-profile" },
2701
+ r.profile ? `@${r.profile}` : tx(t, "noProfile", "(no profile)")),
2702
+ h("span", { className: "hermes-kanban-run-elapsed" }, fmtElapsed(r)),
2703
+ h("span", { className: "hermes-kanban-run-ago" },
2704
+ timeAgo ? timeAgo(r.started_at) : ""),
2705
+ ),
2706
+ r.summary
2707
+ ? h("div", { className: "hermes-kanban-run-summary" }, r.summary)
2708
+ : null,
2709
+ r.error
2710
+ ? h("div", { className: "hermes-kanban-run-error" }, r.error)
2711
+ : null,
2712
+ (r.metadata && Object.keys(r.metadata).length > 0)
2713
+ ? (function () {
2714
+ var json = JSON.stringify(r.metadata, null, 2);
2715
+ var collapsed = json.length > 300;
2716
+ return h("details", {
2717
+ className: "hermes-kanban-run-meta-block",
2718
+ open: !collapsed,
2719
+ },
2720
+ h("summary", { className: "hermes-kanban-run-meta-label" }, "Metadata"),
2721
+ h("code", { className: "hermes-kanban-run-meta" }, json),
2722
+ );
2723
+ })()
2724
+ : null,
2725
+ );
2726
+ }),
2727
+ );
2728
+ }
2729
+
2730
+ // Worker log: loads lazily (one GET on mount), refresh button, tail cap.
2731
+ function WorkerLogSection(props) {
2732
+ const { t } = useI18n();
2733
+ const [state, setState] = useState({ loading: false, data: null, err: null });
2734
+ const load = useCallback(function () {
2735
+ setState({ loading: true, data: null, err: null });
2736
+ SDK.fetchJSON(withBoard(`${API}/tasks/${encodeURIComponent(props.taskId)}/log?tail=100000`, props.boardSlug))
2737
+ .then(function (d) { setState({ loading: false, data: d, err: null }); })
2738
+ .catch(function (e) { setState({ loading: false, data: null, err: String(e.message || e) }); });
2739
+ }, [props.taskId, props.boardSlug]);
2740
+
2741
+ // Auto-load when the section mounts; the user opened the drawer so the
2742
+ // cost is one small HTTP round-trip.
2743
+ useEffect(function () { load(); }, [load]);
2744
+
2745
+ const data = state.data;
2746
+ let body;
2747
+ if (state.loading) {
2748
+ body = h("div", { className: "text-xs text-muted-foreground" },
2749
+ tx(t, "loadingLog", "Loading log…"));
2750
+ } else if (state.err) {
2751
+ body = h("div", { className: "text-xs text-destructive" }, state.err);
2752
+ } else if (!data || !data.exists) {
2753
+ body = h("div", { className: "text-xs text-muted-foreground italic" },
2754
+ tx(t, "noWorkerLog",
2755
+ "— no worker log yet (task hasn't spawned or log was rotated away) —"));
2756
+ } else {
2757
+ body = h("pre", { className: "hermes-kanban-pre hermes-kanban-log" },
2758
+ data.content || "(empty)");
2759
+ }
2760
+
2761
+ return h("div", { className: "hermes-kanban-section" },
2762
+ h("div", { className: "hermes-kanban-section-head-row" },
2763
+ h("span", { className: "hermes-kanban-section-head" },
2764
+ tx(t, "workerLog", "Worker log") + (data && data.size_bytes ? ` (${data.size_bytes} B)` : "")),
2765
+ h("button", {
2766
+ type: "button",
2767
+ onClick: load,
2768
+ className: "hermes-kanban-edit-link",
2769
+ title: "Refresh log",
2770
+ }, "refresh"),
2771
+ ),
2772
+ body,
2773
+ data && data.truncated
2774
+ ? h("div", { className: "text-xs text-muted-foreground" },
2775
+ tx(t, "logTruncated", "(showing last 100 KB — full log at "),
2776
+ data.path,
2777
+ tx(t, "logAt", ")"))
2778
+ : null,
2779
+ );
2780
+ }
2781
+
2782
+ function MetaRow(props) {
2783
+ return h("div", { className: "hermes-kanban-meta-row" },
2784
+ h("span", { className: "hermes-kanban-meta-label" }, props.label),
2785
+ h("span", { className: "hermes-kanban-meta-value" }, props.value),
2786
+ );
2787
+ }
2788
+
2789
+ function TitleEditor(props) {
2790
+ const { t } = useI18n();
2791
+ const [v, setV] = useState(props.initial);
2792
+ const save = function () {
2793
+ const trimmed = v.trim();
2794
+ if (!trimmed) return;
2795
+ props.onSave(trimmed);
2796
+ };
2797
+ return h("div", { className: "hermes-kanban-edit-row" },
2798
+ h(Input, {
2799
+ value: v, autoFocus: true,
2800
+ onChange: function (e) { setV(e.target.value); },
2801
+ onKeyDown: function (e) {
2802
+ if (e.key === "Enter") { e.preventDefault(); save(); }
2803
+ if (e.key === "Escape") props.onCancel();
2804
+ },
2805
+ className: "h-8 text-sm flex-1",
2806
+ }),
2807
+ h(Button, { onClick: save,
2808
+ size: "sm",
2809
+ }, tx(t, "save", "Save")),
2810
+ h(Button, { onClick: props.onCancel,
2811
+ size: "sm",
2812
+ }, tx(t, "cancel", "Cancel")),
2813
+ );
2814
+ }
2815
+
2816
+ function AssigneeEditor(props) {
2817
+ const { t } = useI18n();
2818
+ const [editing, setEditing] = useState(false);
2819
+ const [v, setV] = useState(props.task.assignee || "");
2820
+ useEffect(function () { setV(props.task.assignee || ""); }, [props.task.assignee]);
2821
+ if (!editing) {
2822
+ return h("div", { className: "hermes-kanban-meta-row" },
2823
+ h("span", { className: "hermes-kanban-meta-label" }, tx(t, "assignee", "Assignee")),
2824
+ h("span", {
2825
+ className: "hermes-kanban-meta-value hermes-kanban-editable",
2826
+ onClick: function () { setEditing(true); },
2827
+ title: tx(t, "clickToEditAssignee", "Click to edit assignee"),
2828
+ }, props.task.assignee || tx(t, "unassigned", "unassigned")),
2829
+ );
2830
+ }
2831
+ const save = function () {
2832
+ props.onPatch({ assignee: v.trim() || "" }).then(function () { setEditing(false); });
2833
+ };
2834
+ return h("div", { className: "hermes-kanban-meta-row" },
2835
+ h("span", { className: "hermes-kanban-meta-label" }, tx(t, "assignee", "Assignee")),
2836
+ h(Input, {
2837
+ value: v, autoFocus: true,
2838
+ onChange: function (e) { setV(e.target.value); },
2839
+ onKeyDown: function (e) {
2840
+ if (e.key === "Enter") { e.preventDefault(); save(); }
2841
+ if (e.key === "Escape") setEditing(false);
2842
+ },
2843
+ placeholder: tx(t, "emptyAssignee", "(empty = unassign)"),
2844
+ className: "h-7 text-xs flex-1",
2845
+ style: { textTransform: "none" },
2846
+ autoCapitalize: "none",
2847
+ autoCorrect: "off",
2848
+ spellCheck: false,
2849
+ }),
2850
+ );
2851
+ }
2852
+
2853
+ function PriorityEditor(props) {
2854
+ const { t } = useI18n();
2855
+ const [editing, setEditing] = useState(false);
2856
+ const [v, setV] = useState(String(props.task.priority || 0));
2857
+ useEffect(function () { setV(String(props.task.priority || 0)); }, [props.task.priority]);
2858
+ if (!editing) {
2859
+ return h("div", { className: "hermes-kanban-meta-row" },
2860
+ h("span", { className: "hermes-kanban-meta-label" }, tx(t, "priority", "Priority")),
2861
+ h("span", {
2862
+ className: "hermes-kanban-meta-value hermes-kanban-editable",
2863
+ onClick: function () { setEditing(true); },
2864
+ title: tx(t, "clickToEdit", "Click to edit"),
2865
+ }, String(props.task.priority)),
2866
+ );
2867
+ }
2868
+ const save = function () {
2869
+ props.onPatch({ priority: Number(v) || 0 }).then(function () { setEditing(false); });
2870
+ };
2871
+ return h("div", { className: "hermes-kanban-meta-row" },
2872
+ h("span", { className: "hermes-kanban-meta-label" }, tx(t, "priority", "Priority")),
2873
+ h(Input, {
2874
+ type: "number", value: v, autoFocus: true,
2875
+ onChange: function (e) { setV(e.target.value); },
2876
+ onKeyDown: function (e) {
2877
+ if (e.key === "Enter") { e.preventDefault(); save(); }
2878
+ if (e.key === "Escape") setEditing(false);
2879
+ },
2880
+ className: "h-7 text-xs w-20",
2881
+ }),
2882
+ );
2883
+ }
2884
+
2885
+ function BodyEditor(props) {
2886
+ const { t } = useI18n();
2887
+ const [editing, setEditing] = useState(false);
2888
+ const [v, setV] = useState(props.task.body || "");
2889
+ useEffect(function () { setV(props.task.body || ""); }, [props.task.body]);
2890
+ const save = function () {
2891
+ props.onPatch({ body: v }).then(function () { setEditing(false); });
2892
+ };
2893
+ return h("div", { className: "hermes-kanban-section" },
2894
+ h("div", { className: "hermes-kanban-section-head-row" },
2895
+ h("span", { className: "hermes-kanban-section-head" }, tx(t, "description", "Description")),
2896
+ editing
2897
+ ? h("div", { className: "flex gap-1" },
2898
+ h(Button, { onClick: save,
2899
+ size: "sm",
2900
+ }, tx(t, "save", "Save")),
2901
+ h(Button, { onClick: function () { setEditing(false); setV(props.task.body || ""); },
2902
+ size: "sm",
2903
+ }, tx(t, "cancel", "Cancel")),
2904
+ )
2905
+ : h("button", {
2906
+ type: "button",
2907
+ onClick: function () { setEditing(true); },
2908
+ className: "hermes-kanban-edit-link",
2909
+ title: "Edit description",
2910
+ }, tx(t, "edit", "edit")),
2911
+ ),
2912
+ editing
2913
+ ? h("textarea", {
2914
+ className: "hermes-kanban-textarea",
2915
+ value: v,
2916
+ rows: 8,
2917
+ onChange: function (e) { setV(e.target.value); },
2918
+ })
2919
+ : props.task.body
2920
+ ? h(MarkdownBlock, { source: props.task.body, enabled: props.renderMarkdown })
2921
+ : h("div", { className: "text-xs text-muted-foreground italic" },
2922
+ tx(t, "noDescription", "— no description —")),
2923
+ );
2924
+ }
2925
+
2926
+ function DependencyEditor(props) {
2927
+ const { t } = useI18n();
2928
+ const { task, links, allTasks } = props;
2929
+ const [newParent, setNewParent] = useState("");
2930
+ const [newChild, setNewChild] = useState("");
2931
+ // Filter out self + existing links when offering the "add" dropdown.
2932
+ const candidatesFor = function (excludeSet) {
2933
+ return (allTasks || []).filter(function (tk) {
2934
+ return tk.id !== task.id && !excludeSet.has(tk.id);
2935
+ });
2936
+ };
2937
+ const parentExclude = new Set([task.id, ...(links.parents || [])]);
2938
+ const childExclude = new Set([task.id, ...(links.children || [])]);
2939
+
2940
+ return h("div", { className: "hermes-kanban-section" },
2941
+ h("div", { className: "hermes-kanban-section-head" }, tx(t, "dependencies", "Dependencies")),
2942
+ h("div", { className: "hermes-kanban-deps-row" },
2943
+ h("span", { className: "hermes-kanban-deps-label" }, tx(t, "parents", "Parents:")),
2944
+ h("div", { className: "hermes-kanban-deps-chips" },
2945
+ (links.parents || []).length === 0
2946
+ ? h("span", { className: "hermes-kanban-deps-empty" }, tx(t, "none", "none"))
2947
+ : (links.parents || []).map(function (id) {
2948
+ return h("span", { key: id, className: "hermes-kanban-dep-chip" },
2949
+ id,
2950
+ h("button", {
2951
+ type: "button",
2952
+ className: "hermes-kanban-dep-chip-x",
2953
+ onClick: function () { props.onRemoveParent(id); },
2954
+ title: tx(t, "removeDependency", "Remove dependency"),
2955
+ }, "×"),
2956
+ );
2957
+ }),
2958
+ ),
2959
+ ),
2960
+ h("div", { className: "hermes-kanban-deps-row" },
2961
+ h(Select, Object.assign({
2962
+ value: newParent,
2963
+ className: "h-7 text-xs flex-1",
2964
+ }, selectChangeHandler(setNewParent)),
2965
+ h(SelectOption, { value: "" }, tx(t, "addParent", "— add parent —")),
2966
+ candidatesFor(parentExclude).map(function (tk) {
2967
+ return h(SelectOption, { key: tk.id, value: tk.id },
2968
+ `${tk.id} — ${(tk.title || "").slice(0, 50)}`);
2969
+ }),
2970
+ ),
2971
+ h(Button, {
2972
+ onClick: function () {
2973
+ if (!newParent) return;
2974
+ props.onAddParent(newParent).then(function () { setNewParent(""); });
2975
+ },
2976
+ disabled: !newParent,
2977
+ size: "sm",
2978
+ }, "+ parent"),
2979
+ ),
2980
+ h("div", { className: "hermes-kanban-deps-row" },
2981
+ h("span", { className: "hermes-kanban-deps-label" }, tx(t, "children", "Children:")),
2982
+ h("div", { className: "hermes-kanban-deps-chips" },
2983
+ (links.children || []).length === 0
2984
+ ? h("span", { className: "hermes-kanban-deps-empty" }, tx(t, "none", "none"))
2985
+ : (links.children || []).map(function (id) {
2986
+ return h("span", { key: id, className: "hermes-kanban-dep-chip" },
2987
+ id,
2988
+ h("button", {
2989
+ type: "button",
2990
+ className: "hermes-kanban-dep-chip-x",
2991
+ onClick: function () { props.onRemoveChild(id); },
2992
+ title: tx(t, "removeDependency", "Remove dependency"),
2993
+ }, "×"),
2994
+ );
2995
+ }),
2996
+ ),
2997
+ ),
2998
+ h("div", { className: "hermes-kanban-deps-row" },
2999
+ h(Select, Object.assign({
3000
+ value: newChild,
3001
+ className: "h-7 text-xs flex-1",
3002
+ }, selectChangeHandler(setNewChild)),
3003
+ h(SelectOption, { value: "" }, tx(t, "addChild", "— add child —")),
3004
+ candidatesFor(childExclude).map(function (tk) {
3005
+ return h(SelectOption, { key: tk.id, value: tk.id },
3006
+ `${tk.id} — ${(tk.title || "").slice(0, 50)}`);
3007
+ }),
3008
+ ),
3009
+ h(Button, {
3010
+ onClick: function () {
3011
+ if (!newChild) return;
3012
+ props.onAddChild(newChild).then(function () { setNewChild(""); });
3013
+ },
3014
+ disabled: !newChild,
3015
+ size: "sm",
3016
+ }, "+ child"),
3017
+ ),
3018
+ );
3019
+ }
3020
+
3021
+ function StatusActions(props) {
3022
+ const { t } = useI18n();
3023
+ const task = props.task;
3024
+ const [specifyBusy, setSpecifyBusy] = useState(false);
3025
+ const [specifyMsg, setSpecifyMsg] = useState(null);
3026
+ const b = function (label, patch, enabled, confirmMsg) {
3027
+ return h(Button, {
3028
+ onClick: function () { if (enabled !== false) props.onPatch(patch, { confirm: confirmMsg }); },
3029
+ disabled: enabled === false,
3030
+ size: "sm",
3031
+ }, label);
3032
+ };
3033
+
3034
+ // "Specify" appears only when the task is in the Triage column — the
3035
+ // one column where an auxiliary LLM pass is meaningful. Elsewhere
3036
+ // the backend would return ok:false with "not in triage" anyway,
3037
+ // so hiding the button keeps the action row uncluttered.
3038
+ const specifyButton = (task.status === "triage" && props.onSpecify)
3039
+ ? h(Button, {
3040
+ onClick: function () {
3041
+ if (specifyBusy) return;
3042
+ setSpecifyBusy(true);
3043
+ setSpecifyMsg(null);
3044
+ props.onSpecify().then(function (res) {
3045
+ if (res && res.ok) {
3046
+ const suffix = res.new_title
3047
+ ? ` — retitled: ${res.new_title}`
3048
+ : "";
3049
+ setSpecifyMsg({ ok: true, text: `Specified${suffix}` });
3050
+ } else {
3051
+ setSpecifyMsg({
3052
+ ok: false,
3053
+ text: "Specify failed: " + ((res && res.reason) || "unknown error"),
3054
+ });
3055
+ }
3056
+ }).catch(function (err) {
3057
+ setSpecifyMsg({
3058
+ ok: false,
3059
+ text: "Specify failed: " + (err.message || String(err)),
3060
+ });
3061
+ }).then(function () {
3062
+ setSpecifyBusy(false);
3063
+ });
3064
+ },
3065
+ disabled: specifyBusy,
3066
+ size: "sm",
3067
+ }, specifyBusy ? "Specifying…" : "✨ Specify")
3068
+ : null;
3069
+
3070
+ return h("div", null,
3071
+ h("div", { className: "hermes-kanban-actions" },
3072
+ specifyButton,
3073
+ b("→ triage", { status: "triage" }, task.status !== "triage"),
3074
+ b("→ ready", { status: "ready" }, task.status !== "ready"),
3075
+ // No direct → running button: /tasks/:id PATCH rejects status=running
3076
+ // with 400 (issue #19535). Tasks enter running only through the
3077
+ // dispatcher's claim_task path, which atomically creates the run row,
3078
+ // claim lock, and worker process metadata.
3079
+ b(tx(t, "block", "Block"), { status: "blocked" },
3080
+ task.status === "running" || task.status === "ready",
3081
+ getDestructiveConfirm(t, "blocked")),
3082
+ b(tx(t, "unblock", "Unblock"), { status: "ready" }, task.status === "blocked"),
3083
+ b(tx(t, "complete", "Complete"), { status: "done" },
3084
+ task.status === "running" || task.status === "ready" || task.status === "blocked",
3085
+ getDestructiveConfirm(t, "done")),
3086
+ b(tx(t, "archive", "Archive"), { status: "archived" }, task.status !== "archived",
3087
+ getDestructiveConfirm(t, "archived")),
3088
+ ),
3089
+ specifyMsg ? h("div", {
3090
+ className: specifyMsg.ok
3091
+ ? "hermes-kanban-msg-ok"
3092
+ : "hermes-kanban-msg-err",
3093
+ }, specifyMsg.text) : null,
3094
+ );
3095
+ }
3096
+
3097
+
3098
+ // One toggle per gateway platform the user has a home channel set on
3099
+ // (telegram, discord, slack, etc.). Toggling on creates a kanban_notify_subs
3100
+ // row routed to that platform's home; toggling off removes it. Nothing
3101
+ // renders when no platforms have a home configured — this section stays
3102
+ // invisible for users who haven't set one up.
3103
+ function HomeSubsSection(props) {
3104
+ const { t } = useI18n();
3105
+ const channels = props.homeChannels || [];
3106
+ if (channels.length === 0) return null;
3107
+ const busy = props.homeBusy || {};
3108
+ return h("div", { className: "hermes-kanban-section" },
3109
+ h("div", { className: "hermes-kanban-section-head" },
3110
+ tx(t, "notifyHomeChannels", "Notify home channels")),
3111
+ h("div", { className: "hermes-kanban-home-subs" },
3112
+ channels.map(function (hc) {
3113
+ const isBusy = !!busy[hc.platform];
3114
+ const label = hc.subscribed ? "✓ " + hc.platform : hc.platform;
3115
+ const target = `${hc.name} (${hc.chat_id}${hc.thread_id ? " / " + hc.thread_id : ""})`;
3116
+ const title = hc.subscribed
3117
+ ? `${tx(t, "sendingUpdates", "Sending updates to")} ${target}. Click to stop.`
3118
+ : `${tx(t, "sendNotifications", "Send completed / blocked / gave_up notifications to")} ${target}.`;
3119
+ return h(Button, {
3120
+ key: hc.platform,
3121
+ size: "sm",
3122
+ title: title,
3123
+ disabled: isBusy || !props.onToggle,
3124
+ onClick: function () {
3125
+ if (props.onToggle) props.onToggle(hc.platform, hc.subscribed);
3126
+ },
3127
+ className: hc.subscribed
3128
+ ? "hermes-kanban-home-sub hermes-kanban-home-sub--on"
3129
+ : "hermes-kanban-home-sub",
3130
+ }, label);
3131
+ })
3132
+ )
3133
+ );
3134
+ }
3135
+
3136
+ // -------------------------------------------------------------------------
3137
+ // Register
3138
+ // -------------------------------------------------------------------------
3139
+
3140
+ if (window.__HERMES_PLUGINS__ && typeof window.__HERMES_PLUGINS__.register === "function") {
3141
+ window.__HERMES_PLUGINS__.register("kanban", KanbanPage);
3142
+ }
3143
+ })();