cat-github-watcher
PR monitoring tool for GitHub Copilot’s automated implementation phases
* This document is largely AI-generated. It was generated by sending issues to an agent.
Status
- Currently dogfooding.
- Major bugs have been addressed.
- Frequent breaking changes are expected.
- Notes
- Initially, we attempted implementation with GitHub Actions, but it proved unsuitable for PR monitoring, so we transitioned to a Python version.
- The Python version monitors user-owned repositories for authenticated GitHub users, executing notifications and actions based on PR phases.
Quick Links
| Item | Link | |——|——–| | 📊 GitHub Repository | cat2151/cat-github-watcher |
Overview
This Python tool monitors the phases of GitHub Copilot’s automated implementation PRs, executing appropriate notifications and actions at the right time. It targets user-owned repositories for authenticated GitHub users, leveraging the GraphQL API for efficient PR monitoring.
Features
- Automatic Monitoring of All Repositories: Automatically monitors PRs in user-owned repositories for authenticated GitHub users.
- Leveraging GraphQL API: Achieves fast monitoring through efficient data retrieval.
- Phase Detection: Automatically determines the PR state (phase1: Draft, phase2: Addressing review comments, phase3: Awaiting review, LLM working: Coding agent in progress).
- Dry-run Mode: By default, it only monitors and does not perform actual actions (posting comments, making PRs Ready, sending notifications). It can be safely operated by explicitly enabling actions.
- Automated Comment Posting: Automatically posts appropriate comments based on the phase (requires activation in the config file).
- Multi-Agent Support: Automatically mentions
@codex[agent]for PR creators fromopenai-code-agentor other*-codex-coding-agenttypes,@claude[agent]foranthropic-code-agentor other*-claude-coding-agenttypes, and falls back to@copilototherwise (can be overridden by[coding_agent].agent_name; defaults to @copilot if not set). - Automated Draft PR Ready-up: Automatically changes Draft PRs to Ready status for addressing review comments in phase2 (requires activation in the config file).
- Mobile Notifications: Uses ntfy.sh to send mobile notifications when phase3 (awaiting review) is detected (requires activation in the config file).
- Notifies when individual PRs enter phase3.
- Also notifies when all PRs enter phase3 (message can be configured in TOML).
- Issue List Display: If all PRs are “LLM working,” displays the top N issues (default: 10, changeable via
issue_display_limit) for repositories without open PRs. - Self-Update: Checks for updates on startup and automatically pulls and restarts if updates are found. Furthermore, if
enable_auto_update = trueis set, update checks continue every minute during the monitoring loop (disabled by default). - Local Repository Pull Detection: By default, displays the pullable status of your repositories in the parent directory (Dry-run). Setting
auto_git_pull = truewill automatically pull them (refer to cat-repo-auditor for reference implementation). - Power Saving Mode: When no state changes occur, the monitoring interval is automatically extended to reduce API usage (
no_change_timeoutandreduced_frequency_intervalcan be configured). - Verbose Mode: Displays detailed configuration information on startup and during execution to help detect configuration errors (enabled via
verbose).
Architecture
This tool is a modularized Python application adhering to the Single Responsibility Principle (SRP).
Directory Structure
cat-github-watcher/
├── cat-github-watcher.py # Entry point
├── src/
│ └── gh_pr_phase_monitor/
│ ├── colors.py # ANSI color codes and coloring
│ ├── config.py # Configuration loading and parsing
│ ├── github_client.py # GitHub API integration
│ ├── phase_detector.py # PR phase detection logic
│ ├── comment_manager.py # Comment posting and verification
│ ├── pr_actions.py # PR actions (Ready-up, browser launch)
│ └── main.py # Main execution loop
└── tests/ # Test files
Phase Detection Logic
The tool detects the following four phases:
- phase1 (Draft): PR is in Draft state and has review requests.
- phase2 (Addressing review comments):
copilot-pull-request-reviewerhas posted review comments, and corrections are needed. - phase3 (Awaiting review):
copilot-swe-agenthas completed corrections and is awaiting human review. - LLM working (Coding agent in progress): None of the above apply (e.g., Copilot is implementing).
Usage
Prerequisites
- Python 3.11 or later installed.
- GitHub CLI (
gh) installed and authenticated.gh auth login
Setup
- Clone this repository:
git clone https://github.com/cat2151/cat-github-watcher.git cd cat-github-watcher - Create a configuration file (optional):
cp config.toml.example config.toml - Edit
config.tomlto configure monitoring interval, execution mode, ntfy.sh notifications, Copilot auto-assignment, and auto-merge (optional):# Check interval (e.g., "30s", "1m", "5m", "1h", "1d") interval = "1m" # Maximum number of issues to display from repositories with no PRs # Default is 10, but can be changed to any positive number (e.g., 5, 15, 20) issue_display_limit = 10 # Timeout duration for no state change # If the state of all PRs (phase of each PR) does not change for this duration, # the monitoring interval will switch to power-saving mode (reduced_frequency_interval below). # Set to an empty string "" to disable. # Supported formats: "30s", "1m", "5m", "30m", "1h", "1d" # Default: "30m" (30 minutes - prioritizing stability) no_change_timeout = "30m" # Override the mention for the coding agent used in post comments (defaults to @copilot) [coding_agent] agent_name = "@codex[agent]" # Monitoring interval in power-saving mode # If no state change is detected during the no_change_timeout period, # the monitoring interval switches to this interval to reduce API usage. # It returns to the normal monitoring interval when a change is detected. # Supported formats: "30s", "1m", "5m", "30m", "1h", "1d" # Default: "1h" (1 hour) reduced_frequency_interval = "1h" # Verbose mode - displays detailed configuration information # When enabled, all settings are displayed at startup, and per-repository settings are displayed during execution. # This helps detect configuration errors. # Default: false verbose = false # Color scheme for terminal output # Can be set to "monokai" (default) or "classic" color_scheme = "monokai" # Individual color codes can be overridden in the [colors] section (#RRGGBB format / ANSI allowed) # If omitted, the palette of the above color_scheme will be used. [colors] # phase1 = "#E6DB74" # phase2 = "#66D9EF" # phase3 = "#A6E22E" # llm = "#F92672" # url = "#79C1FF" # url = "\u001b[94m" # Example ANSI in TOML (ESC=[94m) # Toggle display of PR author # Controls whether to display "Author: <login>" in CLI output # Default: false display_pr_author = false # Local repository auto-pull setting (global flag specifically for local repo watcher) # Default (false): Detects and displays pullable repositories only (Dry-run) # If set to true: Automatically git pull pullable repositories # Scans repositories in the parent directory every 5 minutes (executes git fetch) # Note: This behavior is independent of PR actions, so this flag is specified at the top level only. auto_git_pull = false # Base directory for local repository scanning (defaults to parent of current directory if omitted) # local_repo_watcher_base_dir = ".." # Execution control flags for PR actions - can only be specified within [[rulesets]] sections # Global flags are no longer supported (except for auto_git_pull) # To apply settings to all repositories, use 'repositories = ["all"]' # Example ruleset configuration: # [[rulesets]] # name = "Default for all repositories - dry-run mode" # repositories = ["all"] # "all" matches all repositories # enable_execution_phase1_to_phase2 = false # Set to true to make draft PRs ready # enable_execution_phase2_to_phase3 = false # Set to true to post phase2 comments # enable_execution_phase3_send_ntfy = false # Set to true to send ntfy notifications # enable_execution_phase3_to_merge = false # Set to true to merge phase3 PRs # [[rulesets]] # name = "Simple: Auto-assign good first issues to Copilot" # repositories = ["my-repo"] # assign_good_first_old = true # This is enough! The [assign_to_copilot] section is not needed. # # Default behavior: open issue in browser for manual assignment # ntfy.sh notification settings (optional) # Notifications include a clickable action button to open the PR [ntfy] enabled = false # Set to true to enable notifications topic = "<Enter your ntfy.sh topic name here>" # Use an unguessable string as anyone can read/write to it message = "PR is ready for review: {url}" # Message template priority = 4 # Notification priority (1=lowest, 3=default, 4=high, 5=highest) all_phase3_message = "All PRs are now in phase3 (ready for review)" # Message when all PRs are in phase3 # Phase3 auto-merge settings (optional) # Automatically merges the PR once it reaches phase3 (awaiting review). # Before merging, the comment defined below will be posted to the PR. # After successful merge, the feature branch will be automatically deleted. # IMPORTANT: For safety, this feature is disabled by default. # You must explicitly enable it by setting enable_execution_phase3_to_merge = true in rulesets per repository. # IMPORTANT: If auto-merge is enabled, the comment field must be explicitly set. [phase3_merge] comment = "The agent has determined that review comments have been addressed. User review is skipped under user's responsibility. Merging PR." # Comment to post before merging (required when auto-merge is enabled) automated = false # Set to true for automated browser operation to click merge button wait_seconds = 10 # Wait time in seconds after browser launch, before clicking button debug_dir = "debug_screenshots" # Destination for debug info if image recognition fails (default: "debug_screenshots") notification_enabled = true # Display a small notification window at specified coordinates during automated button operation notification_message = "Opening browser and searching for Merge button..." # Message for the notification window notification_width = 400 notification_height = 150 notification_position_x = 100 notification_position_y = 100 maximize_on_first_fail = true # Maximize window and re-search if button not found on first attempt # Auto-assign issues to Copilot (completely optional! This entire section is optional) # # Simple usage: Just set assign_good_first_old = true in rulesets (see example above). # Define this section ONLY if you want to customize the default behavior. # # Assignment behavior is controlled by ruleset flags: # - assign_ci_failure_old: Assigns the oldest "ci-failure" issue (by issue number, default: false) # - assign_deploy_pages_failure_old: Assigns the oldest "deploy-pages-failure" issue (by issue number, default: false) # - assign_good_first_old: Assigns the oldest "good first issue" (by issue number, default: false) # - assign_old: Assigns the oldest issue (by issue number, any label, default: false) # Priority: ci-failure > deploy-pages-failure > good first issue > old issue # # Default behavior (if this section is not defined): # - Automatically clicks buttons via browser automation. # - Uses image recognition with PyAutoGUI. # - Optional OCR fallback if image recognition fails. # - wait_seconds = 2 # # Required: PyAutoGUI installation (pip install pyautogui pillow) # Optional: pytesseract installation required for OCR fallback # # IMPORTANT: For safety, this feature is disabled by default. # You must explicitly enable it by setting assign_ci_failure_old / assign_deploy_pages_failure_old / # assign_good_first_old / assign_old in rulesets per repository. [assign_to_copilot] wait_seconds = 2 # Wait time in seconds after browser launch, before clicking button debug_dir = "debug_screenshots" # Destination for debug info if image recognition fails (default: "debug_screenshots") confidence = 0.8 # Image matching confidence 0.0-1.0 (default: 0.8) enable_ocr_detection = true # Enable OCR fallback (default: true) notification_enabled = true # Display a small notification window at specified coordinates during automated button operation notification_message = "Opening browser and searching for Copilot assignment button..." # Message for the notification window notification_width = 400 notification_height = 150 notification_position_x = 100 notification_position_y = 100 maximize_on_first_fail = true # Maximize window and re-search if button not found on first attempt # enable_html_detection = false # HTML detection fallback (experimental, default: false) -
Prepare Button Screenshots (Only if using automation):
If using automation features (
automated = trueor enablingassign_to_copilot/phase3_merge), PyAutoGUI requires screenshots of the buttons to click.Required Screenshots:
For automated issue assignment (
assign_to_copilotfeature):assign_to_copilot.png- Screenshot of the “Assign to Copilot” buttonassign.png- Screenshot of the “Assign” button
For automated PR merging (
phase3_mergefeature withautomated = true):merge_pull_request.png- Screenshot of the “Merge pull request” buttonconfirm_merge.png- Screenshot of the “Confirm merge” buttondelete_branch.png- Screenshot of the “Delete branch” button (optional)
How to take screenshots:
a. Open a GitHub issue or PR in your browser. b. Find the button you want to automate. c. Take a screenshot of only the button (not the entire screen). d. Save it as a PNG file in the
screenshotsdirectory. e. Use the exact filenames listed above.Tips:
- Screenshots should include only the button, with a small margin.
- Use your OS’s screenshot tool (Windows: Snipping Tool, Mac: Cmd+Shift+4).
- Ensure the button is clearly visible and not obscured.
- If the button’s appearance changes (e.g., due to theme changes), you’ll need to update the screenshots.
- Adjust the
confidencesetting for image recognition if needed (due to DPI scaling or themes).
Automated Debug Information Saving:
- If image recognition fails, debug information is automatically saved.
- Save location:
debug_screenshots/directory (default). - Contents saved:
- Screenshot (entire screen at time of failure):
{button_name}_fail_{timestamp}.png - Candidate region screenshots (if found):
{button_name}_candidate_{timestamp}_{number}.png - Failure info JSON:
{button_name}_fail_{timestamp}.json- Includes button name, timestamp, confidence threshold, screenshot path, template image path.
- Candidate region information (coordinates, size, confidence).
- Screenshot (entire screen at time of failure):
- For debugging, up to 3 candidate regions are detected with lower confidence (0.7, 0.6, 0.5).
- The debug directory can be changed via the
debug_diroption (withinassign_to_copilotorphase3_mergesections).
Fallback Mechanisms (if image recognition fails):
- OCR Detection (enabled by default): Uses pytesseract to detect button text.
- Directly detects text like “Assign to Copilot” on the screen.
- Robust against sub-pixel rendering differences.
- Required: tesseract-ocr installation (system-level).
- Disable:
enable_ocr_detection = false.
Important Requirements:
- Your default browser must already be logged into GitHub.
- Automation uses existing browser sessions (it does not perform new authentication).
- Ensure the correct GitHub window/tab is focused and visible on screen when buttons are clicked.
- If multiple GitHub pages are open, the first button found will be clicked.
Create the screenshots directory:
mkdir screenshots -
Install PyAutoGUI (only if using automation):
For basic image recognition only:
pip install pyautogui pillow pygetwindowIncluding OCR fallback (recommended):
pip install -r requirements-automation.txtIf using OCR, install tesseract-ocr system-wide:
- Windows:
choco install tesseract - macOS:
brew install tesseract - Linux:
apt-get install tesseract-ocr
- Windows:
Execution
Start the tool to begin monitoring:
python3 cat-github-watcher.py [config.toml]
Or, run directly as a Python module:
python3 -m src.gh_pr_phase_monitor.main [config.toml]
Workflow
- Startup: The tool starts monitoring user-owned repositories for authenticated GitHub users.
- PR Detection: Automatically detects repositories with open PRs.
- Phase Determination: Determines the phase of each PR (phase1/2/3, LLM working).
- Action Execution:
- phase1: Dry-run by default (if
enable_execution_phase1_to_phase2 = truein rulesets, Draft PRs are changed to Ready status). - phase2: Dry-run by default (if
enable_execution_phase2_to_phase3 = truein rulesets, a comment is posted asking Copilot to apply changes). - phase3: Opens the PR page in the browser.
- If
enable_execution_phase3_send_ntfy = truein rulesets, an ntfy.sh notification is also sent. - If
enable_execution_phase3_to_merge = truein rulesets, the PR is automatically merged (using global[phase3_merge]settings).
- If
- LLM working: Waits (if all PRs are in this state, issues from repositories without open PRs are displayed).
- phase1: Dry-run by default (if
- Automated Issue Assignment: If all PRs are “LLM working” and there are repositories without open PRs:
- If
assign_ci_failure_old = truein rulesets, the oldest “ci-failure” issue is automatically assigned (by issue number). - If
assign_deploy_pages_failure_old = truein rulesets, the oldest “deploy-pages-failure” issue is automatically assigned (by issue number). - If
assign_good_first_old = truein rulesets, the oldest “good first issue” is automatically assigned (by issue number). - If
assign_old = truein rulesets, the oldest issue is automatically assigned (by issue number, any label). - Priority: ci-failure > deploy-pages-failure > good first issue > old issue.
- Default behavior: Automatically clicks buttons via PyAutoGUI (the
[assign_to_copilot]section is not required). - Required: PyAutoGUI installation and preparation of button screenshots are necessary.
- If
- Repeat: Continues monitoring at the configured interval.
- If no state change occurs for the duration set by
no_change_timeout, it automatically switches to power-saving mode (reduced_frequency_interval) to reduce API usage. - It reverts to the normal monitoring interval when a change is detected.
- If no state change occurs for the duration set by
Dry-run Mode
By default, the tool operates in Dry-run mode and does not perform actual actions. This allows you to safely verify its operation.
- Phase1 (Draft → Ready-up): Displays
[DRY-RUN] Would mark PR as ready for reviewbut does nothing. - Phase2 (Comment posting): Displays
[DRY-RUN] Would post comment for phase2but does nothing. - Phase3 (ntfy notification): Displays
[DRY-RUN] Would send ntfy notificationbut does nothing. - Phase3 (Merge): Displays
[DRY-RUN] Would merge PRbut does nothing. - Local Repository: Displays
[PULLABLE]for pullable repositories and[DRY-RUN] Would pull <repo>but does nothing.
To enable actual actions, set the following flags to true in the [[rulesets]] section of config.toml:
[[rulesets]]
name = "Enable automation for specific repository"
repositories = ["test-repo"] # Or ["all"] for all repositories
enable_execution_phase1_to_phase2 = true # Make Draft PR Ready
enable_execution_phase2_to_phase3 = true # Post Phase2 comment
enable_execution_phase3_send_ntfy = true # Send ntfy notification
enable_execution_phase3_to_merge = true # Merge Phase3 PR
assign_ci_failure_old = true # Auto-assign ci-failure issue
assign_deploy_pages_failure_old = true # Auto-assign deploy-pages-failure issue
assign_good_first_old = true # Auto-assign good first issue
To enable automatic pulling of local repositories, set it at the top level (outside of rulesets):
auto_git_pull = true # Automatically git pull pullable local repositories
Stopping
You can stop monitoring with Ctrl+C.
Important Notes
- GitHub CLI (
gh) must be installed and authenticated. - This tool assumes integration with GitHub Copilot (specifically
copilot-pull-request-reviewerandcopilot-swe-agent). - Only user-owned repositories of authenticated users are monitored. Organization repositories are not included to keep the tool simple and focused (YAGNI principle).
- Be mindful of API rate limits as it uses the GraphQL API.
- If using ntfy.sh notifications, configure a topic on ntfy.sh beforehand.
Testing
The project includes a test suite using pytest:
pytest tests/
License
MIT License - See the LICENSE file for details.
* Note: The English README.md is automatically generated by GitHub Actions using Gemini’s translation based on README.ja.md.
Big Brother is watching your repositories. Now it’s the cat. 🐱