agentleFS
OpenHandsOpenHands/extensions/skills/github-repo-monitor/SKILL.md

github-repo-monitor

Skill167 starsChanged 16 days agoMITView on GitHub

This skill should be used when the user asks to "monitor a GitHub repository", "watch GitHub for issues or PRs", "respond to @OpenHands mentions on GitHub", "set up an OpenHands GitHub integration", "trigger OpenHands from a GitHub comment", or "poll a GitHub repo for a trigger phrase". Guides the user through creating a cron automation that polls a single repository and starts an OpenHands conversation whenever a configurable trigger phrase is detected in an issue or PR comment.

  • Reads credentials
  • Sends data out

We split this skill into the rules it gives an agent

24 rules. About 0 are common, 5 are distinctive, and 19 are very specific to this project.

  • ≈0

    Common · estimate

    Many files like this have a rule of this kind.

  • ≈5

    Distinctive · estimate

    This author's own choices. Worth a look.

  • 19

    Very specific

    Tied to their project's names, files or tools. Probably skip.

  • Common
  • Distinctive
  • Very specific
  • could be automatic: a setting could enforce it
  1. Very specific:

    Create a cron automation that polls a single GitHub repository on a configurable schedule (default: every minute).

    line 17
  2. Very specific:

    When a comment on an issue or PR contains the trigger phrase (default: @OpenHands) it:

    line 21
  3. Very specific:

    When a conversation goes idle/finished/error the agent's final response is posted back as a GitHub comment.

    line 32
  4. Distinctive:

    Verify that the following secret is set in OpenHands Settings → Secrets before proceeding:

    line 48
  5. Distinctive:

    If the token is missing, inform the user and stop — the automation cannot function without GitHub credentials.

    line 64
  6. Distinctive:

    Follow these steps in order.

    line 77
  7. Very specific:

    If the secret is absent: tell the user *"GITHUB_PERSONAL_ACCESS_TOKEN is not set.

    line 83
  8. Very specific:

    If the API returns a non-200 or {"message": "Bad credentials"}: tell the user the token is invalid and ask them to update it.

    line 88
  9. Very specific:

    Ask the user: *"Which GitHub repository should be monitored?

    line 93
  10. Very specific:

    If message: Not Found or message: Bad credentials → inform the user and ask them to check the repo name and token.

    line 113
  11. Very specific:

    If the repo is private and permissions.push is false → inform the user the token does not have write access and comments will fail.

    line 115
  12. Very specific:

    If the check passes, record REPO = "{owner}/{repo}".

    line 117
  13. Very specific:

    Ask the user: *"What trigger phrase should OpenHands respond to?

    line 121
  14. Very specific:

    Ask the user: *"Which GitHub users may trigger this automation?

    line 130
  15. Very specific:

    Default to token-owner-only unless the user explicitly chooses a broader allowlist.

    line 143
  16. Distinctive:

    Ask the user: *"Which event types should be monitored?

    line 148
  17. Very specific:

    Ask the user: *"How often should the automation poll GitHub?

    line 165
  18. Very specific:

    Use a cron expression for a different interval, e.g.: */5 * * * * = every 5 minutes, 0 * * * * = every hour)"*

    line 167
  19. Very specific:

    Default: * * * * * (every minute).

    line 171
  20. Very specific:

    Read scripts/main.py from this skill's directory.

    line 177
  21. Distinctive:

    Run these commands from this skill's directory and write the customised script to a temporary build directory:

    line 188
  22. Very specific:

    Use the Automation backend url_from_agent as OPENHANDS_HOST

    line 207
  23. Very specific:

    If no Automation backend is listed in <RUNTIME_SERVICES>, stop and tell the user to start the full automation stack.

    line 210
  24. Very specific:

    If status ∈ {idle, finished, error, stuck} and enough time has passed since creation (debounce), fetches the agent's final response and …

    line 289

≈ “Common” is an estimate: at least 1 in 5 of the 308,232 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.

Keep what fits and drop the rest. Your version, ready for your agents.
Files
SKILL.md

GitHub Repository Monitor

Create a cron automation that polls a single GitHub repository on a configurable schedule (default: every minute). Windows PowerShell equivalents for the setup, packaging, upload, and API-check shell snippets are in references/windows.md.

When a comment on an issue or PR contains the trigger phrase (default: @OpenHands) it:

  1. Posts a GitHub comment acknowledging the request with a conversation link.
  2. Creates an OpenHands conversation pre-loaded with the issue/PR title, body, labels, and recent comment history for full context.
  3. Posts a summary GitHub comment when the conversation finishes.

On every subsequent run:

  • New trigger comments on an already-tracked issue/PR are forwarded to the running conversation (or re-open a previously closed one).
  • When a conversation goes idle/finished/error the agent's final response is posted back as a GitHub comment.

Local mode only. This automation targets the local OpenHands setup (dev:automation stack). A cloud/webhook variant is out of scope here.


The script imports shared GitHub transport from scripts/github_client.py, installed with this skill. Include it beside main.py when packaging manually, as shown below.

Prerequisites

Required secret

Verify that the following secret is set in OpenHands Settings → Secrets before proceeding:

Secret nameToken typeMinimum permissions
GITHUB_PERSONAL_ACCESS_TOKENClassic PATrepo (private repos) or public_repo (public repos)
GITHUB_PERSONAL_ACCESS_TOKENFine-grained PATIssues: Read and Write

Check with:

curl -s https://api.github.com/user \
  -H "Authorization: Bearer $GITHUB_PERSONAL_ACCESS_TOKEN" \
  -H "Accept: application/vnd.github+json" \
  | python3 -c "import json,sys; d=json.load(sys.stdin); print(d.get('login') or d.get('message'))"

If the token is missing, inform the user and stop — the automation cannot function without GitHub credentials.

Optional secret

Secret nameDefaultPurpose
OPENHANDS_URLhttp://localhost:8000Base URL used to build conversation links in GitHub comments

Setup Workflow

Follow these steps in order.

Step 1 - Verify GITHUB_PERSONAL_ACCESS_TOKEN

Fetch the secret and run the curl check above.

  • If the secret is absent: tell the user "GITHUB_PERSONAL_ACCESS_TOKEN is not set. Please add it in OpenHands Settings → Secrets (classic PAT with repo or public_repo scope, or a fine-grained PAT with Issues: Read and Write)." Then stop.
  • If the API returns a non-200 or {"message": "Bad credentials"}: tell the user the token is invalid and ask them to update it.

Step 2 - Collect repository

Ask the user: "Which GitHub repository should be monitored? (Format: owner/repo, e.g. microsoft/vscode)"

Validate access and write permissions:

curl -s "https://api.github.com/repos/{owner}/{repo}" \
  -H "Authorization: Bearer $GITHUB_PERSONAL_ACCESS_TOKEN" \
  -H "Accept: application/vnd.github+json" \
  | python3 -c "
import json, sys
d = json.load(sys.stdin)
if 'message' in d:
    print('ERROR:', d['message'])
else:
    perms = d.get('permissions', {})
    print(f\"Accessible. Private: {d.get('private')}. Permissions: {perms}\")
"
  • If message: Not Found or message: Bad credentials → inform the user and ask them to check the repo name and token.
  • If the repo is private and permissions.push is false → inform the user the token does not have write access and comments will fail.
  • If the check passes, record REPO = "{owner}/{repo}".

Step 3 - Collect trigger phrase

Ask the user: "What trigger phrase should OpenHands respond to? (Press Enter to use the default: @OpenHands)"

Accepted values: any non-empty string unlikely to appear by accident.

Record as TRIGGER_PHRASE. Default: "@openhands".

Step 4 - Collect allowed GitHub logins

Ask the user: "Which GitHub users may trigger this automation? Press Enter to allow only the authenticated GITHUB_PERSONAL_ACCESS_TOKEN owner. You may also provide comma-separated GitHub logins, or * to allow any non-bot commenter on the monitored repository."

Map the answer to ALLOWED_GITHUB_LOGINS:

User answerALLOWED_GITHUB_LOGINS value
Empty/default["<TOKEN_OWNER>"]
enyst,tofarr["enyst", "tofarr"]
*["*"]

Default to token-owner-only unless the user explicitly chooses a broader allowlist. Record as ALLOWED_GITHUB_LOGINS.

Step 5 - Collect event types

Ask the user: "Which event types should be monitored? Choose one or more: 1. Issue and PR comments (default) 2. PR inline review comments 3. Both (Press Enter to accept the default: issue and PR comments.)"

Map the choice to the EVENT_TYPES list:

ChoiceEVENT_TYPES value
1 (default)["issue_comment"]
2["pr_review_comment"]
3["issue_comment", "pr_review_comment"]

Step 6 - Collect cron schedule

Ask the user: "How often should the automation poll GitHub? (Press Enter for the default: every minute. Use a cron expression for a different interval, e.g.: */5 * * * * = every 5 minutes, 0 * * * * = every hour)"

Default: * * * * * (every minute).

Record as CRON_SCHEDULE.

Step 7 - Generate the automation script

Read scripts/main.py from this skill's directory. Apply exactly five constant substitutions near the top of the file:

PlaceholderReplace with
REPO = "owner/repo"REPO = "{owner_repo}"
TRIGGER_PHRASE = "@openhands"TRIGGER_PHRASE = "{trigger_phrase_lower}"
EVENT_TYPES = ["issue_comment"]EVENT_TYPES = {event_types_list}
ALLOWED_GITHUB_LOGINS = ["<TOKEN_OWNER>"]ALLOWED_GITHUB_LOGINS = {allowed_logins_list}
DEFAULT_OPENHANDS_URL = "http://localhost:8000"DEFAULT_OPENHANDS_URL = "{url}" (keep default if the user has no preference)

Run these commands from this skill's directory and write the customised script to a temporary build directory:

mkdir -p /tmp/github-monitor-build
cp -L scripts/github_client.py /tmp/github-monitor-build/github_client.py
# (write the customised main.py to /tmp/github-monitor-build/main.py)

Validate syntax before packaging:

python3 -m py_compile /tmp/github-monitor-build/main.py && echo "Syntax OK"

Fix any syntax errors before proceeding.

Step 8 - Package and upload

Determine the Automation backend URL and auth from the <RUNTIME_SERVICES> block in your system context:

  • Use the Automation backend url_from_agent as OPENHANDS_HOST
  • Auth: X-Session-API-Key: $OPENHANDS_AUTOMATION_API_KEY

If no Automation backend is listed in <RUNTIME_SERVICES>, stop and tell the user to start the full automation stack.

tar -czf /tmp/github-monitor.tar.gz -C /tmp/github-monitor-build .

# OPENHANDS_HOST: read from <RUNTIME_SERVICES> Automation backend url_from_agent
OPENHANDS_HOST="<automation-url-from-runtime-services>"

TARBALL_PATH=$(curl -s -X POST \
  "${OPENHANDS_HOST}/api/automation/v1/uploads?name=github-repo-monitor" \
  -H "X-Session-API-Key: $OPENHANDS_AUTOMATION_API_KEY" \
  -H "Content-Type: application/gzip" \
  --data-binary @/tmp/github-monitor.tar.gz \
  | python3 -c "import json,sys; print(json.load(sys.stdin)['tarball_path'])")

echo "Uploaded: $TARBALL_PATH"

Step 9 - Create the automation

curl -s -X POST "${OPENHANDS_HOST}/api/automation/v1" \
  -H "X-Session-API-Key: $OPENHANDS_AUTOMATION_API_KEY" \
  -H "Content-Type: application/json" \
  -d "{
    \"name\": \"GitHub Monitor: {owner}/{repo}\",
    \"trigger\": {\"type\": \"cron\", \"schedule\": \"{cron_schedule}\"},
    \"tarball_path\": \"$TARBALL_PATH\",
    \"entrypoint\": \"python3 main.py\",
    \"timeout\": 55
  }" | python3 -m json.tool

Record the returned id.

Step 10 - Confirm

Tell the user:

✅ GitHub Repository Monitor is running!

  • Automation ID: {id}
  • Repository: {owner}/{repo}
  • Trigger phrase: {phrase}
  • Event types: {event_types}
  • Allowed GitHub logins: {allowed_logins}
  • Polling schedule: {cron_schedule}
  • State file: ~/.openhands/workspaces/automation-state/github_poller_{id}.json

From an allowed GitHub login, post a comment containing {phrase} on any issue or PR in {owner}/{repo} to test it. OpenHands will acknowledge with a comment and a link to the new conversation.


Runtime Behaviour (per poll)

Each cron run executes main.py, which:

  1. Loads state from the JSON file (see references/state-schema.md).
  2. Resolves and validates GITHUB_PERSONAL_ACCESS_TOKEN — aborts immediately if absent or invalid.
  3. Polls for new events since the previous last_poll timestamp:
    • GET /repos/{owner}/{repo}/issues/comments?since=… for issue_comment
    • GET /repos/{owner}/{repo}/pulls/comments?since=… for pr_review_comment
  4. Processes matching comments in chronological order:
    • Skips bot accounts (login ending in [bot]) to avoid feedback loops.
    • Skips already-processed comment IDs.
    • Skips comments from logins outside ALLOWED_GITHUB_LOGINS.
    • Checks body for the trigger phrase (case-insensitive).
    • Extracts the issue/PR number from the comment URL.
  5. For each trigger comment, per issue/PR:
    • Active conversation → forwards the new comment directly.
    • Closed conversation → tries to re-open it; falls back to creating a new conversation if the old one is unreachable.
    • No conversation → fetches full context (title, body, labels, last 10 comments) and creates a new conversation with a detailed prompt.
    • Posts a GitHub comment: "🤖 OpenHands is on it! View progress: {url}"
  6. Checks active conversations for completion:
    • If status ∈ {idle, finished, error, stuck} and enough time has passed since creation (debounce), fetches the agent's final response and posts it as a GitHub comment. Marks the conversation closed.
  7. Saves state and fires the completion callback.

Additional Resources

Reference Files

  • references/state-schema.md - State JSON schema, field definitions, and conversation lifecycle diagram.
  • references/github-api.md - GitHub API endpoint reference, token scopes, rate limits, and common error codes.

Script Template

  • scripts/main.py - The complete automation script. Customise the four constants at the top (REPO, TRIGGER_PHRASE, EVENT_TYPES, DEFAULT_OPENHANDS_URL) before packaging.

Troubleshooting

SymptomLikely causeFix
Bot doesn't respond to commentsGITHUB_PERSONAL_ACCESS_TOKEN missing or wrong scopesVerify token with curl /user; check scopes in Step 1
"Bad credentials" in run logsToken expiredRotate token and update the secret in Settings
404 on repo accessRepo name wrong or token has no accessRe-check owner/repo spelling; add token as collaborator
Comments posted but no conversation createdAgent server URL wrongCheck OPENHANDS_URL secret and AGENT_SERVER_URL env var
Same comment processed twiceprocessed_comment_ids clearedState file was deleted; harmless but duplicate comment may appear
Summary never postedConversation stuck in runningOpen the conversation in the OpenHands UI; agent may need input
No events detected after first runlast_poll in the futureDelete the state file to reset; it will be recreated on next run

Download now (ZIP)

Does it work?

Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.

No reviews yet

People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.

  • It worked
  • Partly
  • It didn't
What did you use it for? e.g. Reviewing pull requests in a Rails app

Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.

Your trail

Files you open will collect here, so you can find them again.

Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.

Now do this for your own.

Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.