memory-passport 0.2.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (81) hide show
  1. memory_passport-0.2.0/.gitignore +10 -0
  2. memory_passport-0.2.0/CHANGELOG.md +18 -0
  3. memory_passport-0.2.0/CONTRIBUTING.md +116 -0
  4. memory_passport-0.2.0/LICENSE +21 -0
  5. memory_passport-0.2.0/PKG-INFO +221 -0
  6. memory_passport-0.2.0/PLAN.md +111 -0
  7. memory_passport-0.2.0/README.md +194 -0
  8. memory_passport-0.2.0/SPEC.md +232 -0
  9. memory_passport-0.2.0/docs/ADR-001.md +41 -0
  10. memory_passport-0.2.0/docs/assets/banner.svg +55 -0
  11. memory_passport-0.2.0/docs/assets/excluded.svg +16 -0
  12. memory_passport-0.2.0/docs/assets/flow.svg +67 -0
  13. memory_passport-0.2.0/docs/assets/import.gif +0 -0
  14. memory_passport-0.2.0/docs/assets/merge.gif +0 -0
  15. memory_passport-0.2.0/docs/assets/tags.svg +28 -0
  16. memory_passport-0.2.0/docs/assets/validate.gif +0 -0
  17. memory_passport-0.2.0/examples/sample-vault/areas/ledger-rewrite.md +18 -0
  18. memory_passport-0.2.0/examples/sample-vault/passport.yaml +4 -0
  19. memory_passport-0.2.0/examples/sample-vault/people/priya-nair.md +12 -0
  20. memory_passport-0.2.0/examples/sample-vault/preferences.md +19 -0
  21. memory_passport-0.2.0/examples/sample-vault/profile.md +19 -0
  22. memory_passport-0.2.0/examples/sample-vault/topics/e-ink-devices.md +11 -0
  23. memory_passport-0.2.0/memory_passport/__init__.py +8 -0
  24. memory_passport-0.2.0/memory_passport/cli.py +383 -0
  25. memory_passport-0.2.0/memory_passport/diff.py +81 -0
  26. memory_passport-0.2.0/memory_passport/exclusions.py +137 -0
  27. memory_passport-0.2.0/memory_passport/exporters/__init__.py +6 -0
  28. memory_passport-0.2.0/memory_passport/exporters/base.py +41 -0
  29. memory_passport-0.2.0/memory_passport/exporters/chatgpt.py +50 -0
  30. memory_passport-0.2.0/memory_passport/exporters/claude.py +28 -0
  31. memory_passport-0.2.0/memory_passport/exporters/claude_code.py +50 -0
  32. memory_passport-0.2.0/memory_passport/exporters/cursor.py +30 -0
  33. memory_passport-0.2.0/memory_passport/exporters/markdown.py +15 -0
  34. memory_passport-0.2.0/memory_passport/exporters/prompt.py +67 -0
  35. memory_passport-0.2.0/memory_passport/exporters/registry.py +42 -0
  36. memory_passport-0.2.0/memory_passport/importers/__init__.py +6 -0
  37. memory_passport-0.2.0/memory_passport/importers/base.py +56 -0
  38. memory_passport-0.2.0/memory_passport/importers/builder.py +87 -0
  39. memory_passport-0.2.0/memory_passport/importers/chatgpt.py +140 -0
  40. memory_passport-0.2.0/memory_passport/importers/claude.py +201 -0
  41. memory_passport-0.2.0/memory_passport/importers/copilot.py +16 -0
  42. memory_passport-0.2.0/memory_passport/importers/gemini.py +18 -0
  43. memory_passport-0.2.0/memory_passport/importers/markdown.py +74 -0
  44. memory_passport-0.2.0/memory_passport/importers/registry.py +36 -0
  45. memory_passport-0.2.0/memory_passport/importers/router.py +92 -0
  46. memory_passport-0.2.0/memory_passport/importers/text.py +75 -0
  47. memory_passport-0.2.0/memory_passport/importers/textlist.py +37 -0
  48. memory_passport-0.2.0/memory_passport/inspect_export.py +112 -0
  49. memory_passport-0.2.0/memory_passport/mcp_server.py +103 -0
  50. memory_passport-0.2.0/memory_passport/merge.py +147 -0
  51. memory_passport-0.2.0/memory_passport/model.py +290 -0
  52. memory_passport-0.2.0/memory_passport/schema.py +32 -0
  53. memory_passport-0.2.0/memory_passport/store.py +205 -0
  54. memory_passport-0.2.0/memory_passport/validate.py +270 -0
  55. memory_passport-0.2.0/pyproject.toml +78 -0
  56. memory_passport-0.2.0/spec/frontmatter.schema.json +50 -0
  57. memory_passport-0.2.0/spec/manifest.schema.json +28 -0
  58. memory_passport-0.2.0/tests/conftest.py +35 -0
  59. memory_passport-0.2.0/tests/fixtures/chatgpt/conversations.json +128 -0
  60. memory_passport-0.2.0/tests/fixtures/chatgpt/memories.txt +5 -0
  61. memory_passport-0.2.0/tests/fixtures/chatgpt/user.json +1 -0
  62. memory_passport-0.2.0/tests/fixtures/claude-code/MEMORY.md +2 -0
  63. memory_passport-0.2.0/tests/fixtures/claude-code/feedback_git.md +10 -0
  64. memory_passport-0.2.0/tests/fixtures/claude-code/project_ledger.md +13 -0
  65. memory_passport-0.2.0/tests/fixtures/claude-code/user_profile.md +8 -0
  66. memory_passport-0.2.0/tests/fixtures/claude-export/conversations.json +1 -0
  67. memory_passport-0.2.0/tests/fixtures/claude-export/projects.json +23 -0
  68. memory_passport-0.2.0/tests/fixtures/claude-export/users.json +1 -0
  69. memory_passport-0.2.0/tests/fixtures/claude-memory.txt +6 -0
  70. memory_passport-0.2.0/tests/fixtures/markdown/notes.md +5 -0
  71. memory_passport-0.2.0/tests/fixtures/markdown/people/dana.md +6 -0
  72. memory_passport-0.2.0/tests/test_cli.py +68 -0
  73. memory_passport-0.2.0/tests/test_exclusions.py +81 -0
  74. memory_passport-0.2.0/tests/test_exporters.py +61 -0
  75. memory_passport-0.2.0/tests/test_importers.py +177 -0
  76. memory_passport-0.2.0/tests/test_inspect.py +41 -0
  77. memory_passport-0.2.0/tests/test_mcp.py +28 -0
  78. memory_passport-0.2.0/tests/test_merge_diff.py +106 -0
  79. memory_passport-0.2.0/tests/test_model.py +95 -0
  80. memory_passport-0.2.0/tests/test_store.py +86 -0
  81. memory_passport-0.2.0/tests/test_validate.py +124 -0
@@ -0,0 +1,10 @@
1
+ .venv/
2
+ __pycache__/
3
+ *.pyc
4
+ dist/
5
+ build/
6
+ *.egg-info/
7
+ .pytest_cache/
8
+ .ruff_cache/
9
+ .DS_Store
10
+ _site/
@@ -0,0 +1,18 @@
1
+ # Changelog
2
+
3
+ ## 0.2.0 — 2026-09-22
4
+
5
+ - `passport show`, `passport add`, `passport forget`: use the vault day to day, not just for migration.
6
+ - `passport-mcp`: an MCP server (`pip install "memory-passport[mcp]"`) exposing `list_subjects`, `read_memory`, `remember`, `forget`.
7
+ - `--to prompt` exporter with `--budget`, a `<user_memory>` block for any model or agent.
8
+ - Gemini and Copilot importers from pasted text (`TextListImporter` base for others).
9
+ - Redaction instead of dropping for card, bank, ID and secret spans; health is still dropped.
10
+ - Semantic dedupe: common rewordings ("based in" / "lives in") collapse to one fact in import, merge and diff.
11
+ - `passport validate --stale DAYS` warns about old observed/inferred facts; stated facts never go stale.
12
+ - `passport inspect` reports what an export contains, for bug reports when a product changes its format.
13
+ - Browser playground at mohitagw15856.github.io/memory-passport (Pyodide; nothing uploaded).
14
+ - Release workflow with PyPI trusted publishing on `v*` tags.
15
+
16
+ ## 0.1.0 — 2026-09-22
17
+
18
+ - Spec 0.1, JSON Schemas, validator, ChatGPT/Claude/markdown importers, five exporters, merge and diff.
@@ -0,0 +1,116 @@
1
+ # Contributing
2
+
3
+ Thanks for helping make memory portable. The most useful contribution is an importer or exporter for a product we do not cover yet, and this guide is mostly about that.
4
+
5
+ ## Setup
6
+
7
+ ```bash
8
+ git clone https://github.com/mohitagw15856/memory-passport
9
+ cd memory-passport
10
+ uv sync --group dev
11
+ uv run pytest -q
12
+ uv run ruff check . && uv run ruff format --check .
13
+ ```
14
+
15
+ Python 3.11+. British English in prose and docstrings. Keep the CLI output terse.
16
+
17
+ ## Adding an importer
18
+
19
+ An importer turns a product's export into a `Vault`. Two ways to ship one:
20
+
21
+ - **In this repo:** add `memory_passport/importers/<product>.py` and register it in `registry.py` and `pyproject.toml`.
22
+ - **In your own package:** expose an entry point in the `memory_passport.importers` group. `passport import --from <name>` will find it with no change here.
23
+
24
+ ### 1. Find the real export format
25
+
26
+ Do not guess. Download an export from the product, open it, and put a **trimmed, fake-data** copy in `tests/fixtures/<product>/`. Every importer in this repo is written against a fixture that mirrors the real file layout. Note where memory actually lives; often it is not where you expect (ChatGPT's is inside `conversations.json`, addressed to a tool called `bio`).
27
+
28
+ ### 1a. Shortcut for products with no export
29
+
30
+ If the product will only *list* its memories when asked (Gemini, Copilot), subclass `TextListImporter` instead and set `name`, `help` and `prompt`. See `importers/gemini.py`; it is nine lines.
31
+
32
+ ### 2. Implement the class
33
+
34
+ ```python
35
+ # memory_passport/importers/acme.py
36
+ from pathlib import Path
37
+
38
+ from memory_passport.importers.base import Importer, ImportOptions, ImportResult
39
+ from memory_passport.importers.builder import VaultBuilder
40
+ from memory_passport.importers.text import is_hedged, tidy
41
+ from memory_passport.model import Fact
42
+
43
+
44
+ class AcmeImporter(Importer):
45
+ name = "acme" # used by --from and written into `sources`
46
+ help = "Acme Assistant memory export (memories.json)"
47
+
48
+ def detect(self, path: Path) -> bool:
49
+ return path.name == "memories.json"
50
+
51
+ def load(self, path: Path, options: ImportOptions) -> ImportResult:
52
+ b = VaultBuilder(self.name, allow_health=options.allow_health, do_route=options.route)
53
+ notes = []
54
+ for entry in read_acme(path): # your parsing
55
+ text = tidy(entry["text"])
56
+ tag = "inferred" if is_hedged(text) else "stated" # document your rule!
57
+ b.add(Fact(tag, text, date=entry.get("date")))
58
+ notes.append(f"read {len(...)} memories")
59
+ return ImportResult(vault=b.build(), notes=notes, dropped=b.dropped)
60
+ ```
61
+
62
+ `VaultBuilder` does the boring parts: exclusion scanning (facts that trip a detector go to `dropped`, never into the vault), dedupe by fact key, routing to `people/`, `topics/`, `areas/` or `preferences.md` (pass `to=Route(...)` to override), and frontmatter generation.
63
+
64
+ ### 3. Decide the provenance rule, and write it down
65
+
66
+ The spec says importers MUST default to `[inferred]` unless they have a documented reason to do better. Put the reason in the module docstring. "The product only stores what the user typed" is a reason. "It is probably fine" is not.
67
+
68
+ ### 4. Register it
69
+
70
+ In-repo:
71
+
72
+ ```python
73
+ # registry.py
74
+ from memory_passport.importers.acme import AcmeImporter
75
+ return [ChatGPTImporter, ClaudeImporter, MarkdownImporter, AcmeImporter]
76
+ ```
77
+
78
+ ```toml
79
+ # pyproject.toml
80
+ [project.entry-points."memory_passport.importers"]
81
+ acme = "memory_passport.importers.acme:AcmeImporter"
82
+ ```
83
+
84
+ External package:
85
+
86
+ ```toml
87
+ [project.entry-points."memory_passport.importers"]
88
+ acme = "passport_acme:AcmeImporter"
89
+ ```
90
+
91
+ ### 5. Test it
92
+
93
+ `tests/test_importers.py` shows the pattern. At minimum:
94
+
95
+ - `detect()` is true for the fixture and false for the other fixtures.
96
+ - The vault it produces passes `validate_vault()` with no errors.
97
+ - A fact that should be excluded (put a test card number in the fixture) ends up in `dropped`, not in the vault.
98
+ - Tags are what your documented rule says.
99
+
100
+ ### 6. Update the README comparison table
101
+
102
+ Add a row for the product: what its memory can and cannot express, and how the export and import actually work for users.
103
+
104
+ ## Adding an exporter
105
+
106
+ Same shape, smaller. Subclass `Exporter` in `memory_passport/exporters/`, return an `ExportResult` whose `files` map paths to content (use the single key `"-"` for "print to stdout"), and register it. Read `base.fact_sentence()` before writing: it is how inferred facts get hedged for products that have no provenance, and you should use it rather than dropping the distinction.
107
+
108
+ ## Changing the spec
109
+
110
+ Open an issue first. SPEC.md is versioned; anything that would break a 0.1 reader is a major bump and needs an ADR in `docs/`.
111
+
112
+ ## Pull requests
113
+
114
+ - One importer, exporter or fix per PR.
115
+ - CI runs ruff and pytest on 3.11, 3.12 and 3.13; it must be green.
116
+ - Commit messages in the imperative mood.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 mohitagw15856
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,221 @@
1
+ Metadata-Version: 2.5
2
+ Name: memory-passport
3
+ Version: 0.2.0
4
+ Summary: A portable, plain-text format for AI assistant memory, with converters.
5
+ Project-URL: Homepage, https://github.com/mohitagw15856/memory-passport
6
+ Project-URL: Issues, https://github.com/mohitagw15856/memory-passport/issues
7
+ Author: mohitagw15856
8
+ License-Expression: MIT
9
+ License-File: LICENSE
10
+ Keywords: ai,chatgpt,claude,markdown,memory,portability
11
+ Classifier: Development Status :: 3 - Alpha
12
+ Classifier: Environment :: Console
13
+ Classifier: Intended Audience :: Developers
14
+ Classifier: License :: OSI Approved :: MIT License
15
+ Classifier: Programming Language :: Python :: 3
16
+ Classifier: Programming Language :: Python :: 3.11
17
+ Classifier: Programming Language :: Python :: 3.12
18
+ Classifier: Programming Language :: Python :: 3.13
19
+ Classifier: Topic :: Text Processing :: Markup :: Markdown
20
+ Requires-Python: >=3.11
21
+ Requires-Dist: jsonschema>=4.20
22
+ Requires-Dist: pyyaml>=6.0
23
+ Requires-Dist: typer>=0.12
24
+ Provides-Extra: mcp
25
+ Requires-Dist: mcp>=1.2; extra == 'mcp'
26
+ Description-Content-Type: text/markdown
27
+
28
+ <p align="center">
29
+ <img src="docs/assets/banner.svg" alt="memory-passport: your AI memory, yours to take anywhere" width="100%">
30
+ </p>
31
+
32
+ <p align="center">
33
+ <a href="https://github.com/mohitagw15856/memory-passport/actions/workflows/ci.yml"><img alt="CI" src="https://github.com/mohitagw15856/memory-passport/actions/workflows/ci.yml/badge.svg"></a>
34
+ <img alt="Python 3.11+" src="https://img.shields.io/badge/python-3.11%20%7C%203.12%20%7C%203.13-3776ab?logo=python&logoColor=white">
35
+ <img alt="Spec 0.1" src="https://img.shields.io/badge/spec-0.1-7c3aed">
36
+ <a href="LICENSE"><img alt="MIT" src="https://img.shields.io/badge/licence-MIT-34d399"></a>
37
+ <img alt="British English" src="https://img.shields.io/badge/spelling-British%20🇬🇧-f472b6">
38
+ </p>
39
+
40
+ <h3 align="center">Every assistant remembers you. None of them will tell the others.</h3>
41
+
42
+ <p align="center">
43
+ ChatGPT knows you're vegetarian. Claude knows about the ledger rewrite. Cursor knows you hate semicolons.<br>
44
+ Switch products and you start from zero. <b>memory-passport</b> is a plain-text format, a spec, and a CLI that moves it all.
45
+ </p>
46
+
47
+ <p align="center">
48
+ <img src="docs/assets/flow.svg" alt="Silos in, one vault, any product out" width="100%">
49
+ </p>
50
+
51
+ ## ⏱️ 60-second quickstart
52
+
53
+ ```bash
54
+ pip install memory-passport # or: uv tool install memory-passport
55
+
56
+ # 1. Turn a ChatGPT data export into a vault (memories are hiding inside conversations.json)
57
+ passport import chatgpt-export.zip --out passport
58
+
59
+ # 2. Check it against the spec
60
+ passport validate passport
61
+
62
+ # 3. Paste it into Claude (Settings → Memory → Start import)
63
+ passport export passport --to claude | pbcopy
64
+ ```
65
+
66
+ That is the whole loop. Your vault is a folder of markdown files you can read, edit, and commit to git.
67
+
68
+ <p align="center">
69
+ <img src="docs/assets/import.gif" alt="passport import turning a ChatGPT export into a vault" width="90%">
70
+ </p>
71
+
72
+ ## 🗂️ What a passport looks like
73
+
74
+ ```
75
+ passport/
76
+ ├── passport.yaml # spec_version, allow_health
77
+ ├── profile.md # who you are
78
+ ├── preferences.md # how you like assistants to behave
79
+ ├── people/priya-nair.md # one file per person
80
+ ├── topics/e-ink-devices.md
81
+ └── areas/ledger-rewrite.md # one file per project
82
+ ```
83
+
84
+ Every file is YAML frontmatter plus bullet lines. Every bullet says **where it came from**:
85
+
86
+ ```markdown
87
+ ---
88
+ name: Priya Nair
89
+ description: Sam's manager; sets quarterly priorities and reviews design docs.
90
+ sources: [chatgpt, claude]
91
+ aliases: [Priya, PN]
92
+ updated: "2026-06-11"
93
+ kind: person
94
+ ---
95
+
96
+ - [stated] Prefers decisions written up as one-page ADRs before a meeting.
97
+ - [inferred] Likely based in the Edinburgh office, given meeting times. <!-- src: chatgpt, 2026-05-02 -->
98
+ ```
99
+
100
+ <p align="center">
101
+ <img src="docs/assets/tags.svg" alt="stated, observed and inferred tags" width="100%">
102
+ </p>
103
+
104
+ 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.
105
+
106
+ ## 🚫 What a passport refuses to carry
107
+
108
+ 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:
109
+
110
+ <p align="center">
111
+ <img src="docs/assets/excluded.svg" alt="Excluded categories" width="100%">
112
+ </p>
113
+
114
+ 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](SPEC.md#7-excluded-categories).
115
+
116
+ <p align="center">
117
+ <img src="docs/assets/validate.gif" alt="passport validate catching a card number, then exporting for Claude" width="90%">
118
+ </p>
119
+
120
+ ## 🔀 Merge without losing, diff without squinting
121
+
122
+ 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.
123
+
124
+ <p align="center">
125
+ <img src="docs/assets/merge.gif" alt="passport diff and merge with a conflict" width="90%">
126
+ </p>
127
+
128
+ ```bash
129
+ passport diff vault-a vault-b # + added, - removed, ~ retagged
130
+ passport merge vault-a vault-b --out merged
131
+ ```
132
+
133
+ ## 🧠 Live memory, not just luggage
134
+
135
+ 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.
136
+
137
+ ```bash
138
+ passport add passport "Priya moved to the Edinburgh office." --to person:Priya
139
+ passport show passport priya
140
+ passport show passport -q "british english"
141
+ passport forget passport "works four days a week"
142
+ passport validate passport --stale 365 # flag observed/inferred facts older than a year
143
+ ```
144
+
145
+ **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:
146
+
147
+ ```bash
148
+ claude mcp add passport -e PASSPORT_VAULT=~/passport -- passport-mcp
149
+ ```
150
+
151
+ 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.
152
+
153
+ **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.
154
+
155
+ ## 🌐 Try it in the browser
156
+
157
+ [mohitagw15856.github.io/memory-passport](https://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.
158
+
159
+ ## 🧭 Every command
160
+
161
+ | Command | What it does |
162
+ |---|---|
163
+ | `passport validate <dir> [--strict] [--json]` | Check a vault against the spec. Exit 1 on errors. |
164
+ | `passport import <export> [--from chatgpt\|claude\|gemini\|copilot\|markdown] [--out dir]` | Build a vault. Auto-detects the source when it can. |
165
+ | `passport import … --memory-text memories.txt` | Combine an export with a pasted memory list. |
166
+ | `passport import … --no-route` | Skip the people/topics/areas sorting; everything in `profile.md`. |
167
+ | `passport export <dir> --to prompt\|chatgpt\|claude\|claude-code\|cursor\|markdown [--out path] [--budget N]` | Paste-ready text or files for that product. |
168
+ | `passport merge <a> <b> --out merged` | Merge with conflict markers. Exit 3 if any conflicts. |
169
+ | `passport diff <a> <b> [--json]` | Fact-level diff. Exit 1 if they differ. |
170
+ | `passport show <dir> [subject] [-q words]` | List subjects, print one, or search facts. |
171
+ | `passport add <dir> "fact" [--to subject] [--tag] [--section]` | Append one dated fact, with exclusions applied. |
172
+ | `passport forget <dir> "fact"` | Remove a fact by text (loose match). |
173
+ | `passport inspect <export>` | Say what an export contains without importing it. Paste into bug reports. |
174
+ | `passport importers` / `passport exporters` | List what is installed, including plugins. |
175
+
176
+ ## 🧳 Where the memories actually are
177
+
178
+ Getting memory *out* of products is the annoying part. Here is what each one really offers, and what the passport does with it.
179
+
180
+ | Product | Export exists? | Import exists? | Where memory hides | Provenance | Per-subject structure | Hand-editable | Sensitive-data control |
181
+ |---|---|---|---|---|---|---|---|
182
+ | **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 |
183
+ | **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 |
184
+ | **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 |
185
+ | **Cursor** | No user memory; project rules in `.cursor/rules/*.mdc`. | Write a rule file. | rule files per repo | none | per repo | yes | none |
186
+ | **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 |
187
+ | **Copilot** | No export. Ask it to list its memories; `--from copilot`. | No. | flat list | none | none | via settings UI | delete individual items |
188
+ | **Hermes Agent / custom bots** | Whatever you built; usually a markdown folder. | Same. | your call | your call | your call | yes | your call |
189
+ | **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 |
190
+
191
+ 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.
192
+
193
+ ## 🔌 Pluggable
194
+
195
+ Adding a product is one class with `detect()` and `load()`, registered as an entry point. No fork needed:
196
+
197
+ ```toml
198
+ [project.entry-points."memory_passport.importers"]
199
+ acme = "passport_acme:AcmeImporter"
200
+ ```
201
+
202
+ `passport import --from acme` will find it. The walkthrough is in [CONTRIBUTING.md](CONTRIBUTING.md).
203
+
204
+ ## 📐 The spec
205
+
206
+ [SPEC.md](SPEC.md) defines the layout, the frontmatter (with a [JSON Schema](spec/frontmatter.schema.json)), fact lines, the three tags, the exclusions and why, merge semantics, and what the format deliberately cannot express. [docs/ADR-001.md](docs/ADR-001.md) explains why markdown plus frontmatter beat JSON.
207
+
208
+ The `examples/sample-vault/` folder is a complete, valid vault to poke at.
209
+
210
+ ## 🛠️ Developing
211
+
212
+ ```bash
213
+ git clone https://github.com/mohitagw15856/memory-passport && cd memory-passport
214
+ uv sync --group dev
215
+ uv run pytest -q # fixture exports for every importer
216
+ uv run ruff check .
217
+ ```
218
+
219
+ ## 📜 Licence
220
+
221
+ MIT © [mohitagw15856](https://github.com/mohitagw15856). Your memories are yours; this just helps them travel.
@@ -0,0 +1,111 @@
1
+ # memory-passport — plan
2
+
3
+ Portable AI-assistant memory: one plain-text format any product can export to and import from, plus converters.
4
+
5
+ ## 1. Schema
6
+
7
+ ### Vault layout
8
+
9
+ ```
10
+ vault/
11
+ profile.md # who the user is (role, location, languages, timezone)
12
+ preferences.md # how they like things done (tone, formats, tools, workflows)
13
+ people/<slug>.md # one file per person (partner, colleague, client)
14
+ topics/<slug>.md # one file per domain of interest or expertise
15
+ areas/<slug>.md # one file per ongoing project or responsibility
16
+ passport.yaml # vault manifest: spec_version, created, exported_by
17
+ ```
18
+
19
+ Slugs are lower-case kebab-case ASCII. `profile.md` and `preferences.md` are singletons; the three folders hold zero or more files.
20
+
21
+ ### Frontmatter (YAML)
22
+
23
+ | Field | Type | Required | Notes |
24
+ |---|---|---|---|
25
+ | `name` | string | yes | Human-readable subject name |
26
+ | `description` | string | yes | One line, used for recall ranking |
27
+ | `sources` | list of strings | yes | Products this file draws from, e.g. `chatgpt`, `claude`, `claude-code`, `cursor`, `manual` |
28
+ | `aliases` | list of strings | no | Other names for the subject |
29
+ | `updated` | ISO 8601 date | yes | Last time any fact line changed |
30
+ | `kind` | enum | yes | `profile`, `preferences`, `person`, `topic`, `area` — derived from path, but stored so a file is self-describing when moved |
31
+
32
+ A JSON Schema (draft 2020-12) in `spec/frontmatter.schema.json` is the normative definition; SPEC.md prose explains it.
33
+
34
+ ### Body
35
+
36
+ - Markdown. Optional `##` sections for grouping; sections are free-form and not part of the spec.
37
+ - Every fact is one bullet line: `- [tag] fact text` where tag is `stated`, `observed`, or `inferred`.
38
+ - `[stated]` — the user said it explicitly.
39
+ - `[observed]` — the product saw it happen (files edited, timezone of activity, tools used).
40
+ - `[inferred]` — the product guessed it from patterns. Least trusted; importers should default here when the source product does not distinguish.
41
+ - Optional trailing metadata on a fact line: `<!-- src: chatgpt, 2025-11-03 -->` (kept as an HTML comment so it survives any markdown renderer and can be stripped for paste-export).
42
+ - Lines that are not fact lines (headings, blank, prose) are allowed but the validator warns if a bullet lacks a tag.
43
+
44
+ ### Exclusions
45
+
46
+ The spec forbids by default, and the validator rejects: government ID numbers, payment card / bank account numbers, passwords and API keys, and health diagnoses / medications. Detection is regex plus a small keyword list; health can be opted in per vault via `passport.yaml: allow_health: true`. Rationale in SPEC.md: a passport is designed to be pasted into many products, so it is the worst place to keep anything you would not want a third party to hold; the format should make the safe thing the default.
47
+
48
+ ## 2. Converter architecture
49
+
50
+ ```
51
+ memory_passport/
52
+ model.py # Vault, MemoryFile, Fact dataclasses; parse/serialise
53
+ schema.py # loads JSON Schema; validate_frontmatter()
54
+ validate.py # structural + provenance + exclusion checks -> list[Issue]
55
+ exclusions.py # sensitive-data detectors
56
+ merge.py # three-way-ish merge with <<<<<<< a / ======= / >>>>>>> b markers on conflicting fact lines
57
+ diff.py # per-file, per-fact added/removed/changed
58
+ importers/
59
+ base.py # class Importer(Protocol): name, detect(path) -> bool, load(path) -> Vault
60
+ registry.py # entry-point group `memory_passport.importers` + built-ins
61
+ chatgpt.py
62
+ claude.py
63
+ markdown.py
64
+ exporters/
65
+ base.py # class Exporter(Protocol): name, render(vault) -> str | Path
66
+ registry.py
67
+ chatgpt.py # paste-ready block for Custom Instructions / "remember this"
68
+ claude.py # paste-ready block for Claude project instructions / user preferences
69
+ claude_code.py# writes CLAUDE.md-style + memory/*.md
70
+ cursor.py # .cursor/rules/user.mdc
71
+ markdown.py # identity export
72
+ cli.py # Typer: validate, import, export, merge, diff, list-importers
73
+ ```
74
+
75
+ Importers are pluggable two ways: register in `registry.py`, or expose a `memory_passport.importers` entry point in any third-party package. `passport import --from <name>` looks up the registry by name.
76
+
77
+ ### Real export formats (verified before coding; to be re-checked with web search at implementation time)
78
+
79
+ **ChatGPT** — the data export zip contains `conversations.json`, `user.json`, `message_feedback.json`, `shared_conversations.json`, `chat.html`. There is no memory file. Memories are recoverable from `conversations.json`: when ChatGPT saves a memory it emits an assistant message with `recipient: "bio"` whose `content.parts[0]` is the memory sentence. The importer will:
80
+ 1. Walk every conversation's `mapping`, collect messages where `author.role == "assistant"` and `recipient == "bio"`.
81
+ 2. Also accept a plain-text file pasted from Settings → Personalisation → Manage memories (one memory per line), because many users will find that easier.
82
+ 3. Default tag `[stated]` for bio entries phrased "User said/prefers", `[inferred]` otherwise; heuristic and documented.
83
+ 4. Route each memory to a file by simple classifier (name mentions → people/, "working on/project" → areas/, "prefers/likes" → preferences.md, else profile.md). Anything unrouted lands in `profile.md` under `## Unsorted`.
84
+
85
+ **Claude** — two real inputs:
86
+ 1. claude.ai data export zip: `conversations.json` (list of conversations with `chat_messages[].text`, `sender`), `projects.json`, `users.json`. No memory file. The importer extracts `projects.json` prompt templates as `[stated]` preferences and offers `--from claude --memory-text <file>` for the memory summary pasted from Settings → Memory.
87
+ 2. Claude Code auto-memory dir (`~/.claude/projects/<slug>/memory/*.md`, frontmatter `name`, `description`, `metadata.type` in `user|feedback|project|reference`) plus any `CLAUDE.md`. This is a near-native mapping: `user` → profile/preferences, `project` → areas/, `reference` → topics/, `feedback` → preferences. Tag `[stated]`.
88
+
89
+ **markdown** — any folder of `.md` files; frontmatter fields present are kept, missing ones synthesised; untagged bullets become `[inferred]` with a warning.
90
+
91
+ ### Exporters
92
+
93
+ Each exporter renders the vault into what the target accepts, noting the target's limits in the README comparison table (e.g. ChatGPT Custom Instructions ≈ 1,500 chars each box, no provenance; Claude user preferences free text; Cursor rules files; Hermes/custom bots = the markdown vault itself).
94
+
95
+ ## 3. MVP cut
96
+
97
+ Ship in this order, stopping where you asked:
98
+
99
+ 1. **Spec + validator** — SPEC.md, JSON Schema, `model.py`, `validate.py`, `exclusions.py`, `passport validate`, tests, examples/sample-vault. **→ Stop and show you the spec.**
100
+ 2. **First importer: ChatGPT** — zip + pasted-memories text, fixture export in `tests/fixtures/chatgpt/`. **→ Stop after it works.**
101
+ 3. Claude importer (export zip + Claude Code memory dir), markdown importer.
102
+ 4. Exporters (markdown, claude, claude-code, chatgpt, cursor).
103
+ 5. `merge` and `diff`.
104
+ 6. README quickstart + comparison table, CONTRIBUTING.md, docs/ADR-001.md, CI (ruff + pytest on 3.11–3.13), LICENSE.
105
+
106
+ Out of scope for MVP: Gemini/Cursor importers (no stable export), semantic dedupe on merge (exact-line and normalised-whitespace only), encryption.
107
+
108
+ ## Tooling
109
+
110
+ - Python ≥ 3.11, `uv` for env, `pyproject.toml` (hatchling), deps: `typer`, `pyyaml`, `jsonschema`, `python-frontmatter`. Dev: `pytest`, `ruff`.
111
+ - British English in all prose; MIT, author mohitagw15856.