{"id":46561411,"url":"https://github.com/harveyrandall/bsky-cli","last_synced_at":"2026-04-02T19:14:13.912Z","repository":{"id":342683372,"uuid":"1173555330","full_name":"harveyrandall/bsky-cli","owner":"harveyrandall","description":"A command-line client for Bluesky","archived":false,"fork":false,"pushed_at":"2026-03-28T12:22:08.000Z","size":331,"stargazers_count":16,"open_issues_count":4,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-03-28T15:53:50.666Z","etag":null,"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"],"latest_commit_sha":null,"homepage":null,"language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/harveyrandall.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE.md","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-03-05T13:53:55.000Z","updated_at":"2026-03-28T12:22:11.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/harveyrandall/bsky-cli","commit_stats":null,"previous_names":["harveyrandall/bsky-cli"],"tags_count":19,"template":false,"template_full_name":null,"purl":"pkg:github/harveyrandall/bsky-cli","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/harveyrandall%2Fbsky-cli","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/harveyrandall%2Fbsky-cli/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/harveyrandall%2Fbsky-cli/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/harveyrandall%2Fbsky-cli/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/harveyrandall","download_url":"https://codeload.github.com/harveyrandall/bsky-cli/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/harveyrandall%2Fbsky-cli/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31314167,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-02T12:59:32.332Z","status":"ssl_error","status_checked_at":"2026-04-02T12:54:48.875Z","response_time":89,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["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"],"created_at":"2026-03-07T06:00:37.182Z","updated_at":"2026-04-02T19:14:13.905Z","avatar_url":"https://github.com/harveyrandall.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# bsky-cli\n\n\u003c!-- badges --\u003e\n[![CI](https://github.com/harveyrandall/bsky-cli/actions/workflows/ci.yml/badge.svg)](https://github.com/harveyrandall/bsky-cli/actions/workflows/ci.yml)\n[![npm version](https://img.shields.io/npm/v/@harveyrandall/bsky-cli)](https://www.npmjs.com/package/@harveyrandall/bsky-cli)\n[![license](https://img.shields.io/github/license/harveyrandall/bsky-cli)](LICENSE.md)\n[![downloads](https://img.shields.io/npm/dm/@harveyrandall/bsky-cli)](https://www.npmjs.com/package/@harveyrandall/bsky-cli)\n\n\u003e [!CAUTION]\n\u003e **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.\n\n\u003e [!IMPORTANT]\n\u003e **DM support in v1.9.0 has a proxy routing bug** that causes `bsky dm read \u003chandle\u003e` and `bsky dm send \u003chandle\u003e` 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.\n\nA command-line client for [Bluesky](https://bsky.app), built with TypeScript.\n\n## Install\n\n### npm / yarn / pnpm / bun\n\nRequires Node.js \u003e= 22.\n\n```bash\nnpm install -g @harveyrandall/bsky-cli\n# or\nyarn global add @harveyrandall/bsky-cli\n# or\npnpm add -g @harveyrandall/bsky-cli\n# or\nbun add -g @harveyrandall/bsky-cli\n```\n\n### Homebrew (macOS / Linux)\n\n```bash\nbrew install harveyrandall/bsky-cli/bsky-cli\n```\n\n### Download a binary\n\nStandalone binaries for macOS, Linux, and Windows are attached to every\n[GitHub Release](https://github.com/harveyrandall/bsky-cli/releases).\n\n\u003cdetails\u003e\n\u003csummary\u003eBuild from source\u003c/summary\u003e\n\n```bash\ngit clone https://github.com/harveyrandall/bsky-cli.git\ncd bsky-cli\ncorepack enable\nyarn install\nyarn build\nyarn link:global   # registers `bsky` globally\n```\n\nTo uninstall:\n\n```bash\nyarn unlink:global\n```\n\n\u003c/details\u003e\n\n## Authentication\n\n### Interactive login\n\n```bash\nbsky login alice.bsky.social            # prompts for password (hidden input)\nbsky login alice.bsky.social mypassword  # or pass directly\n```\n\n### Environment variables\n\nAll configuration can be set via environment variables, useful for CI and scripts:\n\n| Variable | Description |\n|----------|-------------|\n| `BSKY_HANDLE` | Bluesky handle |\n| `BSKY_PASSWORD` | App password |\n| `BSKY_HOST` | PDS host URL (default: `https://bsky.social`) |\n| `BSKY_BGS` | BGS host URL (default: `https://bsky.network`) |\n| `BSKY_PROFILE` | Profile name (same as `--profile`) |\n| `BSKY_CONFIG` | Path to config file (same as `--config`) |\n\nPrecedence: CLI args \u003e environment variables \u003e config file \u003e defaults.\n\n```bash\n# No login needed - authenticate directly from env\nBSKY_HANDLE=alice.bsky.social BSKY_PASSWORD=secret bsky tl\n```\n\n### Piped input\n\n```bash\necho \"$APP_PASSWORD\" | bsky login alice.bsky.social\n```\n\n### Multiple accounts\n\nUse `--profile` / `-p` to manage separate accounts:\n\n```bash\nbsky login alice.bsky.social -p personal\nbsky login bob.bsky.social -p work\n\nbsky tl -p personal\nbsky tl -p work\nbsky -p ? tl              # list all profiles\n```\n\n## Data storage\n\nbsky-cli stores session tokens (never passwords) in platform-appropriate locations:\n\n| Platform | Default path | Override |\n|----------|-------------|----------|\n| **macOS** | `~/Library/Application Support/bsky-cli/` | `$XDG_CONFIG_HOME/bsky-cli/` |\n| **Linux** | `~/.config/bsky-cli/` | `$XDG_CONFIG_HOME/bsky-cli/` |\n| **Windows** | `%APPDATA%\\bsky-cli\\` | `$XDG_CONFIG_HOME/bsky-cli/` |\n\nFiles stored:\n- `session.json` — session tokens (did, handle, accessJwt, refreshJwt) with `0o600` permissions\n- `session-{profile}.json` — per-profile sessions\n- `drafts/` — locally saved drafts\n- `scheduled/` — scheduled posts (one JSON file per post)\n\nWhere available, session tokens are also stored in the OS keychain\n(macOS Keychain, GNOME Keyring/libsecret, Windows Credential Manager)\nwith filesystem as fallback.\n\nPasswords are **never** saved to disk. They are used only during `bsky login`\nto obtain session tokens, then discarded from memory.\n\n## Commands\n\n### Feed\n\n```\nbsky timeline|tl [-H handle] [-n count]\nbsky stream [--cursor] [-H handle] [--pattern regex] [--pattern-flags flags]\nbsky thread \u003curi\u003e [-n depth]\n```\n\n### Posting\n\n```\nbsky post \u003ctext\u003e [--stdin] [-i image...] [--image-alt alt...] [--video path] [--video-alt alt]\nbsky reply \u003curi\u003e \u003ctext\u003e\nbsky quote \u003curi\u003e \u003ctext\u003e\nbsky delete \u003curi...\u003e\n```\n\n### Threads\n\n```\nbsky create-thread \u003ctext\u003e [--stdin] [--thread-label] [--draft] [--no-preview]\n                          [--split-on \u003cmarker\u003e] [--skip-validation]\n```\n\nSplits long text into a thread. Uses `///` as the default manual split marker:\n\n```bash\nbsky create-thread \"First post /// Second post /// Third post\"\nbsky create-thread \"Part A --- Part B\" --split-on \"---\"\nbsky create-thread --stdin \u003c essay.txt --thread-label\n```\n\n### Drafts\n\n```\nbsky drafts list\nbsky drafts show \u003cid\u003e\nbsky drafts send \u003cid\u003e\nbsky drafts delete \u003cid\u003e\n```\n\n### Scheduling\n\n```\nbsky schedule post \u003ctext\u003e [--repeat freq] [--times count]\nbsky schedule list|ls [-n count] [-a] [-o asc|desc]\nbsky schedule edit [index]\nbsky schedule delete|rm [index]\nbsky schedule run\nbsky schedule watch [--interval cron]\nbsky schedule enable [--interval minutes]\nbsky schedule disable\nbsky schedule status\nbsky schedule uninstall\n```\n\n### Engagement\n\n```\nbsky like \u003curi...\u003e\nbsky unlike \u003curi...\u003e\nbsky likes \u003curi\u003e\nbsky repost \u003curi...\u003e\nbsky remove-repost|unrepost \u003curi...\u003e\nbsky reposts \u003curi\u003e\n```\n\n### Bookmarks\n\n```\nbsky bookmarks create \u003curi...\u003e\nbsky bookmarks delete \u003curi...\u003e\nbsky bookmarks get [-n count]\n```\n\n### Direct Messages\n\n```\nbsky dm list [-n count] [--unread] [--requests]\nbsky dm read \u003chandle-or-convo-id\u003e [-n count]\nbsky dm send \u003chandle\u003e \u003ctext...\u003e [--stdin]\nbsky dm delete \u003cconvo-id\u003e \u003cmessage-id\u003e\nbsky dm mute \u003cconvo-id\u003e\nbsky dm unmute \u003cconvo-id\u003e\nbsky dm accept \u003cconvo-id\u003e\nbsky dm mark-read [convo-id] [--all]\n```\n\n### Social\n\n```\nbsky follow \u003chandle...\u003e\nbsky unfollow \u003chandle...\u003e\nbsky follows [-H handle]\nbsky followers [-H handle]\nbsky block \u003chandle...\u003e\nbsky unblock \u003chandle...\u003e\nbsky blocks\nbsky mute \u003chandle...\u003e\n```\n\n### Discovery\n\n```\nbsky search \u003cterms...\u003e [-n count]\nbsky search-users \u003cterms...\u003e [-n count]\n```\n\n### Account\n\n```\nbsky profile [-H handle]\nbsky profile-update [displayname] [description] [--avatar file] [--banner file]\nbsky session\nbsky notifs|notification [-a]\nbsky invite-codes [--used]\nbsky app-password list|add|revoke\nbsky report \u003chandle\u003e [--comment text]\nbsky mod-list \u003chandles...\u003e [--name] [--desc]\n```\n\n### Utilities\n\n```\nbsky completions bash|zsh|fish\nbsky config init|path|show|edit\n```\n\n## Global flags\n\n| Flag | Description |\n|------|-------------|\n| `--json` | Output as JSON |\n| `-p, --profile \u003cname\u003e` | Use a named profile |\n| `-v, --verbose` | Verbose output |\n| `-c, --config \u003cpath\u003e` | Path to config file |\n| `--version` | Show version |\n\n## Configuration\n\nbsky-cli supports a TOML config file for persistent defaults. CLI flags always override config values.\n\n```bash\n# Create default config\nbsky config init\n\n# Open in your editor\nbsky config edit\n\n# Use a custom config file\nbsky -c /path/to/config.toml timeline\n```\n\nThe 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).\n\nPrecedence: **CLI flags \u003e environment variables \u003e config file \u003e defaults**\n\n## Shell completions\n\n```bash\n# Bash\nbsky completions bash \u003e\u003e ~/.bashrc\n\n# Zsh\nbsky completions zsh \u003e\u003e ~/.zshrc\n\n# Fish\nbsky completions fish \u003e ~/.config/fish/completions/bsky.fish\n```\n\n## Development\n\n```bash\nyarn dev             # run via tsx (no build needed)\nyarn build           # build to dist/\nyarn typecheck       # tsc --noEmit\nyarn test:run        # run tests once\nyarn test:coverage   # run tests with coverage\nyarn link:global     # build + register globally\nyarn unlink:global   # remove global symlink\n```\n\nSee [CONTRIBUTING.md](CONTRIBUTING.md) for the full contributor guide.\n\n## Roadmap\n\n- [x] Threads with automatic and manual splitting\n- [x] Drafts with offline sync and partial failure recovery\n- [x] Secure credential storage (OS keychain + session tokens, no plaintext passwords)\n- [x] Scheduled and recurring posts with cross-platform automation\n- [x] Direct messages (list, read, send, mute, accept requests)\n- [ ] List creation and management\n- [ ] Starter packs\n- [ ] Moderation lists\n- [ ] Post labels\n- [ ] Auto alt-text for images and videos\n- [ ] OAuth login support\n- [ ] Docker BuildKit for standalone binary builds\n\n## License\n\n[MIT](LICENSE.md)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fharveyrandall%2Fbsky-cli","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fharveyrandall%2Fbsky-cli","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fharveyrandall%2Fbsky-cli/lists"}