@openhands/extensions 0.1.0 → 0.3.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 (354) 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/index.d.ts +2 -0
  15. package/index.js +1 -0
  16. package/marketplaces/large-codebase.json +66 -0
  17. package/marketplaces/openhands-extensions.json +682 -0
  18. package/package.json +12 -10
  19. package/plugins/README.md +30 -0
  20. package/plugins/city-weather/.plugin/plugin.json +13 -0
  21. package/plugins/city-weather/README.md +145 -0
  22. package/plugins/city-weather/commands/now.md +56 -0
  23. package/plugins/cobol-modernization/.plugin/plugin.json +19 -0
  24. package/plugins/cobol-modernization/README.md +201 -0
  25. package/plugins/cobol-modernization/references/troubleshooting.md +18 -0
  26. package/plugins/cobol-modernization/skills/build-setup/SKILL.md +78 -0
  27. package/plugins/cobol-modernization/skills/build-setup/scripts/install-gnucobol.sh +32 -0
  28. package/plugins/cobol-modernization/skills/cobol-modernization-overview/SKILL.md +113 -0
  29. package/plugins/cobol-modernization/skills/mainfraime-removal/SKILL.md +62 -0
  30. package/plugins/cobol-modernization/skills/mainfraime-removal/references/cics-transformation-examples.md +45 -0
  31. package/plugins/cobol-modernization/skills/mainframe-planning/SKILL.md +78 -0
  32. package/plugins/cobol-modernization/skills/to-java-migration/SKILL.md +59 -0
  33. package/plugins/cobol-modernization/skills/to-java-migration/references/cobol-to-java-example.md +58 -0
  34. package/plugins/cobol-modernization/skills/to-java-migration/references/datatype-mappings.md +19 -0
  35. package/plugins/issue-duplicate-checker/.plugin/plugin.json +13 -0
  36. package/plugins/issue-duplicate-checker/README.md +51 -0
  37. package/plugins/issue-duplicate-checker/action.yml +349 -0
  38. package/plugins/issue-duplicate-checker/scripts/auto_close_duplicate_issues.py +569 -0
  39. package/plugins/issue-duplicate-checker/scripts/issue_duplicate_check_openhands.py +681 -0
  40. package/plugins/issue-duplicate-checker/scripts/post_duplicate_notice.js +220 -0
  41. package/plugins/issue-duplicate-checker/scripts/remove_duplicate_candidate_label.js +27 -0
  42. package/plugins/magic-test/.plugin/plugin.json +13 -0
  43. package/plugins/magic-test/skills/magic-word/SKILL.md +33 -0
  44. package/plugins/migration-scoring/.plugin/plugin.json +19 -0
  45. package/plugins/migration-scoring/README.md +244 -0
  46. package/plugins/migration-scoring/skills/migration-mapping/SKILL.md +72 -0
  47. package/plugins/migration-scoring/skills/migration-report/SKILL.md +118 -0
  48. package/plugins/migration-scoring/skills/migration-scoring-overview/SKILL.md +126 -0
  49. package/plugins/migration-scoring/skills/score-quality/SKILL.md +54 -0
  50. package/plugins/migration-scoring/skills/score-quality/references/scoring-criteria.md +30 -0
  51. package/plugins/migration-scoring/skills/score-style/SKILL.md +106 -0
  52. package/plugins/onboarding/.plugin/plugin.json +20 -0
  53. package/plugins/onboarding/README.md +30 -0
  54. package/plugins/onboarding/references/criteria.md +144 -0
  55. package/plugins/onboarding/skills/agent-readiness-report/README.md +23 -0
  56. package/plugins/onboarding/skills/agent-readiness-report/SKILL.md +122 -0
  57. package/plugins/onboarding/skills/agent-readiness-report/scripts/scan_agent_instructions.sh +88 -0
  58. package/plugins/onboarding/skills/agent-readiness-report/scripts/scan_build_env.sh +114 -0
  59. package/plugins/onboarding/skills/agent-readiness-report/scripts/scan_feedback_loops.sh +133 -0
  60. package/plugins/onboarding/skills/agent-readiness-report/scripts/scan_policy.sh +113 -0
  61. package/plugins/onboarding/skills/agent-readiness-report/scripts/scan_workflows.sh +127 -0
  62. package/plugins/onboarding/skills/improve-agent-readiness/README.md +19 -0
  63. package/plugins/onboarding/skills/improve-agent-readiness/SKILL.md +167 -0
  64. package/plugins/onboarding/skills/setup-agents-md/README.md +15 -0
  65. package/plugins/onboarding/skills/setup-agents-md/SKILL.md +150 -0
  66. package/plugins/onboarding/skills/setup-openhands/README.md +20 -0
  67. package/plugins/onboarding/skills/setup-openhands/SKILL.md +56 -0
  68. package/plugins/onboarding/skills/setup-pr-review/README.md +23 -0
  69. package/plugins/onboarding/skills/setup-pr-review/SKILL.md +72 -0
  70. package/plugins/openhands/.plugin/plugin.json +13 -0
  71. package/plugins/openhands/README.md +52 -0
  72. package/plugins/openhands/SKILL.md +61 -0
  73. package/plugins/openhands/commands/create.md +55 -0
  74. package/plugins/openhands/commands/openhands-cloud.md +8 -0
  75. package/plugins/openhands/scripts/run.sh +69 -0
  76. package/plugins/pr-review/.plugin/plugin.json +13 -0
  77. package/plugins/pr-review/README.md +393 -0
  78. package/plugins/pr-review/action.yml +298 -0
  79. package/plugins/pr-review/scripts/agent_script.py +1282 -0
  80. package/plugins/pr-review/scripts/evaluate_review.py +655 -0
  81. package/plugins/pr-review/scripts/prompt.py +260 -0
  82. package/plugins/pr-review/workflows/pr-review-by-openhands.yml +51 -0
  83. package/plugins/pr-review/workflows/pr-review-evaluation.yml +85 -0
  84. package/plugins/qa-changes/.plugin/plugin.json +11 -0
  85. package/plugins/qa-changes/README.md +185 -0
  86. package/plugins/qa-changes/action.yml +181 -0
  87. package/plugins/qa-changes/scripts/agent_script.py +406 -0
  88. package/plugins/qa-changes/scripts/evaluate_qa_changes.py +385 -0
  89. package/plugins/qa-changes/scripts/prompt.py +174 -0
  90. package/plugins/qa-changes/workflows/qa-changes-by-openhands.yml +50 -0
  91. package/plugins/qa-changes/workflows/qa-changes-evaluation.yml +85 -0
  92. package/plugins/release-notes/.plugin/plugin.json +19 -0
  93. package/plugins/release-notes/README.md +283 -0
  94. package/plugins/release-notes/SKILL.md +83 -0
  95. package/plugins/release-notes/action.yml +117 -0
  96. package/plugins/release-notes/commands/release-notes.md +8 -0
  97. package/plugins/release-notes/scripts/agent_script.py +292 -0
  98. package/plugins/release-notes/scripts/generate_release_notes.py +733 -0
  99. package/plugins/release-notes/scripts/prompt.py +90 -0
  100. package/plugins/release-notes/scripts/validate_release_notes.py +328 -0
  101. package/plugins/release-notes/workflows/release-notes.yml +76 -0
  102. package/plugins/vulnerability-remediation/.plugin/plugin.json +19 -0
  103. package/plugins/vulnerability-remediation/README.md +217 -0
  104. package/plugins/vulnerability-remediation/action.yml +187 -0
  105. package/plugins/vulnerability-remediation/scripts/scan_and_remediate.py +561 -0
  106. package/plugins/vulnerability-remediation/workflows/vulnerability-scan.yml +87 -0
  107. package/pyproject.toml +12 -0
  108. package/release-please-config.json +16 -0
  109. package/scripts/build-skills-catalog.mjs +100 -0
  110. package/scripts/sync_extensions.py +494 -0
  111. package/scripts/sync_openhands_sdk_skill.py +264 -0
  112. package/skills/README.md +159 -0
  113. package/skills/add-javadoc/.plugin/plugin.json +18 -0
  114. package/skills/add-javadoc/README.md +40 -0
  115. package/skills/add-javadoc/SKILL.md +35 -0
  116. package/skills/add-javadoc/references/example.md +32 -0
  117. package/skills/add-skill/.plugin/plugin.json +18 -0
  118. package/skills/add-skill/README.md +67 -0
  119. package/skills/add-skill/SKILL.md +47 -0
  120. package/skills/add-skill/scripts/fetch_skill.py +259 -0
  121. package/skills/agent-creator/.plugin/plugin.json +20 -0
  122. package/skills/agent-creator/README.md +104 -0
  123. package/skills/agent-creator/SKILL.md +190 -0
  124. package/skills/agent-creator/commands/agent-creator.md +8 -0
  125. package/skills/agent-creator/references/fallback.md +117 -0
  126. package/skills/agent-memory/.plugin/plugin.json +18 -0
  127. package/skills/agent-memory/README.md +35 -0
  128. package/skills/agent-memory/SKILL.md +30 -0
  129. package/skills/agent-memory/commands/remember.md +8 -0
  130. package/skills/agent-sdk-builder/.plugin/plugin.json +18 -0
  131. package/skills/agent-sdk-builder/README.md +40 -0
  132. package/skills/agent-sdk-builder/SKILL.md +37 -0
  133. package/skills/agent-sdk-builder/commands/agent-builder.md +8 -0
  134. package/skills/azure-devops/.plugin/plugin.json +18 -0
  135. package/skills/azure-devops/README.md +55 -0
  136. package/skills/azure-devops/SKILL.md +50 -0
  137. package/skills/bitbucket/.plugin/plugin.json +17 -0
  138. package/skills/bitbucket/README.md +50 -0
  139. package/skills/bitbucket/SKILL.md +45 -0
  140. package/skills/code-review/.plugin/plugin.json +19 -0
  141. package/skills/code-review/README.md +18 -0
  142. package/skills/code-review/SKILL.md +208 -0
  143. package/skills/code-review/commands/codereview-roasted.md +8 -0
  144. package/skills/code-review/commands/codereview.md +8 -0
  145. package/skills/code-review/references/risk-evaluation.md +41 -0
  146. package/skills/code-review/references/supply-chain-security.md +31 -0
  147. package/skills/code-simplifier/.plugin/plugin.json +21 -0
  148. package/skills/code-simplifier/README.md +30 -0
  149. package/skills/code-simplifier/SKILL.md +91 -0
  150. package/skills/code-simplifier/commands/simplify.md +8 -0
  151. package/skills/code-simplifier/references/code-quality-review.md +86 -0
  152. package/skills/code-simplifier/references/code-reuse-review.md +63 -0
  153. package/skills/code-simplifier/references/efficiency-review.md +81 -0
  154. package/skills/datadog/.plugin/plugin.json +19 -0
  155. package/skills/datadog/README.md +100 -0
  156. package/skills/datadog/SKILL.md +95 -0
  157. package/skills/deno/.plugin/plugin.json +18 -0
  158. package/skills/deno/README.md +5 -0
  159. package/skills/deno/SKILL.md +99 -0
  160. package/skills/deno/references/README.md +6 -0
  161. package/skills/discord/.plugin/plugin.json +18 -0
  162. package/skills/discord/README.md +31 -0
  163. package/skills/discord/SKILL.md +109 -0
  164. package/skills/discord/__init__.py +0 -0
  165. package/skills/discord/references/REFERENCE.md +78 -0
  166. package/skills/discord/scripts/__init__.py +0 -0
  167. package/skills/discord/scripts/_http.py +127 -0
  168. package/skills/discord/scripts/post_webhook.py +106 -0
  169. package/skills/discord/scripts/send_message.py +102 -0
  170. package/skills/docker/.plugin/plugin.json +17 -0
  171. package/skills/docker/README.md +34 -0
  172. package/skills/docker/SKILL.md +29 -0
  173. package/skills/evidence-based-citations/.plugin/plugin.json +20 -0
  174. package/skills/evidence-based-citations/README.md +31 -0
  175. package/skills/evidence-based-citations/SKILL.md +59 -0
  176. package/skills/flarglebargle/.plugin/plugin.json +16 -0
  177. package/skills/flarglebargle/README.md +14 -0
  178. package/skills/flarglebargle/SKILL.md +9 -0
  179. package/skills/frontend-design/.plugin/plugin.json +21 -0
  180. package/skills/frontend-design/LICENSE.txt +177 -0
  181. package/skills/frontend-design/README.md +42 -0
  182. package/skills/frontend-design/SKILL.md +42 -0
  183. package/skills/github/.plugin/plugin.json +19 -0
  184. package/skills/github/README.md +42 -0
  185. package/skills/github/SKILL.md +106 -0
  186. package/skills/github-pr-review/.plugin/plugin.json +18 -0
  187. package/skills/github-pr-review/README.md +145 -0
  188. package/skills/github-pr-review/SKILL.md +186 -0
  189. package/skills/github-pr-review/commands/github-pr-review.md +8 -0
  190. package/skills/github-pr-reviewer/.plugin/plugin.json +20 -0
  191. package/skills/github-pr-reviewer/README.md +34 -0
  192. package/skills/github-pr-reviewer/SKILL.md +89 -0
  193. package/skills/github-pr-reviewer/commands/pr-reviewer:setup.md +8 -0
  194. package/skills/github-repo-monitor/.plugin/plugin.json +22 -0
  195. package/skills/github-repo-monitor/README.md +70 -0
  196. package/skills/github-repo-monitor/SKILL.md +316 -0
  197. package/skills/github-repo-monitor/commands/github-monitor:poll.md +8 -0
  198. package/skills/github-repo-monitor/references/github-api.md +241 -0
  199. package/skills/github-repo-monitor/references/state-schema.md +160 -0
  200. package/skills/github-repo-monitor/scripts/main.py +915 -0
  201. package/skills/github-repo-monitor/tests/test_main.py +400 -0
  202. package/skills/gitlab/.plugin/plugin.json +17 -0
  203. package/skills/gitlab/README.md +37 -0
  204. package/skills/gitlab/SKILL.md +32 -0
  205. package/skills/incident-retrospective/.plugin/plugin.json +21 -0
  206. package/skills/incident-retrospective/README.md +34 -0
  207. package/skills/incident-retrospective/SKILL.md +98 -0
  208. package/skills/incident-retrospective/commands/incident-retro:setup.md +8 -0
  209. package/skills/index.d.ts +11 -0
  210. package/skills/index.js +465 -0
  211. package/skills/iterate/.plugin/plugin.json +13 -0
  212. package/skills/iterate/README.md +25 -0
  213. package/skills/iterate/SKILL.md +399 -0
  214. package/skills/iterate/commands/babysit.md +8 -0
  215. package/skills/iterate/commands/iterate.md +8 -0
  216. package/skills/iterate/commands/verify.md +8 -0
  217. package/skills/iterate/references/heuristics.md +58 -0
  218. package/skills/iterate/references/verification.md +96 -0
  219. package/skills/jupyter/.plugin/plugin.json +18 -0
  220. package/skills/jupyter/README.md +55 -0
  221. package/skills/jupyter/SKILL.md +50 -0
  222. package/skills/kubernetes/.plugin/plugin.json +18 -0
  223. package/skills/kubernetes/README.md +53 -0
  224. package/skills/kubernetes/SKILL.md +48 -0
  225. package/skills/learn-from-code-review/.plugin/plugin.json +19 -0
  226. package/skills/learn-from-code-review/README.md +64 -0
  227. package/skills/learn-from-code-review/SKILL.md +186 -0
  228. package/skills/learn-from-code-review/commands/learn-from-reviews.md +8 -0
  229. package/skills/linear/.plugin/plugin.json +19 -0
  230. package/skills/linear/README.md +58 -0
  231. package/skills/linear/SKILL.md +213 -0
  232. package/skills/linear-triage/.plugin/plugin.json +21 -0
  233. package/skills/linear-triage/README.md +34 -0
  234. package/skills/linear-triage/SKILL.md +91 -0
  235. package/skills/linear-triage/commands/linear-triage:setup.md +8 -0
  236. package/skills/notion/.plugin/plugin.json +17 -0
  237. package/skills/notion/README.md +114 -0
  238. package/skills/notion/SKILL.md +109 -0
  239. package/skills/npm/.plugin/plugin.json +17 -0
  240. package/skills/npm/README.md +14 -0
  241. package/skills/npm/SKILL.md +9 -0
  242. package/skills/openhands-api/.plugin/plugin.json +22 -0
  243. package/skills/openhands-api/README.md +48 -0
  244. package/skills/openhands-api/SKILL.md +399 -0
  245. package/skills/openhands-api/references/README.md +33 -0
  246. package/skills/openhands-api/references/TROUBLESHOOTING.md +81 -0
  247. package/skills/openhands-api/references/example_prompt.md +12 -0
  248. package/skills/openhands-api/scripts/openhands_api.py +606 -0
  249. package/skills/openhands-api/scripts/openhands_api.ts +252 -0
  250. package/skills/openhands-automation/.plugin/plugin.json +19 -0
  251. package/skills/openhands-automation/README.md +89 -0
  252. package/skills/openhands-automation/SKILL.md +875 -0
  253. package/skills/openhands-automation/commands/automation:create.md +8 -0
  254. package/skills/openhands-automation/references/ab-testing.md +185 -0
  255. package/skills/openhands-automation/references/custom-automation.md +644 -0
  256. package/skills/openhands-sdk/.plugin/plugin.json +20 -0
  257. package/skills/openhands-sdk/README.md +22 -0
  258. package/skills/openhands-sdk/SKILL.md +230 -0
  259. package/skills/openhands-sdk/commands/sdk.md +8 -0
  260. package/skills/pdflatex/.plugin/plugin.json +18 -0
  261. package/skills/pdflatex/README.md +39 -0
  262. package/skills/pdflatex/SKILL.md +34 -0
  263. package/skills/prd/.plugin/plugin.json +19 -0
  264. package/skills/prd/README.md +28 -0
  265. package/skills/prd/SKILL.md +237 -0
  266. package/skills/prd/commands/prd.md +8 -0
  267. package/skills/qa-changes/README.md +18 -0
  268. package/skills/qa-changes/SKILL.md +229 -0
  269. package/skills/qa-changes/commands/qa-changes.md +8 -0
  270. package/skills/release-notes/README.md +24 -0
  271. package/skills/release-notes/SKILL.md +19 -0
  272. package/skills/release-notes/commands/release-notes.md +8 -0
  273. package/skills/research-brief/.plugin/plugin.json +20 -0
  274. package/skills/research-brief/README.md +34 -0
  275. package/skills/research-brief/SKILL.md +99 -0
  276. package/skills/research-brief/commands/research-brief:setup.md +8 -0
  277. package/skills/security/.plugin/plugin.json +18 -0
  278. package/skills/security/README.md +38 -0
  279. package/skills/security/SKILL.md +33 -0
  280. package/skills/skill-creator/.plugin/plugin.json +17 -0
  281. package/skills/skill-creator/LICENSE.txt +202 -0
  282. package/skills/skill-creator/README.md +182 -0
  283. package/skills/skill-creator/SKILL.md +545 -0
  284. package/skills/skill-creator/references/output-patterns.md +82 -0
  285. package/skills/skill-creator/references/workflows.md +28 -0
  286. package/skills/skill-creator/scripts/init_skill.py +303 -0
  287. package/skills/skill-creator/scripts/quick_validate.py +95 -0
  288. package/skills/slack-channel-monitor/.plugin/plugin.json +21 -0
  289. package/skills/slack-channel-monitor/README.md +91 -0
  290. package/skills/slack-channel-monitor/SKILL.md +276 -0
  291. package/skills/slack-channel-monitor/commands/slack-monitor:poll.md +8 -0
  292. package/skills/slack-channel-monitor/references/slack-api.md +207 -0
  293. package/skills/slack-channel-monitor/references/state-schema.md +180 -0
  294. package/skills/slack-channel-monitor/scripts/main.py +962 -0
  295. package/skills/slack-standup-digest/.plugin/plugin.json +21 -0
  296. package/skills/slack-standup-digest/README.md +34 -0
  297. package/skills/slack-standup-digest/SKILL.md +92 -0
  298. package/skills/slack-standup-digest/commands/standup-digest:setup.md +8 -0
  299. package/skills/spark-version-upgrade/.plugin/plugin.json +20 -0
  300. package/skills/spark-version-upgrade/README.md +54 -0
  301. package/skills/spark-version-upgrade/SKILL.md +233 -0
  302. package/skills/ssh/.plugin/plugin.json +18 -0
  303. package/skills/ssh/README.md +140 -0
  304. package/skills/ssh/SKILL.md +135 -0
  305. package/skills/swift-linux/.plugin/plugin.json +17 -0
  306. package/skills/swift-linux/README.md +86 -0
  307. package/skills/swift-linux/SKILL.md +81 -0
  308. package/skills/theme-factory/.plugin/plugin.json +19 -0
  309. package/skills/theme-factory/LICENSE.txt +202 -0
  310. package/skills/theme-factory/README.md +58 -0
  311. package/skills/theme-factory/SKILL.md +59 -0
  312. package/skills/theme-factory/theme-showcase.pdf +0 -0
  313. package/skills/theme-factory/themes/arctic-frost.md +19 -0
  314. package/skills/theme-factory/themes/botanical-garden.md +19 -0
  315. package/skills/theme-factory/themes/desert-rose.md +19 -0
  316. package/skills/theme-factory/themes/forest-canopy.md +19 -0
  317. package/skills/theme-factory/themes/golden-hour.md +19 -0
  318. package/skills/theme-factory/themes/midnight-galaxy.md +19 -0
  319. package/skills/theme-factory/themes/modern-minimalist.md +19 -0
  320. package/skills/theme-factory/themes/ocean-depths.md +19 -0
  321. package/skills/theme-factory/themes/sunset-boulevard.md +19 -0
  322. package/skills/theme-factory/themes/tech-innovation.md +19 -0
  323. package/skills/uv/.plugin/plugin.json +18 -0
  324. package/skills/uv/README.md +5 -0
  325. package/skills/uv/SKILL.md +95 -0
  326. package/skills/uv/references/README.md +5 -0
  327. package/skills/vercel/.plugin/plugin.json +18 -0
  328. package/skills/vercel/README.md +108 -0
  329. package/skills/vercel/SKILL.md +103 -0
  330. package/tests/test_add_skill_installs_to_agents_dir.py +42 -0
  331. package/tests/test_catalogs.py +109 -0
  332. package/tests/test_code_review_risk_evaluation.py +94 -0
  333. package/tests/test_github_pr_review_skill.py +114 -0
  334. package/tests/test_issue_duplicate_checker.py +240 -0
  335. package/tests/test_openhands_api_python.py +152 -0
  336. package/tests/test_plugin_manifest.py +83 -0
  337. package/tests/test_pr_review_diff_payload.py +202 -0
  338. package/tests/test_pr_review_feedback.py +263 -0
  339. package/tests/test_pr_review_prompt.py +152 -0
  340. package/tests/test_pr_review_review_context.py +253 -0
  341. package/tests/test_qa_changes.py +232 -0
  342. package/tests/test_qa_changes_evaluation.py +259 -0
  343. package/tests/test_release_notes_generator.py +990 -0
  344. package/tests/test_sdk_loading.py +150 -0
  345. package/tests/test_skill_plugin_loading.py +149 -0
  346. package/tests/test_skills_catalog.py +336 -0
  347. package/tests/test_skills_have_readme.py +66 -0
  348. package/tests/test_sync_extensions.py +292 -0
  349. package/tests/test_workflow_sync.py +46 -0
  350. package/utils/analysis/README.md +7 -0
  351. package/utils/analysis/laminar_signals/README.md +211 -0
  352. package/utils/analysis/laminar_signals/analyze.py +780 -0
  353. package/utils/analysis/laminar_signals/templates/default.j2 +49 -0
  354. package/utils/analysis/laminar_signals/templates/pr_review.j2 +61 -0
@@ -0,0 +1,66 @@
1
+ """Test that all skills have a README.md file in their directory."""
2
+
3
+ import os
4
+ from pathlib import Path
5
+
6
+
7
+ def get_skills_directory():
8
+ """Get the path to the skills directory."""
9
+ # Get the directory containing this test file
10
+ test_dir = Path(__file__).parent
11
+ # Go up one level to the repo root, then into skills/
12
+ return test_dir.parent / "skills"
13
+
14
+
15
+ def test_all_skills_have_readme():
16
+ """Verify that every skill directory contains a README.md file."""
17
+ skills_dir = get_skills_directory()
18
+
19
+ # Get all subdirectories in the skills directory (excluding files)
20
+ skill_dirs = [
21
+ d for d in skills_dir.iterdir()
22
+ if d.is_dir() and not d.name.startswith('.')
23
+ ]
24
+
25
+ assert len(skill_dirs) > 0, "No skill directories found"
26
+
27
+ missing_readmes = []
28
+ for skill_dir in skill_dirs:
29
+ readme_path = skill_dir / "README.md"
30
+ if not readme_path.exists():
31
+ missing_readmes.append(skill_dir.name)
32
+
33
+ assert len(missing_readmes) == 0, (
34
+ f"The following skills are missing README.md: {', '.join(missing_readmes)}"
35
+ )
36
+
37
+
38
+ def test_readme_is_readable():
39
+ """Verify that README.md files are readable (symlinks resolve correctly)."""
40
+ skills_dir = get_skills_directory()
41
+
42
+ skill_dirs = [
43
+ d for d in skills_dir.iterdir()
44
+ if d.is_dir() and not d.name.startswith('.')
45
+ ]
46
+
47
+ unreadable_readmes = []
48
+ for skill_dir in skill_dirs:
49
+ readme_path = skill_dir / "README.md"
50
+ if readme_path.exists():
51
+ try:
52
+ content = readme_path.read_text()
53
+ if len(content) == 0:
54
+ unreadable_readmes.append(f"{skill_dir.name} (empty)")
55
+ except Exception as e:
56
+ unreadable_readmes.append(f"{skill_dir.name} ({e})")
57
+
58
+ assert len(unreadable_readmes) == 0, (
59
+ f"The following README.md files are unreadable: {', '.join(unreadable_readmes)}"
60
+ )
61
+
62
+
63
+ if __name__ == "__main__":
64
+ test_all_skills_have_readme()
65
+ test_readme_is_readable()
66
+ print("All tests passed!")
@@ -0,0 +1,292 @@
1
+ """Tests for scripts/sync_extensions.py core functions."""
2
+
3
+ import sys
4
+ from pathlib import Path
5
+
6
+ import pytest
7
+
8
+ sys.path.insert(0, str(Path(__file__).parent.parent / "scripts"))
9
+ from sync_extensions import (
10
+ CMD_MARKER,
11
+ CommandSpec,
12
+ REPO_ROOT,
13
+ _entry_type,
14
+ build_command_content,
15
+ collect_needed_commands,
16
+ generate_catalog,
17
+ load_marketplaces,
18
+ parse_frontmatter,
19
+ slash_triggers,
20
+ sync_commands,
21
+ )
22
+
23
+
24
+ # ── parse_frontmatter ────────────────────────────────────────────────
25
+
26
+ class TestParseFrontmatter:
27
+ def test_basic(self):
28
+ text = (
29
+ "---\n"
30
+ "name: test-skill\n"
31
+ "description: A test skill\n"
32
+ "triggers:\n"
33
+ " - /test\n"
34
+ "---\n"
35
+ "Body"
36
+ )
37
+ meta = parse_frontmatter(text)
38
+ assert meta["name"] == "test-skill"
39
+ assert meta["description"] == "A test skill"
40
+ assert meta["triggers"] == ["/test"]
41
+
42
+ def test_no_frontmatter(self):
43
+ assert parse_frontmatter("No frontmatter here") == {}
44
+
45
+ def test_empty_frontmatter(self):
46
+ meta = parse_frontmatter("---\n---\nBody")
47
+ assert meta.get("triggers", []) == []
48
+ assert "name" not in meta
49
+
50
+ def test_multiple_triggers(self):
51
+ text = (
52
+ "---\n"
53
+ "name: multi\n"
54
+ "triggers:\n"
55
+ " - /foo\n"
56
+ " - /bar\n"
57
+ " - baz\n"
58
+ "---\n"
59
+ )
60
+ meta = parse_frontmatter(text)
61
+ assert meta["triggers"] == ["/foo", "/bar", "baz"]
62
+
63
+ def test_no_triggers_key(self):
64
+ text = "---\nname: no-triggers\ndescription: desc\n---\n"
65
+ meta = parse_frontmatter(text)
66
+ assert meta["triggers"] == []
67
+
68
+ def test_triggers_with_comments(self):
69
+ text = (
70
+ "---\n"
71
+ "triggers:\n"
72
+ " - /cmd\n"
73
+ " # this is a comment\n"
74
+ " - /other\n"
75
+ "---\n"
76
+ )
77
+ meta = parse_frontmatter(text)
78
+ assert "/cmd" in meta["triggers"]
79
+ assert "/other" in meta["triggers"]
80
+
81
+ def test_folded_block_scalar_description(self):
82
+ text = (
83
+ "---\n"
84
+ "name: test\n"
85
+ "description: >\n"
86
+ " This is a long\n"
87
+ " description text\n"
88
+ "other: value\n"
89
+ "---\n"
90
+ )
91
+ meta = parse_frontmatter(text)
92
+ assert meta["description"] == "This is a long description text"
93
+
94
+ def test_unicode(self):
95
+ text = (
96
+ "---\n"
97
+ "name: unic\u00f6de-skill\n"
98
+ "description: \u00dcn\u00efc\u00f6d\u00e9 description \U0001f680\n"
99
+ "---\n"
100
+ )
101
+ meta = parse_frontmatter(text)
102
+ assert meta["name"] == "unic\u00f6de-skill"
103
+ assert "\U0001f680" in meta["description"]
104
+
105
+
106
+ class TestParseFrontmatterEdgeCases:
107
+ """Edge-case tests ensuring graceful degradation."""
108
+
109
+ def test_malformed_yaml_missing_close(self):
110
+ assert parse_frontmatter("---\nname: incomplete\n") == {}
111
+
112
+ def test_invalid_yaml_returns_empty(self):
113
+ assert parse_frontmatter("---\n: [invalid yaml{{\n---\n") == {}
114
+
115
+ def test_colon_in_description(self):
116
+ text = '---\nname: test\ndescription: "key: value pair"\n---\n'
117
+ meta = parse_frontmatter(text)
118
+ assert meta["description"] == "key: value pair"
119
+
120
+ def test_quoted_values_stripped(self):
121
+ text = "---\nname: \"quoted-name\"\ndescription: 'single-quoted'\n---\n"
122
+ meta = parse_frontmatter(text)
123
+ assert meta["name"] == "quoted-name"
124
+ assert meta["description"] == "single-quoted"
125
+
126
+ def test_triggers_end_at_next_key(self):
127
+ text = (
128
+ "---\n"
129
+ "triggers:\n"
130
+ " - /a\n"
131
+ " - /b\n"
132
+ "name: after-triggers\n"
133
+ "---\n"
134
+ )
135
+ meta = parse_frontmatter(text)
136
+ assert meta["triggers"] == ["/a", "/b"]
137
+ assert meta["name"] == "after-triggers"
138
+
139
+ def test_null_trigger_skipped(self):
140
+ text = (
141
+ "---\n"
142
+ "triggers:\n"
143
+ " - /cmd\n"
144
+ " -\n"
145
+ " - /other\n"
146
+ "---\n"
147
+ )
148
+ meta = parse_frontmatter(text)
149
+ assert "/cmd" in meta["triggers"]
150
+ assert "/other" in meta["triggers"]
151
+
152
+ def test_frontmatter_only_block(self):
153
+ meta = parse_frontmatter("---\nname: only\n---")
154
+ assert meta["name"] == "only"
155
+
156
+
157
+ # ── slash_triggers ────────────────────────────────────────────────────
158
+
159
+ class TestSlashTriggers:
160
+ def test_filters_slash_only(self):
161
+ meta = {"triggers": ["/init", "keyword", "/build"]}
162
+ assert slash_triggers(meta) == ["/init", "/build"]
163
+
164
+ def test_empty(self):
165
+ assert slash_triggers({}) == []
166
+ assert slash_triggers({"triggers": []}) == []
167
+
168
+
169
+ # ── _entry_type ───────────────────────────────────────────────────────
170
+
171
+ class TestEntryType:
172
+ def test_skills_path(self):
173
+ assert _entry_type("./skills/foo") == "skill"
174
+
175
+ def test_plugins_path(self):
176
+ assert _entry_type("./plugins/bar") == "plugin"
177
+
178
+ def test_relative_plugins(self):
179
+ assert _entry_type("../plugins/baz") == "plugin"
180
+
181
+ def test_relative_skills(self):
182
+ assert _entry_type("../skills/qux") == "skill"
183
+
184
+ def test_fallback_is_skill(self):
185
+ assert _entry_type("./nonexistent-xyz") == "skill"
186
+
187
+
188
+ # ── collect_needed_commands ───────────────────────────────────────────
189
+
190
+ class TestCollectNeededCommands:
191
+ def test_returns_command_specs(self):
192
+ specs = collect_needed_commands()
193
+ assert all(isinstance(s, CommandSpec) for s in specs)
194
+ assert len(specs) > 0
195
+
196
+ def test_paths_are_under_commands_dir(self):
197
+ for spec in collect_needed_commands():
198
+ assert spec.path.parent.name == "commands"
199
+ assert spec.path.suffix == ".md"
200
+
201
+
202
+ # ── load_marketplaces ────────────────────────────────────────────────
203
+
204
+ class TestLoadMarketplaces:
205
+ def test_loads_at_least_one(self):
206
+ mps = load_marketplaces()
207
+ assert len(mps) > 0
208
+
209
+ def test_marketplaces_have_required_fields(self):
210
+ for mp in load_marketplaces():
211
+ assert "plugins" in mp
212
+ assert "_file" in mp
213
+
214
+
215
+ # ── generate_catalog ─────────────────────────────────────────────────
216
+
217
+ class TestGenerateCatalog:
218
+ def test_catalog_contains_marketplace_names(self):
219
+ catalog = generate_catalog()
220
+ assert "openhands-extensions" in catalog
221
+
222
+ def test_catalog_has_table_header(self):
223
+ catalog = generate_catalog()
224
+ assert "| Name | Type | Description | Commands |" in catalog
225
+
226
+ def test_catalog_counts_are_consistent(self):
227
+ catalog = generate_catalog()
228
+ assert "marketplace(s)" in catalog
229
+ assert "extensions" in catalog
230
+
231
+ def test_catalog_only_has_skill_or_plugin_types(self):
232
+ """Every table row's Type column must be 'skill' or 'plugin'."""
233
+ catalog = generate_catalog()
234
+ types_found: list[str] = []
235
+ for line in catalog.splitlines():
236
+ # Skip non-table lines, header, and separator
237
+ if not line.startswith("|") or line.startswith("|---") or "Type" in line:
238
+ continue
239
+ cols = [c.strip() for c in line.split("|")]
240
+ # cols[0] is empty (before first |), cols[1]=Name, cols[2]=Type
241
+ if len(cols) >= 3:
242
+ types_found.append(cols[2])
243
+ assert len(types_found) > 0, "Expected at least one table entry"
244
+ invalid = [t for t in types_found if t not in ("skill", "plugin")]
245
+ assert not invalid, f"Found invalid types in catalog: {invalid}"
246
+
247
+
248
+ # ── sync_commands ────────────────────────────────────────────────────
249
+
250
+ class TestSyncCommands:
251
+ def test_check_mode_reports_no_problems_when_in_sync(self):
252
+ problems = sync_commands(check=True)
253
+ assert problems == [], f"Unexpected problems: {problems}"
254
+
255
+ def test_manually_edited_file_detected_in_check_mode(self, tmp_path, monkeypatch):
256
+ """A command file without the header should be flagged in --check mode."""
257
+ # Create a skill with a slash trigger
258
+ skill_dir = tmp_path / "skills" / "test-skill"
259
+ skill_dir.mkdir(parents=True)
260
+ skill_md = skill_dir / "SKILL.md"
261
+ skill_md.write_text(
262
+ "---\nname: test-skill\ndescription: Test\ntriggers:\n - /test-cmd\n---\nBody\n"
263
+ )
264
+ # Create a manually-edited command file (no header)
265
+ cmd_dir = skill_dir / "commands"
266
+ cmd_dir.mkdir()
267
+ (cmd_dir / "test-cmd.md").write_text("Custom content, no header\n")
268
+
269
+ monkeypatch.setattr("sync_extensions.REPO_ROOT", tmp_path)
270
+ monkeypatch.setattr("sync_extensions.SKILL_DIRS", [tmp_path / "skills"])
271
+ monkeypatch.setattr("sync_extensions._slash_cmd_cache", {})
272
+
273
+ problems = sync_commands(check=True)
274
+ assert any("manually-edited" in p for p in problems)
275
+
276
+
277
+ # ── marketplace source paths ─────────────────────────────────────────
278
+
279
+ class TestMarketplaceSourcePaths:
280
+ def test_all_source_paths_exist(self):
281
+ """Every source path in every marketplace should resolve on disk."""
282
+ import json
283
+
284
+ for mp in load_marketplaces():
285
+ mp_file = mp["_file"]
286
+ for plugin in mp["plugins"]:
287
+ src = plugin.get("source", "")
288
+ resolved = REPO_ROOT / src.lstrip("./")
289
+ assert resolved.exists(), (
290
+ f"{mp_file.name}: {plugin.get('name', '?')} source "
291
+ f"'{src}' does not exist at {resolved}"
292
+ )
@@ -0,0 +1,46 @@
1
+ #!/usr/bin/env python3
2
+ """Test that workflow files in .github/workflows/ match copies in plugins/*/workflows/."""
3
+
4
+ from pathlib import Path
5
+
6
+
7
+ def test_workflow_files_are_in_sync():
8
+ """Ensure workflow files in .github/workflows/ are identical to copies in plugin dirs."""
9
+ repo_root = Path(__file__).parent.parent
10
+ workflows_dir = repo_root / ".github" / "workflows"
11
+
12
+ if not workflows_dir.exists():
13
+ return # No workflows directory
14
+
15
+ mismatches = []
16
+ # Check all plugins/*/workflows/ directories
17
+ for copy_path in repo_root.glob("plugins/*/workflows"):
18
+ if not copy_path.is_dir():
19
+ continue
20
+
21
+ for copy_file in copy_path.glob("*.yml"):
22
+ canonical_file = workflows_dir / copy_file.name
23
+ if not canonical_file.exists():
24
+ continue
25
+
26
+ canonical_content = canonical_file.read_text()
27
+ copy_content = copy_file.read_text()
28
+
29
+ if canonical_content != copy_content:
30
+ rel_path = copy_path.relative_to(repo_root)
31
+ mismatches.append(
32
+ f"{rel_path}/{copy_file.name} differs from "
33
+ f".github/workflows/{copy_file.name}"
34
+ )
35
+
36
+ if mismatches:
37
+ raise AssertionError(
38
+ "Workflow files are out of sync:\n"
39
+ + "\n".join(f" - {m}" for m in mismatches)
40
+ + "\n\nRun: cp .github/workflows/<file> <destination> to sync"
41
+ )
42
+
43
+
44
+ if __name__ == "__main__":
45
+ test_workflow_files_are_in_sync()
46
+ print("All workflow files are in sync!")
@@ -0,0 +1,7 @@
1
+ # Signal Analysis Utilities
2
+
3
+ This directory contains utilities for analyzing signals collected from Laminar to understand AI agent behavior patterns.
4
+
5
+ ## Available Utilities
6
+
7
+ - **[laminar_signals/](laminar_signals/)** - Analyze Laminar signal events using LLMs with customizable Jinja2 prompt templates
@@ -0,0 +1,211 @@
1
+ # Laminar Signal Analysis
2
+
3
+ Analyze Laminar signal events using LLMs to identify issues and areas for improvement. Uses function calling to get structured output with clickable trace URLs.
4
+
5
+ **Primary Focus**: Identifying problems and generating actionable recommendations to improve AI agent behavior.
6
+
7
+ ## Directory Structure
8
+
9
+ ```
10
+ laminar_signals/
11
+ ├── analyze.py # Main analysis script
12
+ ├── README.md # This file
13
+ └── templates/
14
+ ├── default.j2 # Generic template for any signal type
15
+ └── pr_review.j2 # Specialized template for PR review signals
16
+ ```
17
+
18
+ ## Prerequisites
19
+
20
+ - Python 3.9+
21
+ - `jinja2` package
22
+
23
+ ```bash
24
+ pip install jinja2
25
+ ```
26
+
27
+ ## Environment Variables
28
+
29
+ | Variable | Required | Default | Description |
30
+ |----------|----------|---------|-------------|
31
+ | `LMNR_PROJECT_API_KEY` | Yes | - | Laminar project API key |
32
+ | `LMNR_PROJECT_ID` | No | - | Laminar project ID (for generating clickable trace URLs) |
33
+ | `LLM_API_KEY` | Yes | - | API key for the LLM |
34
+ | `LLM_MODEL` | No | `gemini-3-pro-preview` | Model to use |
35
+ | `LLM_BASE_URL` | No | `https://llm-proxy.app.all-hands.dev` | Base URL for LLM API |
36
+
37
+ **Note:** To enable clickable trace URLs in the output, set `LMNR_PROJECT_ID` to your Laminar project ID.
38
+ You can find this in your Laminar dashboard URL: `https://laminar.sh/project/{PROJECT_ID}/...`
39
+
40
+ ## Usage
41
+
42
+ ```bash
43
+ # List available signals
44
+ python analyze.py --list-signals
45
+
46
+ # Analyze PR review signals (outputs markdown by default)
47
+ python analyze.py --signal "pr review suggestion and analysis"
48
+
49
+ # Analyze with skill context for grounded recommendations
50
+ python analyze.py --signal "pr review suggestion and analysis" \
51
+ --skill-dir ../../../plugins/pr-review
52
+
53
+ # Output as JSON (structured data with trace URLs)
54
+ python analyze.py --signal "pr review suggestion and analysis" --format json
55
+
56
+ # Analyze with custom prompt template
57
+ python analyze.py --signal "my-signal" --prompt-file my_prompt.j2
58
+
59
+ # Analyze last 30 days and save to file
60
+ python analyze.py --signal "my-signal" --days 30 --output results.md
61
+ ```
62
+
63
+ ## How It Works
64
+
65
+ 1. **Fetches Signals**: Queries the Laminar SQL API to retrieve signal events with trace IDs.
66
+
67
+ 2. **Generates Prompt**: Uses a Jinja2 template focused on identifying issues and improvements.
68
+
69
+ 3. **Loads Skill Context** (optional): If `--skill-dir` is provided, loads the skill/plugin content so recommendations can reference specific prompt verbiage.
70
+
71
+ 4. **LLM Analysis**: Uses **function calling** to get structured output with:
72
+ - `issues`: Problems and failures with severity, frequency, and clickable trace URLs
73
+ - `recommendations`: Specific fixes with priority levels (grounded in skill content when provided)
74
+ - `metrics`: Quantitative statistics (issue rate, etc.)
75
+ - `strengths`: Brief note on what's working (secondary focus)
76
+
77
+ 5. **Output**: Markdown report (default) or JSON for programmatic use.
78
+
79
+ ## Grounding Recommendations in Skills
80
+
81
+ Use `--skill-dir` to provide the current skill/plugin configuration. This allows the LLM to:
82
+ - Reference specific sections of the prompt that need changes
83
+ - Suggest exact wording modifications
84
+ - Ground recommendations in the actual instructions the agent follows
85
+
86
+ ```bash
87
+ # Use a skill directory
88
+ python analyze.py --signal "..." --skill-dir path/to/skills/code-review
89
+
90
+ # Use a plugin directory (loads nested skills and prompt.py)
91
+ python analyze.py --signal "..." --skill-dir path/to/plugins/pr-review
92
+ ```
93
+
94
+ The script automatically loads:
95
+ - `SKILL.md` from the directory
96
+ - `scripts/prompt.py` if present (for plugins)
97
+ - Any nested skills in `skills/` subdirectory
98
+
99
+ ## Output Structure
100
+
101
+ ### Issues (Primary Focus)
102
+
103
+ Each issue includes:
104
+ - **Title**: Short description
105
+ - **Description**: Detailed explanation of the problem
106
+ - **Severity**: critical / high / medium / low
107
+ - **Frequency**: How often this occurs
108
+ - **Trace URLs**: Clickable links to example traces in Laminar
109
+
110
+ ### Recommendations
111
+
112
+ Each recommendation includes:
113
+ - **Title**: Short description
114
+ - **Description**: How to implement the fix
115
+ - **Addresses**: Which issues this fixes
116
+ - **Priority**: high / medium / low
117
+
118
+ ### Example JSON Output
119
+
120
+ ```json
121
+ {
122
+ "issues": [
123
+ {
124
+ "title": "Premature Approval of Functional Defects",
125
+ "description": "The AI approves code with known issues...",
126
+ "severity": "critical",
127
+ "frequency": "10-15% of traces",
128
+ "trace_urls": [
129
+ "https://www.lmnr.ai/traces/a0dbd0c7-116d-776b-9c4b-c7f5f4d0a97e",
130
+ "https://www.lmnr.ai/traces/5de277ff-38f2-e173-4b03-c7332301ea58"
131
+ ]
132
+ }
133
+ ],
134
+ "recommendations": [
135
+ {
136
+ "title": "Enforce Logic Verification Over Style",
137
+ "description": "Modify the prompt to separate style from logic analysis...",
138
+ "addresses": ["Premature Approval of Functional Defects"],
139
+ "priority": "high"
140
+ }
141
+ ],
142
+ "metrics": {
143
+ "total_signals": 50,
144
+ "issue_rate": "30%",
145
+ "key_statistics": [...]
146
+ },
147
+ "strengths": [
148
+ {"title": "Security Analysis", "description": "..."}
149
+ ],
150
+ "summary": "Executive summary of critical improvements needed..."
151
+ }
152
+ ```
153
+
154
+ ### Example Markdown Output
155
+
156
+ ```markdown
157
+ # Agent Improvement Report
158
+
159
+ **Signal:** `pr review suggestion and analysis`
160
+
161
+ ## Executive Summary
162
+
163
+ The AI agent demonstrates strong security awareness but suffers from...
164
+
165
+ ## Issues Requiring Attention
166
+
167
+ ### 1. [CRITICAL] Premature Approval of Functional Defects
168
+
169
+ The AI approves code with known issues...
170
+
171
+ **Frequency:** 10-15% of traces
172
+
173
+ **Example traces:**
174
+ - https://www.lmnr.ai/traces/a0dbd0c7-116d-776b-9c4b-c7f5f4d0a97e
175
+ - https://www.lmnr.ai/traces/5de277ff-38f2-e173-4b03-c7332301ea58
176
+
177
+ ## Recommended Fixes
178
+
179
+ ### 1. [HIGH PRIORITY] Enforce Logic Verification Over Style
180
+
181
+ Modify the prompt to separate style from logic analysis...
182
+
183
+ *Fixes: Premature Approval of Functional Defects*
184
+ ```
185
+
186
+ ## Custom Prompt Templates
187
+
188
+ Create a Jinja2 template (`.j2`) with access to:
189
+
190
+ | Variable | Type | Description |
191
+ |----------|------|-------------|
192
+ | `signals` | `list[dict]` | List of parsed signal objects |
193
+ | `num_signals` | `int` | Number of signals |
194
+ | `signal_name` | `str` | Name of the signal |
195
+
196
+ Each signal object contains:
197
+ - `trace_url`: Full URL to view the trace in Laminar
198
+ - `trace_id`: Trace UUID
199
+ - `timestamp`: When the signal was created
200
+ - `payload`: Parsed payload as dict
201
+ - `payload_json`: Formatted JSON string
202
+ - All payload fields flattened to top level
203
+
204
+ ## Laminar SQL API Reference
205
+
206
+ The script uses Laminar's SQL API at `/v1/sql/query`. Key tables:
207
+
208
+ - `signal_events`: Signal events with trace_id and payload
209
+ - `traces`: Trace-level aggregates
210
+
211
+ See [Laminar SQL Editor documentation](https://docs.laminar.sh/platform/sql-editor) for details.