
Show HN: ThreadShelf – Obsidian and AI chats: vector search, MCP, llama.cpp
Local-first archive, semantic search, and continuation for your AI conversations.
以下正文同步自 Hacker News 最新,版权归原站所有,已转换为易读排版。
Local-first archive, semantic search, and continuation for your AI conversations.
One private workspace across ChatGPT, Claude, Google AI Studio, OpenRouter, LM Studio, and Grok. Search old conversations by meaning, reopen the complete thread, and continue it with a local GGUF model through llama.cpp or the explicitly external OpenRouter provider.
The archive pipeline—parsing, embeddings, LanceDB storage, search, HTTP API, and MCP—runs locally. Conversation generation is optional: llama.cpp stays loopback-only; switching to the clearly marked OpenRouter · external provider sends the selected user/assistant context and new prompt off-device.
You export/copy your chats as JSON (see Get your data).
ThreadShelf parses them into a common format, embeds them locally, and stores them in a local vector database (LanceDB).
You search by meaning in the web UI, open the full original thread, and export any conversation to Markdown — or query the same index from an MCP client.
Optionally start a new chat or continue an archived thread through local llama.cpp or explicitly external OpenRouter.
Architecture at a glance
flowchart LR exports["AI chat exports"] --> parser["Provider parsers"] parser --> turns["Normalized turns"] turns --> chunks["Chunks"] chunks --> embed["Local embeddings"] embed --> db[("LanceDB + thread store")] db --> ui["React UI"] db --> api["HTTP API"] db --> mcp["MCP stdio"] db --> generation["Generation registry"] generation --> llama["llama.cpp<br/>local GGUF"] generation -. "explicit off-device provider" .-> openrouter["OpenRouter"]
Loading
How is this different from other history viewers?
One normalized archive across six providers. It includes sources whose history is otherwise split between exports, Drive files, browser pages, and local application data.
Semantic, not just keyword. Find a conversation by topic even when you don't remember the exact words — plus an exact-match mode for when you do (error strings, identifiers, code).
A local archive with an optional generation layer. Search remains useful without configuring any LLM. When generation is wanted, the primary engine is a loopback-only llama.cpp server; OpenRouter is a separately marked external choice.
One index, three front-ends. The same archive is searchable from the web UI, the HTTP API, and any MCP-capable tool.
Comparison with other tools
ThreadShelf combines a local conversation archive, semantic search, and optional thread continuation. The tools below overlap with that workflow but also cover other needs, including document research and agent orchestration.
This is a documentation snapshot reviewed on 2026-10-03, using the linked upstream sources (LibreChat's documentation is labeled Latest / dev). Installed releases, configuration, and extensions may differ.
What you want to do ThreadShelf's current workflow
Find an old discussion when you cannot remember its wording Search by meaning across the archive, then open the full thread around the matching turn
Locate an exact error, identifier, or code fragment Use keyword mode with collection, role, model, and date filters
Continue an exported conversation with a different model Reopen its stored turns and append a local llama.cpp or explicitly external OpenRouter exchange
Let an existing AI client retrieve past conversations Connect to the MCP server's search and read_thread tools over the same local index
For built-in agent orchestration, document RAG, or research notebooks, the corresponding tools above offer broader workflows. ThreadShelf currently focuses on conversation retrieval, continuation and scoped Obsidian tool workflows; general workflow automation, automatic fact memory and generated answers with archive citations are future directions. An MCP client can already use its search and thread-reading tools to retrieve context; a client using an external model may send that context off-device.
Temporary chats are already available: click the ghost icon beside New chat to start a Private conversation. Its content stays in this tab's browser sessionStorage; it is not saved to the archive or indexed, and generation diagnostics are not persisted. Selecting OpenRouter still sends the conversation off-device. See conversation generation.
Open Markdown/Obsidian in the sidebar and connect any folder of Markdown notes: an Obsidian vault, a Logseq graph, an OpenClaw workspace or a plain .md folder. Obsidian vaults and the OpenClaw workspace found on your computer are offered for one-click connection. Search the notes locally, open the originals in Obsidian, and create new notes on the page. Enable Notes tools in a conversation to let the selected model search, read, create and edit notes as part of a bounded tool-calling workflow.
Allow vault writes is off by default: a connected vault is read-only, including for MCP clients, until you enable writes on the Markdown/Obsidian page or beside the composer. Connecting a different vault turns writes off again. Deletion always pauses the conversation for an explicit checkbox and confirmation showing the model and exact files. Approved deletions move notes to the vault's .trash; cancellation preserves them. MCP offers search/read/create/edit; deletion requires the interactive chat UI.
Only ordinary .md files inside the configured vault are accessible. Hidden folders, attachments, symlinks, junctions and traversal are excluded. Creation never overwrites existing notes. Search reads current files without reimporting; semantic note indexing and automatic project export remain future work.
With local llama.cpp, note context stays on-device. OpenRouter and external MCP models may send retrieved note content off-device. Private chats can still make explicitly enabled vault writes. See Obsidian integration for tools, controls, API routes and limits.
Source How export works Format stability
Google AI Studio Download your Drive "Google AI Studio" folder ⚠️ undocumented, tested July 2026
OpenRouter Browser-console export script (this repo) ⚠️ undocumented
LM Studio Copy local conversation files ⚠️ undocumented, tested on 0.4.x
Grok / xAI Account data export (prod-grok-backend.json) ⚠️ undocumented
ChatGPT / OpenAI Official data export (conversations.json) semi-stable
Claude / Anthropic Official data export semi-stable
⚠️ Format note. Google AI Studio, OpenRouter, LM Studio, and Grok have no documented, stable export schema — their vendors can change it without warning. ThreadShelf is tested against snapshots of specific versions (e.g. LM Studio 0.4.x). If a newer app version changes the shape, parsing may break; please open an issue with an anonymized sample.
You only need the providers you actually use. Put exported files anywhere, then point ThreadShelf at that folder.
Your prompts are saved to Google Drive in a folder named Google AI Studio.
Text chats and Imagen prompt histories from the July 2026 Drive export shape are supported. The format is undocumented and may change.
OpenRouter has no bulk export, so this repo ships two browser-console scripts:
The selectors are pinned to OpenRouter's current chat UI and covered by test/playwright/openrouter-export.spec.js. If OpenRouter changes its markup and the script finds no chats, that test is where to update the contract.
You'll get a file shaped like this (this is what the parser reads):
{ "platform": "openrouter", "exportedAt": "2026-05-24T12:00:00.000Z", "pageTitle": "OpenRouter chat title", "sourceUrl": "https://openrouter.ai/chat/...", "turns": [ { "role": "user", "content": "User message text" }, { "role": "assistant", "content": "Assistant reply", "model": "optional/model-name" } ] }
Verify a download before indexing:
npm run parse -- path/to/openrouter-export.json
See docs/OPENROUTER.md for details and limitations.
LM Studio stores one JSON file per conversation locally — no export step needed, just point ThreadShelf at the folder (paths below):
Paste that path straight into the manual folder path box and index it — or copy it somewhere first:
macOS / Linux
cp -r ~/.lmstudio/conversations ~/lmstudio-export
Windows (PowerShell)
Copy-Item "$env:USERPROFILE.lmstudio\conversations" "$env:USERPROFILE\lmstudio-export" -Recurse
Files are named <id>.conversation.json and look like this (trimmed — the parser reads messages[].versions[currentlySelected], splitting thinking steps from the answer):
{ "name": "My chat", "createdAt": 1700000000000, "lastUsedModel": { "identifier": "gpt-oss-20b" }, "messages": [ { "versions": [ { "type": "singleStep", "role": "user", "content": [{ "type": "text", "text": "Hi" }] } ], "currentlySelected": 0 }, { "versions": [ { "type": "multiStep", "role": "assistant", "steps": [ { "type": "contentBlock", "style": { "type": "thinking" }, "content": [{ "type": "text", "text": "reasoning…" }] }, { "type": "contentBlock", "content": [{ "type": "text", "text": "Hello!" }] } ] } ], "currentlySelected": 0 } ] }
Conversation folders/subfolders are walked recursively. Tested on LM Studio 0.4.x (0.4.16); the format is undocumented and may change.
Note — LM Studio's local server ("router"). LM Studio can also run an OpenAI-compatible local server (Developer tab, default http://localhost:1234) that routes requests across loaded models. That's an inference endpoint, not an export — ThreadShelf indexes the on-disk conversations/ files above, so you don't need the server running to import or search your history.
ChatGPT → Settings → Data controls → Export data. You'll get an email with a zip; index the conversations.json inside it.
Claude → Settings → export your data. Index the exported conversation JSON.
Grok → request your account data export (xAI account/privacy settings). You'll get a download containing a prod-grok-backend.json somewhere under an export_data/.../ folder. Index the folder that contains it — ThreadShelf detects the file by content, so the surrounding directory names don't matter.
The file holds every conversation in one document, shaped roughly like this (the parser reads conversations[].responses[].response, splitting the model's agent_thinking_traces reasoning from its message answer):
{ "conversations": [ { "conversation": { "id": "…", "title": "My chat", "create_time": { "$date": { "$numberLong": "1771000000000" } } }, "responses": [ { "response": { "sender": "human", "message": "Hi", "create_time": { "$date": { "$numberLong": "1771000000000" } } } }, { "response": { "sender": "assistant", "message": "Hello!", "model": "grok-3", "agent_thinking_traces": [{ "thinking_trace": "reasoning…" }] } } ] } ] }
Timestamps are MongoDB extended JSON ({ "$date": { "$numberLong": … } }). The format is undocumented and may change.
npx threadshelf # downloads, starts, opens on http://localhost:3000
That is the whole install. The package ships the prebuilt web UI, so there is nothing to compile and no repository to clone. Pick another port with npx threadshelf 3001, and see npx threadshelf --help for the rest.
npx threadshelf search "what did I decide about caching?" npx threadshelf ingest ./my-exports my-collection npx threadshelf parse ./chatgpt-export.json npx threadshelf-mcp # stdio MCP server
Your archive is never stored inside the npm package: an npx install directory is disposable and npm may wipe it at any time. Persistent data lives in a per-user directory instead, so upgrading or clearing the npm cache leaves your index untouched.
Override it with --data-dir <path> or THREADSHELF_DATA_DIR, and print the resolved locations with npx threadshelf --where. The narrower overrides (LANCEDB_PATH, UPLOADS_DIR, …) still win over the defaults.
Developing on ThreadShelf, or just prefer the source?
npm install # installs server + client (npm workspaces) npm run build:client # builds the React UI into public/ npm start # serves on http://localhost:3000
A repository checkout ke
正文由 探How 从来源站点 RSS 同步,内容未经改写;遇到排版缺失或需要图片、视频时请以原文为准。