memex-mvp 0.9.1 → 0.10.1
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.
- package/HELP.md +35 -6
- package/README.md +37 -1
- package/README.ru.md +1 -2
- package/ingest.js +86 -0
- package/lib/cli/index.js +633 -20
- package/lib/import-telegram.js +156 -0
- package/lib/telegram-decisions.js +153 -0
- package/lib/telegram-discovery.js +328 -0
- package/lib/telegram-notify.js +184 -0
- package/lib/telegram-pending.js +200 -0
- package/package.json +2 -2
- package/server.js +453 -2
- package/skills/install-memex/README.md +1 -1
- package/skills/install-memex/SKILL.md +31 -8
package/HELP.md
CHANGED
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
- **Claude Cowork** (включая субагентов) — сессии из `~/Library/Application Support/Claude/local-agent-mode-sessions/`
|
|
13
13
|
- **Cursor** — composer history из `state.vscdb`
|
|
14
14
|
- **Obsidian** — заметки из настроенных vault'ов
|
|
15
|
-
- **Telegram** — экспорты
|
|
15
|
+
- **Telegram** — v0.10+: daemon сам ловит экспорты из `~/Downloads/Telegram Desktop/` → пакует в pending → ты per-chat подтверждаешь импорт (`memex telegram pending` + `memex telegram import 1 3 5`). Никакого ручного копирования
|
|
16
16
|
|
|
17
17
|
Всё это лежит в **одном файле** `~/.memex/data/memex.db` и доступно через MCP-tools любому AI на твоём компьютере.
|
|
18
18
|
|
|
@@ -290,6 +290,11 @@ Memex по дефолту сортирует по **релевантности**
|
|
|
290
290
|
| `memex_list_sources` | Что и сколько импортировано |
|
|
291
291
|
| `memex_status` | Здоровье memex-sync daemon'a |
|
|
292
292
|
| `memex_sources_status` | Какие источники включены/отключены |
|
|
293
|
+
| `memex_telegram_check` *(v0.10+)* | Состояние Telegram-pipeline: Desktop установлен? login age? сколько pending? Возвращает `next_step` для агента |
|
|
294
|
+
| `memex_telegram_pending` *(v0.10+)* | Список экспортов в `~/.memex/pending/` с chat name + msg count + дата range |
|
|
295
|
+
| `memex_telegram_import` *(v0.10+)* | Импорт выбранных экспортов в memex.db (по индексу или title). Auto-allowlist |
|
|
296
|
+
| `memex_telegram_skip` *(v0.10+)* | Пометить чаты как «не индексировать» — применяется к будущим re-export'ам |
|
|
297
|
+
| `memex_telegram_mode` *(v0.10+)* | Get/set режим: `pick` (default), `auto`, `manual` |
|
|
293
298
|
|
|
294
299
|
---
|
|
295
300
|
|
|
@@ -316,6 +321,19 @@ memex get web-1582ab51a7b7 # полный контент
|
|
|
316
321
|
|
|
317
322
|
memex overview # snapshot корпуса (+ capture streak v0.8.1+)
|
|
318
323
|
memex projects # уникальные project_paths
|
|
324
|
+
|
|
325
|
+
# Telegram pipeline (v0.10+):
|
|
326
|
+
memex telegram check # Desktop? 24h? watcher? pending?
|
|
327
|
+
memex telegram pending # список pending экспортов
|
|
328
|
+
memex telegram import 1 3 5 # импорт по индексам или title'у
|
|
329
|
+
memex telegram skip 4 # не индексировать; запомнить
|
|
330
|
+
memex telegram allow "Family" # auto-import на следующих re-export'ах
|
|
331
|
+
memex telegram block "*bank*" # никогда не индексировать (glob)
|
|
332
|
+
memex telegram mode auto # pick (default) / auto / manual
|
|
333
|
+
memex telegram notifications on --show-titles # macOS notif когда детектится новый export
|
|
334
|
+
memex telegram scan # one-shot ре-скан Downloads
|
|
335
|
+
memex telegram status # all decisions
|
|
336
|
+
|
|
319
337
|
memex help # эта инструкция в терминале
|
|
320
338
|
memex --help # справка по командам
|
|
321
339
|
memex --version
|
|
@@ -395,12 +413,23 @@ npx memex-sync scan
|
|
|
395
413
|
**memex поддерживает оба формата экспорта (v0.9+):**
|
|
396
414
|
|
|
397
415
|
- **«Machine-readable JSON»** ← рекомендуется. Один файл `result.json`, чисто ингестится.
|
|
398
|
-
- **«HTML»** ← тоже работает (с v0.9+). Получишь директорию `ChatExport_<chat>_<date
|
|
416
|
+
- **«HTML»** ← тоже работает (с v0.9+). Получишь директорию `ChatExport_<chat>_<date>/`.
|
|
417
|
+
|
|
418
|
+
**v0.10+ flow — privacy-first, без ручного копирования:**
|
|
419
|
+
|
|
420
|
+
1. Сохрани экспорт куда обычно (по умолчанию `~/Downloads/Telegram Desktop/ChatExport_*`).
|
|
421
|
+
2. Daemon **сам** детектит и переносит в `~/.memex/pending/`.
|
|
422
|
+
3. Получишь уведомление (через 4 канала: tip в CLI, в auto-context хуке Claude, в response любого MCP-tool, optionally macOS native notification). Подробнее: `memex telegram notifications status`.
|
|
423
|
+
4. Запусти `memex telegram pending` → увидишь список с превью.
|
|
424
|
+
5. Импорт: `memex telegram import 1 3 5` (по индексу) или просто скажи AI агенту «импортируй family и work».
|
|
425
|
+
6. Sensitive чаты можно пропустить: `memex telegram skip 4 7` — memex запомнит и не будет предлагать снова.
|
|
426
|
+
|
|
427
|
+
**Старый flow тоже работает** (для совместимости): кинь `result.json` или `ChatExport_*/` прямо в `~/.memex/inbox/` — попадёт сразу в `memex.db` без pending review.
|
|
399
428
|
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
429
|
+
**Modes** (`memex telegram mode <pick|auto|manual>`):
|
|
430
|
+
- `pick` (default): всё через pending, ты явно подтверждаешь
|
|
431
|
+
- `auto`: чаты из allow-list импортятся сами, новые — в pending
|
|
432
|
+
- `manual`: watcher выключен, только ручной inbox
|
|
404
433
|
|
|
405
434
|
Если ингест не произошёл — проверь `~/.memex/data/memex.log`. Для HTML-export'а парсер пишет actionable error если что-то не так (формат изменился у Telegram, директория повреждена, и т.д.).
|
|
406
435
|
|
package/README.md
CHANGED
|
@@ -180,6 +180,37 @@ Perplexity threads need to be made **Public** in the Share dialog first — meme
|
|
|
180
180
|
|
|
181
181
|
---
|
|
182
182
|
|
|
183
|
+
## Telegram chats (v0.10+) — agent walks you through it
|
|
184
|
+
|
|
185
|
+
Telegram-export setup used to be 8 steps. v0.10+ collapses it to 2 (you click in Telegram; you pick which chats to keep). The rest is automatic.
|
|
186
|
+
|
|
187
|
+
**How it works:**
|
|
188
|
+
1. The daemon watches `~/Downloads/Telegram Desktop/` in the background. **No setup needed** — already on after install.
|
|
189
|
+
2. You export a chat from Telegram Desktop (chat → ⋮ → Export chat history → HTML or JSON).
|
|
190
|
+
3. memex detects the export, **moves it to `~/.memex/pending/`** (NOT into your DB yet).
|
|
191
|
+
4. Your AI agent (or you in terminal) calls `memex_telegram_pending` — sees a numbered list with chat name, msg count, date range.
|
|
192
|
+
5. You pick which to import. Sensitive ones (Bank, Therapist, Tinder) — skip. memex remembers and won't ask again.
|
|
193
|
+
6. Future re-exports of allowed chats auto-merge. Skipped ones stay out.
|
|
194
|
+
|
|
195
|
+
**The agent leads.** Just say *"set up Telegram for memex"* (or **install memex** in a fresh session — the install-memex skill v1.2+ proactively offers it). The agent will:
|
|
196
|
+
- Check if Telegram Desktop is installed (give you the right download link if not)
|
|
197
|
+
- Check the 24h post-login export-block window (tell you when you can export)
|
|
198
|
+
- Show the click-path in Telegram
|
|
199
|
+
- Wait for your export, then present the picker
|
|
200
|
+
|
|
201
|
+
**Three modes:** `pick` (default — review each export), `auto` (allowed chats auto-import; new ones go to pending), `manual` (watcher off — drop files yourself).
|
|
202
|
+
|
|
203
|
+
Terminal equivalents: `memex telegram check / pending / import 1 3 5 / skip 2 / mode auto`. Full reference: `memex telegram --help`.
|
|
204
|
+
|
|
205
|
+
**v0.10.1: 4-channel proactive notification.** You'll find out about pending exports from whichever channel reaches you first:
|
|
206
|
+
|
|
207
|
+
1. **In the AI agent (active session)** — `memex_search` / `memex_recent` / `memex_overview` tool responses include a `telegram_pending` field with chat names. Agent surfaces it as a natural aside.
|
|
208
|
+
2. **In the terminal** — any `memex` CLI command appends a 💡 tip line when pending > 0. Throttled to once per 6h.
|
|
209
|
+
3. **macOS native notification** (opt-in) — daemon fires a banner when a new export is staged. `memex telegram notifications on` to enable. Default OFF for lock-screen privacy; add `--show-titles` if you want chat names in the banner.
|
|
210
|
+
4. **Brian Chesky hook (next Claude Code session)** — `memex context` injection includes a "🆕 N exports awaiting review" block with chat names. Claude leads with the question before you type anything.
|
|
211
|
+
|
|
212
|
+
---
|
|
213
|
+
|
|
183
214
|
## What it captures
|
|
184
215
|
|
|
185
216
|
| Source | How it gets in |
|
|
@@ -189,7 +220,7 @@ Perplexity threads need to be made **Public** in the Share dialog first — meme
|
|
|
189
220
|
| Cursor IDE chats | Auto: reads Cursor's local SQLite session store |
|
|
190
221
|
| Continue / Zed | Auto: filesystem watchers per platform |
|
|
191
222
|
| Obsidian notes | Auto: per-vault markdown watcher |
|
|
192
|
-
| Telegram exports |
|
|
223
|
+
| Telegram exports | **v0.10+: auto.** Daemon watches `~/Downloads/Telegram Desktop/`. Each new ChatExport appears in `memex telegram pending` — review chat-by-chat, import the ones you want. Privacy-first: nothing lands in the DB without your `memex telegram import <indices>`. Allow-list remembers your decisions so future re-exports auto-merge. JSON + HTML both supported. (Legacy path still works: drop into `~/.memex/inbox/`.) |
|
|
193
224
|
| Telegram (live) | Run [`memex-bot`](bot/README.md) — captures messages you send/forward to your private bot |
|
|
194
225
|
| **Web pages, AI chat shares, pasted text** | From any MCP agent: *"save https://... to memex"*. Agent fetches; memex stores verbatim. Cloudflare-protected pages (Perplexity, npm.com, Twitter, Medium, …) handled via the agent's r.jina.ai fallback. See [HELP.md §8](HELP.md) |
|
|
195
226
|
|
|
@@ -214,6 +245,11 @@ All sources land in the same FTS5 corpus, searchable by one `memex_search` call.
|
|
|
214
245
|
| `memex_status` | Daemon health: PID, last capture, watched files |
|
|
215
246
|
| `memex_sources_status` | Which sources are captured + the exact CLI to opt out |
|
|
216
247
|
| `memex_help` | Returns the full user guide with concrete use cases |
|
|
248
|
+
| `memex_telegram_check` | v0.10+: Detect Telegram Desktop, login age (24h block), pending count, suggested next step |
|
|
249
|
+
| `memex_telegram_pending` | v0.10+: List exports staged for review with chat name + msg count + dates |
|
|
250
|
+
| `memex_telegram_import` | v0.10+: Import selected exports into memex.db (by index or title) — auto-allowlists |
|
|
251
|
+
| `memex_telegram_skip` | v0.10+: Mark chats as "never index" — applies to future re-exports too |
|
|
252
|
+
| `memex_telegram_mode` | v0.10+: Get/set capture mode: pick (default) · auto · manual |
|
|
217
253
|
|
|
218
254
|
Detailed search parameters (filters, sort, format) live in [HELP.md](HELP.md).
|
|
219
255
|
|
package/README.ru.md
CHANGED
|
@@ -241,8 +241,7 @@ which node # → путь до бинарника node (например /Users
|
|
|
241
241
|
| **Claude Cowork** | `cowork-*.jsonl` (через filename prefix), включая subagents | ✅ работает |
|
|
242
242
|
| **Cursor IDE** (Composer + Chat) | SQLite `state.vscdb` в `~/Library/Application Support/Cursor/` | ✅ работает (poll каждые 5 мин) |
|
|
243
243
|
| **Obsidian** vault notes | `.md` файлы + YAML frontmatter | ✅ работает (FSEvents, hash-based dedupe) |
|
|
244
|
-
| **Telegram (JSON export)** | `result.json` из Desktop
|
|
245
|
-
| **Telegram (HTML export)** | директория `ChatExport_*` из Desktop (v0.9+) | ✅ работает — кидай всю папку в inbox |
|
|
244
|
+
| **Telegram (JSON / HTML export)** | `result.json` или `ChatExport_*/` из Telegram Desktop | ✅ работает — **v0.10+: авто-детект.** Daemon следит за `~/Downloads/Telegram Desktop/`, экспорты сами попадают в pending, юзер per-chat подтверждает |
|
|
246
245
|
| **Telegram (live)** | бот `memex-bot` ловит твои сообщения / форварды | ✅ работает |
|
|
247
246
|
| **Web-страницы, AI-share'ы, paste'ы** | `memex_store_document` — агент fetch'ит, memex хранит verbatim (v0.6+) | ✅ работает |
|
|
248
247
|
| Claude.ai web export | будет в v0.7 | — |
|
package/ingest.js
CHANGED
|
@@ -1332,6 +1332,92 @@ if (!ANY_ONESHOT_MODE && OBSIDIAN_ENABLED) {
|
|
|
1332
1332
|
}
|
|
1333
1333
|
}
|
|
1334
1334
|
|
|
1335
|
+
// -------------------- Telegram Downloads watcher (v0.10+) --------------------
|
|
1336
|
+
// Watches ~/Downloads/Telegram Desktop/ for new ChatExport_* folders or
|
|
1337
|
+
// result.json files. On detection, moves them to ~/.memex/pending/ so the
|
|
1338
|
+
// user can review with `memex telegram pending` before any data lands in
|
|
1339
|
+
// memex.db. Privacy-first — nothing is auto-imported here.
|
|
1340
|
+
const TG_DOWNLOADS_ENABLED = isSourceEnabled('telegram-downloads', CONFIG);
|
|
1341
|
+
// Default Telegram Desktop export location is identical across platforms:
|
|
1342
|
+
// ~/Downloads/Telegram Desktop/. Config can override via sources.telegram-downloads.paths.
|
|
1343
|
+
const TG_DOWNLOADS_PATHS = (() => {
|
|
1344
|
+
if (!TG_DOWNLOADS_ENABLED) return [];
|
|
1345
|
+
const configured = (CONFIG.sources?.['telegram-downloads']?.paths) || [];
|
|
1346
|
+
const defaults = [join(homedir(), 'Downloads', 'Telegram Desktop')];
|
|
1347
|
+
const all = (configured.length ? configured : defaults).map((p) =>
|
|
1348
|
+
p.startsWith('~') ? join(homedir(), p.slice(1)) : p
|
|
1349
|
+
);
|
|
1350
|
+
return all.filter((p) => existsSync(p));
|
|
1351
|
+
})();
|
|
1352
|
+
|
|
1353
|
+
if (!ANY_ONESHOT_MODE && TG_DOWNLOADS_ENABLED && TG_DOWNLOADS_PATHS.length) {
|
|
1354
|
+
for (const dlPath of TG_DOWNLOADS_PATHS) {
|
|
1355
|
+
log(`watching telegram-downloads: ${dlPath}`);
|
|
1356
|
+
const w = chokidar
|
|
1357
|
+
.watch(dlPath, {
|
|
1358
|
+
ignoreInitial: false,
|
|
1359
|
+
awaitWriteFinish: { stabilityThreshold: 2000, pollInterval: 400 },
|
|
1360
|
+
depth: 0,
|
|
1361
|
+
})
|
|
1362
|
+
.on('addDir', (p) => {
|
|
1363
|
+
if (p === dlPath) return; // root itself
|
|
1364
|
+
if (!basename(p).toLowerCase().startsWith('chatexport_')) return;
|
|
1365
|
+
scheduleTelegramStaging(p);
|
|
1366
|
+
})
|
|
1367
|
+
.on('add', (p) => {
|
|
1368
|
+
if (basename(p).toLowerCase() === 'result.json') {
|
|
1369
|
+
scheduleTelegramStaging(p);
|
|
1370
|
+
}
|
|
1371
|
+
})
|
|
1372
|
+
.on('error', (e) => log(`watcher error (telegram-downloads): ${e.message}`));
|
|
1373
|
+
watchers.push(w);
|
|
1374
|
+
}
|
|
1375
|
+
}
|
|
1376
|
+
|
|
1377
|
+
const tgStagingPending = new Map();
|
|
1378
|
+
function scheduleTelegramStaging(srcPath) {
|
|
1379
|
+
if (tgStagingPending.has(srcPath)) clearTimeout(tgStagingPending.get(srcPath));
|
|
1380
|
+
// Use a longer debounce — exports can take several seconds to finish writing
|
|
1381
|
+
tgStagingPending.set(srcPath, setTimeout(async () => {
|
|
1382
|
+
tgStagingPending.delete(srcPath);
|
|
1383
|
+
if (!existsSync(srcPath)) return;
|
|
1384
|
+
try {
|
|
1385
|
+
const { stageExport, listPending } = await import('./lib/telegram-pending.js');
|
|
1386
|
+
const dest = stageExport(srcPath, { moveOrCopy: 'move' });
|
|
1387
|
+
log(`+ telegram-export staged → pending/: ${basename(dest)}`);
|
|
1388
|
+
|
|
1389
|
+
// Channel C: macOS native notification. Default OFF, opt-in via
|
|
1390
|
+
// `memex telegram notifications on [--show-titles]`. Dedup by path
|
|
1391
|
+
// hash so re-stages of the same export don't re-notify.
|
|
1392
|
+
try {
|
|
1393
|
+
const notify = await import('./lib/telegram-notify.js');
|
|
1394
|
+
const state = notify.loadNotifyState();
|
|
1395
|
+
if (state.notifications.enabled && !notify.notifShownFor(state, dest)) {
|
|
1396
|
+
// Look up preview to know the chat name (only if user opted in)
|
|
1397
|
+
const list = listPending();
|
|
1398
|
+
const justStaged = list.find((e) => e.path === dest) || {};
|
|
1399
|
+
const totalPending = list.length;
|
|
1400
|
+
const showTitles = !!state.notifications.show_titles;
|
|
1401
|
+
const title = `memex · ${totalPending} new Telegram chat${totalPending === 1 ? '' : 's'}`;
|
|
1402
|
+
const body = showTitles && justStaged.chat_title
|
|
1403
|
+
? `"${justStaged.chat_title}" — ${justStaged.message_count?.toLocaleString?.() || '?'} msgs. Review with: memex telegram pending`
|
|
1404
|
+
: `Review with: memex telegram pending`;
|
|
1405
|
+
const fired = notify.fireMacosNotification(title, body, { subtitle: 'Ready to review' });
|
|
1406
|
+
if (fired) {
|
|
1407
|
+
notify.markNotifShown(state, [dest]);
|
|
1408
|
+
notify.saveNotifyState(state);
|
|
1409
|
+
log(` notif fired (macOS, ${showTitles ? 'with' : 'no'} title)`);
|
|
1410
|
+
}
|
|
1411
|
+
}
|
|
1412
|
+
} catch (e) {
|
|
1413
|
+
log(` notif skipped: ${e.message}`);
|
|
1414
|
+
}
|
|
1415
|
+
} catch (e) {
|
|
1416
|
+
log(`! telegram-export stage failed (${basename(srcPath)}): ${e.message}`);
|
|
1417
|
+
}
|
|
1418
|
+
}, 5000));
|
|
1419
|
+
}
|
|
1420
|
+
|
|
1335
1421
|
// Synchronous one-shot walk for scan-obsidian / scan modes.
|
|
1336
1422
|
function scanObsidian() {
|
|
1337
1423
|
if (OBSIDIAN_VAULTS.length === 0) {
|