@openhands/extensions 0.17.0 → 0.19.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 (76) hide show
  1. package/.agents/skills/custom-codereview-guide.md +7 -0
  2. package/.github/dependabot.yml +24 -0
  3. package/.github/scripts/check_issue_readiness.py +272 -0
  4. package/.github/scripts/check_pr_description.py +226 -0
  5. package/.github/scripts/post-readiness-comment.mjs +184 -0
  6. package/.github/scripts/refresh_linked_pr_checks.py +171 -0
  7. package/.github/workflows/issue-readiness-check.yml +142 -0
  8. package/.github/workflows/pr-description-check.yml +37 -0
  9. package/.release-please-manifest.json +1 -1
  10. package/AGENTS.md +16 -0
  11. package/README.md +13 -2
  12. package/automations/README.md +3 -2
  13. package/automations/bundle-index.js +10 -1
  14. package/automations/catalog/github-agents-md-maintainer/manifest.json +101 -0
  15. package/automations/catalog/github-issue-to-pr/manifest.json +112 -0
  16. package/automations/catalog/github-pr-reviewer/manifest.json +6 -0
  17. package/automations/catalog/github-repo-monitor/manifest.json +6 -0
  18. package/automations/catalog/jira-issue-to-bitbucket-pr/manifest.json +23 -0
  19. package/automations/catalog/jira-issue-to-gitlab-mr/manifest.json +23 -0
  20. package/automations/catalog/linear-issue-to-bitbucket-pr/manifest.json +23 -0
  21. package/automations/catalog/linear-issue-to-github-pr/manifest.json +23 -0
  22. package/automations/catalog/linear-issue-to-gitlab-mr/manifest.json +23 -0
  23. package/automations/catalog/news-digest/manifest.json +81 -0
  24. package/automations/catalog/qa-changes/manifest.json +72 -0
  25. package/automations/catalog/slack-github-linear-daily-organization.json +27 -0
  26. package/automations/catalog-index.js +25 -7
  27. package/automations/catalog.schema.json +35 -2
  28. package/automations/index.d.ts +34 -1
  29. package/automations/interface.json +3 -1
  30. package/integrations/catalog/datadog.json +2 -2
  31. package/integrations/catalog/gitlab.json +41 -0
  32. package/integrations/catalog/notion.json +1 -1
  33. package/integrations/catalog-index.js +71 -69
  34. package/integrations/index.d.ts +1 -1
  35. package/marketplaces/openhands-extensions.json +59 -6
  36. package/package.json +1 -1
  37. package/plugins/pr-review/scripts/prompt.py +2 -0
  38. package/pyproject.toml +1 -1
  39. package/python/openhands_extensions/_version.py +1 -1
  40. package/skills/github-agents-md-maintainer/.plugin/plugin.json +19 -0
  41. package/skills/github-agents-md-maintainer/README.md +59 -0
  42. package/skills/github-agents-md-maintainer/SKILL.md +271 -0
  43. package/skills/github-agents-md-maintainer/commands/agents-md-setup.md +8 -0
  44. package/skills/github-agents-md-maintainer/references/state-schema.md +113 -0
  45. package/skills/github-agents-md-maintainer/scripts/main.py +1187 -0
  46. package/skills/github-issue-to-pr/.plugin/plugin.json +19 -0
  47. package/skills/github-issue-to-pr/README.md +84 -0
  48. package/skills/github-issue-to-pr/SKILL.md +373 -0
  49. package/skills/github-issue-to-pr/commands/issue-to-pr-setup.md +8 -0
  50. package/skills/github-issue-to-pr/references/state-schema.md +139 -0
  51. package/skills/github-issue-to-pr/scripts/main.py +1305 -0
  52. package/skills/github-pr-reviewer/SKILL.md +2 -1
  53. package/skills/github-pr-reviewer/scripts/main.py +45 -4
  54. package/skills/github-pr-reviewer/tests/test_main.py +74 -0
  55. package/skills/index.js +38 -2
  56. package/skills/news-digest/.plugin/plugin.json +19 -0
  57. package/skills/news-digest/README.md +71 -0
  58. package/skills/news-digest/SKILL.md +284 -0
  59. package/skills/news-digest/commands/news-digest-setup.md +8 -0
  60. package/skills/news-digest/references/state-schema.md +178 -0
  61. package/skills/news-digest/scripts/main.py +1250 -0
  62. package/skills/openhands-sdk/SKILL.md +1 -0
  63. package/skills/ticket-to-code-change/.plugin/plugin.json +21 -0
  64. package/skills/ticket-to-code-change/README.md +5 -0
  65. package/skills/ticket-to-code-change/SKILL.md +50 -0
  66. package/skills/ticket-to-code-change/commands/ticket-to-code-change-setup.md +8 -0
  67. package/tests/fixtures/automations/github-repo-monitor.json +77 -0
  68. package/tests/test_automation_setup.py +56 -0
  69. package/tests/test_catalogs.py +5 -3
  70. package/tests/test_check_issue_readiness.py +135 -0
  71. package/tests/test_check_pr_description.py +228 -0
  72. package/tests/test_github_agents_md_maintainer.py +352 -0
  73. package/tests/test_github_issue_to_pr.py +539 -0
  74. package/tests/test_news_digest.py +646 -0
  75. package/tests/test_pr_review_prompt.py +10 -0
  76. package/tests/test_refresh_linked_pr_checks.py +130 -0
@@ -21,8 +21,17 @@
21
21
  "pattern": "^[a-z0-9]+(-[a-z0-9]+)*$",
22
22
  "description": "Stable identifier. Must equal the name of the directory holding this manifest."
23
23
  },
24
+ "version": {
25
+ "type": "string",
26
+ "pattern": "^\\d+\\.\\d+\\.\\d+$",
27
+ "description": "Semantic version of this template. Present only on entries whose direct setup sends provenance to the automation service; bump it when the prompt, form, or filter changes meaningfully."
28
+ },
24
29
  "name": { "type": "string", "minLength": 1 },
25
30
  "category": { "type": "string", "minLength": 1 },
31
+ "icon": {
32
+ "description": "The glyph the card shows, named from the same closed set the interface manifest's navigation uses. Absent means the host derives one from requires.integrations, which is right for an automation whose identity is the service it talks to and wrong for one whose identity is the work itself.",
33
+ "enum": ["layout-dashboard", "sparkles", "bot", "circle-alert", "activity", "timer"]
34
+ },
26
35
  "description": { "type": "string", "minLength": 1 },
27
36
  "requires": { "$ref": "#/$defs/requires" },
28
37
  "popularityRank": { "type": "integer", "minimum": 0 },
@@ -37,9 +46,34 @@
37
46
  "type": "string",
38
47
  "minLength": 1
39
48
  },
49
+ "impact": {
50
+ "description": "The value statement the host renders from an installed automation's run history. Declare it only when its basis honestly backs the phrase: for completed-runs, one completed run must always perform exactly one of the stated units of work, manually dispatched runs included, so a poller that can complete having produced nothing must phrase the run itself, never the downstream outcome.",
51
+ "type": "object",
52
+ "additionalProperties": false,
53
+ "required": ["basis", "one", "other"],
54
+ "properties": {
55
+ "basis": {
56
+ "description": "What the statement counts. The only basis today is the automation's lifetime COMPLETED-run count; a host that meets a basis it does not know renders nothing.",
57
+ "const": "completed-runs"
58
+ },
59
+ "one": { "$ref": "#/$defs/impactCopy" },
60
+ "other": {
61
+ "$comment": "The plural phrase must show the number; the singular may spell it out instead.",
62
+ "allOf": [{ "$ref": "#/$defs/impactCopy" }],
63
+ "pattern": "\\{\\{count\\}\\}"
64
+ }
65
+ }
66
+ },
40
67
  "setup": { "$ref": "#/$defs/setup" }
41
68
  },
42
69
  "$defs": {
70
+ "impactCopy": {
71
+ "description": "One value-statement phrase. Markup-free, and the only placeholder it may open is {{count}}, which the host substitutes with the formatted run count.",
72
+ "allOf": [
73
+ { "$ref": "#/$defs/copy" },
74
+ { "not": { "pattern": "\\{\\{(?!count\\}\\})" } }
75
+ ]
76
+ },
43
77
  "setup": {
44
78
  "description": "The extension-owned configuration experience for this automation: what the deployment must support, what must be connected first, what the user is asked, how a draft is validated, what request is sent, and which analytics stages are emitted. It never describes what the automation does at runtime - that is the preset, owned by OpenHands/automation. It is data, not code - there is no key that accepts JavaScript, no markup in copy, no free-form URL, and no secret value.",
45
79
  "type": "object",
@@ -233,9 +267,8 @@
233
267
  "required": ["integrations"],
234
268
  "properties": {
235
269
  "integrations": {
236
- "description": "Keyed by integration id, each of which must resolve to an integrations/catalog entry.",
270
+ "description": "Keyed by integration id, each of which must resolve to an integrations/catalog entry. Empty when the automation connects to nothing and needs no credential - a requirement worth stating, and the reason the key stays required rather than becoming optional.",
237
271
  "type": "object",
238
- "minProperties": 1,
239
272
  "propertyNames": { "type": "string", "minLength": 1 },
240
273
  "additionalProperties": {
241
274
  "type": "object",
@@ -1,7 +1,24 @@
1
1
  export interface RecommendedAutomation {
2
2
  id: string;
3
+ /**
4
+ * Semantic version of this template. Present only on entries whose direct
5
+ * setup sends provenance to the automation service; bumped when the prompt,
6
+ * form, or filter changes meaningfully.
7
+ */
8
+ version?: string;
3
9
  name: string;
4
10
  category: string;
11
+ /**
12
+ * The glyph the card shows, from the closed set the interface manifest's
13
+ * navigation uses. Absent means the host derives one from the integrations.
14
+ */
15
+ icon?:
16
+ | "layout-dashboard"
17
+ | "sparkles"
18
+ | "bot"
19
+ | "circle-alert"
20
+ | "activity"
21
+ | "timer";
5
22
  description: string;
6
23
  requires: AutomationPrerequisites;
7
24
  popularityRank: number;
@@ -13,10 +30,26 @@ export interface RecommendedAutomation {
13
30
  */
14
31
  skill?: string;
15
32
  exampleImplementation: string;
33
+ /**
34
+ * The value statement the host renders from an installed automation's run
35
+ * history. Declared only when its basis honestly backs the phrase: for
36
+ * `completed-runs`, one completed run must always perform exactly one of
37
+ * the stated units of work, manually dispatched runs included, so a poller
38
+ * that can complete having produced nothing phrases the run itself, never
39
+ * the downstream outcome. `{{count}}` is the host-substituted run count; a
40
+ * host that meets a basis it does not know renders nothing.
41
+ */
42
+ impact?: AutomationImpact;
16
43
  /** Present when this automation ships an extension-owned setup experience. */
17
44
  setup?: AutomationSetup;
18
45
  }
19
46
 
47
+ export interface AutomationImpact {
48
+ basis: "completed-runs";
49
+ one: string;
50
+ other: string;
51
+ }
52
+
20
53
  /**
21
54
  * The extension-owned configuration experience for one automation.
22
55
  *
@@ -83,7 +116,7 @@ export interface AutomationIntegrationRequirement {
83
116
  }
84
117
 
85
118
  export interface AutomationPrerequisites {
86
- /** Keyed by integration catalog id. */
119
+ /** Keyed by integration catalog id. Empty when the automation needs nothing connected. */
87
120
  integrations: Record<string, AutomationIntegrationRequirement>;
88
121
  /** Deployment capabilities this automation cannot run without. */
89
122
  features?: string[];
@@ -150,7 +150,9 @@
150
150
  },
151
151
  "featuredAutomationIds": [
152
152
  "github-pr-reviewer",
153
- "github-repo-monitor",
153
+ "github-agents-md-maintainer",
154
+ "news-digest",
155
+ "github-issue-to-pr",
154
156
  "slack-channel-monitor"
155
157
  ],
156
158
  "responderIntegrationIds": ["github", "slack"]
@@ -43,7 +43,7 @@
43
43
  "urlEditable": true,
44
44
  "headerFields": [
45
45
  {
46
- "key": "DD_API_KEY",
46
+ "key": "DD-API-KEY",
47
47
  "label": "Datadog API key",
48
48
  "type": "password",
49
49
  "required": true,
@@ -51,7 +51,7 @@
51
51
  "helperText": "Created in Datadog under Organization Settings → API Keys. Used to authenticate to the Datadog MCP server. See the [setup docs](https://docs.datadoghq.com/mcp_server/setup/?tab=other#authentication)."
52
52
  },
53
53
  {
54
- "key": "DD_APPLICATION_KEY",
54
+ "key": "DD-APPLICATION-KEY",
55
55
  "label": "Datadog Application key",
56
56
  "type": "password",
57
57
  "required": true,
@@ -0,0 +1,41 @@
1
+ {
2
+ "id": "gitlab",
3
+ "name": "GitLab",
4
+ "description": "Repositories, issues, merge requests, and CI/CD workflows through the GitLab API.",
5
+ "categories": [
6
+ "Engineering",
7
+ "Source control"
8
+ ],
9
+ "appUrl": "https://gitlab.com",
10
+ "docsUrl": "https://docs.gitlab.com/api/",
11
+ "notes": "Supports GitLab.com projects through a personal access token.",
12
+ "popularityRank": 60,
13
+ "connectionOptions": [
14
+ {
15
+ "id": "api",
16
+ "provider": "http",
17
+ "auth": {
18
+ "strategy": "api_key",
19
+ "apiKeyHeaderName": "PRIVATE-TOKEN",
20
+ "credentialLabel": "Personal access token",
21
+ "credentialPlaceholder": "glpat-...",
22
+ "credentialHelp": "Create a token in [GitLab access token settings](https://gitlab.com/-/user_settings/personal_access_tokens) with the api scope.",
23
+ "credentialSecretName": "GITLAB_TOKEN",
24
+ "saveCredentialAsSecretByDefault": true
25
+ },
26
+ "http": {
27
+ "apiBaseUrl": "https://gitlab.com/api/v4",
28
+ "openApiUrl": "https://gitlab.com/gitlab-org/gitlab/-/raw/master/doc/api/openapi/openapi_v2.yaml"
29
+ }
30
+ }
31
+ ],
32
+ "iconBg": "var(--oh-surface)",
33
+ "logoUrl": "https://cdn.simpleicons.org/gitlab/FC6D26",
34
+ "keywords": [
35
+ "git",
36
+ "merge request",
37
+ "repo",
38
+ "issues",
39
+ "code"
40
+ ]
41
+ }
@@ -52,7 +52,7 @@
52
52
  ],
53
53
  "envFields": [
54
54
  {
55
- "key": "NOTION_API_KEY",
55
+ "key": "NOTION_TOKEN",
56
56
  "label": "Internal integration token",
57
57
  "type": "password",
58
58
  "placeholder": "ntn_...",
@@ -9,75 +9,76 @@ import entry3 from "./catalog/atlassian.json" with { type: "json" };
9
9
  import entry4 from "./catalog/linear.json" with { type: "json" };
10
10
  import entry5 from "./catalog/notion.json" with { type: "json" };
11
11
  import entry6 from "./catalog/vanta.json" with { type: "json" };
12
- import entry7 from "./catalog/ordinal.json" with { type: "json" };
13
- import entry8 from "./catalog/elevenlabs.json" with { type: "json" };
14
- import entry9 from "./catalog/bitbucket.json" with { type: "json" };
15
- import entry10 from "./catalog/xero.json" with { type: "json" };
16
- import entry11 from "./catalog/quickbooks.json" with { type: "json" };
17
- import entry12 from "./catalog/sonarqube.json" with { type: "json" };
18
- import entry13 from "./catalog/okta.json" with { type: "json" };
19
- import entry14 from "./catalog/netlify.json" with { type: "json" };
20
- import entry15 from "./catalog/vercel.json" with { type: "json" };
21
- import entry16 from "./catalog/supabase.json" with { type: "json" };
22
- import entry17 from "./catalog/posthog.json" with { type: "json" };
23
- import entry18 from "./catalog/sentry.json" with { type: "json" };
24
- import entry19 from "./catalog/datadog.json" with { type: "json" };
25
- import entry20 from "./catalog/canva.json" with { type: "json" };
26
- import entry21 from "./catalog/miro.json" with { type: "json" };
27
- import entry22 from "./catalog/webflow.json" with { type: "json" };
28
- import entry23 from "./catalog/zoom.json" with { type: "json" };
29
- import entry24 from "./catalog/discord.json" with { type: "json" };
30
- import entry25 from "./catalog/stripe.json" with { type: "json" };
31
- import entry26 from "./catalog/intercom.json" with { type: "json" };
32
- import entry27 from "./catalog/hubspot.json" with { type: "json" };
33
- import entry28 from "./catalog/salesforce.json" with { type: "json" };
34
- import entry29 from "./catalog/sharepoint.json" with { type: "json" };
35
- import entry30 from "./catalog/onedrive.json" with { type: "json" };
36
- import entry31 from "./catalog/microsoft-teams.json" with { type: "json" };
37
- import entry32 from "./catalog/microsoft-outlook.json" with { type: "json" };
38
- import entry33 from "./catalog/box.json" with { type: "json" };
39
- import entry34 from "./catalog/airtable.json" with { type: "json" };
40
- import entry35 from "./catalog/monday.json" with { type: "json" };
41
- import entry36 from "./catalog/trello.json" with { type: "json" };
42
- import entry37 from "./catalog/asana.json" with { type: "json" };
43
- import entry38 from "./catalog/confluence.json" with { type: "json" };
44
- import entry39 from "./catalog/jira.json" with { type: "json" };
45
- import entry40 from "./catalog/google-calendar.json" with { type: "json" };
46
- import entry41 from "./catalog/gmail.json" with { type: "json" };
47
- import entry42 from "./catalog/google-sheets.json" with { type: "json" };
48
- import entry43 from "./catalog/google-drive.json" with { type: "json" };
49
- import entry44 from "./catalog/figma.json" with { type: "json" };
50
- import entry45 from "./catalog/google-docs.json" with { type: "json" };
51
- import entry46 from "./catalog/apify.json" with { type: "json" };
52
- import entry47 from "./catalog/atlassian-rovo.json" with { type: "json" };
53
- import entry48 from "./catalog/brave-search.json" with { type: "json" };
54
- import entry49 from "./catalog/browser-mcp.json" with { type: "json" };
55
- import entry50 from "./catalog/clickhouse.json" with { type: "json" };
56
- import entry51 from "./catalog/cloudflare-bindings.json" with { type: "json" };
57
- import entry52 from "./catalog/cloudflare-browser-rendering.json" with { type: "json" };
58
- import entry53 from "./catalog/cloudflare-builds.json" with { type: "json" };
59
- import entry54 from "./catalog/cloudflare-docs.json" with { type: "json" };
60
- import entry55 from "./catalog/cloudflare-observability.json" with { type: "json" };
61
- import entry56 from "./catalog/deepwiki.json" with { type: "json" };
62
- import entry57 from "./catalog/everything.json" with { type: "json" };
63
- import entry58 from "./catalog/exa.json" with { type: "json" };
64
- import entry59 from "./catalog/fetch.json" with { type: "json" };
65
- import entry60 from "./catalog/filesystem.json" with { type: "json" };
66
- import entry61 from "./catalog/firecrawl.json" with { type: "json" };
67
- import entry62 from "./catalog/git.json" with { type: "json" };
68
- import entry63 from "./catalog/huggingface.json" with { type: "json" };
69
- import entry64 from "./catalog/kagi.json" with { type: "json" };
70
- import entry65 from "./catalog/memory.json" with { type: "json" };
71
- import entry66 from "./catalog/mongodb.json" with { type: "json" };
72
- import entry67 from "./catalog/neon.json" with { type: "json" };
73
- import entry68 from "./catalog/obsidian.json" with { type: "json" };
74
- import entry69 from "./catalog/paypal.json" with { type: "json" };
75
- import entry70 from "./catalog/playwright.json" with { type: "json" };
76
- import entry71 from "./catalog/redis.json" with { type: "json" };
77
- import entry72 from "./catalog/resend.json" with { type: "json" };
78
- import entry73 from "./catalog/sequential-thinking.json" with { type: "json" };
79
- import entry74 from "./catalog/superhuman-mail.json" with { type: "json" };
80
- import entry75 from "./catalog/time.json" with { type: "json" };
12
+ import entry7 from "./catalog/gitlab.json" with { type: "json" };
13
+ import entry8 from "./catalog/ordinal.json" with { type: "json" };
14
+ import entry9 from "./catalog/elevenlabs.json" with { type: "json" };
15
+ import entry10 from "./catalog/bitbucket.json" with { type: "json" };
16
+ import entry11 from "./catalog/xero.json" with { type: "json" };
17
+ import entry12 from "./catalog/quickbooks.json" with { type: "json" };
18
+ import entry13 from "./catalog/sonarqube.json" with { type: "json" };
19
+ import entry14 from "./catalog/okta.json" with { type: "json" };
20
+ import entry15 from "./catalog/netlify.json" with { type: "json" };
21
+ import entry16 from "./catalog/vercel.json" with { type: "json" };
22
+ import entry17 from "./catalog/supabase.json" with { type: "json" };
23
+ import entry18 from "./catalog/posthog.json" with { type: "json" };
24
+ import entry19 from "./catalog/sentry.json" with { type: "json" };
25
+ import entry20 from "./catalog/datadog.json" with { type: "json" };
26
+ import entry21 from "./catalog/canva.json" with { type: "json" };
27
+ import entry22 from "./catalog/miro.json" with { type: "json" };
28
+ import entry23 from "./catalog/webflow.json" with { type: "json" };
29
+ import entry24 from "./catalog/zoom.json" with { type: "json" };
30
+ import entry25 from "./catalog/discord.json" with { type: "json" };
31
+ import entry26 from "./catalog/stripe.json" with { type: "json" };
32
+ import entry27 from "./catalog/intercom.json" with { type: "json" };
33
+ import entry28 from "./catalog/hubspot.json" with { type: "json" };
34
+ import entry29 from "./catalog/salesforce.json" with { type: "json" };
35
+ import entry30 from "./catalog/sharepoint.json" with { type: "json" };
36
+ import entry31 from "./catalog/onedrive.json" with { type: "json" };
37
+ import entry32 from "./catalog/microsoft-teams.json" with { type: "json" };
38
+ import entry33 from "./catalog/microsoft-outlook.json" with { type: "json" };
39
+ import entry34 from "./catalog/box.json" with { type: "json" };
40
+ import entry35 from "./catalog/airtable.json" with { type: "json" };
41
+ import entry36 from "./catalog/monday.json" with { type: "json" };
42
+ import entry37 from "./catalog/trello.json" with { type: "json" };
43
+ import entry38 from "./catalog/asana.json" with { type: "json" };
44
+ import entry39 from "./catalog/confluence.json" with { type: "json" };
45
+ import entry40 from "./catalog/jira.json" with { type: "json" };
46
+ import entry41 from "./catalog/google-calendar.json" with { type: "json" };
47
+ import entry42 from "./catalog/gmail.json" with { type: "json" };
48
+ import entry43 from "./catalog/google-sheets.json" with { type: "json" };
49
+ import entry44 from "./catalog/google-drive.json" with { type: "json" };
50
+ import entry45 from "./catalog/figma.json" with { type: "json" };
51
+ import entry46 from "./catalog/google-docs.json" with { type: "json" };
52
+ import entry47 from "./catalog/apify.json" with { type: "json" };
53
+ import entry48 from "./catalog/atlassian-rovo.json" with { type: "json" };
54
+ import entry49 from "./catalog/brave-search.json" with { type: "json" };
55
+ import entry50 from "./catalog/browser-mcp.json" with { type: "json" };
56
+ import entry51 from "./catalog/clickhouse.json" with { type: "json" };
57
+ import entry52 from "./catalog/cloudflare-bindings.json" with { type: "json" };
58
+ import entry53 from "./catalog/cloudflare-browser-rendering.json" with { type: "json" };
59
+ import entry54 from "./catalog/cloudflare-builds.json" with { type: "json" };
60
+ import entry55 from "./catalog/cloudflare-docs.json" with { type: "json" };
61
+ import entry56 from "./catalog/cloudflare-observability.json" with { type: "json" };
62
+ import entry57 from "./catalog/deepwiki.json" with { type: "json" };
63
+ import entry58 from "./catalog/everything.json" with { type: "json" };
64
+ import entry59 from "./catalog/exa.json" with { type: "json" };
65
+ import entry60 from "./catalog/fetch.json" with { type: "json" };
66
+ import entry61 from "./catalog/filesystem.json" with { type: "json" };
67
+ import entry62 from "./catalog/firecrawl.json" with { type: "json" };
68
+ import entry63 from "./catalog/git.json" with { type: "json" };
69
+ import entry64 from "./catalog/huggingface.json" with { type: "json" };
70
+ import entry65 from "./catalog/kagi.json" with { type: "json" };
71
+ import entry66 from "./catalog/memory.json" with { type: "json" };
72
+ import entry67 from "./catalog/mongodb.json" with { type: "json" };
73
+ import entry68 from "./catalog/neon.json" with { type: "json" };
74
+ import entry69 from "./catalog/obsidian.json" with { type: "json" };
75
+ import entry70 from "./catalog/paypal.json" with { type: "json" };
76
+ import entry71 from "./catalog/playwright.json" with { type: "json" };
77
+ import entry72 from "./catalog/redis.json" with { type: "json" };
78
+ import entry73 from "./catalog/resend.json" with { type: "json" };
79
+ import entry74 from "./catalog/sequential-thinking.json" with { type: "json" };
80
+ import entry75 from "./catalog/superhuman-mail.json" with { type: "json" };
81
+ import entry76 from "./catalog/time.json" with { type: "json" };
81
82
 
82
83
  export const INTEGRATION_CATALOG_ENTRIES = [
83
84
  entry0,
@@ -156,4 +157,5 @@ export const INTEGRATION_CATALOG_ENTRIES = [
156
157
  entry73,
157
158
  entry74,
158
159
  entry75,
160
+ entry76,
159
161
  ];
@@ -17,7 +17,7 @@ export type IntegrationTransport =
17
17
  apiKeyOptional?: boolean;
18
18
  /**
19
19
  * Named request headers the user must supply (e.g. Datadog's
20
- * `DD_API_KEY` / `DD_APPLICATION_KEY`). Values are sent verbatim as
20
+ * `DD-API-KEY` / `DD-APPLICATION-KEY`). Values are sent verbatim as
21
21
  * headers on every MCP request. The direct analog of stdio's
22
22
  * `envFields`: each entry renders one input in the install modal,
23
23
  * `type: "password"` entries are secret-saved by default, and
@@ -5,7 +5,7 @@
5
5
  "email": "contact@all-hands.dev"
6
6
  },
7
7
  "metadata": {
8
- "description": "Official skills and plugins for OpenHands \u2014 the open-source AI software engineer.",
8
+ "description": "Official skills and plugins for OpenHands — the open-source AI software engineer.",
9
9
  "maintainer": "OpenHands",
10
10
  "homepage": "https://github.com/OpenHands/extensions"
11
11
  },
@@ -13,7 +13,7 @@
13
13
  {
14
14
  "name": "agent-creator",
15
15
  "source": "./skills/agent-creator",
16
- "description": "Create file-based sub-agents as Markdown files \u2014 no Python code required. Guides the user through a structured interview and generates a ready-to-deploy .md agent file following the OpenHands SDK specification.",
16
+ "description": "Create file-based sub-agents as Markdown files — no Python code required. Guides the user through a structured interview and generates a ready-to-deploy .md agent file following the OpenHands SDK specification.",
17
17
  "category": "agent-authoring",
18
18
  "keywords": [
19
19
  "agent",
@@ -438,7 +438,7 @@
438
438
  {
439
439
  "name": "openhands",
440
440
  "source": "./plugins/openhands",
441
- "description": "Unified OpenHands plugin \u2014 bundles Cloud CLI, REST API (openhands-api), and Automations (openhands-automation) into a single plugin.",
441
+ "description": "Unified OpenHands plugin — bundles Cloud CLI, REST API (openhands-api), and Automations (openhands-automation) into a single plugin.",
442
442
  "category": "openhands",
443
443
  "keywords": [
444
444
  "openhands",
@@ -480,7 +480,7 @@
480
480
  {
481
481
  "name": "pr-review",
482
482
  "source": "./plugins/pr-review",
483
- "description": "Automated PR code review \u2014 analyzes diffs and posts inline review comments via the GitHub API.",
483
+ "description": "Automated PR code review — analyzes diffs and posts inline review comments via the GitHub API.",
484
484
  "category": "code-quality",
485
485
  "keywords": [
486
486
  "pr-review",
@@ -492,7 +492,7 @@
492
492
  {
493
493
  "name": "qa-changes",
494
494
  "source": "./plugins/qa-changes",
495
- "description": "Validate pull request changes by actually running the code \u2014 setting up the environment, exercising changed behavior, and posting a structured QA report.",
495
+ "description": "Validate pull request changes by actually running the code — setting up the environment, exercising changed behavior, and posting a structured QA report.",
496
496
  "category": "quality-assurance",
497
497
  "keywords": [
498
498
  "qa",
@@ -656,7 +656,7 @@
656
656
  {
657
657
  "name": "iterate",
658
658
  "source": "./skills/iterate",
659
- "description": "Iterate on a GitHub pull request \u2014 drive it through CI, code review, and QA until merge-ready. Monitors state, fixes failures, addresses review feedback, retries flaky checks, and pushes fixes in one continuous loop.",
659
+ "description": "Iterate on a GitHub pull request — drive it through CI, code review, and QA until merge-ready. Monitors state, fixes failures, addresses review feedback, retries flaky checks, and pushes fixes in one continuous loop.",
660
660
  "category": "code-quality",
661
661
  "keywords": [
662
662
  "github",
@@ -681,6 +681,32 @@
681
681
  "official"
682
682
  ]
683
683
  },
684
+ {
685
+ "name": "github-agents-md-maintainer",
686
+ "source": "./skills/github-agents-md-maintainer",
687
+ "description": "Create an automation that keeps AGENTS.md current in one or more GitHub repositories. On a schedule an agent reads the repository, creates or updates AGENTS.md, and opens a pull request - staying quiet while one of its own pull requests is still open.",
688
+ "category": "automations",
689
+ "keywords": [
690
+ "github",
691
+ "agents-md",
692
+ "documentation",
693
+ "maintenance",
694
+ "automation"
695
+ ]
696
+ },
697
+ {
698
+ "name": "github-issue-to-pr",
699
+ "source": "./skills/github-issue-to-pr",
700
+ "description": "Create an automation that implements GitHub issues when a configurable trigger label is applied. Clones the default branch for the agent, then commits, pushes, and opens the pull request itself - the agent is handed no GitHub credential.",
701
+ "category": "automations",
702
+ "keywords": [
703
+ "github",
704
+ "issue",
705
+ "pull-request",
706
+ "automation",
707
+ "integration"
708
+ ]
709
+ },
684
710
  {
685
711
  "name": "github-pr-reviewer",
686
712
  "source": "./skills/github-pr-reviewer",
@@ -781,6 +807,33 @@
781
807
  "jira",
782
808
  "atlassian"
783
809
  ]
810
+ },
811
+ {
812
+ "name": "news-digest",
813
+ "source": "./skills/news-digest",
814
+ "description": "Create an automation that reads public RSS and Atom feeds on a schedule, keeps what is new and matches the configured topics, and has an agent write a short digest of it. Needs no credentials.",
815
+ "category": "automations",
816
+ "keywords": [
817
+ "news",
818
+ "rss",
819
+ "atom",
820
+ "digest"
821
+ ]
822
+ },
823
+ {
824
+ "name": "ticket-to-code-change",
825
+ "source": "./skills/ticket-to-code-change",
826
+ "description": "Set up Jira or Linear ticket-to-code-change automations for GitHub, GitLab, and Bitbucket.",
827
+ "category": "automations",
828
+ "keywords": [
829
+ "jira",
830
+ "linear",
831
+ "github",
832
+ "gitlab",
833
+ "bitbucket",
834
+ "pull-request",
835
+ "automation"
836
+ ]
784
837
  }
785
838
  ]
786
839
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@openhands/extensions",
3
- "version": "0.17.0",
3
+ "version": "0.19.0",
4
4
  "description": "Public OpenHands extension catalogs for skills, plugins, integrations, and automation templates.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -78,6 +78,8 @@ When posting a review, keep the review body brief unless your active review inst
78
78
 
79
79
  For dependency update PRs, do **NOT** approve a target version that was published less than 7 days ago. First-party packages maintained by the same organization as the reviewed repository are intentionally excluded from this 7-day waiting rule, but still scrutinize them for supply-chain risk.
80
80
 
81
+ Before reviewing, you MUST read the repository's own guidance to understand the repo first: read `AGENTS.md` at the repository root (and any nested `AGENTS.md` covering the changed files), plus other relevant docs when present — e.g. `CONTRIBUTING.md`, `CLAUDE.md`, `.cursorrules`, and any review or coding-guideline docs. Apply that guidance to your review.
82
+
81
83
  Review the PR changes below and identify issues that need to be addressed.
82
84
 
83
85
  ## Pull Request Information
package/pyproject.toml CHANGED
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "openhands-extensions"
3
- version = "0.17.0"
3
+ version = "0.19.0"
4
4
  description = "OpenHands extensions, plugins, and skills (Python bindings for the integration catalog)"
5
5
  requires-python = ">=3.12"
6
6
  dependencies = [
@@ -18,7 +18,7 @@ from importlib.metadata import PackageNotFoundError, version
18
18
  #: Kept in lock-step with pyproject.toml/package.json by the version test.
19
19
  #: release-please only rewrites a semver found on the annotated line itself, so
20
20
  #: the annotation must stay on the assignment; on its own line it is a no-op.
21
- _FALLBACK_VERSION = "0.17.0" # x-release-please-version
21
+ _FALLBACK_VERSION = "0.19.0" # x-release-please-version
22
22
 
23
23
  try:
24
24
  __version__: str = version("openhands-extensions")
@@ -0,0 +1,19 @@
1
+ {
2
+ "name": "github-agents-md-maintainer",
3
+ "version": "1.0.0",
4
+ "description": "Create an automation that keeps AGENTS.md current in one or more GitHub repositories. On a schedule it clones the default branch, has an agent read the repository and create or update AGENTS.md, and opens a pull request - staying quiet while one of its own pull requests is still open.",
5
+ "author": {
6
+ "name": "OpenHands",
7
+ "email": "contact@all-hands.dev"
8
+ },
9
+ "homepage": "https://github.com/OpenHands/extensions",
10
+ "repository": "https://github.com/OpenHands/extensions",
11
+ "license": "MIT",
12
+ "keywords": [
13
+ "github",
14
+ "agents-md",
15
+ "documentation",
16
+ "maintenance",
17
+ "automation"
18
+ ]
19
+ }
@@ -0,0 +1,59 @@
1
+ # AGENTS.md Maintainer
2
+
3
+ Create an automation that keeps `AGENTS.md` current in one or more GitHub
4
+ repositories, on a schedule.
5
+
6
+ ## Trigger
7
+
8
+ This skill is activated by:
9
+
10
+ - `/agents-md:setup`
11
+
12
+ ## Features
13
+
14
+ - Creates `AGENTS.md` when it is missing, updates it when the repository has
15
+ moved on, and leaves it alone when it is accurate
16
+ - Runs weekly by default, keyed by ISO week, so extra runs inside a week are
17
+ harmless
18
+ - Stays quiet while one of its own pull requests is still open, instead of
19
+ stacking a pull request per week over the same file
20
+ - Maintains several repositories from one automation, each with its own state
21
+ - Clones the default branch for the agent and removes the clone when the run ends
22
+ - Lets the agent open the pull request, and opens it in Python when the agent
23
+ did not - a failed push or a dead conversation never loses the work
24
+ - Opens draft pull requests by default, titled `docs: update AGENTS.md` or
25
+ `docs: add AGENTS.md`
26
+
27
+ ## What the agent is asked to do
28
+
29
+ Read the repository - layout, the build, test, lint and format commands as they
30
+ are actually defined, language and framework versions, contributing docs - and
31
+ edit `AGENTS.md` to match. It is told to treat an existing file as someone
32
+ else's writing: correct what is wrong, add what is missing, remove what no
33
+ longer exists, and leave the rest alone. Only knowledge that helps in most
34
+ future tasks belongs there, which is the rule the `agent-memory` skill sets out;
35
+ task-specific notes and unverified commands do not.
36
+
37
+ ## Prerequisites
38
+
39
+ Set `GITHUB_PERSONAL_ACCESS_TOKEN` in OpenHands Settings -> Secrets, able to
40
+ read the repositories, **write contents** (to push the branch) and **write pull
41
+ requests**. No issue permission is needed - this automation never comments on an
42
+ issue.
43
+
44
+ The automation runtime must have `git` available.
45
+
46
+ ## Quick Start
47
+
48
+ Ask OpenHands:
49
+
50
+ > "Set up an AGENTS.md maintenance automation for `myorg/backend` and
51
+ > `myorg/frontend`, weekly."
52
+
53
+ ## See Also
54
+
55
+ - [SKILL.md](SKILL.md) - Full setup workflow reference
56
+ - [references/state-schema.md](references/state-schema.md) - State document and
57
+ task lifecycle
58
+ - [../agent-memory/SKILL.md](../agent-memory/SKILL.md) - What belongs in an
59
+ AGENTS.md, and what does not