|
1 | 1 | # CLAUDE.md |
2 | 2 |
|
3 | | -This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. |
| 3 | +Guidance for Claude Code when working with this repository. |
4 | 4 |
|
5 | 5 | ## Project Overview |
6 | 6 |
|
7 | | -SparseTree is a genealogy toolkit for creating local databases of your family tree, validating data, curating favorites, and generating sparse family tree visualizations that can be printed on posters. It connects to genealogy providers (FamilySearch, Ancestry, WikiTree, 23andMe) to download ancestry data, stores it locally as JSON, and provides a web UI for browsing, searching, and visualizing your tree. The "sparse tree" feature lets you mark interesting ancestors as favorites and generate a simplified visualization showing only those people connected through their lineage. |
| 7 | +SparseTree is a genealogy toolkit for creating local databases of your family tree from multiple providers (FamilySearch, Ancestry, WikiTree, 23andMe), with a web UI for browsing, searching, and visualizing your tree. The "sparse tree" feature lets you mark interesting ancestors as favorites and generate simplified visualizations. |
8 | 8 |
|
9 | | -## Commands |
| 9 | +## Quick Reference |
10 | 10 |
|
11 | | -### Download ancestry data |
12 | | -```bash |
13 | | -FS_ACCESS_TOKEN=YOUR_TOKEN node index PERSON_ID |
14 | | -# Options: |
15 | | -# --max=N Limit to N generations |
16 | | -# --ignore=ID1,ID2 Skip specific person IDs |
17 | | -# --cache=all|complete|none Cache behavior (default: all) |
18 | | -# --oldest=YEAR Only include people born after YEAR (supports BC notation) |
19 | | -# --tsv=true Also log to TSV file during indexing |
20 | | -``` |
| 11 | +| Resource | Description | |
| 12 | +|----------|-------------| |
| 13 | +| [docs/architecture.md](./docs/architecture.md) | Data model, storage layout, identity system | |
| 14 | +| [docs/api.md](./docs/api.md) | API endpoint reference | |
| 15 | +| [docs/cli.md](./docs/cli.md) | CLI command reference | |
| 16 | +| [docs/development.md](./docs/development.md) | Development setup, PM2, browser automation | |
| 17 | +| [docs/providers.md](./docs/providers.md) | Genealogy provider configuration | |
| 18 | +| [docs/roadmap.md](./docs/roadmap.md) | Detailed phase documentation | |
| 19 | +| [PLAN.md](./PLAN.md) | High-level roadmap | |
21 | 20 |
|
22 | | -### Find lineage path between two people |
23 | | -```bash |
24 | | -node find ROOT_ID ANCESTOR_ID |
25 | | -# Options: |
26 | | -# --method=s|l|r shortest/longest/random path (default: s) |
27 | | -``` |
28 | | - |
29 | | -### Export database to TSV |
30 | | -```bash |
31 | | -node tsv DB_ID |
32 | | -``` |
| 21 | +## Essential Commands |
33 | 22 |
|
34 | | -### Print sorted by date |
35 | 23 | ```bash |
36 | | -node print DB_ID [--bio] |
37 | | -``` |
| 24 | +# Development |
| 25 | +pm2 restart ecosystem.config.cjs # Restart app (ports 6373/6374) |
| 26 | +npm run build # Build all packages |
38 | 27 |
|
39 | | -### Purge records from cache |
40 | | -```bash |
41 | | -node purge ID1,ID2 |
42 | | -``` |
| 28 | +# Migrations |
| 29 | +npx tsx scripts/migrate.ts # Run migrations |
| 30 | +npx tsx scripts/migrate.ts --status # Check status |
43 | 31 |
|
44 | | -### Prune unused person files |
45 | | -```bash |
46 | | -node prune DB_ID |
47 | | -``` |
| 32 | +# Update |
| 33 | +./update.sh # Pull, build, migrate, restart |
48 | 34 |
|
49 | | -## Architecture |
50 | | - |
51 | | -### Data Flow |
52 | | -1. `index.js` fetches person data from FamilySearch API via `lib/fscget.js` |
53 | | -2. Raw API responses stored in `data/person/{ID}.json` |
54 | | -3. `lib/json2person.js` transforms API data to simplified person objects |
55 | | -4. Compiled graph database saved to `data/db-{ID}.json` |
56 | | - |
57 | | -### Key Files |
58 | | -- `config.js` - API credentials (via `FS_ACCESS_TOKEN` env var), rate limiting delays, and "known unknowns" filter list |
59 | | -- `lib/fs.client.js` - FamilySearch API client wrapper using `fs-js-lite` |
60 | | -- `lib/json2person.js` - Transforms raw API JSON to person objects with: name, lifespan, location, parents[], children[], occupation, bio |
61 | | -- `lib/pathShortest.js`, `lib/pathLongest.js`, `lib/pathRandom.js` - Graph traversal algorithms for finding lineage paths |
62 | | - |
63 | | -### Data Structure |
64 | | -Person object in database: |
65 | | -```javascript |
66 | | -{ |
67 | | - name: string, |
68 | | - lifespan: "BIRTH-DEATH", // supports BC notation |
69 | | - location: string, |
70 | | - parents: [ID, ID], |
71 | | - children: [ID, ...], // populated during db save |
72 | | - occupation: string, |
73 | | - bio: string |
74 | | -} |
| 35 | +# Download ancestry |
| 36 | +FS_ACCESS_TOKEN=TOKEN node index PERSON_ID --max=10 |
75 | 37 | ``` |
76 | 38 |
|
77 | | -### Authentication |
78 | | -Get your access token from browser dev tools when logged into FamilySearch - copy the Authorization header value (without "Bearer" prefix). Tokens last 24+ hours. |
79 | | - |
80 | | -## Web UI |
81 | | - |
82 | | -### Development |
83 | | -The app runs via PM2 with live reload enabled. No need to run `npm run dev` - just edit files and changes will auto-reload. |
| 39 | +## Project Structure |
84 | 40 |
|
85 | | -```bash |
86 | | -# App is already running on: |
87 | | -# - Frontend: http://localhost:6373 |
88 | | -# - Backend: http://localhost:6374 |
89 | | - |
90 | | -# If needed to restart: |
91 | | -pm2 restart ecosystem.config.cjs |
92 | 41 | ``` |
93 | | - |
94 | | -### Structure |
95 | | -- `client/` - React + Vite + Tailwind frontend |
96 | | -- `server/` - Express API backend |
97 | | -- `shared/` - TypeScript types shared between client/server |
98 | | - |
99 | | -### API Endpoints |
100 | | -- `GET /api/databases` - List all graph databases |
101 | | -- `GET /api/persons/:dbId` - List persons in database |
102 | | -- `GET /api/persons/:dbId/:id/tree` - Get tree data for D3 |
103 | | -- `GET /api/search/:dbId?q=&location=&occupation=` - Search with filters |
104 | | -- `POST /api/path/:dbId` - Find path (body: source, target, method) |
105 | | -- `GET /api/indexer/events` - SSE stream for indexer progress |
106 | | -- `GET /api/export/:dbId/tsv` - Export as TSV |
107 | | - |
108 | | -### AI Toolkit Integration |
109 | | -The server integrates `@portos/ai-toolkit` for AI provider management: |
110 | | -- `GET/POST /api/providers` - Manage AI providers |
111 | | -- `GET/POST /api/runs` - Execute and track AI runs |
112 | | -- `GET/POST /api/prompts` - Manage prompt templates |
113 | | - |
114 | | -Provider configuration stored in `data/ai/providers.json`. |
115 | | - |
116 | | -## Browser Automation |
117 | | - |
118 | | -Persistent Chrome with CDP on port 9920: |
119 | | -```bash |
120 | | -./.browser/start.sh |
| 42 | +client/ # React + Vite + Tailwind frontend |
| 43 | +server/ # Express API backend |
| 44 | +shared/ # TypeScript types |
| 45 | +lib/ # CLI tools (index, find, purge, etc.) |
| 46 | +scripts/ # Migration scripts |
| 47 | +data/ # Local storage (git-ignored) |
| 48 | +docs/ # Documentation |
121 | 49 | ``` |
122 | | -Profile data stored in `.browser/data/`. Connect via `ws://localhost:9920`. |
123 | | - |
124 | | -## Git Workflow |
125 | | - |
126 | | -- **dev**: Active development (auto-bumps patch on CI pass) |
127 | | -- **main**: Production releases only |
128 | | -- PR `dev → main` creates tagged release and preps next version |
129 | | -- **Use `/gitup` to push** - The dev branch receives auto version bump commits from CI. Always use `git pull --rebase --autostash && git push` (or `/gitup`) instead of plain `git push`. |
130 | | -- Update `.changelog/v{major}.{minor}.x.md` when making changes (see Release Changelog Process below) |
131 | | -- **Commit after each feature or bug fix** - lint, commit, and push automatically to keep work safe |
132 | 50 |
|
133 | | -## Release Changelog Process |
| 51 | +## Architecture Summary |
134 | 52 |
|
135 | | -All release notes are maintained in `.changelog/v{major}.{minor}.x.md` files. Each minor version series has a single changelog file that accumulates changes throughout development. **No root CHANGELOG.md** - all changelog content lives in `.changelog/`. |
136 | | - |
137 | | -### During Development |
138 | | - |
139 | | -**Always update `.changelog/v0.2.x.md`** when you make changes: |
140 | | -- Add entries under appropriate emoji sections (🎉 Features, 🐛 Fixes, 🔧 Improvements, 🗑️ Removed) |
141 | | -- Keep the version as `v0.2.x` throughout development (don't change it to 0.2.2, 0.2.3, etc.) |
142 | | -- Group related changes together for clarity |
143 | | -- Explain the "why" not just the "what" |
144 | | - |
145 | | -### Before Releasing to Main |
146 | | - |
147 | | -Final review before merging `dev → main`: |
148 | | -- Ensure all changes are documented in `.changelog/v0.2.x.md` |
149 | | -- Add the release date (update "YYYY-MM-DD" to actual date) |
150 | | -- Polish descriptions for clarity |
151 | | -- Commit the changelog |
152 | | - |
153 | | -### On Release (Automated) |
154 | | - |
155 | | -When merging to `main`, the GitHub Actions workflow automatically: |
156 | | -1. Reads `.changelog/v0.2.x.md` |
157 | | -2. Replaces all instances of `0.2.x` with actual version (e.g., `0.2.5`) |
158 | | -3. Creates the GitHub release with substituted changelog |
159 | | -4. Renames `v0.2.x.md` → `v0.2.5.md` (preserves git history) |
160 | | -5. Bumps dev to next minor version (e.g., 0.3.0) |
161 | | - |
162 | | -See `.changelog/README.md` for detailed format and best practices. |
163 | | - |
164 | | -## Data Storage Architecture |
165 | | - |
166 | | -SparseTree uses a hybrid storage model: |
167 | | -- **JSON files** (`data/`) - Source of truth for raw API data |
168 | | -- **SQLite database** (`data/sparsetree.db`) - Fast query index with FTS5 search, recursive CTEs for path finding |
169 | | -- **Content-addressed blobs** (`data/blobs/`) - Deduplicated media storage |
170 | | - |
171 | | -### SQLite Schema |
172 | | -Key tables in `server/src/db/schema.sql`: |
173 | | -- `person` - Canonical person records with ULID primary keys |
174 | | -- `external_identity` - Maps provider IDs (FamilySearch, Ancestry, etc.) to canonical IDs |
175 | | -- `parent_edge` / `spouse_edge` - Relationship graphs with provenance |
176 | | -- `vital_event` - Birth, death, burial events with dates/places |
177 | | -- `claim` - Extensible facts (occupation, religion, bio, etc.) |
178 | | -- `person_fts` - FTS5 virtual table for full-text search |
179 | | - |
180 | | -### ID Mapping |
181 | | -- Canonical IDs: ULIDs (26-char, sortable, no special chars) |
182 | | -- External IDs: Provider-specific (e.g., FamilySearch `GW21-BZR`) |
183 | | -- `idMappingService` handles bidirectional lookup with in-memory cache |
184 | | - |
185 | | -## Data Migrations |
186 | | - |
187 | | -SparseTree uses a migration system for schema and data changes. Migrations are tracked in `data/.data-version` (data migrations) and the SQLite `migration` table (schema migrations). |
188 | | - |
189 | | -### Running Migrations |
190 | | -```bash |
191 | | -# Run all pending migrations |
192 | | -npx tsx scripts/migrate.ts |
193 | | - |
194 | | -# Preview what would run (no changes made) |
195 | | -npx tsx scripts/migrate.ts --dry-run |
196 | | - |
197 | | -# Check migration status |
198 | | -npx tsx scripts/migrate.ts --status |
199 | | - |
200 | | -# Rollback last N migrations |
201 | | -npx tsx scripts/migrate.ts --rollback=1 |
| 53 | +``` |
| 54 | +Layer 3: Local Overrides → User edits (SQLite local_override) |
| 55 | +Layer 2: Normalized Data → SQLite (person, life_event, parent_edge, etc.) |
| 56 | +Layer 1: Raw Provider Cache → JSON files (data/person/*.json) |
202 | 57 | ``` |
203 | 58 |
|
204 | | -### Creating New Migrations |
205 | | -1. **Schema migrations** (`server/src/db/migrations/`): Add new files like `002_add_column.ts` |
206 | | -2. **Data migrations** (`scripts/migrate.ts`): Add entries to `dataMigrations` array |
207 | | - |
208 | | -Migration naming convention: `NNN_description` (e.g., `001_initial`, `002_add_indexes`) |
| 59 | +- **Canonical IDs**: ULIDs (26-char, owned by SparseTree) |
| 60 | +- **External IDs**: Provider-specific (FamilySearch, Ancestry, etc.) |
| 61 | +- **SQLite**: Fast queries with FTS5 search, JSON as source of truth |
209 | 62 |
|
210 | | -### Migration in Release Process |
211 | | -- Data migrations run automatically via `update.sh` |
212 | | -- When creating migrations that affect existing data: |
213 | | - 1. Test with `--dry-run` first |
214 | | - 2. Provide a `down()` function when possible for rollback |
215 | | - 3. Document the migration in the changelog |
216 | | - 4. Consider backwards compatibility with older app versions |
| 63 | +## Git Workflow |
217 | 64 |
|
218 | | -## Updating SparseTree |
| 65 | +- **dev**: Active development (auto-bumps patch on CI) |
| 66 | +- **main**: Production releases only |
| 67 | +- **Push pattern**: `git pull --rebase --autostash && git push` |
| 68 | +- **Changelog**: Update `.changelog/v{major}.{minor}.x.md` with changes |
| 69 | +- **Commit often**: After each feature or bug fix |
| 70 | + |
| 71 | +## Code Guidelines |
| 72 | + |
| 73 | +- ES modules (`"type": "module"`) |
| 74 | +- Functional programming over classes |
| 75 | +- No `try/catch` if avoidable |
| 76 | +- No `window.alert`/`window.confirm` - use toast and modals |
| 77 | +- Full URL paths for routes (no modals without deep links) |
| 78 | +- DRY and YAGNI patterns |
| 79 | +- Never use `pm2 kill` or `pm2 delete all` |
| 80 | + |
| 81 | +## Key Files |
| 82 | + |
| 83 | +| File | Purpose | |
| 84 | +|------|---------| |
| 85 | +| `config.js` | API credentials, rate limits | |
| 86 | +| `lib/json2person.js` | Transform API → person objects | |
| 87 | +| `lib/sqlite-writer.js` | Write to SQLite during indexing | |
| 88 | +| `server/src/db/schema.sql` | Full SQLite schema | |
| 89 | +| `server/src/services/id-mapping.service.ts` | Canonical ↔ external ID lookup | |
| 90 | +| `ecosystem.config.cjs` | PM2 configuration | |
219 | 91 |
|
220 | | -Use `update.sh` to pull the latest code and apply all updates: |
| 92 | +## Browser Automation |
221 | 93 |
|
222 | 94 | ```bash |
223 | | -# Full update: pull, install, build, migrate, restart |
224 | | -./update.sh |
225 | | - |
226 | | -# Preview what would happen |
227 | | -./update.sh --dry-run |
228 | | - |
229 | | -# Update without restarting PM2 |
230 | | -./update.sh --no-restart |
231 | | - |
232 | | -# Update from a specific branch |
233 | | -./update.sh --branch=dev |
| 95 | +./.browser/start.sh # Start Chrome with CDP |
| 96 | +# Default CDP port: 9920 |
| 97 | +# Profile: .browser/data/ |
234 | 98 | ``` |
235 | 99 |
|
236 | | -The update script: |
237 | | -1. Checks for uncommitted changes (fails if dirty) |
238 | | -2. Pulls latest from main (or specified branch) |
239 | | -3. Installs npm dependencies |
240 | | -4. Builds the application |
241 | | -5. Runs pending data migrations |
242 | | -6. Restarts PM2 services |
| 100 | +Web UI: `/settings/browser` for connection, `/providers/genealogy` for logins. |
243 | 101 |
|
244 | 102 | ## Notes |
245 | | -- The database has cyclic loop issues (people linked as their own ancestors) - use longest path method to detect these |
246 | | -- ES modules (`"type": "module"` in package.json) |
247 | | -- Rate limiting built-in with random delays between API calls |
248 | | -- SQLite auto-enables when `data/sparsetree.db` exists with data |
| 103 | + |
| 104 | +- Database has cyclic loops - use `--method=l` (longest path) to detect |
| 105 | +- SQLite auto-enables when `data/sparsetree.db` exists |
| 106 | +- Rate limiting built into API calls |
| 107 | +- Credentials encrypted with AES-256-GCM in `data/credentials.json` |
0 commit comments