# Generating PR Age Charts
Build an interactive CLI tool that visualizes pull request age distribution for a GitHub repository using Copilot's built-in capabilities.
> **Runnable example:** [recipe/pr_visualization.py](recipe/pr_visualization.py)
>
> ```bash
> cd recipe && pip install -r requirements.txt
> # Auto-detect from current git repo
> python pr_visualization.py
>
> # Specify a repo explicitly
> python pr_visualization.py --repo github/copilot-sdk
> ```
## Example scenario
You want to understand how long PRs have been open in a repository. This tool detects the current Git repo or accepts a repo as input, then lets Copilot fetch PR data via the GitHub MCP Server and generate a chart image.
## Prerequisites
```bash
pip install copilot-sdk
```
## Usage
```bash
# Auto-detect from current git repo
python pr_visualization.py
# Specify a repo explicitly
python pr_visualization.py --repo github/copilot-sdk
```
## Full example: pr_visualization.py
```python
#!/usr/bin/env python3
import subprocess
import sys
import os
from copilot import CopilotClient
# ============================================================================
# Git & GitHub Detection
# ============================================================================
def is_git_repo():
try:
subprocess.run(
["git", "rev-parse", "--git-dir"],
check=True,
capture_output=True
)
return True
except (subprocess.CalledProcessError, FileNotFoundError):
return False
def get_github_remote():
try:
result = subprocess.run(
["git", "remote", "get-url", "origin"],
check=True,
capture_output=True,
text=True
)
remote_url = result.stdout.strip()
# Handle SSH: git@github.com:owner/repo.git
import re
ssh_match = re.search(r"git@github\.com:(.+/.+?)(?:\.git)?$", remote_url)
if ssh_match:
return ssh_match.group(1)
# Handle HTTPS: https://github.com/owner/repo.git
https_match = re.search(r"https://github\.com/(.+/.+?)(?:\.git)?$", remote_url)
if https_match:
return https_match.group(1)
return None
except (subprocess.CalledProcessError, FileNotFoundError):
return None
def parse_args():
args = sys.argv[1:]
if "--repo" in args:
idx = args.index("--repo")
if idx + 1 < len(args):
return {"repo": args[idx + 1]}
return {}
def prompt_for_repo():
return input("Enter GitHub repo (owner/repo): ").strip()
# ============================================================================
# Main Application
# ============================================================================
def main():
print("š PR Age Chart Generator\n")
# Determine the repository
args = parse_args()
repo = None
if "repo" in args:
repo = args["repo"]
print(f"š¦ Using specified repo: {repo}")
elif is_git_repo():
detected = get_github_remote()
if detected:
repo = detected
print(f"š¦ Detected GitHub repo: {repo}")
else:
print("ā ļø Git repo found but no GitHub remote detected.")
repo = prompt_for_repo()
else:
print("š Not in a git repository.")
repo = prompt_for_repo()
if not repo or "/" not in repo:
print("ā Invalid repo format. Expected: owner/repo")
sys.exit(1)
owner, repo_name = repo.split("/", 1)
# Create Copilot client - no custom tools needed!
client = CopilotClient(log_level="error")
client.start()
session = client.create_session(
model="gpt-5",
system_message={
"content": f"""
You are analyzing pull requests for the GitHub repository: {owner}/{repo_name}
The current working directory is: {os.getcwd()}
- Use the GitHub MCP Server tools to fetch PR data
- Use your file and code execution tools to generate charts
- Save any generated images to the current working directory
- Be concise in your responses
"""
}
)
# Set up event handling
def handle_event(event):
if event["type"] == "assistant.message":
print(f"\nš¤ {event['data']['content']}\n")
elif event["type"] == "tool.execution_start":
print(f" āļø {event['data']['toolName']}")
session.on(handle_event)
# Initial prompt - let Copilot figure out the details
print("\nš Starting analysis...\n")
session.send(prompt=f"""
Fetch the open pull requests for {owner}/{repo_name} from the last week.
Calculate the age of each PR in days.
Then generate a bar chart image showing the distribution of PR ages
(group them into sensible buckets like <1 day, 1-3 days, etc.).
Save the chart as "pr-age-chart.png" in the current directory.
Finally, summarize the PR health - average age, oldest PR, and how many might be considered stale.
""")
session.wait_for_idle()
# Interactive loop
print("\nš” Ask follow-up questions or type \"exit\" to quit.\n")
print("Examples:")
print(" - \"Expand to the last month\"")
print(" - \"Show me the 5 oldest PRs\"")
print(" - \"Generate a pie chart instead\"")
print(" - \"Group by author instead of age\"")
print()
while True:
user_input = input("You: ").strip()
if user_input.lower() in ["exit", "quit"]:
print("š Goodbye!")
break
if user_input:
session.send(prompt=user_input)
session.wait_for_idle()
session.destroy()
client.stop()
if __name__ == "__main__":
main()
```
## How it works
1. **Repository detection**: Checks `--repo` flag ā git remote ā prompts user
2. **No custom tools**: Relies entirely on Copilot CLI's built-in capabilities:
- **GitHub MCP Server** - Fetches PR data from GitHub
- **File tools** - Saves generated chart images
- **Code execution** - Generates charts using Python/matplotlib or other methods
3. **Interactive session**: After initial analysis, user can ask for adjustments
## Why this approach?
| Aspect | Custom Tools | Built-in Copilot |
| --------------- | ----------------- | --------------------------------- |
| Code complexity | High | **Minimal** |
| Maintenance | You maintain | **Copilot maintains** |
| Flexibility | Fixed logic | **AI decides best approach** |
| Chart types | What you coded | **Any type Copilot can generate** |
| Data grouping | Hardcoded buckets | **Intelligent grouping** |