# bsky-cli [![CI](https://github.com/harveyrandall/bsky-cli/actions/workflows/ci.yml/badge.svg)](https://github.com/harveyrandall/bsky-cli/actions/workflows/ci.yml) [![npm version](https://img.shields.io/npm/v/@harveyrandall/bsky-cli)](https://www.npmjs.com/package/@harveyrandall/bsky-cli) [![license](https://img.shields.io/github/license/harveyrandall/bsky-cli)](LICENSE.md) [![downloads](https://img.shields.io/npm/dm/@harveyrandall/bsky-cli)](https://www.npmjs.com/package/@harveyrandall/bsky-cli) > [!CAUTION] > **Versions 1.5.0 and earlier stored credentials in plaintext.** Upgrade to v1.6.0 or later immediately. If you were on an affected version, change your Bluesky app password after upgrading. > [!IMPORTANT] > **DM support in v1.9.0 has a proxy routing bug** that causes `bsky dm read ` and `bsky dm send ` to fail with "Method Not Implemented". Upgrade to v1.9.1 or later for the fix. See [Version Issues](https://harveyrandall.github.io/bsky-cli/version-issues/) for details. A command-line client for [Bluesky](https://bsky.app), built with TypeScript. ## Install ### npm / yarn / pnpm / bun Requires Node.js >= 22. ```bash npm install -g @harveyrandall/bsky-cli # or yarn global add @harveyrandall/bsky-cli # or pnpm add -g @harveyrandall/bsky-cli # or bun add -g @harveyrandall/bsky-cli ``` ### Homebrew (macOS / Linux) ```bash brew install harveyrandall/bsky-cli/bsky-cli ``` ### Download a binary Standalone binaries for macOS, Linux, and Windows are attached to every [GitHub Release](https://github.com/harveyrandall/bsky-cli/releases).
Build from source ```bash git clone https://github.com/harveyrandall/bsky-cli.git cd bsky-cli corepack enable yarn install yarn build yarn link:global # registers `bsky` globally ``` To uninstall: ```bash yarn unlink:global ```
## Authentication ### Interactive login ```bash bsky login alice.bsky.social # prompts for password (hidden input) bsky login alice.bsky.social mypassword # or pass directly ``` ### Environment variables All configuration can be set via environment variables, useful for CI and scripts: | Variable | Description | |----------|-------------| | `BSKY_HANDLE` | Bluesky handle | | `BSKY_PASSWORD` | App password | | `BSKY_HOST` | PDS host URL (default: `https://bsky.social`) | | `BSKY_BGS` | BGS host URL (default: `https://bsky.network`) | | `BSKY_PROFILE` | Profile name (same as `--profile`) | | `BSKY_CONFIG` | Path to config file (same as `--config`) | Precedence: CLI args > environment variables > config file > defaults. ```bash # No login needed - authenticate directly from env BSKY_HANDLE=alice.bsky.social BSKY_PASSWORD=secret bsky tl ``` ### Piped input ```bash echo "$APP_PASSWORD" | bsky login alice.bsky.social ``` ### Multiple accounts Use `--profile` / `-p` to manage separate accounts: ```bash bsky login alice.bsky.social -p personal bsky login bob.bsky.social -p work bsky tl -p personal bsky tl -p work bsky -p ? tl # list all profiles ``` ## Data storage bsky-cli stores session tokens (never passwords) in platform-appropriate locations: | Platform | Default path | Override | |----------|-------------|----------| | **macOS** | `~/Library/Application Support/bsky-cli/` | `$XDG_CONFIG_HOME/bsky-cli/` | | **Linux** | `~/.config/bsky-cli/` | `$XDG_CONFIG_HOME/bsky-cli/` | | **Windows** | `%APPDATA%\bsky-cli\` | `$XDG_CONFIG_HOME/bsky-cli/` | Files stored: - `session.json` — session tokens (did, handle, accessJwt, refreshJwt) with `0o600` permissions - `session-{profile}.json` — per-profile sessions - `drafts/` — locally saved drafts - `scheduled/` — scheduled posts (one JSON file per post) Where available, session tokens are also stored in the OS keychain (macOS Keychain, GNOME Keyring/libsecret, Windows Credential Manager) with filesystem as fallback. Passwords are **never** saved to disk. They are used only during `bsky login` to obtain session tokens, then discarded from memory. ## Commands ### Feed ``` bsky timeline|tl [-H handle] [-n count] bsky stream [--cursor] [-H handle] [--pattern regex] [--pattern-flags flags] bsky thread [-n depth] ``` ### Posting ``` bsky post [--stdin] [-i image...] [--image-alt alt...] [--video path] [--video-alt alt] bsky reply bsky quote bsky delete ``` ### Threads ``` bsky create-thread [--stdin] [--thread-label] [--draft] [--no-preview] [--split-on ] [--skip-validation] ``` Splits long text into a thread. Uses `///` as the default manual split marker: ```bash bsky create-thread "First post /// Second post /// Third post" bsky create-thread "Part A --- Part B" --split-on "---" bsky create-thread --stdin < essay.txt --thread-label ``` ### Drafts ``` bsky drafts list bsky drafts show bsky drafts send bsky drafts delete ``` ### Scheduling ``` bsky schedule post [--repeat freq] [--times count] bsky schedule list|ls [-n count] [-a] [-o asc|desc] bsky schedule edit [index] bsky schedule delete|rm [index] bsky schedule run bsky schedule watch [--interval cron] bsky schedule enable [--interval minutes] bsky schedule disable bsky schedule status bsky schedule uninstall ``` ### Engagement ``` bsky like bsky unlike bsky likes bsky repost bsky remove-repost|unrepost bsky reposts ``` ### Bookmarks ``` bsky bookmarks create bsky bookmarks delete bsky bookmarks get [-n count] ``` ### Direct Messages ``` bsky dm list [-n count] [--unread] [--requests] bsky dm read [-n count] bsky dm send [--stdin] bsky dm delete bsky dm mute bsky dm unmute bsky dm accept bsky dm mark-read [convo-id] [--all] ``` ### Social ``` bsky follow bsky unfollow bsky follows [-H handle] bsky followers [-H handle] bsky block bsky unblock bsky blocks bsky mute ``` ### Discovery ``` bsky search [-n count] bsky search-users [-n count] ``` ### Account ``` bsky profile [-H handle] bsky profile-update [displayname] [description] [--avatar file] [--banner file] bsky session bsky notifs|notification [-a] bsky invite-codes [--used] bsky app-password list|add|revoke bsky report [--comment text] bsky mod-list [--name] [--desc] ``` ### Utilities ``` bsky completions bash|zsh|fish bsky config init|path|show|edit ``` ## Global flags | Flag | Description | |------|-------------| | `--json` | Output as JSON | | `-p, --profile ` | Use a named profile | | `-v, --verbose` | Verbose output | | `-c, --config ` | Path to config file | | `--version` | Show version | ## Configuration bsky-cli supports a TOML config file for persistent defaults. CLI flags always override config values. ```bash # Create default config bsky config init # Open in your editor bsky config edit # Use a custom config file bsky -c /path/to/config.toml timeline ``` The config file lives at the platform default: `~/.config/bsky-cli/config.toml` (Linux), `~/Library/Application Support/bsky-cli/config.toml` (macOS), or `%APPDATA%/bsky-cli/config.toml` (Windows). Precedence: **CLI flags > environment variables > config file > defaults** ## Shell completions ```bash # Bash bsky completions bash >> ~/.bashrc # Zsh bsky completions zsh >> ~/.zshrc # Fish bsky completions fish > ~/.config/fish/completions/bsky.fish ``` ## Development ```bash yarn dev # run via tsx (no build needed) yarn build # build to dist/ yarn typecheck # tsc --noEmit yarn test:run # run tests once yarn test:coverage # run tests with coverage yarn link:global # build + register globally yarn unlink:global # remove global symlink ``` See [CONTRIBUTING.md](CONTRIBUTING.md) for the full contributor guide. ## Roadmap - [x] Threads with automatic and manual splitting - [x] Drafts with offline sync and partial failure recovery - [x] Secure credential storage (OS keychain + session tokens, no plaintext passwords) - [x] Scheduled and recurring posts with cross-platform automation - [x] Direct messages (list, read, send, mute, accept requests) - [ ] List creation and management - [ ] Starter packs - [ ] Moderation lists - [ ] Post labels - [ ] Auto alt-text for images and videos - [ ] OAuth login support - [ ] Docker BuildKit for standalone binary builds ## License [MIT](LICENSE.md)