| Crates.io | github-fetch |
| lib.rs | github-fetch |
| version | 0.1.0 |
| created_at | 2025-12-11 05:50:21.089948+00 |
| updated_at | 2025-12-11 05:50:21.089948+00 |
| description | A comprehensive GitHub API client for fetching issues, PRs, reviews, discussions, and diffs with filtering support |
| homepage | https://github.com/ZhangHanDong/github-fetch |
| repository | https://github.com/ZhangHanDong/github-fetch |
| max_upload_size | |
| id | 1979212 |
| size | 162,425 |
A Rust library for fetching GitHub issues, pull requests, discussions, reviews, and diff information via the GitHub API.
Add to your Cargo.toml:
[dependencies]
github-fetch = "0.1"
tokio = { version = "1", features = ["full"] }
anyhow = "1.0"
Set your GitHub token as an environment variable:
export GITHUB_TOKEN=ghp_your_token_here
use github_fetch::{GitHubFetcher, Repository};
#[tokio::main]
async fn main() -> anyhow::Result<()> {
let fetcher = GitHubFetcher::new(None)?;
let repo = Repository::new("tokio-rs", "tokio");
// Fetch a PR with reviews and diff
let pr = fetcher.fetch_pr(&repo, 1234).await?;
let reviews = fetcher.fetch_pr_reviews(&repo, 1234).await?;
let files = fetcher.fetch_pr_files(&repo, 1234).await?;
println!("PR: {}", pr.title);
println!("Reviews: {}", reviews.len());
println!("Files changed: {}", files.len());
Ok(())
}
use github_fetch::GitHubFetcherBuilder;
let fetcher = GitHubFetcherBuilder::new()
.token("ghp_your_token")
.user_agent("my-app/1.0.0")
.rate_limit(30)
.max_retries(5)
.build()?;
use github_fetch::{GitHubFetcher, Repository, IssueFilters, IssueState};
let filters = IssueFilters {
state: IssueState::Closed,
include_labels: vec!["bug".to_string()],
min_comments: Some(3),
code_blocks_only: true,
..Default::default()
};
let result = fetcher.fetch_issues_with_limit(&repo, &filters, 10).await?;
let reviews = fetcher.fetch_pr_reviews(&repo, 2865).await?;
for review in &reviews {
println!("{}: {}", review.user.login, review.state);
// state: APPROVED, CHANGES_REQUESTED, COMMENTED, DISMISSED, PENDING
}
let review_comments = fetcher.fetch_pr_review_comments(&repo, 2865).await?;
for comment in &review_comments {
println!("File: {} Line: {:?}", comment.path, comment.line);
println!("Comment: {}", comment.body);
println!("Diff:\n{}", comment.diff_hunk);
}
let files = fetcher.fetch_pr_files(&repo, 2865).await?;
for file in &files {
println!("{}: +{} -{}", file.filename, file.additions, file.deletions);
if let Some(patch) = &file.patch {
println!("{}", patch);
}
}
let discussion = fetcher.fetch_discussion(&repo, 3766).await?;
println!("Title: {}", discussion.title);
// Or fetch by URL
let discussion = fetcher.fetch_discussion_by_url(
"https://github.com/actix/actix-web/discussions/3766"
).await?;
pub struct PrReview {
pub id: u64,
pub user: GitHubUser,
pub body: Option<String>,
pub state: String, // APPROVED, CHANGES_REQUESTED, COMMENTED, DISMISSED, PENDING
pub submitted_at: Option<DateTime<Utc>>,
pub html_url: String,
pub commit_id: Option<String>,
}
pub struct PrReviewComment {
pub id: u64,
pub user: GitHubUser,
pub body: String,
pub path: String, // File path
pub line: Option<u32>, // Line number
pub diff_hunk: String, // Diff context
pub side: Option<String>, // LEFT or RIGHT
pub in_reply_to_id: Option<u64>,
}
pub struct PrFile {
pub filename: String,
pub status: String, // Added, Modified, Removed, Renamed
pub additions: u32,
pub deletions: u32,
pub patch: Option<String>, // Diff content
}
export GITHUB_TOKEN=ghp_your_token
cargo run --example fetch_tokio_issue
cargo run --example fetch_axum_pr
cargo run --example fetch_pr_review
cargo run --example fetch_with_filters
cargo run --example fetch_actix_discussion
cargo run --example advanced_usage
use github_fetch::{GitHubFetcher, GitHubFetchError, Repository};
match fetcher.fetch_issue(&repo, 999999).await {
Ok(issue) => println!("Found: {}", issue.title),
Err(GitHubFetchError::NotFound(msg)) => println!("Not found: {}", msg),
Err(GitHubFetchError::AuthError(msg)) => println!("Auth error: {}", msg),
Err(GitHubFetchError::RateLimitExceeded) => println!("Rate limited!"),
Err(e) => println!("Error: {}", e),
}
This project includes a Claude Code Skill for PR review and fixing. The skill automatically fetches PR information, analyzes review comments, fixes code issues, and re-reviews changes.
claude plugins add https://github.com/ZhangHanDong/github-fetch
# Global (available in all projects)
cp -r skills/pr-review ~/.claude/skills/
# Project-level (available in current project, can be committed to git)
mkdir -p .claude/skills
cp -r skills/pr-review .claude/skills/
Download github-fetch-plugin.zip from releases and extract to your skills directory.
After installation, just ask Claude naturally:
Review PR https://github.com/tokio-rs/axum/pull/2865
Analyze the review comments on owner/repo#123 and fix the issues
What changes are requested in this PR? https://github.com/owner/repo/pull/456
You: Review PR https://github.com/owner/repo/pull/123
Claude:
## PR #123: Fix authentication bug
**Author:** @developer | **State:** Open
### Reviews
| Reviewer | State | Comment |
|----------|-------|---------|
| @reviewer1 | CHANGES_REQUESTED | Please add error handling |
| @reviewer2 | COMMENTED | Consider using Result type |
### Files Changed (2 files, +45 -12)
- `src/auth.rs` (+30 -10) Modified
- `src/lib.rs` (+15 -2) Modified
### Review Comments
#### src/auth.rs:42
> @reviewer1: This should handle the None case
### Action Items
1. [ ] Handle None case in authenticate() (src/auth.rs:42)
2. [ ] Add Result return type (src/auth.rs:45)
Would you like me to fix these issues?
You: Yes, fix them
Claude: [Reads files, applies fixes, re-reviews...]
All issues have been fixed:
- Added match expression for Option handling
- Changed return type to Result<(), AuthError>
Re-reviewing... No new issues found.
GITHUB_TOKEN environment variable must be setskills/pr-review/
├── SKILL.md # Main instructions for Claude
├── WORKFLOW.md # Detailed workflow reference
└── scripts/
└── fetch_pr.rs # Example fetch script
MIT OR Apache-2.0