octo-agent 0.11.2

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 (319) hide show
  1. checksums.yaml +7 -0
  2. data/.clacky/skills/commit/SKILL.md +423 -0
  3. data/.clacky/skills/gem-release/SKILL.md +199 -0
  4. data/.clacky/skills/gem-release/scripts/release.sh +304 -0
  5. data/.clacky/skills/oss-upload/SKILL.md +47 -0
  6. data/.octorules +106 -0
  7. data/.rspec +3 -0
  8. data/.rubocop.yml +8 -0
  9. data/CHANGELOG.md +76 -0
  10. data/CODE_OF_CONDUCT.md +132 -0
  11. data/CONTRIBUTING.md +92 -0
  12. data/Dockerfile +28 -0
  13. data/LICENSE.txt +22 -0
  14. data/POSITIONING.md +46 -0
  15. data/README.md +134 -0
  16. data/README_CN.md +134 -0
  17. data/Rakefile +34 -0
  18. data/benchmark/fixtures/sample_project/Gemfile +3 -0
  19. data/benchmark/fixtures/sample_project/lib/api_handler.rb +32 -0
  20. data/benchmark/fixtures/sample_project/lib/order_calculator.rb +23 -0
  21. data/benchmark/fixtures/sample_project/lib/user_renderer.rb +20 -0
  22. data/benchmark/fixtures/sample_project/spec/order_calculator_spec.rb +20 -0
  23. data/benchmark/results/EVALUATION_REPORT.md +165 -0
  24. data/benchmark/results/baseline_20260511_174424.json +128 -0
  25. data/benchmark/results/report_20260511_175256.json +271 -0
  26. data/benchmark/results/report_20260511_175444.json +271 -0
  27. data/benchmark/results/treatment_20260511_175103.json +130 -0
  28. data/benchmark/runner.rb +441 -0
  29. data/bin/octo +7 -0
  30. data/docs/agent-first-ui-design.md +77 -0
  31. data/docs/billing-system.md +318 -0
  32. data/docs/channel-architecture.md +235 -0
  33. data/docs/engineering-article.md +343 -0
  34. data/docs/session-skill-invocation.md +69 -0
  35. data/docs/time_machine_design.md +247 -0
  36. data/docs/ui2-architecture.md +124 -0
  37. data/homebrew/README.md +96 -0
  38. data/homebrew/openocto.rb +24 -0
  39. data/lib/octo/agent/hook_manager.rb +61 -0
  40. data/lib/octo/agent/llm_caller.rb +800 -0
  41. data/lib/octo/agent/memory_updater.rb +246 -0
  42. data/lib/octo/agent/message_compressor.rb +225 -0
  43. data/lib/octo/agent/message_compressor_helper.rb +869 -0
  44. data/lib/octo/agent/next_message_suggester.rb +215 -0
  45. data/lib/octo/agent/session_serializer.rb +685 -0
  46. data/lib/octo/agent/skill_auto_creator.rb +114 -0
  47. data/lib/octo/agent/skill_evolution.rb +61 -0
  48. data/lib/octo/agent/skill_manager.rb +466 -0
  49. data/lib/octo/agent/skill_reflector.rb +89 -0
  50. data/lib/octo/agent/system_prompt_builder.rb +101 -0
  51. data/lib/octo/agent/time_machine.rb +214 -0
  52. data/lib/octo/agent/tool_executor.rb +454 -0
  53. data/lib/octo/agent/tool_registry.rb +150 -0
  54. data/lib/octo/agent.rb +2180 -0
  55. data/lib/octo/agent_config.rb +989 -0
  56. data/lib/octo/agent_profile.rb +112 -0
  57. data/lib/octo/anthropic_stream_aggregator.rb +137 -0
  58. data/lib/octo/background_task_registry.rb +324 -0
  59. data/lib/octo/banner.rb +34 -0
  60. data/lib/octo/bedrock_stream_aggregator.rb +137 -0
  61. data/lib/octo/block_font.rb +331 -0
  62. data/lib/octo/cli.rb +968 -0
  63. data/lib/octo/client.rb +623 -0
  64. data/lib/octo/default_agents/SOUL.md +3 -0
  65. data/lib/octo/default_agents/USER.md +1 -0
  66. data/lib/octo/default_agents/base_prompt.md +66 -0
  67. data/lib/octo/default_agents/coding/profile.yml +2 -0
  68. data/lib/octo/default_agents/coding/system_prompt.md +67 -0
  69. data/lib/octo/default_agents/general/profile.yml +2 -0
  70. data/lib/octo/default_agents/general/system_prompt.md +16 -0
  71. data/lib/octo/default_parsers/doc_parser.rb +69 -0
  72. data/lib/octo/default_parsers/docx_parser.rb +188 -0
  73. data/lib/octo/default_parsers/pdf_parser.rb +120 -0
  74. data/lib/octo/default_parsers/pdf_parser_ocr.py +103 -0
  75. data/lib/octo/default_parsers/pdf_parser_plumber.py +62 -0
  76. data/lib/octo/default_parsers/pptx_parser.rb +140 -0
  77. data/lib/octo/default_parsers/xlsx_parser.rb +121 -0
  78. data/lib/octo/default_skills/browser-setup/SKILL.md +426 -0
  79. data/lib/octo/default_skills/channel-manager/SKILL.md +623 -0
  80. data/lib/octo/default_skills/channel-manager/dingtalk_setup.rb +191 -0
  81. data/lib/octo/default_skills/channel-manager/discord_setup.rb +199 -0
  82. data/lib/octo/default_skills/channel-manager/feishu_setup.rb +574 -0
  83. data/lib/octo/default_skills/channel-manager/import_lark_skills.rb +97 -0
  84. data/lib/octo/default_skills/channel-manager/install_feishu_skills.rb +105 -0
  85. data/lib/octo/default_skills/channel-manager/weixin_setup.rb +274 -0
  86. data/lib/octo/default_skills/code-explorer/SKILL.md +36 -0
  87. data/lib/octo/default_skills/cron-task-creator/SKILL.md +257 -0
  88. data/lib/octo/default_skills/cron-task-creator/evals/evals.json +38 -0
  89. data/lib/octo/default_skills/onboard/SKILL.md +578 -0
  90. data/lib/octo/default_skills/onboard/scripts/import_external_skills.rb +413 -0
  91. data/lib/octo/default_skills/onboard/scripts/install_builtin_skills.rb +97 -0
  92. data/lib/octo/default_skills/persist-memory/SKILL.md +59 -0
  93. data/lib/octo/default_skills/personal-website/SKILL.md +113 -0
  94. data/lib/octo/default_skills/personal-website/publish.rb +235 -0
  95. data/lib/octo/default_skills/product-help/SKILL.md +123 -0
  96. data/lib/octo/default_skills/product-help/docs/agent-config.md +74 -0
  97. data/lib/octo/default_skills/product-help/docs/best-practices.md +49 -0
  98. data/lib/octo/default_skills/product-help/docs/browser-tool.md +53 -0
  99. data/lib/octo/default_skills/product-help/docs/built-in-skills.md +43 -0
  100. data/lib/octo/default_skills/product-help/docs/cli-reference.md +82 -0
  101. data/lib/octo/default_skills/product-help/docs/create-your-first-skill.md +47 -0
  102. data/lib/octo/default_skills/product-help/docs/faq.md +98 -0
  103. data/lib/octo/default_skills/product-help/docs/how-to-use-a-skill.md +58 -0
  104. data/lib/octo/default_skills/product-help/docs/installation.md +59 -0
  105. data/lib/octo/default_skills/product-help/docs/memory-system.md +61 -0
  106. data/lib/octo/default_skills/product-help/docs/octorules.md +62 -0
  107. data/lib/octo/default_skills/product-help/docs/session-management.md +63 -0
  108. data/lib/octo/default_skills/product-help/docs/skill-basics.md +55 -0
  109. data/lib/octo/default_skills/product-help/docs/skill-frontmatter.md +61 -0
  110. data/lib/octo/default_skills/product-help/docs/web-server.md +49 -0
  111. data/lib/octo/default_skills/product-help/docs/what-is-octo.md +37 -0
  112. data/lib/octo/default_skills/product-help/docs/windows-installation.md +36 -0
  113. data/lib/octo/default_skills/product-help/docs/writing-tips.md +53 -0
  114. data/lib/octo/default_skills/recall-memory/SKILL.md +65 -0
  115. data/lib/octo/default_skills/skill-add/SKILL.md +59 -0
  116. data/lib/octo/default_skills/skill-add/scripts/install_from_zip.rb +295 -0
  117. data/lib/octo/default_skills/skill-creator/SKILL.md +602 -0
  118. data/lib/octo/default_skills/skill-creator/agents/analyzer.md +274 -0
  119. data/lib/octo/default_skills/skill-creator/agents/comparator.md +202 -0
  120. data/lib/octo/default_skills/skill-creator/agents/grader.md +223 -0
  121. data/lib/octo/default_skills/skill-creator/eval-viewer/generate_review.py +471 -0
  122. data/lib/octo/default_skills/skill-creator/eval-viewer/viewer.html +1325 -0
  123. data/lib/octo/default_skills/skill-creator/references/schemas.md +430 -0
  124. data/lib/octo/default_skills/skill-creator/scripts/__init__.py +0 -0
  125. data/lib/octo/default_skills/skill-creator/scripts/aggregate_benchmark.py +401 -0
  126. data/lib/octo/default_skills/skill-creator/scripts/generate_report.py +326 -0
  127. data/lib/octo/default_skills/skill-creator/scripts/improve_description.py +310 -0
  128. data/lib/octo/default_skills/skill-creator/scripts/quick_validate.py +103 -0
  129. data/lib/octo/default_skills/skill-creator/scripts/run_eval.py +317 -0
  130. data/lib/octo/default_skills/skill-creator/scripts/run_loop.py +331 -0
  131. data/lib/octo/default_skills/skill-creator/scripts/utils.py +47 -0
  132. data/lib/octo/default_skills/skill-creator/scripts/validate_skill_frontmatter.rb +143 -0
  133. data/lib/octo/idle_compression_timer.rb +115 -0
  134. data/lib/octo/json_ui_controller.rb +204 -0
  135. data/lib/octo/message_format/anthropic.rb +409 -0
  136. data/lib/octo/message_format/bedrock.rb +361 -0
  137. data/lib/octo/message_format/open_ai.rb +222 -0
  138. data/lib/octo/message_history.rb +373 -0
  139. data/lib/octo/openai_stream_aggregator.rb +130 -0
  140. data/lib/octo/plain_ui_controller.rb +166 -0
  141. data/lib/octo/providers.rb +534 -0
  142. data/lib/octo/server/browser_manager.rb +397 -0
  143. data/lib/octo/server/channel/adapters/base.rb +82 -0
  144. data/lib/octo/server/channel/adapters/dingtalk/adapter.rb +314 -0
  145. data/lib/octo/server/channel/adapters/dingtalk/api_client.rb +391 -0
  146. data/lib/octo/server/channel/adapters/dingtalk/stream_client.rb +203 -0
  147. data/lib/octo/server/channel/adapters/discord/adapter.rb +229 -0
  148. data/lib/octo/server/channel/adapters/discord/api_client.rb +107 -0
  149. data/lib/octo/server/channel/adapters/discord/gateway_client.rb +270 -0
  150. data/lib/octo/server/channel/adapters/feishu/adapter.rb +320 -0
  151. data/lib/octo/server/channel/adapters/feishu/bot.rb +478 -0
  152. data/lib/octo/server/channel/adapters/feishu/file_processor.rb +36 -0
  153. data/lib/octo/server/channel/adapters/feishu/message_parser.rb +129 -0
  154. data/lib/octo/server/channel/adapters/feishu/ws_client.rb +423 -0
  155. data/lib/octo/server/channel/adapters/telegram/adapter.rb +375 -0
  156. data/lib/octo/server/channel/adapters/telegram/api_client.rb +205 -0
  157. data/lib/octo/server/channel/adapters/wecom/adapter.rb +148 -0
  158. data/lib/octo/server/channel/adapters/wecom/media_downloader.rb +115 -0
  159. data/lib/octo/server/channel/adapters/wecom/ws_client.rb +395 -0
  160. data/lib/octo/server/channel/adapters/weixin/adapter.rb +692 -0
  161. data/lib/octo/server/channel/adapters/weixin/api_client.rb +402 -0
  162. data/lib/octo/server/channel/channel_config.rb +178 -0
  163. data/lib/octo/server/channel/channel_manager.rb +468 -0
  164. data/lib/octo/server/channel/channel_ui_controller.rb +224 -0
  165. data/lib/octo/server/channel.rb +33 -0
  166. data/lib/octo/server/discover.rb +77 -0
  167. data/lib/octo/server/epipe_safe_io.rb +105 -0
  168. data/lib/octo/server/http_server.rb +3554 -0
  169. data/lib/octo/server/scheduler.rb +317 -0
  170. data/lib/octo/server/server_master.rb +325 -0
  171. data/lib/octo/server/session_registry.rb +431 -0
  172. data/lib/octo/server/web_ui_controller.rb +487 -0
  173. data/lib/octo/session_manager.rb +385 -0
  174. data/lib/octo/skill.rb +466 -0
  175. data/lib/octo/skill_loader.rb +328 -0
  176. data/lib/octo/tools/base.rb +118 -0
  177. data/lib/octo/tools/browser.rb +625 -0
  178. data/lib/octo/tools/edit.rb +165 -0
  179. data/lib/octo/tools/file_reader.rb +549 -0
  180. data/lib/octo/tools/glob.rb +162 -0
  181. data/lib/octo/tools/grep.rb +356 -0
  182. data/lib/octo/tools/invoke_skill.rb +96 -0
  183. data/lib/octo/tools/list_tasks.rb +54 -0
  184. data/lib/octo/tools/redo_task.rb +41 -0
  185. data/lib/octo/tools/request_user_feedback.rb +84 -0
  186. data/lib/octo/tools/security.rb +333 -0
  187. data/lib/octo/tools/terminal/output_cleaner.rb +63 -0
  188. data/lib/octo/tools/terminal/persistent_session.rb +268 -0
  189. data/lib/octo/tools/terminal/safe_rm.sh +106 -0
  190. data/lib/octo/tools/terminal/session_manager.rb +213 -0
  191. data/lib/octo/tools/terminal.rb +1828 -0
  192. data/lib/octo/tools/todo_manager.rb +374 -0
  193. data/lib/octo/tools/trash_manager.rb +388 -0
  194. data/lib/octo/tools/undo_task.rb +35 -0
  195. data/lib/octo/tools/web_fetch.rb +242 -0
  196. data/lib/octo/tools/web_search.rb +260 -0
  197. data/lib/octo/tools/write.rb +77 -0
  198. data/lib/octo/ui2/block_font.rb +10 -0
  199. data/lib/octo/ui2/components/base_component.rb +163 -0
  200. data/lib/octo/ui2/components/command_suggestions.rb +290 -0
  201. data/lib/octo/ui2/components/common_component.rb +96 -0
  202. data/lib/octo/ui2/components/inline_input.rb +226 -0
  203. data/lib/octo/ui2/components/input_area.rb +1338 -0
  204. data/lib/octo/ui2/components/message_component.rb +99 -0
  205. data/lib/octo/ui2/components/modal_component.rb +419 -0
  206. data/lib/octo/ui2/components/todo_area.rb +149 -0
  207. data/lib/octo/ui2/components/tool_component.rb +107 -0
  208. data/lib/octo/ui2/components/welcome_banner.rb +139 -0
  209. data/lib/octo/ui2/layout_manager.rb +807 -0
  210. data/lib/octo/ui2/line_editor.rb +363 -0
  211. data/lib/octo/ui2/markdown_renderer.rb +100 -0
  212. data/lib/octo/ui2/output_buffer.rb +370 -0
  213. data/lib/octo/ui2/progress_handle.rb +362 -0
  214. data/lib/octo/ui2/progress_indicator.rb +55 -0
  215. data/lib/octo/ui2/screen_buffer.rb +273 -0
  216. data/lib/octo/ui2/terminal_detector.rb +119 -0
  217. data/lib/octo/ui2/theme_manager.rb +85 -0
  218. data/lib/octo/ui2/themes/base_theme.rb +105 -0
  219. data/lib/octo/ui2/themes/hacker_theme.rb +62 -0
  220. data/lib/octo/ui2/themes/minimal_theme.rb +56 -0
  221. data/lib/octo/ui2/thinking_verbs.rb +26 -0
  222. data/lib/octo/ui2/ui_controller.rb +1625 -0
  223. data/lib/octo/ui2/view_renderer.rb +177 -0
  224. data/lib/octo/ui2.rb +40 -0
  225. data/lib/octo/ui_interface.rb +154 -0
  226. data/lib/octo/utils/arguments_parser.rb +191 -0
  227. data/lib/octo/utils/browser_detector.rb +195 -0
  228. data/lib/octo/utils/encoding.rb +92 -0
  229. data/lib/octo/utils/environment_detector.rb +140 -0
  230. data/lib/octo/utils/file_ignore_helper.rb +170 -0
  231. data/lib/octo/utils/file_processor.rb +601 -0
  232. data/lib/octo/utils/gitignore_parser.rb +154 -0
  233. data/lib/octo/utils/limit_stack.rb +152 -0
  234. data/lib/octo/utils/logger.rb +124 -0
  235. data/lib/octo/utils/login_shell.rb +72 -0
  236. data/lib/octo/utils/model_pricing.rb +646 -0
  237. data/lib/octo/utils/parser_manager.rb +165 -0
  238. data/lib/octo/utils/path_helper.rb +15 -0
  239. data/lib/octo/utils/scripts_manager.rb +59 -0
  240. data/lib/octo/utils/string_matcher.rb +158 -0
  241. data/lib/octo/utils/trash_directory.rb +112 -0
  242. data/lib/octo/utils/workspace_rules.rb +46 -0
  243. data/lib/octo/version.rb +5 -0
  244. data/lib/octo/web/app.css +7141 -0
  245. data/lib/octo/web/app.js +543 -0
  246. data/lib/octo/web/apple-touch-icon.png +0 -0
  247. data/lib/octo/web/auth.js +150 -0
  248. data/lib/octo/web/channels.js +276 -0
  249. data/lib/octo/web/datepicker.js +205 -0
  250. data/lib/octo/web/favicon.png +0 -0
  251. data/lib/octo/web/i18n.js +1073 -0
  252. data/lib/octo/web/icon-512.png +0 -0
  253. data/lib/octo/web/icon-dark.svg +25 -0
  254. data/lib/octo/web/icon.svg +29 -0
  255. data/lib/octo/web/index.html +871 -0
  256. data/lib/octo/web/marked.min.js +69 -0
  257. data/lib/octo/web/onboard.js +491 -0
  258. data/lib/octo/web/profile.js +442 -0
  259. data/lib/octo/web/sessions.js +4421 -0
  260. data/lib/octo/web/settings.js +913 -0
  261. data/lib/octo/web/sidebar.js +32 -0
  262. data/lib/octo/web/skills.js +885 -0
  263. data/lib/octo/web/tasks.js +297 -0
  264. data/lib/octo/web/theme.js +105 -0
  265. data/lib/octo/web/trash.js +343 -0
  266. data/lib/octo/web/vendor/hljs/highlight.min.js +1244 -0
  267. data/lib/octo/web/vendor/hljs/hljs-theme.css +95 -0
  268. data/lib/octo/web/vendor/katex/auto-render.min.js +1 -0
  269. data/lib/octo/web/vendor/katex/fonts/KaTeX_AMS-Regular.woff2 +0 -0
  270. data/lib/octo/web/vendor/katex/fonts/KaTeX_Caligraphic-Bold.woff2 +0 -0
  271. data/lib/octo/web/vendor/katex/fonts/KaTeX_Caligraphic-Regular.woff2 +0 -0
  272. data/lib/octo/web/vendor/katex/fonts/KaTeX_Fraktur-Bold.woff2 +0 -0
  273. data/lib/octo/web/vendor/katex/fonts/KaTeX_Fraktur-Regular.woff2 +0 -0
  274. data/lib/octo/web/vendor/katex/fonts/KaTeX_Main-Bold.woff2 +0 -0
  275. data/lib/octo/web/vendor/katex/fonts/KaTeX_Main-BoldItalic.woff2 +0 -0
  276. data/lib/octo/web/vendor/katex/fonts/KaTeX_Main-Italic.woff2 +0 -0
  277. data/lib/octo/web/vendor/katex/fonts/KaTeX_Main-Regular.woff2 +0 -0
  278. data/lib/octo/web/vendor/katex/fonts/KaTeX_Math-BoldItalic.woff2 +0 -0
  279. data/lib/octo/web/vendor/katex/fonts/KaTeX_Math-Italic.woff2 +0 -0
  280. data/lib/octo/web/vendor/katex/fonts/KaTeX_SansSerif-Bold.woff2 +0 -0
  281. data/lib/octo/web/vendor/katex/fonts/KaTeX_SansSerif-Italic.woff2 +0 -0
  282. data/lib/octo/web/vendor/katex/fonts/KaTeX_SansSerif-Regular.woff2 +0 -0
  283. data/lib/octo/web/vendor/katex/fonts/KaTeX_Script-Regular.woff2 +0 -0
  284. data/lib/octo/web/vendor/katex/fonts/KaTeX_Size1-Regular.woff2 +0 -0
  285. data/lib/octo/web/vendor/katex/fonts/KaTeX_Size2-Regular.woff2 +0 -0
  286. data/lib/octo/web/vendor/katex/fonts/KaTeX_Size3-Regular.woff2 +0 -0
  287. data/lib/octo/web/vendor/katex/fonts/KaTeX_Size4-Regular.woff2 +0 -0
  288. data/lib/octo/web/vendor/katex/fonts/KaTeX_Typewriter-Regular.woff2 +0 -0
  289. data/lib/octo/web/vendor/katex/katex.min.css +1 -0
  290. data/lib/octo/web/vendor/katex/katex.min.js +1 -0
  291. data/lib/octo/web/version.js +449 -0
  292. data/lib/octo/web/weixin-qr.html +209 -0
  293. data/lib/octo/web/ws-dispatcher.js +357 -0
  294. data/lib/octo/web/ws.js +128 -0
  295. data/lib/octo.rb +145 -0
  296. data/scripts/build/build.sh +329 -0
  297. data/scripts/build/lib/apt.sh +56 -0
  298. data/scripts/build/lib/brew.sh +89 -0
  299. data/scripts/build/lib/colors.sh +17 -0
  300. data/scripts/build/lib/gem.sh +95 -0
  301. data/scripts/build/lib/mise.sh +125 -0
  302. data/scripts/build/lib/network.sh +157 -0
  303. data/scripts/build/lib/os.sh +57 -0
  304. data/scripts/build/lib/shell.sh +37 -0
  305. data/scripts/build/src/install.sh.cc +174 -0
  306. data/scripts/build/src/install_browser.sh.cc +101 -0
  307. data/scripts/build/src/install_full.sh.cc +290 -0
  308. data/scripts/build/src/install_rails_deps.sh.cc +145 -0
  309. data/scripts/build/src/install_system_deps.sh.cc +123 -0
  310. data/scripts/build/src/uninstall.sh.cc +101 -0
  311. data/scripts/install.ps1 +532 -0
  312. data/scripts/install.sh +567 -0
  313. data/scripts/install_browser.sh +479 -0
  314. data/scripts/install_full.sh +838 -0
  315. data/scripts/install_rails_deps.sh +746 -0
  316. data/scripts/install_system_deps.sh +518 -0
  317. data/scripts/uninstall.sh +287 -0
  318. data/sig/octo.rbs +4 -0
  319. metadata +614 -0
@@ -0,0 +1,105 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'uri'
4
+ require 'net/http'
5
+ require 'json'
6
+
7
+ require_relative '../skill-add/scripts/install_from_zip'
8
+
9
+ # Install Feishu-related skills from the octo platform.
10
+ #
11
+ # Calls GET /api/v1/skills/feishu — same payload shape as /api/v1/skills/builtin:
12
+ # { "skills": [{ "name": "lark-doc", "download_url": "https://..." }, ...] }
13
+ #
14
+ # Each skill is installed sequentially via ZipSkillInstaller into ~/.octo/skills/<name>/.
15
+ #
16
+ # Usage:
17
+ # ruby install_feishu_skills.rb
18
+ #
19
+ # Output:
20
+ # Diagnostics → STDERR
21
+ # Last line → JSON: {"installed":N,"attempted":N}
22
+ # Exit code → always 0
23
+
24
+ class FeishuSkillsInstaller
25
+ PRIMARY_HOST = ENV.fetch('OCTO_LICENSE_SERVER', 'https://www.octo.com')
26
+ FALLBACK_HOST = 'https://octo.up.railway.app'
27
+ API_HOSTS = ENV['OCTO_LICENSE_SERVER'] ? [PRIMARY_HOST] : [PRIMARY_HOST, FALLBACK_HOST]
28
+ API_PATH = '/api/v1/skills/feishu'
29
+ API_OPEN_TIMEOUT = 5
30
+ API_READ_TIMEOUT = 10
31
+
32
+ def initialize
33
+ @target_dir = File.join(Dir.home, '.octo', 'skills')
34
+ @installed = 0
35
+ @attempted = 0
36
+ @errors = []
37
+ end
38
+
39
+ def run
40
+ skills = fetch_skill_list
41
+ if skills.nil? || skills.empty?
42
+ emit_summary
43
+ return
44
+ end
45
+
46
+ skills.each { |skill| install_one(skill) }
47
+ ensure
48
+ emit_summary
49
+ end
50
+
51
+ private def fetch_skill_list
52
+ API_HOSTS.each do |host|
53
+ begin
54
+ uri = URI.parse(host + API_PATH)
55
+ Net::HTTP.start(uri.host, uri.port,
56
+ use_ssl: uri.scheme == 'https',
57
+ open_timeout: API_OPEN_TIMEOUT,
58
+ read_timeout: API_READ_TIMEOUT) do |http|
59
+ response = http.request(Net::HTTP::Get.new(uri.request_uri))
60
+ if response.code.to_i == 200
61
+ payload = JSON.parse(response.body)
62
+ return Array(payload['skills'])
63
+ else
64
+ @errors << "API #{host}: HTTP #{response.code}"
65
+ end
66
+ end
67
+ rescue StandardError => e
68
+ @errors << "API #{host}: #{e.class}: #{e.message}"
69
+ end
70
+ end
71
+ nil
72
+ end
73
+
74
+ private def install_one(skill)
75
+ name = skill['name'].to_s
76
+ download_url = skill['download_url'].to_s
77
+ @attempted += 1
78
+
79
+ if name.empty? || download_url.empty?
80
+ @errors << "skill payload missing name or download_url: #{skill.inspect}"
81
+ return
82
+ end
83
+
84
+ result = ZipSkillInstaller.new(
85
+ download_url,
86
+ skill_name: name,
87
+ target_dir: @target_dir,
88
+ skip_if_exists: false
89
+ ).perform
90
+ @installed += result[:installed].size
91
+ @errors.concat(result[:errors]) if result[:errors].any?
92
+ rescue StandardError => e
93
+ @errors << "#{name}: #{e.class}: #{e.message}"
94
+ end
95
+
96
+ private def emit_summary
97
+ unless @errors.empty?
98
+ warn '[install-feishu-skills] non-fatal errors:'
99
+ @errors.each { |e| warn " - #{e}" }
100
+ end
101
+ puts JSON.generate(installed: @installed, attempted: @attempted)
102
+ end
103
+ end
104
+
105
+ FeishuSkillsInstaller.new.run if __FILE__ == $0
@@ -0,0 +1,274 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ # weixin_setup.rb — Automated Weixin (WeChat iLink) channel setup.
5
+ #
6
+ # Modes:
7
+ # --fetch-qr Output JSON {qrcode_url, qrcode_id} then exit — used by Agent/browser flow
8
+ # --qrcode-id <id> Skip QR fetch, use existing qrcode_id, long-poll until confirmed, then save
9
+ # (default) Full flow: fetch QR, display ASCII/URL, long-poll, save
10
+ #
11
+ # Environment (injected by octo server when run via Skill):
12
+ # OCTO_SERVER_PORT — port octo server listens on (default: 8888)
13
+ # OCTO_SERVER_HOST — host (default: 127.0.0.1)
14
+
15
+ require "json"
16
+ require "net/http"
17
+ require "net/https"
18
+ require "uri"
19
+ require "base64"
20
+ require "securerandom"
21
+ require "cgi"
22
+ require "shellwords"
23
+ require "openssl"
24
+
25
+ # ---------------------------------------------------------------------------
26
+ # Config
27
+ # ---------------------------------------------------------------------------
28
+
29
+ ILINK_BASE_URL = "https://ilinkai.weixin.qq.com"
30
+ BOT_TYPE = "3"
31
+ QR_POLL_TIMEOUT_S = 37 # slightly above server's 35s long-poll
32
+ LOGIN_DEADLINE_S = 5 * 60
33
+
34
+ OCTO_SERVER_URL = begin
35
+ host = ENV.fetch("OCTO_SERVER_HOST", "127.0.0.1")
36
+ port = ENV.fetch("OCTO_SERVER_PORT", "8888")
37
+ "http://#{host}:#{port}"
38
+ end
39
+
40
+ # ---------------------------------------------------------------------------
41
+ # Mode parsing
42
+ # ---------------------------------------------------------------------------
43
+
44
+ FETCH_QR_MODE = ARGV.include?("--fetch-qr")
45
+ QRCODE_ID_IDX = ARGV.index("--qrcode-id")
46
+ GIVEN_QRCODE_ID = QRCODE_ID_IDX ? ARGV[QRCODE_ID_IDX + 1] : nil
47
+
48
+ # ---------------------------------------------------------------------------
49
+ # Logging (suppress in --fetch-qr mode so stdout is clean JSON)
50
+ # ---------------------------------------------------------------------------
51
+
52
+ def step(msg); $stderr.puts("[weixin-setup] #{msg}") unless FETCH_QR_MODE; end
53
+ def ok(msg); $stderr.puts("[weixin-setup] ✅ #{msg}") unless FETCH_QR_MODE; end
54
+
55
+ # In fetch-qr mode, write to stderr so stdout stays clean JSON
56
+ def log(msg)
57
+ if FETCH_QR_MODE
58
+ $stderr.puts("[weixin-setup] #{msg}")
59
+ else
60
+ $stderr.puts("[weixin-setup] #{msg}")
61
+ end
62
+ end
63
+
64
+ def fail!(msg)
65
+ if FETCH_QR_MODE
66
+ $stdout.puts(JSON.generate({ error: msg }))
67
+ else
68
+ $stderr.puts("[weixin-setup] ❌ #{msg}")
69
+ end
70
+ exit 1
71
+ end
72
+
73
+ # ---------------------------------------------------------------------------
74
+ # iLink HTTP helpers
75
+ # ---------------------------------------------------------------------------
76
+
77
+ def random_wechat_uin
78
+ uint32 = SecureRandom.random_bytes(4).unpack1("N")
79
+ Base64.strict_encode64(uint32.to_s)
80
+ end
81
+
82
+ def ilink_get(path, extra_headers: {}, timeout: 15)
83
+ uri = URI("#{ILINK_BASE_URL}/#{path}")
84
+ http = Net::HTTP.new(uri.host, uri.port)
85
+ http.use_ssl = true
86
+ http.verify_mode = OpenSSL::SSL::VERIFY_PEER
87
+ http.read_timeout = timeout
88
+ http.open_timeout = 10
89
+
90
+ req = Net::HTTP::Get.new(uri.request_uri)
91
+ req["AuthorizationType"] = "ilink_bot_token"
92
+ req["X-WECHAT-UIN"] = random_wechat_uin
93
+ extra_headers.each { |k, v| req[k] = v }
94
+
95
+ res = http.request(req)
96
+ fail!("HTTP #{res.code} from #{path}: #{res.body.slice(0, 200)}") unless res.is_a?(Net::HTTPSuccess)
97
+ JSON.parse(res.body)
98
+ rescue Net::ReadTimeout, Net::OpenTimeout
99
+ nil # caller handles timeout
100
+ rescue => e
101
+ fail!("iLink request failed (#{path}): #{e.message}")
102
+ end
103
+
104
+ # ---------------------------------------------------------------------------
105
+ # QR code display (non-fetch-qr mode only)
106
+ # ---------------------------------------------------------------------------
107
+
108
+ def display_qr(qrcode_url)
109
+ displayed = false
110
+
111
+ # 1. Try ASCII via qrencode CLI
112
+ if system("which qrencode > /dev/null 2>&1")
113
+ ascii = `qrencode -t ANSIUTF8 -o - #{Shellwords.shellescape(qrcode_url)} 2>/dev/null`
114
+ if $?.success? && !ascii.empty?
115
+ puts ascii
116
+ displayed = true
117
+ end
118
+ end
119
+
120
+ # 2. Generate PNG and open in Preview
121
+ unless displayed
122
+ tmp_path = "/tmp/octo-weixin-qr-#{Process.pid}.png"
123
+ if system("which qrencode > /dev/null 2>&1") &&
124
+ system("qrencode", "-o", tmp_path, qrcode_url, exception: false)
125
+ step("QR code saved to: #{tmp_path}")
126
+ system("open", tmp_path, exception: false) if RUBY_PLATFORM.include?("darwin")
127
+ displayed = true
128
+ end
129
+ end
130
+
131
+ # 3. Last resort: print URL
132
+ unless displayed
133
+ $stderr.puts("[weixin-setup] Open this URL with WeChat to login:")
134
+ puts " #{qrcode_url}"
135
+ end
136
+ end
137
+
138
+ # ---------------------------------------------------------------------------
139
+ # Octo server — save credentials
140
+ # ---------------------------------------------------------------------------
141
+
142
+ def save_to_server(token:, base_url:)
143
+ uri = URI("#{OCTO_SERVER_URL}/api/channels/weixin")
144
+ body = JSON.generate({ token: token, base_url: base_url })
145
+
146
+ http = Net::HTTP.new(uri.host, uri.port)
147
+ http.read_timeout = 15
148
+ http.open_timeout = 5
149
+
150
+ req = Net::HTTP::Post.new(uri.path, "Content-Type" => "application/json")
151
+ req.body = body
152
+
153
+ res = http.request(req)
154
+ data = JSON.parse(res.body) rescue {}
155
+
156
+ unless res.is_a?(Net::HTTPSuccess) && data["ok"]
157
+ fail!("Failed to save Weixin config: #{data["error"] || res.body.slice(0, 200)}")
158
+ end
159
+
160
+ ok("Credentials saved via octo server")
161
+ rescue => e
162
+ fail!("Could not reach octo server: #{e.message}")
163
+ end
164
+
165
+ # ---------------------------------------------------------------------------
166
+ # Long-poll loop (shared by all modes)
167
+ # ---------------------------------------------------------------------------
168
+
169
+ def poll_until_confirmed(qrcode)
170
+ deadline = Time.now + LOGIN_DEADLINE_S
171
+ scanned_once = false
172
+
173
+ loop do
174
+ fail!("Login timed out. Please run setup again.") if Time.now > deadline
175
+
176
+ resp = ilink_get(
177
+ "ilink/bot/get_qrcode_status?qrcode=#{CGI.escape(qrcode)}",
178
+ extra_headers: { "iLink-App-ClientVersion" => "1" },
179
+ timeout: QR_POLL_TIMEOUT_S
180
+ )
181
+
182
+ next if resp.nil? # read timeout = server-side long-poll ended, retry
183
+
184
+ case resp["status"]
185
+ when "wait"
186
+ # still waiting
187
+ when "scaned"
188
+ unless scanned_once
189
+ $stderr.puts("[weixin-setup] WeChat scanned! Please confirm in the app...")
190
+ scanned_once = true
191
+ end
192
+ when "confirmed"
193
+ token = resp["bot_token"].to_s.strip
194
+ base_url = resp["baseurl"].to_s.strip
195
+ base_url = ILINK_BASE_URL if base_url.empty?
196
+ fail!("Login confirmed but no token received") if token.empty?
197
+ return { token: token, base_url: base_url }
198
+ when "expired"
199
+ fail!("QR code expired. Please run setup again.")
200
+ else
201
+ $stderr.puts("[weixin-setup] Unknown status: #{resp["status"]}, continuing...")
202
+ end
203
+ end
204
+ end
205
+
206
+ # ===========================================================================
207
+ # Main
208
+ # ===========================================================================
209
+
210
+ # ---------------------------------------------------------------------------
211
+ # Mode 1: --fetch-qr → output JSON to stdout, exit
212
+ # ---------------------------------------------------------------------------
213
+
214
+ if FETCH_QR_MODE
215
+ $stderr.puts("[weixin-setup] Fetching QR code from iLink...")
216
+ qr_resp = ilink_get("ilink/bot/get_bot_qrcode?bot_type=#{CGI.escape(BOT_TYPE)}")
217
+ fail!("No qrcode in response: #{qr_resp.inspect}") unless qr_resp&.dig("qrcode")
218
+
219
+ qrcode = qr_resp["qrcode"]
220
+ # qrcode_img_content is the URL encoded in the QR (not a base64 image)
221
+ qrcode_url = qr_resp["qrcode_img_content"].to_s.strip
222
+ qrcode_url = "https://liteapp.weixin.qq.com/q/#{qrcode}" if qrcode_url.empty? || !qrcode_url.start_with?("http")
223
+
224
+ $stdout.puts(JSON.generate({ qrcode_id: qrcode, qrcode_url: qrcode_url }))
225
+ exit 0
226
+ end
227
+
228
+ # ---------------------------------------------------------------------------
229
+ # Mode 2: --qrcode-id <id> → skip fetch, poll with existing id, save
230
+ # ---------------------------------------------------------------------------
231
+
232
+ if GIVEN_QRCODE_ID
233
+ $stderr.puts("[weixin-setup] Using existing QR session: #{GIVEN_QRCODE_ID}")
234
+ $stderr.puts("[weixin-setup] Waiting for scan confirmation...")
235
+ result = poll_until_confirmed(GIVEN_QRCODE_ID)
236
+ $stderr.puts("[weixin-setup] Confirmed! Saving credentials...")
237
+ save_to_server(token: result[:token], base_url: result[:base_url])
238
+ $stderr.puts("[weixin-setup] ✅ Weixin channel configured!")
239
+ exit 0
240
+ end
241
+
242
+ # ---------------------------------------------------------------------------
243
+ # Mode 3: default — full flow (terminal: ASCII QR + long-poll)
244
+ # ---------------------------------------------------------------------------
245
+
246
+ $stderr.puts("[weixin-setup] Fetching QR code from iLink...")
247
+ qr_resp = ilink_get("ilink/bot/get_bot_qrcode?bot_type=#{CGI.escape(BOT_TYPE)}")
248
+ fail!("No qrcode in response: #{qr_resp.inspect}") unless qr_resp&.dig("qrcode")
249
+
250
+ qrcode = qr_resp["qrcode"]
251
+ qrcode_url = qr_resp["qrcode_img_content"].to_s.strip
252
+ qrcode_url = "https://liteapp.weixin.qq.com/q/#{qrcode}" if qrcode_url.empty? || !qrcode_url.start_with?("http")
253
+
254
+ puts
255
+ puts "━" * 60
256
+ puts " Scan the QR code below with WeChat, then confirm in the app."
257
+ puts "━" * 60
258
+ display_qr(qrcode_url)
259
+ puts
260
+
261
+ $stderr.puts("[weixin-setup] Waiting for scan... (timeout: #{LOGIN_DEADLINE_S / 60} minutes)")
262
+ result = poll_until_confirmed(qrcode)
263
+
264
+ $stderr.puts("[weixin-setup] Login confirmed! Saving credentials...")
265
+ save_to_server(token: result[:token], base_url: result[:base_url])
266
+
267
+ puts
268
+ puts "━" * 60
269
+ puts "[weixin-setup] ✅ Weixin channel configured!"
270
+ puts " The adapter will start receiving messages immediately."
271
+ puts "━" * 60
272
+ puts
273
+
274
+ exit 0
@@ -0,0 +1,36 @@
1
+ ---
2
+ name: code-explorer
3
+ description: Use this skill when exploring, analyzing, or understanding project/code structure. Required for tasks like "analyze project", "explore codebase", "understand how X works".
4
+ agent: coding
5
+ fork_agent: true
6
+ model: lite
7
+ forbidden_tools:
8
+ - write
9
+ - edit
10
+ auto_summarize: true
11
+ ---
12
+
13
+ # Code Explorer Subagent
14
+
15
+ You are now running in a **forked subagent** mode optimized for fast code exploration.
16
+
17
+ ## Your Mission
18
+ Quickly explore and analyze the codebase to answer questions or gather information.
19
+
20
+ ## Your Restrictions
21
+ - NO modifications: You CANNOT use `write` or `edit` tools
22
+ - Read-only: Your role is to ANALYZE, not to change
23
+
24
+ ## Workflow — follow this order strictly
25
+
26
+ 1. **List the file tree** — run `glob` with `**/*` to get an overview of the project structure
27
+ 2. **Read README.md** — if it exists, read it to understand the project purpose and layout
28
+ 3. **Find relevant files** — based on the task, use `grep` to locate key patterns or specific files
29
+ 4. **Read only what's needed** — use `file_reader` only on the files directly relevant to the question
30
+ 5. **Report clearly** — provide a concise, actionable summary
31
+
32
+ ## Rules
33
+ - Do NOT read files blindly — always have a reason before opening a file
34
+ - Do NOT read every file in a directory — be selective
35
+ - Prefer `grep` over `file_reader` for finding specific patterns
36
+ - Stop as soon as you have enough information to answer the question
@@ -0,0 +1,257 @@
1
+ ---
2
+ name: cron-task-creator
3
+ description: 'Create, manage, and run scheduled automated tasks (cron jobs) in Octo. Use this skill whenever the user wants to create a new automated task or cron job, set up recurring automation, schedule something to run daily/weekly/hourly, view all scheduled tasks, edit an existing task prompt or cron schedule, enable or disable a task, delete a task, check task run history or logs, or run a task immediately via the WebUI. Trigger on phrases like 定时任务, 自动化任务, 每天自动, 创建任务, cron, 定时执行, scheduled task, automate this, run every day, set up automation, edit my task, list my tasks, what tasks do I have, disable task, run task now, task history, etc.'
4
+ disable-model-invocation: false
5
+ user-invocable: true
6
+ ---
7
+
8
+ # Cron Task Creator
9
+
10
+ A skill for creating, managing, and running scheduled automated tasks in Octo.
11
+
12
+ ## Architecture Overview
13
+
14
+ ```
15
+ Storage:
16
+ ~/.octo/tasks/<name>.md # Task prompt file (self-contained AI instruction)
17
+ ~/.octo/schedules.yml # All scheduled plans (YAML list)
18
+ ~/.octo/logger/octo-*.log # Execution logs (daily rotation)
19
+
20
+ API Base: http://${OCTO_SERVER_HOST}:${OCTO_SERVER_PORT}
21
+
22
+ Cron-Tasks API (unified — manages task file + schedule together):
23
+ GET /api/cron-tasks → list all cron tasks with schedule info
24
+ POST /api/cron-tasks → create task + schedule {name, content, cron, enabled?}
25
+ PATCH /api/cron-tasks/:name → update {content?, cron?, enabled?}
26
+ DELETE /api/cron-tasks/:name → delete task file + schedule
27
+ POST /api/cron-tasks/:name/run → execute immediately (creates a new session)
28
+ ```
29
+
30
+ ## Cron Expression Quick Reference
31
+
32
+ | Expression | Meaning |
33
+ |-----------------|---------------------------|
34
+ | `0 9 * * 1-5` | Weekdays at 09:00 |
35
+ | `0 9 * * *` | Every day at 09:00 |
36
+ | `0 */2 * * *` | Every 2 hours |
37
+ | `*/30 * * * *` | Every 30 minutes |
38
+ | `0 19 * * *` | Every day at 19:00 |
39
+ | `0 8 * * 1` | Every Monday at 08:00 |
40
+ | `0 0 1 * *` | First day of every month |
41
+
42
+ Field order: `minute hour day-of-month month day-of-week`
43
+
44
+ ---
45
+
46
+ ## Operations
47
+
48
+ ### 1. LIST — Show all tasks
49
+
50
+ ```bash
51
+ curl -s http://${OCTO_SERVER_HOST}:${OCTO_SERVER_PORT}/api/cron-tasks
52
+ ```
53
+
54
+ Display each task: name, cron schedule, enabled status, content preview.
55
+
56
+ If no tasks exist, inform the user and offer to create one or show templates.
57
+
58
+ **Key tip**: Remind the user that the Octo WebUI Task Panel (sidebar → Tasks) also shows all tasks and supports direct management.
59
+
60
+ ---
61
+
62
+ ### 2. CREATE — New task
63
+
64
+ **Step 1: Gather required info** (only ask for what's missing)
65
+ - What should the task DO? (goal, behavior, output format)
66
+ - How often should it run? (or is it manual-only without a schedule?)
67
+ - Any specific parameters? (URLs, file paths, output location, language)
68
+
69
+ **Step 2: Generate task name**
70
+ - Rule: only `[a-z0-9_-]`, lowercase, no spaces
71
+ - Examples: `daily_report`, `price_monitor`, `weekly_summary`
72
+
73
+ **Step 3: Write the task prompt**
74
+
75
+ The prompt must be:
76
+ - **Self-contained**: the agent running it has zero prior context — include everything needed
77
+ - **Written as direct instructions** to an AI agent (imperative, not conversational)
78
+ - **Detailed**: include URLs, file paths, output format, language, expected output location
79
+
80
+ Good task prompt example:
81
+ ```
82
+ You are a price monitoring assistant. Complete the following task:
83
+
84
+ ## Goal
85
+ Check the current BTC price on CoinGecko, compare with yesterday's price, and log an alert if the change exceeds 5%.
86
+
87
+ ## Steps
88
+ 1. Fetch https://api.coingecko.com/api/v3/simple/price?ids=bitcoin&vs_currencies=usd&include_24hr_change=true
89
+ 2. Parse the JSON response to get current price and 24h change
90
+ 3. If |change| > 5%, write an alert to ~/price_alerts/alert_YYYY-MM-DD.txt
91
+ 4. Print the current price and change percentage
92
+
93
+ Execute immediately.
94
+ ```
95
+
96
+ **Step 4: Create via API**
97
+
98
+ ```bash
99
+ curl -s -X POST http://${OCTO_SERVER_HOST}:${OCTO_SERVER_PORT}/api/cron-tasks \
100
+ -H "Content-Type: application/json" \
101
+ -d '{
102
+ "name": "task_name",
103
+ "content": "task prompt content...",
104
+ "cron": "0 9 * * *",
105
+ "enabled": true
106
+ }'
107
+ ```
108
+
109
+ **Step 5: Confirm creation**
110
+
111
+ ```
112
+ ✅ Task created successfully!
113
+
114
+ 📋 Task name: daily_standup
115
+ ⏰ Schedule: Weekdays at 09:00 (cron: 0 9 * * 1-5)
116
+
117
+ View and manage this task in the Octo WebUI → Tasks panel. Click ▶ Run to execute immediately.
118
+ ```
119
+
120
+ ---
121
+
122
+ ### 3. EDIT — Modify an existing task
123
+
124
+ **Step 1**: Identify the task (if unclear, LIST first and ask)
125
+
126
+ **Step 2**: Show current state via LIST or ask user to confirm
127
+
128
+ **Step 3**: Update via API
129
+
130
+ ```bash
131
+ # Update content only
132
+ curl -s -X PATCH http://${OCTO_SERVER_HOST}:${OCTO_SERVER_PORT}/api/cron-tasks/task_name \
133
+ -H "Content-Type: application/json" \
134
+ -d '{"content": "new prompt content..."}'
135
+
136
+ # Update cron schedule only
137
+ curl -s -X PATCH http://${OCTO_SERVER_HOST}:${OCTO_SERVER_PORT}/api/cron-tasks/task_name \
138
+ -H "Content-Type: application/json" \
139
+ -d '{"cron": "0 8 * * 1-5"}'
140
+
141
+ # Update both
142
+ curl -s -X PATCH http://${OCTO_SERVER_HOST}:${OCTO_SERVER_PORT}/api/cron-tasks/task_name \
143
+ -H "Content-Type: application/json" \
144
+ -d '{"content": "...", "cron": "0 8 * * 1-5"}'
145
+ ```
146
+
147
+ **Step 4**: Confirm changes
148
+
149
+ ```
150
+ ✅ Task updated!
151
+ 📋 daily_standup
152
+ Schedule: 0 9 * * 1-5 → 0 8 * * 1-5 (now weekdays at 08:00)
153
+ ```
154
+
155
+ ---
156
+
157
+ ### 4. ENABLE / DISABLE — Toggle a task
158
+
159
+ ```bash
160
+ # Disable
161
+ curl -s -X PATCH http://${OCTO_SERVER_HOST}:${OCTO_SERVER_PORT}/api/cron-tasks/task_name \
162
+ -H "Content-Type: application/json" \
163
+ -d '{"enabled": false}'
164
+
165
+ # Enable
166
+ curl -s -X PATCH http://${OCTO_SERVER_HOST}:${OCTO_SERVER_PORT}/api/cron-tasks/task_name \
167
+ -H "Content-Type: application/json" \
168
+ -d '{"enabled": true}'
169
+ ```
170
+
171
+ Confirm:
172
+ ```
173
+ ✅ daily_standup has been disabled.
174
+ To re-enable: say "enable daily_standup"
175
+ ```
176
+
177
+ ---
178
+
179
+ ### 5. DELETE — Remove a task
180
+
181
+ Always confirm before deleting (unless the user has explicitly said to delete):
182
+
183
+ ```
184
+ ⚠️ Are you sure you want to delete daily_standup? This cannot be undone.
185
+ ```
186
+
187
+ ```bash
188
+ curl -s -X DELETE http://${OCTO_SERVER_HOST}:${OCTO_SERVER_PORT}/api/cron-tasks/task_name
189
+ ```
190
+
191
+ ---
192
+
193
+ ### 6. HISTORY — View run history
194
+
195
+ Read the daily log files directly:
196
+
197
+ ```bash
198
+ grep "task_name" ~/.octo/logger/octo-$(date +%Y-%m-%d).log | tail -20
199
+ ```
200
+
201
+ Or search across recent days:
202
+ ```bash
203
+ grep -h "task_name" ~/.octo/logger/octo-*.log | tail -30
204
+ ```
205
+
206
+ Display format:
207
+ ```
208
+ 📊 Run History: ai_news_x_daily
209
+
210
+ Mar 10 19:00 ❌ Failed — JSON::ParserError: unexpected end of input
211
+ Mar 09 19:00 ✅ Success — took 1m 42s
212
+ Mar 08 19:00 ✅ Success — took 2m 10s
213
+ ```
214
+
215
+ ---
216
+
217
+ ### 7. RUN NOW — Execute immediately
218
+
219
+ ```bash
220
+ curl -s -X POST http://${OCTO_SERVER_HOST}:${OCTO_SERVER_PORT}/api/cron-tasks/task_name/run
221
+ ```
222
+
223
+ This creates a new session. Tell the user:
224
+ ```
225
+ ▶️ Task started in a new session.
226
+ View it in the Octo WebUI → Sessions panel.
227
+ ```
228
+
229
+ ---
230
+
231
+ ### 8. TEMPLATES — Browse common task templates
232
+
233
+ When user says "what templates are there" or "what can I automate":
234
+
235
+ ```
236
+ 📚 Common Task Templates — pick one to get started:
237
+
238
+ 1. 📰 AI News Digest — Daily fetch of AI news from X/RSS, generate Markdown report
239
+ 2. 💰 Price Monitor — Check crypto/stock prices on a schedule, log alerts on anomalies
240
+ 3. 📊 Weekly Work Summary — Every Monday, summarize last week's work into a report
241
+ 4. 🌤 Weather Reminder — Fetch weather every morning and save to file
242
+ 5. 🔍 Competitor Monitor — Periodically scrape competitor sites for changes
243
+ 6. 📝 Journal Prompt — Evening reminder to journal with daily reflection questions
244
+ 7. 🔗 Link Health Check — Periodically verify specified URLs are accessible
245
+ 8. 📂 File Backup — Regularly back up a specified directory to another location
246
+
247
+ Tell me which one interests you, or describe your own use case!
248
+ ```
249
+
250
+ ---
251
+
252
+ ## Important Notes
253
+
254
+ - Task names: only `[a-z0-9_-]`, no spaces, no uppercase
255
+ - Task prompt files must be **self-contained** — the executing agent has no prior memory
256
+ - Octo server must be running for cron to trigger automatically (checked every minute)
257
+ - The WebUI Task Panel is the preferred interface for managing tasks — always remind the user to check it after changes