feat: add github-repo-monitor skill#259
Conversation
Adds a new skill that creates a cron automation to monitor a GitHub repository for comments on issues and PRs. When a comment containing a configurable trigger phrase (@OpenHands by default) is detected the skill: - Verifies GITHUB_TOKEN is set and has comment-posting permissions - Fetches the full issue/PR context (title, body, labels, last 10 comments) - Creates an OpenHands conversation pre-loaded with that context - Posts a GitHub acknowledgement comment with a conversation link - Forwards follow-up trigger comments to the running conversation - Re-opens closed conversations on new triggers (creates fresh if deleted) - Posts a summary GitHub comment when the conversation finishes Mirrors the structure and patterns of slack-channel-monitor, adapted for the GitHub API. Includes SKILL.md setup workflow, main.py template, state-schema.md, github-api.md reference, and plugin metadata files. Co-authored-by: openhands <openhands@all-hands.dev>
all-hands-bot
left a comment
There was a problem hiding this comment.
[CRITICAL ISSUES]
-
Marketplace Registration Missing: The skill is not registered in any marketplace JSON file. Per repo conventions, every skill under
skills/must be listed in a marketplace or CI will fail withSkills missing from marketplace: [github-repo-monitor]. Add an entry tomarketplaces/openhands-extensions.jsonfollowing the format ofslack-channel-monitor. -
Hardcoded Trigger Phrase: Line 552 hardcodes "@OpenHands mention" in the acknowledgement message, but
TRIGGER_PHRASEis configurable. Users who set a custom trigger will see incorrect acknowledgements. -
PR Description Typos: Multiple duplicated/malformed sentences: "Each cron run of the resulting autoEach cron run", "Forwards subsequent trigger comments on the same issue/PR to th - Forwards subsequent", "skips commenent posted by" (typo: commenent → comment).
[IMPROVEMENT OPPORTUNITIES]
See inline comments for suggestions on path handling and acknowledgement message clarity.
[RISK ASSESSMENT]
- [Overall PR]
⚠️ Risk Assessment: 🟡 MEDIUM
This adds a GitHub polling automation with API access and conversation management. The code quality is good with proper error handling and bot detection. Main risks: API rate limiting (mitigated by 1-min poll interval), state corruption (mitigated by atomic JSON writes), and feedback loops (mitigated by bot detection). The security model is sound - tokens from secrets, proper permission checks.
VERDICT:
❌ Needs rework: Add marketplace registration and fix hardcoded trigger phrase before merging.
KEY INSIGHT:
Clean state management and sensible conversation lifecycle (create → forward → close → re-open), but missing marketplace entry will block CI.
Improve this review? If any feedback above seems incorrect or irrelevant to this repository, you can teach the reviewer to do better:
- Add a
.agents/skills/custom-codereview-guide.mdfile to your branch (or edit it if one already exists) with the/codereviewtrigger and the context the reviewer is missing (e.g., "Security concerns about X do not apply here because Y"). See the customization docs for the required frontmatter format.- Re-request a review - the reviewer reads guidelines from the PR branch, so your changes take effect immediately.
- When your PR is merged, the guideline file goes through normal code review by repository maintainers.
Resolve with AI? Install the iterate skill in your agent and run
/iterateto automatically drive this PR through CI, review, and QA until it's merge-ready.Was this review helpful? React with 👍 or 👎 to give feedback.
Was this automated review useful? React with 👍 or 👎 to this review to help us measure review quality.
Workflow run: https://github.com/OpenHands/extensions/actions/runs/26307372305
…f hardcoded string Co-authored-by: openhands <openhands@all-hands.dev>
Three issues flagged by the test suite: 1. Add YAML frontmatter to SKILL.md Skill.load() requires a 'description' field in the frontmatter block at the top of SKILL.md. Added 'name' and 'description' matching the pattern of all other skills in the repo. 2. Convert .claude-plugin and .codex-plugin to symlinks The test suite enforces that these must be symlinks pointing to .plugin, not regular files. Replaced both plain files with proper symlinks (ln -s .plugin .claude-plugin / .codex-plugin). 3. Add skill to openhands-extensions.json marketplace Every skill directory must be listed in at least one marketplace JSON file. Added the github-repo-monitor entry after github-pr-review. Co-authored-by: openhands <openhands@all-hands.dev>
|
@openhands-ai[bot] it looks like you haven't created an OpenHands account yet. Please sign up at OpenHands Cloud and try again. |
Auto-generated by scripts/sync_extensions.py catalog Co-authored-by: openhands <openhands@all-hands.dev>
|
@openhands-ai[bot] it looks like you haven't created an OpenHands account yet. Please sign up at OpenHands Cloud and try again. |
Both github-repo-monitor and slack-channel-monitor were calling _get_agent_dict() which read the user's LLM config from /api/settings but discarded the configured agent type, hardcoding kind='Agent' instead. 'Agent' is the abstract base class — it only exposes think and finish. 'CodeActAgent' is the concrete implementation with bash, file_editor, browser, and the full action tool suite. Any automation that needs to actually *do* things (run tests, call APIs, edit files) was silently getting a neutered agent. Fix: read agent_settings.agent from /api/settings and use it as the kind, defaulting to 'CodeActAgent' if not set. Co-authored-by: openhands <openhands@all-hands.dev>
Two suggestions from all-hands-bot code review:
1. pathlib for state file path (PRRT_kwDOQcEru86ENp2h)
Replace os.path.dirname/abspath/join/makedirs chain with
pathlib.Path operations. Clearer intent and more Pythonic.
2. Differentiated acknowledgement messages (PRRT_kwDOQcEru86ENp2l)
Track whether a conversation was freshly created or resumed from
a closed state. New conversations get 'OpenHands is on it!';
resumed conversations get 'Resuming work on this {item_type}.'
so users are not confused seeing a 'started' message for work
that was already in progress.
Co-authored-by: openhands <openhands@all-hands.dev>
all-hands-bot
left a comment
There was a problem hiding this comment.
🟡 Acceptable - Core implementation is solid but has testing gaps.
The github-repo-monitor skill follows good patterns from slack-channel-monitor, uses proper state management, and correctly handles the trigger phrase configuration. Previous review feedback (pathlib usage, differentiated acknowledgements, marketplace registration) has been addressed.
Key concerns: lacks automated tests and end-to-end verification evidence (see inline comments).
Was this automated review useful? React with 👍 or 👎 to this review to help us measure review quality.
Workflow run: https://github.com/OpenHands/extensions/actions/runs/26309248262
Three improvements from all-hands-bot review (PRRT_kwDOQcEru86EOJad,
PRRT_kwDOQcEru86EOJaj):
1. Refactor _process_trigger_comment (PRRT_kwDOQcEru86EOJad)
Extract two focused helpers:
- _ensure_conversation() handles all state transitions: re-opens a
closed conversation if reachable, falls back to creating a new one,
returns (conv_id, resumed) so the caller knows which path was taken.
- _post_acknowledgement() owns the GitHub comment wording.
Also extracted _is_bot_comment() and _has_trigger() from the inline
poll-loop logic so they are independently testable.
Guarded the entry point with if __name__ == '__main__' so the module
can be imported by tests without executing main().
2. Fix load_state for corrupted JSON
Wraps json.load() in try/except (json.JSONDecodeError, OSError) and
falls back to the default state rather than crashing the run.
3. Add 29-test unit suite (PRRT_kwDOQcEru86EOJaj)
tests/test_main.py covers:
- load_state: missing file, valid JSON, corrupted JSON, empty file
- save_state/load_state round-trip
- _is_bot_comment: [bot] suffix, Bot type, human, null/missing user
- _has_trigger: exact match, case-insensitive, absent, empty/None body
- processed-comment-id persistence across save/load
- _ensure_conversation state transitions: new, reopen, fallback-to-new,
unknown status, create failure
- _post_acknowledgement: new vs resumed wording, trigger phrase footer
Co-authored-by: openhands <openhands@all-hands.dev>
all-hands-bot
left a comment
There was a problem hiding this comment.
🟡 Acceptable - Solid implementation following established patterns.
The github-repo-monitor skill is well-structured, mirrors slack-channel-monitor conventions, and includes comprehensive test coverage (29 tests, all passing). Previous review feedback has been fully addressed.
One minor optimization opportunity noted inline, but it's a pragmatic trade-off that won't impact typical use cases.
[RISK ASSESSMENT]
- [Overall PR]
⚠️ Risk Assessment: 🟢 LOW
This is a new skill addition that doesn't modify existing functionality. It has comprehensive unit tests, follows established automation patterns, proper error handling, and secure secret management. The implementation is straightforward and the blast radius is limited to users who explicitly enable this automation.
VERDICT:
✅ Worth merging: Core logic is sound, well-tested, and ready for production use.
KEY INSIGHT:
This PR demonstrates good engineering judgment by reusing proven patterns from slack-channel-monitor while adapting them appropriately for GitHub's API model.
Was this automated review useful? React with 👍 or 👎 to this review to help us measure review quality.
Workflow run: https://github.com/OpenHands/extensions/actions/runs/26309956229
| f"/repos/{repo}/issues/{issue_number}/comments", | ||
| {"per_page": 100}, | ||
| ) | ||
| recent_comments = all_comments[-CONTEXT_COMMENT_LIMIT:] |
There was a problem hiding this comment.
🟡 Suggestion: This fetches ALL comments via pagination then takes the last 10. For issues with 1000+ comments, this consumes unnecessary API quota and bandwidth.
However, this is a pragmatic trade-off:
- Simpler logic
- Most issues have < 100 comments
- Only happens once per trigger (not on every poll)
- GitHub API doesn't have native "last N comments" support
Consider adding a comment documenting this behavior and the trade-off. No action required unless targeting repos with extremely high-volume issues.
all-hands-bot
left a comment
There was a problem hiding this comment.
🟢 Good taste - Clean implementation following proven patterns.
The github-repo-monitor skill is well-structured and mirrors the slack-channel-monitor conventions effectively. Previous review feedback has been fully addressed: marketplace registration ✓, pathlib usage ✓, differentiated acknowledgements ✓, comprehensive test coverage (29 tests) ✓, bot detection ✓, trigger phrase handling ✓.
The unresolved pagination comment (line 322) represents a pragmatic trade-off that won't impact typical use cases.
[RISK ASSESSMENT]
- [Overall PR]
⚠️ Risk Assessment: 🟢 LOW
This is a new skill addition with no impact on existing functionality. The implementation follows established patterns, includes comprehensive unit test coverage, and has been through multiple review cycles. The only runtime risk is to users who opt-in by creating this automation.
VERDICT:
✅ Worth merging: Solid implementation with good test coverage.
KEY INSIGHT:
The skill demonstrates good architectural taste by extracting focused helper functions (_ensure_conversation, _post_acknowledgement, _is_bot_comment, _has_trigger) that keep complexity low and make the code maintainable.
Was this automated review useful? React with 👍 or 👎 to this review to help us measure review quality.
Workflow run: https://github.com/OpenHands/extensions/actions/runs/26310116466
…gent
dict.get(key, default) only uses the default when the key is ABSENT.
/api/settings returns agent_settings.agent=null for most users, so
.get('agent', 'CodeActAgent') returns None — producing {kind: null}
in the conversation creation payload and a 500 from the server.
Fixed in both skills with: .get('agent') or 'CodeActAgent'
Added regression tests: TestGetAgentDict (3 cases).
Co-authored-by: openhands <openhands@all-hands.dev>
…think/finish limitation
Two related bugs fixed in both github-repo-monitor and slack-channel-monitor:
1. Wrong default agent kind
'CodeActAgent' is a full-app class not registered in the SDK agent
registry. The only valid SDK kinds are 'Agent' and 'ACPAgent'.
Change fallback: 'CodeActAgent' -> 'Agent'.
2. No tools in conversation agent dict
The SDK's Agent has ONLY FinishTool and ThinkTool by default (see
openhands/sdk/tool/builtins/__init__.py: BUILT_IN_TOOLS = [FinishTool,
ThinkTool]). Bash and file editing come from openhands-tools and must
be explicitly listed in the 'tools' field. Without this, conversations
report 'I only have think and finish tools'.
Add: tools=[{name:TerminalTool},{name:FileEditorTool}] to agent dict.
Updated tests: renamed fallback tests to expect 'Agent' (not
'CodeActAgent'), added test_tools_always_included to guard against
regressions on the tools omission.
Co-authored-by: openhands <openhands@all-hands.dev>
… 'file_editor')
The POST /api/conversations OpenAPI spec has two conflicting naming
conventions for tools:
- The Tool schema examples use class names: 'TerminalTool', 'FileEditorTool'
- The request worked example uses registered names: 'terminal', 'file_editor'
We used the class names and got the same '500 / Object of type ValueError
is not JSON serializable' crash, which is the server's error-handler bug
(it tries json.dumps(caught_ValueError_object) instead of str(e)) making
the real cause invisible.
Switch to the worked-example names ('terminal', 'file_editor') which
match what the running server actually accepts.
Co-authored-by: openhands <openhands@all-hands.dev>
Root cause: /api/settings returns the full-app agent registry name (e.g. 'CodeActAgent', 'BrowsingAgent') in agent_settings.agent. The automation SDK is an entirely separate runtime that only accepts ['ACPAgent', 'Agent'] as valid kinds. The previous 'or "Agent"' fallback only guarded against null/missing values — a truthy 'CodeActAgent' sailed straight through, producing: 'Unknown kind CodeActAgent for AgentBase; Expected one of: [...]' Fix: remove the settings agent name lookup entirely. Hardcode 'Agent'. Update test_explicit_agent_name_is_used → test_full_app_agent_name_not_forwarded to assert that all app-registry names (CodeActAgent, BrowsingAgent, …) are rejected rather than forwarded. Co-authored-by: openhands <openhands@all-hands.dev>
|
@OpenHands read this PR; clone yourself agent-sdk and automation repos from the same org to investigate the code as you may need. Does this skill or anything in the code enforce that the automation set up by this skill ONLY responds to particular logins? e.g. if “enyst” sets it up on their local machine using this skill, is the agent told to only do it for the github_token’s login, or is the code enforcing any of that somehow? In other words, if “tofarr” has the github monitor for “yeehaw!” on their machine, can “enyst” trigger a conversation by merely commenting with “yeehaw!” on the monitored repo? Do not guess. Investigate. Respond directly on github here. Note that this PR is closed, you must not push code changes, although you can make changes locally if you need to test. |
|
I'm on it! enyst can track my progress at all-hands.dev |
|
Investigated the PR diff, the merged Short answer: no, this skill/code does not enforce "only respond to the GitHub login that owns What I found:
So for the concrete example: yes - assuming This PR comment was created by an AI agent (OpenHands) on behalf of the requester. |
|
Follow-up recommendation: put the login restriction in the deterministic monitor script, not in the prompt, SDK, or automation service. Best shape:
I would not put this solely in If this later becomes an event/webhook-based automation, an extra This PR comment was created by an AI agent (OpenHands) on behalf of the requester. |
|
I created a new branch from Summary: the monitor now defaults to allowing only the authenticated This PR comment was created by an AI agent (OpenHands) on behalf of the requester. |
Summary
Adds a new
github-repo-monitorskill that creates a cron automation to monitor a GitHub repository for issue and PR comments.This skill mirrors the structure and conventions of the existing
slack-channel-monitorskill, adapted for the GitHub API.What the skill does
When invoked, the skill guides the agent through a 9-step setup workflow:
GITHUB_TOKENis set in OpenHands Secretsowner/repoformat)@OpenHands)issue_comment,pr_review_comment, or both)main.pyRuntime behaviour (per poll)
Each cron run executes
GET /repos/OpenHands/extensions/issues/comments?since={last_poll}(and/or PR review comments). For each comment containing the trigger phrase:Files added
Screenshots
Create the automation using a conversation:

In my github repository I tag the agent:

And I get a conversation:

And a result posted back to github:

Design notes
owner/reposlack-channel-monitor; local- Cron-based polling — consistent withslack-channel-monitor; local- **Croversation still exists it is re-opened; if deleted a fresh one is createduser.type == "Bot"or logins ending in[bot]This PR was created by an AI agent (OpenHands) on behalf of the repository maintainer.