browsing-bluesky
Browse Bluesky content via API and firehose - search posts, fetch user activity, sample trending topics, read feeds and lists, analyze and categorize accounts. Supports authenticated access for personalized feeds. Use for Bluesky research, user monitoring, trend analysis, feed reading, firehose sampling, account categorization.
git clone --depth 1 https://github.com/oaustegard/claude-skills /tmp/browsing-bluesky && cp -r /tmp/browsing-bluesky/browsing-bluesky ~/.claude/skills/browsing-blueskySKILL.md
# Browsing Bluesky
Access Bluesky content through public APIs and real-time firehose. Supports optional authentication for personalized feeds. Includes account analysis for categorization.
## Implementation
Add skill directory to path and import:
```python
import sys
sys.path.insert(0, '/path/to/skills/browsing-bluesky') # or use .claude/skills symlink path
from browsing_bluesky import (
# Core browsing
search_posts, get_user_posts, get_profile, get_feed_posts, sample_firehose,
get_thread, get_quotes, get_likes, get_reposts,
get_followers, get_following, search_users,
# Trending
get_trending, get_trending_topics,
# Account analysis
get_all_following, get_all_followers, extract_post_text,
extract_keywords, analyze_account, analyze_accounts,
# Authentication utilities
is_authenticated, get_authenticated_user, clear_session
)
```
## Authentication (Optional)
Authentication enables personalized feeds (like Paper Skygest) that require knowing who's asking.
### Setup
1. Create an app password at Bluesky: **Settings → Privacy and Security → App Passwords**
2. Set environment variables:
```bash
export BSKY_HANDLE="yourhandle.bsky.social"
export BSKY_APP_PASSWORD="xxxx-xxxx-xxxx-xxxx"
```
### Behavior
- **Transparent**: All functions work identically with or without credentials
- **Automatic**: Auth headers are added opportunistically when credentials exist
- **Graceful**: Failed auth silently falls back to public access
- **Secure**: Tokens cached in memory only, never logged or persisted
### Check Auth Status
```python
if is_authenticated():
print(f"Logged in as: {get_authenticated_user()}")
else:
print("Using public access")
# Clear session if needed (e.g., switching accounts)
clear_session()
```
## Research Workflows
### Investigate a Topic
Use `search_posts()` with query syntax matching bsky.app advanced search:
- Basic terms: `event sourcing`
- Exact phrases: `"event sourcing"`
- User filter: `from:acairns.co.uk` or use `author=` param
- Date filter: `since:2025-01-01` or use `since=` param
- Hashtags, mentions, domain links: `#python mentions:user domain:github.com`
Combine query syntax with function params for complex searches.
### Monitor a User
1. Fetch profile with `get_profile(handle)` for context (bio, follower count, post count)
2. Get recent posts with `get_user_posts(handle, limit=N)`
3. For topic-specific user content, use `search_posts(query, author=handle)`
### Discover What's Trending
**Recommended workflow** — trending API first, firehose for deep dives:
#### 1. Quick scan with trending topics (~500 tokens)
```python
topics = get_trending_topics(limit=10)
# Returns: {topics: [{topic, display_name, description, link}, ...],
# suggested: [...]}
```
#### 2. Rich trends with post counts and actors
```python
trends = get_trending(limit=10)
for t in trends:
print(f"{t['display_name']} — {t['post_count']} posts ({t['status']})")
# Each trend includes: topic, display_name, link, started_at,
# post_count, status, category, actors
```
#### 3. Targeted exploration of selected trends
```python
posts = search_posts(trend["topic"], limit=25)
```
#### 4. Optional: Firehose for velocity monitoring or long-tail discovery
**Prerequisites**: Install Node.js dependencies once per session:
```bash
cd /home/claude && npm install ws https-proxy-agent 2>/dev/null
```
```python
data = sample_firehose(duration=30) # Full firehose sample
data = sample_firehose(duration=20, filter="python") # Filtered sample
```
Returns dict with keys:
- **window**: `{startTime, endTime, durationSeconds}` — sampling time range
- **stats**: `{totalReceived, totalPosts, postsPerSecond, filter, languages}` — volume metrics and language breakdown
- **topWords**: `[[word, count], ...]` — top 50 words (count >= 3)
- **topPhrases**: `[[bigram, count], ...]` — top 30 bigrams (count >= 2)
- **topTrigrams**: `[[trigram, count], ...]` — top 20 trigrams (count >= 2)
- **entities**: `[[entity, count], ...]` — top 25 handles/hashtags (count >= 2)
- **samplePosts**: `[{text, altTexts, hasImages}, ...]` — first 50 matching posts
### Read Feeds and Lists
`get_feed_posts()` accepts:
- List URLs: `https://bsky.app/profile/austegard.com/lists/3lankcdrlip2f`
- Feed URLs: `https://bsky.app/profile/did:plc:xxx/feed/feedname`
- AT-URIs: `at://did:plc:xxx/app.bsky.graph.list/xyz`
The function extracts the AT-URI from URLs automatically.
### Explore a Thread
Fetch full thread context for a post with parents and replies:
```python
thread = get_thread("https://bsky.app/profile/user/post/xyz", depth=10)
# Returns: {post: {...}, parent: {...}, replies: [...]}
```
### Find Quote Posts
Discover posts that quote a specific post:
```python
quotes = get_quotes("https://bsky.app/profile/user/post/xyz")
for q in quotes:
print(f"@{q['author_handle']}: {q['text'][:80]}")
```
### Analyze Engagement
Get users who engaged with a post:
```python
likes = get_likes(post_url)
reposts = get_reposts(post_url)
# Accepts both URLs and AT-URIs
likes = get_likes("at://did:plc:.../app.bsky.feed.post/...")
```
### Read Embed Images
Every parsed post carries an `images` field — a list of
`{alt, url, transcription}` dicts, one per embed image. The legacy
`image_alts: list[str]` field is preserved (non-empty alts only).
When alt text is *missing* and the image content matters, opt in to model
transcription via the `transcribe` parameter on any post-fetch function
(`get_user_posts`, `search_posts`, `get_feed_posts`, `get_thread`,
`get_quotes`):
```python
# Routine/bulk work (zeitgeist, inbox review, news scans) —
# gemini-2.5-flash-lite is the recommended default. Cheapest production
# model anywhere ($0.10/$0.40 per 1M tokens), ~95% accuracy on dense
# screenshots in May 2026 benchmarks:
posts = get_user_posts("ayourtch.bsky.social", limit=40, transcribe="gemini-lite")
# Token-perfect transcription, still cheap:
posts = get_user_posts(..., tGitHub repository access in containerized environments using REST API and credential detection. Use when git clone fails, or when accessing private repos/writing files via API.
Securely manages API credentials for multiple providers (Anthropic Claude, Google Gemini, GitHub). Use when skills need to access stored API keys for external service invocations.
Guidance for asking clarifying questions when user requests are ambiguous, have multiple valid approaches, or require critical decisions. Use when implementation choices exist that could significantly affect outcomes.
>-
>-
Generate progressive disclosure indexes for GitHub repositories to use as Claude project knowledge. Use when setting up projects referencing external documentation, creating searchable indexes of technical blogs or knowledge bases, combining multiple repos into one index, or when user mentions "index", "github repo", "project knowledge", or "documentation reference".
Analyze and categorize Bluesky accounts by topic using keyword extraction. Use when users mention Bluesky account analysis, following/follower lists, topic discovery, account curation, or network analysis.
Cross-context adversarial review for deliverables before shipping. Use when producing blog posts, technical recommendations, analysis briefs, code, or any artifact where accuracy matters more than speed. Triggers on "challenge this", "review before shipping", "adversarial pass", "stress test this".