- TypeScript 99.9%
- JavaScript 0.1%
Port from social-app: no AT Protocol lexicon involved, just building a translate.google.com URL and opening it in the browser (source auto-detected, target guessed from the system locale). Bound to `T` in Timeline/Thread/Profile/SearchView (posts tab). |
||
|---|---|---|
| .tangled/workflows | ||
| public | ||
| src | ||
| .gitignore | ||
| .prettierignore | ||
| .prettierrc.json | ||
| eslint.config.js | ||
| LICENSE | ||
| package.json | ||
| pnpm-lock.yaml | ||
| pnpm-workspace.yaml | ||
| README.md | ||
| tsconfig.json | ||
| vitest.config.ts | ||
bsky-tui
A keyboard-driven terminal UI client for Bluesky / AT Protocol, inspired by Discordo.
Built with TypeScript + Ink (React-style TUI). Authenticates via AT Protocol OAuth — no App Passwords required.
Status: MVP complete. Daily-driver usable. Actively developed.
Features
- Timeline — home feed, custom feeds, and lists via a unified feed picker
- Threads — full reply tree with keyboard navigation
- Compose — new posts, replies, and quote posts with multiline editor
- Reactions — like and repost with optimistic updates
- Notifications — mentions, likes, reposts, follows
- Direct messages — 1:1 and group chat (AT Protocol DMs)
- Search — posts and people; follow/unfollow from results
- Profile — bio, post history, follow/mute/block actions
- Follows — follower and following lists
- Mouse support — click posts, buttons, and nav items throughout
- Keyboard-first — vim-style navigation; full keybind reference via
? - OSC 8 hyperlinks — clickable URLs, mentions, and hashtags in supporting terminals
Installation
Requirements: Node.js ≥ 18, pnpm
git clone https://knot.soverth.blue/did:plc:mwgzrmy3nvvpd2qajuvvfiu6/bsky-tui
cd bsky-tui
pnpm install
pnpm build
node dist/index.js
Authentication
bsky-tui uses AT Protocol OAuth (PKCE + PAR + DPoP). It acts as a public native client — no client secret, no App Password.
Hosted mode (default)
The OAuth client_id is https://soverth.blue/bsky-tui/client-metadata.json. This file must be publicly reachable before OAuth will work. Once the metadata URL is live:
pnpm dev # opens a browser tab for the OAuth flow
Loopback mode (local dev / no hosted metadata yet)
BSKY_TUI_OAUTH_MODE=loopback pnpm dev
Loopback mode starts a local HTTP server to complete the redirect without requiring a hosted client_id.
Keybindings
| Key | Action |
|---|---|
j / k |
Move down / up |
Enter |
Open thread |
l / r |
Like / repost |
c |
Compose new post |
n |
Reply |
q |
Quote post |
p |
Open profile |
Tab |
Feed picker |
N |
Notifications |
M |
Messages (DMs) |
/ |
Search |
F |
Follows |
f |
Follow / unfollow |
g / G |
Jump to top / bottom |
? |
Help screen |
Ctrl+q |
Quit |
Press ? inside the app for the full reference.
Development
pnpm install
pnpm dev # run with tsx (hot-ish reload via restart)
pnpm typecheck # tsc --noEmit
pnpm lint # eslint
pnpm format # prettier --write
pnpm test # vitest
pnpm build # tsc → dist/
CI runs on Tangled Spindle (.tangled/workflows/ci.yaml): typecheck → lint → format check → test → build.
Architecture
The codebase follows a strict layered architecture:
src/
core/ # models, ports (interfaces), use-cases, mappers — zero framework deps
services/ # AT Protocol API, OAuth, storage — implements core ports
state/ # useReducer + Context slices per feature
ui/ # Ink components, views, hooks
keybindings/ # key definitions
shared/ # tiny utilities
wiring.ts # composition root
Business logic in core/ is framework-agnostic and unit-tested with Vitest. UI components use ink-testing-library.
Design documents
| Doc | Contents |
|---|---|
docs/01-requirements.md |
Requirements |
docs/02-features.md |
Feature list and screens |
docs/03-architecture.md |
Architecture and data flow |
docs/04-directory-structure.md |
Directory layout |
docs/05-roadmap.md |
Phased roadmap |