@openhands/extensions 0.18.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.
- package/.agents/skills/custom-codereview-guide.md +7 -0
- package/.github/dependabot.yml +24 -0
- package/.github/scripts/check_issue_readiness.py +272 -0
- package/.github/scripts/check_pr_description.py +226 -0
- package/.github/scripts/post-readiness-comment.mjs +184 -0
- package/.github/scripts/refresh_linked_pr_checks.py +171 -0
- package/.github/workflows/issue-readiness-check.yml +142 -0
- package/.github/workflows/pr-description-check.yml +37 -0
- package/.release-please-manifest.json +1 -1
- package/AGENTS.md +16 -0
- package/README.md +10 -2
- package/automations/bundle-index.js +1 -1
- package/automations/catalog/github-agents-md-maintainer/manifest.json +5 -0
- package/automations/catalog/github-issue-to-pr/manifest.json +5 -0
- package/automations/catalog/github-pr-reviewer/manifest.json +5 -0
- package/automations/catalog/github-repo-monitor/manifest.json +5 -0
- package/automations/catalog/jira-issue-to-bitbucket-pr/manifest.json +23 -0
- package/automations/catalog/jira-issue-to-gitlab-mr/manifest.json +23 -0
- package/automations/catalog/linear-issue-to-bitbucket-pr/manifest.json +23 -0
- package/automations/catalog/linear-issue-to-github-pr/manifest.json +23 -0
- package/automations/catalog/linear-issue-to-gitlab-mr/manifest.json +23 -0
- package/automations/catalog/news-digest/manifest.json +5 -0
- package/automations/catalog/qa-changes/manifest.json +72 -0
- package/automations/catalog/slack-github-linear-daily-organization.json +27 -0
- package/automations/catalog-index.js +18 -6
- package/automations/catalog.schema.json +25 -0
- package/automations/index.d.ts +16 -0
- package/integrations/catalog/datadog.json +2 -2
- package/integrations/catalog/gitlab.json +41 -0
- package/integrations/catalog/notion.json +1 -1
- package/integrations/catalog-index.js +71 -69
- package/integrations/index.d.ts +1 -1
- package/marketplaces/openhands-extensions.json +21 -6
- package/package.json +1 -1
- package/plugins/pr-review/scripts/prompt.py +2 -0
- package/pyproject.toml +1 -1
- package/python/openhands_extensions/_version.py +1 -1
- package/skills/github-pr-reviewer/SKILL.md +2 -1
- package/skills/github-pr-reviewer/scripts/main.py +45 -4
- package/skills/github-pr-reviewer/tests/test_main.py +74 -0
- package/skills/index.js +11 -2
- package/skills/openhands-sdk/SKILL.md +1 -0
- package/skills/ticket-to-code-change/.plugin/plugin.json +21 -0
- package/skills/ticket-to-code-change/README.md +5 -0
- package/skills/ticket-to-code-change/SKILL.md +50 -0
- package/skills/ticket-to-code-change/commands/ticket-to-code-change-setup.md +8 -0
- package/tests/test_automation_setup.py +22 -0
- package/tests/test_catalogs.py +2 -2
- package/tests/test_check_issue_readiness.py +135 -0
- package/tests/test_check_pr_description.py +228 -0
- package/tests/test_pr_review_prompt.py +10 -0
- package/tests/test_refresh_linked_pr_checks.py +130 -0
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": "qa-changes",
|
|
3
|
+
"name": "QA Changes Agent",
|
|
4
|
+
"category": "Code review",
|
|
5
|
+
"description": "When a pull request is opened for review, run the QA changes methodology — set up the environment, exercise the changed behavior as a real user would, and post the outcome by editing the PR description with a QA Agent section.",
|
|
6
|
+
"requires": {
|
|
7
|
+
"integrations": {
|
|
8
|
+
"github": {
|
|
9
|
+
"message": "Used to read the pull request, check the author's commit history, and edit the PR description."
|
|
10
|
+
}
|
|
11
|
+
},
|
|
12
|
+
"features": [
|
|
13
|
+
"repoClone",
|
|
14
|
+
"presetPrompt",
|
|
15
|
+
"webhookDelivery"
|
|
16
|
+
]
|
|
17
|
+
},
|
|
18
|
+
"popularityRank": 85,
|
|
19
|
+
"estimatedSetupMinutes": 5,
|
|
20
|
+
"skill": "qa-changes",
|
|
21
|
+
"exampleImplementation": "Trigger: GitHub pull_request.opened and pull_request.ready_for_review events\nRequired secret: GITHUB_TOKEN\n\n1. Receive a GitHub pull_request event (opened or ready_for_review).\n2. Filter: the PR must not be a draft (draft == false), the PR description must not already contain a QA Agent section, and the PR author must have more than 3 commits already in main.\n3. Start an OpenHands conversation that clones the repo, checks out the PR head, and runs the /qa-changes methodology: understand the change, set up the environment, exercise the changed behavior as a real user would.\n4. Instead of posting a review comment, edit the PR description to append a horizontal rule followed by a QA Agent section containing the structured QA report.",
|
|
22
|
+
"setup": {
|
|
23
|
+
"version": "1.0",
|
|
24
|
+
"mode": "direct",
|
|
25
|
+
"form": {
|
|
26
|
+
"triggers": {
|
|
27
|
+
"event": {
|
|
28
|
+
"on": {
|
|
29
|
+
"type": "select",
|
|
30
|
+
"label": "Trigger on",
|
|
31
|
+
"help": "Which GitHub pull request event starts a QA run. Opened fires when a non-draft PR is created; Ready for review fires when a draft PR is marked ready.",
|
|
32
|
+
"default": "pull_request.opened",
|
|
33
|
+
"required": true,
|
|
34
|
+
"options": [
|
|
35
|
+
{
|
|
36
|
+
"value": "pull_request.opened",
|
|
37
|
+
"label": "Pull request opened"
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
"value": "pull_request.ready_for_review",
|
|
41
|
+
"label": "Pull request ready for review"
|
|
42
|
+
}
|
|
43
|
+
]
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
},
|
|
47
|
+
"args": {
|
|
48
|
+
"repository": {
|
|
49
|
+
"type": "repo-picker",
|
|
50
|
+
"label": "Repository",
|
|
51
|
+
"help": "The repository whose pull requests will be QA'd.",
|
|
52
|
+
"provider": "github",
|
|
53
|
+
"required": true
|
|
54
|
+
},
|
|
55
|
+
"ref": {
|
|
56
|
+
"type": "text",
|
|
57
|
+
"label": "Base branch",
|
|
58
|
+
"help": "Branch checked out when the agent runs QA.",
|
|
59
|
+
"default": "main",
|
|
60
|
+
"required": true,
|
|
61
|
+
"constraints": {
|
|
62
|
+
"minLength": 1,
|
|
63
|
+
"maxLength": 255
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
},
|
|
68
|
+
"prompt": "A pull request was opened or marked ready for review in {{form.repository}}. Before doing any QA work, run these gate checks and stop immediately (making no changes) if any fails:\n\n1. The PR must not be a draft — it must be open for review. If the PR is a draft, stop.\n2. The PR author (the human who opened it) must have more than 3 commits already merged into the {{form.ref}} branch. Use the GitHub API to count the author's commits in {{form.ref}}. If the count is 3 or fewer, stop without making any changes.\n3. The PR description must not already contain a section named QA Agent (i.e., a header like ## QA Agent or ### QA Agent). If that section already exists, stop without making any changes or starting a conversation.\n\nIf all gate checks pass, proceed with the QA validation using the /qa-changes skill methodology:\n\n- Phase 1: Understand the change. Read the PR diff, title, and description. Identify the goal of the PR. Classify every changed file. Form a hypothesis about what the PR should achieve.\n- Phase 2: Set up the environment. Read the repo's bootstrap instructions, install dependencies, and build if needed. Note CI status but do not re-run tests.\n- Phase 3: Exercise the changed behavior. Actually run the software the way a real user would — start servers, run CLI commands, make HTTP requests, open browsers. Do NOT run the test suite, linters, or code analysis. For bug fixes, reproduce the bug before and after the fix. Show before/after evidence.\n- Phase 4: Report results.\n\nIMPORTANT — how to post your report:\nDo NOT post a new comment, review, or inline review comment. Instead, EDIT the PR description (the pull request body) to append your QA report at the very end, after a horizontal rule (---), under a new section titled QA Agent. Use the GitHub API (PATCH /repos/{owner}/{repo}/pulls/{pr_number} with a body field) to update the PR description. Append the following structure to the existing PR body:\n\n---\n\n## QA Agent\n\n{Your full QA report following the /qa-changes skill report format: verdict, summary, Does this PR achieve its stated goal? section, status table, collapsible evidence in details blocks, and issues found.}\n\nDo not make any other changes to the PR description — only append the QA Agent section at the end. Do not post any comments or reviews.",
|
|
69
|
+
"filter": "!pull_request.draft && !icontains(pull_request.body, 'QA Agent') && glob(repository.full_name, '{{form.repository}}')",
|
|
70
|
+
"message": "This deployment cannot run the webhook-driven QA automation directly. Set it up in this conversation instead: confirm the repository to QA, the base branch, and the PR event to trigger on, then create the automation."
|
|
71
|
+
}
|
|
72
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": "slack-github-linear-daily-organization",
|
|
3
|
+
"name": "Slack + GitHub + Linear daily organization",
|
|
4
|
+
"category": "Personal productivity",
|
|
5
|
+
"description": "Every morning, reconcile your open GitHub PRs with Linear issues, align active work, surface relevant Slack context, and send yourself a concise daily plan.",
|
|
6
|
+
"exampleImplementation": "Trigger: cron, every morning at 7:00 AM in the configured timezone\nRequired integrations: GitHub, Linear, Slack\n\n1. Verify access to all three services before making changes.\n2. Reconcile open GitHub PRs with issues and import missing issues into Linear.\n3. Align active Linear work with open PRs and recent Slack context.\n4. Send a concise, prioritized daily plan to the configured Slack recipient.",
|
|
7
|
+
"requiredIntegrationIds": [
|
|
8
|
+
"slack",
|
|
9
|
+
"github",
|
|
10
|
+
"linear"
|
|
11
|
+
],
|
|
12
|
+
"popularityRank": 88,
|
|
13
|
+
"estimatedSetupMinutes": 10,
|
|
14
|
+
"suggestedTrigger": {
|
|
15
|
+
"type": "cron",
|
|
16
|
+
"schedule": "0 7 * * *",
|
|
17
|
+
"timezone": "[IANA_TIMEZONE]"
|
|
18
|
+
},
|
|
19
|
+
"placeholders": [
|
|
20
|
+
"[GITHUB_USERNAME]",
|
|
21
|
+
"[LINEAR_ASSIGNEE_QUERY]",
|
|
22
|
+
"[GITHUB_ORG_OR_REPO_SCOPE]",
|
|
23
|
+
"[SLACK_RECIPIENT_QUERY]",
|
|
24
|
+
"[IANA_TIMEZONE]"
|
|
25
|
+
],
|
|
26
|
+
"prompt": "You are running a daily GitHub/Linear/Slack organization automation.\n\nSchedule context:\n- This automation runs every morning at 7:00 AM in [IANA_TIMEZONE].\n- Treat \"today\" and \"past 1-3 days\" in [IANA_TIMEZONE].\n\nImportant safety and access rules:\n1. First, verify that you have working access to GitHub, Linear, and Slack using the available official tools/APIs. A minimal successful read for each service is enough.\n2. If any one of GitHub, Linear, or Slack is unavailable, missing credentials, or returns an authorization error, stop immediately. Do not make partial updates. If Slack is available, send the configured Slack recipient a short message saying which service was unavailable and that no changes were made; otherwise finish with that error.\n3. Prefer official APIs/MCP tools over browser interaction. Do not browse web UIs unless no API/tool path exists.\n4. When posting to Slack or creating/updating human-readable external-service content, include a short disclosure that the content was generated by an AI agent (OpenHands) on behalf of the user.\n\nIdentity and scope:\n- GitHub user to inspect: [GITHUB_USERNAME].\n- GitHub issue scope: [GITHUB_ORG_OR_REPO_SCOPE]. Examples: visible repositories in one or more organizations, or an explicit allowlist of repositories.\n- Linear assignee: resolve by searching for [LINEAR_ASSIGNEE_QUERY]. Do not guess an ID if multiple plausible matches exist.\n- Slack recipient for the final daily plan: resolve by searching for [SLACK_RECIPIENT_QUERY], or use the current authenticated Slack user if that is the intended recipient.\n\nTask 1 — Ensure the user's open PRs are associated with GitHub issues:\n1. Find all open pull requests authored by [GITHUB_USERNAME] across the configured GitHub scope.\n2. For each PR, determine whether it is already associated with a GitHub issue. Count an association if the PR body has a clear issue reference or closing keyword (for example, #123, owner/repo#123, fixes, closes, or resolves), or if GitHub's API exposes linked/closing issues.\n3. If a PR has no associated issue:\n a. Search open issues in the same repository for an obvious matching issue using the PR title, branch name, and summary. Only treat an issue as obvious if the title/topic clearly matches; do not force weak matches.\n b. If an obvious open issue exists, use that issue.\n c. If none exists, create a new GitHub issue in the same repository summarizing the PR's purpose and linking back to the PR. Include an AI disclosure in the issue body.\n d. Update the PR description to include a non-closing association such as \"Related issue: #123\" or \"Related issue: owner/repo#123\" for cross-repo references. Preserve the existing PR description. Avoid \"Fixes\", \"Closes\", or \"Resolves\" unless the PR already used a closing keyword or the issue clearly should close when the PR merges.\n\nTask 2 — Ensure GitHub issues are represented in Linear:\n1. Find all open GitHub issues, excluding PRs, in the configured GitHub issue scope.\n2. For each issue, verify that it has a corresponding Linear issue. Match by GitHub issue URL first, then by exact repository/name/number and title if needed.\n3. If no Linear issue exists, create one in the appropriate Linear team/project using the GitHub issue title, URL, repository, labels, and a concise summary. Include an AI disclosure in the Linear issue description. Choose an appropriate priority for newly created Linear issues using this rubric:\n - Urgent: security, production outage, data loss, severe user-blocking regression, active incident.\n - High: important bug/regression, release blocker, broadly affecting users, high-value requested work.\n - Medium: normal feature/bug/task with moderate impact.\n - Low: cleanup, docs, nice-to-have, unclear/low-impact backlog.\n - No priority only if there is insufficient information.\n4. For every GitHub issue that has one or more open PRs associated with it, ensure the corresponding Linear issue is assigned to the configured Linear assignee and has a started/In Progress status. If the Linear issue is already completed or canceled, do not reopen it; mention this in the final report instead.\n5. Do not downgrade or otherwise change priorities of existing Linear issues merely because they seem wrong. If an existing priority is missing and the issue is actively in progress due to an open PR, you may set a reasonable priority. If a priority seems unclear or incorrect, flag it in the final Slack report rather than changing it.\n\nTask 3 — Check Slack context for active Linear issues:\n1. Inspect active Linear issues assigned to the configured assignee and not completed/canceled, especially urgent, high-priority, and in-progress issues.\n2. Search Slack public content and, if available in the tool configuration, private/DM-accessible content for the last 1-3 days for requests, mentions, or discussions related to those Linear issue identifiers, issue titles, linked GitHub issues/PRs, and relevant repository names.\n3. Summarize actionable Slack requests/discussions. Include links to Slack messages/threads when the Slack tooling provides them.\n4. Do not expose private Slack content beyond what is necessary for the configured recipient's own daily plan.\n\nTask 4 — Produce and send the daily plan:\n1. Review all active Linear issues assigned to the configured assignee, prioritizing Urgent then High then Medium then Low, while also considering Slack requests, open PR status, blockers, and issue age.\n2. Create a concise plan for what the recipient should work on today, focusing on the most urgent and important issues first.\n3. Include:\n - Top 3-5 recommended focus items with Linear identifiers/titles and links.\n - Any GitHub PR issue-association work performed.\n - Any GitHub issues imported into Linear.\n - Any Linear issues moved to In Progress or assigned to the configured assignee because an open PR exists.\n - Any Slack requests/discussions from the past 1-3 days relevant to active Linear issues.\n - Any issue priorities that are unclear or appear incorrect; explicitly say you did not change those priorities.\n - Any access or matching limitations.\n4. Send the report to the configured Slack recipient as a DM if possible. If DM is not possible, use the best available Slack destination for that recipient. Include the AI disclosure in the message.\n5. Finish with a short execution summary including counts of PRs checked, PRs updated, GitHub issues created, GitHub issues imported to Linear, Linear issues updated, and Slack discussions found.\n\nBe careful and conservative: avoid duplicate GitHub issues, duplicate Linear issues, weak associations, or broad destructive changes."
|
|
27
|
+
}
|
|
@@ -8,12 +8,18 @@ import entry2 from "./catalog/github-issue-to-pr/manifest.json" with { type: "js
|
|
|
8
8
|
import entry3 from "./catalog/slack-standup-digest/manifest.json" with { type: "json" };
|
|
9
9
|
import entry4 from "./catalog/slack-channel-monitor/manifest.json" with { type: "json" };
|
|
10
10
|
import entry5 from "./catalog/linear-triage-assistant/manifest.json" with { type: "json" };
|
|
11
|
-
import entry6 from "./catalog/
|
|
12
|
-
import entry7 from "./catalog/
|
|
13
|
-
import entry8 from "./catalog/
|
|
14
|
-
import entry9 from "./catalog/
|
|
15
|
-
import entry10 from "./catalog/
|
|
16
|
-
import entry11 from "./catalog/
|
|
11
|
+
import entry6 from "./catalog/linear-issue-to-github-pr/manifest.json" with { type: "json" };
|
|
12
|
+
import entry7 from "./catalog/linear-issue-to-gitlab-mr/manifest.json" with { type: "json" };
|
|
13
|
+
import entry8 from "./catalog/linear-issue-to-bitbucket-pr/manifest.json" with { type: "json" };
|
|
14
|
+
import entry9 from "./catalog/jira-issue-to-pr/manifest.json" with { type: "json" };
|
|
15
|
+
import entry10 from "./catalog/qa-changes/manifest.json" with { type: "json" };
|
|
16
|
+
import entry11 from "./catalog/jira-issue-to-gitlab-mr/manifest.json" with { type: "json" };
|
|
17
|
+
import entry12 from "./catalog/research-brief-writer/manifest.json" with { type: "json" };
|
|
18
|
+
import entry13 from "./catalog/jira-issue-to-bitbucket-pr/manifest.json" with { type: "json" };
|
|
19
|
+
import entry14 from "./catalog/github-agents-md-maintainer/manifest.json" with { type: "json" };
|
|
20
|
+
import entry15 from "./catalog/upstream-fork-sync/manifest.json" with { type: "json" };
|
|
21
|
+
import entry16 from "./catalog/incident-retrospective-drafter/manifest.json" with { type: "json" };
|
|
22
|
+
import entry17 from "./catalog/news-digest/manifest.json" with { type: "json" };
|
|
17
23
|
|
|
18
24
|
export const AUTOMATION_CATALOG_ENTRIES = [
|
|
19
25
|
entry0,
|
|
@@ -28,4 +34,10 @@ export const AUTOMATION_CATALOG_ENTRIES = [
|
|
|
28
34
|
entry9,
|
|
29
35
|
entry10,
|
|
30
36
|
entry11,
|
|
37
|
+
entry12,
|
|
38
|
+
entry13,
|
|
39
|
+
entry14,
|
|
40
|
+
entry15,
|
|
41
|
+
entry16,
|
|
42
|
+
entry17,
|
|
31
43
|
];
|
|
@@ -46,9 +46,34 @@
|
|
|
46
46
|
"type": "string",
|
|
47
47
|
"minLength": 1
|
|
48
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
|
+
},
|
|
49
67
|
"setup": { "$ref": "#/$defs/setup" }
|
|
50
68
|
},
|
|
51
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
|
+
},
|
|
52
77
|
"setup": {
|
|
53
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.",
|
|
54
79
|
"type": "object",
|
package/automations/index.d.ts
CHANGED
|
@@ -30,10 +30,26 @@ export interface RecommendedAutomation {
|
|
|
30
30
|
*/
|
|
31
31
|
skill?: string;
|
|
32
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;
|
|
33
43
|
/** Present when this automation ships an extension-owned setup experience. */
|
|
34
44
|
setup?: AutomationSetup;
|
|
35
45
|
}
|
|
36
46
|
|
|
47
|
+
export interface AutomationImpact {
|
|
48
|
+
basis: "completed-runs";
|
|
49
|
+
one: string;
|
|
50
|
+
other: string;
|
|
51
|
+
}
|
|
52
|
+
|
|
37
53
|
/**
|
|
38
54
|
* The extension-owned configuration experience for one automation.
|
|
39
55
|
*
|
|
@@ -43,7 +43,7 @@
|
|
|
43
43
|
"urlEditable": true,
|
|
44
44
|
"headerFields": [
|
|
45
45
|
{
|
|
46
|
-
"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": "
|
|
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
|
+
}
|
|
@@ -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/
|
|
13
|
-
import entry8 from "./catalog/
|
|
14
|
-
import entry9 from "./catalog/
|
|
15
|
-
import entry10 from "./catalog/
|
|
16
|
-
import entry11 from "./catalog/
|
|
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/
|
|
37
|
-
import entry32 from "./catalog/microsoft-
|
|
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/
|
|
48
|
-
import entry43 from "./catalog/google-
|
|
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/
|
|
57
|
-
import entry52 from "./catalog/cloudflare-
|
|
58
|
-
import entry53 from "./catalog/cloudflare-
|
|
59
|
-
import entry54 from "./catalog/cloudflare-
|
|
60
|
-
import entry55 from "./catalog/cloudflare-
|
|
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/
|
|
80
|
-
import entry75 from "./catalog/
|
|
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
|
];
|
package/integrations/index.d.ts
CHANGED
|
@@ -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
|
-
* `
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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",
|
|
@@ -819,6 +819,21 @@
|
|
|
819
819
|
"atom",
|
|
820
820
|
"digest"
|
|
821
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
|
+
]
|
|
822
837
|
}
|
|
823
838
|
]
|
|
824
839
|
}
|
package/package.json
CHANGED
|
@@ -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
|
@@ -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.19.0" # x-release-please-version
|
|
22
22
|
|
|
23
23
|
try:
|
|
24
24
|
__version__: str = version("openhands-extensions")
|
|
@@ -139,7 +139,7 @@ Record as `CRON_SCHEDULE`.
|
|
|
139
139
|
|
|
140
140
|
### Step 6 - Generate the automation script
|
|
141
141
|
|
|
142
|
-
Read `scripts/main.py` from this skill's directory. Apply exactly
|
|
142
|
+
Read `scripts/main.py` from this skill's directory. Apply exactly six constant
|
|
143
143
|
substitutions near the top of the file:
|
|
144
144
|
|
|
145
145
|
> The script also reads a `config.json` shipped beside it, if there is one, over
|
|
@@ -154,6 +154,7 @@ substitutions near the top of the file:
|
|
|
154
154
|
| `TRIGGER_LABEL = "openhands-review"` | `TRIGGER_LABEL = "{trigger_label}"` |
|
|
155
155
|
| `REVIEW_TONE = "thorough"` | `REVIEW_TONE = "{review_tone}"` |
|
|
156
156
|
| `REVIEW_STYLE_INSTRUCTIONS = ""` | `REVIEW_STYLE_INSTRUCTIONS = "{style_instructions}"` |
|
|
157
|
+
| `REPO_REVIEW_GUIDE_PATH = ".agents/skills/custom-codereview-guide.md"` | leave unchanged to auto-load a repo review guide at this path, or set to `""` to disable |
|
|
157
158
|
| `DEFAULT_OPENHANDS_URL = "http://localhost:8000"` | leave unchanged unless the user has a preference |
|
|
158
159
|
|
|
159
160
|
Use a safe string writer such as `json.dumps(value)` when inserting user-provided
|