ge

ge: GitHub Engineering tools for AI agents.

Reduces boilerplate when working on GitHub issues and PRs with AI coding agents. Fetches context, downloads media, checks freshness, and assembles everything into a structured document the agent can consume.

Usage:

import ge

# Prepare full context for an issue
ctx = ge.prepare_issue('owner/repo', 42)

# Prepare full context for a PR
ctx = ge.prepare_pr('owner/repo', 7)

# Or from a full URL
ctx = ge.prepare('https://github.com/owner/repo/issues/42')
ge.analyze_issue(repo, number)[source]

Analyze an issue for staleness and current relevance.

Returns a dict with:
  • state: open/closed

  • age_days: days since creation

  • last_activity_days: days since last comment/event

  • labels: list of label names

  • related_prs: PRs that reference this issue

  • related_commits: commits mentioning this issue

  • referenced_files: files mentioned in the issue body

  • signals: list of human-readable observations about freshness

  • recommendation: ‘proceed’, ‘investigate’, or ‘likely_resolved’

>>> # analysis = analyze_issue('owner/repo', 42)
ge.analyze_pr(repo, number)[source]

Analyze a PR for review context and merge readiness.

Returns a dict with state, review status, CI signals, conflicts, etc.

>>> # analysis = analyze_pr('owner/repo', 7)
ge.copy_images_to_clipboard(*image_paths, tile='3x', geometry='800x600+10+10', montage_path=None)[source]

Create a montage of images and copy it to the macOS clipboard.

Combines multiple images into a single grid image using ImageMagick, then copies it to the clipboard via osascript so the user can paste it into Claude Code with Cmd+V.

Parameters:
  • *image_paths – Paths to image files to combine.

  • tile – Montage tile layout (e.g. ‘3x’ for 3 columns).

  • geometry – Tile geometry (size+padding).

  • montage_path – Where to save the montage file. Defaults to /tmp/ge_montage.png.

Returns:

Path to the montage file.

Return type:

str

>>> # path = copy_images_to_clipboard('img1.png', 'img2.png')
ge.describe_images(*image_paths, prompt='Describe what you see in these images in detail. If they appear to be screenshots of a bug or UI issue, describe the problem visible.', model='claude-sonnet-4-5-20250514', max_tokens=4096)[source]

Use the Claude API to describe images, returning a text description.

Requires the anthropic package and a valid ANTHROPIC_API_KEY.

Parameters:
  • *image_paths – One or more paths to image files.

  • prompt – The text prompt sent alongside the images.

  • model – Claude model to use for vision analysis.

  • max_tokens – Maximum tokens in the response.

Returns:

The model’s textual description of the images.

Return type:

str

>>> # description = describe_images('screenshot.png', 'error.jpg')
ge.extract_video_frames(video_path, *, n_frames=5, output_dir=None, mode='scene', scene_threshold=0.3)[source]

Extract representative frames from a video file using ffmpeg.

Two modes are available:

  • mode='scene' (default): Uses ffmpeg’s scene change detection filter to extract frames where visual content actually changes. Better for bug reproduction videos where you want one frame per distinct UI state. scene_threshold controls sensitivity (0.0–1.0, lower = more frames). Falls back to ‘uniform’ if scene detection yields no frames.

  • mode='uniform': Extracts n_frames evenly-spaced frames.

Returns list of paths to extracted frame images (JPEG).

>>> # frames = extract_video_frames('demo.mp4')
>>> # frames = extract_video_frames('demo.mp4', mode='uniform', n_frames=5)

Find recent commits that reference this issue number.

>>> # commits = find_related_commits('owner/repo', 42)

Find PRs that reference or close this issue.

Uses the timeline API to find cross-referenced PRs, and also searches for PRs mentioning the issue number.

>>> # prs = find_related_prs('owner/repo', 42)
ge.get_comments(repo, number)[source]

Fetch all comments on an issue or PR.

>>> # comments = get_comments('owner/repo', 42)
ge.get_discussion(repo, number)[source]

Fetch a GitHub Discussion via GraphQL (discussions aren’t in REST API).

>>> # disc = get_discussion('owner/repo', 5)
ge.get_issue(repo, number)[source]

Fetch a GitHub issue with body, labels, assignees, state, etc.

>>> # issue = get_issue('owner/repo', 42)
ge.get_pr(repo, number)[source]

Fetch a GitHub pull request with body, diff stats, merge state, etc.

>>> # pr = get_pr('owner/repo', 7)
ge.get_pr_diff(repo, number)[source]

Fetch the raw diff of a PR.

>>> # diff = get_pr_diff('owner/repo', 7)
ge.install_skills(*, target_dir=None)[source]

Symlink ge skills into ~/.claude/skills/ for global discovery.

Creates symlinks from target_dir/<skill> to the skill folders bundled with the ge package. Existing correct symlinks are left alone; a warning is printed if a symlink points elsewhere.

Parameters:

target_dir – Destination directory. Defaults to ~/.claude/skills/.

ge.prepare(url_or_spec, number=None, *, output_dir=None, describe_media=True, **kwargs)[source]

Prepare context from a GitHub URL or repo+number.

Automatically detects whether it’s an issue, PR, or discussion.

When describe_media is True (default) and the anthropic package is available, downloaded images are described via the Claude API so the agent gets visual context without manual image pasting.

>>> # ctx = ge.prepare('https://github.com/owner/repo/issues/42')
>>> # ctx = ge.prepare('owner/repo', 42)
>>> # ctx = ge.prepare('https://github.com/owner/repo/discussions/5')
ge.prepare_discussion(repo, number, *, output_dir=None, download_media_flag=True, describe_media=True)[source]

Prepare full context for a GitHub Discussion.

Fetches the discussion and its comments via GraphQL, downloads media, and writes a context document.

>>> # ctx = prepare_discussion('owner/repo', 5)
ge.prepare_issue(repo, number, *, output_dir=None, download_media_flag=True, describe_media=True)[source]

Prepare full context for working on a GitHub issue.

Fetches the issue, comments, media, and performs staleness analysis. Writes a context document and manifest to output_dir.

When describe_media is True (default) and the anthropic package is installed with ANTHROPIC_API_KEY set, downloaded images are sent to the Claude API for automated description. The description is included in the context document so the agent has visual context without manual image pasting.

Returns dict with all assembled context.

>>> # ctx = prepare_issue('owner/repo', 42)
ge.prepare_pr(repo, number, *, output_dir=None, download_media_flag=True, include_diff=True, describe_media=True)[source]

Prepare full context for working on or reviewing a GitHub PR.

>>> # ctx = prepare_pr('owner/repo', 7)
ge.process_all_media(markdown, output_dir=None)[source]

Download all media and extract video frames.

Returns a dict with:
  • url_map, manifest, rewritten_markdown (from download_media)

  • video_frames: {video_local_path: [frame_paths]}

  • images: list of local image paths (for user to paste into agent)

  • all_visual_files: combined list of images + video frames

>>> # result = process_all_media(issue_body)
ge.resolve_target(user_input, *, current_repo=None)[source]

Resolve flexible user input into a structured target for ge workflows.

Accepts any of these forms:
  • A GitHub URL: https://github.com/owner/repo/issues/42

  • A folder path to pre-prepared context: ~/.cache/ge/owner/repo/issue_42

  • A bare number: 42 or #42 (uses current_repo)

  • owner/repo#42 or owner/repo/42

  • owner/repo 42

Returns a dict with:
  • repo: "owner/repo" (or None if only a folder was given)

  • number: int

  • kind: "issue" | "pr" | "discussion" | None

  • context_dir: path to pre-prepared context (str or None)

  • context_md: path to the context markdown file (str or None)

  • has_prepared_context: bool — whether context files already exist

  • source: how the input was resolved ("url", "folder", "number", "repo_number")

>>> r = resolve_target('https://github.com/owner/repo/issues/42')
>>> r['repo'], r['number'], r['kind'], r['source']
('owner/repo', 42, 'issue', 'url')
>>> r = resolve_target('#42', current_repo='owner/repo')
>>> r['repo'], r['number'], r['source']
('owner/repo', 42, 'number')
>>> r = resolve_target('owner/repo#42')
>>> r['repo'], r['number'], r['source']
('owner/repo', 42, 'repo_number')
ge.uninstall_skills(*, target_dir=None)[source]

Remove ge skill symlinks from ~/.claude/skills/.

Only removes symlinks that point back into the ge package’s skill directory. Non-symlinks and symlinks owned by other packages are left untouched.

Parameters:

target_dir – Directory to remove from. Defaults to ~/.claude/skills/.