diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..6baabcf --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,113 @@ +# CLAUDE.md + +This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. + +## Project Overview + +"Thought" is a Rust CLI application for real-time streaming and posting to the ATProto network using `stream.thought.blip` records. It functions like an IRC client for Bluesky, featuring a terminal UI (TUI) for live message streaming. + +**Key Technology Stack:** +- Rust with async/await (Tokio runtime) +- ATProto API for authentication and posting +- Jetstream WebSocket for real-time firehose +- Ratatui for terminal UI +- Custom record type: `stream.thought.blip` + +## Building and Running + +```bash +# Build (development) +cargo build + +# Build (release) +cargo build --release + +# Run tests +cargo test + +# Lint +cargo clippy + +# Run the CLI +./target/release/thought + +# Login first-time use +./target/release/thought login + +# Main TUI mode (recommended) +./target/release/thought stream +``` + +## Architecture + +### Module Structure + +- **main.rs**: CLI entry point, command routing, and authentication setup +- **client.rs**: ATProto HTTP client for authentication and posting blips +- **credentials.rs**: Secure credential and session storage (platform-specific config directories) +- **jetstream.rs**: WebSocket client for Jetstream firehose, DID resolution, and message filtering +- **tui.rs**: Ratatui-based terminal UI with scrollable message feed +- **interactive.rs**: Coordinator for TUI and simple REPL modes + +### Authentication Flow + +1. User provides handle/email and app password via `thought login` +2. Credentials stored in platform config directory (macOS: `~/Library/Application Support/com.thoughtstream.think/`) +3. Session tokens (accessJwt/refreshJwt) cached separately in `session.json` +4. Token refresh handled transparently by `create_authenticated_client()` in main.rs:133 +5. Failed refresh triggers re-login with stored credentials + +**Important**: Session management uses both `credentials.json` (username/password/PDS URI) and `session.json` (JWT tokens). The client automatically refreshes tokens before operations. + +### Real-time Message Flow + +1. TUI spawns Jetstream listener in background task +2. Listener connects to `wss://jetstream2.us-west.bsky.network/subscribe` with collection filter +3. Incoming commits filtered for `stream.thought.blip` collection and "create" operations +4. DIDs resolved to handles via `app.bsky.actor.getProfile` (with caching) +5. Messages sent through mpsc channel to TUI for rendering +6. User's own messages filtered out using stored DID + +### TUI Rendering + +- Messages displayed in **reverse chronological order** (newest first) +- Scrollable with arrow keys/PageUp/PageDown +- Auto-scrolls to bottom when at offset 0 +- User's own posts styled in bold green +- Connection status tracked via channel activity +- Maximum 1000 messages buffered + +## Development Notes + +### Credential Storage Paths +- macOS: `~/Library/Application Support/com.thoughtstream.think/` +- Linux: `~/.config/thoughtstream/think/` +- Windows: `%APPDATA%\thoughtstream\think\` + +### Token Refresh Logic +The client implements automatic token refresh (client.rs:106-137): +1. Attempt operation with current access token +2. On 401/ExpiredToken error, call `refresh_session()` +3. Retry operation with new token +4. If refresh fails, clear session and prompt re-login + +### Jetstream Connection +- Attempts simple connection first, then falls back to collection-filtered URL +- Reconnects with 5-second delay on failure +- Uses User-Agent: `think-cli/0.1.0` +- Filters messages on client side even with server-side collection filter + +### Publishing Blips +Records posted with structure: +```json +{ + "$type": "stream.thought.blip", + "content": "", + "createdAt": "" +} +``` +Posted to `com.atproto.repo.createRecord` with user's DID as repo. + +## Note on Julia Dependencies + +The `Project.toml` and `Manifest.toml` files are unrelated to the Rust project—likely leftover artifacts or separate experiments (Franklin static site generator). The actual Rust dependencies are in `Cargo.toml`. diff --git a/src/tui.rs b/src/tui.rs index 677ce22..d0bf244 100644 --- a/src/tui.rs +++ b/src/tui.rs @@ -249,6 +249,13 @@ async fn run_tui_loop( message, true, )); + + // Save updated session (in case token was refreshed) + if let Ok(store) = crate::credentials::CredentialStore::new() { + if let Some(session) = client.get_session() { + let _ = store.store_session(session); + } + } } Err(e) => { // Add error message