# bird 🐦 — fast X CLI for tweeting, replying, and reading `bird` is a fast X CLI for tweeting, replying, and reading via X/Twitter GraphQL (cookie auth). ## Disclaimer This project uses X/Twitter’s **undocumented** web GraphQL API (and cookie auth). X can change endpoints, query IDs, and anti-bot behavior at any time — **expect this to break without notice**. ## Install ```bash npm install -g @steipete/bird # or pnpm add -g @steipete/bird # or bun add -g @steipete/bird # one-shot (no install) bunx @steipete/bird whoami ``` Homebrew (macOS, prebuilt Bun binary): ```bash brew install steipete/tap/bird ``` ## Quickstart ```bash # Show the logged-in account bird whoami # Discover command help bird help whoami # Read a tweet (URL or ID) bird read https://x.com/user/status/1234567890123456789 bird 1234567890123456789 --json # Thread + replies bird thread https://x.com/user/status/1234567890123456789 bird replies 1234567890123456789 # Search + mentions bird search "from:steipete" -n 5 bird mentions -n 5 bird mentions --user @steipete -n 5 # Bookmarks bird bookmarks -n 5 bird bookmarks --folder-id 123456789123456789 -n 5 # https://x.com/i/bookmarks/ # Likes bird likes -n 5 # Following (who you follow) bird following -n 20 bird following --user 12345678 -n 10 # by user ID # Followers (who follows you) bird followers -n 20 bird followers --user 12345678 -n 10 # by user ID # Refresh GraphQL query IDs cache (no rebuild) bird query-ids --fresh ``` ## Commands - `bird tweet ""` — post a new tweet. - `bird reply ""` — reply to a tweet using its ID or URL. - `bird help [command]` — show help (or help for a subcommand). - `bird query-ids [--fresh] [--json]` — inspect or refresh cached GraphQL query IDs. - `bird read [--json]` — fetch tweet content as text or JSON. - `bird [--json]` — shorthand for `read` when only a URL or ID is provided. - `bird replies [--json]` — list replies to a tweet. - `bird thread [--json]` — show the full conversation thread. - `bird search "" [-n count] [--json]` — search for tweets matching a query. - `bird mentions [-n count] [--user @handle] [--json]` — find tweets mentioning a user (defaults to the authenticated user). - `bird bookmarks [-n count] [--folder-id id] [--json]` — list your bookmarked tweets (or a specific bookmark folder). - `bird likes [-n count] [--json]` — list your liked tweets. - `bird following [--user ] [-n count] [--json]` — list users that you (or another user) follow. - `bird followers [--user ] [-n count] [--json]` — list users that follow you (or another user). - `bird whoami` — print which Twitter account your cookies belong to. - `bird check` — show which credentials are available and where they were sourced from. Global options: - `--timeout `: abort requests after the given timeout (milliseconds). - `--cookie-timeout `: cookie extraction timeout for keychain/OS helpers (milliseconds). - `--quote-depth `: max quoted tweet depth in JSON output (default: 1; 0 disables). - `--plain`: stable output (no emoji, no color). - `--no-emoji`: disable emoji output. - `--no-color`: disable ANSI colors (or set `NO_COLOR=1`). - `--cookie-source `: choose browser cookie source (repeatable; order matters). ## Authentication (GraphQL) GraphQL mode uses your existing X/Twitter web session (no password prompt). It sends requests to internal X endpoints and authenticates via cookies (`auth_token`, `ct0`). Write operations: - `tweet`/`reply` primarily use GraphQL (`CreateTweet`). - If GraphQL returns error `226` (“automated request”), `bird` falls back to the legacy `statuses/update.json` endpoint. `bird` resolves credentials in this order: 1. CLI flags: `--auth-token`, `--ct0` 2. Environment variables: `AUTH_TOKEN`, `CT0` (fallback: `TWITTER_AUTH_TOKEN`, `TWITTER_CT0`) 3. Browser cookies via `@steipete/sweet-cookie` (override via `--cookie-source` order) Browser cookie sources: - Safari: `~/Library/Cookies/Cookies.binarycookies` (fallback: `~/Library/Containers/com.apple.Safari/Data/Library/Cookies/Cookies.binarycookies`) - Chrome: `~/Library/Application Support/Google/Chrome//Cookies` - Firefox: `~/Library/Application Support/Firefox/Profiles//cookies.sqlite` ## Config (JSON5) Config precedence: CLI flags > env vars > project config > global config. - Global: `~/.config/bird/config.json5` - Project: `./.birdrc.json5` Example `~/.config/bird/config.json5`: ```json5 { // Cookie source order for browser extraction (string or array) cookieSource: ["firefox", "safari"], firefoxProfile: "default-release", cookieTimeoutMs: 20000, timeoutMs: 20000, quoteDepth: 1 } ``` Environment shortcuts: - `BIRD_TIMEOUT_MS` - `BIRD_COOKIE_TIMEOUT_MS` - `BIRD_QUOTE_DEPTH` ## Output - `--json` prints raw tweet objects for read/replies/thread/search/mentions/bookmarks. - `read` returns full text for Notes and Articles when present. - Use `--plain` for stable, script-friendly output (no emoji, no color). ### JSON Schema When using `--json`, tweet objects include: | Field | Type | Description | |-------|------|-------------| | `id` | string | Tweet ID | | `text` | string | Full tweet text (includes Note/Article content when present) | | `author` | object | `{ username, name }` | | `authorId` | string? | Author's user ID | | `createdAt` | string | Timestamp | | `replyCount` | number | Number of replies | | `retweetCount` | number | Number of retweets | | `likeCount` | number | Number of likes | | `conversationId` | string | Thread conversation ID | | `inReplyToStatusId` | string? | Parent tweet ID (present if this is a reply) | | `quotedTweet` | object? | Embedded quote tweet (same schema; depth controlled by `--quote-depth`) | When using `--json` with `following`/`followers`, user objects include: | Field | Type | Description | |-------|------|-------------| | `id` | string | User ID | | `username` | string | Username/handle | | `name` | string | Display name | | `description` | string? | User bio | | `followersCount` | number? | Followers count | | `followingCount` | number? | Following count | | `isBlueVerified` | boolean? | Blue verified flag | | `profileImageUrl` | string? | Profile image URL | | `createdAt` | string? | Account creation timestamp | ## Query IDs (GraphQL) X rotates GraphQL “query IDs” frequently. Each GraphQL operation is addressed as: - `operationName` (e.g. `TweetDetail`, `CreateTweet`) - `queryId` (rotating ID baked into X’s web client bundles) `bird` ships with a baseline mapping in `src/lib/query-ids.json` (copied into `dist/` on build). At runtime, it can refresh that mapping by scraping X’s public web client bundles and caching the result on disk. Runtime cache: - Default path: `~/.config/bird/query-ids-cache.json` - Override path: `BIRD_QUERY_IDS_CACHE=/path/to/file.json` - TTL: 24h (stale cache is still used, but marked “not fresh”) Auto-recovery: - On GraphQL `404` (query ID invalid), `bird` forces a refresh once and retries. - For `TweetDetail`/`SearchTimeline`, `bird` also rotates through a small set of known fallback IDs to reduce breakage while refreshing. Refresh on demand: ```bash bird query-ids --fresh ``` Exit codes: - `0`: success - `1`: runtime error (network/auth/etc) - `2`: invalid usage/validation (e.g. bad `--user` handle) ## Version `bird --version` prints `package.json` version plus current git sha when available, e.g. `0.3.0 (3df7969b)`. ## Media uploads - Attach media with `--media` (repeatable) and optional `--alt` per item. - Up to 4 images/GIFs, or 1 video (no mixing). Supported: jpg, jpeg, png, webp, gif, mp4, mov. - Images/GIFs + 1 video supported (uploads via Twitter legacy upload endpoint + cookies; video may take longer to process). Example: ```bash bird tweet "hi" --media img.png --alt "desc" ``` ## Development ```bash cd ~/Projects/bird pnpm install pnpm run build # dist/ + bun binary pnpm run build:dist # dist/ only pnpm run build:binary pnpm run dev tweet "Test" pnpm run dev -- --plain check pnpm test pnpm run lint ``` ## Notes - GraphQL uses internal X endpoints and can be rate limited (429). - Query IDs rotate; refresh at runtime with `bird query-ids --fresh` (or update the baked baseline via `pnpm run graphql:update`).