Activity Timeline Generator
Generate comprehensive timelines of GitHub or GitLab activity for a specified user and time period.
When to use this skill
- •User asks for their GitHub/GitLab activity or contribution history
- •User wants to see what they worked on in a given month
- •User asks for a summary of their recent work
- •User mentions "activity", "timeline", "contributions", or "what did I work on"
Arguments
Parse the user's request for:
- •Platform: GitHub or GitLab (infer from context, check CLAUDE.md, or ask)
- •Username: GitHub/GitLab username (check CLAUDE.md or ask if not specified)
- •Time period: Specific month (e.g., "January 2026") or "past 30 days" (default)
- •Output file: Where to save the timeline (default:
./timeline-YYYY-MM.md)
Execution steps
1. Determine parameters
Platform: GitHub (gh) or GitLab (glab) - infer or ask Username: From CLAUDE.md, conversation context, or ask user Period: Parse from request, default to past 30 days Output: Parse from request or generate default filename
2. Calculate date range
For a specific month:
START_DATE=YYYY-MM-01
END_DATE=YYYY-MM-{last day of month}
For past 30 days:
START_DATE=$(date -d "30 days ago" +%Y-%m-%d) END_DATE=$(date +%Y-%m-%d)
GitHub Instructions (gh CLI)
Fetch GitHub data
User events (recent activity feed)
Note: Events API only retains ~90 days of history. Filter by date range in jq.
gh api users/{username}/events --paginate --jq '.[] | select(.created_at >= "{START_DATE}" and .created_at <= "{END_DATE}T23:59:59Z") | {type: .type, repo: .repo.name, created_at: .created_at, payload_action: .payload.action, ref: .payload.ref, ref_type: .payload.ref_type}'
PRs authored (using API for reliable date filtering)
gh api "search/issues?q=author:{username}+type:pr+created:{START_DATE}..{END_DATE}" --jq '.items[] | {title: .title, repo: (.repository_url | split("/") | .[-1]), created_at: .created_at, state: .state, url: .html_url, number: .number}'
Issues created (using API for reliable date filtering)
gh api "search/issues?q=author:{username}+type:issue+created:{START_DATE}..{END_DATE}" --jq '.items[] | {title: .title, repo: (.repository_url | split("/") | .[-1]), created_at: .created_at, state: .state, url: .html_url, number: .number}'
Releases from user's repos
gh api users/{username}/repos --jq '.[].name' | while read repo; do
gh api "repos/{username}/$repo/releases" --jq '.[] | select(.published_at >= "{START_DATE}") | {repo: "'$repo'", tag: .tag_name, published_at: .published_at, url: .html_url}' 2>/dev/null
done
Issues/PRs involving user (for external contributions)
gh api "search/issues?q=involves:{username}+created:{START_DATE}..{END_DATE}+-author:{username}" --jq '.items[] | {title: .title, repo: (.repository_url | split("/") | .[-1]), created_at: .created_at, url: .html_url}'
Open PRs authored by user (all time, still active)
gh api "search/issues?q=author:{username}+type:pr+state:open&per_page=100" --jq '.items[] | {title: .title, repo: (.repository_url | split("/") | .[-2:] | join("/")), created_at: .created_at, url: .html_url, number: .number, draft: .draft, labels: [.labels[].name]}'
Commits to specific repos (if needed for detail)
gh api "repos/{owner}/{repo}/commits?since={START_DATE}&author={username}" --jq '.[] | {sha: .sha[0:7], date: .commit.author.date, message: (.commit.message | split("\n")[0])}'
Collect all repos with activity
Gather unique repos from user's account and from the events data. Note: Parse repos from the events you already fetched rather than re-fetching.
# Get user's own repos
gh api users/{username}/repos --paginate --jq '.[].full_name'
# Also extract unique repos from the events API response (org repos where user contributes)
# Parse from the events data you fetched earlier, extracting unique .repo values
Star counts and changes for specific repos
For repos you want to track (user's repos + org repos they maintain), fetch individually:
gh api "repos/{owner}/{repo}" --jq '{repo: .name, full_name: .full_name, stars: .stargazers_count, url: .html_url}'
Star events on a repo (who starred during period)
gh api "repos/{owner}/{repo}/stargazers" -H "Accept: application/vnd.github.star+json" --paginate --jq '.[] | select(.starred_at >= "{START_DATE}" and .starred_at <= "{END_DATE}") | {user: .user.login, starred_at: .starred_at}' 2>/dev/null
To count stars gained in period:
gh api "repos/{owner}/{repo}/stargazers" -H "Accept: application/vnd.github.star+json" --paginate --jq '.[] | select(.starred_at >= "{START_DATE}" and .starred_at <= "{END_DATE}")' 2>/dev/null | wc -l
Note: The stargazers endpoint with timestamps requires the star+json media type. This includes org repos where the user has activity (e.g., maintainer of ratatui/tachyonfx). To calculate net change, compare against previous period or track unstar events (which aren't directly available via API).
GitLab Instructions (glab CLI)
Fetch GitLab data
User's merge requests
glab api "merge_requests?author_username={username}&created_after={START_DATE}T00:00:00Z&scope=all" --paginate | jq '.[] | {title: .title, project: .references.full, created_at: .created_at, state: .state, url: .web_url, iid: .iid, merged_at: .merged_at}'
User's issues
glab api "issues?author_username={username}&created_after={START_DATE}T00:00:00Z&scope=all" --paginate | jq '.[] | {title: .title, project: .references.full, created_at: .created_at, state: .state, url: .web_url, iid: .iid}'
User's events/activity
glab api "users/{username}/events?after={START_DATE}" --paginate | jq '.[] | {action: .action_name, target_type: .target_type, target_title: .target_title, created_at: .created_at, project: .project_id}'
Projects user contributed to
glab api "users/{username}/contributed_projects" | jq '.[] | {name: .name, path: .path_with_namespace, url: .web_url}'
Commits in a specific project
glab api "projects/{project_id}/repository/commits?author={username}&since={START_DATE}T00:00:00Z" | jq '.[] | {sha: .short_id, date: .created_at, message: (.title)}'
MR reviews/approvals
glab api "merge_requests?reviewer_username={username}&created_after={START_DATE}T00:00:00Z&scope=all" --paginate | jq '.[] | {title: .title, project: .references.full, created_at: .created_at, url: .web_url}'
Releases (per project)
glab api "projects/{project_id}/releases" | jq '.[] | select(.created_at >= "{START_DATE}") | {tag: .tag_name, name: .name, created_at: .created_at, url: ._links.self}'
Open MRs authored by user (all time, still active)
glab api "merge_requests?author_username={username}&state=opened&scope=all" --paginate | jq '.[] | {title: .title, project: .references.full, created_at: .created_at, url: .web_url, iid: .iid, draft: .draft, labels: .labels, description: .description}'
Organize into timeline
Group events chronologically and categorize by:
- •Releases - Version releases with links
- •MRs/PRs merged - Merge/pull requests that were merged
- •MRs/PRs opened - Merge/pull requests opened (note if still open)
- •Issues - Issues created or closed
- •Code reviews - Reviews on others' MRs/PRs
- •Repos starred - Repos the user starred (GitHub only)
- •Notable commits - Significant commits (features, fixes)
- •Repository star changes - New stars gained on user's repos (GitHub only)
- •Active PRs - All open PRs authored by the user (see below)
Generate summary statistics
For the summary section, calculate:
- •Number of releases per project
- •MRs/PRs authored (merged count)
- •External MRs/PRs reviewed
- •Issues created/closed
- •Repos starred by user (GitHub)
- •Stars gained on user's repositories (GitHub) - include per-repo breakdown if significant
- •Notable external contributors (if applicable)
Summarize active PRs
Fetch all open PRs authored by the user and create an intelligent summary:
- •Group by repository - Organize PRs by the repository they target
- •Categorize by type - Infer from title/labels: feature, bugfix, docs, refactor, dependency update
- •Note age - Flag PRs that have been open for a long time (>2 weeks) as potentially stale
- •Identify draft PRs - Mark draft PRs separately as work-in-progress
- •Summarize intent - For each PR, write a brief 1-line summary based on title and body (if available)
When summarizing, look for patterns:
- •Multiple PRs in the same repo suggest active development focus
- •PRs across different orgs show breadth of contribution
- •Long-open PRs may need attention or follow-up
- •Draft PRs indicate ongoing work
For each active PR, include:
- •Repository name
- •PR number and title (linked)
- •Age (days since opened)
- •Status indicators:
[draft],[stale],[waiting-review] - •Brief context if discernible from title/body
Write output
Create markdown file with structure:
# {Platform} Activity Timeline: {Period Description}
## {Month/Period} {Year}
### {Date Range or Week}
- **{repo} {version} released** - {description}
- **{repo} MR/PR !/{#}{number} merged** - {title}
- ...
---
## {Period} Summary
| Metric | Count |
|--------|-------|
| **Releases** | X |
| **MRs/PRs merged** | X |
| **Stars gained** | +X |
| ...
### Repository Star Changes (GitHub)
| Repository | Stars | Change |
|------------|-------|--------|
| repo-name | 150 | +12 |
| other-repo | 45 | +3 |
### Notable External Contributors
- **{username}** - {contribution count/description}
---
## Active Pull Requests
Summary of all open PRs authored by {username} as of {current_date}.
### {repository_name}
| PR | Title | Age | Status |
|----|-------|-----|--------|
| [#{number}]({url}) | {title} | {X} days | {status_badges} |
**Context:** {brief summary of what these PRs are about, patterns noticed}
### {another_repository}
...
**Overall:** {X} open PRs across {Y} repositories. {Any observations about focus areas, stale PRs needing attention, etc.}
Format output
After writing the markdown file, format it with prettier to ensure tables are properly aligned:
prettier --write {output_file}
This ensures markdown tables display correctly when viewed in the terminal.
Edge cases
- •No activity: Report "No activity found for this period"
- •Rate limiting: If API returns rate limit errors, wait and retry or inform user
- •Private repos/projects: Activity from private repos may not appear depending on permissions
- •Events API limit: GitHub Events API only retains ~90 days of history; for older periods, rely on commit/MR/issue searches instead
- •GitLab self-hosted: May need different API base URL; glab should handle this if configured
Platform detection tips
- •If user mentions "MR" or "merge request" -> GitLab
- •If user mentions "PR" or "pull request" -> GitHub
- •Check CLAUDE.md for configured usernames per platform
- •If unclear, ask: "Which platform - GitHub or GitLab?"