@openhands/extensions 0.23.0 → 0.24.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 +14 -12
- package/.claude-plugin/marketplace.json +14 -0
- package/.codex-plugin/marketplace.json +14 -0
- package/.github/workflows/check-extensions.yml +6 -6
- package/.github/workflows/deprecation-check.yml +1 -1
- package/.github/workflows/issue-readiness-check.yml +37 -106
- package/.github/workflows/npm-publish.yml +2 -2
- package/.github/workflows/pr-description-check.yml +1 -1
- package/.github/workflows/pypi-publish.yml +3 -3
- package/.github/workflows/tests.yml +2 -2
- package/.plugin/marketplace.json +14 -0
- package/.release-please-manifest.json +1 -1
- package/AGENTS.md +10 -1
- package/README.md +3 -2
- package/automations/bundle-index.js +2 -2
- package/automations/catalog/github-pr-reviewer/manifest.json +4 -4
- package/integrations/catalog/granola.json +38 -0
- package/integrations/catalog-index.js +16 -14
- package/marketplaces/openhands-extensions.json +14 -0
- package/package.json +1 -1
- package/pyproject.toml +2 -2
- package/python/openhands_extensions/_version.py +1 -1
- package/skills/add-skill/SKILL.md +5 -2
- package/skills/canvas-extension-api/.claude-plugin/plugin.json +19 -0
- package/skills/canvas-extension-api/.codex-plugin/plugin.json +19 -0
- package/skills/canvas-extension-api/.plugin/plugin.json +19 -0
- package/skills/canvas-extension-api/README.md +33 -0
- package/skills/canvas-extension-api/SKILL.md +254 -0
- package/skills/canvas-extension-api/references/acceptance-checklist.md +20 -0
- package/skills/canvas-extension-api/references/backend-safety.md +9 -0
- package/skills/canvas-extension-api/references/connections.md +126 -0
- package/skills/canvas-extension-api/references/packaging-recipes.md +43 -0
- package/skills/canvas-extension-api/references/sidecar-pattern.md +49 -0
- package/skills/canvas-extension-api/references/testing-and-installation.md +286 -0
- package/skills/canvas-extension-api/references/v1-contract.md +250 -0
- package/skills/canvas-extension-api/scripts/validate-extension.mjs +92 -0
- package/skills/canvas-extension-api/scripts/validate-extension.test.mjs +60 -0
- package/skills/github-pr-reviewer/README.md +6 -1
- package/skills/github-pr-reviewer/SKILL.md +24 -1
- package/skills/github-pr-reviewer/scripts/main.py +40 -11
- package/skills/github-pr-reviewer/scripts/worker.py +12 -3
- package/skills/github-pr-reviewer/tests/test_main.py +69 -0
- package/skills/index.js +11 -3
- package/tests/fixtures/automations/github-pr-reviewer.json +9 -9
- package/tests/test_github_reviewer_delivery.py +128 -1
- package/.github/scripts/check_issue_readiness.py +0 -272
- package/.github/scripts/post-readiness-comment.mjs +0 -184
- package/tests/test_check_issue_readiness.py +0 -135
|
@@ -14,7 +14,12 @@ This skill is activated by:
|
|
|
14
14
|
- Reviews PRs on demand from a GitHub reviewer request or label event
|
|
15
15
|
- Watches several repositories from a single automation, each with its own state
|
|
16
16
|
- Processes each review request or label application idempotently
|
|
17
|
-
- Supports re-review by requesting the bot again or re-applying the label
|
|
17
|
+
- Supports re-review by requesting the bot again or re-applying the label. Each
|
|
18
|
+
explicit request refreshes mutable GitHub state (head, PR body, reviews,
|
|
19
|
+
comments, review requests, linked issues, and current-head checks) instead of
|
|
20
|
+
trusting what an earlier turn observed, so a same-head re-review sees a linked
|
|
21
|
+
issue that has since gained or lost readiness, or a check that has since moved.
|
|
22
|
+
Repository analysis already done, such as reading `AGENTS.md`, is retained
|
|
18
23
|
- Suppresses stale reviews when the PR head commit changes mid-review
|
|
19
24
|
- Hands the agent the reviewed commit already checked out, and removes that
|
|
20
25
|
checkout when the review ends, so nothing accumulates between runs
|
|
@@ -29,6 +29,28 @@ state persistence, stale-result suppression, the repository checkout, and its
|
|
|
29
29
|
removal are all handled in Python. The LLM is invoked only for the review
|
|
30
30
|
itself.
|
|
31
31
|
|
|
32
|
+
Each explicit review request - a re-applied trigger label, or a new reviewer
|
|
33
|
+
request in event mode - is a fresh review. The conversation for a PR is reused,
|
|
34
|
+
but its earlier turns must not be trusted as current: before deciding a verdict
|
|
35
|
+
the reviewer re-fetches the mutable GitHub state (the exact head, the PR body,
|
|
36
|
+
review comments and threads, review requests, the linked issues' bodies and
|
|
37
|
+
labels, and the current-head Actions results) and ignores any earlier finding,
|
|
38
|
+
verdict, or label/priority claim that state no longer supports. Repository
|
|
39
|
+
analysis the conversation already did, such as reading `AGENTS.md`, stays
|
|
40
|
+
useful and is not repeated.
|
|
41
|
+
|
|
42
|
+
The review prompt starts with a scope gate: using the repository's own guidance
|
|
43
|
+
(its scope categories and ownership boundaries, not a list of individual PR
|
|
44
|
+
numbers), the reviewer decides whether the change belongs in this repository
|
|
45
|
+
and has the product/architecture direction it needs. When it does not, the review
|
|
46
|
+
stops with a single `event: COMMENT` review that says whether the change should
|
|
47
|
+
move repositories, close, or receive a maintainer decision, and ends with the
|
|
48
|
+
`🛑 MAINTAINER DECISION REQUIRED` verdict. That outcome is **neither an approval
|
|
49
|
+
nor a change request**: it does not approve or merge the PR. The completion
|
|
50
|
+
handler recognizes the verdict and requests one configured maintainer through the
|
|
51
|
+
same handoff used after an approval. An in-scope change continues the existing
|
|
52
|
+
review unchanged.
|
|
53
|
+
|
|
32
54
|
The script prepares each review's workspace before the agent starts: the pull
|
|
33
55
|
request's head commit is downloaded as a tarball and extracted to a directory of
|
|
34
56
|
its own, which becomes the conversation's working directory. The agent is told
|
|
@@ -278,7 +300,8 @@ For each repository:
|
|
|
278
300
|
paths, and symlinks skipped rather than materialised.
|
|
279
301
|
- Starts an OpenHands conversation **whose working directory is that
|
|
280
302
|
checkout**, with a review prompt carrying PR metadata, the exact head SHA,
|
|
281
|
-
|
|
303
|
+
label event details, and the requirement to re-fetch the current mutable
|
|
304
|
+
GitHub state before deciding a verdict.
|
|
282
305
|
- Posts an acknowledgement comment with the label event, head SHA, and
|
|
283
306
|
conversation link.
|
|
284
307
|
- Records the review in state with `status: "active"` and the checkout path.
|
|
@@ -52,6 +52,12 @@ REVIEW_STYLE_INSTRUCTIONS = ""
|
|
|
52
52
|
REPO_REVIEW_GUIDE_PATH = ".agents/skills/custom-codereview-guide.md"
|
|
53
53
|
DEFAULT_OPENHANDS_URL = "http://localhost:8000"
|
|
54
54
|
|
|
55
|
+
# A review that ends with this marker is a scope stop: the reviewer found the
|
|
56
|
+
# change out of scope, or needing a product/architecture decision, before the
|
|
57
|
+
# technical review. The completion handler hands it to a maintainer without
|
|
58
|
+
# approving or merging the PR.
|
|
59
|
+
MAINTAINER_DECISION_VERDICT = "🛑 MAINTAINER DECISION REQUIRED"
|
|
60
|
+
|
|
55
61
|
CONFIG_FILENAME = "config.json"
|
|
56
62
|
|
|
57
63
|
# Config keys, paired with the type each must have. A wrong type is a hard
|
|
@@ -787,22 +793,45 @@ def _build_review_prompt(
|
|
|
787
793
|
f"\nPR Description:\n---\n{body}\n---\n\n"
|
|
788
794
|
"Required workflow:\n"
|
|
789
795
|
f"1. {workspace}\n"
|
|
790
|
-
"2.
|
|
796
|
+
"2. CURRENT STATE - treat this request as a fresh review, never a continuation of "
|
|
797
|
+
"earlier observations. Before the scope gate and before deciding any verdict, re-fetch "
|
|
798
|
+
"the current mutable GitHub state for this pull request and act only on what you read "
|
|
799
|
+
"now: whether the exact head still matches the Head SHA above, the current PR title and "
|
|
800
|
+
"body, the current review comments and threads, the current review requests, the current "
|
|
801
|
+
"GitHub Actions check results for that head, and the current body and labels of every "
|
|
802
|
+
"linked issue the PR references - a linked issue may have gained or lost a readiness "
|
|
803
|
+
"label (for example `ready-for-dev`) or changed priority since an earlier turn. If an "
|
|
804
|
+
"earlier turn in this conversation reviewed this PR or its linked issues, that analysis "
|
|
805
|
+
"and the repository guidance you read remain useful background, but every mutable fact "
|
|
806
|
+
"above must be re-established now; never repeat an earlier finding, verdict, or "
|
|
807
|
+
"label/priority claim that the state you just read does not support.\n"
|
|
808
|
+
f" Use `gh` or GitHub REST API calls with `{github_token_secret}`; never print secret values.\n"
|
|
809
|
+
"3. Before reviewing, you MUST read the repository's own guidance to understand the repo first.\n"
|
|
791
810
|
" Read `AGENTS.md` at the repository root (and any nested `AGENTS.md` covering the "
|
|
792
811
|
"changed files), plus other relevant docs when present - e.g. `CONTRIBUTING.md`, "
|
|
793
812
|
"`CLAUDE.md`, `.cursorrules`, and any review or coding-guideline docs. Apply that "
|
|
794
813
|
"guidance to your review.\n"
|
|
795
|
-
"
|
|
796
|
-
"
|
|
797
|
-
|
|
798
|
-
"
|
|
814
|
+
"4. SCOPE GATE - before inspecting changed files, reading the diff, or running any test, "
|
|
815
|
+
"use the repository guidance above (its scope categories and ownership boundaries) to decide "
|
|
816
|
+
"whether this change belongs in this repository and has the product/architecture direction "
|
|
817
|
+
"it needs. If it does, continue the technical review unchanged. If it does not, or needs a "
|
|
818
|
+
"product/architecture decision, stop here and publish exactly one review with "
|
|
819
|
+
f"`POST /repos/{repo}/pulls/{number}/reviews`, using `commit_id` equal to the Head SHA above "
|
|
820
|
+
"and `event: COMMENT`. Briefly say whether the change should move repositories, close, or "
|
|
821
|
+
"receive a maintainer decision; when it belongs elsewhere, name the likely owning repository "
|
|
822
|
+
"only if the evidence supports it. Do not run tests or report implementation findings. This "
|
|
823
|
+
"outcome is not an approval, and its body ends with the verdict on its own line: "
|
|
824
|
+
f"`{MAINTAINER_DECISION_VERDICT}`.\n"
|
|
825
|
+
"5. Otherwise continue the technical review. Inspect the PR discussion, existing review "
|
|
826
|
+
"comments, changed files, and the diff, together with the surrounding code in the workspace.\n"
|
|
827
|
+
"6. Ground every finding in the workspace code. Before using an inline location, verify that "
|
|
799
828
|
"the path and line are part of this pull request's diff. Compare every changed branch with "
|
|
800
829
|
"the base behavior, including side effects outside the reported bug; a revision, event, or "
|
|
801
830
|
"delivery identifier proves only the inputs it actually includes, not that unrelated profile, "
|
|
802
831
|
"credential, configuration, or external state stayed unchanged. Do not add speculative or "
|
|
803
832
|
"out-of-scope notes: every blocking or non-blocking observation must identify demonstrated "
|
|
804
833
|
"behavior on the current head and explain why it matters to the merge decision.\n"
|
|
805
|
-
f"
|
|
834
|
+
f"7. Publish one review with `POST /repos/{repo}/pulls/{number}/reviews`, using "
|
|
806
835
|
"`commit_id` equal to the Head SHA above. Use `event: APPROVE` when there are "
|
|
807
836
|
"no material findings; otherwise use `event: COMMENT`. Never use "
|
|
808
837
|
"`REQUEST_CHANGES`.\n"
|
|
@@ -813,19 +842,19 @@ def _build_review_prompt(
|
|
|
813
842
|
" Put the overall assessment in `body`, and each line-specific finding in the `comments` "
|
|
814
843
|
"array with `path`, `line`, `side: RIGHT`, and `body`.\n"
|
|
815
844
|
" Only create inline comments for actionable findings; do not open praise or nitpick threads.\n"
|
|
816
|
-
"
|
|
845
|
+
"8. If a finding cannot be attached to a changed line, put it in the review body instead. "
|
|
817
846
|
"If the API rejects the inline positions, retry with every finding in the body and no `comments` array. "
|
|
818
847
|
"If GitHub forbids the configured bot from approving its own PR, retry the clean review with "
|
|
819
848
|
"`event: COMMENT` and keep the approved verdict.\n"
|
|
820
|
-
"
|
|
849
|
+
"9. Begin the review body with this disclosure: "
|
|
821
850
|
"`_This review was posted by an AI agent (OpenHands)._`\n"
|
|
822
|
-
"
|
|
851
|
+
"10. End the review body with a verdict on its own line: either `✅ APPROVED` "
|
|
823
852
|
"or `🔄 CHANGES REQUESTED`.\n"
|
|
824
|
-
"
|
|
853
|
+
"11. If there are no material issues, still publish a review saying so, with the "
|
|
825
854
|
"disclosure and the verdict.\n"
|
|
826
855
|
f"\nReview instructions:\n{tone}{extra}{guide_section}\n\n"
|
|
827
856
|
"After GitHub accepts the review, output exactly `GITHUB_REVIEW_POSTED`. "
|
|
828
|
-
"If publishing still fails after the fallback in step
|
|
857
|
+
"If publishing still fails after the fallback in step 8, output the complete review text "
|
|
829
858
|
"so it can be posted as a comment instead."
|
|
830
859
|
)
|
|
831
860
|
|
|
@@ -123,7 +123,10 @@ class PullRequestReviewer(GitHubRepository):
|
|
|
123
123
|
"the repository's appropriate focused tests in the workspace. Do not "
|
|
124
124
|
"modify tracked files.\n"
|
|
125
125
|
f"- Re-read {self.repository} PR #{number} immediately before reporting. "
|
|
126
|
-
|
|
126
|
+
"Confirm its current body, labels, review threads, and the head's check "
|
|
127
|
+
"results, and re-read every linked issue's current body and labels: an "
|
|
128
|
+
"issue's readiness or priority may have changed since an earlier turn. "
|
|
129
|
+
f"If the head is no longer `{sha}`, {moved_head_instruction}.\n"
|
|
127
130
|
f"- {trigger_completion} after completing any configured "
|
|
128
131
|
"human-review handoff. Never paste JSON artifacts or full command logs "
|
|
129
132
|
"into comments.\n"
|
|
@@ -148,6 +151,7 @@ class PullRequestReviewer(GitHubRepository):
|
|
|
148
151
|
if not completed:
|
|
149
152
|
return False
|
|
150
153
|
approved = None
|
|
154
|
+
maintainer_decision = False
|
|
151
155
|
for review in sorted(
|
|
152
156
|
completed, key=lambda item: item["submitted_at"], reverse=True
|
|
153
157
|
):
|
|
@@ -158,9 +162,14 @@ class PullRequestReviewer(GitHubRepository):
|
|
|
158
162
|
if body.endswith("🔄 CHANGES REQUESTED"):
|
|
159
163
|
approved = False
|
|
160
164
|
break
|
|
161
|
-
|
|
165
|
+
if body.endswith(workflow.MAINTAINER_DECISION_VERDICT):
|
|
166
|
+
maintainer_decision = True
|
|
167
|
+
break
|
|
168
|
+
if approved is None and not maintainer_decision:
|
|
162
169
|
return False
|
|
163
|
-
|
|
170
|
+
# A scope stop is not an approval: it requests the maintainer decision
|
|
171
|
+
# the change is missing, through the same handoff as an approval.
|
|
172
|
+
if approved or maintainer_decision:
|
|
164
173
|
maintainers = parse_maintainers(self.config.get("maintainers"))
|
|
165
174
|
if maintainers:
|
|
166
175
|
try:
|
|
@@ -538,6 +538,75 @@ class TestRepoReviewGuide(unittest.TestCase):
|
|
|
538
538
|
self.assertIn("Do not add speculative or out-of-scope notes", prompt)
|
|
539
539
|
self.assertIn("forbids the configured bot from approving its own PR", prompt)
|
|
540
540
|
|
|
541
|
+
def test_prompt_requires_refreshing_mutable_github_state(self):
|
|
542
|
+
"""A resumed conversation must not trust an earlier turn's observations."""
|
|
543
|
+
prompt = main._build_review_prompt(
|
|
544
|
+
"owner/repo",
|
|
545
|
+
self._pr(),
|
|
546
|
+
"0123456789abcdef",
|
|
547
|
+
{"id": "1", "created_at": "t"},
|
|
548
|
+
)
|
|
549
|
+
|
|
550
|
+
assert "CURRENT STATE" in prompt
|
|
551
|
+
for surface in (
|
|
552
|
+
"still matches the Head SHA",
|
|
553
|
+
"current PR title and body",
|
|
554
|
+
"current review comments and threads",
|
|
555
|
+
"current review requests",
|
|
556
|
+
"GitHub Actions check results",
|
|
557
|
+
"body and labels of every linked issue",
|
|
558
|
+
):
|
|
559
|
+
self.assertIn(surface, prompt)
|
|
560
|
+
# The readiness case from the report: a linked issue gaining a label.
|
|
561
|
+
self.assertIn("ready-for-dev", prompt)
|
|
562
|
+
|
|
563
|
+
def test_prompt_forbids_repeating_an_earlier_observation(self):
|
|
564
|
+
prompt = main._build_review_prompt(
|
|
565
|
+
"owner/repo",
|
|
566
|
+
self._pr(),
|
|
567
|
+
"0123456789abcdef",
|
|
568
|
+
{"id": "1", "created_at": "t"},
|
|
569
|
+
)
|
|
570
|
+
|
|
571
|
+
self.assertIn("fresh review", prompt)
|
|
572
|
+
self.assertIn("re-established now", prompt)
|
|
573
|
+
self.assertIn(
|
|
574
|
+
"never repeat an earlier finding, verdict, or "
|
|
575
|
+
"label/priority claim",
|
|
576
|
+
prompt,
|
|
577
|
+
)
|
|
578
|
+
|
|
579
|
+
def test_prompt_refreshes_state_before_the_scope_gate_and_keeps_analysis(self):
|
|
580
|
+
prompt = main._build_review_prompt(
|
|
581
|
+
"owner/repo",
|
|
582
|
+
self._pr(),
|
|
583
|
+
"0123456789abcdef",
|
|
584
|
+
{"id": "1", "created_at": "t"},
|
|
585
|
+
)
|
|
586
|
+
|
|
587
|
+
# The refresh precedes the verdict-shaping steps, while the repository
|
|
588
|
+
# analysis the conversation already did stays part of the workflow.
|
|
589
|
+
self.assertLess(prompt.index("CURRENT STATE"), prompt.index("SCOPE GATE"))
|
|
590
|
+
self.assertLess(prompt.index("CURRENT STATE"), prompt.index("MUST read"))
|
|
591
|
+
self.assertIn("useful background", prompt)
|
|
592
|
+
self.assertIn("AGENTS.md", prompt)
|
|
593
|
+
|
|
594
|
+
def test_prompt_scope_gate_stops_before_the_technical_review(self):
|
|
595
|
+
prompt = main._build_review_prompt(
|
|
596
|
+
"owner/repo",
|
|
597
|
+
self._pr(),
|
|
598
|
+
"0123456789abcdef",
|
|
599
|
+
{"id": "1", "created_at": "t"},
|
|
600
|
+
)
|
|
601
|
+
|
|
602
|
+
gate = prompt.index("SCOPE GATE")
|
|
603
|
+
self.assertLess(gate, prompt.index("Inspect the PR discussion"))
|
|
604
|
+
self.assertLess(gate, prompt.index("Ground every finding"))
|
|
605
|
+
self.assertIn("before inspecting changed files, reading the diff, or running", prompt)
|
|
606
|
+
self.assertIn("scope categories and ownership boundaries", prompt)
|
|
607
|
+
self.assertIn("continue the technical review unchanged", prompt)
|
|
608
|
+
self.assertIn("move repositories, close, or receive a maintainer decision", prompt)
|
|
609
|
+
|
|
541
610
|
|
|
542
611
|
class TestNormalizeRepo(unittest.TestCase):
|
|
543
612
|
"""A repository is written down in more than one way, and every API path in
|
package/skills/index.js
CHANGED
|
@@ -19,7 +19,7 @@ export const SKILLS_CATALOG = [
|
|
|
19
19
|
"name": "add-skill",
|
|
20
20
|
"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.",
|
|
21
21
|
"triggers": [],
|
|
22
|
-
"content": "# Add Skill\n\nImport skills from GitHub repositories into the current workspace.\n\n## Workflow\n\nWhen a user requests to add a skill from a GitHub URL:\n\n1. **Parse the URL** to extract repository owner, name, and skill path\n2. **Fetch the skill** using the bundled script:\n ```bash\n python3
|
|
22
|
+
"content": "# Add Skill\n\nImport skills from GitHub repositories into the current workspace.\n\n## Workflow\n\nWhen a user requests to add a skill from a GitHub URL:\n\n1. **Parse the URL** to extract repository owner, name, and skill path\n2. **Fetch the skill** using the bundled script. Run it from this skill's\n directory (the directory containing this SKILL.md file):\n ```bash\n python3 scripts/fetch_skill.py \"<github-url>\" \"<workspace-path>\"\n ```\n3. **Verify** that SKILL.md exists in the destination\n4. **Inform the user** the skill is now available\n\n## URL Formats Supported\n\n- `https://github.com/owner/repo/tree/main/path/to/skill`\n- `https://github.com/owner/repo/skill-name`\n- `github.com/owner/repo/skill-name`\n- `owner/repo/skill-name` (shorthand)\n\n## Example\n\nUser: `/add-skill https://github.com/OpenHands/extensions/tree/main/skills/codereview`\n\n```bash\n# Run the fetch script\npython3 scripts/fetch_skill.py \"https://github.com/OpenHands/extensions/tree/main/skills/codereview\" \"/path/to/workspace\"\n\n# Verify installation\nls /path/to/workspace/.agents/skills/codereview/SKILL.md\n```\n\nOn Windows, use `python` if `python3` is not available and verify with PowerShell, for example: `Test-Path C:\\path\\to\\workspace\\.agents\\skills\\codereview\\SKILL.md`.\n\nResponse: \"✅ Added `codereview` to your workspace. The skill is now available.\"\n\n## Notes\n\n- Installs workspace-locally into `<workspace>/.agents/skills/<skill-name>/`,\n not globally; that workspace is where the skill becomes available\n- Creates `.agents/skills/` directory if it doesn't exist\n- Uses `GITHUB_TOKEN` for authentication (required for private repos)\n- Warns before overwriting existing skills with the same name",
|
|
23
23
|
"category": "agent-authoring",
|
|
24
24
|
"defaultEnabled": true
|
|
25
25
|
},
|
|
@@ -101,6 +101,14 @@ export const SKILLS_CATALOG = [
|
|
|
101
101
|
"content": "You are working with **Bitbucket Data Center** (self-hosted Bitbucket Server). You have\naccess to an environment variable, `BITBUCKET_DATA_CENTER_TOKEN`, which contains a basic\nauth token in the format `username:your-token` that allows you to interact with the git\nrepository and the REST API.\n\n> Environment variable names are case-sensitive. If `BITBUCKET_DATA_CENTER_TOKEN` is not\n> present, use whichever case variant actually exists (for example\n> `bitbucket_data_center_token`). Run `env | grep -i 'bitbucket_data_center'` to find it.\n\n- REST API base URL: `https://{domain}/rest/api/1.0`\n- Repository identifier format: `PROJECT/repo_slug` (project key, slash, repo slug)\n\nYou can use this token to interact with the Bitbucket Data Center REST API:\n```bash\ncurl -u \"${BITBUCKET_DATA_CENTER_TOKEN}\" https://{domain}/rest/api/1.0/...\n```\n\n<IMPORTANT>\nALWAYS use the Bitbucket Data Center API for operations instead of a web browser.\nALWAYS use the `create_bitbucket_data_center_pr` tool to open a pull request\n</IMPORTANT>\n\nIf you encounter authentication issues when pushing to Bitbucket Data Center (such as password prompts or permission errors), the old token may have expired. In such case, update the remote URL to include the current token: `git remote set-url origin https://${BITBUCKET_DATA_CENTER_TOKEN}@{domain}/scm/{project_lower}/{repo}.git`\n\nThe token is a `username:token` pair, so if the username or token contains characters that are reserved in URLs (such as `@`), split on the first `:` and URL-encode each part before embedding it in a remote:\n\n```bash\nBB_USER=\"${BITBUCKET_DATA_CENTER_TOKEN%%:*}\" && \\\nBB_PASS=\"${BITBUCKET_DATA_CENTER_TOKEN#*:}\" && \\\nENCODED_USER=$(python3 -c 'import sys, urllib.parse; print(urllib.parse.quote(sys.argv[1], safe=\"\"))' \"$BB_USER\") && \\\nENCODED_PASS=$(python3 -c 'import sys, urllib.parse; print(urllib.parse.quote(sys.argv[1], safe=\"\"))' \"$BB_PASS\") && \\\ngit remote set-url origin \"https://${ENCODED_USER}:${ENCODED_PASS}@{domain}/scm/{project_lower}/{repo}.git\"\n```\n\nHere are some instructions for pushing, but ONLY do this if the user asks you to:\n* NEVER push directly to the `main` or `master` branch\n* Git config (username and email) is pre-set. Do not modify.\n* You may already be on a branch starting with `openhands-workspace`. Create a new branch with a better name before pushing.\n* Use the `create_bitbucket_data_center_pr` tool to create a pull request, if you haven't already\n* Once you've created your own branch or a pull request, continue to update it. Do NOT create a new one unless you are explicitly asked to. Update the PR title and description as necessary, but don't change the branch name.\n* Use the main branch as the base branch, unless the user requests otherwise\n* After opening or updating a pull request, send the user a short message with a link to the pull request.\n* Do NOT mark a pull request as ready to review unless the user explicitly says so\n* Do all of the above in as few steps as possible. E.g. you could push changes with one step by running the following bash commands:\n```bash\ngit remote -v && git branch # to find the current org, repo and branch\ngit checkout -b create-widget && git add . && git commit -m \"Create widget\" && git push -u origin create-widget\n```",
|
|
102
102
|
"category": "code-hosting"
|
|
103
103
|
},
|
|
104
|
+
{
|
|
105
|
+
"name": "canvas-extension-api",
|
|
106
|
+
"description": "This skill should be used when the user asks to \"create an OpenHands App\", \"scaffold a Canvas App\", \"build an app with the Canvas Extensions API\", \"build an Agent Canvas App\", \"bundle a single-file Canvas App\", \"build a Sidecar-backed Canvas App\", \"add onboarding for an App service\", \"add a custom interface to Agent Canvas\", \"validate an OpenHands App\", or mentions Canvas Apps, Agent Canvas extensions, Blob-importable app bundles, registerPage, app pages, Sidecars, or canvas extension packages.",
|
|
107
|
+
"triggers": [],
|
|
108
|
+
"content": "# Canvas Extensions API\n\nBuild and validate OpenHands Apps with the Canvas Extensions API, targeting the currently implemented manifest schema 1 and host API 1 routed-page ABI.\n\nUse the naming layers consistently:\n\n- **OpenHands Apps** - customer-facing category for interfaces people discover, install, enable, and open.\n- **Apps for Agent Canvas** - product phrasing that clarifies where apps run.\n- **Canvas Extensions API** - developer platform used to build apps.\n- **Canvas extension package** - technical artifact containing `canvas-extension.json` and its entrypoint.\n- **OpenHands Extensions** - ecosystem and repository umbrella for apps, skills, plugins, automations, and integrations.\n\nUse current product labels in user-facing instructions: **Apps for Agent Canvas**, **Add app**, **Installed apps**, **App source**, **Enable trusted app**, and **Build an app**. Keep technical identifiers such as `canvas-extension.json`, `/api/canvas-extensions/*`, `host.extension`, and `/extensions/*` unchanged unless the target implementation changes them.\n\nTreat apps as trusted, same-realm browser code owned by the active Agent Server. Keep the categories distinct: apps extend what people can do in Agent Canvas, plugins extend agent runtime capabilities, and skills provide agent instructions and knowledge.\n\n\n## Prefer the proven App authoring loop\n\nTreat an App as one authenticated browser dependency graph that Canvas imports from a Blob URL. Do not build a hosted SPA: the production result must be exactly one self-contained browser ESM entrypoint that exports `activate(host)`. Always ship this UI package even when it uses a separately installed Sidecar; make the App page the guided UI for Sidecar setup after the App is opened. Never treat a Sidecar as another browser chunk or an automatic extension install hook.\n\nFor a new App, create an independent package in the target repository and implement its own UI, tests, and build tooling. Follow the Vite library-build reference in `references/packaging-recipes.md`; do not copy a shared starter or introduce a repository-wide runtime/workspace unless the target repository explicitly requires it.\n\nKeep all source, scripts, dependencies, tests, and checked-in `extension.js` inside the App package. Implement one declared page with explicit root, nested, and unknown-route behavior. Inject scoped CSS per mount and clean it up with framework roots, requests, listeners, timers, Workers, and object URLs. Build validation must verify `dist/extension.js` before synchronizing it to the App root. Do not synchronize an unverified artifact. Use `CHROME_PATH` when the Chrome executable is elsewhere.\n\nFor an existing App, run the reusable static gate before and after its own checks:\n\n```sh\nnode /path/to/canvas-extension-api/scripts/validate-extension.mjs /path/to/app\nnode /path/to/canvas-extension-api/scripts/validate-extension.mjs /path/to/app --dist --marker <required-feature-marker>\n```\n\nTreat the static validator as a gate, not a replacement for the browser Blob smoke test. Read `references/packaging-recipes.md` before adding CSS, raw assets, dynamic modules, Workers, or WASM. Read `references/backend-safety.md` before any Agent Server integration or persistence. Read `references/sidecar-pattern.md` before designing a separately installed service or its onboarding. Read `references/acceptance-checklist.md` before reporting local Canvas compatibility.\n\n## Establish the target\n\nStart by locating the target directory instead of assuming the current workspace. Inspect repository instructions, existing package management, build tooling, tests, and git status before editing.\n\nMake an early architecture decision: browser-only App, Agent Server-integrated App, Sidecar-backed App, or explicitly deployment-specific App. Keep the portable host API 1 surface separate from deployment-owned capabilities.\n\nClarify only choices that materially affect implementation:\n\n- app purpose and page behavior;\n- target repository and subdirectory;\n- dependency-free JavaScript versus a bundled TypeScript/framework project;\n- Agent Server endpoints or host metadata required;\n- whether to build only, install locally, or prepare publication instructions;\n- for a Sidecar: execution location, owner, connection route, secrets and data, lifecycle actions, and portability across Agent Canvas deployments.\n\nDefault to one app with one routed page when requirements are otherwise clear. Keep the first implementation small and dependency-free unless the requested UI clearly benefits from a framework or the repository already has a bundler.\n\nWhen creating multiple Apps in one repository, give every App an independent package root containing its own `canvas-extension.json`, entrypoint, version, tests, README, and build tooling. Do not introduce a monorepo, shared runtime, or common build foundation unless the target repository explicitly requires one. Keep app manifest names globally distinct within the Agent Server installation.\n\nTreat installation as one app per request. The current Customize -> Apps flow accepts one `source`, optional `ref`, and optional `repo_path`; it does not recursively discover or bulk-install every manifest in a repository. For a remote repository, add each app separately using the same source/ref and its own `repo_path`. For a backend-local source, select each app package directory as the source path.\n\nRead `references/v1-contract.md` before implementing unfamiliar Canvas Extensions API behavior. Read `references/connections.md` before connecting to Agent Server, the Automation service, or WebSocket endpoints. Read `references/sidecar-pattern.md` before proposing a Sidecar bridge, onboarding, or operator action. Read `references/testing-and-installation.md` before installing or testing inside Agent Canvas, especially for a multi-app repository.\n\n## Inspect current upstream behavior\n\nTreat the v1 contract as young and subject to change. Before substantial work, compare the installed or target OpenHands version with the current upstream sources when access is available:\n\n- `specs/canvas-extensions.md`\n- `src/types/canvas-extension.ts`\n- `src/components/features/canvas-extensions/canvas-extensions-runtime.tsx`\n- `src/routes/canvas-extension-page.tsx`\n- `docs/CANVAS_EXTENSIONS_TESTING.md`\n\nDo not invent planned surfaces such as conversation tabs, slots, themes, or visualizer replacement. Implement only contributions supported by the target version. Routed pages are the only implemented contribution in host API 1.\n\n## Create the app package\n\nPlace `canvas-extension.json` at the app package root. Point `entrypoint` to one self-contained browser ESM file inside that root.\n\nUse this minimal manifest shape:\n\n```json\n{\n \"schema_version\": 1,\n \"name\": \"example-dashboard\",\n \"display_name\": \"Example dashboard\",\n \"version\": \"0.1.0\",\n \"description\": \"A backend-specific project dashboard.\",\n \"entrypoint\": \"extension.js\",\n \"contributes\": {\n \"pages\": [\n {\n \"id\": \"dashboard\",\n \"title\": \"Dashboard\",\n \"path\": \"/dashboard\",\n \"nav_label\": \"Dashboard\"\n }\n ]\n }\n}\n```\n\nFollow these invariants:\n\n- Use lowercase letters, digits, and hyphens for app names and page IDs.\n- Use absolute kebab-case page paths with a leading slash.\n- Keep every manifest path within the app package root.\n- Give each page ID and path a unique value.\n- Register only page IDs declared in the manifest.\n- Keep the entrypoint free of unresolved bare imports and external chunks.\n- Bundle dependencies, CSS, and small assets into the entrypoint when using build tooling.\n\nAdd a concise `README.md` when the app is intended for reuse or publication. Document purpose, build command if any, output entrypoint, installation coordinate, and verification steps. Avoid adding explanatory change-log documents.\n\n## Implement activation and pages\n\nExport an `activate` function from the ESM entrypoint:\n\n```js\nexport function activate(host) {\n if (host.apiVersion !== \"1\") {\n throw new Error(`This app requires host API 1.`);\n }\n\n return host.registerPage(\"dashboard\", ({ container, path, navigate }) => {\n const root = document.createElement(\"section\");\n root.textContent = path ? `Nested route: ${path}` : \"Dashboard\";\n container.append(root);\n\n return () => root.remove();\n });\n}\n```\n\nUse the host contract deliberately:\n\n- Read immutable metadata from `host.extension` and `host.backend`.\n- Call `host.registerPage(id, mount)` during activation.\n- Use the mount callback's `path` as the route remainder below the declared page path.\n- Treat every nested-route navigation as a fresh mount: Canvas disposes the previous mount, clears the container, and invokes the mount callback with the new remainder. DOM and framework state do not persist across route changes; keep intentional cross-route state in the URL, backend, or activation-scoped state and clean it up on deactivation.\n- Use `navigate(absoluteCanvasPath)` for Canvas-aware routing.\n- Use `host.agentServer.request({ path, method, body, headers })` for authenticated calls to the owning Agent Server.\n- Pass only root-relative request paths beginning with one `/`; never pass absolute URLs or `//` paths.\n\nPrefer [`@openhands/typescript-client`](https://github.com/OpenHands/software-agent-sdk/tree/main/clients/typescript) for bundled TypeScript integrations whenever it covers the required Agent Server API and the necessary connection inputs are available. Prefer its typed clients, models, compatibility checks, and WebSocket lifecycle over hand-written transport code. Bundle the client into the self-contained app entrypoint; never leave a bare package import or external runtime chunk.\n\nRespect the host API 1 connection boundary:\n\n- Treat `host.agentServer.request` as the portable Agent Server HTTP path; Canvas selects the active backend and supplies authentication.\n- Do not derive an Agent Server URL from `window.location` or extract session keys from Canvas internals.\n- Do not send Automation service requests through `host.agentServer.request` unless the backend explicitly documents an Agent Server proxy for them. Automation is a separate service with deployment-provided base URL and authentication.\n- Do not open a direct Agent Server WebSocket from a portable v1 app. The host currently exposes neither the owning server origin nor a WebSocket/auth capability. Poll through `host.agentServer.request`, add a backend-owned bridge, or feature-detect a future host subscription API.\n- Use the TypeScript client's Agent Server WebSocket support in trusted standalone or future host-enabled contexts, and stop it during disposal. Do not treat the Agent Server client as an Automation service client.\n\nReturn cleanup from every layer that creates effects:\n\n- Return the unregister function or an activation disposer from `activate`.\n- Return a mount disposer for DOM nodes, timers, listeners, observers, subscriptions, and outstanding state.\n- Make async work disposal-aware so late responses cannot mutate an unmounted page.\n- Remove injected styles when the page unmounts.\n\nAssume activation, mounting, and disposal may happen repeatedly during hot enable/disable, updates, backend switches, reconnects, and route changes.\n\n## Build a production-quality page\n\nRender within the supplied `container`; do not replace unrelated Canvas DOM. Scope CSS under an app-specific root class. Prefer Canvas CSS variables with sensible fallbacks rather than copying host implementation classes.\n\nProvide:\n\n- semantic structure and headings inside the supplied container. Canvas already owns the outer `<main aria-label={page title}>`, so do not add a nested `main` or duplicate page landmark label;\n- keyboard-operable controls and visible focus;\n- responsive behavior for narrow layouts;\n- loading, empty, stale, and error states;\n- reduced-motion handling for animation;\n- text-safe rendering through DOM APIs or escaping rather than untrusted `innerHTML`;\n- clear handling for malformed or partial Agent Server responses.\n\nAvoid global event handlers, prototype changes, global CSS selectors, and ambient state unless unavoidable. Same-realm execution means these effects have full Canvas authority and cleanup is only best-effort.\n\n## Add tests\n\nTest real app code with a DOM environment and a small Canvas Extensions API host test double. Test at minimum:\n\n1. `activate` registers every declared page exactly once.\n2. The mount renders its initial state.\n3. Agent Server requests use the expected root-relative path and method.\n4. Nested navigation disposes the prior mount and remounts with the correct route remainder; test any intentionally preserved state explicitly.\n5. The mount disposer removes DOM and stops effects.\n6. The activation disposer unregisters contributions.\n7. Unsupported host API versions fail clearly when compatibility is checked.\n8. Network and malformed-response errors render safely.\n\nUse the repository's existing test infrastructure. Do not introduce a new framework when adequate tests already exist. For dependency-free fixtures, Vitest with a DOM environment matches the upstream example, but it is not part of the Canvas Extensions API runtime contract.\n\n## Validate the package\n\nRun the bundled validator before reporting completion:\n\n```sh\nnode /path/to/canvas-extension-api/scripts/validate-extension.mjs /path/to/app-package\n```\n\nThen run the target repository's formatter, linter, tests, and build. Inspect the final entrypoint rather than assuming the bundler configuration worked:\n\n- confirm the file exists at the manifest path;\n- confirm it exports `activate`;\n- reject bare imports and dynamic external chunks;\n- confirm CSS and required small assets are bundled or embedded;\n- confirm the output runs as browser ESM without Node globals.\n\nTreat validator warnings as prompts for inspection, not proof of invalidity. The helper uses conservative static checks and cannot replace loading the app bundle in Canvas.\n\n## Install and verify safely\n\nInstall only when requested. Installation and enablement are separate product actions: installation must leave the app disabled, and enabling executes trusted same-realm code.\n\nFor a backend-local app, install the path as interpreted on the Agent Server machine. For a Git-hosted app, provide `source`, optional `ref`, and optional `repo_path`. Never assume a frontend-local path exists inside a remote or containerized backend.\n\nFor multiple apps in one repository, produce an install matrix listing app name, manifest directory, source, ref, and `repo_path`. Submit one Add app operation per row. For remote repositories, omit `repo_path` only when the selected app lives at the repository root. For backend-local sources, make `source` the selected app package directory. Do not claim that selecting a repository root installs nested apps.\n\nValidate and test every app package independently, then run shared repository checks once. Report partial failures by app name rather than treating one passing app package as validation of the entire repository.\n\nVerify the lifecycle in Canvas:\n\n1. Install and confirm the inventory entry is disabled.\n2. Review source, revision, manifest metadata, and contributions.\n3. Enable and accept the trusted-code disclosure: “This app runs trusted JavaScript inside Agent Canvas and can make authenticated requests to the active Agent Server. Review its source and revision before enabling it.”\n4. Open the navigation item and exercise root and nested routes.\n5. Disable and confirm navigation and mounted UI disappear.\n6. Re-enable without restarting Canvas.\n7. Switch backend or reconnect when relevant and confirm isolation.\n8. Uninstall only when requested.\n\nDo not enable, uninstall, publish, push, or open a pull request without the user's authorization.\n\n## Report completion\n\nSummarize:\n\n- app location and contributed pages;\n- manifest schema and host API targeted;\n- build output and validation results;\n- tests run and their results;\n- installation status, explicitly stating whether it remains disabled;\n- remaining limitations tied to the current v1 ABI.\n\n## Resources\n\n- `references/v1-contract.md` - exact manifest, host API, lifecycle, routing, trust, and current limitations.\n- `references/connections.md` - Agent Server HTTP, Automation service, WebSocket, and TypeScript client connection guidance.\n- `references/testing-and-installation.md` - test strategy, manual Canvas workflow, and installation coordinates.\n- `references/packaging-recipes.md` - one-file Vite, CSS, assets, Workers, and WASM rules.\n- `references/backend-safety.md` - authenticated requests, command safety, persistence, and prerequisite onboarding.\n- `references/acceptance-checklist.md` - automated checks and the local install/enable/reload lifecycle.\n- `scripts/validate-extension.mjs` - dependency-free static artifact validator for an App package directory.",
|
|
109
|
+
"category": "agent-authoring",
|
|
110
|
+
"defaultEnabled": true
|
|
111
|
+
},
|
|
104
112
|
{
|
|
105
113
|
"name": "code-review",
|
|
106
114
|
"description": "Review code changes for material correctness, security, compatibility, and maintainability risks, grounded in the current repository and acceptance criteria.",
|
|
@@ -275,7 +283,7 @@ export const SKILLS_CATALOG = [
|
|
|
275
283
|
"triggers": [
|
|
276
284
|
"/pr-reviewer:setup"
|
|
277
285
|
],
|
|
278
|
-
"content": "# GitHub PR Reviewer Automation\n\n## Agent Canvas catalog\n\nFor new Agent Canvas installations, use the **GitHub code review** catalog\nentry. Its deterministic `worker.py` delegates each requested exact head to a\nstable conversation using the selected agent profile. It supports GitHub\nreviewer-request events and scheduled label scans. The manual upload flow below\nremains for existing deployments and is deprecated for new installations.\n\nCreate a cron automation that watches one or more GitHub repositories for pull\nrequests with a review trigger label, starts an OpenHands review conversation\nonce per label event, and publishes the AI review to GitHub.\nWindows PowerShell equivalents for the setup, packaging, upload, and API-check shell snippets are in `references/windows.md`.\n\nThe automation script is deterministic: PR discovery, label-event tracking,\nstate persistence, stale-result suppression, the repository checkout, and its\nremoval are all handled in Python. The LLM is invoked only for the review\nitself.\n\nThe script prepares each review's workspace before the agent starts: the pull\nrequest's head commit is downloaded as a tarball and extracted to a directory of\nits own, which becomes the conversation's working directory. The agent is told\nnot to clone, fetch, check out, or delete anything, and the script removes the\ncheckout once the conversation has stopped. Nothing accumulates between runs.\n\n---\n\nThe script imports shared GitHub transport from\n`scripts/github_client.py`, installed with this skill. Include it beside\n`main.py` when packaging manually, as shown below; catalog bundles include it\nautomatically.\n\n## Prerequisites\n\n### Required secret\n\nVerify that the following secret is set in **OpenHands Settings -> Secrets**:\n\n| Secret name | Token type | Minimum permissions |\n|---|---|---|\n| `GITHUB_PERSONAL_ACCESS_TOKEN` | Classic PAT | `repo` for private repos or `public_repo` for public repos |\n| `GITHUB_PERSONAL_ACCESS_TOKEN` | Fine-grained PAT | Contents: Read, Metadata: Read, Pull requests: **Read and Write**, Issues: Read and Write |\n\nPull-request **write** access is required because the agent publishes a pull\nrequest review, not just an issue comment. The Agent Canvas catalog worker may\nalso request a configured human reviewer after approval. A token with only Pull\nrequests: Read will poll happily and then fail at the point of publishing or\nrequesting the handoff.\n\nWhen several repositories are monitored, the token must cover all of them.\n\nCheck with:\n```bash\ncurl -s https://api.github.com/user \\\n -H \"Authorization: Bearer $GITHUB_PERSONAL_ACCESS_TOKEN\" \\\n | python3 -c \"import json,sys; d=json.load(sys.stdin); print(d.get('login') or d.get('message'))\"\n```\n\nIf the token is missing or invalid, inform the user and stop.\n\n---\n\n## Setup Workflow\n\nFollow these steps in order.\n\n### Step 1 - Verify `GITHUB_PERSONAL_ACCESS_TOKEN`\n\nRun the `curl` check above.\n\n- If absent: *\"GITHUB_PERSONAL_ACCESS_TOKEN is not set. Please add it in\n OpenHands Settings -> Secrets.\"* Stop.\n- If the API returns `{\"message\": \"Bad credentials\"}`: tell the user the\n token is invalid and ask them to update it. Stop.\n\n### Step 2 - Collect repositories\n\nAsk: *\"Which GitHub repositories should be monitored?\n(Format: `owner/repo`, e.g. `myorg/backend`. List several separated by commas to\nreview them all from one automation.)\"*\n\nValidate access to **each** repository:\n```bash\ncurl -s \"https://api.github.com/repos/{owner}/{repo}\" \\\n -H \"Authorization: Bearer $GITHUB_PERSONAL_ACCESS_TOKEN\" \\\n | python3 -c \"\nimport json, sys\nd = json.load(sys.stdin)\nif 'message' in d:\n print('ERROR:', d['message'])\nelse:\n print(f\\\"Accessible. Private: {d.get('private')}. Permissions: {d.get('permissions')}\\\")\n\"\n```\n\nRecord every accepted repository into `REPOS = [\"{owner}/{repo}\", ...]`. If one\nrepository fails the check, say which and ask whether to continue without it.\n\nEach repository is polled independently and keeps its own state, so pull-request\nnumbers never collide between them. The trigger label, tone, and schedule are\nshared by all of them; a repository needing different settings wants its own\nautomation.\n\n### Step 3 - Collect trigger label\n\nAsk: *\"Which PR label should trigger a review?\n(Press Enter for the default: `openhands-review`.)\"*\n\nRecord the answer as `TRIGGER_LABEL`. If the label does not exist yet, tell the\nuser that GitHub will still record the event once the label is created and\napplied to a PR.\n\nThe automation reviews a PR when it sees the latest matching `labeled` event for\nthat label. To request another review later, remove and re-apply the label.\n\n### Step 4 - Collect review tone\n\nAsk: *\"What review tone should the reviewer use?\n 1. Thorough (default) - comprehensive coverage of correctness, security, tests, style\n 2. Concise - high-signal only, skips minor style feedback\n 3. Friendly - constructive and encouraging\n(Press Enter for Thorough, or type your choice or any custom style description)\"*\n\nMap the choice to `REVIEW_TONE`:\n\n| Answer | `REVIEW_TONE` | `REVIEW_STYLE_INSTRUCTIONS` |\n|---|---|---|\n| 1 / Enter | `\"thorough\"` | `\"\"` |\n| 2 | `\"concise\"` | `\"\"` |\n| 3 | `\"friendly\"` | `\"\"` |\n| Custom text, e.g. `strict but kind` | `\"thorough\"` | the custom text verbatim |\n\n### Step 5 - Collect cron schedule\n\nAsk: *\"How often should the automation poll for labeled PRs?\n(Press Enter for the default: every 5 minutes.\nUse a cron expression for a different interval, e.g. `0 * * * *` = hourly)\"*\n\nDefault: `*/5 * * * *`.\n\nRecord as `CRON_SCHEDULE`.\n\n### Step 6 - Generate the automation script\n\nRead `scripts/main.py` from this skill's directory. Apply exactly six constant\nsubstitutions near the top of the file:\n\n> The script also reads a `config.json` shipped beside it, if there is one, over\n> these constants. That is how the catalog entry\n> (`automations/catalog/github-pr-reviewer/`) configures an unmodified copy,\n> since a declarative host cannot rewrite Python. This setup path substitutes the\n> constants and ships no `config.json`, so the two never collide.\n\n| Placeholder | Replace with |\n|---|---|\n| `REPOS = [\"owner/repo\"]` | `REPOS = [\"{owner_repo}\", ...]` - one entry per repository collected in Step 2 |\n| `TRIGGER_LABEL = \"openhands-review\"` | `TRIGGER_LABEL = \"{trigger_label}\"` |\n| `REVIEW_TONE = \"thorough\"` | `REVIEW_TONE = \"{review_tone}\"` |\n| `REVIEW_STYLE_INSTRUCTIONS = \"\"` | `REVIEW_STYLE_INSTRUCTIONS = \"{style_instructions}\"` |\n| `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 |\n| `DEFAULT_OPENHANDS_URL = \"http://localhost:8000\"` | leave unchanged unless the user has a preference |\n\nUse a safe string writer such as `json.dumps(value)` when inserting user-provided\nrepository names, labels, or style instructions into Python string literals.\n`json.dumps(list_of_repos)` produces the whole `REPOS` list safely in one step.\n\nRun these commands from this skill's directory and write the customized script\nto a temporary build directory:\n```bash\nmkdir -p /tmp/pr-reviewer-build\ncp -L scripts/github_client.py /tmp/pr-reviewer-build/github_client.py\n# write the customized main.py to /tmp/pr-reviewer-build/main.py\n```\n\nValidate syntax before packaging:\n```bash\npython3 -m py_compile /tmp/pr-reviewer-build/main.py && echo \"Syntax OK\"\n```\n\nFix any syntax errors before proceeding.\n\n### Step 7 - Package and upload\n\nDetermine the Automation backend URL and auth from the `<RUNTIME_SERVICES>`\nblock in your system context:\n- **OPENHANDS_HOST**: the Automation backend `url_from_agent`\n- **Auth**: `X-Session-API-Key: $OPENHANDS_AUTOMATION_API_KEY`\n\n```bash\ntar -czf /tmp/pr-reviewer.tar.gz -C /tmp/pr-reviewer-build .\n\nTARBALL_PATH=$(curl -s -X POST \\\n \"${OPENHANDS_HOST}/api/automation/v1/uploads?name=github-pr-reviewer\" \\\n -H \"X-Session-API-Key: $OPENHANDS_AUTOMATION_API_KEY\" \\\n -H \"Content-Type: application/gzip\" \\\n --data-binary @/tmp/pr-reviewer.tar.gz \\\n | python3 -c \"import json,sys; print(json.load(sys.stdin)['tarball_path'])\")\n\necho \"Uploaded: $TARBALL_PATH\"\n```\n\n### Step 8 - Register the automation\n\n```bash\ncurl -s -X POST \"${OPENHANDS_HOST}/api/automation/v1\" \\\n -H \"X-Session-API-Key: $OPENHANDS_AUTOMATION_API_KEY\" \\\n -H \"Content-Type: application/json\" \\\n -d \"{\n \\\"name\\\": \\\"GitHub PR Reviewer: {repo_summary} label {trigger_label}\\\",\n \\\"trigger\\\": {\\\"type\\\": \\\"cron\\\", \\\"schedule\\\": \\\"{cron_schedule}\\\"},\n \\\"tarball_path\\\": \\\"$TARBALL_PATH\\\",\n \\\"entrypoint\\\": \\\"python3 main.py\\\",\n \\\"timeout\\\": 600\n }\" | python3 -m json.tool\n```\n\nUse the single repository as `{repo_summary}` when there is one, and something\nlike `3 repos` when there are several. A poll now downloads a tarball per queued\nreview, so the timeout allows for that; a run never waits for a review to\nfinish, only for it to be started.\n\nRecord the returned `id`.\n\n### Step 9 - Confirm\n\nTell the user:\n\n> ✅ **GitHub PR Reviewer** is running!\n>\n> - Automation ID: `{id}`\n> - Repositories: `{owner}/{repo}`, ... (one line each)\n> - Trigger label: `{trigger_label}`\n> - Review tone: `{tone}`\n> - Polling schedule: `{cron_schedule}`\n> - State file per repository:\n> `~/.openhands/workspaces/automation-state/github_pr_reviewer_label_event_{id}_{owner}__{repo}.json`\n>\n> Apply the `{trigger_label}` label to a pull request to queue a review. Each\n> label event is processed once. To request another review, remove and re-apply\n> the label.\n>\n> The review is published as a pull request review on the head commit, with\n> inline comments where a finding maps to a changed line.\n\n---\n\n## Runtime Behaviour (per poll)\n\nEach cron run executes `main.py`, which resolves and validates\n`GITHUB_PERSONAL_ACCESS_TOKEN` once, then processes every repository in `REPOS`\nindependently. One repository failing does not stop the others; the run fails\nonly if every repository fails.\n\nFor each repository:\n\n1. Loads that repository's state (see `references/state-schema.md`).\n2. Verifies repository access.\n3. Lists open PRs, newest-updated first.\n4. For each open PR carrying `TRIGGER_LABEL`:\n - Refetches current PR metadata to avoid acting on stale list data.\n - Finds the latest matching GitHub `labeled` issue event.\n - Skips the event if it has already been tracked.\n - Downloads the PR's head commit as a tarball and extracts it to\n `{WORKSPACE_BASE}/repositories/{owner}__{repo}/pr-{number}-{sha12}`. The\n archive is checked as it is unpacked: a single root, no absolute or `..`\n paths, and symlinks skipped rather than materialised.\n - Starts an OpenHands conversation **whose working directory is that\n checkout**, with a review prompt carrying PR metadata, the exact head SHA,\n and label event details.\n - Posts an acknowledgement comment with the label event, head SHA, and\n conversation link.\n - Records the review in state with `status: \"active\"` and the checkout path.\n - If the checkout or the conversation cannot be created, the checkout is\n removed and nothing is recorded, so the next poll retries the label event.\n5. For each active review conversation:\n - Marks it closed without posting if the PR has closed or merged.\n - Suppresses stale results if the PR head SHA changed after the review was\n queued.\n - When the conversation reaches `idle`, `finished`, `error`, or `stuck`,\n asks GitHub whether a review by the token's own user exists for that head\n SHA. If it does, the review is complete. If it does not, the agent's final\n response is posted as a comment so the work is not lost.\n - Abandons a conversation that has not reached a terminal status within two\n hours, so its checkout can be reclaimed.\n6. Removes the checkout of every finished review, but only after confirming the\n conversation has stopped - deleting it under a running agent would remove its\n working directory. When that cannot be confirmed the directory is left alone\n and the next poll tries again.\n7. Saves that repository's state atomically.\n\nThe completion callback fires once for the whole run.\n\n---\n\n## Additional Resources\n\n- **`references/state-schema.md`** - State JSON schema, field definitions, and\n review lifecycle diagram.\n- **`scripts/main.py`** - The complete automation script. Customize the five\n constants at the top before packaging.\n- **`tests/test_main.py`** - Unit tests for the checkout, its removal, and state\n handling. Run them from the skill root with `python -m pytest tests/` after\n editing the script.\n\n---\n\n## Troubleshooting\n\n| Symptom | Likely cause | Fix |\n|---|---|---|\n| Bot never queues reviews | Trigger label not present or no matching `labeled` event | Apply the configured label to the PR |\n| \"Bad credentials\" in run logs | Token expired | Rotate and update `GITHUB_PERSONAL_ACCESS_TOKEN` |\n| 404 on repo access | Repo name wrong or no access | Re-check the entry in `REPOS` and the token's permissions |\n| One repository is skipped, others work | That repository failed its access check | Read the `=== owner/repo ===` block in the run log |\n| Same PR not reviewed after new commits | Label event was already processed | Remove and re-apply the trigger label |\n| Review result never posts | Conversation still running or stuck | Open the conversation link from the acknowledgement comment |\n| Stale review suppressed | PR head SHA changed while the agent was reviewing | Re-apply the trigger label after the latest commit |\n| Review arrives as a plain comment, not a review | Publishing failed, so the script posted the text as a fallback | Check that the token has Pull requests: Read and Write |\n| Agent reports it cannot clone the repo | Prompt asked it not to; the workspace is already the checkout | No action - the code is at the head SHA in its working directory |\n| Checkouts remain under `repositories/` | Their conversations had not stopped yet | They are removed by a later poll once the conversation is terminal |",
|
|
286
|
+
"content": "# GitHub PR Reviewer Automation\n\n## Agent Canvas catalog\n\nFor new Agent Canvas installations, use the **GitHub code review** catalog\nentry. Its deterministic `worker.py` delegates each requested exact head to a\nstable conversation using the selected agent profile. It supports GitHub\nreviewer-request events and scheduled label scans. The manual upload flow below\nremains for existing deployments and is deprecated for new installations.\n\nCreate a cron automation that watches one or more GitHub repositories for pull\nrequests with a review trigger label, starts an OpenHands review conversation\nonce per label event, and publishes the AI review to GitHub.\nWindows PowerShell equivalents for the setup, packaging, upload, and API-check shell snippets are in `references/windows.md`.\n\nThe automation script is deterministic: PR discovery, label-event tracking,\nstate persistence, stale-result suppression, the repository checkout, and its\nremoval are all handled in Python. The LLM is invoked only for the review\nitself.\n\nEach explicit review request - a re-applied trigger label, or a new reviewer\nrequest in event mode - is a fresh review. The conversation for a PR is reused,\nbut its earlier turns must not be trusted as current: before deciding a verdict\nthe reviewer re-fetches the mutable GitHub state (the exact head, the PR body,\nreview comments and threads, review requests, the linked issues' bodies and\nlabels, and the current-head Actions results) and ignores any earlier finding,\nverdict, or label/priority claim that state no longer supports. Repository\nanalysis the conversation already did, such as reading `AGENTS.md`, stays\nuseful and is not repeated.\n\nThe review prompt starts with a scope gate: using the repository's own guidance\n(its scope categories and ownership boundaries, not a list of individual PR\nnumbers), the reviewer decides whether the change belongs in this repository\nand has the product/architecture direction it needs. When it does not, the review\nstops with a single `event: COMMENT` review that says whether the change should\nmove repositories, close, or receive a maintainer decision, and ends with the\n`🛑 MAINTAINER DECISION REQUIRED` verdict. That outcome is **neither an approval\nnor a change request**: it does not approve or merge the PR. The completion\nhandler recognizes the verdict and requests one configured maintainer through the\nsame handoff used after an approval. An in-scope change continues the existing\nreview unchanged.\n\nThe script prepares each review's workspace before the agent starts: the pull\nrequest's head commit is downloaded as a tarball and extracted to a directory of\nits own, which becomes the conversation's working directory. The agent is told\nnot to clone, fetch, check out, or delete anything, and the script removes the\ncheckout once the conversation has stopped. Nothing accumulates between runs.\n\n---\n\nThe script imports shared GitHub transport from\n`scripts/github_client.py`, installed with this skill. Include it beside\n`main.py` when packaging manually, as shown below; catalog bundles include it\nautomatically.\n\n## Prerequisites\n\n### Required secret\n\nVerify that the following secret is set in **OpenHands Settings -> Secrets**:\n\n| Secret name | Token type | Minimum permissions |\n|---|---|---|\n| `GITHUB_PERSONAL_ACCESS_TOKEN` | Classic PAT | `repo` for private repos or `public_repo` for public repos |\n| `GITHUB_PERSONAL_ACCESS_TOKEN` | Fine-grained PAT | Contents: Read, Metadata: Read, Pull requests: **Read and Write**, Issues: Read and Write |\n\nPull-request **write** access is required because the agent publishes a pull\nrequest review, not just an issue comment. The Agent Canvas catalog worker may\nalso request a configured human reviewer after approval. A token with only Pull\nrequests: Read will poll happily and then fail at the point of publishing or\nrequesting the handoff.\n\nWhen several repositories are monitored, the token must cover all of them.\n\nCheck with:\n```bash\ncurl -s https://api.github.com/user \\\n -H \"Authorization: Bearer $GITHUB_PERSONAL_ACCESS_TOKEN\" \\\n | python3 -c \"import json,sys; d=json.load(sys.stdin); print(d.get('login') or d.get('message'))\"\n```\n\nIf the token is missing or invalid, inform the user and stop.\n\n---\n\n## Setup Workflow\n\nFollow these steps in order.\n\n### Step 1 - Verify `GITHUB_PERSONAL_ACCESS_TOKEN`\n\nRun the `curl` check above.\n\n- If absent: *\"GITHUB_PERSONAL_ACCESS_TOKEN is not set. Please add it in\n OpenHands Settings -> Secrets.\"* Stop.\n- If the API returns `{\"message\": \"Bad credentials\"}`: tell the user the\n token is invalid and ask them to update it. Stop.\n\n### Step 2 - Collect repositories\n\nAsk: *\"Which GitHub repositories should be monitored?\n(Format: `owner/repo`, e.g. `myorg/backend`. List several separated by commas to\nreview them all from one automation.)\"*\n\nValidate access to **each** repository:\n```bash\ncurl -s \"https://api.github.com/repos/{owner}/{repo}\" \\\n -H \"Authorization: Bearer $GITHUB_PERSONAL_ACCESS_TOKEN\" \\\n | python3 -c \"\nimport json, sys\nd = json.load(sys.stdin)\nif 'message' in d:\n print('ERROR:', d['message'])\nelse:\n print(f\\\"Accessible. Private: {d.get('private')}. Permissions: {d.get('permissions')}\\\")\n\"\n```\n\nRecord every accepted repository into `REPOS = [\"{owner}/{repo}\", ...]`. If one\nrepository fails the check, say which and ask whether to continue without it.\n\nEach repository is polled independently and keeps its own state, so pull-request\nnumbers never collide between them. The trigger label, tone, and schedule are\nshared by all of them; a repository needing different settings wants its own\nautomation.\n\n### Step 3 - Collect trigger label\n\nAsk: *\"Which PR label should trigger a review?\n(Press Enter for the default: `openhands-review`.)\"*\n\nRecord the answer as `TRIGGER_LABEL`. If the label does not exist yet, tell the\nuser that GitHub will still record the event once the label is created and\napplied to a PR.\n\nThe automation reviews a PR when it sees the latest matching `labeled` event for\nthat label. To request another review later, remove and re-apply the label.\n\n### Step 4 - Collect review tone\n\nAsk: *\"What review tone should the reviewer use?\n 1. Thorough (default) - comprehensive coverage of correctness, security, tests, style\n 2. Concise - high-signal only, skips minor style feedback\n 3. Friendly - constructive and encouraging\n(Press Enter for Thorough, or type your choice or any custom style description)\"*\n\nMap the choice to `REVIEW_TONE`:\n\n| Answer | `REVIEW_TONE` | `REVIEW_STYLE_INSTRUCTIONS` |\n|---|---|---|\n| 1 / Enter | `\"thorough\"` | `\"\"` |\n| 2 | `\"concise\"` | `\"\"` |\n| 3 | `\"friendly\"` | `\"\"` |\n| Custom text, e.g. `strict but kind` | `\"thorough\"` | the custom text verbatim |\n\n### Step 5 - Collect cron schedule\n\nAsk: *\"How often should the automation poll for labeled PRs?\n(Press Enter for the default: every 5 minutes.\nUse a cron expression for a different interval, e.g. `0 * * * *` = hourly)\"*\n\nDefault: `*/5 * * * *`.\n\nRecord as `CRON_SCHEDULE`.\n\n### Step 6 - Generate the automation script\n\nRead `scripts/main.py` from this skill's directory. Apply exactly six constant\nsubstitutions near the top of the file:\n\n> The script also reads a `config.json` shipped beside it, if there is one, over\n> these constants. That is how the catalog entry\n> (`automations/catalog/github-pr-reviewer/`) configures an unmodified copy,\n> since a declarative host cannot rewrite Python. This setup path substitutes the\n> constants and ships no `config.json`, so the two never collide.\n\n| Placeholder | Replace with |\n|---|---|\n| `REPOS = [\"owner/repo\"]` | `REPOS = [\"{owner_repo}\", ...]` - one entry per repository collected in Step 2 |\n| `TRIGGER_LABEL = \"openhands-review\"` | `TRIGGER_LABEL = \"{trigger_label}\"` |\n| `REVIEW_TONE = \"thorough\"` | `REVIEW_TONE = \"{review_tone}\"` |\n| `REVIEW_STYLE_INSTRUCTIONS = \"\"` | `REVIEW_STYLE_INSTRUCTIONS = \"{style_instructions}\"` |\n| `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 |\n| `DEFAULT_OPENHANDS_URL = \"http://localhost:8000\"` | leave unchanged unless the user has a preference |\n\nUse a safe string writer such as `json.dumps(value)` when inserting user-provided\nrepository names, labels, or style instructions into Python string literals.\n`json.dumps(list_of_repos)` produces the whole `REPOS` list safely in one step.\n\nRun these commands from this skill's directory and write the customized script\nto a temporary build directory:\n```bash\nmkdir -p /tmp/pr-reviewer-build\ncp -L scripts/github_client.py /tmp/pr-reviewer-build/github_client.py\n# write the customized main.py to /tmp/pr-reviewer-build/main.py\n```\n\nValidate syntax before packaging:\n```bash\npython3 -m py_compile /tmp/pr-reviewer-build/main.py && echo \"Syntax OK\"\n```\n\nFix any syntax errors before proceeding.\n\n### Step 7 - Package and upload\n\nDetermine the Automation backend URL and auth from the `<RUNTIME_SERVICES>`\nblock in your system context:\n- **OPENHANDS_HOST**: the Automation backend `url_from_agent`\n- **Auth**: `X-Session-API-Key: $OPENHANDS_AUTOMATION_API_KEY`\n\n```bash\ntar -czf /tmp/pr-reviewer.tar.gz -C /tmp/pr-reviewer-build .\n\nTARBALL_PATH=$(curl -s -X POST \\\n \"${OPENHANDS_HOST}/api/automation/v1/uploads?name=github-pr-reviewer\" \\\n -H \"X-Session-API-Key: $OPENHANDS_AUTOMATION_API_KEY\" \\\n -H \"Content-Type: application/gzip\" \\\n --data-binary @/tmp/pr-reviewer.tar.gz \\\n | python3 -c \"import json,sys; print(json.load(sys.stdin)['tarball_path'])\")\n\necho \"Uploaded: $TARBALL_PATH\"\n```\n\n### Step 8 - Register the automation\n\n```bash\ncurl -s -X POST \"${OPENHANDS_HOST}/api/automation/v1\" \\\n -H \"X-Session-API-Key: $OPENHANDS_AUTOMATION_API_KEY\" \\\n -H \"Content-Type: application/json\" \\\n -d \"{\n \\\"name\\\": \\\"GitHub PR Reviewer: {repo_summary} label {trigger_label}\\\",\n \\\"trigger\\\": {\\\"type\\\": \\\"cron\\\", \\\"schedule\\\": \\\"{cron_schedule}\\\"},\n \\\"tarball_path\\\": \\\"$TARBALL_PATH\\\",\n \\\"entrypoint\\\": \\\"python3 main.py\\\",\n \\\"timeout\\\": 600\n }\" | python3 -m json.tool\n```\n\nUse the single repository as `{repo_summary}` when there is one, and something\nlike `3 repos` when there are several. A poll now downloads a tarball per queued\nreview, so the timeout allows for that; a run never waits for a review to\nfinish, only for it to be started.\n\nRecord the returned `id`.\n\n### Step 9 - Confirm\n\nTell the user:\n\n> ✅ **GitHub PR Reviewer** is running!\n>\n> - Automation ID: `{id}`\n> - Repositories: `{owner}/{repo}`, ... (one line each)\n> - Trigger label: `{trigger_label}`\n> - Review tone: `{tone}`\n> - Polling schedule: `{cron_schedule}`\n> - State file per repository:\n> `~/.openhands/workspaces/automation-state/github_pr_reviewer_label_event_{id}_{owner}__{repo}.json`\n>\n> Apply the `{trigger_label}` label to a pull request to queue a review. Each\n> label event is processed once. To request another review, remove and re-apply\n> the label.\n>\n> The review is published as a pull request review on the head commit, with\n> inline comments where a finding maps to a changed line.\n\n---\n\n## Runtime Behaviour (per poll)\n\nEach cron run executes `main.py`, which resolves and validates\n`GITHUB_PERSONAL_ACCESS_TOKEN` once, then processes every repository in `REPOS`\nindependently. One repository failing does not stop the others; the run fails\nonly if every repository fails.\n\nFor each repository:\n\n1. Loads that repository's state (see `references/state-schema.md`).\n2. Verifies repository access.\n3. Lists open PRs, newest-updated first.\n4. For each open PR carrying `TRIGGER_LABEL`:\n - Refetches current PR metadata to avoid acting on stale list data.\n - Finds the latest matching GitHub `labeled` issue event.\n - Skips the event if it has already been tracked.\n - Downloads the PR's head commit as a tarball and extracts it to\n `{WORKSPACE_BASE}/repositories/{owner}__{repo}/pr-{number}-{sha12}`. The\n archive is checked as it is unpacked: a single root, no absolute or `..`\n paths, and symlinks skipped rather than materialised.\n - Starts an OpenHands conversation **whose working directory is that\n checkout**, with a review prompt carrying PR metadata, the exact head SHA,\n label event details, and the requirement to re-fetch the current mutable\n GitHub state before deciding a verdict.\n - Posts an acknowledgement comment with the label event, head SHA, and\n conversation link.\n - Records the review in state with `status: \"active\"` and the checkout path.\n - If the checkout or the conversation cannot be created, the checkout is\n removed and nothing is recorded, so the next poll retries the label event.\n5. For each active review conversation:\n - Marks it closed without posting if the PR has closed or merged.\n - Suppresses stale results if the PR head SHA changed after the review was\n queued.\n - When the conversation reaches `idle`, `finished`, `error`, or `stuck`,\n asks GitHub whether a review by the token's own user exists for that head\n SHA. If it does, the review is complete. If it does not, the agent's final\n response is posted as a comment so the work is not lost.\n - Abandons a conversation that has not reached a terminal status within two\n hours, so its checkout can be reclaimed.\n6. Removes the checkout of every finished review, but only after confirming the\n conversation has stopped - deleting it under a running agent would remove its\n working directory. When that cannot be confirmed the directory is left alone\n and the next poll tries again.\n7. Saves that repository's state atomically.\n\nThe completion callback fires once for the whole run.\n\n---\n\n## Additional Resources\n\n- **`references/state-schema.md`** - State JSON schema, field definitions, and\n review lifecycle diagram.\n- **`scripts/main.py`** - The complete automation script. Customize the five\n constants at the top before packaging.\n- **`tests/test_main.py`** - Unit tests for the checkout, its removal, and state\n handling. Run them from the skill root with `python -m pytest tests/` after\n editing the script.\n\n---\n\n## Troubleshooting\n\n| Symptom | Likely cause | Fix |\n|---|---|---|\n| Bot never queues reviews | Trigger label not present or no matching `labeled` event | Apply the configured label to the PR |\n| \"Bad credentials\" in run logs | Token expired | Rotate and update `GITHUB_PERSONAL_ACCESS_TOKEN` |\n| 404 on repo access | Repo name wrong or no access | Re-check the entry in `REPOS` and the token's permissions |\n| One repository is skipped, others work | That repository failed its access check | Read the `=== owner/repo ===` block in the run log |\n| Same PR not reviewed after new commits | Label event was already processed | Remove and re-apply the trigger label |\n| Review result never posts | Conversation still running or stuck | Open the conversation link from the acknowledgement comment |\n| Stale review suppressed | PR head SHA changed while the agent was reviewing | Re-apply the trigger label after the latest commit |\n| Review arrives as a plain comment, not a review | Publishing failed, so the script posted the text as a fallback | Check that the token has Pull requests: Read and Write |\n| Agent reports it cannot clone the repo | Prompt asked it not to; the workspace is already the checkout | No action - the code is at the head SHA in its working directory |\n| Checkouts remain under `repositories/` | Their conversations had not stopped yet | They are removed by a later poll once the conversation is terminal |",
|
|
279
287
|
"category": "automations"
|
|
280
288
|
},
|
|
281
289
|
{
|
|
@@ -676,5 +684,5 @@ export const SKILLS_CATALOG = [
|
|
|
676
684
|
"category": "integrations"
|
|
677
685
|
}
|
|
678
686
|
];
|
|
679
|
-
export const DEFAULT_ENABLED_SKILL_NAMES = ["add-skill","agent-canvas-environment","agent-memory","agent-sdk-builder","code-review","docker","github","openhands-api","openhands-automation","openhands-sdk","skill-creator"];
|
|
687
|
+
export const DEFAULT_ENABLED_SKILL_NAMES = ["add-skill","agent-canvas-environment","agent-memory","agent-sdk-builder","canvas-extension-api","code-review","docker","github","openhands-api","openhands-automation","openhands-sdk","skill-creator"];
|
|
680
688
|
export default SKILLS_CATALOG;
|
|
@@ -67,7 +67,7 @@
|
|
|
67
67
|
"timeout": 300,
|
|
68
68
|
"template": {
|
|
69
69
|
"id": "github-pr-reviewer",
|
|
70
|
-
"version": "1.
|
|
70
|
+
"version": "1.5.0",
|
|
71
71
|
"config": {
|
|
72
72
|
"repos": [
|
|
73
73
|
"OpenHands/agent-server-gui"
|
|
@@ -106,7 +106,7 @@
|
|
|
106
106
|
"timeout": 300,
|
|
107
107
|
"template": {
|
|
108
108
|
"id": "github-pr-reviewer",
|
|
109
|
-
"version": "1.
|
|
109
|
+
"version": "1.5.0",
|
|
110
110
|
"config": {
|
|
111
111
|
"repos": [
|
|
112
112
|
"OpenHands/agent-server-gui"
|
|
@@ -209,7 +209,7 @@
|
|
|
209
209
|
"timeout": 300,
|
|
210
210
|
"template": {
|
|
211
211
|
"id": "github-pr-reviewer",
|
|
212
|
-
"version": "1.
|
|
212
|
+
"version": "1.5.0",
|
|
213
213
|
"config": {
|
|
214
214
|
"repos": [
|
|
215
215
|
"OpenHands/agent-server-gui",
|
|
@@ -249,7 +249,7 @@
|
|
|
249
249
|
"timeout": 300,
|
|
250
250
|
"template": {
|
|
251
251
|
"id": "github-pr-reviewer",
|
|
252
|
-
"version": "1.
|
|
252
|
+
"version": "1.5.0",
|
|
253
253
|
"config": {
|
|
254
254
|
"repos": [
|
|
255
255
|
"OpenHands/agent-server-gui",
|
|
@@ -290,7 +290,7 @@
|
|
|
290
290
|
"preset_metadata": {
|
|
291
291
|
"template": {
|
|
292
292
|
"id": "github-pr-reviewer",
|
|
293
|
-
"version": "1.
|
|
293
|
+
"version": "1.5.0",
|
|
294
294
|
"config": {
|
|
295
295
|
"repos": [
|
|
296
296
|
"OpenHands/agent-server-gui",
|
|
@@ -343,7 +343,7 @@
|
|
|
343
343
|
"timeout": 300,
|
|
344
344
|
"template": {
|
|
345
345
|
"id": "github-pr-reviewer",
|
|
346
|
-
"version": "1.
|
|
346
|
+
"version": "1.5.0",
|
|
347
347
|
"config": {
|
|
348
348
|
"repos": [
|
|
349
349
|
"OpenHands/agent-server-gui"
|
|
@@ -434,7 +434,7 @@
|
|
|
434
434
|
"timeout": 300,
|
|
435
435
|
"template": {
|
|
436
436
|
"id": "github-pr-reviewer",
|
|
437
|
-
"version": "1.
|
|
437
|
+
"version": "1.5.0",
|
|
438
438
|
"config": {
|
|
439
439
|
"repos": [
|
|
440
440
|
"OpenHands/agent-server-gui"
|
|
@@ -528,7 +528,7 @@
|
|
|
528
528
|
"timeout": 300,
|
|
529
529
|
"template": {
|
|
530
530
|
"id": "github-pr-reviewer",
|
|
531
|
-
"version": "1.
|
|
531
|
+
"version": "1.5.0",
|
|
532
532
|
"config": {
|
|
533
533
|
"repos": [
|
|
534
534
|
"OpenHands/agent-server-gui"
|
|
@@ -602,7 +602,7 @@
|
|
|
602
602
|
"timeout": 300,
|
|
603
603
|
"template": {
|
|
604
604
|
"id": "github-pr-reviewer",
|
|
605
|
-
"version": "1.
|
|
605
|
+
"version": "1.5.0",
|
|
606
606
|
"config": {
|
|
607
607
|
"repos": [
|
|
608
608
|
"OpenHands/automation"
|