How I built a tool that checks if a GitHub issue already has a PR — and why that's the feature nobody built
Source: Dev.to
The problem
Finding a beginner‑friendly issue, forking the repo, setting up the dev environment, and reading the codebase can feel great—until you check the issue again and see a recent comment like:
“Hey I’m working on this, should have a PR up soon.”
Two hours wasted. This happens over and over because most issue‑discovery tools (e.g., goodfirstissue.dev, up‑for‑grabs.net, codetriage) rely on static lists or periodic scrapes. They show you issues, but they don’t tell you whether someone is already quietly working on them.
Introducing GitTrek
GitTrek fills that gap. It looks for the “digital traces” developers leave when they start working on an issue:
| Trace type | How it appears |
|---|---|
| PR Mentions | The issue number is mentioned in a PR description or commit message (e.g., Fixes #847). |
| Linked Branches | A branch is created from the issue via GitHub’s UI, then a PR is opened from that branch. |
Both traces are exposed through GitHub’s GraphQL API, specifically via the timelineItems field on issues.
Relevant GraphQL events
CROSS_REFERENCED_EVENT– Fires when a PR or commit mentions the issue number. The PR is the source of the event.CONNECTED_EVENT– Fires when a branch linked to the issue becomes a pull request. The PR is the subject of the event.
Pro tip: Mixing up
sourceandsubjectyields null results without errors.
Sample query
issue(number: $issueNumber) {
timelineItems(
first: 25,
itemTypes: [CROSS_REFERENCED_EVENT, CONNECTED_EVENT]
) {
nodes {
... on CrossReferencedEvent {
source {
... on PullRequest {
number
state # OPEN | CLOSED | MERGED
isDraft
}
}
}
... on ConnectedEvent {
subject {
... on PullRequest {
number
state
isDraft
}
}
}
}
}
linkedBranches(first: 3) {
totalCount
}
}
How GitTrek visualises competition
GitTrek color‑codes each issue based on the data above:
| Badge | Meaning |
|---|---|
| Active PR | Open non‑draft PR exists — high competition |
| Someone started | Draft PR linked — proceed carefully |
| Branch exists | linkedBranches > 0 but no PR yet — early signal |
| Safe to claim | No linked PRs or branches found |
Checking 20 issues requires 21 API calls (1 search + 20 status checks). To keep the UI snappy, GitTrek shows the issue list immediately and “hydrates” the badges in parallel.
Hydrating badges in the background
// Fetch fresh data from APIs in the background
// This ensures one badge failure doesn't block the entire dashboard
const settlements = await Promise.allSettled([
fetch(`/api/github/badges/pull-shark?username=${user}`).then(r => r.json()),
fetch(`/api/github/badges/starstruck?username=${user}`).then(r => r.json()),
// ... more badge checks
]);
// Safely extract results even if some failed
const [pullShark, starstruck, ...] = settlements.map(s =>
s.status === "fulfilled" ? s.value : null
);
// Apply fallback values for failed items
const psData = pullShark || { count: 0 };
Promise.allSettled is used instead of Promise.all so that a single failing check (e.g., permission issue or rate limit) doesn’t break the whole dashboard.
Beyond “ghost PR” detection
GitTrek is also a companion for open‑source growth:
- Repository Quality Gates – Filter by stars, forks, and the presence of a
CONTRIBUTING.md. - Live Achievement Tracking – Monitor progress toward badges like Pull Shark, Galaxy Brain, and YOLO using live GraphQL calculations.
- Focus Mission – If you’re a few PRs away from a badge, GitTrek builds a custom search query to surface the exact issues you need.
Getting started
- Live app:
- Source code:
GitTrek is free, open source, and requires no setup for browsing. Connect your GitHub account only if you want personalized badge tracking.
Your experience
Have you ever wasted time on an issue that was already being worked on? Share your story in the comments—curious how common this actually is.