What it does
Contacts fills in the gaps in your people files in two ways. First, it resolves locations: when Patina creates a new person file, it matches them by name, phone, or email against your macOS address book and pulls in their city and state. Second, it resolves names: when Beeper shows a phone number instead of a name, the system looks it up in your contacts so it knows who you're talking to.
Used by
Setup
Grant Full Disk Access
The Contacts database lives in a protected location on macOS. Your terminal app (Terminal, iTerm, Warp, etc.) needs Full Disk Access to read it. Go to System Settings → Privacy & Security → Full Disk Access and add your terminal app.
Run the prefetch script
Contact resolution runs through a prefetch step to avoid triggering macOS permission dialogs during a Claude session. Run it manually the first time:
bash scripts/prefetch-tcc-data.sh
This reads the AddressBook database and caches the results. It matches people by name, phone number, and email address.
Automatic from here
Every time /sync-people runs, it calls the location resolver at the end. Any people file with Location: Unknown gets checked against your address book. The contacts map is also available to /morning, /digest, and any other skill that needs to resolve a phone number or email to a name.
How it works
The prefetch script queries the macOS AddressBook SQLite database directly and does two things. For locations, it tries three matching strategies in order: full name, phone number (stripped of formatting), and email. When it finds a match with an address on file, it writes the city and state into the person's markdown file. For name resolution, it builds a JSON lookup map of phone numbers and emails to names, stored at work/contacts-map.json. Skills like /morning and /digest use this map to identify who's behind a phone number in Beeper messages. No data leaves your machine.
