When GraphQL endpoints return 404 and fall back to REST API v1.1,
pagination now works correctly by:
- Accepting cursor parameter in getFollowersViaRest and getFollowingViaRest
- Passing cursor to REST API requests
- Extracting next_cursor_str from REST responses
- Returning nextCursor in the result for proper --all pagination
- bird follow <username-or-id>: follow a user by handle or ID
- bird unfollow <username-or-id>: unfollow a user by handle or ID
Uses REST API /1.1/friendships/create.json and /destroy.json with GraphQL fallback.
Accepts @handles, bare usernames, or numeric user IDs.
Twitter API sometimes returns partial errors (e.g., is_translatable field
failures) alongside valid thread data. Previously, any error caused the
entire request to fail even when usable data was present.
Now we check if threaded_conversation_with_injections_v2.instructions
exists before failing on errors, allowing successful thread fetching
despite non-fatal API errors.
Co-Authored-By: Claude Opus 4.5 <[email protected]>
Library changes:
- Add getAllLikes(options) method for fetching all likes with cursor support
- Refactor getLikes to use private getLikesPaged helper
- Return nextCursor for resumable pagination
- Support maxPages option to limit pages fetched
CLI changes:
- Add --all flag to fetch all likes
- Add --max-pages to limit pagination
- Add --cursor to resume from previous fetch
- Output nextCursor in JSON mode for resumable pagination
This brings likes to feature parity with bookmarks.
Co-Authored-By: Claude Opus 4.5 <[email protected]>
# Conflicts:
# src/commands/users.ts
Adds --all, --max-pages, and --cursor options to search command, enabling users to fetch all search results through automatic pagination. Follows the same pattern as bookmarks pagination for consistency.
Completely rewrites the news command to fetch from multiple Explore tabs
(For You, News, Sports, Entertainment) using GenericTimelineById GraphQL
API instead of just the ExplorePage initialTimeline. This delivers 15+
AI-curated headlines instead of 3, a 5x improvement in content discovery.
Key Changes:
- Add GenericTimelineById query ID support with auto-refresh capability
- Implement multi-tab fetching with cross-tab headline deduplication
- Add CLI flags for granular tab filtering (--for-you, --news-only,
--sports, --entertainment, --trending-only)
- Fetch from 4 tabs by default (excludes trending to reduce noise)
- Add early stopping optimization when count is reached
- Handle tab-level errors gracefully without failing entire request
Implementation:
- Added TIMELINE_IDS constant with base64 timeline identifiers
- Created fetchTimelineTab() method for GenericTimelineById requests
- Created parseTimelineTabItems() for new response structure
- Removed old parseNewsItems() and extractNewsItemsFromInstructions()
- Updated all 7 tests to mock GenericTimelineById responses
API Changes:
- NewsFetchOptions: Added `tabs?: ExploreTab[]` option
- ExploreTab type exported for library consumers
- Backward compatible - existing code continues to work
Documentation:
- Added comprehensive "News & Trending" section with examples
- Updated command reference with all new flags
- Added library usage examples with tab filtering
- Updated JSON schema documentation
Testing:
- All 268 tests passing
- Real-world verified: fetches 15+ AI headlines across tabs
- Tab filtering verified: --sports, --entertainment, etc all work
Co-Authored-By: Claude Sonnet 4.5 <[email protected]>
Add comprehensive news/trending feature that fetches AI-generated news
headlines from Twitter's "For You" page. This feature specifically
targets the AI-curated news items that Twitter displays under "Today's
News" section, not generic trending hashtags.
- Uses Twitter's ExplorePage GraphQL API endpoint to access the Explore
page timeline data where AI headlines are located
- AI headlines are identified by the explicit `is_ai_trend: true` flag
in the API response
- Fallback heuristic detection for full-sentence headlines (5+ words)
with "News" or time indicators in social context
- Headlines are found in "stories-*" modules within the initialTimeline
structure under a "Today's News" header
- Follows the existing mixin pattern (withNews) for composability
- Supports both AI-only filtering and mixed trending/AI results
- `bird news` - Fetch news and trending topics (mixed results)
- `bird news --ai-only` - Fetch ONLY AI-curated headlines
- `bird news -n <count>` - Limit number of results
- `bird news --json` - Output as JSON
- `bird news --json-full` - Include raw API response
- `bird news --with-tweets` - Enrich with related tweets
- `bird trending` - Alias for news command
- AI headlines are clearly marked with "AI · " category prefix
- Automatic deduplication of duplicate headlines
- Rich formatting with category, time, post count, and URLs
- src/commands/news.ts - CLI command implementation
- src/lib/twitter-client-news.ts - Core news fetching functionality
- tests/commands.news.test.ts - Command validation tests (7 tests)
- tests/twitter-client-coverage.news.test.ts - API coverage tests (7 tests)
- README.md - Added documentation for news command
- src/cli/program.ts - Registered news command
- src/lib/index.ts - Exported news-related types
- src/lib/twitter-client.ts - Integrated withNews mixin
- src/lib/query-ids.json - Added ExplorePage query ID
- src/lib/twitter-client-constants.ts - Added ExplorePage constant
- src/lib/twitter-client-features.ts - Added buildExploreFeatures()
- scripts/update-query-ids.ts - Added ExplorePage to update script
- All 268 existing tests continue to pass
- Added 14 new tests covering command validation and API functionality
- Tested with real Twitter data confirming AI headline detection
- All TypeScript compilation and linting checks pass
Co-Authored-By: Claude Sonnet 4.5 <[email protected]>
Add support for extracting rich content from X's long-form tweets,
including embedded code snippets, markdown blocks, quoted tweets,
and other structured content that was previously lost.
Changes:
- Add fieldToggles with withArticleRichContentState to TweetDetail API request
- Implement Draft.js content_state parser (renderContentState) that converts
blocks and entities to readable markdown format
- Add content_state type definition to GraphqlTweetResult
Supported content:
- Block types: paragraphs, headers, ordered/unordered lists, blockquotes
- Entity types: MARKDOWN (code blocks), DIVIDER, TWEET, LINK, IMAGE
No impact on regular tweets - rich content only adds payload when present.
Includes 20 unit tests for the parser and an opt-in live smoke test.
Co-authored-by: Christian Catalan <[email protected]>
Add --all, --max-pages, --delay, and --cursor options to both thread
and replies commands, enabling fetching of all conversation content
with rate-limit-friendly pagination.
Changes:
- Modified fetchTweetDetail() to accept optional cursor parameter
- Added getRepliesPaged() and getThreadPaged() methods with pagination loop
- Updated CLI commands with new options (--all, --max-pages, --delay, --cursor)
- Added TweetDetailPaginationOptions interface
- JSON output includes nextCursor for scripting/resumption
Options:
- --all: Fetch all pages (no hard limit)
- --max-pages <n>: Limit number of pages when using --all
- --delay <ms>: Delay between page fetches (default: 1000ms)
- --cursor <string>: Resume from a previous cursor
Follows patterns established by bookmarks command and PR #34 (user-tweets).
Co-authored-by: Christian Catalan <[email protected]>
Adds new CLI command to fetch tweets from any user's profile timeline.
Usage:
bird user-tweets @username [-n count] [--pages N] [--json]
Features:
- GraphQL UserByScreenName for username → userId resolution
- Cursor-based pagination with configurable delay (rate-limit safe)
- Hard cap of 10 pages to prevent abuse
- Input validation via normalizeHandle()
Includes 17 unit tests and live smoke test.
Co-authored-by: Christian Catalan <[email protected]>
The article type had title defined twice (at lines 83 and 135), causing
TypeScript compilation to fail with TS2300: Duplicate identifier 'title'.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <[email protected]>
- Use startsWith check instead of fragile length heuristic for
detecting full article body vs preview mode
- Add media display for quote tweets (🖼️/🎬/🔄 indicators)
- Add preview_text to GraphqlTweetResult type definition to avoid
type assertions
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <[email protected]>
Enhance the text output format to show more useful information:
- Article tweets: Show 📰 indicator with title + preview text in feeds,
full body when reading single tweets
- Quote tweets: Display quoted content with ┌─ QT @user formatting
- Media: Show 🖼️/🎬/🔄 indicators with URLs for photos/videos/GIFs
Also refactors single tweet read to use shared printTweets function
for consistent formatting.
Follow the pattern from twitter-client-timelines.ts where query ID
methods are private to the mixin that uses them, rather than protected
in the base class.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <[email protected]>
Add `bird home` command to fetch the authenticated user's home timeline.
- Supports "For You" feed (default) and "Following" feed (--latest)
- Uses HomeTimeline and HomeLatestTimeline GraphQL operations
- Includes count option (-n), JSON output, and pagination
Usage:
bird home # Get "For You" feed
bird home --latest # Get "Following" (chronological) feed
bird home -n 50 # Fetch 50 tweets
Adds --all, --max-pages, and --cursor options to list-timeline command, enabling users to fetch all tweets from a list through automatic pagination. Follows the same pattern as bookmarks pagination for consistency.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Sonnet 4.5 <[email protected]>
Use OSC 8 escape sequences to make tweet and list URLs clickable
in supported terminals (iTerm2, Ghostty, Kitty, WezTerm, VS Code, etc.).
- Add hyperlink() helper function in output.ts
- Apply hyperlinks in printTweets() for tweet URLs
- Apply hyperlinks in printLists() for list URLs
- Add hyperlinks flag to OutputConfig (auto-disabled for non-TTY)
- Automatically falls back to plain text in --plain mode or when piped
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <[email protected]>
Add pagination support to `bird following` and `bird followers` commands,
similar to existing pagination in search/bookmarks/likes.
Changes:
- Add `cursor` parameter to `getFollowing()` and `getFollowers()` client methods
- Return `nextCursor` in `FollowingResult` for pagination continuation
- Add `--cursor <cursor>` CLI option for manual pagination
- Add `--all` CLI flag to automatically fetch all pages
- Add `--max-pages <number>` option to limit pages when using --all
- Add input validation for --max-pages (requires --all or --cursor)
- Add deduplication using Set to prevent duplicate users
- Add 1-second delay between pages to avoid overwhelming the API
- Update `-n/--count` description to clarify it's per-page
- Add unit tests for cursor parameter and nextCursor response
Usage examples:
# Fetch first page (default 20 users)
bird following
# Fetch with specific page size
bird following -n 50
# Use cursor for next page
bird following --cursor "CURSOR_FROM_PREVIOUS"
# Fetch ALL following users automatically (with rate limiting)
bird following --all --json
# Limit to first 5 pages
bird following --all --max-pages 5
# Same options work for followers
bird followers --all
Note: REST API fallback does not support cursor pagination.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <[email protected]>
X/Twitter now requires vibe_api_enabled, responsive_web_text_conversations_enabled, tweetypie_unmention_optimization_enabled, and interactive_text_enabled to be explicitly set in lists API requests.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Sonnet 4.5 <[email protected]>