msgsearch 0.2.2__tar.gz → 0.2.4__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 (54) hide show
  1. {msgsearch-0.2.2 → msgsearch-0.2.4}/CHANGELOG.md +22 -0
  2. {msgsearch-0.2.2 → msgsearch-0.2.4}/PKG-INFO +41 -30
  3. {msgsearch-0.2.2 → msgsearch-0.2.4}/README.md +40 -29
  4. {msgsearch-0.2.2 → msgsearch-0.2.4}/pyproject.toml +1 -1
  5. {msgsearch-0.2.2 → msgsearch-0.2.4}/src/msgsearch/__init__.py +1 -1
  6. {msgsearch-0.2.2 → msgsearch-0.2.4}/src/msgsearch/cli.py +6 -0
  7. {msgsearch-0.2.2 → msgsearch-0.2.4}/.claude/agents/corpus-explorer.md +0 -0
  8. {msgsearch-0.2.2 → msgsearch-0.2.4}/.claude/agents/retrieval-critic.md +0 -0
  9. {msgsearch-0.2.2 → msgsearch-0.2.4}/.claude/commands/bench.md +0 -0
  10. {msgsearch-0.2.2 → msgsearch-0.2.4}/.claude/commands/go.md +0 -0
  11. {msgsearch-0.2.2 → msgsearch-0.2.4}/.claude/commands/recon.md +0 -0
  12. {msgsearch-0.2.2 → msgsearch-0.2.4}/.claude/hooks/verify-retrieval.py +0 -0
  13. {msgsearch-0.2.2 → msgsearch-0.2.4}/.claude/settings.json +0 -0
  14. {msgsearch-0.2.2 → msgsearch-0.2.4}/.github/workflows/ci.yml +0 -0
  15. {msgsearch-0.2.2 → msgsearch-0.2.4}/.github/workflows/release.yml +0 -0
  16. {msgsearch-0.2.2 → msgsearch-0.2.4}/.gitignore +0 -0
  17. {msgsearch-0.2.2 → msgsearch-0.2.4}/AGENTS.md +0 -0
  18. {msgsearch-0.2.2 → msgsearch-0.2.4}/ARCHITECTURE.md +0 -0
  19. {msgsearch-0.2.2 → msgsearch-0.2.4}/CLAUDE.md +0 -0
  20. {msgsearch-0.2.2 → msgsearch-0.2.4}/CONTRIBUTING.md +0 -0
  21. {msgsearch-0.2.2 → msgsearch-0.2.4}/LICENSE +0 -0
  22. {msgsearch-0.2.2 → msgsearch-0.2.4}/eval/__init__.py +0 -0
  23. {msgsearch-0.2.2 → msgsearch-0.2.4}/eval/bench.py +0 -0
  24. {msgsearch-0.2.2 → msgsearch-0.2.4}/eval/gold.jsonl +0 -0
  25. {msgsearch-0.2.2 → msgsearch-0.2.4}/eval/label.py +0 -0
  26. {msgsearch-0.2.2 → msgsearch-0.2.4}/eval/results/baseline.json +0 -0
  27. {msgsearch-0.2.2 → msgsearch-0.2.4}/eval/results/retriever.json +0 -0
  28. {msgsearch-0.2.2 → msgsearch-0.2.4}/eval/results/retriever_bm25.json +0 -0
  29. {msgsearch-0.2.2 → msgsearch-0.2.4}/eval/results/retriever_dense.json +0 -0
  30. {msgsearch-0.2.2 → msgsearch-0.2.4}/eval/results/retriever_norerank.json +0 -0
  31. {msgsearch-0.2.2 → msgsearch-0.2.4}/eval/retriever.py +0 -0
  32. {msgsearch-0.2.2 → msgsearch-0.2.4}/eval/retriever_bm25.py +0 -0
  33. {msgsearch-0.2.2 → msgsearch-0.2.4}/eval/retriever_dense.py +0 -0
  34. {msgsearch-0.2.2 → msgsearch-0.2.4}/eval/retriever_norerank.py +0 -0
  35. {msgsearch-0.2.2 → msgsearch-0.2.4}/src/msgsearch/attributed_body.py +0 -0
  36. {msgsearch-0.2.2 → msgsearch-0.2.4}/src/msgsearch/chunk.py +0 -0
  37. {msgsearch-0.2.2 → msgsearch-0.2.4}/src/msgsearch/config.py +0 -0
  38. {msgsearch-0.2.2 → msgsearch-0.2.4}/src/msgsearch/contacts.py +0 -0
  39. {msgsearch-0.2.2 → msgsearch-0.2.4}/src/msgsearch/doctor.py +0 -0
  40. {msgsearch-0.2.2 → msgsearch-0.2.4}/src/msgsearch/embedder.py +0 -0
  41. {msgsearch-0.2.2 → msgsearch-0.2.4}/src/msgsearch/explore.py +0 -0
  42. {msgsearch-0.2.2 → msgsearch-0.2.4}/src/msgsearch/extract.py +0 -0
  43. {msgsearch-0.2.2 → msgsearch-0.2.4}/src/msgsearch/index.py +0 -0
  44. {msgsearch-0.2.2 → msgsearch-0.2.4}/src/msgsearch/py.typed +0 -0
  45. {msgsearch-0.2.2 → msgsearch-0.2.4}/src/msgsearch/search.py +0 -0
  46. {msgsearch-0.2.2 → msgsearch-0.2.4}/src/msgsearch/sync.py +0 -0
  47. {msgsearch-0.2.2 → msgsearch-0.2.4}/src/msgsearch/tagging.py +0 -0
  48. {msgsearch-0.2.2 → msgsearch-0.2.4}/tests/test_chunk.py +0 -0
  49. {msgsearch-0.2.2 → msgsearch-0.2.4}/tests/test_cli.py +4 -4
  50. {msgsearch-0.2.2 → msgsearch-0.2.4}/tests/test_contacts.py +0 -0
  51. {msgsearch-0.2.2 → msgsearch-0.2.4}/tests/test_index.py +0 -0
  52. {msgsearch-0.2.2 → msgsearch-0.2.4}/tests/test_search.py +0 -0
  53. {msgsearch-0.2.2 → msgsearch-0.2.4}/tests/test_sync.py +0 -0
  54. {msgsearch-0.2.2 → msgsearch-0.2.4}/tests/test_tagging.py +0 -0
@@ -3,6 +3,28 @@
3
3
  Notable changes to msgsearch. Retrieval changes carry the measurement that
4
4
  justified them; see `ARCHITECTURE.md` for the full evaluation.
5
5
 
6
+ ## [0.2.4] — 2026-09-06
7
+
8
+ Documentation only; released because PyPI freezes a project's description at
9
+ publish time.
10
+
11
+ ### Fixed
12
+ - The Setup section explained the gated model twice, and the second copy used a
13
+ development path (`./.venv/bin/msgsearch`) that an installed user does not have.
14
+ - Model access is now spelled out step by step: create an account, accept the
15
+ Gemma licence, create a **Read** token, run `msgsearch login`. It previously
16
+ assumed a HuggingFace account already existed and did not say which kind of
17
+ token to make.
18
+ - States explicitly that the model downloads automatically on first index, about
19
+ 1.2 GB, so nobody goes looking for a download step that does not exist.
20
+
21
+ ## [0.2.3] — 2026-09-06
22
+
23
+ ### Fixed
24
+ - Model-loading failures printed a Python traceback around their instructions,
25
+ which reads as a crash rather than "you need to log in" and buries the steps
26
+ under a stack. They now print the guidance alone and exit non-zero.
27
+
6
28
  ## [0.2.2] — 2026-09-06
7
29
 
8
30
  ### Added
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: msgsearch
3
- Version: 0.2.2
3
+ Version: 0.2.4
4
4
  Summary: Search your iMessage history by meaning, entirely on your own machine.
5
5
  Project-URL: Homepage, https://github.com/dhruv1707/msgsearch
6
6
  Project-URL: Documentation, https://github.com/dhruv1707/msgsearch/blob/main/README.md
@@ -168,49 +168,60 @@ arch -arm64 python3 -m venv .venv # must be an arm64 interpreter
168
168
 
169
169
  ## Setup
170
170
 
171
- Get access to the embedding model. `google/embeddinggemma-300m` is gated, so:
171
+ ### 1. Model access
172
172
 
173
- 1. Accept the licence at <https://huggingface.co/google/embeddinggemma-300m>
174
- 2. Create a read token at <https://huggingface.co/settings/tokens>
175
- 3. Run `msgsearch login` and paste it
173
+ The default embedding model, `google/embeddinggemma-300m`, is gated: Google
174
+ requires you to accept its licence before downloading. This is a one-time thing.
176
175
 
177
- `msgsearch login` checks afterwards that you can actually reach the model, since
178
- being logged in and having accepted the licence are different things — and the
179
- failure for the second looks identical to the first.
176
+ 1. **Create a HuggingFace account** if you do not have one <https://huggingface.co/join>. Free.
177
+ 2. **Accept the licence.** Open <https://huggingface.co/google/embeddinggemma-300m>,
178
+ sign in, and click the button acknowledging the Gemma terms at the top of the
179
+ page. Approval is normally immediate.
180
+ 3. **Create a token.** Go to <https://huggingface.co/settings/tokens>, click
181
+ *Create new token*, give it the **Read** role, and copy it. It looks like
182
+ `hf_...`.
183
+ 4. **Log in:**
180
184
 
181
- Any sentence-transformers model works instead, for example
182
- `MSGSEARCH_EMBED_MODEL=BAAI/bge-small-en-v1.5`.
185
+ ```bash
186
+ msgsearch login
187
+ ```
183
188
 
184
- Take a snapshot of the Messages database. **Never point this tool at
185
- `~/Library/Messages`**: that file is live, Messages.app holds locks on it, and it
186
- is irreplaceable.
189
+ Paste the token when prompted. It is stored in `~/.cache/huggingface`, not by
190
+ msgsearch. You can also pass it directly with `msgsearch login --token hf_...`.
191
+
192
+ `msgsearch login` checks afterwards that the model is genuinely reachable, rather
193
+ than only that the token is valid. Those are different conditions — you can be
194
+ perfectly logged in and still be refused because step 2 was skipped — and
195
+ HuggingFace reports both as the same 401 saying "please log in", which sends
196
+ people back to re-do the step they already did.
197
+
198
+ The model itself downloads automatically the first time you index, about 1.2 GB,
199
+ cached in `~/.cache/huggingface`. There is no separate download step.
200
+
201
+ **Don't want a HuggingFace account?** Use an ungated model instead. Retrieval is
202
+ somewhat weaker, but nothing else changes:
187
203
 
188
204
  ```bash
189
- msgsearch sync
205
+ export MSGSEARCH_EMBED_MODEL=BAAI/bge-small-en-v1.5
190
206
  ```
191
207
 
192
- This needs Full Disk Access, which macOS grants to the *application* running the
193
- command (System Settings → Privacy & Security → Full Disk Access). A terminal
194
- usually has it; an editor's integrated terminal often does not.
195
-
196
- It uses SQLite's backup API rather than `cp`, which matters more than it sounds.
197
- Messages runs in WAL mode, so your most recent messages live in a `chat.db-wal`
198
- sidecar until they are checkpointed — `cp chat.db` alone loses exactly the
199
- messages you are most likely to search for, silently. The snapshot folds the
200
- write-ahead log in and leaves a single self-contained file.
208
+ ### 2. Snapshot your messages
201
209
 
202
- The default embedding model, `google/embeddinggemma-300m`, is gated. You must
203
- accept the Gemma licence at
204
- <https://huggingface.co/google/embeddinggemma-300m> and authenticate:
210
+ **Never point this tool at `~/Library/Messages`** — that file is live, Messages
211
+ holds locks on it, and it is irreplaceable.
205
212
 
206
213
  ```bash
207
- ./.venv/bin/msgsearch login
214
+ msgsearch sync
208
215
  ```
209
216
 
210
- Any sentence-transformers model works instead if you would rather not, for example
211
- `MSGSEARCH_EMBED_MODEL=BAAI/bge-small-en-v1.5`.
217
+ This needs Full Disk Access (see above for how to grant it). It uses SQLite's
218
+ backup API rather than `cp`, which matters more than it sounds: Messages runs in
219
+ WAL mode, so your most recent messages live in a `chat.db-wal` sidecar until they
220
+ are checkpointed. `cp chat.db` alone drops exactly the messages you are most
221
+ likely to search for, silently. The snapshot folds the write-ahead log in and
222
+ leaves a single self-contained file.
212
223
 
213
- ### Names
224
+ ### 3. Names (optional)
214
225
 
215
226
  Without names, speakers appear as phone numbers. Resolving them makes results
216
227
  readable *and* improves retrieval, because the speaker label is part of the text
@@ -122,49 +122,60 @@ arch -arm64 python3 -m venv .venv # must be an arm64 interpreter
122
122
 
123
123
  ## Setup
124
124
 
125
- Get access to the embedding model. `google/embeddinggemma-300m` is gated, so:
125
+ ### 1. Model access
126
126
 
127
- 1. Accept the licence at <https://huggingface.co/google/embeddinggemma-300m>
128
- 2. Create a read token at <https://huggingface.co/settings/tokens>
129
- 3. Run `msgsearch login` and paste it
127
+ The default embedding model, `google/embeddinggemma-300m`, is gated: Google
128
+ requires you to accept its licence before downloading. This is a one-time thing.
130
129
 
131
- `msgsearch login` checks afterwards that you can actually reach the model, since
132
- being logged in and having accepted the licence are different things — and the
133
- failure for the second looks identical to the first.
130
+ 1. **Create a HuggingFace account** if you do not have one <https://huggingface.co/join>. Free.
131
+ 2. **Accept the licence.** Open <https://huggingface.co/google/embeddinggemma-300m>,
132
+ sign in, and click the button acknowledging the Gemma terms at the top of the
133
+ page. Approval is normally immediate.
134
+ 3. **Create a token.** Go to <https://huggingface.co/settings/tokens>, click
135
+ *Create new token*, give it the **Read** role, and copy it. It looks like
136
+ `hf_...`.
137
+ 4. **Log in:**
134
138
 
135
- Any sentence-transformers model works instead, for example
136
- `MSGSEARCH_EMBED_MODEL=BAAI/bge-small-en-v1.5`.
139
+ ```bash
140
+ msgsearch login
141
+ ```
137
142
 
138
- Take a snapshot of the Messages database. **Never point this tool at
139
- `~/Library/Messages`**: that file is live, Messages.app holds locks on it, and it
140
- is irreplaceable.
143
+ Paste the token when prompted. It is stored in `~/.cache/huggingface`, not by
144
+ msgsearch. You can also pass it directly with `msgsearch login --token hf_...`.
145
+
146
+ `msgsearch login` checks afterwards that the model is genuinely reachable, rather
147
+ than only that the token is valid. Those are different conditions — you can be
148
+ perfectly logged in and still be refused because step 2 was skipped — and
149
+ HuggingFace reports both as the same 401 saying "please log in", which sends
150
+ people back to re-do the step they already did.
151
+
152
+ The model itself downloads automatically the first time you index, about 1.2 GB,
153
+ cached in `~/.cache/huggingface`. There is no separate download step.
154
+
155
+ **Don't want a HuggingFace account?** Use an ungated model instead. Retrieval is
156
+ somewhat weaker, but nothing else changes:
141
157
 
142
158
  ```bash
143
- msgsearch sync
159
+ export MSGSEARCH_EMBED_MODEL=BAAI/bge-small-en-v1.5
144
160
  ```
145
161
 
146
- This needs Full Disk Access, which macOS grants to the *application* running the
147
- command (System Settings → Privacy & Security → Full Disk Access). A terminal
148
- usually has it; an editor's integrated terminal often does not.
149
-
150
- It uses SQLite's backup API rather than `cp`, which matters more than it sounds.
151
- Messages runs in WAL mode, so your most recent messages live in a `chat.db-wal`
152
- sidecar until they are checkpointed — `cp chat.db` alone loses exactly the
153
- messages you are most likely to search for, silently. The snapshot folds the
154
- write-ahead log in and leaves a single self-contained file.
162
+ ### 2. Snapshot your messages
155
163
 
156
- The default embedding model, `google/embeddinggemma-300m`, is gated. You must
157
- accept the Gemma licence at
158
- <https://huggingface.co/google/embeddinggemma-300m> and authenticate:
164
+ **Never point this tool at `~/Library/Messages`** — that file is live, Messages
165
+ holds locks on it, and it is irreplaceable.
159
166
 
160
167
  ```bash
161
- ./.venv/bin/msgsearch login
168
+ msgsearch sync
162
169
  ```
163
170
 
164
- Any sentence-transformers model works instead if you would rather not, for example
165
- `MSGSEARCH_EMBED_MODEL=BAAI/bge-small-en-v1.5`.
171
+ This needs Full Disk Access (see above for how to grant it). It uses SQLite's
172
+ backup API rather than `cp`, which matters more than it sounds: Messages runs in
173
+ WAL mode, so your most recent messages live in a `chat.db-wal` sidecar until they
174
+ are checkpointed. `cp chat.db` alone drops exactly the messages you are most
175
+ likely to search for, silently. The snapshot folds the write-ahead log in and
176
+ leaves a single self-contained file.
166
177
 
167
- ### Names
178
+ ### 3. Names (optional)
168
179
 
169
180
  Without names, speakers appear as phone numbers. Resolving them makes results
170
181
  readable *and* improves retrieval, because the speaker label is part of the text
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "msgsearch"
7
- version = "0.2.2"
7
+ version = "0.2.4"
8
8
  description = "Search your iMessage history by meaning, entirely on your own machine."
9
9
  readme = "README.md"
10
10
  license = { file = "LICENSE" }
@@ -13,6 +13,6 @@ The pipeline, in the order data flows through it:
13
13
  Nothing here sends message text anywhere. Both models run locally.
14
14
  """
15
15
 
16
- __version__ = "0.2.2"
16
+ __version__ = "0.2.4"
17
17
 
18
18
  __all__ = ["__version__"]
@@ -398,6 +398,12 @@ def main(argv: list[str] | None = None) -> int:
398
398
  return args.handler(args)
399
399
  except KeyboardInterrupt:
400
400
  return 130
401
+ except RuntimeError as error:
402
+ # Model loading raises RuntimeError carrying instructions the user can
403
+ # act on. A traceback around them reads as "this crashed" rather than
404
+ # "you need to log in", and buries the steps under a stack.
405
+ print(error, file=sys.stderr)
406
+ return 1
401
407
 
402
408
 
403
409
  if __name__ == "__main__":
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
@@ -73,10 +73,6 @@ class TestEntryPoint(unittest.TestCase):
73
73
  cli.main(["nonsense"])
74
74
 
75
75
 
76
- if __name__ == "__main__":
77
- unittest.main()
78
-
79
-
80
76
  class TestLoginCommand(unittest.TestCase):
81
77
  """Authentication is a msgsearch command because `hf` is not on the PATH.
82
78
 
@@ -98,3 +94,7 @@ class TestLoginCommand(unittest.TestCase):
98
94
 
99
95
  def test_token_is_optional_so_it_can_prompt(self):
100
96
  self.assertIsNone(self.parser.parse_args(["login"]).token)
97
+
98
+
99
+ if __name__ == "__main__":
100
+ unittest.main()
File without changes
File without changes