Work

My CRM: local-first relationship management

Over five thousand LinkedIn connections, a scoring engine, and a weekday enrichment pipeline. SQLite on my Mac, no hosted database, and a pre-commit hook that keeps personal data out of git.

Status
in use
Stack
Python, SQLite, Claude Code skills, LinkedIn data export

The problem

Twenty-five years in sales, technology and teaching produced a network that is the most valuable thing I own. It lived in a LinkedIn export, a contacts dump, and my memory. None of those tell you who to call this week, or notice when someone changes jobs.

The constraint

This is other people’s personal information. The design rule from the first day was that it stays local: SQLite on my own machine, no hosted database, nothing identifying committed to a repository. Convenience never outranks that.

What I built

A command-line CRM in Python over a SQLite database. The LinkedIn export is the seed. A scoring engine tiers every contact by relationship strength and relevance, and a separate watch list tracks people I am following without investing in outreach, which is how I keep up with former students.

A weekday enrichment pipeline works through the queue a small batch at a time. Three Claude Code skills sit on top: one harvests profiles, one drafts messages, one syncs new connections. A scheduled morning briefing tells me who to reach out to and why, in plain English, with each name linked to the profile.

Code lives in git. Data does not. A pre-commit hook blocks real email addresses, phone numbers and data files from being committed. The README’s first section explains that git does not version hooks, so a fresh clone has no protection until the hook is installed, and that the bootstrap script installs it for exactly that reason. The guard is a seatbelt, not a lock, and the docs say so.

What it cost or taught

The briefings got worse before they got better. The first versions led with pipeline statistics that never changed what I should do. The rule now is that a number appears in the briefing only if it changes the action. Everything else is noise wearing a chart.