ATlast — you'll never need to find your favorites on another platform again. Find your favs in the ATmosphere.
atproto
1# CLAUDE.md 2 3This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. 4 5# Project Instructions 6 7## Decision Graph Workflow 8 9**THIS IS MANDATORY. Log decisions IN REAL-TIME, not retroactively.** 10 11### The Core Rule 12 13``` 14BEFORE you do something -> Log what you're ABOUT to do 15AFTER it succeeds/fails -> Log the outcome 16CONNECT immediately -> Link every node to its parent 17AUDIT regularly -> Check for missing connections 18``` 19 20### Behavioral Triggers - MUST LOG WHEN: 21 22| Trigger | Log Type | Example | 23|---------|----------|---------| 24| User asks for a new feature | `goal` **with -p** | "Add dark mode" | 25| Choosing between approaches | `decision` | "Choose state management" | 26| About to write/edit code | `action` | "Implementing Redux store" | 27| Something worked or failed | `outcome` | "Redux integration successful" | 28| Notice something interesting | `observation` | "Existing code uses hooks" | 29 30### CRITICAL: Capture VERBATIM User Prompts 31 32**Prompts must be the EXACT user message, not a summary.** When a user request triggers new work, capture their full message word-for-word. 33 34**BAD - summaries are useless for context recovery:** 35```bash 36# DON'T DO THIS - this is a summary, not a prompt 37deciduous add goal "Add auth" -p "User asked: add login to the app" 38``` 39 40**GOOD - verbatim prompts enable full context recovery:** 41```bash 42# Use --prompt-stdin for multi-line prompts 43deciduous add goal "Add auth" -c 90 --prompt-stdin << 'EOF' 44I need to add user authentication to the app. Users should be able to sign up 45with email/password, and we need OAuth support for Google and GitHub. The auth 46should use JWT tokens with refresh token rotation. 47EOF 48 49# Or use the prompt command to update existing nodes 50deciduous prompt 42 << 'EOF' 51The full verbatim user message goes here... 52EOF 53``` 54 55**When to capture prompts:** 56- Root `goal` nodes: YES - the FULL original request 57- Major direction changes: YES - when user redirects the work 58- Routine downstream nodes: NO - they inherit context via edges 59 60**Updating prompts on existing nodes:** 61```bash 62deciduous prompt <node_id> "full verbatim prompt here" 63cat prompt.txt | deciduous prompt <node_id> # Multi-line from stdin 64``` 65 66Prompts are viewable in the TUI detail panel (`deciduous tui`) and web viewer. 67 68### ⚠️ CRITICAL: Maintain Connections 69 70**The graph's value is in its CONNECTIONS, not just nodes.** 71 72| When you create... | IMMEDIATELY link to... | 73|-------------------|------------------------| 74| `outcome` | The action/goal it resolves | 75| `action` | The goal/decision that spawned it | 76| `option` | Its parent decision | 77| `observation` | Related goal/action | 78 79**Root `goal` nodes are the ONLY valid orphans.** 80 81### Quick Commands 82 83```bash 84deciduous add goal "Title" -c 90 -p "User's original request" 85deciduous add action "Title" -c 85 86deciduous link FROM TO -r "reason" # DO THIS IMMEDIATELY! 87deciduous serve # View live (auto-refreshes every 30s) 88deciduous sync # Export for static hosting 89 90# Metadata flags 91# -c, --confidence 0-100 Confidence level 92# -p, --prompt "..." Store the user prompt (use when semantically meaningful) 93# -f, --files "a.rs,b.rs" Associate files 94# -b, --branch <name> Git branch (auto-detected) 95# --commit <hash|HEAD> Link to git commit (use HEAD for current commit) 96 97# Branch filtering 98deciduous nodes --branch main 99deciduous nodes -b feature-auth 100``` 101 102### ⚠️ CRITICAL: Link Commits to Actions/Outcomes 103 104**After every git commit, link it to the decision graph!** 105 106```bash 107git commit -m "feat: add auth" 108deciduous add action "Implemented auth" -c 90 --commit HEAD 109deciduous link <goal_id> <action_id> -r "Implementation" 110``` 111 112The `--commit HEAD` flag captures the commit hash and links it to the node. The web viewer will show commit messages, authors, and dates. 113 114### Git Commit Message Format 115 116**Keep commit messages clean and concise:** 117- NO "Generated with Claude Code" or similar by-lines 118- NO "Co-Authored-By:" lines 119- NO "Files updated:" sections or file lists 120- Use concise summary line describing the change 121- Add additional details if needed, but keep it focused 122 123**CRITICAL: Commit separate concerns separately** 124- Each commit should address ONE specific fix or feature 125- DO NOT bundle multiple unrelated changes into a single commit 126- If fixing multiple bugs, create separate commits for each bug fix 127- Example: Login typeahead fix, card spacing fix, and toast refactor = 3 commits 128 129**Example:** 130```bash 131git commit -m "remove deprecated 'followed' field and cleanup codebase 132 133Removed backward compatibility code for deprecated 'followed' field: 134- Removed from AtprotoMatch type 135- Updated 6 files to use only followStatus Record 136- Replaced simple boolean check with multi-lexicon support 137- Database schema preserved (no migration needed) 138 139Also removed empty 'nul' file created accidentally." 140``` 141 142### Git History & Deployment 143 144**CRITICAL: Include decision graph in code commits** 145 146Always run `deciduous sync` BEFORE committing code changes, then include the graph updates in the same commit: 147 148```bash 149# After making code changes and adding decision nodes: 150deciduous sync # Export graph 151git add src/your-file.ts docs/ # Stage code + graph 152git commit -m "your change" # Single commit with both 153``` 154 155**DO NOT create separate commits for graph updates.** The decision graph is part of the code history and should be committed together. 156 157```bash 158# Export graph AND git history for web viewer 159deciduous sync 160 161# This creates: 162# - docs/graph-data.json (decision graph) 163# - docs/git-history.json (commit info for linked nodes) 164``` 165 166To deploy to GitHub Pages: 1671. `deciduous sync` to export 1682. Push to GitHub 1693. Settings > Pages > Deploy from branch > /docs folder 170 171Your graph will be live at `https://<user>.github.io/<repo>/` 172 173### Branch-Based Grouping 174 175Nodes are auto-tagged with the current git branch. Configure in `.deciduous/config.toml`: 176```toml 177[branch] 178main_branches = ["main", "master"] 179auto_detect = true 180``` 181 182### Audit Checklist (Before Every Sync) 183 1841. Does every **outcome** link back to what caused it? 1852. Does every **action** link to why you did it? 1863. Any **dangling outcomes** without parents? 187 188### Session Start Checklist 189 190```bash 191deciduous nodes # What decisions exist? 192deciduous edges # How are they connected? Any gaps? 193git status # Current state 194``` 195 196### Multi-User Sync 197 198Share decisions across teammates: 199 200```bash 201# Export your branch's decisions 202deciduous diff export --branch feature-x -o .deciduous/patches/my-feature.json 203 204# Apply patches from teammates (idempotent) 205deciduous diff apply .deciduous/patches/*.json 206 207# Preview before applying 208deciduous diff apply --dry-run .deciduous/patches/teammate.json 209``` 210 211PR workflow: Export patch → commit patch file → PR → teammates apply. 212 213### API Trace Capture 214 215Capture Claude API traffic to correlate decisions with actual API work: 216 217```bash 218# Run Claude through the deciduous proxy 219deciduous proxy -- claude 220 221# View traces in TUI (press 't' for Trace view) 222deciduous tui 223 224# View traces in web viewer (click "Traces" tab) 225deciduous serve 226``` 227 228**Auto-linking**: When running through `deciduous proxy`, any `deciduous add` commands automatically link to the active API span. You'll see output like: 229 230``` 231Created action #42 "Implementing auth" [traced: span #7] 232``` 233 234This lets you see exactly which API calls produced which decisions - perfect for "vibe coding" visibility. 235 236**Trace commands:** 237```bash 238deciduous trace sessions # List all sessions 239deciduous trace spans <session_id> # List spans in a session 240deciduous trace link <session_id> <node_id> # Manual linking 241deciduous trace prune --days 30 # Cleanup old traces 242``` 243 244## Project Overview 245 246ATlast is a web application that helps users find their followed accounts from other social platforms (TikTok, Instagram, Twitter/X) on Bluesky/AT Protocol. Users upload their data export files, which are parsed for usernames, then searched in the AT Protocol network. 247 248## Development Commands 249 250### Monorepo Structure 251 252This project uses pnpm workspaces with three packages: 253- `packages/web` - React frontend 254- `packages/functions` - Netlify serverless functions 255- `packages/shared` - Shared TypeScript types 256 257### Local Development 258 259**IMPORTANT**: Due to Netlify CLI monorepo detection, you must use the `--filter` flag: 260 261```bash 262# Mock mode (frontend only, no backend/OAuth/database) 263pnpm run dev:mock 264 265# Full mode (with backend, OAuth, database) - RECOMMENDED 266npx netlify-cli dev --filter @atlast/web 267 268# Alternative: Update root package.json scripts to use npx netlify-cli 269pnpm run dev # If scripts are updated 270 271# Build for production 272pnpm run build 273 274# Initialize local database 275pnpm run init-db 276 277# Generate encryption keys for OAuth 278pnpm run generate-key 279``` 280 281**Note**: On Windows, `netlify` command may not work in Git Bash. Use `npx netlify-cli` instead, or run from Windows CMD/PowerShell. 282 283### Environment Configuration 284 285Two development modes supported: 286 287**Mock Mode** (.env.mock): 288- Set `VITE_LOCAL_MOCK=true` 289- No database or OAuth required 290- Uses MockApiAdapter for fake data 291 292**Full Mode** (.env): 293- Set `VITE_LOCAL_MOCK=false` 294- Requires PostgreSQL (local or Neon) 295- Requires OAuth keys and configuration 296- Must use `http://127.0.0.1:8888` (NOT localhost) for OAuth to work 297- See CONTRIBUTING.md for detailed setup 298 299## Architecture Overview 300 301### Frontend (React + TypeScript + Vite) 302 303**Core Structure:** 304- `src/pages/` - Page components (Login, Upload, Results, etc.) 305- `src/components/` - Reusable UI components 306- `src/lib/parsers/` - File parsing logic for different platforms 307- `src/lib/api/` - API client with adapter pattern (Real vs Mock) 308- `src/contexts/` - React contexts (SettingsContext for theme/preferences) 309- `src/hooks/` - Custom React hooks 310 311**API Client Pattern:** 312The app uses an adapter pattern for the API layer: 313- `src/lib/api/client.ts` - Factory that returns Real or Mock adapter based on ENV.IS_LOCAL_MOCK 314- `src/lib/api/adapters/RealApiAdapter.ts` - Calls Netlify Functions 315- `src/lib/api/adapters/MockApiAdapter.ts` - Returns fake data for frontend development 316- `src/lib/api/IApiClient.ts` - Interface both adapters implement 317 318**Platform Parsers:** 319- `src/lib/parsers/platformDefinitions.ts` - Defines parsing rules for each platform (Instagram, TikTok, etc.) 320- `src/lib/parsers/fileExtractor.ts` - Handles ZIP file uploads and extracts usernames 321- `src/lib/parsers/parserLogic.ts` - Implements extraction logic (HTML regex, JSON path traversal, TEXT regex) 322 323Each platform has multiple ParseRule entries defining: 324- `zipPath` - Location inside ZIP archive 325- `format` - "HTML" | "TEXT" | "JSON" 326- `rule` - Regex pattern string or JSON key path array 327 328### Backend (Netlify Functions) 329 330**Function Structure:** 331``` 332netlify/functions/ 333├── core/ 334│ ├── middleware/ # Auth, error handling, session security 335│ ├── types/ # Shared types 336│ ├── errors/ # Custom error classes 337│ └── config/ # Configuration 338├── infrastructure/ 339│ ├── oauth/ # OAuth client factory, stores 340│ ├── database/ # Database connection, service layer 341│ ├── cache/ # Caching utilities 342│ └── lexicons/ # AT Protocol lexicons 343├── services/ # Business logic (SessionService, FollowService, etc.) 344├── repositories/ # Data access layer 345└── utils/ # Shared utilities 346``` 347 348**Key Functions:** 349- `oauth-start.ts` / `oauth-callback.ts` - AT Protocol OAuth flow 350- `session.ts` - Session management and validation 351- `batch-search-actors.ts` - Searches multiple usernames on Bluesky (includes ranking algorithm) 352- `check-follow-status.ts` - Checks if user follows specific DIDs 353- `batch-follow-users.ts` - Bulk follow operations 354- `save-results.ts` - Persists search results to database 355- `get-uploads.ts` - Retrieves user's upload history 356 357**Authentication Pattern:** 358All protected endpoints use: 3591. `withAuthErrorHandling()` middleware wrapper 3602. `AuthenticatedHandler` type (provides `context.sessionId`, `context.did`, `context.event`) 3613. `SessionService.getAgentForSession()` to get authenticated AT Protocol agent 362 363**Database:** 364- PostgreSQL via Neon serverless 365- Accessed through `DatabaseService` (infrastructure/database/) 366- Repositories pattern for data access (repositories/) 367 368**OAuth:** 369- AT Protocol OAuth using `@atproto/oauth-client-node` 370- OAuth client factory creates client with session/state stores 371- Private key (ES256) stored in `OAUTH_PRIVATE_KEY` env var 372- Public JWK served at `/jwks` endpoint 373- Must use `127.0.0.1` (not localhost) for local OAuth redirects 374 375### UI/Styling 376 377**Tailwind CSS with dual theme support:** 378- Light mode: purple/cyan color scheme 379- Dark mode: cyan/purple inverted 380- All components use `dark:` variant classes 381 382**Color System (from CONTRIBUTING.md):** 383- Text: purple-950/cyan-50 (primary), purple-750/cyan-250 (secondary) 384- Borders: cyan-500/purple-500 with opacity variants 385- Buttons: orange-600 (primary CTA), slate-600/700 (secondary) 386- Backgrounds: white/slate-900 (primary), purple-50/slate-900 (secondary) 387- Selected states: cyan-50 border-cyan-500 / purple-950/30 border-purple-500 388- Accents: orange-500/amber-500 (badges, progress) 389 390**Key Patterns:** 391- Mobile-first responsive design 392- List virtualization with `@tanstack/react-virtual` for large result sets 393- Code splitting and lazy loading for pages 394- Error boundaries throughout the app 395- Loading skeletons for async operations 396 397### Search & Matching Algorithm 398 399**Username Matching (batch-search-actors.ts):** 400The search uses a scoring system (0-100): 4011. Exact handle match (before first dot): 100 4022. Exact full handle match: 90 4033. Exact display name match: 80 4044. Partial handle match (contains): 60 4055. Partial full handle match: 50 4066. Partial display name match: 40 4077. Reverse partial match: 30 408 409All comparisons use normalized strings (lowercase, no special chars). 410Returns top 5 ranked results per username. 411 412**Result Enrichment:** 413- Fetches profiles in batches of 25 for post/follower counts 414- Checks follow status using custom lexicons (default: `app.bsky.graph.follow`) 415- Attaches follow status to each actor result 416 417## Key Technical Details 418 419### AT Protocol Integration 420- Uses `@atproto/api` for Bluesky API interactions 421- Uses `@atcute/identity-resolver` for DID resolution 422- Supports custom lexicons for experimental features 423- OAuth flow follows AT Protocol OAuth spec 424 425### File Upload Flow 4261. User uploads ZIP file (Instagram/TikTok data export) 4272. `fileExtractor.ts` reads ZIP in browser (using JSZip) 4283. Matches file paths to platform rules from `platformDefinitions.ts` 4294. `parserLogic.ts` extracts usernames (regex for HTML/TEXT, path traversal for JSON) 4305. Deduplicates and returns username list 4316. Frontend calls `/batch-search-actors` with username batches (max 50) 4327. Results stored in database via `/save-results` 433 434### Session Management 435- Session IDs stored in httpOnly cookies 436- Sessions linked to encrypted OAuth state stores 437- Session validation middleware checks database and expiry 438- Sessions tied to specific DIDs (user accounts) 439 440### Deployment 441- Hosted on Netlify 442- Static frontend built with Vite 443- Serverless functions for backend 444- Database on Neon (PostgreSQL) 445- OAuth redirects configured in netlify.toml 446 447## Adding New Features 448 449### Adding a New Social Platform 4501. Add parsing rules to `src/lib/parsers/platformDefinitions.ts`: 451```typescript 452export const PLATFORM_RULES: Record<string, ParseRule[]> = { 453 newplatform: [ 454 { 455 zipPath: "path/in/zip/file.json", 456 format: "JSON", 457 rule: ["key", "path", "to", "username"], 458 }, 459 ], 460}; 461``` 4622. Test with real data export from that platform 4633. Update UI in platform selection components 464 465### Adding a New API Endpoint 4661. Create function in `netlify/functions/your-endpoint.ts` 4672. Use `withAuthErrorHandling()` for protected endpoints 4683. Implement `AuthenticatedHandler` type 4694. Add method to `IApiClient.ts` interface 4705. Implement in both `RealApiAdapter.ts` and `MockApiAdapter.ts` 4716. Use via `apiClient.yourMethod()` in components 472 473### Adding Database Models 4741. Create migration script in `scripts/` 4752. Run against local database 4763. Update repository in `netlify/functions/repositories/` 4774. Update DatabaseService if needed 478 479## Important Notes 480 481- **OAuth Localhost Issue**: Must use `127.0.0.1:8888` not `localhost:8888` for local OAuth to work 482- **Batch Limits**: Search endpoint limited to 50 usernames per request 483- **Profile Fetching**: Batched in groups of 25 to avoid rate limits 484- **Normalization**: All username comparisons use lowercase + special char removal 485- **Security**: CSP headers configured in netlify.toml, session security middleware prevents CSRF 486- **Error Handling**: Custom error classes (ValidationError, AuthenticationError, etc.) with proper HTTP status codes