Every assistant remembers you. None of them will tell the others.
ChatGPT knows you're vegetarian. Claude knows about the ledger rewrite. Cursor knows you hate semicolons.
Switch products and you start from zero. memory-passport is a plain-text format, a spec, and a CLI that moves it all.
⏱️ 60-second quickstart
pip install memory-passport # or: uv tool install memory-passport
# 1. Turn a ChatGPT data export into a vault (memories are hiding inside conversations.json)
passport import chatgpt-export.zip --out passport
# 2. Check it against the spec
passport validate passport
# 3. Paste it into Claude (Settings → Memory → Start import)
passport export passport --to claude | pbcopy
That is the whole loop. Your vault is a folder of markdown files you can read, edit, and commit to git.
🗂️ What a passport looks like
passport/
├── passport.yaml # spec_version, allow_health
├── profile.md # who you are
├── preferences.md # how you like assistants to behave
├── people/priya-nair.md # one file per person
├── topics/e-ink-devices.md
└── areas/ledger-rewrite.md # one file per project
Every file is YAML frontmatter plus bullet lines. Every bullet says where it came from:
---
name: Priya Nair
description: Sam's manager; sets quarterly priorities and reviews design docs.
sources: [chatgpt, claude]
aliases: [Priya, PN]
updated: "2026-06-11"
kind: person
---
- [stated] Prefers decisions written up as one-page ADRs before a meeting.
- [inferred] Likely based in the Edinburgh office, given meeting times. <!-- src: chatgpt, 2026-05-02 -->
No product records provenance today. They should. Until they do, the importers tag conservatively and the exporters hedge anything that was only a guess ("Possibly: …") so a wrong inference never gets promoted to fact on the way in to the next assistant.
🚫 What a passport refuses to carry
A passport is designed to be pasted into many products, so it is the worst possible place for anything you would not hand to a third party. The validator rejects these outright:
Importers redact the span (has a Monzo card [redacted card-number]) and tell you, so the useful half of the sentence survives. Health is the one opt-in category (--allow-health), because an assistant that knows about your dietary restriction is genuinely more useful. The rest have no everyday use. Full reasoning in SPEC.md §7.
🔀 Merge without losing, diff without squinting
Two vaults from two products will disagree. merge never picks silently: the same fact with different tags keeps the more trusted one, new facts are added, and contradictions get git-style conflict markers that fail validation until you resolve them. "Lives in Manchester" and "User is based in Manchester" count as the same fact; a small phrasing table catches the common rewordings without a model.
passport diff vault-a vault-b # + added, - removed, ~ retagged
passport merge vault-a vault-b --out merged
🧠 Live memory, not just luggage
Once you have a vault, keep using it. Add facts by hand, ask it questions, and let your assistants read and write it directly over MCP.
passport add passport "Priya moved to the Edinburgh office." --to person:Priya
passport show passport priya
passport show passport -q "british english"
passport forget passport "works four days a week"
passport validate passport --stale 365 # flag observed/inferred facts older than a year
MCP server. pip install "memory-passport[mcp]" gives you passport-mcp with four tools: list_subjects, read_memory, remember, forget. Register it once and Claude Code, Cursor or Claude Desktop use the same plain files you edit by hand:
claude mcp add passport -e PASSPORT_VAULT=~/passport -- passport-mcp
Everything a client writes goes through the same exclusion and dedupe rules as the CLI. A card number pasted into remember comes out as [redacted card-number]; a health diagnosis is refused.
Prompt export. passport export passport --to prompt --budget 2000 renders a <user_memory> block for any model, API call or agent persona. Over budget, it drops inferred facts first, then observed, then the oldest stated.
🌐 Try it in the browser
mohitagw15856.github.io/memory-passport runs the real validator, importers and exporters in your browser via Pyodide. Pick a vault folder, paste a memory list, download the result. Nothing is uploaded.
🧭 Every command
| Command | What it does |
|---|---|
passport validate <dir> [--strict] [--json] |
Check a vault against the spec. Exit 1 on errors. |
passport import <export> [--from chatgpt|claude|gemini|copilot|markdown] [--out dir] |
Build a vault. Auto-detects the source when it can. |
passport import … --memory-text memories.txt |
Combine an export with a pasted memory list. |
passport import … --no-route |
Skip the people/topics/areas sorting; everything in profile.md. |
passport export <dir> --to prompt|chatgpt|claude|claude-code|cursor|markdown [--out path] [--budget N] |
Paste-ready text or files for that product. |
passport merge <a> <b> --out merged |
Merge with conflict markers. Exit 3 if any conflicts. |
passport diff <a> <b> [--json] |
Fact-level diff. Exit 1 if they differ. |
passport show <dir> [subject] [-q words] |
List subjects, print one, or search facts. |
passport add <dir> "fact" [--to subject] [--tag] [--section] |
Append one dated fact, with exclusions applied. |
passport forget <dir> "fact" |
Remove a fact by text (loose match). |
passport inspect <export> |
Say what an export contains without importing it. Paste into bug reports. |
passport importers / passport exporters |
List what is installed, including plugins. |
🧳 Where the memories actually are
Getting memory out of products is the annoying part. Here is what each one really offers, and what the passport does with it.
| Product | Export exists? | Import exists? | Where memory hides | Provenance | Per-subject structure | Hand-editable | Sensitive-data control |
|---|---|---|---|---|---|---|---|
| ChatGPT | Data export zip, but no memory file. Memories are recoverable from conversations.json as messages to the bio tool, or copy the Manage memories list. |
No. Custom instructions (2 × 1,500 chars) or "remember this" in chat. | flat list of sentences | none | none | via settings UI only | delete individual memories |
| Claude (claude.ai) | Copy from Settings → Memory, or ask it to write memories out verbatim. Data export has projects but no memory. | Yes: Settings → Memory → Start import (experimental). | prose summary | none | by topic in the summary | edit the summary text | "include sensitive topics" toggle |
| Claude Code | It is already files: ~/.claude/projects/<p>/memory/*.md with frontmatter. |
Drop files in the folder. | markdown files | none, but a type field |
one file per memory | yes | none |
| Cursor | No user memory; project rules in .cursor/rules/*.mdc. |
Write a rule file. | rule files per repo | none | per repo | yes | none |
| Gemini | No export. Copy Saved info or ask it to list everything; --from gemini. |
No. | Saved Info list | none | none | via settings UI | delete individual items |
| Copilot | No export. Ask it to list its memories; --from copilot. |
No. | flat list | none | none | via settings UI | delete individual items |
| Hermes Agent / custom bots | Whatever you built; usually a markdown folder. | Same. | your call | your call | your call | yes | your call |
| memory-passport | It is the export. | It is the import. | profile.md, preferences.md, people/, topics/, areas/ |
[stated] [observed] [inferred] + per-fact source and date |
one file per subject, five kinds | yes, it is markdown | validator refuses cards, IDs, secrets; health opt-in |
Corrections welcome. Products change their exports without notice; each importer's module docstring says exactly which fields it reads, and the fixtures in tests/fixtures/ mirror the real layouts.
🔌 Pluggable
Adding a product is one class with detect() and load(), registered as an entry point. No fork needed:
[project.entry-points."memory_passport.importers"]
acme = "passport_acme:AcmeImporter"
passport import --from acme will find it. The walkthrough is in CONTRIBUTING.md.
📐 The spec
SPEC.md defines the layout, the frontmatter (with a JSON Schema), fact lines, the three tags, the exclusions and why, merge semantics, and what the format deliberately cannot express. docs/ADR-001.md explains why markdown plus frontmatter beat JSON.
The examples/sample-vault/ folder is a complete, valid vault to poke at.
🛠️ Developing
git clone https://github.com/mohitagw15856/memory-passport && cd memory-passport
uv sync --group dev
uv run pytest -q # fixture exports for every importer
uv run ruff check .
📜 Licence
MIT © mohitagw15856. Your memories are yours; this just helps them travel.
Release files for memory-passport 0.2.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| memory_passport-0.2.0.tar.gz | 458.8 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| memory_passport-0.2.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 514.4 kB
Release files / memory_passport-0.2.0.tar.gz
| Download URL | memory_passport-0.2.0.tar.gz |
|---|---|
| Size | 458.8 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
0f147d930fb513842e2867e6d28bb4b2c6d7d317e9b779eb45ddf819ac5640e3
|
|
BLAKE2b-256 checksum How to use checksums |
9325a3f40330af5a3e1a49218ccd3117cc9a36c54c7906027d57827ff738bc15
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 22, 2026.
Transparency logRelease files / memory_passport-0.2.0-py3-none-any.whl
| Download URL | memory_passport-0.2.0-py3-none-any.whl |
|---|---|
| Size | 55.6 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
9b59a8205d5b345454696ebd040da9633286094cf18521e17872bff67e330da5
|
|
BLAKE2b-256 checksum How to use checksums |
125f4a93e927d6181d1ae9024ecb8a9711b9f685b9ab6b906e285c9d3a54c95f
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 22, 2026.
Transparency log