@openhands/extensions 0.16.0 → 0.18.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.
- package/.release-please-manifest.json +1 -1
- package/AGENTS.md +1 -0
- package/README.md +5 -2
- package/automations/README.md +78 -11
- package/automations/bundle-index.js +18 -0
- package/automations/catalog/github-agents-md-maintainer/manifest.json +96 -0
- package/automations/catalog/github-issue-to-pr/manifest.json +107 -0
- package/automations/catalog/github-pr-reviewer/manifest.json +24 -7
- package/automations/catalog/github-repo-monitor/manifest.json +29 -34
- package/automations/catalog/jira-issue-to-pr/manifest.json +3 -3
- package/automations/catalog/news-digest/manifest.json +76 -0
- package/automations/catalog-index.js +13 -7
- package/automations/catalog.schema.json +112 -3
- package/automations/index.d.ts +66 -1
- package/automations/index.js +14 -0
- package/automations/interface.json +6 -2
- package/automations/interface.schema.json +6 -2
- package/index.d.ts +1 -1
- package/index.js +1 -1
- package/integrations/catalog/posthog.json +41 -3
- package/integrations/catalog/sonarqube.json +78 -0
- package/integrations/catalog-index.js +65 -63
- package/marketplaces/openhands-extensions.json +49 -0
- package/package.json +1 -1
- package/plugins/qa-changes/action.yml +1 -1
- package/pyproject.toml +1 -1
- package/python/openhands_extensions/_version.py +1 -1
- package/scripts/build-automation-catalog.mjs +30 -0
- package/scripts/build-skills-catalog.mjs +24 -7
- package/skills/add-skill/SKILL.md +1 -1
- package/skills/github-agents-md-maintainer/.plugin/plugin.json +19 -0
- package/skills/github-agents-md-maintainer/README.md +59 -0
- package/skills/github-agents-md-maintainer/SKILL.md +271 -0
- package/skills/github-agents-md-maintainer/commands/agents-md-setup.md +8 -0
- package/skills/github-agents-md-maintainer/references/state-schema.md +113 -0
- package/skills/github-agents-md-maintainer/scripts/main.py +1187 -0
- package/skills/github-issue-to-pr/.plugin/plugin.json +19 -0
- package/skills/github-issue-to-pr/README.md +84 -0
- package/skills/github-issue-to-pr/SKILL.md +373 -0
- package/skills/github-issue-to-pr/commands/issue-to-pr-setup.md +8 -0
- package/skills/github-issue-to-pr/references/state-schema.md +139 -0
- package/skills/github-issue-to-pr/scripts/main.py +1305 -0
- package/skills/github-pr-reviewer/README.md +12 -6
- package/skills/github-pr-reviewer/SKILL.md +93 -31
- package/skills/github-pr-reviewer/commands/pr-reviewer-setup.md +1 -1
- package/skills/github-pr-reviewer/references/state-schema.md +68 -18
- package/skills/github-pr-reviewer/scripts/main.py +540 -103
- package/skills/github-pr-reviewer/tests/test_main.py +503 -0
- package/skills/index.d.ts +6 -0
- package/skills/index.js +56 -17
- package/skills/jira-issue-to-pr/scripts/main.py +4 -2
- package/skills/news-digest/.plugin/plugin.json +19 -0
- package/skills/news-digest/README.md +71 -0
- package/skills/news-digest/SKILL.md +284 -0
- package/skills/news-digest/commands/news-digest-setup.md +8 -0
- package/skills/news-digest/references/state-schema.md +178 -0
- package/skills/news-digest/scripts/main.py +1250 -0
- package/skills/openhands-sdk/SKILL.md +2 -0
- package/skills/skill-creator/SKILL.md +1 -1
- package/skills/slack-channel-monitor/scripts/main.py +3 -2
- package/tests/fixtures/automations/capabilities.json +30 -0
- package/tests/fixtures/automations/github-pr-reviewer.json +319 -55
- package/tests/fixtures/automations/github-repo-monitor.json +230 -62
- package/tests/test_automation_setup.py +260 -31
- package/tests/test_catalogs.py +46 -1
- package/tests/test_github_agents_md_maintainer.py +352 -0
- package/tests/test_github_issue_to_pr.py +539 -0
- package/tests/test_news_digest.py +646 -0
- package/tests/test_skills_catalog.py +182 -36
- package/tests/test_slack_channel_monitor.py +1 -1
|
@@ -14,69 +14,70 @@ import entry8 from "./catalog/elevenlabs.json" with { type: "json" };
|
|
|
14
14
|
import entry9 from "./catalog/bitbucket.json" with { type: "json" };
|
|
15
15
|
import entry10 from "./catalog/xero.json" with { type: "json" };
|
|
16
16
|
import entry11 from "./catalog/quickbooks.json" with { type: "json" };
|
|
17
|
-
import entry12 from "./catalog/
|
|
18
|
-
import entry13 from "./catalog/
|
|
19
|
-
import entry14 from "./catalog/
|
|
20
|
-
import entry15 from "./catalog/
|
|
21
|
-
import entry16 from "./catalog/
|
|
22
|
-
import entry17 from "./catalog/
|
|
23
|
-
import entry18 from "./catalog/
|
|
24
|
-
import entry19 from "./catalog/
|
|
25
|
-
import entry20 from "./catalog/
|
|
26
|
-
import entry21 from "./catalog/
|
|
27
|
-
import entry22 from "./catalog/
|
|
28
|
-
import entry23 from "./catalog/
|
|
29
|
-
import entry24 from "./catalog/
|
|
30
|
-
import entry25 from "./catalog/
|
|
31
|
-
import entry26 from "./catalog/
|
|
32
|
-
import entry27 from "./catalog/
|
|
33
|
-
import entry28 from "./catalog/
|
|
34
|
-
import entry29 from "./catalog/
|
|
35
|
-
import entry30 from "./catalog/
|
|
36
|
-
import entry31 from "./catalog/microsoft-
|
|
37
|
-
import entry32 from "./catalog/
|
|
38
|
-
import entry33 from "./catalog/
|
|
39
|
-
import entry34 from "./catalog/
|
|
40
|
-
import entry35 from "./catalog/
|
|
41
|
-
import entry36 from "./catalog/
|
|
42
|
-
import entry37 from "./catalog/
|
|
43
|
-
import entry38 from "./catalog/
|
|
44
|
-
import entry39 from "./catalog/
|
|
45
|
-
import entry40 from "./catalog/
|
|
46
|
-
import entry41 from "./catalog/
|
|
47
|
-
import entry42 from "./catalog/google-
|
|
48
|
-
import entry43 from "./catalog/
|
|
49
|
-
import entry44 from "./catalog/
|
|
50
|
-
import entry45 from "./catalog/
|
|
51
|
-
import entry46 from "./catalog/
|
|
52
|
-
import entry47 from "./catalog/
|
|
53
|
-
import entry48 from "./catalog/
|
|
54
|
-
import entry49 from "./catalog/
|
|
55
|
-
import entry50 from "./catalog/
|
|
56
|
-
import entry51 from "./catalog/cloudflare-
|
|
57
|
-
import entry52 from "./catalog/cloudflare-
|
|
58
|
-
import entry53 from "./catalog/cloudflare-
|
|
59
|
-
import entry54 from "./catalog/cloudflare-
|
|
60
|
-
import entry55 from "./catalog/
|
|
61
|
-
import entry56 from "./catalog/
|
|
62
|
-
import entry57 from "./catalog/
|
|
63
|
-
import entry58 from "./catalog/
|
|
64
|
-
import entry59 from "./catalog/
|
|
65
|
-
import entry60 from "./catalog/
|
|
66
|
-
import entry61 from "./catalog/
|
|
67
|
-
import entry62 from "./catalog/
|
|
68
|
-
import entry63 from "./catalog/
|
|
69
|
-
import entry64 from "./catalog/
|
|
70
|
-
import entry65 from "./catalog/
|
|
71
|
-
import entry66 from "./catalog/
|
|
72
|
-
import entry67 from "./catalog/
|
|
73
|
-
import entry68 from "./catalog/
|
|
74
|
-
import entry69 from "./catalog/
|
|
75
|
-
import entry70 from "./catalog/
|
|
76
|
-
import entry71 from "./catalog/
|
|
77
|
-
import entry72 from "./catalog/
|
|
78
|
-
import entry73 from "./catalog/
|
|
79
|
-
import entry74 from "./catalog/
|
|
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" };
|
|
80
81
|
|
|
81
82
|
export const INTEGRATION_CATALOG_ENTRIES = [
|
|
82
83
|
entry0,
|
|
@@ -154,4 +155,5 @@ export const INTEGRATION_CATALOG_ENTRIES = [
|
|
|
154
155
|
entry72,
|
|
155
156
|
entry73,
|
|
156
157
|
entry74,
|
|
158
|
+
entry75,
|
|
157
159
|
];
|
|
@@ -29,6 +29,7 @@
|
|
|
29
29
|
"source": "./skills/add-skill",
|
|
30
30
|
"description": "Add (import) an OpenHands skill from a GitHub repository into the current workspace.",
|
|
31
31
|
"category": "agent-authoring",
|
|
32
|
+
"defaultEnabled": true,
|
|
32
33
|
"keywords": [
|
|
33
34
|
"skill",
|
|
34
35
|
"import",
|
|
@@ -41,6 +42,7 @@
|
|
|
41
42
|
"source": "./skills/agent-memory",
|
|
42
43
|
"description": "Persist and retrieve repository-specific knowledge using AGENTS.md files. Use when you want to save important information about a codebase (build commands, code style, workflows) for future sessions.",
|
|
43
44
|
"category": "agent-authoring",
|
|
45
|
+
"defaultEnabled": true,
|
|
44
46
|
"keywords": [
|
|
45
47
|
"memory",
|
|
46
48
|
"knowledge",
|
|
@@ -53,6 +55,7 @@
|
|
|
53
55
|
"source": "./skills/agent-sdk-builder",
|
|
54
56
|
"description": "Guided workflow for building custom AI agents using the OpenHands Software Agent SDK. Use when you want to create a new agent through an interactive interview process that gathers requirements and generates implementation plans.",
|
|
55
57
|
"category": "agent-authoring",
|
|
58
|
+
"defaultEnabled": true,
|
|
56
59
|
"keywords": [
|
|
57
60
|
"agent",
|
|
58
61
|
"sdk",
|
|
@@ -80,6 +83,7 @@
|
|
|
80
83
|
"source": "./skills/openhands-automation",
|
|
81
84
|
"description": "Create and manage OpenHands automations - scheduled tasks that run in sandboxes. Use the prompt preset to create automations from natural language, or manage existing automations.",
|
|
82
85
|
"category": "automations",
|
|
86
|
+
"defaultEnabled": true,
|
|
83
87
|
"keywords": [
|
|
84
88
|
"automation",
|
|
85
89
|
"cron",
|
|
@@ -93,6 +97,7 @@
|
|
|
93
97
|
"source": "./skills/agent-canvas-environment",
|
|
94
98
|
"description": "Work effectively inside a local Agent Canvas environment, including local agent-server auth, safe workspace hygiene, and local conversation delegation.",
|
|
95
99
|
"category": "agent-authoring",
|
|
100
|
+
"defaultEnabled": true,
|
|
96
101
|
"keywords": [
|
|
97
102
|
"agent-canvas",
|
|
98
103
|
"openhands",
|
|
@@ -106,6 +111,7 @@
|
|
|
106
111
|
"source": "./skills/openhands-sdk",
|
|
107
112
|
"description": "Reference skill for the OpenHands Software Agent SDK - build AI agents with custom tools, LLM configuration, conversations, sub-agent delegation, MCP integration, security, and persistence.",
|
|
108
113
|
"category": "agent-authoring",
|
|
114
|
+
"defaultEnabled": true,
|
|
109
115
|
"keywords": [
|
|
110
116
|
"sdk",
|
|
111
117
|
"agent",
|
|
@@ -182,6 +188,7 @@
|
|
|
182
188
|
"source": "./skills/code-review",
|
|
183
189
|
"description": "Rigorous code review focusing on data structures, simplicity, security, pragmatism, and risk/safety evaluation. Provides brutally honest, actionable feedback on pull requests or merge requests, including a risk assessment for every review. Use when reviewing code changes.",
|
|
184
190
|
"category": "code-quality",
|
|
191
|
+
"defaultEnabled": true,
|
|
185
192
|
"keywords": [
|
|
186
193
|
"code-review",
|
|
187
194
|
"quality",
|
|
@@ -232,6 +239,7 @@
|
|
|
232
239
|
"source": "./skills/docker",
|
|
233
240
|
"description": "Run Docker commands within a container environment, including starting the Docker daemon and managing containers. Use when building, running, or managing Docker containers and images.",
|
|
234
241
|
"category": "environment",
|
|
242
|
+
"defaultEnabled": true,
|
|
235
243
|
"keywords": [
|
|
236
244
|
"docker",
|
|
237
245
|
"container",
|
|
@@ -268,6 +276,7 @@
|
|
|
268
276
|
"source": "./skills/github",
|
|
269
277
|
"description": "Interact with GitHub repositories, pull requests, issues, and workflows using the GITHUB_TOKEN environment variable and GitHub CLI. Use when working with code hosted on GitHub or managing GitHub resources.",
|
|
270
278
|
"category": "code-hosting",
|
|
279
|
+
"defaultEnabled": true,
|
|
271
280
|
"keywords": [
|
|
272
281
|
"github",
|
|
273
282
|
"git",
|
|
@@ -444,6 +453,7 @@
|
|
|
444
453
|
"source": "./skills/openhands-api",
|
|
445
454
|
"description": "Use the OpenHands Cloud REST API (V1) and agent-server APIs to create and manage Cloud or local backend conversations, including multi-conversation delegation workflows. Includes minimal Python and TypeScript clients under scripts/.",
|
|
446
455
|
"category": "agent-authoring",
|
|
456
|
+
"defaultEnabled": true,
|
|
447
457
|
"keywords": [
|
|
448
458
|
"openhands",
|
|
449
459
|
"api",
|
|
@@ -534,6 +544,7 @@
|
|
|
534
544
|
"source": "./skills/skill-creator",
|
|
535
545
|
"description": "Guide for creating effective skills. This skill should be used when users want to create a new skill (or update an existing skill) that extends Claude's capabilities with specialized knowledge, workflows, or tool integrations.",
|
|
536
546
|
"category": "agent-authoring",
|
|
547
|
+
"defaultEnabled": true,
|
|
537
548
|
"keywords": [
|
|
538
549
|
"skill",
|
|
539
550
|
"plugin",
|
|
@@ -670,6 +681,32 @@
|
|
|
670
681
|
"official"
|
|
671
682
|
]
|
|
672
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
|
+
},
|
|
673
710
|
{
|
|
674
711
|
"name": "github-pr-reviewer",
|
|
675
712
|
"source": "./skills/github-pr-reviewer",
|
|
@@ -770,6 +807,18 @@
|
|
|
770
807
|
"jira",
|
|
771
808
|
"atlassian"
|
|
772
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
|
+
]
|
|
773
822
|
}
|
|
774
823
|
]
|
|
775
824
|
}
|
package/package.json
CHANGED
|
@@ -160,7 +160,7 @@ runs:
|
|
|
160
160
|
# so we enforce the time limit via the coreutils timeout command.
|
|
161
161
|
TIMEOUT_SECONDS=$((TIMEOUT_MINUTES * 60))
|
|
162
162
|
timeout "${TIMEOUT_SECONDS}" \
|
|
163
|
-
uv run --no-project --with openhands-sdk --with openhands-tools --with
|
|
163
|
+
uv run --no-project --with openhands-sdk --with openhands-tools --with lmnr \
|
|
164
164
|
python ../extensions/plugins/qa-changes/scripts/agent_script.py
|
|
165
165
|
|
|
166
166
|
- name: Upload logs as artifact
|
package/pyproject.toml
CHANGED
|
@@ -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.
|
|
21
|
+
_FALLBACK_VERSION = "0.18.0" # x-release-please-version
|
|
22
22
|
|
|
23
23
|
try:
|
|
24
24
|
__version__: str = version("openhands-extensions")
|
|
@@ -4,6 +4,7 @@ import path from "node:path";
|
|
|
4
4
|
const root = process.cwd();
|
|
5
5
|
const catalogDir = path.join(root, "automations", "catalog");
|
|
6
6
|
const outputPath = path.join(root, "automations", "catalog-index.js");
|
|
7
|
+
const bundleOutputPath = path.join(root, "automations", "bundle-index.js");
|
|
7
8
|
|
|
8
9
|
const directories = (await readdir(catalogDir, { withFileTypes: true }))
|
|
9
10
|
.filter((entry) => entry.isDirectory())
|
|
@@ -37,6 +38,25 @@ const imports = indexedAutomations
|
|
|
37
38
|
.join("\n");
|
|
38
39
|
const entries = indexedAutomations.map(({ importName }) => ` ${importName},`).join("\n");
|
|
39
40
|
|
|
41
|
+
// A bundle entry names its files by repository-relative path, so the one copy
|
|
42
|
+
// of a script shipped by both a skill and the catalog stays in one place. The
|
|
43
|
+
// package cannot ship a path, though - a browser host has no repository - so
|
|
44
|
+
// the contents are inlined here at build time.
|
|
45
|
+
const bundles = {};
|
|
46
|
+
for (const { entry } of automations) {
|
|
47
|
+
const files = entry.setup?.bundle?.files;
|
|
48
|
+
if (!files) continue;
|
|
49
|
+
bundles[entry.id] = {};
|
|
50
|
+
for (const [packedPath, source] of Object.entries(files).sort()) {
|
|
51
|
+
if (!/^(skills|automations)\//.test(source) || source.includes("..")) {
|
|
52
|
+
throw new Error(
|
|
53
|
+
`${entry.id}: bundle file "${packedPath}" names ${source}, which is outside skills/ or automations/`,
|
|
54
|
+
);
|
|
55
|
+
}
|
|
56
|
+
bundles[entry.id][packedPath] = await readFile(path.join(root, source), "utf8");
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
40
60
|
const header = `// This file is auto-generated by scripts/build-automation-catalog.mjs.
|
|
41
61
|
// Do not edit it manually. To update it after changing automations/catalog/*/manifest.json,
|
|
42
62
|
// run: npm run build:automations
|
|
@@ -49,3 +69,13 @@ ${entries}
|
|
|
49
69
|
];
|
|
50
70
|
`;
|
|
51
71
|
await writeFile(outputPath, body);
|
|
72
|
+
|
|
73
|
+
const bundleHeader = `// This file is auto-generated by scripts/build-automation-catalog.mjs.
|
|
74
|
+
// Do not edit it manually. It inlines the files each bundle entry ships, read from
|
|
75
|
+
// the repository paths its manifest names. To update it, run: npm run build:automations
|
|
76
|
+
|
|
77
|
+
`;
|
|
78
|
+
await writeFile(
|
|
79
|
+
bundleOutputPath,
|
|
80
|
+
`${bundleHeader}export const AUTOMATION_BUNDLE_FILES = ${JSON.stringify(bundles, null, 2)};\n`,
|
|
81
|
+
);
|
|
@@ -36,8 +36,8 @@ export const SKILL_CATEGORY_IDS = [
|
|
|
36
36
|
|
|
37
37
|
const FALLBACK_CATEGORY = "other";
|
|
38
38
|
|
|
39
|
-
/** Build a `skill directory name -> {category, file}` map from every manifest. */
|
|
40
|
-
export function
|
|
39
|
+
/** Build a `skill directory name -> {category, defaultEnabled, file}` map from every manifest. */
|
|
40
|
+
export function buildEntryMap(marketplacesDir) {
|
|
41
41
|
const map = new Map();
|
|
42
42
|
|
|
43
43
|
for (const filename of readdirSync(marketplacesDir).filter((f) => f.endsWith(".json")).sort()) {
|
|
@@ -56,14 +56,27 @@ export function buildCategoryMap(marketplacesDir) {
|
|
|
56
56
|
);
|
|
57
57
|
}
|
|
58
58
|
|
|
59
|
+
if ("defaultEnabled" in entry && typeof entry.defaultEnabled !== "boolean") {
|
|
60
|
+
throw new Error(
|
|
61
|
+
`${filename}: skill "${name}" has defaultEnabled ${JSON.stringify(entry.defaultEnabled)}, expected a boolean`,
|
|
62
|
+
);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
const defaultEnabled = entry.defaultEnabled === true;
|
|
66
|
+
|
|
59
67
|
const existing = map.get(name);
|
|
60
68
|
if (existing && existing.category !== category) {
|
|
61
69
|
throw new Error(
|
|
62
70
|
`Conflicting categories for skill "${name}": ${existing.file} says "${existing.category}", ${filename} says "${category}"`,
|
|
63
71
|
);
|
|
64
72
|
}
|
|
73
|
+
if (existing && existing.defaultEnabled !== defaultEnabled) {
|
|
74
|
+
throw new Error(
|
|
75
|
+
`Conflicting defaultEnabled for skill "${name}": ${existing.file} says ${existing.defaultEnabled}, ${filename} says ${defaultEnabled}`,
|
|
76
|
+
);
|
|
77
|
+
}
|
|
65
78
|
|
|
66
|
-
map.set(name, { category, file: filename });
|
|
79
|
+
map.set(name, { category, defaultEnabled, file: filename });
|
|
67
80
|
}
|
|
68
81
|
}
|
|
69
82
|
|
|
@@ -114,7 +127,7 @@ export function parseFrontmatter(raw) {
|
|
|
114
127
|
*/
|
|
115
128
|
export function buildCatalog(skillsDir, marketplacesDir = MARKETPLACES_DIR) {
|
|
116
129
|
const entries = [];
|
|
117
|
-
const
|
|
130
|
+
const manifestEntries = buildEntryMap(marketplacesDir);
|
|
118
131
|
const uncategorized = [];
|
|
119
132
|
|
|
120
133
|
for (const dir of readdirSync(skillsDir).sort()) {
|
|
@@ -133,7 +146,7 @@ export function buildCatalog(skillsDir, marketplacesDir = MARKETPLACES_DIR) {
|
|
|
133
146
|
const fm = parseFrontmatter(parts[1]);
|
|
134
147
|
const body = parts.slice(2).join("---").trim();
|
|
135
148
|
|
|
136
|
-
const mapped =
|
|
149
|
+
const mapped = manifestEntries.get(dir);
|
|
137
150
|
if (!mapped) uncategorized.push(dir);
|
|
138
151
|
|
|
139
152
|
entries.push({
|
|
@@ -142,6 +155,8 @@ export function buildCatalog(skillsDir, marketplacesDir = MARKETPLACES_DIR) {
|
|
|
142
155
|
triggers: fm.triggers,
|
|
143
156
|
content: body,
|
|
144
157
|
category: mapped?.category ?? FALLBACK_CATEGORY,
|
|
158
|
+
// Only when true, so off-by-default has one shape.
|
|
159
|
+
...(mapped?.defaultEnabled ? { defaultEnabled: true } : {}),
|
|
145
160
|
...(fm.license ? { license: fm.license } : {}),
|
|
146
161
|
...(fm.compatibility ? { compatibility: fm.compatibility } : {}),
|
|
147
162
|
});
|
|
@@ -160,14 +175,16 @@ export function buildCatalog(skillsDir, marketplacesDir = MARKETPLACES_DIR) {
|
|
|
160
175
|
const isMain = process.argv[1] && fileURLToPath(import.meta.url) === process.argv[1];
|
|
161
176
|
if (isMain) {
|
|
162
177
|
const entries = buildCatalog(SKILLS_DIR);
|
|
178
|
+
const defaultEnabledNames = entries.filter((e) => e.defaultEnabled).map((e) => e.name);
|
|
163
179
|
|
|
164
180
|
const source = `// Auto-generated by scripts/build-skills-catalog.mjs — do not edit.
|
|
165
|
-
// Source of truth: skills/*/SKILL.md and marketplaces/*.json (category)
|
|
181
|
+
// Source of truth: skills/*/SKILL.md and marketplaces/*.json (category, defaultEnabled)
|
|
166
182
|
export const SKILL_CATEGORY_IDS = ${JSON.stringify(SKILL_CATEGORY_IDS)};
|
|
167
183
|
export const SKILLS_CATALOG = ${JSON.stringify(entries, null, 2)};
|
|
184
|
+
export const DEFAULT_ENABLED_SKILL_NAMES = ${JSON.stringify(defaultEnabledNames)};
|
|
168
185
|
export default SKILLS_CATALOG;
|
|
169
186
|
`;
|
|
170
187
|
|
|
171
188
|
writeFileSync(OUTPUT, source);
|
|
172
|
-
console.log(`Generated ${OUTPUT} with ${entries.length} skills`);
|
|
189
|
+
console.log(`Generated ${OUTPUT} with ${entries.length} skills (${defaultEnabledNames.length} default-enabled)`);
|
|
173
190
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: add-skill
|
|
3
|
-
description:
|
|
3
|
+
description: Import an existing skill from a GitHub repository URL into the current workspace. Use only when the user provides or references a GitHub URL/repo to fetch from (e.g., `/add-skill https://github.com/OpenHands/extensions/tree/main/skills/codereview` or "add the codereview skill from https://github.com/OpenHands/extensions/"). Handles fetching the skill files and placing them in .agents/skills/. This does not author new skills — to create a new skill from scratch (no source URL), use the skill-creator skill instead.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# Add Skill
|
|
@@ -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
|