@openhands/extensions 0.1.0 → 0.2.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 (347) hide show
  1. package/.agents/skills/custom-codereview-guide.md +25 -0
  2. package/.github/pull_request_template.md +38 -0
  3. package/.github/release.yml +14 -0
  4. package/.github/workflows/check-extensions.yml +72 -0
  5. package/.github/workflows/npm-publish.yml +89 -0
  6. package/.github/workflows/pr.yml +30 -0
  7. package/.github/workflows/release.yml +24 -0
  8. package/.github/workflows/tests.yml +25 -0
  9. package/.github/workflows/vulnerability-scan.yml +87 -0
  10. package/.release-please-manifest.json +3 -0
  11. package/AGENTS.md +132 -0
  12. package/README.md +10 -0
  13. package/analysis_results.md +162 -0
  14. package/marketplaces/large-codebase.json +66 -0
  15. package/marketplaces/openhands-extensions.json +682 -0
  16. package/package.json +4 -10
  17. package/plugins/README.md +30 -0
  18. package/plugins/city-weather/.plugin/plugin.json +13 -0
  19. package/plugins/city-weather/README.md +145 -0
  20. package/plugins/city-weather/commands/now.md +56 -0
  21. package/plugins/cobol-modernization/.plugin/plugin.json +19 -0
  22. package/plugins/cobol-modernization/README.md +201 -0
  23. package/plugins/cobol-modernization/references/troubleshooting.md +18 -0
  24. package/plugins/cobol-modernization/skills/build-setup/SKILL.md +78 -0
  25. package/plugins/cobol-modernization/skills/build-setup/scripts/install-gnucobol.sh +32 -0
  26. package/plugins/cobol-modernization/skills/cobol-modernization-overview/SKILL.md +113 -0
  27. package/plugins/cobol-modernization/skills/mainfraime-removal/SKILL.md +62 -0
  28. package/plugins/cobol-modernization/skills/mainfraime-removal/references/cics-transformation-examples.md +45 -0
  29. package/plugins/cobol-modernization/skills/mainframe-planning/SKILL.md +78 -0
  30. package/plugins/cobol-modernization/skills/to-java-migration/SKILL.md +59 -0
  31. package/plugins/cobol-modernization/skills/to-java-migration/references/cobol-to-java-example.md +58 -0
  32. package/plugins/cobol-modernization/skills/to-java-migration/references/datatype-mappings.md +19 -0
  33. package/plugins/issue-duplicate-checker/.plugin/plugin.json +13 -0
  34. package/plugins/issue-duplicate-checker/README.md +51 -0
  35. package/plugins/issue-duplicate-checker/action.yml +349 -0
  36. package/plugins/issue-duplicate-checker/scripts/auto_close_duplicate_issues.py +569 -0
  37. package/plugins/issue-duplicate-checker/scripts/issue_duplicate_check_openhands.py +681 -0
  38. package/plugins/issue-duplicate-checker/scripts/post_duplicate_notice.js +220 -0
  39. package/plugins/issue-duplicate-checker/scripts/remove_duplicate_candidate_label.js +27 -0
  40. package/plugins/magic-test/.plugin/plugin.json +13 -0
  41. package/plugins/magic-test/skills/magic-word/SKILL.md +33 -0
  42. package/plugins/migration-scoring/.plugin/plugin.json +19 -0
  43. package/plugins/migration-scoring/README.md +244 -0
  44. package/plugins/migration-scoring/skills/migration-mapping/SKILL.md +72 -0
  45. package/plugins/migration-scoring/skills/migration-report/SKILL.md +118 -0
  46. package/plugins/migration-scoring/skills/migration-scoring-overview/SKILL.md +126 -0
  47. package/plugins/migration-scoring/skills/score-quality/SKILL.md +54 -0
  48. package/plugins/migration-scoring/skills/score-quality/references/scoring-criteria.md +30 -0
  49. package/plugins/migration-scoring/skills/score-style/SKILL.md +106 -0
  50. package/plugins/onboarding/.plugin/plugin.json +20 -0
  51. package/plugins/onboarding/README.md +30 -0
  52. package/plugins/onboarding/references/criteria.md +144 -0
  53. package/plugins/onboarding/skills/agent-readiness-report/README.md +23 -0
  54. package/plugins/onboarding/skills/agent-readiness-report/SKILL.md +122 -0
  55. package/plugins/onboarding/skills/agent-readiness-report/scripts/scan_agent_instructions.sh +88 -0
  56. package/plugins/onboarding/skills/agent-readiness-report/scripts/scan_build_env.sh +114 -0
  57. package/plugins/onboarding/skills/agent-readiness-report/scripts/scan_feedback_loops.sh +133 -0
  58. package/plugins/onboarding/skills/agent-readiness-report/scripts/scan_policy.sh +113 -0
  59. package/plugins/onboarding/skills/agent-readiness-report/scripts/scan_workflows.sh +127 -0
  60. package/plugins/onboarding/skills/improve-agent-readiness/README.md +19 -0
  61. package/plugins/onboarding/skills/improve-agent-readiness/SKILL.md +167 -0
  62. package/plugins/onboarding/skills/setup-agents-md/README.md +15 -0
  63. package/plugins/onboarding/skills/setup-agents-md/SKILL.md +150 -0
  64. package/plugins/onboarding/skills/setup-openhands/README.md +20 -0
  65. package/plugins/onboarding/skills/setup-openhands/SKILL.md +56 -0
  66. package/plugins/onboarding/skills/setup-pr-review/README.md +23 -0
  67. package/plugins/onboarding/skills/setup-pr-review/SKILL.md +72 -0
  68. package/plugins/openhands/.plugin/plugin.json +13 -0
  69. package/plugins/openhands/README.md +52 -0
  70. package/plugins/openhands/SKILL.md +61 -0
  71. package/plugins/openhands/commands/create.md +55 -0
  72. package/plugins/openhands/commands/openhands-cloud.md +8 -0
  73. package/plugins/openhands/scripts/run.sh +69 -0
  74. package/plugins/pr-review/.plugin/plugin.json +13 -0
  75. package/plugins/pr-review/README.md +393 -0
  76. package/plugins/pr-review/action.yml +298 -0
  77. package/plugins/pr-review/scripts/agent_script.py +1282 -0
  78. package/plugins/pr-review/scripts/evaluate_review.py +655 -0
  79. package/plugins/pr-review/scripts/prompt.py +260 -0
  80. package/plugins/pr-review/workflows/pr-review-by-openhands.yml +51 -0
  81. package/plugins/pr-review/workflows/pr-review-evaluation.yml +85 -0
  82. package/plugins/qa-changes/.plugin/plugin.json +11 -0
  83. package/plugins/qa-changes/README.md +185 -0
  84. package/plugins/qa-changes/action.yml +181 -0
  85. package/plugins/qa-changes/scripts/agent_script.py +406 -0
  86. package/plugins/qa-changes/scripts/evaluate_qa_changes.py +385 -0
  87. package/plugins/qa-changes/scripts/prompt.py +174 -0
  88. package/plugins/qa-changes/workflows/qa-changes-by-openhands.yml +50 -0
  89. package/plugins/qa-changes/workflows/qa-changes-evaluation.yml +85 -0
  90. package/plugins/release-notes/.plugin/plugin.json +19 -0
  91. package/plugins/release-notes/README.md +283 -0
  92. package/plugins/release-notes/SKILL.md +83 -0
  93. package/plugins/release-notes/action.yml +117 -0
  94. package/plugins/release-notes/commands/release-notes.md +8 -0
  95. package/plugins/release-notes/scripts/agent_script.py +292 -0
  96. package/plugins/release-notes/scripts/generate_release_notes.py +733 -0
  97. package/plugins/release-notes/scripts/prompt.py +90 -0
  98. package/plugins/release-notes/scripts/validate_release_notes.py +328 -0
  99. package/plugins/release-notes/workflows/release-notes.yml +76 -0
  100. package/plugins/vulnerability-remediation/.plugin/plugin.json +19 -0
  101. package/plugins/vulnerability-remediation/README.md +217 -0
  102. package/plugins/vulnerability-remediation/action.yml +187 -0
  103. package/plugins/vulnerability-remediation/scripts/scan_and_remediate.py +561 -0
  104. package/plugins/vulnerability-remediation/workflows/vulnerability-scan.yml +87 -0
  105. package/pyproject.toml +12 -0
  106. package/release-please-config.json +16 -0
  107. package/scripts/sync_extensions.py +494 -0
  108. package/scripts/sync_openhands_sdk_skill.py +264 -0
  109. package/skills/README.md +159 -0
  110. package/skills/add-javadoc/.plugin/plugin.json +18 -0
  111. package/skills/add-javadoc/README.md +40 -0
  112. package/skills/add-javadoc/SKILL.md +35 -0
  113. package/skills/add-javadoc/references/example.md +32 -0
  114. package/skills/add-skill/.plugin/plugin.json +18 -0
  115. package/skills/add-skill/README.md +67 -0
  116. package/skills/add-skill/SKILL.md +47 -0
  117. package/skills/add-skill/scripts/fetch_skill.py +259 -0
  118. package/skills/agent-creator/.plugin/plugin.json +20 -0
  119. package/skills/agent-creator/README.md +104 -0
  120. package/skills/agent-creator/SKILL.md +190 -0
  121. package/skills/agent-creator/commands/agent-creator.md +8 -0
  122. package/skills/agent-creator/references/fallback.md +117 -0
  123. package/skills/agent-memory/.plugin/plugin.json +18 -0
  124. package/skills/agent-memory/README.md +35 -0
  125. package/skills/agent-memory/SKILL.md +30 -0
  126. package/skills/agent-memory/commands/remember.md +8 -0
  127. package/skills/agent-sdk-builder/.plugin/plugin.json +18 -0
  128. package/skills/agent-sdk-builder/README.md +40 -0
  129. package/skills/agent-sdk-builder/SKILL.md +37 -0
  130. package/skills/agent-sdk-builder/commands/agent-builder.md +8 -0
  131. package/skills/azure-devops/.plugin/plugin.json +18 -0
  132. package/skills/azure-devops/README.md +55 -0
  133. package/skills/azure-devops/SKILL.md +50 -0
  134. package/skills/bitbucket/.plugin/plugin.json +17 -0
  135. package/skills/bitbucket/README.md +50 -0
  136. package/skills/bitbucket/SKILL.md +45 -0
  137. package/skills/code-review/.plugin/plugin.json +19 -0
  138. package/skills/code-review/README.md +18 -0
  139. package/skills/code-review/SKILL.md +208 -0
  140. package/skills/code-review/commands/codereview-roasted.md +8 -0
  141. package/skills/code-review/commands/codereview.md +8 -0
  142. package/skills/code-review/references/risk-evaluation.md +41 -0
  143. package/skills/code-review/references/supply-chain-security.md +31 -0
  144. package/skills/code-simplifier/.plugin/plugin.json +21 -0
  145. package/skills/code-simplifier/README.md +30 -0
  146. package/skills/code-simplifier/SKILL.md +91 -0
  147. package/skills/code-simplifier/commands/simplify.md +8 -0
  148. package/skills/code-simplifier/references/code-quality-review.md +86 -0
  149. package/skills/code-simplifier/references/code-reuse-review.md +63 -0
  150. package/skills/code-simplifier/references/efficiency-review.md +81 -0
  151. package/skills/datadog/.plugin/plugin.json +19 -0
  152. package/skills/datadog/README.md +100 -0
  153. package/skills/datadog/SKILL.md +95 -0
  154. package/skills/deno/.plugin/plugin.json +18 -0
  155. package/skills/deno/README.md +5 -0
  156. package/skills/deno/SKILL.md +99 -0
  157. package/skills/deno/references/README.md +6 -0
  158. package/skills/discord/.plugin/plugin.json +18 -0
  159. package/skills/discord/README.md +31 -0
  160. package/skills/discord/SKILL.md +109 -0
  161. package/skills/discord/__init__.py +0 -0
  162. package/skills/discord/references/REFERENCE.md +78 -0
  163. package/skills/discord/scripts/__init__.py +0 -0
  164. package/skills/discord/scripts/_http.py +127 -0
  165. package/skills/discord/scripts/post_webhook.py +106 -0
  166. package/skills/discord/scripts/send_message.py +102 -0
  167. package/skills/docker/.plugin/plugin.json +17 -0
  168. package/skills/docker/README.md +34 -0
  169. package/skills/docker/SKILL.md +29 -0
  170. package/skills/evidence-based-citations/.plugin/plugin.json +20 -0
  171. package/skills/evidence-based-citations/README.md +31 -0
  172. package/skills/evidence-based-citations/SKILL.md +59 -0
  173. package/skills/flarglebargle/.plugin/plugin.json +16 -0
  174. package/skills/flarglebargle/README.md +14 -0
  175. package/skills/flarglebargle/SKILL.md +9 -0
  176. package/skills/frontend-design/.plugin/plugin.json +21 -0
  177. package/skills/frontend-design/LICENSE.txt +177 -0
  178. package/skills/frontend-design/README.md +42 -0
  179. package/skills/frontend-design/SKILL.md +42 -0
  180. package/skills/github/.plugin/plugin.json +19 -0
  181. package/skills/github/README.md +42 -0
  182. package/skills/github/SKILL.md +106 -0
  183. package/skills/github-pr-review/.plugin/plugin.json +18 -0
  184. package/skills/github-pr-review/README.md +145 -0
  185. package/skills/github-pr-review/SKILL.md +148 -0
  186. package/skills/github-pr-review/commands/github-pr-review.md +8 -0
  187. package/skills/github-pr-reviewer/.plugin/plugin.json +20 -0
  188. package/skills/github-pr-reviewer/README.md +34 -0
  189. package/skills/github-pr-reviewer/SKILL.md +89 -0
  190. package/skills/github-pr-reviewer/commands/pr-reviewer:setup.md +8 -0
  191. package/skills/github-repo-monitor/.plugin/plugin.json +22 -0
  192. package/skills/github-repo-monitor/README.md +70 -0
  193. package/skills/github-repo-monitor/SKILL.md +316 -0
  194. package/skills/github-repo-monitor/commands/github-monitor:poll.md +8 -0
  195. package/skills/github-repo-monitor/references/github-api.md +241 -0
  196. package/skills/github-repo-monitor/references/state-schema.md +160 -0
  197. package/skills/github-repo-monitor/scripts/main.py +915 -0
  198. package/skills/github-repo-monitor/tests/test_main.py +400 -0
  199. package/skills/gitlab/.plugin/plugin.json +17 -0
  200. package/skills/gitlab/README.md +37 -0
  201. package/skills/gitlab/SKILL.md +32 -0
  202. package/skills/incident-retrospective/.plugin/plugin.json +21 -0
  203. package/skills/incident-retrospective/README.md +34 -0
  204. package/skills/incident-retrospective/SKILL.md +98 -0
  205. package/skills/incident-retrospective/commands/incident-retro:setup.md +8 -0
  206. package/skills/iterate/.plugin/plugin.json +13 -0
  207. package/skills/iterate/README.md +25 -0
  208. package/skills/iterate/SKILL.md +399 -0
  209. package/skills/iterate/commands/babysit.md +8 -0
  210. package/skills/iterate/commands/iterate.md +8 -0
  211. package/skills/iterate/commands/verify.md +8 -0
  212. package/skills/iterate/references/heuristics.md +58 -0
  213. package/skills/iterate/references/verification.md +96 -0
  214. package/skills/jupyter/.plugin/plugin.json +18 -0
  215. package/skills/jupyter/README.md +55 -0
  216. package/skills/jupyter/SKILL.md +50 -0
  217. package/skills/kubernetes/.plugin/plugin.json +18 -0
  218. package/skills/kubernetes/README.md +53 -0
  219. package/skills/kubernetes/SKILL.md +48 -0
  220. package/skills/learn-from-code-review/.plugin/plugin.json +19 -0
  221. package/skills/learn-from-code-review/README.md +64 -0
  222. package/skills/learn-from-code-review/SKILL.md +186 -0
  223. package/skills/learn-from-code-review/commands/learn-from-reviews.md +8 -0
  224. package/skills/linear/.plugin/plugin.json +19 -0
  225. package/skills/linear/README.md +58 -0
  226. package/skills/linear/SKILL.md +213 -0
  227. package/skills/linear-triage/.plugin/plugin.json +21 -0
  228. package/skills/linear-triage/README.md +34 -0
  229. package/skills/linear-triage/SKILL.md +91 -0
  230. package/skills/linear-triage/commands/linear-triage:setup.md +8 -0
  231. package/skills/notion/.plugin/plugin.json +17 -0
  232. package/skills/notion/README.md +114 -0
  233. package/skills/notion/SKILL.md +109 -0
  234. package/skills/npm/.plugin/plugin.json +17 -0
  235. package/skills/npm/README.md +14 -0
  236. package/skills/npm/SKILL.md +9 -0
  237. package/skills/openhands-api/.plugin/plugin.json +22 -0
  238. package/skills/openhands-api/README.md +48 -0
  239. package/skills/openhands-api/SKILL.md +399 -0
  240. package/skills/openhands-api/references/README.md +33 -0
  241. package/skills/openhands-api/references/TROUBLESHOOTING.md +81 -0
  242. package/skills/openhands-api/references/example_prompt.md +12 -0
  243. package/skills/openhands-api/scripts/openhands_api.py +606 -0
  244. package/skills/openhands-api/scripts/openhands_api.ts +252 -0
  245. package/skills/openhands-automation/.plugin/plugin.json +19 -0
  246. package/skills/openhands-automation/README.md +89 -0
  247. package/skills/openhands-automation/SKILL.md +875 -0
  248. package/skills/openhands-automation/commands/automation:create.md +8 -0
  249. package/skills/openhands-automation/references/ab-testing.md +185 -0
  250. package/skills/openhands-automation/references/custom-automation.md +644 -0
  251. package/skills/openhands-sdk/.plugin/plugin.json +20 -0
  252. package/skills/openhands-sdk/README.md +22 -0
  253. package/skills/openhands-sdk/SKILL.md +229 -0
  254. package/skills/openhands-sdk/commands/sdk.md +8 -0
  255. package/skills/pdflatex/.plugin/plugin.json +18 -0
  256. package/skills/pdflatex/README.md +39 -0
  257. package/skills/pdflatex/SKILL.md +34 -0
  258. package/skills/prd/.plugin/plugin.json +19 -0
  259. package/skills/prd/README.md +28 -0
  260. package/skills/prd/SKILL.md +237 -0
  261. package/skills/prd/commands/prd.md +8 -0
  262. package/skills/qa-changes/README.md +18 -0
  263. package/skills/qa-changes/SKILL.md +229 -0
  264. package/skills/qa-changes/commands/qa-changes.md +8 -0
  265. package/skills/release-notes/README.md +24 -0
  266. package/skills/release-notes/SKILL.md +19 -0
  267. package/skills/release-notes/commands/release-notes.md +8 -0
  268. package/skills/research-brief/.plugin/plugin.json +20 -0
  269. package/skills/research-brief/README.md +34 -0
  270. package/skills/research-brief/SKILL.md +99 -0
  271. package/skills/research-brief/commands/research-brief:setup.md +8 -0
  272. package/skills/security/.plugin/plugin.json +18 -0
  273. package/skills/security/README.md +38 -0
  274. package/skills/security/SKILL.md +33 -0
  275. package/skills/skill-creator/.plugin/plugin.json +17 -0
  276. package/skills/skill-creator/LICENSE.txt +202 -0
  277. package/skills/skill-creator/README.md +182 -0
  278. package/skills/skill-creator/SKILL.md +545 -0
  279. package/skills/skill-creator/references/output-patterns.md +82 -0
  280. package/skills/skill-creator/references/workflows.md +28 -0
  281. package/skills/skill-creator/scripts/init_skill.py +303 -0
  282. package/skills/skill-creator/scripts/quick_validate.py +95 -0
  283. package/skills/slack-channel-monitor/.plugin/plugin.json +21 -0
  284. package/skills/slack-channel-monitor/README.md +91 -0
  285. package/skills/slack-channel-monitor/SKILL.md +276 -0
  286. package/skills/slack-channel-monitor/commands/slack-monitor:poll.md +8 -0
  287. package/skills/slack-channel-monitor/references/slack-api.md +207 -0
  288. package/skills/slack-channel-monitor/references/state-schema.md +180 -0
  289. package/skills/slack-channel-monitor/scripts/main.py +962 -0
  290. package/skills/slack-standup-digest/.plugin/plugin.json +21 -0
  291. package/skills/slack-standup-digest/README.md +34 -0
  292. package/skills/slack-standup-digest/SKILL.md +92 -0
  293. package/skills/slack-standup-digest/commands/standup-digest:setup.md +8 -0
  294. package/skills/spark-version-upgrade/.plugin/plugin.json +20 -0
  295. package/skills/spark-version-upgrade/README.md +54 -0
  296. package/skills/spark-version-upgrade/SKILL.md +233 -0
  297. package/skills/ssh/.plugin/plugin.json +18 -0
  298. package/skills/ssh/README.md +140 -0
  299. package/skills/ssh/SKILL.md +135 -0
  300. package/skills/swift-linux/.plugin/plugin.json +17 -0
  301. package/skills/swift-linux/README.md +86 -0
  302. package/skills/swift-linux/SKILL.md +81 -0
  303. package/skills/theme-factory/.plugin/plugin.json +19 -0
  304. package/skills/theme-factory/LICENSE.txt +202 -0
  305. package/skills/theme-factory/README.md +58 -0
  306. package/skills/theme-factory/SKILL.md +59 -0
  307. package/skills/theme-factory/theme-showcase.pdf +0 -0
  308. package/skills/theme-factory/themes/arctic-frost.md +19 -0
  309. package/skills/theme-factory/themes/botanical-garden.md +19 -0
  310. package/skills/theme-factory/themes/desert-rose.md +19 -0
  311. package/skills/theme-factory/themes/forest-canopy.md +19 -0
  312. package/skills/theme-factory/themes/golden-hour.md +19 -0
  313. package/skills/theme-factory/themes/midnight-galaxy.md +19 -0
  314. package/skills/theme-factory/themes/modern-minimalist.md +19 -0
  315. package/skills/theme-factory/themes/ocean-depths.md +19 -0
  316. package/skills/theme-factory/themes/sunset-boulevard.md +19 -0
  317. package/skills/theme-factory/themes/tech-innovation.md +19 -0
  318. package/skills/uv/.plugin/plugin.json +18 -0
  319. package/skills/uv/README.md +5 -0
  320. package/skills/uv/SKILL.md +95 -0
  321. package/skills/uv/references/README.md +5 -0
  322. package/skills/vercel/.plugin/plugin.json +18 -0
  323. package/skills/vercel/README.md +108 -0
  324. package/skills/vercel/SKILL.md +103 -0
  325. package/tests/test_add_skill_installs_to_agents_dir.py +42 -0
  326. package/tests/test_catalogs.py +109 -0
  327. package/tests/test_code_review_risk_evaluation.py +94 -0
  328. package/tests/test_issue_duplicate_checker.py +240 -0
  329. package/tests/test_openhands_api_python.py +152 -0
  330. package/tests/test_plugin_manifest.py +83 -0
  331. package/tests/test_pr_review_diff_payload.py +202 -0
  332. package/tests/test_pr_review_feedback.py +263 -0
  333. package/tests/test_pr_review_prompt.py +152 -0
  334. package/tests/test_pr_review_review_context.py +253 -0
  335. package/tests/test_qa_changes.py +232 -0
  336. package/tests/test_qa_changes_evaluation.py +259 -0
  337. package/tests/test_release_notes_generator.py +990 -0
  338. package/tests/test_sdk_loading.py +150 -0
  339. package/tests/test_skill_plugin_loading.py +149 -0
  340. package/tests/test_skills_have_readme.py +66 -0
  341. package/tests/test_sync_extensions.py +292 -0
  342. package/tests/test_workflow_sync.py +46 -0
  343. package/utils/analysis/README.md +7 -0
  344. package/utils/analysis/laminar_signals/README.md +211 -0
  345. package/utils/analysis/laminar_signals/analyze.py +780 -0
  346. package/utils/analysis/laminar_signals/templates/default.j2 +49 -0
  347. package/utils/analysis/laminar_signals/templates/pr_review.j2 +61 -0
@@ -0,0 +1,276 @@
1
+ ---
2
+ name: slack-channel-monitor
3
+ description: >
4
+ This skill should be used when the user asks to "monitor a Slack channel",
5
+ "watch Slack for messages", "create a Slack bot that responds to mentions",
6
+ "set up an OpenHands Slack integration", "trigger OpenHands from Slack",
7
+ "respond to @openhands in Slack", or "poll Slack channels for a trigger
8
+ phrase". Guides the user through creating a cron automation that watches up
9
+ to 10 Slack channels and starts an OpenHands conversation whenever a
10
+ configurable trigger phrase is detected.
11
+ triggers:
12
+ - /slack-monitor:poll
13
+ ---
14
+
15
+ # Slack Channel Monitor
16
+
17
+ Create a cron automation that polls up to 10 Slack channels every minute.
18
+ When a message containing the **trigger phrase** (default: `@openhands`) is
19
+ detected it:
20
+
21
+ 1. Adds a 👀 reaction to the triggering message.
22
+ 2. Opens an OpenHands conversation with the message and recent channel context.
23
+ 3. Posts a reply in the Slack thread with a link to the conversation.
24
+
25
+ On every subsequent run:
26
+ - Replies in the thread are forwarded to the running conversation.
27
+ - When the conversation finishes (or errors), the agent's final response is
28
+ posted back to the Slack thread.
29
+
30
+ > **Local mode only.** This automation targets the local OpenHands setup
31
+ > (`dev:automation` stack). A cloud/webhook-based variant is out of scope here.
32
+
33
+ ---
34
+
35
+ ## Prerequisites
36
+
37
+ ### Required secrets
38
+
39
+ Verify that at least one of the following secrets is set in
40
+ **OpenHands Settings → Secrets** before proceeding:
41
+
42
+ | Secret name | Token type | Minimum scopes |
43
+ |---|---|---|
44
+ | `SLACK_BOT_TOKEN` | Bot (`xoxb-…`) | `channels:history`, `channels:read`, `reactions:write`, `chat:write` |
45
+ | `SLACK_USER_TOKEN` | User (`xoxp-…`) | Same as bot, plus `search:read` for multi-channel efficiency |
46
+
47
+ Check with:
48
+ ```bash
49
+ # For bot token:
50
+ curl -s https://slack.com/api/auth.test -H "Authorization: Bearer $SLACK_BOT_TOKEN" \
51
+ | python3 -c "import json,sys; d=json.load(sys.stdin); print('ok' if d.get('ok') else d.get('error'))"
52
+
53
+ # For user token:
54
+ curl -s https://slack.com/api/auth.test -H "Authorization: Bearer $SLACK_USER_TOKEN" \
55
+ | python3 -c "import json,sys; d=json.load(sys.stdin); print('ok' if d.get('ok') else d.get('error'))"
56
+ ```
57
+
58
+ If neither token is present, inform the user and stop - the automation cannot
59
+ function without Slack credentials.
60
+
61
+ ### Optional secret
62
+
63
+ | Secret name | Default | Purpose |
64
+ |---|---|---|
65
+ | `OPENHANDS_URL` | `http://localhost:8000` | Base URL used to build conversation links posted in Slack |
66
+
67
+ ---
68
+
69
+ ## Setup Workflow
70
+
71
+ Follow these steps in order.
72
+
73
+ ### Step 1 - Collect channels
74
+
75
+ Ask the user: *"Which Slack channels should be monitored? You can provide
76
+ channel names (e.g. `#general`) or IDs (e.g. `C0123456789`)."*
77
+
78
+ **If the user provides channel names**, resolve them to IDs:
79
+
80
+ ```bash
81
+ SLACK_TOKEN="${SLACK_BOT_TOKEN:-$SLACK_USER_TOKEN}"
82
+ curl -s "https://slack.com/api/conversations.list?types=public_channel,private_channel&limit=200&exclude_archived=true" \
83
+ -H "Authorization: Bearer $SLACK_TOKEN" \
84
+ | python3 -c "
85
+ import json, sys
86
+ data = json.load(sys.stdin)
87
+ if not data.get('ok'):
88
+ print('ERROR:', data.get('error'))
89
+ exit(1)
90
+ names = set(n.lstrip('#') for n in ['CHANNEL_NAMES_HERE'.split(',')])
91
+ for ch in data.get('channels', []):
92
+ if ch['name'] in names:
93
+ print(f\"{ch['name']} → {ch['id']}\")
94
+ "
95
+ ```
96
+
97
+ Replace `CHANNEL_NAMES_HERE` with the comma-separated names the user provided.
98
+
99
+ **If `conversations.list` returns `missing_scope` or `not_authed`:**
100
+ Inform the user: *"The token doesn't have permission to list channels. Please
101
+ provide the channel IDs directly (right-click a channel in Slack → Copy link -
102
+ the last path segment starting with `C` is the ID)."*
103
+
104
+ **If the bot token lacks `channels:read`** for private channels, the user can
105
+ either invite the bot first (`/invite @botname`) or switch to a user token.
106
+
107
+ Collect up to 10 channel IDs. Record them as a Python list literal, e.g.:
108
+ ```python
109
+ ["C0123456789", "C9876543210"]
110
+ ```
111
+
112
+ ### Step 2 - Collect trigger phrase
113
+
114
+ Ask the user: *"What trigger phrase should OpenHands respond to?
115
+ (Press Enter to use the default: `@openhands`)"*
116
+
117
+ Accepted values: any non-empty string unlikely to appear accidentally, e.g.
118
+ `@openhands`, `jazz hands`, `take-me-to-funky-town`.
119
+
120
+ ### Step 3 - Generate the automation script
121
+
122
+ Read `scripts/main.py` from this skill's directory. Apply exactly three
123
+ constant substitutions near the top of the file:
124
+
125
+ | Placeholder | Replace with |
126
+ |---|---|
127
+ | `TRIGGER_PHRASE = "@openhands"` | `TRIGGER_PHRASE = "{user_phrase}"` |
128
+ | `CHANNEL_IDS: list[str] = []` | `CHANNEL_IDS: list[str] = {channel_id_list}` |
129
+ | `DEFAULT_OPENHANDS_URL = "http://localhost:8000"` | `DEFAULT_OPENHANDS_URL = "{url}"` (keep default if user has no preference) |
130
+
131
+ Write the customised script to a temporary directory:
132
+ ```bash
133
+ mkdir -p /tmp/slack-monitor-build
134
+ # (write the customised main.py to /tmp/slack-monitor-build/main.py)
135
+ ```
136
+
137
+ Validate syntax before packaging:
138
+ ```bash
139
+ python3 -m py_compile /tmp/slack-monitor-build/main.py && echo "Syntax OK"
140
+ ```
141
+
142
+ Fix any syntax errors before proceeding.
143
+
144
+ ### Step 4 - Package and upload
145
+
146
+ Determine the Automation backend URL and auth from the `<RUNTIME_SERVICES>`
147
+ block in your system context:
148
+ - Use the **Automation backend** `url_from_agent` as `OPENHANDS_HOST`
149
+ - Auth: `X-Session-API-Key: $OPENHANDS_AUTOMATION_API_KEY`
150
+
151
+ If no Automation backend is listed in `<RUNTIME_SERVICES>`, stop and tell
152
+ the user to start the full automation stack.
153
+
154
+ ```bash
155
+ tar -czf /tmp/slack-monitor.tar.gz -C /tmp/slack-monitor-build .
156
+
157
+ # OPENHANDS_HOST: read from <RUNTIME_SERVICES> Automation backend url_from_agent
158
+ OPENHANDS_HOST="<automation-url-from-runtime-services>"
159
+
160
+ TARBALL_PATH=$(curl -s -X POST \
161
+ "${OPENHANDS_HOST}/api/automation/v1/uploads?name=slack-channel-monitor" \
162
+ -H "X-Session-API-Key: $OPENHANDS_AUTOMATION_API_KEY" \
163
+ -H "Content-Type: application/gzip" \
164
+ --data-binary @/tmp/slack-monitor.tar.gz \
165
+ | python3 -c "import json,sys; print(json.load(sys.stdin)['tarball_path'])")
166
+
167
+ echo "Uploaded: $TARBALL_PATH"
168
+ ```
169
+
170
+ If the upload fails with a size error, the tarball must be under 1 MB.
171
+ `main.py` is under 15 KB so this should never trigger.
172
+
173
+ ### Step 5 - Create the automation
174
+
175
+ ```bash
176
+ curl -s -X POST "${OPENHANDS_HOST}/api/automation/v1" \
177
+ -H "X-Session-API-Key: $OPENHANDS_AUTOMATION_API_KEY" \
178
+ -H "Content-Type: application/json" \
179
+ -d "{
180
+ \"name\": \"Slack Channel Monitor\",
181
+ \"trigger\": {\"type\": \"cron\", \"schedule\": \"* * * * *\"},
182
+ \"tarball_path\": \"$TARBALL_PATH\",
183
+ \"entrypoint\": \"python3 main.py\",
184
+ \"timeout\": 55
185
+ }" | python3 -m json.tool
186
+ ```
187
+
188
+ A 55-second timeout keeps runs well within the 60-second cron window.
189
+
190
+ Record the returned `id` - share it with the user as confirmation.
191
+
192
+ ### Step 6 - Confirm
193
+
194
+ Tell the user:
195
+
196
+ > ✅ **Slack Channel Monitor** is running!
197
+ >
198
+ > - Automation ID: `{id}`
199
+ > - Channels: `{channel list}`
200
+ > - Trigger phrase: `{phrase}`
201
+ > - Polling every minute via cron `* * * * *`
202
+ > - State file: `~/.openhands/workspaces/automation-state/slack_poller_{id}.json`
203
+ >
204
+ > Send a message containing `{phrase}` in any monitored channel to test it.
205
+ > The bot will react with 👀 and reply with a link to the new conversation.
206
+
207
+ ---
208
+
209
+ ## Runtime Behaviour (per poll)
210
+
211
+ Each cron run executes `main.py`, which runs **10 polling iterations** (every
212
+ 5 seconds) within the 55-second timeout window. Each iteration:
213
+
214
+ 1. **Loads state** from the JSON file (see `references/state-schema.md`).
215
+ 2. **Resolves the Slack token** - checks `SLACK_USER_TOKEN` then `SLACK_BOT_TOKEN`.
216
+ 3. **Fetches new messages:**
217
+ - User token + `search:read` + > 1 channel → single `search.messages` call
218
+ (searches for the trigger phrase across all channels).
219
+ - Otherwise → one `conversations.history` call per channel.
220
+ 4. **Fetches thread replies** - one `conversations.replies` call per tracked thread.
221
+ 5. **Processes messages** in chronological order:
222
+ - Skips messages already in `processed_ts` (dedup across the overlap window).
223
+ - Skips bot messages and any `ts` in `bot_message_ts`.
224
+ - Reply in a tracked thread (active **or** closed) → forwards to the existing
225
+ conversation; re-opens closed conversations automatically.
226
+ - Contains trigger phrase → 👀 reaction, create conversation, post link.
227
+ - Thread replies: agent receives full thread history for context.
228
+ - Root messages: agent receives the trigger text only.
229
+ 6. **Checks conversation statuses** - for each active conversation where
230
+ `time.time() - last_activity > 15 s`:
231
+ - If status is `idle`, `finished`, `error`, or `stuck` → fetch the agent's
232
+ final response via `/api/conversations/{id}/agent_final_response` and post
233
+ it to the Slack thread. Mark conversation `closed`.
234
+ 7. **Advances `last_poll`** to `now - 10 s` (overlap window prevents boundary
235
+ races). If a conversation creation failed, pins `last_poll` further back to
236
+ retry on the next iteration.
237
+ 8. **Saves state** (including `processed_ts`) and continues to the next iteration.
238
+ 9. After all iterations, fires the completion callback.
239
+
240
+ Debug output is written to both stdout and a persistent log at:
241
+ ```
242
+ {WORKSPACE_BASE_ROOT}/automation-state/slack_poller_debug.log
243
+ ```
244
+
245
+ ---
246
+
247
+ ## Additional Resources
248
+
249
+ ### Reference Files
250
+
251
+ - **`references/slack-api.md`** - Slack token types, required scopes, API
252
+ endpoint reference, rate limits, and common error codes.
253
+ - **`references/state-schema.md`** - State JSON schema, field definitions,
254
+ example file, and conversation lifecycle diagram.
255
+
256
+ ### Script Template
257
+
258
+ - **`scripts/main.py`** - The complete automation script. Customise the three
259
+ constants at the top (`TRIGGER_PHRASE`, `CHANNEL_IDS`, `DEFAULT_OPENHANDS_URL`)
260
+ before packaging.
261
+
262
+ ---
263
+
264
+ ## Troubleshooting
265
+
266
+ | Symptom | Likely cause | Fix |
267
+ |---|---|---|
268
+ | Bot doesn't react to messages | Token missing or bot not in channel | Verify token with `auth.test`; `/invite @botname` |
269
+ | `not_in_channel` error in run logs | Bot token used but bot not a member | Invite bot or switch to user token |
270
+ | `missing_scope` error | Token lacks required scopes | Re-install Slack app with correct scopes (see `references/slack-api.md`) |
271
+ | No messages detected | `last_poll` timestamp is in the future | Delete the state file to reset; it will be recreated on next run |
272
+ | Conversation link 404 | `OPENHANDS_URL` points to wrong host | Set the `OPENHANDS_URL` secret to the correct base URL |
273
+ | Summary never posted | Conversation stuck in `running` state | Check conversation in the OpenHands UI; the agent may need intervention |
274
+ | Duplicate conversations created | `processed_ts` state missing or corrupted | Delete the state file to reset; dedup will rebuild on next run |
275
+ | Trigger message processed on each cron run | State file deleted between runs | Ensure `automation-state/` directory is persistent across runs |
276
+ | Debug info needed | Need detailed per-message trace | Check `{WORKSPACE_BASE_ROOT}/automation-state/slack_poller_debug.log` |
@@ -0,0 +1,8 @@
1
+ ---
2
+ # auto-generated by sync_extensions.py
3
+ description: This skill should be used when the user asks to "monitor a Slack channel", "watch Slack for messages", "create a Slack bot that responds to mentions", "set up an OpenHands Slack integration", "trigger OpenHands from Slack", "respond to @openhands in Slack", or "poll Slack channels for a trigger phrase". Guides the user through creating a cron automation that watches up to 10 Slack channels and starts an OpenHands conversation whenever a configurable trigger phrase is detected.
4
+ ---
5
+
6
+ Read and follow the complete instructions in the SKILL.md file located in this skill's directory.
7
+
8
+ $ARGUMENTS
@@ -0,0 +1,207 @@
1
+ # Slack API Reference
2
+
3
+ Reference material for the `slack-channel-monitor` skill. Consult this file
4
+ when resolving token issues, diagnosing permission errors, or adjusting the
5
+ polling strategy.
6
+
7
+ ---
8
+
9
+ ## Token Types
10
+
11
+ | Type | Prefix | Typical source | Relevant scopes |
12
+ |------|--------|---------------|-----------------|
13
+ | **Bot token** | `xoxb-` | OAuth install / Slack App → Install App | `channels:history`, `channels:read`, `reactions:write`, `chat:write` |
14
+ | **User token** | `xoxp-` | OAuth flow on behalf of a workspace member | Same as bot + `search:read` for multi-channel search |
15
+
16
+ ### Choosing a token
17
+
18
+ - **Prefer a bot token** for single-channel monitoring or when `search:read` is
19
+ unavailable. One `conversations.history` call per channel per minute is fine
20
+ for < 10 channels.
21
+ - **Use a user token** with `search:read` when monitoring multiple channels, to
22
+ reduce API calls by querying all channels in a single `search.messages` request.
23
+
24
+ ### Checking token type at runtime
25
+
26
+ The script detects token type by checking which secret name is set:
27
+
28
+ 1. `SLACK_USER_TOKEN` (checked first - user token preferred for multi-channel)
29
+ 2. `SLACK_BOT_TOKEN`
30
+
31
+ Set the appropriate secret in **OpenHands Settings → Secrets**.
32
+
33
+ ---
34
+
35
+ ## Required Scopes
36
+
37
+ ### Bot token (`xoxb-`)
38
+
39
+ | Scope | Used for |
40
+ |-------|----------|
41
+ | `channels:history` | Read messages from public channels |
42
+ | `groups:history` | Read messages from private channels (if monitoring any) |
43
+ | `channels:read` | Resolve channel names → IDs |
44
+ | `reactions:write` | Add 👀 reaction to trigger messages |
45
+ | `chat:write` | Post conversation links and summaries back to Slack |
46
+
47
+ ### User token (`xoxp-`) - additional scope
48
+
49
+ | Scope | Used for |
50
+ |-------|----------|
51
+ | `search:read` | `search.messages` across multiple channels in one request |
52
+
53
+ ---
54
+
55
+ ## Relevant API Endpoints
56
+
57
+ ### `conversations.history`
58
+ Fetch messages from a single channel newer than a timestamp.
59
+
60
+ ```
61
+ GET https://slack.com/api/conversations.history
62
+ ?channel=CHANNEL_ID
63
+ &oldest=UNIX_TIMESTAMP (exclusive - messages strictly after this)
64
+ &limit=100
65
+ &inclusive=false
66
+ ```
67
+
68
+ Returns a `messages` array. Each message has `ts`, `user`, `text`, `thread_ts`
69
+ (present if the message is in a thread or is a threaded reply).
70
+
71
+ **Bot must be invited to the channel** (or the token must have `channels:history`
72
+ for public channels without joining).
73
+
74
+ ---
75
+
76
+ ### `conversations.replies`
77
+ Fetch replies inside a specific thread newer than a timestamp.
78
+
79
+ ```
80
+ GET https://slack.com/api/conversations.replies
81
+ ?channel=CHANNEL_ID
82
+ &ts=THREAD_ROOT_TS
83
+ &oldest=UNIX_TIMESTAMP
84
+ &limit=100
85
+ &inclusive=false
86
+ ```
87
+
88
+ The first item in `messages` is always the parent message - the script drops it
89
+ when comparing `ts == thread_root_ts`.
90
+
91
+ ---
92
+
93
+ ### `search.messages`
94
+ Search for messages matching a query across channels (user token only).
95
+
96
+ ```
97
+ GET https://slack.com/api/search.messages
98
+ ?query=QUERY_STRING
99
+ &count=100
100
+ &sort=timestamp
101
+ &sort_dir=asc
102
+ ```
103
+
104
+ **Query syntax used by this skill:**
105
+
106
+ ```
107
+ "@openhands" in:<#C0123456> in:<#C9876543> after:2026-01-01
108
+ ```
109
+
110
+ - `in:<#CHANNEL_ID>` - restrict to a channel (channel ID or name both work)
111
+ - `after:YYYY-MM-DD` - date-level precision only (the script post-filters by
112
+ precise Unix timestamp)
113
+ - Phrase in quotes - exact match
114
+
115
+ **Limitations:**
116
+ - Date-only precision for `after:` - cannot filter to the minute
117
+ - Results sorted by relevance by default; use `sort=timestamp` to get chronological order
118
+ - `count` max is 100 per page (pagination supported via `page` parameter)
119
+ - Requires `search:read` scope - not available to bot tokens
120
+
121
+ ---
122
+
123
+ ### `reactions.add`
124
+ Add an emoji reaction to a message.
125
+
126
+ ```
127
+ POST https://slack.com/api/reactions.add
128
+ {
129
+ "channel": "CHANNEL_ID",
130
+ "name": "eyes",
131
+ "timestamp": "MESSAGE_TS"
132
+ }
133
+ ```
134
+
135
+ Error `already_reacted` is safe to ignore.
136
+
137
+ ---
138
+
139
+ ### `chat.postMessage`
140
+ Post a message to a channel, optionally within a thread.
141
+
142
+ ```
143
+ POST https://slack.com/api/chat.postMessage
144
+ {
145
+ "channel": "CHANNEL_ID",
146
+ "text": "Message text",
147
+ "thread_ts": "THREAD_ROOT_TS" // omit for top-level messages
148
+ }
149
+ ```
150
+
151
+ Returns `ts` of the posted message - **store this in `bot_message_ts`** in the
152
+ state file to prevent the bot from processing its own messages.
153
+
154
+ ---
155
+
156
+ ### `conversations.list`
157
+ List channels visible to the token (used to resolve names → IDs during setup).
158
+
159
+ ```
160
+ GET https://slack.com/api/conversations.list
161
+ ?types=public_channel,private_channel
162
+ &limit=200
163
+ &exclude_archived=true
164
+ ```
165
+
166
+ Supports cursor-based pagination via the `response_metadata.next_cursor` field.
167
+
168
+ ---
169
+
170
+ ### `auth.test`
171
+ Verify a token and retrieve the associated user/bot ID.
172
+
173
+ ```
174
+ GET https://slack.com/api/auth.test
175
+ ```
176
+
177
+ Returns `user_id` (used by the script to detect and skip its own messages).
178
+
179
+ ---
180
+
181
+ ## Common Errors
182
+
183
+ | Error | Cause | Fix |
184
+ |-------|-------|-----|
185
+ | `not_in_channel` | Bot hasn't been invited | `/invite @botname` in the channel |
186
+ | `missing_scope` | Token lacks a required scope | Re-install the Slack app with the correct scopes |
187
+ | `channel_not_found` | Channel ID is wrong | Use `conversations.list` to verify the ID |
188
+ | `ratelimited` | Too many API calls | Slack allows ~50 requests/min per token; < 10 channels is well within limits |
189
+ | `invalid_auth` | Token expired or revoked | Regenerate the token and update the secret |
190
+
191
+ ---
192
+
193
+ ## Rate Limits
194
+
195
+ Slack applies per-method rate limits (Tier 2 = ~20 req/min, Tier 3 = ~50 req/min).
196
+ With < 10 channels polled every minute:
197
+
198
+ | Method | Tier | Calls/min | Headroom |
199
+ |--------|------|-----------|---------|
200
+ | `conversations.history` | Tier 3 | ≤ 10 | Comfortable |
201
+ | `conversations.replies` | Tier 3 | ≤ active threads | Fine unless hundreds of threads |
202
+ | `search.messages` | Tier 2 | 1 | Fine |
203
+ | `reactions.add` | Tier 2 | ≤ triggers/min | Fine |
204
+ | `chat.postMessage` | Tier 3 | ≤ triggers + summaries | Fine |
205
+
206
+ No rate-limit handling is implemented in the script. If you hit limits the
207
+ run will fail and retry on the next cron tick.
@@ -0,0 +1,180 @@
1
+ # State File Schema
2
+
3
+ The automation maintains a JSON state file that persists across polling runs.
4
+ This file is the source of truth for which conversations are active, which
5
+ timestamps have been processed, and which messages were posted by the bot.
6
+
7
+ ---
8
+
9
+ ## File Location
10
+
11
+ ```
12
+ {WORKSPACE_BASE_ROOT}/automation-state/slack_poller_{automation_id}.json
13
+ ```
14
+
15
+ Where `WORKSPACE_BASE_ROOT` is derived by going two levels up from the
16
+ `WORKSPACE_BASE` environment variable (stripping `automation-runs/{run_id}`).
17
+
18
+ Example on a local install:
19
+
20
+ ```
21
+ ~/.openhands/workspaces/automation-state/slack_poller_abc12345-….json
22
+ ```
23
+
24
+ The `automation_id` is read from the `AUTOMATION_EVENT_PAYLOAD` environment
25
+ variable (field `automation_id`).
26
+
27
+ ---
28
+
29
+ ## Top-Level Schema
30
+
31
+ ```jsonc
32
+ {
33
+ "version": 1, // schema version (integer)
34
+ "bot_user_id": "UBOTID123", // Slack user_id of the bot/token owner
35
+ // cached from auth.test; null until first run
36
+ "last_poll": {
37
+ "C0123456789": "1716576000.123456" // channel_id → float Unix timestamp (string)
38
+ // set to (now - POLL_OVERLAP_SECONDS) at the
39
+ // END of each run; pinned back if triggers fail
40
+ },
41
+ "conversations": { ... }, // see ConversationRecord below
42
+ "bot_message_ts": [ // rolling list of Slack 'ts' values for
43
+ "1716576100.000200" // messages THIS bot posted; used to skip
44
+ ], // self-messages during processing
45
+ "processed_ts": [ // rolling list of message ts values that have
46
+ "1716576050.000100" // already been fully handled (dedup across the
47
+ ] // overlap window between iterations)
48
+ }
49
+ ```
50
+
51
+ ---
52
+
53
+ ## `conversations` Map
54
+
55
+ Key: `"{channel_id}:{thread_root_ts}"` - uniquely identifies a Slack thread.
56
+
57
+ Value: **ConversationRecord**
58
+
59
+ ```jsonc
60
+ {
61
+ // Required fields
62
+ "conversation_id": "550e8400-e29b-41d4-a716-446655440000",
63
+ // OpenHands conversation UUID
64
+ "channel_id": "C0123456789", // Slack channel ID
65
+ "thread_ts": "1716576000.000100",
66
+ // Slack thread root timestamp
67
+ // (= msg_ts for top-level trigger messages)
68
+ "status": "active", // "active" | "closed"
69
+ "last_activity": 1716576060.0, // float Unix timestamp of the last time the
70
+ // script sent a message to this conversation
71
+ // (creation time, or time a reply was forwarded)
72
+ }
73
+ ```
74
+
75
+ ### `status` values
76
+
77
+ | Value | Meaning |
78
+ |-------|---------|
79
+ | `active` | Conversation is running or awaiting more input; replies will be forwarded |
80
+ | `closed` | Summary has been posted to Slack; no further processing |
81
+
82
+ Closed conversations are retained in the map indefinitely (the map stays small
83
+ since there are < 10 channels and the trigger rate is typically low). If the
84
+ map grows unexpectedly, closed entries older than a configurable TTL can be
85
+ pruned.
86
+
87
+ ---
88
+
89
+ ## `bot_message_ts` List
90
+
91
+ A rolling list (max `MAX_BOT_TS = 2000` entries) of Slack `ts` values for
92
+ messages posted BY the bot. This prevents the script from treating its own
93
+ replies as user messages.
94
+
95
+ Entries are added when:
96
+ - The bot posts a conversation link (on trigger detection)
97
+ - The bot posts a summary (on conversation completion)
98
+
99
+ ---
100
+
101
+ ## `processed_ts` List
102
+
103
+ A rolling list (max `MAX_PROCESSED_TS = 2000` entries) of Slack `ts` values
104
+ for messages that have already been fully handled by this script.
105
+
106
+ Because `last_poll` is set to `(now - POLL_OVERLAP_SECONDS)` rather than
107
+ exactly `now`, messages near the boundary are re-fetched on the next iteration.
108
+ `processed_ts` provides a second deduplication layer that prevents these
109
+ re-fetched messages from being processed twice (e.g., triggering a duplicate
110
+ conversation or forwarding the same reply multiple times).
111
+
112
+ Entries are added when a message is either skipped (not human, already handled)
113
+ or successfully processed (trigger detected and conversation created, or reply
114
+ forwarded).
115
+
116
+ ---
117
+
118
+ ## Transition Diagram
119
+
120
+ ```
121
+ [trigger detected]
122
+
123
+
124
+ status = "active"
125
+ last_activity = now
126
+
127
+ (next run or later runs)
128
+
129
+ ┌─────┴──────────────────────────────────────────┐
130
+ │ User sends a reply in the thread │
131
+ │ → send_to_conversation() called │
132
+ │ → last_activity = now │
133
+ └─────────────────────────────────────────────────┘
134
+
135
+ (when time.time() - last_activity > DONE_DEBOUNCE
136
+ AND conversation_status ∈ {idle, finished, error, stuck})
137
+
138
+
139
+ Post summary to Slack thread
140
+ status = "closed"
141
+ ```
142
+
143
+ ---
144
+
145
+ ## Example State File
146
+
147
+ ```json
148
+ {
149
+ "version": 1,
150
+ "bot_user_id": "U04AB1CDEF",
151
+ "last_poll": {
152
+ "C0123456789": "1716576060.000000",
153
+ "C9876543210": "1716576060.000000"
154
+ },
155
+ "conversations": {
156
+ "C0123456789:1716575900.000100": {
157
+ "conversation_id": "550e8400-e29b-41d4-a716-446655440000",
158
+ "channel_id": "C0123456789",
159
+ "thread_ts": "1716575900.000100",
160
+ "status": "active",
161
+ "last_activity": 1716575902.3
162
+ },
163
+ "C9876543210:1716570000.000500": {
164
+ "conversation_id": "7c9e6679-7425-40de-944b-e07fc1f90ae7",
165
+ "channel_id": "C9876543210",
166
+ "thread_ts": "1716570000.000500",
167
+ "status": "closed",
168
+ "last_activity": 1716572100.0
169
+ }
170
+ },
171
+ "bot_message_ts": [
172
+ "1716575903.000200",
173
+ "1716572105.000100"
174
+ ],
175
+ "processed_ts": [
176
+ "1716575900.000100",
177
+ "1716572000.000500"
178
+ ]
179
+ }
180
+ ```