you-wrapped
v0.3.1
Published
Turn the record your Mac already keeps into a portrait of your year. Runs entirely on your machine.
Downloads
49
Maintainers
Readme
you-wrapped
A portrait of your year, assembled from the record your Mac already keeps.
Not "you sent 40,000 texts 🎉". A real read: the one trait that explains most of your behavior, the trend you can't see from inside it, a dated day where two commitments collided, and the wins you've been discounting.
npx you-wrappedZero dependencies. Runs entirely on your machine.
What it reads
| Source | What it takes | |---|---| | iMessage | Your sent messages — language, rhythm, who you talk to | | Apple Notes | What you write to nobody; tasks you keep rewriting | | Contacts | Names, so numbers become people | | Chrome + Safari | Searches, split by desktop vs phone — they're different minds | | Photos | Metadata only. Dates and coordinates. No images are ever opened. |
Missing a source? It's skipped with a reason. Partial data still makes a portrait.
What it writes
you-wrapped-out/
signals.json aggregate counts and trends — no message content
evidence.local.md your own words, quoted — KEEP THIS PRIVATE
prompt.md paste into Claude if you don't want to use an API key
portrait.json the analysis (with --synthesize)
wrapped.html your portrait (with --synthesize)
wrapped.share.html redacted, safe to post
wrapped.html has two modes: swipeable story cards (arrows, tap, or swipe) and a
full long-read with every finding at length. Press R to switch.Usage
npx you-wrapped # extract + analyze, write the prompt
npx you-wrapped --synthesize # also call Claude (needs ANTHROPIC_API_KEY)
npx you-wrapped --days 400 # how far back to read
npx you-wrapped --render p.json # re-render a portrait you already haveWithout --synthesize, nothing touches the network at all. Open prompt.md,
paste it into Claude or ChatGPT, save the JSON it returns, and run --render.
Privacy
This reads your life. The design follows from that.
- Local by default. No telemetry, no analytics, no server. There is nothing to opt out of.
--synthesizeis the only network call. It sends aggregate signals plus short quotes to Anthropic using your key. Nothing is stored by this tool.- Two outputs, two audiences.
wrapped.htmlhas your names in it.wrapped.share.htmlstrips emails, phones, addresses, links, tokens, and every name harvested from your own contacts and chat titles, then replaces coordinates with city labels. Redaction is deny-by-default: it strips first, and only what survives gets rendered. evidence.local.mdis the sensitive file. It's named that way on purpose. Don't post it.
Read wrapped.share.html before you share it. It's your life; verify the scrub.
Requirements
macOS, Node 22.5+. Your terminal needs Full Disk Access: System Settings → Privacy & Security → Full Disk Access → add Terminal/iTerm.
Why the extraction is the hard part
Four things silently return wrong answers instead of errors, and every one of them took real debugging:
- ~44% of iMessages have
text = NULL. The body lives inattributedBody, an NSKeyedArchiver typedstream blob. Skip it and you lose half the corpus with no error. - Contacts aren't in the obvious database.
AddressBook-v22.abcddbis near-empty; real records live inSources/<UUID>/. - Apple Notes bodies are gzipped protobuf — and inflating leaves binary framing between text runs that has to be filtered heuristically.
- Timestamps overflow. Messages stores nanoseconds since 2001, Chrome stores
microseconds since 1601. Both exceed
Number.MAX_SAFE_INTEGER, so the conversion has to happen inside SQLite.
License
MIT. Your data is yours; this tool never sees it.
