| name | github-profile-blog |
| description | Write an educational blog post analyzing a GitHub profile's public activity. Use when the user wants to turn their GitHub activity, contributions, or projects into a blog article, or mentions "blog about my GitHub" or a writeup of their open-source work. |
GitHub Profile Activity Blog (Generic)
This skill generates an educational blog post about your GitHub activity from the last 2 months.
Generic Design: Works for any user with GitHub auth. Fetches both own repos AND external contributions.
Configuration (Read from Global Memory & Config)
CRITICAL: Use global memory and config system, not project-specific memory.
Read ~/.claude/config.json for:
USERNAME - GitHub username (from user.username)
BLOG_DIR - Blog directory path (from paths.blog_dir)
DEFAULT_LANGUAGE - Default language setting (from blog.default_language)
Read ~/.claude/memory/blog-config.md for:
BLOGGED_PROJECTS - List of already blogged projects (to avoid duplicates)
IGNORED_REPOS - Repos to exclude (empty repos, test repos, etc.)
First-Time Setup
If config doesn't exist:
- Copy
config.json.example to ~/.claude/config.json
- Edit with your personal information
- Run skill again
Utility Scripts
The skill uses utility scripts in ~/.claude/utils/:
-
blog-scan.py - Scans existing blog posts for mentioned projects
python3 ~/.claude/utils/blog-scan.py
Output: /tmp/blog-scan-results.json
-
github-scan.py - Template for GitHub activity scanning
Variables Used
from lib.config import get_config
config = get_config()
USERNAME = config["user"]["username"] or mcp__github__get_me()["login"]
BLOG_DIR = config["paths"]["blog_dir"]
from datetime import datetime, timedelta
since_date = (datetime.now() - timedelta(days=60)).strftime("%Y-%m-%d")
POSTS_DIR = f"{BLOG_DIR}/_posts/"
IMAGES_DIR = f"{BLOG_DIR}/assets/img/"
Workflow
Step 1: Ask Language Preference (FIRST)
Use AskUserQuestion to ask:
Blog gönderisi hangi dilde olsun?
1. Türkçe
2. İngilizce
3. Hem Türkçe hem İngilizce (iki ayrı post)
Default: Option 1 (Turkish) if not specified.
Step 2: Fetch Last 2 Months of GitHub Activity
Use GitHub MCP tools to fetch ALL activity (both own repos and external contributions):
user_info = mcp__github__get_me()
username = user_info["login"]
since_date = (datetime.now() - timedelta(days=60)).strftime("%Y-%m-%d")
mcp__github__search_pull_requests(query="author:{username} is:pr")
mcp__github__search_issues(query="author:{username} is:issue")
mcp__github__search_repositories(query="user:{username}")
for repo in user_repos:
mcp__github__list_commits(owner=username, repo=repo["name"], since=since_date)
CRITICAL: Skip any private repositories (check visibility: private).
Step 3: Read All Existing Blog Posts
Read all files from $POSTS_DIR to check what has been mentioned.
Parse each blog post for:
- Repository names mentioned
- Project names mentioned
- Contribution types mentioned
- Dates to establish timeline
Step 4: Categorize Activities
Organize the GitHub data into:
Mentioned in Blog:
- Items found in existing blog posts
- Include reference to which blog post mentioned it
Unmentioned:
- New repositories created
- Pull requests to other projects (external contributions)
- Issues opened in any repo
- Comments made
- Forks made
Step 5: Display Selection List
Show the user a formatted list with generic placeholders:
## GitHub Aktiviteleri (Son 2 Ay)
### ✅ Blog'da Bahsedilen:
- [x] [Project-Name] - [Brief Description] (Blog: [Date])
- [x] [Another-Project] - [Description] (Blog: [Date])
### 🆕 Blog'da Bahsedilmeyen:
#### 📁 Your Repositories:
- [ ] [Repo-Name] - [Brief Description]
- [ ] [Another-Repo] - [Description]
#### 🔧 Pull Requests (External Contributions):
- [ ] [Owner/Repo] - [PR Title] (#[PR-Number])
- [ ] [Owner/Repo] - [PR Title] (#[PR-Number])
#### 🐛 Issues (Opened in Any Repo):
- [ ] [Owner/Repo] - [Issue Title] (#[Issue-Number])
- [ ] [Owner/Repo] - [Issue Title] (#[Issue-Number])
---
**Lütfen blog yazmak istediğiniz konuları seçin:**
- Girin: `1,3,5` (sadece seçilenler)
- Girin: `hepsi` veya `all` (tümü)
- Girin: `yok` veya `none` (iptal)
Step 6: Get User Selection
Use AskUserQuestion or wait for user input to get their selection.
Step 7: Estimate Line Count
Calculate and show estimate:
estimated_lines = len(selected_items) * 50
print(f"Seçilen {len(selected_items)} item için tahmini blog uzunluğu: ~{estimated_lines} satır")
print("Onaylıyor musunuz? Veya tercih ettiğiniz aralığı belirtin (örn: 200-250 satır)")
Wait for user confirmation or preferred range.
Step 8: Learn Writing Style
Read the last 2-3 blog posts from $POSTS_DIR to understand:
- Tone and voice
- Structure patterns
- Technical depth
- Educational approach
Step 9: Generate Blog Content
For each selected item, create a section that includes:
- Problem/Challenge: What was being solved
- Implementation: How it was done
- Technical Details: Code snippets, architecture
- Lessons Learned: What others should learn
- Warnings/Gotchas: What to watch out for
Adjust length based on user's preferred range if specified.
Example Structure:
---
title: "GitHub Aktivitelerim: [Month-Month] [Year]"
description: "Son iki ayda yaptığım açık kaynak katkıları ve projeler"
date: YYYY-MM-DD HH:MM:SS +0300
categories: [Open Source, Development]
tags: [mcp, typescript, contribution]
image:
path: /assets/img/YYYY-MM-DD-github-activities/banner.png
alt: "GitHub Activity Summary"
---
## Son İki Ayın Özeti
Son 60 gündür yoğun bir şekilde açık kaynak projelere katkıda bulundum...
## [Repo-Owner/Repo-Name] PR'ı
### Problemi Keşfetmek
[Describe the problem discovered]
### Çözüm Süreci
[Technical details with code examples]
### PR'ın Durumu
[#PR-Number numaralı PR](link) [durum]...
## [Repository-Name] Projesi
### Proje Hakkında
[Project details with challenges and solutions]
### Öğrendiklerim
[Lessons learned]
Step 10: Find and Download Images
Use your configured image-search MCP (e.g. a Google / Bing image-search MCP server):
- Search for banner image (GitHub/contribution themed)
- Search for project-specific images
- Search for technical diagram images
- Download to
$BLOG_DIR/assets/img/YYYY-MM-DD-github-activities/
Skip this step if no image-search MCP is configured.
Step 11: Show Draft
Display the complete draft in the terminal.
Step 12: Wait for User Approval
Ask: "Taslak onaylanıyor mu? Blog dosyası oluşturulsun mu?"
Step 13: Write Blog File (On Approval)
If approved:
- Create the file at
$POSTS_DIR/YYYY-MM-DD-github-activities.md
- If bilingual: Create two files (
-tr.md and -en.md)
Step 14: Create DEV.to Draft (Optional)
Skip this step entirely if no DEV.to publishing MCP is configured. Otherwise, use it to create the article draft:
- Set
published: false
- Use appropriate tags (max 4)
Step 15: Ask for DEV.to Publish Approval
Ask: "DEV.to'da publish edilsin mi?"
Step 16: Publish to DEV.to (On Approval)
Update the draft to published: true via the same MCP used in Step 14.
Critical Requirements
- Config system: Use
~/.claude/config.json for user settings (username, blog_dir, language)
- Global Memory: Use
~/.claude/memory/blog-config.md for state (blogged projects, ignored repos)
- Config loader: Use
from lib.config import get_config, get_blogged_projects in utilities
- Auto-detect username: Use
config["user"]["username"] or fallback to mcp__github__get_me()["login"]
- Dynamic date calculation: 2 months ago from today, not hardcoded
- Skip empty repos: Check repo has commits before including
- Skip private repos: Filter by
visibility: public
- Cross-reference blogs: Don't duplicate blogged projects
- Use utility scripts: Leverage
~/.claude/utils/blog-scan.py
- Generic examples: Use placeholders like
[Project-Name], not specific names
- Estimated line count: Show estimate before generating, allow user to specify range
- List both mentioned and unmentioned items
- Let user select specific items - don't auto-select
- Educational focus - teach lessons, show warnings
- Read previous blog posts for style matching
- Show draft first - never auto-write
- Separate approvals for file write and DEV.to publish
Filtering Rules (IMPORTANT)
When scanning GitHub activity, EXCLUDE:
-
Private repositories: Check visibility: private
-
Empty repositories: Check for commits (some repos are created but never used)
-
Ignored repos: Read IGNORED_REPOS from memory
- Example entry:
[OWNER]/[REPO] with reason (e.g. empty repo, archived)
-
Already blogged: Check BLOGGED_PROJECTS from memory
def should_include_repo(repo):
if repo.get("visibility") == "private":
return False
if repo["full_name"] in IGNORED_REPOS:
return False
if repo["full_name"] in BLOGGED_PROJECTS:
return False
if repo.get("size", 0) == 0:
return False
return True
Educational Content Guidelines
For each selected item:
- What was the challenge? - Be specific
- How did you solve it? - Step-by-step
- What did you learn? - Teachable moments
- What should others watch out for? - Warnings and gotchas
- Code examples - When relevant
GitHub MCP Query Patterns
mcp__github__search_pull_requests(query="author:USERNAME is:pr")
mcp__github__search_issues(query="author:USERNAME is:issue")
mcp__github__search_repositories(query="user:USERNAME")
since_date = "2026-01-11"
Memory Reference
Config Path: ~/.claude/config.json
This file contains user settings (username, blog directory, language, profiles).
Global Memory Path: ~/.claude/memory/blog-config.md
This file contains:
- Blogged projects tracker (YYYY-MM-DD | repo | blog-file)
- Ignored repositories list
- Writing style notes
- Common categories and tags
Utility Scripts Reference
~/.claude/utils/blog-scan.py
Scans blog posts for GitHub repo references.
Usage:
result = subprocess.run(
["python3", "~/.claude/utils/blog-scan.py"],
capture_output=True, text=True
)
Returns:
mentioned_projects: List of repos found in blog posts
all_github_links: All GitHub links found
posts: Blog post metadata
~/.claude/utils/github-scan.py
Template for scanning GitHub activity (uses MCP calls).
Key Functions:
scan_blog_posts() - Find mentioned repos
calculate_since_date() - Dynamic date calculation
main() - Full scan workflow