thread-archive 0.0.2__py3-none-any.whl

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 (132) hide show
  1. thread_archive/__init__.py +34 -0
  2. thread_archive/_api.py +2235 -0
  3. thread_archive/_config.py +126 -0
  4. thread_archive/_importers/__init__.py +81 -0
  5. thread_archive/_importers/_continuation.py +136 -0
  6. thread_archive/_importers/_cursor.py +103 -0
  7. thread_archive/_importers/_events.py +244 -0
  8. thread_archive/_importers/_line_stream.py +193 -0
  9. thread_archive/_importers/_read.py +82 -0
  10. thread_archive/_importers/_result.py +17 -0
  11. thread_archive/_importers/_sidecar.py +113 -0
  12. thread_archive/_importers/_state.py +240 -0
  13. thread_archive/_importers/_titles.py +179 -0
  14. thread_archive/_importers/antigravity.py +254 -0
  15. thread_archive/_importers/claude_code.py +293 -0
  16. thread_archive/_importers/claude_science.py +330 -0
  17. thread_archive/_importers/cloth.py +20 -0
  18. thread_archive/_importers/codex.py +324 -0
  19. thread_archive/_importers/cowork.py +107 -0
  20. thread_archive/_importers/cursor.py +432 -0
  21. thread_archive/_importers/exports.py +389 -0
  22. thread_archive/_importers/grok.py +600 -0
  23. thread_archive/_importers/opencode.py +538 -0
  24. thread_archive/_knowledge/__init__.py +67 -0
  25. thread_archive/_knowledge/_claims.py +116 -0
  26. thread_archive/_knowledge/_community.py +75 -0
  27. thread_archive/_knowledge/graph.py +208 -0
  28. thread_archive/_knowledge/materialize.py +212 -0
  29. thread_archive/_knowledge/write.py +438 -0
  30. thread_archive/_launchd.py +167 -0
  31. thread_archive/_mcp/__init__.py +1 -0
  32. thread_archive/_mcp/librarian.py +151 -0
  33. thread_archive/_mcp/server.py +307 -0
  34. thread_archive/_retrieval/__init__.py +280 -0
  35. thread_archive/_retrieval/_classify.py +80 -0
  36. thread_archive/_retrieval/_codex.py +157 -0
  37. thread_archive/_retrieval/_context.py +123 -0
  38. thread_archive/_retrieval/_extract.py +184 -0
  39. thread_archive/_retrieval/embed.py +186 -0
  40. thread_archive/_retrieval/format.py +149 -0
  41. thread_archive/_retrieval/fts.py +538 -0
  42. thread_archive/_retrieval/rank.py +228 -0
  43. thread_archive/_retrieval/read.py +908 -0
  44. thread_archive/_retrieval/rerank.py +143 -0
  45. thread_archive/_retrieval/vectors.py +611 -0
  46. thread_archive/_scripts/__init__.py +1 -0
  47. thread_archive/_scripts/backfill_recompute.py +328 -0
  48. thread_archive/_scripts/backfill_reconcile.py +296 -0
  49. thread_archive/_scripts/denamespace_dedup_keys.py +120 -0
  50. thread_archive/_scripts/recover_dropped_events.py +190 -0
  51. thread_archive/_scripts/repair_grok_tool_names.py +118 -0
  52. thread_archive/_scripts/repair_grok_tool_names_backup_20260704T155625Z.json +275 -0
  53. thread_archive/_scripts/repair_grok_tool_names_plan_20260704.json +435 -0
  54. thread_archive/_setup/__init__.py +12 -0
  55. thread_archive/_setup/clients.py +89 -0
  56. thread_archive/_setup/wizard.py +474 -0
  57. thread_archive/_store/__init__.py +45 -0
  58. thread_archive/_store/_base.py +173 -0
  59. thread_archive/_store/_defaults.py +57 -0
  60. thread_archive/_store/_types.py +38 -0
  61. thread_archive/_store/models.py +370 -0
  62. thread_archive/_store/schema.py +84 -0
  63. thread_archive/_thread_import/__init__.py +40 -0
  64. thread_archive/_thread_import/api.py +78 -0
  65. thread_archive/_thread_import/event_builder.py +810 -0
  66. thread_archive/_thread_import/exporters/__init__.py +9 -0
  67. thread_archive/_thread_import/exporters/_cursor_kv_mixin.py +166 -0
  68. thread_archive/_thread_import/exporters/_cursor_parse_mixin.py +149 -0
  69. thread_archive/_thread_import/exporters/cursor.py +582 -0
  70. thread_archive/_thread_import/exporters/cursor_parse.py +145 -0
  71. thread_archive/_thread_import/parsers/__init__.py +191 -0
  72. thread_archive/_thread_import/parsers/base.py +698 -0
  73. thread_archive/_thread_import/parsers/chatgpt.py +722 -0
  74. thread_archive/_thread_import/parsers/chatgpt_content.py +332 -0
  75. thread_archive/_thread_import/parsers/claude.py +443 -0
  76. thread_archive/_thread_import/parsers/claude_code.py +1035 -0
  77. thread_archive/_thread_import/parsers/claude_code_blocks.py +415 -0
  78. thread_archive/_thread_import/parsers/claude_code_ide.py +122 -0
  79. thread_archive/_thread_import/parsers/claude_code_sessions.py +90 -0
  80. thread_archive/_thread_import/parsers/config/__init__.py +26 -0
  81. thread_archive/_thread_import/parsers/config/base.py +220 -0
  82. thread_archive/_thread_import/parsers/cursor.py +538 -0
  83. thread_archive/_thread_import/parsers/cursor_blocks.py +365 -0
  84. thread_archive/_thread_import/parsers/pipeline/__init__.py +29 -0
  85. thread_archive/_thread_import/parsers/pipeline/base.py +251 -0
  86. thread_archive/_thread_import/parsers/pipeline/interfaces.py +191 -0
  87. thread_archive/_thread_import/parsers/transformers/__init__.py +22 -0
  88. thread_archive/_thread_import/parsers/transformers/active_path.py +87 -0
  89. thread_archive/_thread_import/parsers/transformers/coalescing.py +389 -0
  90. thread_archive/_thread_import/parsers/transformers/ide_context.py +158 -0
  91. thread_archive/_thread_import/parsers/transformers/thinking_merge.py +68 -0
  92. thread_archive/_thread_import/parsers/types/__init__.py +56 -0
  93. thread_archive/_thread_import/parsers/types/chatgpt.py +99 -0
  94. thread_archive/_thread_import/parsers/types/claude.py +120 -0
  95. thread_archive/_thread_import/parsers/types/claude_code.py +139 -0
  96. thread_archive/_thread_import/parsers/types/cursor.py +109 -0
  97. thread_archive/_thread_import/parsers/validators/__init__.py +83 -0
  98. thread_archive/_thread_import/parsers/validators/base.py +168 -0
  99. thread_archive/_thread_import/parsers/validators/content.py +80 -0
  100. thread_archive/_thread_import/parsers/validators/referential.py +57 -0
  101. thread_archive/_thread_import/parsers/validators/thinking.py +143 -0
  102. thread_archive/_thread_import/parsers/validators/types.py +87 -0
  103. thread_archive/_thread_import/schemas/__init__.py +231 -0
  104. thread_archive/_thread_import/schemas/chatgpt/v1.json +197 -0
  105. thread_archive/_thread_import/schemas/chatgpt/v2.json +203 -0
  106. thread_archive/_thread_import/schemas/claude/v1.json +188 -0
  107. thread_archive/_thread_import/schemas/claude_code/v1.json +183 -0
  108. thread_archive/_thread_import/schemas/cursor/v1.json +143 -0
  109. thread_archive/_thread_import/schemas/cursor/v2.json +200 -0
  110. thread_archive/_thread_import/timestamps.py +57 -0
  111. thread_archive/_thread_import/tool_names.py +48 -0
  112. thread_archive/_truth/__init__.py +40 -0
  113. thread_archive/_truth/jsonl_log.py +2340 -0
  114. thread_archive/_truth/repair.py +322 -0
  115. thread_archive/_watcher/__init__.py +57 -0
  116. thread_archive/_watcher/base.py +65 -0
  117. thread_archive/_watcher/daemon.py +289 -0
  118. thread_archive/_watcher/export_drop.py +203 -0
  119. thread_archive/_watcher/exthost.py +316 -0
  120. thread_archive/_watcher/lazy.py +117 -0
  121. thread_archive/_watcher/sources.py +616 -0
  122. thread_archive/_web/__init__.py +15 -0
  123. thread_archive/_web/server.py +299 -0
  124. thread_archive/_web/static/assets/index-DECSH1Mz.css +10 -0
  125. thread_archive/_web/static/assets/index-Djq0FK0y.js +101 -0
  126. thread_archive/_web/static/index.html +13 -0
  127. thread_archive/cli.py +746 -0
  128. thread_archive-0.0.2.dist-info/METADATA +296 -0
  129. thread_archive-0.0.2.dist-info/RECORD +132 -0
  130. thread_archive-0.0.2.dist-info/WHEEL +4 -0
  131. thread_archive-0.0.2.dist-info/entry_points.txt +6 -0
  132. thread_archive-0.0.2.dist-info/licenses/LICENSE +21 -0
@@ -0,0 +1,296 @@
1
+ Metadata-Version: 2.4
2
+ Name: thread-archive
3
+ Version: 0.0.2
4
+ Summary: Serverless-native local archive for AI conversations: JSONL truth log + rebuildable SQLite index, searched and read locally, exposed over MCP.
5
+ Project-URL: Repository, https://github.com/ellamental/thread_archive
6
+ Project-URL: Changelog, https://github.com/ellamental/thread_archive/blob/main/CHANGELOG.md
7
+ Author: Ella
8
+ License-Expression: MIT
9
+ License-File: LICENSE
10
+ Keywords: archive,conversations,jsonl,mcp,serverless,sqlite
11
+ Classifier: Development Status :: 3 - Alpha
12
+ Classifier: Environment :: Console
13
+ Classifier: Intended Audience :: Developers
14
+ Classifier: Operating System :: MacOS
15
+ Classifier: Operating System :: POSIX :: Linux
16
+ Classifier: Programming Language :: Python :: 3
17
+ Classifier: Programming Language :: Python :: 3.11
18
+ Classifier: Programming Language :: Python :: 3.12
19
+ Classifier: Programming Language :: Python :: 3.13
20
+ Classifier: Topic :: Text Processing :: Indexing
21
+ Classifier: Topic :: Utilities
22
+ Requires-Python: >=3.11
23
+ Requires-Dist: leidenalg>=0.10
24
+ Requires-Dist: mcp>=1.0
25
+ Requires-Dist: networkx>=3.2
26
+ Requires-Dist: numpy>=1.26
27
+ Requires-Dist: python-igraph>=0.11
28
+ Requires-Dist: scipy>=1.11
29
+ Requires-Dist: sqlalchemy>=2.0
30
+ Provides-Extra: dev
31
+ Requires-Dist: build>=1.2; extra == 'dev'
32
+ Requires-Dist: pytest-cov>=5.0; extra == 'dev'
33
+ Requires-Dist: pytest>=8.0; extra == 'dev'
34
+ Requires-Dist: ruff>=0.6; extra == 'dev'
35
+ Provides-Extra: embeddings
36
+ Requires-Dist: einops>=0.7; extra == 'embeddings'
37
+ Requires-Dist: sentence-transformers>=3.0; extra == 'embeddings'
38
+ Description-Content-Type: text/markdown
39
+
40
+ # thread-archive
41
+
42
+ A serverless-native, single-user local archive for AI conversations.
43
+
44
+ It watches local AI-tool stores, imports provider transcripts into one event
45
+ model, writes an append-only **JSONL truth log**, rebuilds a **SQLite** index from
46
+ that truth, and searches/reads conversations locally — exposed as a Python library
47
+ and over MCP (`thread_search`, `thread_read`).
48
+
49
+ There is one storage/runtime path: **JSONL + SQLite**. No Postgres, no Neo4j, no
50
+ external search engines, no ops service. JSONL is truth; SQLite is a rebuildable
51
+ projection — deleting `index.db` and running `archive reindex` loses nothing.
52
+
53
+ It is a standalone, dependency-free package: a conversation-archive engine with
54
+ no server, no external services, and no ties to any host application.
55
+
56
+ ## Install
57
+
58
+ Needs Python ≥ 3.11. Installs the `thread_archive` setup command, the
59
+ `archive` operator CLI, both MCP servers (`archive-mcp`,
60
+ `archive-librarian-mcp`), and the pre-built web viewer — no node.
61
+
62
+ ```bash
63
+ pip install thread-archive # lexical core (+ Leiden community detection)
64
+ pip install 'thread-archive[embeddings]' # optional: local semantic search (heavy: torch)
65
+ thread_archive # then: run setup
66
+ ```
67
+
68
+ `thread_archive` is the front door. On first run it discovers this machine's
69
+ conversation stores and shows what it found — counts, sizes, date ranges —
70
+ *before* touching anything, then asks: import (all, a selection, or skip),
71
+ install the always-on watcher (macOS LaunchAgent; includes the web viewer at
72
+ :8787), and wire the MCP servers into detected clients (the `claude` CLI, or
73
+ it prints the JSON block for any other client). Every choice is skippable and
74
+ persists in `<home>/config.json`; a disabled source stays disabled across
75
+ every ingest path. Re-running `thread_archive` shows status; `thread_archive
76
+ setup` revisits the choices. Non-interactive (agents, scripts):
77
+ `thread_archive --yes` accepts every default — without `--yes`, a non-TTY run
78
+ only prints guidance and never ingests.
79
+
80
+ Skipped the watcher? Still covered: `archive-mcp` cohosts **lazy catch-up
81
+ ingest** — a background pass at startup and (throttled) around tool calls
82
+ imports whatever landed in your local AI-tool stores since the last pass. On
83
+ a fresh archive give the first pass a minute to chew before expecting search
84
+ hits; the watcher install (`thread_archive setup`, or `archive daemon
85
+ install`) is the always-fresh upgrade. With the daemon installed, the MCP
86
+ servers' lazy passes degrade to no-op lock probes — exactly one process
87
+ ingests at a time, however many Claude Code sessions are open.
88
+
89
+ **The clone path — for curation.** Clone the repo, open it in Claude Code, and
90
+ say *"install this — follow claude-install.md"*. The clone carries what a pip
91
+ install doesn't: the `.mcp.json` template and the `/librarian` skill (+ its
92
+ enforcement hook) that drives topic-graph curation. Take this route when you
93
+ want the knowledge layer worked, not just search.
94
+
95
+ ```bash
96
+ git clone https://github.com/ellamental/thread_archive.git thread-archive && cd thread-archive
97
+ claude # then: "install this, following claude-install.md"
98
+ ```
99
+
100
+ **Manual path (from a clone).**
101
+
102
+ ```bash
103
+ python3 -m venv .venv
104
+ .venv/bin/pip install -e . # lexical core (+ Leiden community detection)
105
+ .venv/bin/pip install -e '.[embeddings]' # optional: local semantic search (heavy: torch)
106
+ .venv/bin/pytest tests/ -q # confirm green (add `-m package` for the wheel/sdist release lane)
107
+
108
+ # wire the two MCP servers into this clone's .mcp.json (absolute venv paths)
109
+ sed "s|ABSOLUTE_REPO_PATH|$(pwd)|g" .mcp.json.example > .mcp.json
110
+
111
+ # bring conversations in, then check
112
+ .venv/bin/archive watch --once # or: archive import <path> --provider <name>
113
+ .venv/bin/archive status
114
+ ```
115
+
116
+ Restart Claude Code in the repo so it loads `.mcp.json` (the search + librarian MCP
117
+ servers) and `.claude/` (the `/librarian` skill + its enforcement hook). Then curate the
118
+ topic graph — `/librarian` interactively, or for a large backlog the bulk driver:
119
+
120
+ ```bash
121
+ .venv/bin/python scripts/librarian_backfill.py --workers 4 --batch 25
122
+ ```
123
+
124
+ ## CLI
125
+
126
+ Two commands, split human from operator. **`thread_archive`** is the human
127
+ front door — first-run setup (discover → consent → import → watcher → MCP
128
+ wiring), then the status view; `thread_archive setup` re-enters the flow.
129
+ **`archive`** is the operator seam:
130
+
131
+ ```bash
132
+ archive import <path> # import a transcript / provider store
133
+ archive watch # watch local AI-tool stores and import incrementally
134
+ archive reindex # rebuild index.db from the JSONL truth directory
135
+ archive verify # integrity check: truth parses + matches the index
136
+ archive repair # quarantine damaged truth lines; restore committed content from the index
137
+ archive backup <dest> # mirror the truth dir (keeps hardlink generations under <dest>/.generations)
138
+ archive restore-drill <dest> # prove the backup restores: rebuild an index from the mirror + smoke read/search
139
+ archive nightly <dest> # the scheduled pipeline: backup → verify (age-gated escalation) → restore drill
140
+ archive status # archive health / counts / last verify + backup + drill outcomes
141
+ archive daemon <action> # macOS: install/uninstall/restart/status the always-on watcher LaunchAgent
142
+ ```
143
+
144
+ The CLI is private operational tooling (see Stability below) — the process
145
+ seam the LaunchAgents, cron, and operators use. Retrieval deliberately has no
146
+ CLI verbs: search and read are the public `archive-mcp` tools, and the web
147
+ viewer is cohosted by `archive watch --web`.
148
+
149
+ ## Layout
150
+
151
+ ```text
152
+ src/thread_archive/
153
+ _api.py # internal coordination layer the CLI / MCP / web call into
154
+ cli.py # the `archive` command
155
+ _setup/ # the `thread_archive` command: first-run setup + status
156
+ _config.py # truth dir + index path resolution, config.json (source opt-outs)
157
+ _store/ # SQLite store + schema
158
+ _truth/ # JSONL truth log + reindex
159
+ _importers/ # incremental import orchestration
160
+ _retrieval/ # FTS5 + vector search, read reconstruction
161
+ _knowledge/ # topic graph: event-sourced curation + Leiden analytics
162
+ _watcher/ # local-source watcher (self-feeding ingest)
163
+ _mcp/ # library-native MCP servers (read + librarian)
164
+ _web/ # read-only viewer: stdlib server + built bundle (cohosted by `watch --web`)
165
+ _launchd.py # `archive daemon`: generates + loads the watcher LaunchAgent (macOS)
166
+ _thread_import/ # vendored provider parsers (a clean, dependency-free island)
167
+ frontend/ # the viewer's React+Vite source (dev-only; builds into _web/static/)
168
+ host/ # operator layer: Makefile over `archive daemon`, backup agent, family-manifest writer
169
+ scripts/ # operator tools (e.g. the librarian backfill driver)
170
+ tests/install/ # isolated Docker install test + fixtures
171
+ ```
172
+
173
+ ## Stability
174
+
175
+ The public API is exactly two things:
176
+
177
+ - **the retrieval MCP tools** — `thread_search` and `thread_read`, served by
178
+ `archive-mcp`;
179
+ - **the on-disk truth format** — versioned by `manifest.json`'s `version` and
180
+ specified in [docs/format.md](https://github.com/ellamental/thread_archive/blob/main/docs/format.md).
181
+ Data written by one release
182
+ stays readable by the next; a reader refuses a truth directory newer than it
183
+ understands. Programmatic read access to the documented stores (`index.db`
184
+ is plain SQLite; the truth directory is documented JSONL) rides on this
185
+ contract.
186
+
187
+ Everything else is private support machinery and may change without notice:
188
+ the `thread_archive` and `archive` CLIs, the librarian MCP server, the web
189
+ viewer, and every Python module — there is **no public Python API**. More surface gets exposed
190
+ deliberately as it matures. `tests/test_public_api.py` ratchets the boundary.
191
+
192
+ ## Web viewer
193
+
194
+ The always-on watcher cohosts a local search + reader UI: `archive watch --web`
195
+ (the shipped LaunchAgent passes it) serves at `http://127.0.0.1:8787` — a stdlib
196
+ HTTP server handing out a pre-built React bundle plus a few JSON endpoints, in
197
+ the watcher's *own* process. One process, one SQLite engine — the viewer reads
198
+ concurrently with the watcher's writes, which WAL makes safe (`store/_base.py`).
199
+ No second daemon, and no standalone `web` verb: the viewer exists where the
200
+ persistent URL is.
201
+
202
+ **Runtime is node-free**: the bundle is built ahead of time and committed under
203
+ `web/static/`, so `pip install` never touches node. Node is a *build*-only tool:
204
+
205
+ ```bash
206
+ # rebuild the bundle after editing the frontend (node only here):
207
+ cd frontend && npm install && npm run build # → ../src/thread_archive/web/static/
208
+ ```
209
+
210
+ **Archive-links.** With that persistent server, the archive owns the editor
211
+ "open this conversation" link itself: `GET /api/archive-link?id=<session-uuid>&source=claude-code`
212
+ resolves the session to its thread via `ImportState` and returns `{thread_id, url}`,
213
+ or `&redirect=1` → a `302` to `/archive/<id>`. (Local — no separate backend
214
+ involved.) `id` may repeat — a caller that cannot tell which uuid it holds is the
215
+ session id sends every candidate, best guess first, and the first that resolves
216
+ wins; ids that were never imported are skipped, not fatal.
217
+
218
+ ## What it does
219
+
220
+ - **Ingests 9 providers** into one event model — Claude Code, Codex, Grok, Antigravity,
221
+ cloth, Cowork (transcript line-streams) and Cursor, OpenCode, Claude Science (SQLite
222
+ scanners). Imports are idempotent, atomic, and survive a full reindex losslessly. A
223
+ `cc-exthost` watcher also recovers mid-turn Claude Code steering messages that never
224
+ reach the session JSONL.
225
+ - **Self-feeds** — the watcher tails local stores and ingests incrementally; events land
226
+ in the JSONL truth *before* their commit (no checkpoint in the hot loop). Zero-daemon
227
+ by default (`archive-mcp` cohosts lazy catch-up ingest), with a one-command macOS
228
+ LaunchAgent upgrade (`archive daemon install`) for always-fresh — no external service
229
+ either way.
230
+ - **Declares itself** — the installer writes the thread-family manifest
231
+ `<home>/product.json` (`host/write-manifest.py`; `make install-agent` runs
232
+ it), so family consumers discover the archive by enumeration. Spec:
233
+ `docs/spec/product-json.md` in the thread monorepo.
234
+ - **Searches locally** — FTS5 lexical (boolean / phrase / pipe-OR / code-identifier),
235
+ optionally fused with local semantic vectors and a cross-encoder re-rank; plus
236
+ transcript reconstruction for reading.
237
+ - **Rebuilds losslessly** — `rm index.db && archive reindex` reconstructs the entire
238
+ index from the JSONL truth; a `cp`/`rsync` of the truth dir *is* the backup.
239
+ - **Curatable** — an event-sourced topic graph with Leiden communities (see below),
240
+ driven on demand by the `/librarian` skill.
241
+
242
+ ## MCP
243
+
244
+ Two servers, split read from write. **`thread-archive`** (`archive-mcp`) serves the
245
+ read-only tools — `thread_search` / `thread_read` — and cohosts lazy catch-up
246
+ ingest in its own process (throttled, cross-process-safe via the ingest-owner
247
+ lock; `THREAD_ARCHIVE_MCP_INGEST=0` disables it). **`thread-archive-librarian`**
248
+ (`archive-librarian-mcp`) is the curatorial *write* surface — topic/link/citation
249
+ writes + the reads a librarian needs (`review_queue`, `topic_search`,
250
+ `thread_user_messages`). Keeping them separate means a read-only client never gets
251
+ curation power. Client config:
252
+
253
+ ```json
254
+ {
255
+ "mcpServers": {
256
+ "thread-archive": {
257
+ "command": "archive-mcp",
258
+ "env": { "THREAD_ARCHIVE_HOME": "~/.thread/archive" }
259
+ },
260
+ "thread-archive-librarian": {
261
+ "command": "archive-librarian-mcp",
262
+ "env": { "THREAD_ARCHIVE_HOME": "~/.thread/archive" }
263
+ }
264
+ }
265
+ }
266
+ ```
267
+
268
+ ## Knowledge layer (the topic graph)
269
+
270
+ There are exactly two kinds of thread: imported **conversations** and curated **topics**
271
+ (`thread_type='topic'`). A topic is modeled as a thread on purpose — so the graph's edges
272
+ (`thread_links`) and message→topic citations (`topic_messages`) reference one id space.
273
+ Reads/analytics live in the knowledge layer (`_knowledge/`) — PageRank,
274
+ communities (**Leiden**, the algorithm Neo4j GDS ran, with a networkx-Louvain fail-soft
275
+ fallback), bridges, peers.
276
+
277
+ Curation is **event-sourced**. Every write (`create_topic`, `link_threads`,
278
+ `add_topic_evidence`, `merge_topics`, …) appends a `KgEvent` to an
279
+ append-only `truth/kg_events.jsonl` and folds it into the SQLite projection in one
280
+ transaction. The log is the source of truth for curation; `thread_links` / `topic_messages`
281
+ are rebuildable from it — `reindex` replays the log (idempotent upsert + tombstone) to
282
+ reconstruct them, so an unlink/merge/archive is recorded history, never silent loss. The
283
+ librarian skill (`.claude/skills/librarian/`) drives the write MCP to clear the
284
+ citation backlog on demand (a conversation is 'reviewed' once it gains its first
285
+ citation/link — there is no per-thread summary).
286
+
287
+ ## License
288
+
289
+ MIT — see [LICENSE](https://github.com/ellamental/thread_archive/blob/main/LICENSE).
290
+
291
+ ## Origin
292
+
293
+ thread-archive is the standalone member of a larger personal project ("thread"), built to
294
+ stand on its own — serverless, dependency-free, no backend or external services. The
295
+ package is self-contained, but it's young: expect the occasional rough edge or stray
296
+ reference to its parent project.
@@ -0,0 +1,132 @@
1
+ thread_archive/__init__.py,sha256=1YtZeHc1ZrjnIZde0t1QkUPEqRPGaorkzRpsaHEJGEM,1581
2
+ thread_archive/_api.py,sha256=VHR198pQUEt1EJBc5PXBVqugxEkEBIIp_qWDYUmkJiY,103578
3
+ thread_archive/_config.py,sha256=51DKgkYDTY88KVxotc5-Va4pxjNG0fOvQhojuV6aquU,4795
4
+ thread_archive/_launchd.py,sha256=Cr9AeUXCLn4bIm0H2B4VtyXbgXoQfVA0hPoWJw1p6Tg,5887
5
+ thread_archive/cli.py,sha256=gCrim9tkPpc7EEjYzqDRGDJszU7xpgHp110HjnvB2vo,31582
6
+ thread_archive/_importers/__init__.py,sha256=E8F5-GwjQh8upq-kxCSRDAbyAOo5zUbgDkdOkpp8NxY,2709
7
+ thread_archive/_importers/_continuation.py,sha256=XAyG-THeOIaWWdwGz8JfDLySqtAbBv3U99inVTxOZQw,5522
8
+ thread_archive/_importers/_cursor.py,sha256=3Izh5SS9tSX6pq3V_ycxUG86EZ0YhQ8jAnb6ODtqGCo,4545
9
+ thread_archive/_importers/_events.py,sha256=79mzog_ftaAW_J5ELc_vTvLCvwXB6ET9lNDG18MAF-w,9713
10
+ thread_archive/_importers/_line_stream.py,sha256=d8uQBLViIblb8Pl1NN8TbOx9-aU5Q5kzu62mL-wXJ4A,6790
11
+ thread_archive/_importers/_read.py,sha256=OzgqxJkGvoO5siBoa6dGT2GAfoCbFtYCg4OHDUejYn0,3201
12
+ thread_archive/_importers/_result.py,sha256=Uko5Vi97SHk5hEPnhisiN-Qp8meq2PhlResc2FCRmro,379
13
+ thread_archive/_importers/_sidecar.py,sha256=SxW_Gm2wrJicXkzGFPsoF32D4nBbi2EJos1sly-zIRU,3993
14
+ thread_archive/_importers/_state.py,sha256=jqrniKpnNovskFHkuCoBODI6Eil9WqqUF11IaLgBvgw,9294
15
+ thread_archive/_importers/_titles.py,sha256=PRtiAEq3w5cZaxF3i9HnGKtRPfT8E-jvarFHrT3lmFM,7124
16
+ thread_archive/_importers/antigravity.py,sha256=O0_Bts8Ak8PTi1xpk0GzWATagwy7cYyT0f035jwzRVk,9488
17
+ thread_archive/_importers/claude_code.py,sha256=Bqz3tmhhCfsoWZQEt-3sEyOm9EDukgPd1wBhomknS74,12486
18
+ thread_archive/_importers/claude_science.py,sha256=bS6tpSwW_q3N9nm1d5DA2ZUy2Ekg3M0fgEpq1OLUZDU,13321
19
+ thread_archive/_importers/cloth.py,sha256=Xwy8nqkVPeEEs42x7tHSkUB4X_guY8P4aXSmBniNvUg,1107
20
+ thread_archive/_importers/codex.py,sha256=YuB5SjwxCheixbSAqUxDVxsqnw7DNCfc3FE-vBXmztI,11915
21
+ thread_archive/_importers/cowork.py,sha256=vwlEefqMoKmbn9YZUYz7QD6DxhXfQ_4wFFpRRWxmUyQ,4222
22
+ thread_archive/_importers/cursor.py,sha256=DAGR30vsKLN8UHpTxikTuXQXB_-QY8cQ0-OLCelNaUg,17695
23
+ thread_archive/_importers/exports.py,sha256=LmUI4Ip-Mxy71pIyzy5gFHP5cIibvp-RevdeJxBP1OE,16154
24
+ thread_archive/_importers/grok.py,sha256=4VQg7sIrKXjyz8DhhnEFoa5GCPhcCJKEB1mn4_mG9_E,22079
25
+ thread_archive/_importers/opencode.py,sha256=NG-tFmb6FjpQWM3W0N9xuJZrRiGc0kQx8SPWUwM8-P4,22949
26
+ thread_archive/_knowledge/__init__.py,sha256=cnSYqwdQ87c1jekkl_DxoSW5wX9sKGXQ-g4vp8qyUNo,1825
27
+ thread_archive/_knowledge/_claims.py,sha256=3_usBaoTSjqYD-9qol3y1oSQPlYJt1qVf_EfDKx2YwA,4484
28
+ thread_archive/_knowledge/_community.py,sha256=SspOc0H3kFEY1dowlYEIKaU_IerSZ8md2oqqTO0my04,3030
29
+ thread_archive/_knowledge/graph.py,sha256=bP34HkFCndcXkSrqqNOMYxf0ve9BxcYURLt7yoCldug,7437
30
+ thread_archive/_knowledge/materialize.py,sha256=s9bbiLMN2zBtoAoz2ChOGQn0k8jQkLy6GvHnukKXifM,9273
31
+ thread_archive/_knowledge/write.py,sha256=KcOZtDoFSzlA-XG1SDz3AcbuISiZ29hZ3x5CaBpFsM4,19193
32
+ thread_archive/_mcp/__init__.py,sha256=6PSpxiOHcC2T8kz3HeYhLOJRtwuTBnxzv88WRsvVPDo,89
33
+ thread_archive/_mcp/librarian.py,sha256=vMmNlfcYjmecGhn1g_CIHVGO20uqTjEXb6Yx4RLG1uc,6094
34
+ thread_archive/_mcp/server.py,sha256=mjE9ENyxYCzUdhgMuXtY6xklKvB36ug_4vVxdRG1-IM,14577
35
+ thread_archive/_retrieval/__init__.py,sha256=xv2EMZrtMQ1JTqqptFOa8ht4a5ghcw4-vmqp5Q7iyuI,13355
36
+ thread_archive/_retrieval/_classify.py,sha256=bUMTHTkZXUs-bg945sBIgUAIFg-abPMPmSZLCt7aNZI,3053
37
+ thread_archive/_retrieval/_codex.py,sha256=0RTn90Etdz8nr6e56KQEGClieNK_32WorAQQsPhHO78,6199
38
+ thread_archive/_retrieval/_context.py,sha256=b3mXGaxMag90vlSgEm7PiwrveD4HyT4A88Re-8rFBv4,5199
39
+ thread_archive/_retrieval/_extract.py,sha256=Gj5TAZSfHVu3mMB4FD5ECZ5HsVTt_58Js4-ln0OZJ8I,7506
40
+ thread_archive/_retrieval/embed.py,sha256=_lhaWkEwmocF3lksjvQIUt5qzmXoIeFrIhsyVShiAkY,7304
41
+ thread_archive/_retrieval/format.py,sha256=gwTRut2Z-8TZzXHAXdvBSMw353sOEoEZj8PJkBCrNP0,5803
42
+ thread_archive/_retrieval/fts.py,sha256=MOfBVhcIKal_MZuQm7WXOBCh0ZKm08O0ylqIzNQ6eTA,23647
43
+ thread_archive/_retrieval/rank.py,sha256=xNl35MUGY3KQhDShsmEHZrPnfKul3uykV_pO4io1jP8,10268
44
+ thread_archive/_retrieval/read.py,sha256=jD2QGWzPazaqsP3H138suBnZxFIev0RqJdJL0mn6vlg,40982
45
+ thread_archive/_retrieval/rerank.py,sha256=q2x5_p7gxv6y6ReuNB2rODGC2-PtUGfWA0U1qIq2YA4,5604
46
+ thread_archive/_retrieval/vectors.py,sha256=0HDlcSkH3AR_6Y4nH0DkeUvTcaYZtCE5Gp9vIoF1drA,26391
47
+ thread_archive/_scripts/__init__.py,sha256=BawS1JIk0h9jfCS39xSZVmxL5f_wK9CsVhgiob6BztM,63
48
+ thread_archive/_scripts/backfill_recompute.py,sha256=--rYFaZijXt1ySWWmmRzelEemXrOjUgoC1wfQYUAe-c,16322
49
+ thread_archive/_scripts/backfill_reconcile.py,sha256=5BkG2FwXb-n2qrbyjoGOPtAWD7x0CrgC1lo63FDiRwY,13285
50
+ thread_archive/_scripts/denamespace_dedup_keys.py,sha256=70z_ESpq8sKhbk0DvbdfLY_niceBGeoNhFAPvZ0vb4U,5211
51
+ thread_archive/_scripts/recover_dropped_events.py,sha256=2_lCwMjow8XZwRnyU5Ch8i3lua2f7j--hTw4Xw-s2qE,8562
52
+ thread_archive/_scripts/repair_grok_tool_names.py,sha256=f5oBLIC6phCUgJ5uT0p1wx8_N126gltI3JgfUSinPTw,5253
53
+ thread_archive/_scripts/repair_grok_tool_names_backup_20260704T155625Z.json,sha256=2T05ysx16NFvn5B3nTbNdTm-fcz3dF9lRy74Ae9PvaU,42961
54
+ thread_archive/_scripts/repair_grok_tool_names_plan_20260704.json,sha256=gCVYZKLAzhOnWuMskkP09FhygzLg1Ju9sKuK2EZTnOs,163203
55
+ thread_archive/_setup/__init__.py,sha256=qPb6X0ydZAix5afnprGNeB3zD1R0mSHx6hmNCeeQbsM,367
56
+ thread_archive/_setup/clients.py,sha256=ETGbNcsFKUzuCXUspEJbJo_1WacI4rM3qTuSj_9uLQQ,3088
57
+ thread_archive/_setup/wizard.py,sha256=gcrIMHGBTnrvc4TiPPmd6Yi6LOXm3kKOatywP0vo9I4,20003
58
+ thread_archive/_store/__init__.py,sha256=5cyA3Ym9A09I3WoGl9X-7BcaV5OeV428gKwgCDRpu5k,863
59
+ thread_archive/_store/_base.py,sha256=eAQ8rRFZPkn3kS5QYh1bg2pfbalqa35CmfsxbBEOBMM,5838
60
+ thread_archive/_store/_defaults.py,sha256=Ka7FhzF52pIG9hL1OaKXsPFHeOYp2VP9KxowACGOODA,1497
61
+ thread_archive/_store/_types.py,sha256=x5RU9vCCAReplCKpIkTaG9ig0YCD44svfQgHSSY-GdM,1828
62
+ thread_archive/_store/models.py,sha256=Wvd-Em8A3JQi1s20o2_ZKsjdCmKOcgyoZ_x-AjXVf6c,18496
63
+ thread_archive/_store/schema.py,sha256=6Fl9GsluiTJSodi44qVxpvkP1YTg6oNRISlt75iS_xg,3699
64
+ thread_archive/_thread_import/__init__.py,sha256=0jBlCqhcygUcIqFm8sKJ6AY59N8UeHRAUeqlyWpk9Gw,1177
65
+ thread_archive/_thread_import/api.py,sha256=zLEoSmuEy51FZG_VYuvbZFt84bI6yg-1b8JVOk7xoGs,2314
66
+ thread_archive/_thread_import/event_builder.py,sha256=nNPEn0N-fokyNkBBUzpteOc7hJd2KpXziu5XjatS2gM,33209
67
+ thread_archive/_thread_import/timestamps.py,sha256=QB1rUK0BMMaGs5Y5uN7mrLPdBE0V7CYNRoV5x-1O_O4,2030
68
+ thread_archive/_thread_import/tool_names.py,sha256=SppZCIvgxC1cjQmxkLdnss9E3J7u9io2Eh0vO44vy54,1696
69
+ thread_archive/_thread_import/exporters/__init__.py,sha256=lQy_oUdYkXXEUzEfhq96Va5BXnh3a0MaORcJZ83Vp18,155
70
+ thread_archive/_thread_import/exporters/_cursor_kv_mixin.py,sha256=c3-QzLAmEL16V6Cow53Cdvn5Oh-7Ip2DkMiDMsk8ELA,6209
71
+ thread_archive/_thread_import/exporters/_cursor_parse_mixin.py,sha256=dmKfno75SgnIqS0LafugVQJysT3_6TJRN_5IC6DP_iI,5970
72
+ thread_archive/_thread_import/exporters/cursor.py,sha256=BvRY_OiQXFqpd5_5a7cv1a8GrCxnBhv4HkOD0D2CEfE,21011
73
+ thread_archive/_thread_import/exporters/cursor_parse.py,sha256=Vgb2Y5prKfKHXMAoDT9ZS6IBGOJL-sPfXCLKnSCwWRE,5129
74
+ thread_archive/_thread_import/parsers/__init__.py,sha256=djz7PVrCUMF57IQh2JAAaqR13C-rKfzjxgNKLyPrHts,4683
75
+ thread_archive/_thread_import/parsers/base.py,sha256=S1vLZg5w7R07C1pxddSmNFU6awTHNH3Hg-KmLW4qglY,24065
76
+ thread_archive/_thread_import/parsers/chatgpt.py,sha256=32uxzpOSPyZ1ZgexwKajYza0pJO_dKaRh8lzjpHYJ5Q,27899
77
+ thread_archive/_thread_import/parsers/chatgpt_content.py,sha256=YhwZiJQb2f34g6fQjfP0nUmbYdAQ7UeZsIsdjwhLzTc,11473
78
+ thread_archive/_thread_import/parsers/claude.py,sha256=S8YRq8oaU7hEtEBxdr1w0denorgv1pXz9U2mZBWLK3o,16594
79
+ thread_archive/_thread_import/parsers/claude_code.py,sha256=J9fXP9bVaccqAJ0bY28z9KT7Cp-0ZtCd6FdXaAyYi7Q,42236
80
+ thread_archive/_thread_import/parsers/claude_code_blocks.py,sha256=RMjjYaIBGpQpJEh6Mwgh2mF6wVukaBCviaJQzyXeV3E,15296
81
+ thread_archive/_thread_import/parsers/claude_code_ide.py,sha256=JjVWdpkPxBiM8exQwZ3yA0aKIEIvd8FEmY8QNMfH6NI,4509
82
+ thread_archive/_thread_import/parsers/claude_code_sessions.py,sha256=1FPnWyPTEnhLHqVU9qg9B54OJPLFqivUn-WKcXhQ5z8,2911
83
+ thread_archive/_thread_import/parsers/cursor.py,sha256=3UTCfiDinW_razvdO46dC_O4tqayl8Ts9DzLWWcnwkc,18845
84
+ thread_archive/_thread_import/parsers/cursor_blocks.py,sha256=zDto4MkmFzi-5Q0Y0qoKiWdKdxxonInb39HjTH-EZU4,13650
85
+ thread_archive/_thread_import/parsers/config/__init__.py,sha256=a2sScw18UxdsCjxt9Z_TkEHq_XJD5RICzlDuqpxlWW8,543
86
+ thread_archive/_thread_import/parsers/config/base.py,sha256=XkWanwBbXPzkMGcwVX1Howr8pkhiatUpDTwYM8I7CdI,7184
87
+ thread_archive/_thread_import/parsers/pipeline/__init__.py,sha256=gE3A8Zp8v-BHraL091WrmTbgbwf5DSPN7KFVtsHyUq0,698
88
+ thread_archive/_thread_import/parsers/pipeline/base.py,sha256=XOKGnVcnXI4tMFWB3tz1a-Znib2sHAOnwC2NCPs_1Uw,8081
89
+ thread_archive/_thread_import/parsers/pipeline/interfaces.py,sha256=94lQs_eocP8BHYggo4V-8VExa-9fTrzyIwfcNQ_4fi4,6139
90
+ thread_archive/_thread_import/parsers/transformers/__init__.py,sha256=Bsv_BmGcS_lEPGN_ocFzE_OwoW9HRncYNSk_iuhqxEA,633
91
+ thread_archive/_thread_import/parsers/transformers/active_path.py,sha256=S7Wz4BQ96IKoM0QKVXAc_GMOxdTGFIyafMWJ681c8Yo,2792
92
+ thread_archive/_thread_import/parsers/transformers/coalescing.py,sha256=SWLOCHGFBTq1pHCFfV364JhWXtjLkwWVYtY2KW4Vk5E,13470
93
+ thread_archive/_thread_import/parsers/transformers/ide_context.py,sha256=djA0VFa_s9VE5BvfU20WrT20NxVqjyVqYxJ3wdxbHTE,5227
94
+ thread_archive/_thread_import/parsers/transformers/thinking_merge.py,sha256=0pBAd1-t96cHc2u_KZxB1PDnYRnycSNj9cFaOiy8SzY,2374
95
+ thread_archive/_thread_import/parsers/types/__init__.py,sha256=6NLXrGmSChcN63qRBSyz0eWUEkq00vhOa8oLl9-FXtU,1136
96
+ thread_archive/_thread_import/parsers/types/chatgpt.py,sha256=MGWK5gi0Gbs6J1ZRB7j7iOkdtr7Al6PaclMxqURcKoU,3283
97
+ thread_archive/_thread_import/parsers/types/claude.py,sha256=vEnd3wlRLIqNuzA0HUQ_CRY7lb4FHrD1Xc9PRgLjgAQ,3628
98
+ thread_archive/_thread_import/parsers/types/claude_code.py,sha256=Ql_JfUiQ4Z36z9GImJsWAF5HYykcjZpEKi1UzIPa7c4,4627
99
+ thread_archive/_thread_import/parsers/types/cursor.py,sha256=bOty8b070yGNE6amgdAvWxOTA9teN1VIDHxviKl7ajw,3867
100
+ thread_archive/_thread_import/parsers/validators/__init__.py,sha256=RuMVB7DeGIAb8HO5cY4S-eey8r62l85WHgcX4TZAYPo,3272
101
+ thread_archive/_thread_import/parsers/validators/base.py,sha256=VieqEJLc6daZDOrVyF_s-IdoM5jcvYJjgHWOQVJ1kIQ,5223
102
+ thread_archive/_thread_import/parsers/validators/content.py,sha256=9eB4UOedRMaKgOBYy7qubQQUT4qj0cDWPFGBB3uJtrA,2746
103
+ thread_archive/_thread_import/parsers/validators/referential.py,sha256=01i8EbOZ9XSDCLsUeGefqeLpX4V7rdhCZsK5RLTqTMo,1936
104
+ thread_archive/_thread_import/parsers/validators/thinking.py,sha256=gqBzN9iVm1FPPzBqnabomAD2jorBY7joVHUySdLTTu4,5351
105
+ thread_archive/_thread_import/parsers/validators/types.py,sha256=qM99w6pIxFRCj66L8P4m3cgh_cQBU_2fLBOid3JLLqg,2810
106
+ thread_archive/_thread_import/schemas/__init__.py,sha256=YK23WcIDX9d8v_YwkmMGVyYnIvcMogZDvrUYIE1wE6c,6905
107
+ thread_archive/_thread_import/schemas/chatgpt/v1.json,sha256=-0g9cpRCdpSUVKTucdgDCERp1mNGgIXg_Yr__CMtLg4,4980
108
+ thread_archive/_thread_import/schemas/chatgpt/v2.json,sha256=EDYDnqalPDGGOPFuWXJVXkp_ns9uZiSJh2qPtWaWEwc,5367
109
+ thread_archive/_thread_import/schemas/claude/v1.json,sha256=vcpuXiDQo7TnLoZ_dB8fA6fXBzlcTwSJqVYrYY1eu_M,4338
110
+ thread_archive/_thread_import/schemas/claude_code/v1.json,sha256=kDLBgIJYCDd66fAnD0ts7SedpeeyhaNbhBFW4fac-3w,4769
111
+ thread_archive/_thread_import/schemas/cursor/v1.json,sha256=YYRP7uCHsPUBErZDY4ayDmKcp1CsGatGLh3tVcx-TWg,3705
112
+ thread_archive/_thread_import/schemas/cursor/v2.json,sha256=XYEZiDoNqgh3yZZBWL8PJrQ7qDC3johTMUnUkwunT1g,5273
113
+ thread_archive/_truth/__init__.py,sha256=AD_01J7xRtoi6Vgsx0VdQgXb4ArzOUbR1hTRkILZ4VM,861
114
+ thread_archive/_truth/jsonl_log.py,sha256=a44C5fnAybZ_hfMqdC-Z3GInv0aTfgPzqXrRSfVrNpg,112625
115
+ thread_archive/_truth/repair.py,sha256=6ucXIdwDfIf9cRFB0d84xhoz_qSRBhJvLYY8inlwl7U,13946
116
+ thread_archive/_watcher/__init__.py,sha256=UrB72M14OMceMnLHb0w5GRv4hRrd6TuEKd_Bk4ZhuZo,1646
117
+ thread_archive/_watcher/base.py,sha256=7Gr8mYatuWNHAhyyTY13qgDjZd5V4VGXxHVoB9USfBI,2090
118
+ thread_archive/_watcher/daemon.py,sha256=LL1Yia61r0SmOSd3yOUj1lMi_f8FwjmVFYRvolRLC7w,14057
119
+ thread_archive/_watcher/export_drop.py,sha256=091W0oSaKk3XAfgNpYLtoHsMQWrXp_-ZXnccsnG8N0U,8299
120
+ thread_archive/_watcher/exthost.py,sha256=mn_tEJ3C-cao1ut4TVrPVhPi0fzvdmG3FVje2_aSW3E,14169
121
+ thread_archive/_watcher/lazy.py,sha256=vIgov52JXvPi6VtK_uD8ExWQPqtmW3QHNry8vjxRSEE,4381
122
+ thread_archive/_watcher/sources.py,sha256=njMKFnmJFqNS3tgae1QrWoFXVBpPl-mtpgeTc26Prt8,24835
123
+ thread_archive/_web/__init__.py,sha256=1V2Wgv6-t4LVSJsqfU5osC6q4JI1ezJ-lnHXtFvnruE,658
124
+ thread_archive/_web/server.py,sha256=nh56fIahpgvLruO-dOgA_8ei-ltVC49rNS7x67YL2G8,12236
125
+ thread_archive/_web/static/index.html,sha256=pirPsnwD7f181FtpGG-4TToCPE-1rlj044ZW30Twn6k,399
126
+ thread_archive/_web/static/assets/index-DECSH1Mz.css,sha256=SFnjTv3L91novFjCV7U1bmdKTBW-J2qFqeO0Jn2X1Ms,9386
127
+ thread_archive/_web/static/assets/index-Djq0FK0y.js,sha256=gOz_YDHPDYrgXFSOg8YyC01VlrEytoiFce81eIsfMAU,511764
128
+ thread_archive-0.0.2.dist-info/METADATA,sha256=Cp370oTP1Cj7c0eBbSmZVKvluGNOGeMEl-oLJUz-b8o,14991
129
+ thread_archive-0.0.2.dist-info/WHEEL,sha256=lCkmxWfQsSc9CfIClYeavTdQeEX2toPqufh9gI35EQA,87
130
+ thread_archive-0.0.2.dist-info/entry_points.txt,sha256=QjJSKuUc-5HM01pMPReWzLqTXvPH5HGEScGwAPwuSvM,245
131
+ thread_archive-0.0.2.dist-info/licenses/LICENSE,sha256=XcDY4f99OtZKWxDBAAAasHoz5dgYFAHMdv2X9pjDt-0,1061
132
+ thread_archive-0.0.2.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: hatchling 1.31.0
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
@@ -0,0 +1,6 @@
1
+ [console_scripts]
2
+ archive = thread_archive.cli:main
3
+ archive-librarian-mcp = thread_archive._mcp.librarian:main
4
+ archive-mcp = thread_archive._mcp.server:main
5
+ thread-archive = thread_archive._setup:main
6
+ thread_archive = thread_archive._setup:main
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Ella
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.