GitHub Issue Tracking for Developers

If It Moved
You’ll Know

One command checks every issue you’re involved in, flags what actually changed, and tells you what to do next. It remembers the details so you don’t have to.

You’re listed on issues across a dozen repos. No single place tracks which ones actually moved.

The external problem

You’re the author, a commenter, or just mentioned on issues spread across repos you don’t maintain. Some have new comments, some got closed, some got duplicated by someone else’s report, and nothing tells you which.

The internal problem

Checking in means opening each issue cold and rereading old threads just to remember what you already knew, hoping you don’t skim past the one comment that actually needed a reply.

What's at stake

A maintainer’s question sits unanswered for two weeks. You comment on an issue someone already fixed. A PR that needs your review goes stale while you’re busy elsewhere.

Five steps carry an issue from fetch to a queued action.

Run it as a one-time audit or a daily check-in. The tracker file carries context between runs, so repeat runs get faster and go deeper.

01

Discover & Fetch

Pulls every open issue you’re the author, commenter, or mentioned party on, plus anything reopened or closed since the last run, straight from the GitHub API.

02

Cross-Check Duplicates

Batches of issues get analyzed in parallel: new comments read, duplicate and related issues searched for by keyword, and PR health (merge state, CI, reviews) checked where it applies.

03

Cache to Tracker

Every fact gets written to the tracker file immediately: state changes, new duplicates, filled-in context. If the session gets interrupted here, nothing is lost.

04

Goal-Driven Next Steps

Each issue’s recommendation is driven by the goal you set for it: get a fix merged, get a maintainer to respond, or just monitor for an upstream fix.

05

Report and Act

A tight report of what changed, what’s new, and what to do today, plus queued actions the skill can run directly, like posting a comment or rebasing a PR branch, once you approve them.

The formats and API calls it runs on.

Every fetch runs through documented gh CLI command templates instead of ad hoc queries, and every tracker write follows a strict schema for what gets overwritten versus what only fills a gap, so the tracker stays reliable across sessions.

Reference

gh CLI Patterns

Issue Metadata

Fetch state, labels, comment count, timestamps:

gh api repos/OWNER/REPO/issues/NUMBER --jq '{state: .state, labels: [.labels[].name], comments: .comments, updated: .updated_at, created: .created_at}'

Issue Comments

Last 3 comments with author, date, full body:

gh api repos/OWNER/REPO/issues/NUMBER/comments --jq '.[-3:] | .[] | {author: .user.login, date: (.created_at | split("T")[0]), body: .body}'

All comments (for drafting — verify claims before posting):

gh api repos/OWNER/REPO/issues/NUMBER/comments --jq '.[] | {author: .user.login, date: .created_at, body: .body}'

Comments since a date:

gh api repos/OWNER/REPO/issues/NUMBER/comments --jq '[.[] | select(.created_at > "DATE")] | .[] | {author: .user.login, date: (.created_at | split("T")[0]), body: .body}'

Truncation rules: Never truncate comments on the primary issue being reviewed — technical

details (addresses, error codes, test counts) get lost. For secondary fetches (known

duplicates, upstream), [0:1500] is acceptable to manage context size.

Issue Body

Full issue body (read before commenting on unfamiliar issues):

gh api repos/OWNER/REPO/issues/NUMBER --jq '.body'

Search: Issues Involving User

Open issues with recent activity:

gh api "search/issues?q=involves:USERNAME+updated:>DATE+is:open" --jq '.items[] | "#\(.number) \(.repository_url | split("/") | .[-2:] | join("/")) — \(.title) [updated: \(.updated_at)]"'

Search: Issues by Author

gh api "search/issues?q=author:USERNAME+is:open&per_page=100&sort=updated" --jq '.items[] | "#\(.number) \(.repository_url | split("/") | .[-2:] | join("/")) — \(.title) [updated: \(.updated_at | split("T")[0])]"'

Search: Issues by Commenter (excluding authored)

gh api "search/issues?q=commenter:USERNAME+is:open+-author:USERNAME&per_page=100&sort=updated" --jq '.items[] | "#\(.number) \(.repository_url | split("/") | .[-2:] | join("/")) — \(.title) [updated: \(.updated_at | split("T")[0])]"'

Search: Keyword Search for Duplicates

Search for issues matching keywords, created after a specific date:

gh api "search/issues?q=repo:OWNER/REPO+is:open+created:>DATE+KEYWORD1+KEYWORD2&per_page=10" --jq '.items[] | "#\(.number) — \(.title) [\(.created_at | split("T")[0])] @\(.user.login)"'

Use multiple keyword variations per search to catch different phrasings. Example for an OAuth issue:

  • Search 1: OAuth redirect MCP
  • Search 2: clientId mcp add
  • Search 3: Slack plugin authentication

Search: Recently Closed Issues

gh api "search/issues?q=involves:USERNAME+is:closed+closed:>DATE&per_page=50&sort=updated" --jq '.items[] | "#\(.number) \(.repository_url | split("/") | .[-2:] | join("/")) — \(.title) [closed: \(.closed_at | split("T")[0])]"'

Post Comment

gh issue comment NUMBER --repo OWNER/REPO --body "$(cat <<'EOF'
Comment body here.
Supports **markdown**.
EOF
)"

Always use heredoc (<<'EOF') for comment bodies to handle special characters and newlines.

Quick State Check

Check if an issue is still open (fast, minimal data):

gh api repos/OWNER/REPO/issues/NUMBER --jq '.state'

PR Status Check

gh api repos/OWNER/REPO/pulls/NUMBER --jq '{state: .state, merged: .merged, title: .title, updated: .updated_at}'

PR Health (run when the tracked item is a PR)

Merge-readiness, review decision, and CI status in one call:

gh pr view NUMBER --repo OWNER/REPO \
  --json state,mergeStateStatus,mergeable,reviewDecision,statusCheckRollup,reviews

gh pr view --json resolves reviewDecision and statusCheckRollup via GraphQL under the

hood — the REST issues endpoint cannot. Use this template (not the issues endpoint) whenever

you need merge/CI/review state.

Reading the result:

  • mergeStateStatusDIRTY means merge conflict; BLOCKED means a required check or review

is failing/missing; CLEAN/UNSTABLE means mergeable (UNSTABLE = non-required checks failing).

  • mergeableCONFLICTING confirms a conflict; MERGEABLE is clean; UNKNOWN means GitHub

is still computing it.

  • reviewDecisionAPPROVED, CHANGES_REQUESTED, or REVIEW_REQUIRED.
  • statusCheckRollup — array of checks; surface any with conclusion of FAILURE/TIMED_OUT/CANCELLED.

Review interpretation (do not misread):

  • A review with state: COMMENTED is not an approval. Only state: APPROVED counts.
  • Reviewers whose login ends in [bot] do not satisfy a human-review requirement — record

them separately from human reviews.

Rate Limiting

GitHub API has rate limits. If running many parallel requests:

  • Authenticated requests: 5000/hour
  • Search API: 30 requests/minute
  • If you hit limits, batch searches and add short delays between search batches
  • Metadata fetches (repos/OWNER/REPO/issues/NUMBER) don't count against search limits

Parallel Execution Strategy

To maximize speed, batch API calls by type:

Batch 1 (parallel): All active issue metadata + comments (these are REST calls, not search)

Batch 2 (parallel): All known duplicate/related issue checks (REST calls)

Batch 3 (parallel): All keyword searches for new duplicates (search API — respect 30/min limit)

Batch 4 (parallel): User involvement search + closed issue checks + upstream checks

Batches 1 and 2 can run simultaneously. Batch 3 should start after a brief pause if Batch 1+2 included many calls. Batch 4 can run with Batch 1.

Use it now. It's free.

Works on all platforms. Pick yours and get set up in under a minute.

Download & upload in 60 seconds

One click downloads the zip and opens Claude.ai.

01

Click below — the zip downloads and Claude.ai opens.

02

Click the + button in the skills column on the left.

03

Select Create a skill.

04

Select Upload a skill and upload the zip.

05

Start your issue check-in by running /estack-github-issue-tracker.

GitHub Issue Tracker ships in E-Stack, a set of 20 free skills installed by one command. See the whole stack.