contacts-unify
Skill6 starsChanged 4 months agoMITView on GitHub
Merge and dedupe macOS, iCloud, Google, Zoho, or VCF contacts with reviewable provenance.
In this repository27 other files it gives its agents.
- agent-skills-agentsAGENTS.md
- agent-skills-claudeCLAUDE.md
- agent-log-forensicsSkill
- changelog-writerSkill
- chezmoi-driftSkill
- decision-logSkill
- dependency-pinningSkill
- django-smoke-alarmSkill
- empathy-auditSkill
- first-contactSkill
- handle-prSkill
- launch-sequenceSkill
- parallel-isolated-app-testingSkill
- post-mortemSkill
- pre-mortemSkill
- profile-meSkill
- ralph-loopSkill
- release-operatorSkill
- remote-host-verifierSkill
- research-personSkill
- scope-hammerSkill
- second-opinionsSkill
- status-copy-trust-auditSkill
- support-stormSkill
- trust-auditSkill
- visual-qa-loopSkill
- wide-open-brainstormSkill
The file
We split this skill into the rules it gives an agent
7 rules. About 0 are common, 5 are distinctive, and 2 are very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈5
Distinctive · estimate
This author's own choices. Worth a look.
2
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Distinctive: line 30
Consent & authorization (read before running): only operate on contacts/messages belonging to the user, on a machine they own or have …
- Distinctive: line 33
names explicitly: Never infer a research shortlist from message volume and run it unattended, and don't dossier minors or obviously …
- Distinctive: line 67
Search/filter for large books.
- Distinctive: line 84
Ask the user who's worth a deep dive: Before mining, offer to focus: high-value people, specific groups, or traits (e.g.
- Very specific: line 88
Mine the message graph (read-only): Run scripts/imessage_signal.py — it copies chat.db to a temp file, opens it read-only, and ranks …
- Very specific: line 106
Read-only on chat.db (work on a copy).
- Distinctive: line 108
No outbound sync and no message content in any shared vault without explicit approval.
≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
Contacts Unify
Turn a messy, multi-source pile of contacts into one deduplicated, trustworthy address book — with a review UI simple enough for a non-technical person to drive. Optionally, mine iMessage history to find the people worth a deeper OSINT dossier.
Why this matters: duplicates and inconsistency make people stop maintaining their contacts at all. The win is a single source of truth they'll actually keep feeding.
Scope of local_only: Phase A (dedupe) is fully local — no network egress. Phase B is opt-in
and its dossier step (research-person) does reach the public web (LinkedIn, gov/org sites). Treat
Phase B network access as a deliberate, separately-approved step, never an automatic follow-on.
Before anything else: tell the user what will be read (Contacts DB, exports, optionally Messages) and what will happen (backup, then a dry-run preview), and get a go-ahead. All data stays on the machine; don't paste contact or message content into external services.
Consent & authorization (read before running): only operate on contacts/messages belonging to the user, on a machine they own or have explicit permission to access (including any SSH host). Phase B researches other people from the user's private data — only build a dossier on someone the user names explicitly. Never infer a research shortlist from message volume and run it unattended, and don't dossier minors or obviously sensitive relationships.
When to use
- "Fix / clean up / merge my contacts", duplicate cards, contacts scattered across accounts.
- Consolidating iCloud + several Google accounts + Zoho + stray VCF/CSV exports.
- "Who do I talk to most?" → rank message contacts, then research the important ones.
Two phases (run either or both)
Phase A — Unify & dedupe (the core)
Full plan: references/unify-plan.md (read it before building). Summary of the workflow:
- Confirm the target + sources. Whose contacts? On the local Mac, or over SSH to another box
(e.g.
ssh <host>→sudo -u <user> -i)? Inventory which sources exist:- macOS Contacts / iCloud — sqlite
~/Library/Application Support/AddressBook/Sources/*/AddressBook-v22.abcddb(readmode=rofor rich fields +ZCREATIONDATE/ZMODIFICATIONDATE);osascriptfor the live list and for writes. - Google accounts (often several) — Google Contacts export / People API / Takeout
.vcf/CSV. - Zoho, legacy Gmail, and any loose
.vcf/.csvon Desktop/Downloads. - Tag every contact with its source account as provenance.
- macOS Contacts / iCloud — sqlite
- Back up first, always. Whole AddressBook DB →
~/Desktop/Contacts-backup-<ts>/; every source → timestamped snapshot in the working dir. This is non-negotiable. - Group & classify. Group by normalized name; cross-link groups sharing a normalized phone
(last 10 digits) or email even under different names. Classify each:
identical(auto-merge),combine(safe union),review(disjoint → maybe different people → default keep-separate). Name-only cards (businesses, placeholders with no phone/email) can't be matched on identifiers — group them by normalized name only and always route them toreview, never silent auto-merge. - Review UI. Launch the self-contained web page (stock macOS python,
http.server, no pip). Each duplicate group shows every competing field value with its label (home/work/cell), source account, and age ("edited yesterday", "added 3mo ago"). The user picks the winning value per field; losers become secondary labelled fields or are dropped if marked outdated. Search/filter for large books. Save records decisions; Apply performs merges/deletes. Without dates + labels, "Keep/Delete" is meaningless — dates and labels are mandatory. - Apply — dry-run by default. Nothing mutates without
--live. Before any delete, export the removed cards todeleted-contacts-<ts>.vcf(drag-to-restore undo). Auto-merge only truly identical cards (same normalized phone/email set — no info lost). - Never sync back to any cloud account until the user names an explicit target and approves.
Starter code: scripts/contacts_cleanup.py (relative to this skill's directory) is a single-source (macOS)
merger with a web review UI, backup, undo, and classification. It is dry-run by default; --live (or
CC_LIVE=1) is required to merge or delete. Note that in live mode it also auto-combines exact duplicates at
startup, so run it dry first and show the user the preview. Read it, then extend it to multi-source ingest +
per-field winner selection rather than rebuilding. Run it only after the backup step has succeeded.
Phase B — iMessage mining → dossiers (optional deep dive)
Full plan: references/imessage-dossier-plan.md.
- Ask the user who's worth a deep dive. Before mining, offer to focus: high-value people, specific groups, or traits (e.g. "state legislators", "arts-org board members", "clients", "family"). Let them steer the shortlist — don't research everyone. The ranking informs the conversation; it does not authorize research. A dossier runs only for a person the user names.
- Mine the message graph (read-only). Run
scripts/imessage_signal.py— it copieschat.dbto a temp file, opens it read-only, and ranks handles by volume and recency (plus a 1:1-vs-group signal). It reads counts and timestamps only; message content never leaves the machine. Reciprocity is an unreliable column — chat.db nulls the sender on the user's own messages, so rank on volume + recency, not reciprocity. Output: rankedpeople-signal.csv/json+ gap lists ("high-frequency but not in contacts" / "in contacts but never messaged"). Full Disk Access required for the running user. - Resolve identities against the unified address book from Phase A (collapse phone+email splits).
- Fan out dossiers. For the approved shortlist, invoke the
research-personskill to build confidence-marked ObsidianPeople/notes, enriched from public sources appropriate to the person's world — LinkedIn, and (for civic figures) state legislature sites, nonprofit/arts-group sites, and lobbyist or disclosure registries. Private message content stays internal context, never published.
Safety rules (both phases)
- Dry-run by default;
--liveonly after the user sees what would change. - Back up before any mutation; every delete is reversible via exported
.vcf. - Read-only on
chat.db(work on a copy); never write the live Messages or Contacts DB directly except through the reviewed,--live-gated apply step. - No outbound sync and no message content in any shared vault without explicit approval.
Deliverables
- Working dir (a new folder outside any shared/synced repo, e.g.
~/contacts-merge/<person>/) with source snapshots,contacts-data.json,contacts-decisions.(csv|json), and the review HTML. - A launcher (
.commandfile) and a short friend-readableREADME.md, if the user wants to hand this off. - Final summary: counts auto-merged, sent to review, left alone; paths to the backup and the undo
.vcf. - (Phase B)
people-signal.(csv|json)+research-persondossiers for the approved shortlist.
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

