https://github.com/harveyrandall/bsky-cli
A command-line client for Bluesky
https://github.com/harveyrandall/bsky-cli
at-protocol atproto atproto-api authenticated-transfer-protocol bluesky bsky cli command-line command-line-interface decentralized javascript js node social social-media social-network terminal ts typescript websocket
Last synced: 2 months ago
JSON representation
A command-line client for Bluesky
- Host: GitHub
- URL: https://github.com/harveyrandall/bsky-cli
- Owner: harveyrandall
- License: mit
- Created: 2026-03-05T13:53:55.000Z (3 months ago)
- Default Branch: main
- Last Pushed: 2026-03-28T12:22:08.000Z (2 months ago)
- Last Synced: 2026-03-28T15:53:50.666Z (2 months ago)
- Topics: at-protocol, atproto, atproto-api, authenticated-transfer-protocol, bluesky, bsky, cli, command-line, command-line-interface, decentralized, javascript, js, node, social, social-media, social-network, terminal, ts, typescript, websocket
- Language: TypeScript
- Size: 323 KB
- Stars: 16
- Watchers: 0
- Forks: 0
- Open Issues: 4
-
Metadata Files:
- Readme: README.md
- Contributing: CONTRIBUTING.md
- License: LICENSE.md
Awesome Lists containing this project
README
# bsky-cli
[](https://github.com/harveyrandall/bsky-cli/actions/workflows/ci.yml)
[](https://www.npmjs.com/package/@harveyrandall/bsky-cli)
[](LICENSE.md)
[](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)