@openhands/extensions 0.20.0 → 0.22.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 (305) hide show
  1. package/.claude-plugin/marketplace.json +881 -0
  2. package/.codex-plugin/marketplace.json +881 -0
  3. package/.github/workflows/check-extensions.yml +4 -0
  4. package/.github/workflows/npm-publish.yml +9 -3
  5. package/.github/workflows/pr-artifacts.yml +294 -0
  6. package/.github/workflows/pr-description-check.yml +4 -2
  7. package/.plugin/marketplace.json +881 -0
  8. package/.release-please-manifest.json +1 -1
  9. package/AGENTS.md +27 -3
  10. package/README.md +6 -2
  11. package/automations/bundle-index.js +22 -3
  12. package/automations/catalog/github-agents-md-maintainer/manifest.json +2 -1
  13. package/automations/catalog/github-delivery-watchdog/manifest.json +79 -0
  14. package/automations/catalog/github-issue-to-pr/manifest.json +24 -15
  15. package/automations/catalog/github-issue-triage/manifest.json +73 -0
  16. package/automations/catalog/github-pr-reviewer/manifest.json +23 -15
  17. package/automations/catalog/gitlab-issue-to-mr/manifest.json +124 -0
  18. package/automations/catalog-index.js +17 -11
  19. package/integrations/catalog/reportportal.json +76 -0
  20. package/integrations/catalog-index.js +6 -4
  21. package/marketplaces/openhands-extensions.json +42 -0
  22. package/package.json +1 -1
  23. package/plugins/city-weather/.claude-plugin/plugin.json +13 -0
  24. package/plugins/city-weather/.codex-plugin/plugin.json +13 -0
  25. package/plugins/cobol-modernization/.claude-plugin/plugin.json +19 -0
  26. package/plugins/cobol-modernization/.codex-plugin/plugin.json +19 -0
  27. package/plugins/issue-duplicate-checker/.claude-plugin/plugin.json +13 -0
  28. package/plugins/issue-duplicate-checker/.codex-plugin/plugin.json +13 -0
  29. package/plugins/magic-test/.claude-plugin/plugin.json +13 -0
  30. package/plugins/magic-test/.codex-plugin/plugin.json +13 -0
  31. package/plugins/migration-scoring/.claude-plugin/plugin.json +19 -0
  32. package/plugins/migration-scoring/.codex-plugin/plugin.json +19 -0
  33. package/plugins/onboarding/.claude-plugin/plugin.json +20 -0
  34. package/plugins/onboarding/.codex-plugin/plugin.json +20 -0
  35. package/plugins/onboarding/skills/agent-readiness-report/references/criteria.md +144 -0
  36. package/plugins/onboarding/skills/improve-agent-readiness/references/criteria.md +144 -0
  37. package/plugins/openhands/.claude-plugin/plugin.json +13 -0
  38. package/plugins/openhands/.codex-plugin/plugin.json +13 -0
  39. package/plugins/openhands/skills/openhands-api/.claude-plugin/plugin.json +22 -0
  40. package/plugins/openhands/skills/openhands-api/.codex-plugin/plugin.json +22 -0
  41. package/plugins/openhands/skills/openhands-api/.plugin/plugin.json +22 -0
  42. package/plugins/openhands/skills/openhands-api/README.md +52 -0
  43. package/plugins/openhands/skills/openhands-api/SKILL.md +469 -0
  44. package/plugins/openhands/skills/openhands-api/references/README.md +33 -0
  45. package/plugins/openhands/skills/openhands-api/references/TROUBLESHOOTING.md +81 -0
  46. package/plugins/openhands/skills/openhands-api/references/example_prompt.md +12 -0
  47. package/plugins/openhands/skills/openhands-api/references/windows.md +44 -0
  48. package/plugins/openhands/skills/openhands-api/scripts/openhands_api.py +606 -0
  49. package/plugins/openhands/skills/openhands-api/scripts/openhands_api.ts +252 -0
  50. package/plugins/openhands/skills/openhands-automation/.claude-plugin/plugin.json +19 -0
  51. package/plugins/openhands/skills/openhands-automation/.codex-plugin/plugin.json +19 -0
  52. package/plugins/openhands/skills/openhands-automation/.plugin/plugin.json +19 -0
  53. package/plugins/openhands/skills/openhands-automation/README.md +89 -0
  54. package/plugins/openhands/skills/openhands-automation/SKILL.md +899 -0
  55. package/plugins/openhands/skills/openhands-automation/commands/automation-create.md +8 -0
  56. package/plugins/openhands/skills/openhands-automation/references/ab-testing.md +185 -0
  57. package/plugins/openhands/skills/openhands-automation/references/custom-automation.md +832 -0
  58. package/plugins/openhands/skills/openhands-automation/references/security.md +84 -0
  59. package/plugins/openhands/skills/openhands-automation/references/windows.md +57 -0
  60. package/plugins/openhands/skills/openhands-sdk/.claude-plugin/plugin.json +20 -0
  61. package/plugins/openhands/skills/openhands-sdk/.codex-plugin/plugin.json +20 -0
  62. package/plugins/openhands/skills/openhands-sdk/.plugin/plugin.json +20 -0
  63. package/plugins/openhands/skills/openhands-sdk/README.md +22 -0
  64. package/plugins/openhands/skills/openhands-sdk/SKILL.md +245 -0
  65. package/plugins/openhands/skills/openhands-sdk/commands/sdk.md +8 -0
  66. package/plugins/pr-review/.claude-plugin/plugin.json +13 -0
  67. package/plugins/pr-review/.codex-plugin/plugin.json +13 -0
  68. package/plugins/pr-review/skills/code-review/.claude-plugin/plugin.json +19 -0
  69. package/plugins/pr-review/skills/code-review/.codex-plugin/plugin.json +19 -0
  70. package/plugins/pr-review/skills/code-review/.plugin/plugin.json +19 -0
  71. package/plugins/pr-review/skills/code-review/README.md +18 -0
  72. package/plugins/pr-review/skills/code-review/SKILL.md +209 -0
  73. package/plugins/pr-review/skills/code-review/commands/codereview-roasted.md +8 -0
  74. package/plugins/pr-review/skills/code-review/commands/codereview.md +8 -0
  75. package/plugins/pr-review/skills/code-review/references/risk-evaluation.md +41 -0
  76. package/plugins/pr-review/skills/code-review/references/supply-chain-security.md +31 -0
  77. package/plugins/pr-review/skills/github-pr-review/.claude-plugin/plugin.json +18 -0
  78. package/plugins/pr-review/skills/github-pr-review/.codex-plugin/plugin.json +18 -0
  79. package/plugins/pr-review/skills/github-pr-review/.plugin/plugin.json +18 -0
  80. package/plugins/pr-review/skills/github-pr-review/README.md +145 -0
  81. package/plugins/pr-review/skills/github-pr-review/SKILL.md +187 -0
  82. package/plugins/pr-review/skills/github-pr-review/commands/github-pr-review.md +8 -0
  83. package/plugins/pr-review/skills/github-pr-review/references/windows.md +47 -0
  84. package/plugins/qa-changes/.claude-plugin/plugin.json +11 -0
  85. package/plugins/qa-changes/.codex-plugin/plugin.json +11 -0
  86. package/plugins/qa-changes/skills/github-pr-review/.claude-plugin/plugin.json +18 -0
  87. package/plugins/qa-changes/skills/github-pr-review/.codex-plugin/plugin.json +18 -0
  88. package/plugins/qa-changes/skills/github-pr-review/.plugin/plugin.json +18 -0
  89. package/plugins/qa-changes/skills/github-pr-review/README.md +145 -0
  90. package/plugins/qa-changes/skills/github-pr-review/SKILL.md +187 -0
  91. package/plugins/qa-changes/skills/github-pr-review/commands/github-pr-review.md +8 -0
  92. package/plugins/qa-changes/skills/github-pr-review/references/windows.md +47 -0
  93. package/plugins/qa-changes/skills/qa-changes/README.md +18 -0
  94. package/plugins/qa-changes/skills/qa-changes/SKILL.md +229 -0
  95. package/plugins/qa-changes/skills/qa-changes/commands/qa-changes.md +8 -0
  96. package/plugins/release-notes/.claude-plugin/plugin.json +19 -0
  97. package/plugins/release-notes/.codex-plugin/plugin.json +19 -0
  98. package/plugins/release-notes/skills/release-notes/README.md +24 -0
  99. package/plugins/release-notes/skills/release-notes/SKILL.md +19 -0
  100. package/plugins/release-notes/skills/release-notes/commands/release-notes.md +8 -0
  101. package/plugins/vulnerability-remediation/.claude-plugin/plugin.json +19 -0
  102. package/plugins/vulnerability-remediation/.codex-plugin/plugin.json +19 -0
  103. package/plugins/vulnerability-remediation/skills/github/.claude-plugin/plugin.json +19 -0
  104. package/plugins/vulnerability-remediation/skills/github/.codex-plugin/plugin.json +19 -0
  105. package/plugins/vulnerability-remediation/skills/github/.plugin/plugin.json +19 -0
  106. package/plugins/vulnerability-remediation/skills/github/README.md +42 -0
  107. package/plugins/vulnerability-remediation/skills/github/SKILL.md +107 -0
  108. package/plugins/vulnerability-remediation/skills/github/references/windows.md +48 -0
  109. package/plugins/vulnerability-remediation/skills/github/scripts/agent_conversation.py +143 -0
  110. package/plugins/vulnerability-remediation/skills/github/scripts/github_client.py +230 -0
  111. package/plugins/vulnerability-remediation/skills/security/.claude-plugin/plugin.json +18 -0
  112. package/plugins/vulnerability-remediation/skills/security/.codex-plugin/plugin.json +18 -0
  113. package/plugins/vulnerability-remediation/skills/security/.plugin/plugin.json +18 -0
  114. package/plugins/vulnerability-remediation/skills/security/README.md +38 -0
  115. package/plugins/vulnerability-remediation/skills/security/SKILL.md +33 -0
  116. package/pyproject.toml +2 -2
  117. package/python/openhands_extensions/_version.py +1 -1
  118. package/skills/add-javadoc/.claude-plugin/plugin.json +18 -0
  119. package/skills/add-javadoc/.codex-plugin/plugin.json +18 -0
  120. package/skills/add-skill/.claude-plugin/plugin.json +18 -0
  121. package/skills/add-skill/.codex-plugin/plugin.json +18 -0
  122. package/skills/agent-canvas-environment/.claude-plugin/plugin.json +19 -0
  123. package/skills/agent-canvas-environment/.codex-plugin/plugin.json +19 -0
  124. package/skills/agent-creator/.claude-plugin/plugin.json +20 -0
  125. package/skills/agent-creator/.codex-plugin/plugin.json +20 -0
  126. package/skills/agent-memory/.claude-plugin/plugin.json +18 -0
  127. package/skills/agent-memory/.codex-plugin/plugin.json +18 -0
  128. package/skills/agent-sdk-builder/.claude-plugin/plugin.json +18 -0
  129. package/skills/agent-sdk-builder/.codex-plugin/plugin.json +18 -0
  130. package/skills/azure-devops/.claude-plugin/plugin.json +18 -0
  131. package/skills/azure-devops/.codex-plugin/plugin.json +18 -0
  132. package/skills/bitbucket/.claude-plugin/plugin.json +17 -0
  133. package/skills/bitbucket/.codex-plugin/plugin.json +17 -0
  134. package/skills/bitbucket-cloud/.claude-plugin/plugin.json +18 -0
  135. package/skills/bitbucket-cloud/.codex-plugin/plugin.json +18 -0
  136. package/skills/bitbucket-data-center/.claude-plugin/plugin.json +19 -0
  137. package/skills/bitbucket-data-center/.codex-plugin/plugin.json +19 -0
  138. package/skills/code-review/.claude-plugin/plugin.json +19 -0
  139. package/skills/code-review/.codex-plugin/plugin.json +19 -0
  140. package/skills/code-simplifier/.claude-plugin/plugin.json +21 -0
  141. package/skills/code-simplifier/.codex-plugin/plugin.json +21 -0
  142. package/skills/datadog/.claude-plugin/plugin.json +19 -0
  143. package/skills/datadog/.codex-plugin/plugin.json +19 -0
  144. package/skills/deno/.claude-plugin/plugin.json +18 -0
  145. package/skills/deno/.codex-plugin/plugin.json +18 -0
  146. package/skills/discord/.claude-plugin/plugin.json +18 -0
  147. package/skills/discord/.codex-plugin/plugin.json +18 -0
  148. package/skills/docker/.claude-plugin/plugin.json +17 -0
  149. package/skills/docker/.codex-plugin/plugin.json +17 -0
  150. package/skills/evidence-based-citations/.claude-plugin/plugin.json +20 -0
  151. package/skills/evidence-based-citations/.codex-plugin/plugin.json +20 -0
  152. package/skills/flarglebargle/.claude-plugin/plugin.json +16 -0
  153. package/skills/flarglebargle/.codex-plugin/plugin.json +16 -0
  154. package/skills/frontend-design/.claude-plugin/plugin.json +21 -0
  155. package/skills/frontend-design/.codex-plugin/plugin.json +21 -0
  156. package/skills/github/.claude-plugin/plugin.json +19 -0
  157. package/skills/github/.codex-plugin/plugin.json +19 -0
  158. package/skills/github/scripts/agent_conversation.py +143 -0
  159. package/skills/github/scripts/github_client.py +230 -0
  160. package/skills/github-actions/.claude-plugin/plugin.json +19 -0
  161. package/skills/github-actions/.codex-plugin/plugin.json +19 -0
  162. package/skills/github-agents-md-maintainer/.claude-plugin/plugin.json +19 -0
  163. package/skills/github-agents-md-maintainer/.codex-plugin/plugin.json +19 -0
  164. package/skills/github-agents-md-maintainer/SKILL.md +7 -1
  165. package/skills/github-agents-md-maintainer/scripts/github_client.py +230 -0
  166. package/skills/github-agents-md-maintainer/scripts/main.py +4 -40
  167. package/skills/github-delivery-watchdog/.claude-plugin/plugin.json +9 -0
  168. package/skills/github-delivery-watchdog/.codex-plugin/plugin.json +9 -0
  169. package/skills/github-delivery-watchdog/.plugin/plugin.json +9 -0
  170. package/skills/github-delivery-watchdog/README.md +10 -0
  171. package/skills/github-delivery-watchdog/SKILL.md +35 -0
  172. package/skills/github-delivery-watchdog/commands/github-delivery-watchdog.md +8 -0
  173. package/skills/github-delivery-watchdog/scripts/github_client.py +230 -0
  174. package/skills/github-delivery-watchdog/scripts/worker.py +96 -0
  175. package/skills/github-issue-to-pr/.claude-plugin/plugin.json +19 -0
  176. package/skills/github-issue-to-pr/.codex-plugin/plugin.json +19 -0
  177. package/skills/github-issue-to-pr/SKILL.md +15 -1
  178. package/skills/github-issue-to-pr/scripts/agent_conversation.py +143 -0
  179. package/skills/github-issue-to-pr/scripts/github_client.py +230 -0
  180. package/skills/github-issue-to-pr/scripts/main.py +19 -50
  181. package/skills/github-issue-to-pr/scripts/worker.py +172 -0
  182. package/skills/github-issue-triage/.claude-plugin/plugin.json +9 -0
  183. package/skills/github-issue-triage/.codex-plugin/plugin.json +9 -0
  184. package/skills/github-issue-triage/.plugin/plugin.json +9 -0
  185. package/skills/github-issue-triage/README.md +9 -0
  186. package/skills/github-issue-triage/SKILL.md +34 -0
  187. package/skills/github-issue-triage/commands/github-issue-triage.md +8 -0
  188. package/skills/github-issue-triage/scripts/agent_conversation.py +143 -0
  189. package/skills/github-issue-triage/scripts/github_client.py +230 -0
  190. package/skills/github-issue-triage/scripts/worker.py +106 -0
  191. package/skills/github-pr-review/.claude-plugin/plugin.json +18 -0
  192. package/skills/github-pr-review/.codex-plugin/plugin.json +18 -0
  193. package/skills/github-pr-reviewer/.claude-plugin/plugin.json +20 -0
  194. package/skills/github-pr-reviewer/.codex-plugin/plugin.json +20 -0
  195. package/skills/github-pr-reviewer/SKILL.md +15 -1
  196. package/skills/github-pr-reviewer/scripts/agent_conversation.py +143 -0
  197. package/skills/github-pr-reviewer/scripts/github_client.py +230 -0
  198. package/skills/github-pr-reviewer/scripts/main.py +23 -49
  199. package/skills/github-pr-reviewer/scripts/worker.py +94 -0
  200. package/skills/github-repo-monitor/.claude-plugin/plugin.json +22 -0
  201. package/skills/github-repo-monitor/.codex-plugin/plugin.json +22 -0
  202. package/skills/github-repo-monitor/SKILL.md +7 -1
  203. package/skills/github-repo-monitor/scripts/github_client.py +230 -0
  204. package/skills/github-repo-monitor/scripts/main.py +4 -46
  205. package/skills/gitlab/.claude-plugin/plugin.json +17 -0
  206. package/skills/gitlab/.codex-plugin/plugin.json +17 -0
  207. package/skills/gitlab-issue-to-mr/.claude-plugin/plugin.json +19 -0
  208. package/skills/gitlab-issue-to-mr/.codex-plugin/plugin.json +19 -0
  209. package/skills/gitlab-issue-to-mr/.plugin/plugin.json +19 -0
  210. package/skills/gitlab-issue-to-mr/README.md +92 -0
  211. package/skills/gitlab-issue-to-mr/SKILL.md +408 -0
  212. package/skills/gitlab-issue-to-mr/commands/issue-to-mr-setup.md +8 -0
  213. package/skills/gitlab-issue-to-mr/references/state-schema.md +140 -0
  214. package/skills/gitlab-issue-to-mr/scripts/main.py +1448 -0
  215. package/skills/incident-retrospective/.claude-plugin/plugin.json +21 -0
  216. package/skills/incident-retrospective/.codex-plugin/plugin.json +21 -0
  217. package/skills/index.js +52 -6
  218. package/skills/iterate/.claude-plugin/plugin.json +13 -0
  219. package/skills/iterate/.codex-plugin/plugin.json +13 -0
  220. package/skills/jira-issue-to-pr/.claude-plugin/plugin.json +19 -0
  221. package/skills/jira-issue-to-pr/.codex-plugin/plugin.json +19 -0
  222. package/skills/jupyter/.claude-plugin/plugin.json +18 -0
  223. package/skills/jupyter/.codex-plugin/plugin.json +18 -0
  224. package/skills/kubernetes/.claude-plugin/plugin.json +18 -0
  225. package/skills/kubernetes/.codex-plugin/plugin.json +18 -0
  226. package/skills/learn-from-code-review/.claude-plugin/plugin.json +19 -0
  227. package/skills/learn-from-code-review/.codex-plugin/plugin.json +19 -0
  228. package/skills/linear/.claude-plugin/plugin.json +19 -0
  229. package/skills/linear/.codex-plugin/plugin.json +19 -0
  230. package/skills/linear-triage/.claude-plugin/plugin.json +21 -0
  231. package/skills/linear-triage/.codex-plugin/plugin.json +21 -0
  232. package/skills/news-digest/.claude-plugin/plugin.json +19 -0
  233. package/skills/news-digest/.codex-plugin/plugin.json +19 -0
  234. package/skills/notion/.claude-plugin/plugin.json +17 -0
  235. package/skills/notion/.codex-plugin/plugin.json +17 -0
  236. package/skills/npm/.claude-plugin/plugin.json +17 -0
  237. package/skills/npm/.codex-plugin/plugin.json +17 -0
  238. package/skills/openhands-api/.claude-plugin/plugin.json +22 -0
  239. package/skills/openhands-api/.codex-plugin/plugin.json +22 -0
  240. package/skills/openhands-automation/.claude-plugin/plugin.json +19 -0
  241. package/skills/openhands-automation/.codex-plugin/plugin.json +19 -0
  242. package/skills/openhands-automation/README.md +1 -1
  243. package/skills/openhands-automation/SKILL.md +18 -8
  244. package/skills/openhands-enterprise-troubleshooting/.claude-plugin/plugin.json +20 -0
  245. package/skills/openhands-enterprise-troubleshooting/.codex-plugin/plugin.json +20 -0
  246. package/skills/openhands-enterprise-troubleshooting/.plugin/plugin.json +20 -0
  247. package/skills/openhands-enterprise-troubleshooting/README.md +79 -0
  248. package/skills/openhands-enterprise-troubleshooting/SKILL.md +339 -0
  249. package/skills/openhands-enterprise-troubleshooting/references/diagnostics.md +698 -0
  250. package/skills/openhands-enterprise-troubleshooting/references/support-bundle-analysis.md +570 -0
  251. package/skills/openhands-enterprise-troubleshooting/scripts/bundle_triage.py +532 -0
  252. package/skills/openhands-sdk/.claude-plugin/plugin.json +20 -0
  253. package/skills/openhands-sdk/.codex-plugin/plugin.json +20 -0
  254. package/skills/openhands-sdk/SKILL.md +2 -0
  255. package/skills/pdflatex/.claude-plugin/plugin.json +18 -0
  256. package/skills/pdflatex/.codex-plugin/plugin.json +18 -0
  257. package/skills/plain-english-content/.claude-plugin/plugin.json +15 -0
  258. package/skills/plain-english-content/.codex-plugin/plugin.json +15 -0
  259. package/skills/prd/.claude-plugin/plugin.json +19 -0
  260. package/skills/prd/.codex-plugin/plugin.json +19 -0
  261. package/skills/research-brief/.claude-plugin/plugin.json +20 -0
  262. package/skills/research-brief/.codex-plugin/plugin.json +20 -0
  263. package/skills/security/.claude-plugin/plugin.json +18 -0
  264. package/skills/security/.codex-plugin/plugin.json +18 -0
  265. package/skills/skill-creator/.claude-plugin/plugin.json +17 -0
  266. package/skills/skill-creator/.codex-plugin/plugin.json +17 -0
  267. package/skills/slack-channel-monitor/.claude-plugin/plugin.json +21 -0
  268. package/skills/slack-channel-monitor/.codex-plugin/plugin.json +21 -0
  269. package/skills/slack-standup-digest/.claude-plugin/plugin.json +21 -0
  270. package/skills/slack-standup-digest/.codex-plugin/plugin.json +21 -0
  271. package/skills/spark-version-upgrade/.claude-plugin/plugin.json +20 -0
  272. package/skills/spark-version-upgrade/.codex-plugin/plugin.json +20 -0
  273. package/skills/ssh/.claude-plugin/plugin.json +18 -0
  274. package/skills/ssh/.codex-plugin/plugin.json +18 -0
  275. package/skills/swift-linux/.claude-plugin/plugin.json +17 -0
  276. package/skills/swift-linux/.codex-plugin/plugin.json +17 -0
  277. package/skills/technical-writing/.claude-plugin/plugin.json +15 -0
  278. package/skills/technical-writing/.codex-plugin/plugin.json +15 -0
  279. package/skills/theme-factory/.claude-plugin/plugin.json +19 -0
  280. package/skills/theme-factory/.codex-plugin/plugin.json +19 -0
  281. package/skills/ticket-to-code-change/.claude-plugin/plugin.json +21 -0
  282. package/skills/ticket-to-code-change/.codex-plugin/plugin.json +21 -0
  283. package/skills/upstream-fork-sync/.claude-plugin/plugin.json +21 -0
  284. package/skills/upstream-fork-sync/.codex-plugin/plugin.json +21 -0
  285. package/skills/uv/.claude-plugin/plugin.json +18 -0
  286. package/skills/uv/.codex-plugin/plugin.json +18 -0
  287. package/skills/vercel/.claude-plugin/plugin.json +18 -0
  288. package/skills/vercel/.codex-plugin/plugin.json +18 -0
  289. package/tests/fixtures/automations/capabilities.json +6 -3
  290. package/tests/fixtures/automations/github-pr-reviewer.json +64 -43
  291. package/tests/github_automation_helpers.py +31 -0
  292. package/tests/test_agent_conversation_dispatch.py +180 -0
  293. package/tests/test_automation_setup.py +3 -3
  294. package/tests/test_github_automation_foundation.py +73 -0
  295. package/tests/test_github_developer_delivery.py +150 -0
  296. package/tests/test_github_reviewer_delivery.py +110 -0
  297. package/tests/test_github_skill_installation.py +63 -0
  298. package/tests/test_github_triage_delivery.py +90 -0
  299. package/tests/test_github_watchdog_delivery.py +233 -0
  300. package/tests/test_gitlab_issue_to_mr.py +823 -0
  301. package/.pr/custom-automation-event-trigger.png +0 -0
  302. package/.pr/custom-automation-plugin-fields.png +0 -0
  303. package/.pr/custom-automation-prompt-review.png +0 -0
  304. package/.pr/custom-automation-prompt-scheduled.png +0 -0
  305. package/.pr/custom-automation-upload-fields.png +0 -0
@@ -17,7 +17,8 @@
17
17
  "timezone": "UTC",
18
18
  "repositories": [
19
19
  "OpenHands/agent-server-gui"
20
- ]
20
+ ],
21
+ "githubTokenSecret": "GITHUB_PERSONAL_ACCESS_TOKEN"
21
22
  },
22
23
  "upload": {
23
24
  "request": {
@@ -29,7 +30,9 @@
29
30
  "contentType": "application/gzip",
30
31
  "packs": [
31
32
  "config.json",
32
- "main.py"
33
+ "main.py",
34
+ "github_client.py",
35
+ "worker.py"
33
36
  ]
34
37
  },
35
38
  "response": {
@@ -57,17 +60,18 @@
57
60
  "timezone": "UTC"
58
61
  },
59
62
  "tarball_path": "oh-internal://uploads/00000000-0000-0000-0000-000000000000",
60
- "entrypoint": "python3 main.py",
61
- "timeout": 600,
63
+ "entrypoint": "python3 worker.py",
64
+ "timeout": 300,
62
65
  "template": {
63
66
  "id": "github-pr-reviewer",
64
- "version": "1.0.0",
67
+ "version": "1.1.0",
65
68
  "config": {
66
69
  "repos": [
67
70
  "OpenHands/agent-server-gui"
68
71
  ],
69
72
  "trigger_label": "openhands-review",
70
- "review_tone": "thorough"
73
+ "review_tone": "thorough",
74
+ "github_token_secret": "GITHUB_PERSONAL_ACCESS_TOKEN"
71
75
  }
72
76
  }
73
77
  }
@@ -93,17 +97,18 @@
93
97
  "timezone": "UTC"
94
98
  },
95
99
  "tarball_path": "oh-internal://uploads/2c1f9a70-77c4-4a1e-9a9d-6f1f2b0c1d34",
96
- "entrypoint": "python3 main.py",
97
- "timeout": 600,
100
+ "entrypoint": "python3 worker.py",
101
+ "timeout": 300,
98
102
  "template": {
99
103
  "id": "github-pr-reviewer",
100
- "version": "1.0.0",
104
+ "version": "1.1.0",
101
105
  "config": {
102
106
  "repos": [
103
107
  "OpenHands/agent-server-gui"
104
108
  ],
105
109
  "trigger_label": "openhands-review",
106
- "review_tone": "thorough"
110
+ "review_tone": "thorough",
111
+ "github_token_secret": "GITHUB_PERSONAL_ACCESS_TOKEN"
107
112
  }
108
113
  }
109
114
  }
@@ -124,8 +129,8 @@
124
129
  },
125
130
  "tarball_path": "oh-internal://uploads/8d2c1f90-7e34-4a55-b0d1-6c9e3a2f4b88",
126
131
  "setup_script_path": "setup.sh",
127
- "entrypoint": ".venv/bin/python main.py",
128
- "timeout": 600,
132
+ "entrypoint": "python3 worker.py",
133
+ "timeout": 300,
129
134
  "keep_alive": null,
130
135
  "enabled": true,
131
136
  "last_triggered_at": null,
@@ -146,7 +151,8 @@
146
151
  "repositories": [
147
152
  "OpenHands/agent-server-gui",
148
153
  "OpenHands/automation"
149
- ]
154
+ ],
155
+ "githubTokenSecret": "GITHUB_PERSONAL_ACCESS_TOKEN"
150
156
  },
151
157
  "upload": {
152
158
  "request": {
@@ -158,7 +164,9 @@
158
164
  "contentType": "application/gzip",
159
165
  "packs": [
160
166
  "config.json",
161
- "main.py"
167
+ "main.py",
168
+ "github_client.py",
169
+ "worker.py"
162
170
  ]
163
171
  },
164
172
  "response": {
@@ -186,18 +194,19 @@
186
194
  "timezone": "UTC"
187
195
  },
188
196
  "tarball_path": "oh-internal://uploads/00000000-0000-0000-0000-000000000000",
189
- "entrypoint": "python3 main.py",
190
- "timeout": 600,
197
+ "entrypoint": "python3 worker.py",
198
+ "timeout": 300,
191
199
  "template": {
192
200
  "id": "github-pr-reviewer",
193
- "version": "1.0.0",
201
+ "version": "1.1.0",
194
202
  "config": {
195
203
  "repos": [
196
204
  "OpenHands/agent-server-gui",
197
205
  "OpenHands/automation"
198
206
  ],
199
207
  "trigger_label": "openhands-review",
200
- "review_tone": "thorough"
208
+ "review_tone": "thorough",
209
+ "github_token_secret": "GITHUB_PERSONAL_ACCESS_TOKEN"
201
210
  }
202
211
  }
203
212
  }
@@ -223,18 +232,19 @@
223
232
  "timezone": "UTC"
224
233
  },
225
234
  "tarball_path": "oh-internal://uploads/2c1f9a70-77c4-4a1e-9a9d-6f1f2b0c1d34",
226
- "entrypoint": "python3 main.py",
227
- "timeout": 600,
235
+ "entrypoint": "python3 worker.py",
236
+ "timeout": 300,
228
237
  "template": {
229
238
  "id": "github-pr-reviewer",
230
- "version": "1.0.0",
239
+ "version": "1.1.0",
231
240
  "config": {
232
241
  "repos": [
233
242
  "OpenHands/agent-server-gui",
234
243
  "OpenHands/automation"
235
244
  ],
236
245
  "trigger_label": "openhands-review",
237
- "review_tone": "thorough"
246
+ "review_tone": "thorough",
247
+ "github_token_secret": "GITHUB_PERSONAL_ACCESS_TOKEN"
238
248
  }
239
249
  }
240
250
  }
@@ -255,8 +265,8 @@
255
265
  },
256
266
  "tarball_path": "oh-internal://uploads/8d2c1f90-7e34-4a55-b0d1-6c9e3a2f4b88",
257
267
  "setup_script_path": "setup.sh",
258
- "entrypoint": ".venv/bin/python main.py",
259
- "timeout": 600,
268
+ "entrypoint": "python3 worker.py",
269
+ "timeout": 300,
260
270
  "keep_alive": null,
261
271
  "enabled": true,
262
272
  "last_triggered_at": null,
@@ -265,14 +275,15 @@
265
275
  "preset_metadata": {
266
276
  "template": {
267
277
  "id": "github-pr-reviewer",
268
- "version": "1.0.0",
278
+ "version": "1.1.0",
269
279
  "config": {
270
280
  "repos": [
271
281
  "OpenHands/agent-server-gui",
272
282
  "OpenHands/automation"
273
283
  ],
274
284
  "trigger_label": "openhands-review",
275
- "review_tone": "thorough"
285
+ "review_tone": "thorough",
286
+ "github_token_secret": "GITHUB_PERSONAL_ACCESS_TOKEN"
276
287
  }
277
288
  }
278
289
  }
@@ -290,7 +301,8 @@
290
301
  "timezone": "UTC",
291
302
  "repositories": [
292
303
  "OpenHands/agent-server-gui"
293
- ]
304
+ ],
305
+ "githubTokenSecret": "GITHUB_PERSONAL_ACCESS_TOKEN"
294
306
  },
295
307
  "preflight": {
296
308
  "request": {
@@ -307,17 +319,18 @@
307
319
  "timezone": "UTC"
308
320
  },
309
321
  "tarball_path": "oh-internal://uploads/00000000-0000-0000-0000-000000000000",
310
- "entrypoint": "python3 main.py",
311
- "timeout": 600,
322
+ "entrypoint": "python3 worker.py",
323
+ "timeout": 300,
312
324
  "template": {
313
325
  "id": "github-pr-reviewer",
314
- "version": "1.0.0",
326
+ "version": "1.1.0",
315
327
  "config": {
316
328
  "repos": [
317
329
  "OpenHands/agent-server-gui"
318
330
  ],
319
331
  "trigger_label": "openhands-review",
320
- "review_tone": "concise"
332
+ "review_tone": "concise",
333
+ "github_token_secret": "GITHUB_PERSONAL_ACCESS_TOKEN"
321
334
  }
322
335
  }
323
336
  }
@@ -351,7 +364,8 @@
351
364
  "timezone": "UTC",
352
365
  "repositories": [
353
366
  "OpenHands/agent-server-gui"
354
- ]
367
+ ],
368
+ "githubTokenSecret": "GITHUB_PERSONAL_ACCESS_TOKEN"
355
369
  },
356
370
  "upload": {
357
371
  "request": {
@@ -363,7 +377,9 @@
363
377
  "contentType": "application/gzip",
364
378
  "packs": [
365
379
  "config.json",
366
- "main.py"
380
+ "main.py",
381
+ "github_client.py",
382
+ "worker.py"
367
383
  ]
368
384
  },
369
385
  "response": {
@@ -388,17 +404,18 @@
388
404
  "timezone": "UTC"
389
405
  },
390
406
  "tarball_path": "oh-internal://uploads/2c1f9a70-77c4-4a1e-9a9d-6f1f2b0c1d34",
391
- "entrypoint": "python3 main.py",
392
- "timeout": 600,
407
+ "entrypoint": "python3 worker.py",
408
+ "timeout": 300,
393
409
  "template": {
394
410
  "id": "github-pr-reviewer",
395
- "version": "1.0.0",
411
+ "version": "1.1.0",
396
412
  "config": {
397
413
  "repos": [
398
414
  "OpenHands/agent-server-gui"
399
415
  ],
400
416
  "trigger_label": "openhands-review",
401
- "review_tone": "concise"
417
+ "review_tone": "concise",
418
+ "github_token_secret": "GITHUB_PERSONAL_ACCESS_TOKEN"
402
419
  }
403
420
  }
404
421
  }
@@ -435,7 +452,8 @@
435
452
  "timezone": "UTC",
436
453
  "repositories": [
437
454
  "OpenHands/agent-server-gui"
438
- ]
455
+ ],
456
+ "githubTokenSecret": "GITHUB_PERSONAL_ACCESS_TOKEN"
439
457
  },
440
458
  "upload": {
441
459
  "request": {
@@ -447,7 +465,9 @@
447
465
  "contentType": "application/gzip",
448
466
  "packs": [
449
467
  "config.json",
450
- "main.py"
468
+ "main.py",
469
+ "github_client.py",
470
+ "worker.py"
451
471
  ]
452
472
  },
453
473
  "response": {
@@ -472,17 +492,18 @@
472
492
  "timezone": "UTC"
473
493
  },
474
494
  "tarball_path": "oh-internal://uploads/2c1f9a70-77c4-4a1e-9a9d-6f1f2b0c1d34",
475
- "entrypoint": "python3 main.py",
476
- "timeout": 600,
495
+ "entrypoint": "python3 worker.py",
496
+ "timeout": 300,
477
497
  "template": {
478
498
  "id": "github-pr-reviewer",
479
- "version": "1.0.0",
499
+ "version": "1.1.0",
480
500
  "config": {
481
501
  "repos": [
482
502
  "OpenHands/agent-server-gui"
483
503
  ],
484
504
  "trigger_label": "openhands-review",
485
- "review_tone": "concise"
505
+ "review_tone": "concise",
506
+ "github_token_secret": "GITHUB_PERSONAL_ACCESS_TOKEN"
486
507
  }
487
508
  },
488
509
  "repos": [
@@ -0,0 +1,31 @@
1
+ """Exercise the actual shipped entrypoints with mocked GitHub/SDK boundaries."""
2
+
3
+ import importlib.util
4
+ import json
5
+ import sys
6
+ from pathlib import Path
7
+
8
+ ROOT = Path(__file__).resolve().parents[1]
9
+
10
+
11
+ def worker(name, tmp_path, monkeypatch):
12
+ manifest = json.loads(
13
+ (ROOT / f"automations/catalog/{name}/manifest.json").read_text()
14
+ )
15
+ bundle = tmp_path / name
16
+ bundle.mkdir()
17
+ for filename, source in manifest["setup"]["bundle"]["files"].items():
18
+ (bundle / filename).write_text((ROOT / source).read_text())
19
+ for module in (
20
+ "agent_conversation",
21
+ "main",
22
+ "github_client",
23
+ "qa_prompt",
24
+ "worker",
25
+ ):
26
+ monkeypatch.delitem(sys.modules, module, raising=False)
27
+ monkeypatch.syspath_prepend(str(bundle))
28
+ spec = importlib.util.spec_from_file_location("worker", bundle / "worker.py")
29
+ module = importlib.util.module_from_spec(spec)
30
+ spec.loader.exec_module(module)
31
+ return module
@@ -0,0 +1,180 @@
1
+ import json
2
+ from unittest.mock import MagicMock
3
+ from uuid import NAMESPACE_URL, UUID, uuid5
4
+
5
+ import agent_conversation
6
+ import github_client
7
+ from openhands.sdk.conversation.state import ConversationExecutionStatus
8
+
9
+
10
+ def _dispatcher(monkeypatch):
11
+ monkeypatch.setenv("AGENT_SERVER_URL", "http://agent")
12
+ monkeypatch.setenv("SESSION_API_KEY", "session")
13
+ monkeypatch.setenv(
14
+ "AUTOMATION_AGENT_PROFILE_ID", "11111111-1111-4111-8111-111111111111"
15
+ )
16
+ monkeypatch.setenv(
17
+ "AUTOMATION_EVENT_PAYLOAD", json.dumps({"automation_id": "automation-1"})
18
+ )
19
+ return agent_conversation.AgentConversationDispatcher()
20
+
21
+
22
+ def _state_key(subject):
23
+ conversation_id = uuid5(NAMESPACE_URL, f"automation-1:{subject}")
24
+ return f"agent-conversation-{conversation_id}"
25
+
26
+
27
+ def _fake_kv(monkeypatch, state):
28
+ def request(key, method, value=None):
29
+ if method == "GET":
30
+ return state.get(key)
31
+ state[key] = value
32
+ return {"key": key, "value": value}
33
+
34
+ monkeypatch.setattr(agent_conversation, "_kv_request", request)
35
+
36
+
37
+ def test_github_secret_falls_back_to_agent_server(monkeypatch):
38
+ monkeypatch.delenv("REVIEW_TOKEN", raising=False)
39
+ monkeypatch.setenv("AGENT_SERVER_URL", "http://agent")
40
+ monkeypatch.setenv("SESSION_API_KEY", "session")
41
+ secret = MagicMock()
42
+ secret.get_value.return_value = "saved-token"
43
+ workspace = MagicMock()
44
+ workspace.get_secrets.return_value = {"REVIEW_TOKEN": secret}
45
+ monkeypatch.setattr(
46
+ "openhands.sdk.workspace.RemoteWorkspace", lambda **kwargs: workspace
47
+ )
48
+
49
+ assert github_client._load_secret("REVIEW_TOKEN") == "saved-token"
50
+ workspace.get_secrets.assert_called_once_with(["REVIEW_TOKEN"])
51
+ workspace.reset_client.assert_called_once_with()
52
+
53
+
54
+ def test_new_subject_uses_selected_profile_and_persists_mapping(monkeypatch):
55
+ state = {}
56
+ _fake_kv(monkeypatch, state)
57
+ monkeypatch.setattr(agent_conversation, "_register_tools", lambda: None)
58
+ workspace = MagicMock()
59
+ workspace.__enter__.return_value = workspace
60
+ monkeypatch.setattr(
61
+ agent_conversation, "RemoteWorkspace", lambda **kwargs: workspace
62
+ )
63
+ missing = agent_conversation.httpx.HTTPStatusError(
64
+ "missing",
65
+ request=MagicMock(),
66
+ response=MagicMock(status_code=404),
67
+ )
68
+ monkeypatch.setattr(
69
+ agent_conversation.RemoteConversation, "attach", MagicMock(side_effect=missing)
70
+ )
71
+ conversation = MagicMock()
72
+ create = MagicMock(return_value=conversation)
73
+ monkeypatch.setattr(agent_conversation.RemoteConversation, "create", create)
74
+
75
+ with _dispatcher(monkeypatch) as dispatcher:
76
+ result = dispatcher.deliver("repo:issue:7", "revision-1", "work")
77
+
78
+ request = create.call_args.args[1]
79
+ assert request.agent_profile_id == UUID("11111111-1111-4111-8111-111111111111")
80
+ assert request.initial_message.run is True
81
+ assert result["disposition"] == "created"
82
+ record = state[_state_key("repo:issue:7")]
83
+ assert record == {
84
+ "subject": "repo:issue:7",
85
+ "conversation_id": result["conversation_id"],
86
+ "delivery": "revision-1",
87
+ }
88
+
89
+
90
+ def test_known_subject_resumes_once_per_delivery(monkeypatch):
91
+ state = {
92
+ _state_key("repo:pr:9"): {
93
+ "subject": "repo:pr:9",
94
+ "conversation_id": "22222222-2222-4222-8222-222222222222",
95
+ "delivery": "head-1",
96
+ }
97
+ }
98
+ _fake_kv(monkeypatch, state)
99
+ monkeypatch.setattr(agent_conversation, "_register_tools", lambda: None)
100
+ workspace = MagicMock()
101
+ workspace.__enter__.return_value = workspace
102
+ monkeypatch.setattr(
103
+ agent_conversation, "RemoteWorkspace", lambda **kwargs: workspace
104
+ )
105
+ conversation = MagicMock()
106
+ conversation.state.execution_status = ConversationExecutionStatus.FINISHED
107
+ attach = MagicMock(return_value=conversation)
108
+ monkeypatch.setattr(agent_conversation.RemoteConversation, "attach", attach)
109
+ with _dispatcher(monkeypatch) as dispatcher:
110
+ duplicate = dispatcher.deliver("repo:pr:9", "head-1", "old")
111
+ resumed = dispatcher.deliver("repo:pr:9", "head-2", "new")
112
+
113
+ assert duplicate["disposition"] == "deduplicated"
114
+ assert resumed["disposition"] == "resumed"
115
+ conversation.send_message.assert_called_once_with("new")
116
+ conversation.run.assert_called_once_with(blocking=False)
117
+
118
+
119
+ def test_same_delivery_resumes_paused_conversation(monkeypatch):
120
+ state = {
121
+ _state_key("repo:pr:9"): {
122
+ "subject": "repo:pr:9",
123
+ "conversation_id": "22222222-2222-4222-8222-222222222222",
124
+ "delivery": "head-1",
125
+ }
126
+ }
127
+ _fake_kv(monkeypatch, state)
128
+ monkeypatch.setattr(agent_conversation, "_register_tools", lambda: None)
129
+ workspace = MagicMock()
130
+ workspace.__enter__.return_value = workspace
131
+ monkeypatch.setattr(
132
+ agent_conversation, "RemoteWorkspace", lambda **kwargs: workspace
133
+ )
134
+ conversation = MagicMock()
135
+ conversation.state.execution_status = ConversationExecutionStatus.PAUSED
136
+ monkeypatch.setattr(
137
+ agent_conversation.RemoteConversation,
138
+ "attach",
139
+ MagicMock(return_value=conversation),
140
+ )
141
+
142
+ with _dispatcher(monkeypatch) as dispatcher:
143
+ result = dispatcher.deliver("repo:pr:9", "head-1", "old")
144
+
145
+ assert result["disposition"] == "resumed"
146
+ conversation.send_message.assert_not_called()
147
+ conversation.run.assert_called_once_with(blocking=False)
148
+
149
+
150
+ def test_subjects_use_independent_kv_records(monkeypatch):
151
+ state = {}
152
+ _fake_kv(monkeypatch, state)
153
+ monkeypatch.setattr(agent_conversation, "_register_tools", lambda: None)
154
+ workspace = MagicMock()
155
+ workspace.__enter__.return_value = workspace
156
+ monkeypatch.setattr(
157
+ agent_conversation, "RemoteWorkspace", lambda **kwargs: workspace
158
+ )
159
+ missing = agent_conversation.httpx.HTTPStatusError(
160
+ "missing",
161
+ request=MagicMock(),
162
+ response=MagicMock(status_code=404),
163
+ )
164
+ monkeypatch.setattr(
165
+ agent_conversation.RemoteConversation, "attach", MagicMock(side_effect=missing)
166
+ )
167
+ monkeypatch.setattr(
168
+ agent_conversation.RemoteConversation,
169
+ "create",
170
+ MagicMock(side_effect=[MagicMock(), MagicMock()]),
171
+ )
172
+
173
+ with _dispatcher(monkeypatch) as dispatcher:
174
+ dispatcher.deliver("repo:issue:7", "revision-1", "first")
175
+ dispatcher.deliver("repo:issue:8", "revision-1", "second")
176
+
177
+ assert set(state) == {
178
+ _state_key("repo:issue:7"),
179
+ _state_key("repo:issue:8"),
180
+ }
@@ -676,9 +676,9 @@ def test_schema_rejects_content_a_setup_block_must_never_carry() -> None:
676
676
  rejected.append(("{{env.GITHUB_TOKEN}}", with_unknown_placeholder))
677
677
 
678
678
  with_secret_value = deepcopy(entry)
679
- with_secret_value["requires"]["integrations"]["github"]["value"] = (
680
- "ghp_notarealtokenvalue00"
681
- )
679
+ with_secret_value["requires"]["integrations"]["github"] = {
680
+ "value": "ghp_notarealtokenvalue00"
681
+ }
682
682
  rejected.append(("value", with_secret_value))
683
683
 
684
684
  with_repeated_identity = deepcopy(entry)
@@ -0,0 +1,73 @@
1
+ """Contracts for the shared GitHub automation foundation."""
2
+
3
+ import json
4
+ from urllib.error import HTTPError
5
+
6
+ import github_client
7
+ import pytest
8
+
9
+
10
+ def test_repository_runs_are_isolated(tmp_path, monkeypatch):
11
+ monkeypatch.chdir(tmp_path)
12
+ monkeypatch.setattr("sys.argv", ["worker.py"])
13
+ (tmp_path / "config.json").write_text(
14
+ json.dumps(
15
+ {
16
+ "repos": ["owner/failed", "owner/next"],
17
+ "github_token_secret": "GITHUB_TOKEN",
18
+ }
19
+ )
20
+ )
21
+ visited = []
22
+
23
+ class Automation:
24
+ def __init__(self, *, repository, github_token_secret, conversation):
25
+ assert github_token_secret == "GITHUB_TOKEN"
26
+ self.repository = repository
27
+
28
+ def run(self):
29
+ visited.append(self.repository)
30
+ if self.repository == "owner/failed":
31
+ raise TimeoutError
32
+
33
+ with pytest.raises(RuntimeError, match="owner/failed"):
34
+ github_client.run_repositories(Automation)
35
+
36
+ assert visited == ["owner/failed", "owner/next"]
37
+
38
+
39
+ def test_pagination_rejects_a_non_list_response(monkeypatch):
40
+ monkeypatch.setattr(
41
+ github_client, "github_request", lambda *args, **kwargs: ({}, {})
42
+ )
43
+
44
+ with pytest.raises(TypeError, match="paginated GitHub list"):
45
+ github_client.github_paginate("token", "/repos/owner/repo/issues")
46
+
47
+
48
+ @pytest.mark.parametrize(
49
+ "state,reason,expected",
50
+ [
51
+ ("closed", "completed", True),
52
+ ("closed", "not_planned", False),
53
+ ("open", None, False),
54
+ ],
55
+ )
56
+ def test_dependencies_must_be_completed(state, reason, expected):
57
+ repository = object.__new__(github_client.GitHubRepository)
58
+ repository._completed_dependencies = {}
59
+ repository.gh = lambda *args: {"state": state, "state_reason": reason}
60
+
61
+ assert repository.dependencies_complete({"body": "Depends on: #12"}) is expected
62
+
63
+
64
+ def test_dependency_permission_errors_fail_closed():
65
+ repository = object.__new__(github_client.GitHubRepository)
66
+ repository._completed_dependencies = {}
67
+
68
+ def denied(*args):
69
+ raise HTTPError("https://api.github.com", 403, "Forbidden", {}, None)
70
+
71
+ repository.gh = denied
72
+ with pytest.raises(HTTPError):
73
+ repository.dependencies_complete({"body": "Depends on: #12"})