@colbymchenry/cmem 0.2.32 → 0.2.36

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/README.md CHANGED
@@ -62,6 +62,8 @@ Ever closed your terminal and lost that perfect Claude conversation? The one whe
62
62
 
63
63
  🔄 **Automatic Sync** — A background daemon watches your Claude Code sessions and keeps everything indexed in real-time.
64
64
 
65
+ 💾 **Never Lost** — Every conversation is automatically backed up. Even if Claude Code clears its storage, cmem restores your sessions instantly when you resume them.
66
+
65
67
  📦 **100% Local & Private** — Everything runs on your machine. No cloud services, no API keys, no external servers. Just SQLite + local AI embeddings.
66
68
 
67
69
  ## Quick Start
@@ -215,6 +217,7 @@ Browse sessions organized by project folder.
215
217
  All data is stored locally in `~/.cmem/`:
216
218
  - `sessions.db` — SQLite database with vector embeddings
217
219
  - `models/` — Downloaded embedding model (~130MB)
220
+ - `backups/` — Full copies of all conversation JSONLs (auto-restored if Claude deletes them)
218
221
 
219
222
  Claude Code sessions are read from `~/.claude/`.
220
223
 
package/dist/cli.js CHANGED
@@ -17,9 +17,9 @@ __export(install_exports, {
17
17
  uninstallCommand: () => uninstallCommand
18
18
  });
19
19
  import chalk9 from "chalk";
20
- import { writeFileSync, unlinkSync, existsSync as existsSync8, mkdirSync as mkdirSync2 } from "fs";
20
+ import { writeFileSync, unlinkSync, existsSync as existsSync9, mkdirSync as mkdirSync2 } from "fs";
21
21
  import { homedir as homedir2 } from "os";
22
- import { join as join5, dirname as dirname4 } from "path";
22
+ import { join as join5, dirname as dirname5 } from "path";
23
23
  import { execSync as execSync2 } from "child_process";
24
24
  function getCmemPath() {
25
25
  try {
@@ -32,7 +32,7 @@ function getCmemPath() {
32
32
  function getNodeBinDir() {
33
33
  try {
34
34
  const nodePath = execSync2("which node", { encoding: "utf-8" }).trim();
35
- return dirname4(nodePath);
35
+ return dirname5(nodePath);
36
36
  } catch {
37
37
  return "/usr/local/bin";
38
38
  }
@@ -84,16 +84,16 @@ async function installCommand() {
84
84
  return;
85
85
  }
86
86
  console.log(chalk9.cyan("Installing cmem watch daemon...\n"));
87
- if (!existsSync8(LAUNCH_AGENTS_DIR)) {
87
+ if (!existsSync9(LAUNCH_AGENTS_DIR)) {
88
88
  mkdirSync2(LAUNCH_AGENTS_DIR, { recursive: true });
89
89
  }
90
90
  const cmemDir = join5(homedir2(), ".cmem");
91
- if (!existsSync8(cmemDir)) {
91
+ if (!existsSync9(cmemDir)) {
92
92
  mkdirSync2(cmemDir, { recursive: true });
93
93
  }
94
94
  const cmemPath = getCmemPath();
95
95
  console.log(chalk9.dim(`Using cmem at: ${cmemPath}`));
96
- if (existsSync8(PLIST_PATH)) {
96
+ if (existsSync9(PLIST_PATH)) {
97
97
  console.log(chalk9.yellow("LaunchAgent already exists. Unloading first..."));
98
98
  try {
99
99
  execSync2(`launchctl unload "${PLIST_PATH}"`, { stdio: "ignore" });
@@ -126,7 +126,7 @@ async function uninstallCommand() {
126
126
  return;
127
127
  }
128
128
  console.log(chalk9.cyan("Uninstalling cmem watch daemon...\n"));
129
- if (!existsSync8(PLIST_PATH)) {
129
+ if (!existsSync9(PLIST_PATH)) {
130
130
  console.log(chalk9.yellow("LaunchAgent not found. Nothing to uninstall."));
131
131
  return;
132
132
  }
@@ -152,7 +152,7 @@ async function statusCommand() {
152
152
  return;
153
153
  }
154
154
  console.log(chalk9.cyan("cmem watch daemon status\n"));
155
- if (!existsSync8(PLIST_PATH)) {
155
+ if (!existsSync9(PLIST_PATH)) {
156
156
  console.log(chalk9.yellow("Status: Not installed"));
157
157
  console.log(chalk9.dim("Run: cmem install"));
158
158
  return;
@@ -195,8 +195,8 @@ var init_install = __esm({
195
195
  import { program } from "commander";
196
196
  import { render } from "ink";
197
197
  import React2 from "react";
198
- import { existsSync as existsSync10, readFileSync as readFileSync4 } from "fs";
199
- import { join as join7, dirname as dirname6 } from "path";
198
+ import { existsSync as existsSync11, readFileSync as readFileSync4 } from "fs";
199
+ import { join as join7, dirname as dirname7 } from "path";
200
200
  import { fileURLToPath as fileURLToPath2 } from "url";
201
201
  import { spawn as spawn2 } from "child_process";
202
202
 
@@ -205,12 +205,84 @@ import { useState as useState2, useEffect as useEffect2, useCallback as useCallb
205
205
  import { Box as Box6, Text as Text6, useInput, useApp } from "ink";
206
206
  import TextInput2 from "ink-text-input";
207
207
  import Spinner from "ink-spinner";
208
- import { basename as basename4, dirname as dirname2 } from "path";
209
- import { existsSync as existsSync5 } from "fs";
208
+ import { basename as basename5, dirname as dirname3 } from "path";
209
+ import { existsSync as existsSync6 } from "fs";
210
+
211
+ // src/utils/config.ts
212
+ import { homedir } from "os";
213
+ import { join, basename, dirname } from "path";
214
+ import { mkdirSync, existsSync, copyFileSync } from "fs";
215
+ var CMEM_DIR = join(homedir(), ".cmem");
216
+ var DB_PATH = join(CMEM_DIR, "sessions.db");
217
+ var MODELS_DIR = join(CMEM_DIR, "models");
218
+ var BACKUPS_DIR = join(CMEM_DIR, "backups");
219
+ var CLAUDE_DIR = join(homedir(), ".claude");
220
+ var CLAUDE_PROJECTS_DIR = join(CLAUDE_DIR, "projects");
221
+ var CLAUDE_SESSIONS_DIR = join(CLAUDE_DIR, "sessions");
222
+ var EMBEDDING_MODEL = "nomic-ai/nomic-embed-text-v1.5";
223
+ var EMBEDDING_DIMENSIONS = 768;
224
+ var MAX_EMBEDDING_CHARS = 8e3;
225
+ var MAX_MESSAGE_PREVIEW_CHARS = 500;
226
+ var MAX_MESSAGES_FOR_CONTEXT = 20;
227
+ function ensureCmemDir() {
228
+ if (!existsSync(CMEM_DIR)) {
229
+ mkdirSync(CMEM_DIR, { recursive: true });
230
+ }
231
+ }
232
+ function ensureModelsDir() {
233
+ ensureCmemDir();
234
+ if (!existsSync(MODELS_DIR)) {
235
+ mkdirSync(MODELS_DIR, { recursive: true });
236
+ }
237
+ }
238
+ function ensureBackupsDir() {
239
+ ensureCmemDir();
240
+ if (!existsSync(BACKUPS_DIR)) {
241
+ mkdirSync(BACKUPS_DIR, { recursive: true });
242
+ }
243
+ }
244
+ function getBackupPath(sourceFile) {
245
+ const projectDir = basename(dirname(sourceFile));
246
+ const sessionFile = basename(sourceFile);
247
+ return join(BACKUPS_DIR, projectDir, sessionFile);
248
+ }
249
+ function backupSessionFile(sourceFile) {
250
+ try {
251
+ if (!existsSync(sourceFile)) return false;
252
+ ensureBackupsDir();
253
+ const backupPath = getBackupPath(sourceFile);
254
+ const backupDir = dirname(backupPath);
255
+ if (!existsSync(backupDir)) {
256
+ mkdirSync(backupDir, { recursive: true });
257
+ }
258
+ copyFileSync(sourceFile, backupPath);
259
+ return true;
260
+ } catch {
261
+ return false;
262
+ }
263
+ }
264
+ function hasBackup(sourceFile) {
265
+ const backupPath = getBackupPath(sourceFile);
266
+ return existsSync(backupPath);
267
+ }
268
+ function restoreFromBackup(sourceFile) {
269
+ try {
270
+ const backupPath = getBackupPath(sourceFile);
271
+ if (!existsSync(backupPath)) return false;
272
+ const sourceDir = dirname(sourceFile);
273
+ if (!existsSync(sourceDir)) {
274
+ mkdirSync(sourceDir, { recursive: true });
275
+ }
276
+ copyFileSync(backupPath, sourceFile);
277
+ return true;
278
+ } catch {
279
+ return false;
280
+ }
281
+ }
210
282
 
211
283
  // src/ui/components/Header.tsx
212
284
  import { Box, Text } from "ink";
213
- import { basename } from "path";
285
+ import { basename as basename2 } from "path";
214
286
  import { jsx, jsxs } from "react/jsx-runtime";
215
287
  var Header = ({ embeddingsReady, projectFilter }) => {
216
288
  return /* @__PURE__ */ jsxs(Box, { marginBottom: 1, justifyContent: "space-between", children: [
@@ -218,7 +290,7 @@ var Header = ({ embeddingsReady, projectFilter }) => {
218
290
  /* @__PURE__ */ jsx(Text, { bold: true, color: "cyan", children: "cmem" }),
219
291
  projectFilter && /* @__PURE__ */ jsxs(Text, { color: "yellow", children: [
220
292
  " \u{1F4C1} ",
221
- basename(projectFilter)
293
+ basename2(projectFilter)
222
294
  ] }),
223
295
  !embeddingsReady && /* @__PURE__ */ jsx(Text, { color: "yellow", dimColor: true, children: " (loading model...)" })
224
296
  ] }),
@@ -251,7 +323,7 @@ var SearchInput = ({
251
323
 
252
324
  // src/ui/components/SessionList.tsx
253
325
  import { Box as Box3, Text as Text3 } from "ink";
254
- import { basename as basename2 } from "path";
326
+ import { basename as basename3 } from "path";
255
327
 
256
328
  // src/utils/format.ts
257
329
  function formatTimeAgo(timestamp) {
@@ -362,7 +434,7 @@ var SessionList = ({
362
434
  var SessionItem = ({ session, isSelected }) => {
363
435
  const hasCustomTitle = !!session.customTitle;
364
436
  const displayTitle = truncate(session.customTitle || session.title, 38);
365
- const folderName = session.projectPath ? truncate(basename2(session.projectPath), 38) : "";
437
+ const folderName = session.projectPath ? truncate(basename3(session.projectPath), 38) : "";
366
438
  const msgs = String(session.messageCount).padStart(3);
367
439
  const updated = formatTimeAgo(session.updatedAt);
368
440
  const getTitleColor = () => {
@@ -524,33 +596,6 @@ import Database from "better-sqlite3";
524
596
  import * as sqliteVec from "sqlite-vec";
525
597
  import { existsSync as existsSync3 } from "fs";
526
598
 
527
- // src/utils/config.ts
528
- import { homedir } from "os";
529
- import { join } from "path";
530
- import { mkdirSync, existsSync } from "fs";
531
- var CMEM_DIR = join(homedir(), ".cmem");
532
- var DB_PATH = join(CMEM_DIR, "sessions.db");
533
- var MODELS_DIR = join(CMEM_DIR, "models");
534
- var CLAUDE_DIR = join(homedir(), ".claude");
535
- var CLAUDE_PROJECTS_DIR = join(CLAUDE_DIR, "projects");
536
- var CLAUDE_SESSIONS_DIR = join(CLAUDE_DIR, "sessions");
537
- var EMBEDDING_MODEL = "nomic-ai/nomic-embed-text-v1.5";
538
- var EMBEDDING_DIMENSIONS = 768;
539
- var MAX_EMBEDDING_CHARS = 8e3;
540
- var MAX_MESSAGE_PREVIEW_CHARS = 500;
541
- var MAX_MESSAGES_FOR_CONTEXT = 20;
542
- function ensureCmemDir() {
543
- if (!existsSync(CMEM_DIR)) {
544
- mkdirSync(CMEM_DIR, { recursive: true });
545
- }
546
- }
547
- function ensureModelsDir() {
548
- ensureCmemDir();
549
- if (!existsSync(MODELS_DIR)) {
550
- mkdirSync(MODELS_DIR, { recursive: true });
551
- }
552
- }
553
-
554
599
  // src/parser/index.ts
555
600
  import { readFileSync, readdirSync, existsSync as existsSync2, statSync } from "fs";
556
601
  import { join as join2 } from "path";
@@ -1308,6 +1353,19 @@ function createEmbeddingText(title, summary, messages) {
1308
1353
  }
1309
1354
 
1310
1355
  // src/ui/hooks/useSessions.ts
1356
+ import { existsSync as existsSync5 } from "fs";
1357
+ function isRecoverable(session) {
1358
+ if (!session.sourceFile) return false;
1359
+ if (existsSync5(session.sourceFile)) return true;
1360
+ if (hasBackup(session.sourceFile)) return true;
1361
+ return false;
1362
+ }
1363
+ function ensureBackedUp(session) {
1364
+ if (!session.sourceFile) return;
1365
+ if (existsSync5(session.sourceFile) && !hasBackup(session.sourceFile)) {
1366
+ backupSessionFile(session.sourceFile);
1367
+ }
1368
+ }
1311
1369
  function useSessions(options = {}) {
1312
1370
  const [sessions, setSessions] = useState([]);
1313
1371
  const [allSessions, setAllSessions] = useState([]);
@@ -1372,13 +1430,15 @@ function useSessions(options = {}) {
1372
1430
  const loadSessions = useCallback(() => {
1373
1431
  try {
1374
1432
  const loaded = projectFilter ? listHumanSessionsByProject(projectFilter) : listHumanSessions();
1433
+ loaded.forEach(ensureBackedUp);
1434
+ const recoverable = loaded.filter(isRecoverable);
1375
1435
  const favIds = getFavoriteSessionIds();
1376
1436
  const orderMap = getProjectOrders();
1377
1437
  setFavoriteSessionIds(favIds);
1378
1438
  setProjectOrderMap(orderMap);
1379
1439
  setHasFavSessions(hasFavoriteSessions());
1380
1440
  setHasCustomOrder(hasCustomProjectOrder());
1381
- const sorted = smartSort(loaded, favIds);
1441
+ const sorted = smartSort(recoverable, favIds);
1382
1442
  setAllSessions(sorted);
1383
1443
  setSessions(sorted);
1384
1444
  if (!projectFilter) {
@@ -1511,9 +1571,6 @@ function useSessions(options = {}) {
1511
1571
 
1512
1572
  // src/ui/App.tsx
1513
1573
  import { jsx as jsx6, jsxs as jsxs6 } from "react/jsx-runtime";
1514
- var clearScreen = () => {
1515
- process.stdout.write("\x1B[2J\x1B[0f");
1516
- };
1517
1574
  var App = ({ onResume, projectFilter: initialProjectFilter }) => {
1518
1575
  const { exit } = useApp();
1519
1576
  const {
@@ -1537,6 +1594,7 @@ var App = ({ onResume, projectFilter: initialProjectFilter }) => {
1537
1594
  const [renameValue, setRenameValue] = useState2("");
1538
1595
  const [currentTab, setCurrentTab] = useState2("global");
1539
1596
  const [selectedProjectPath, setSelectedProjectPath] = useState2(null);
1597
+ const [renderKey, setRenderKey] = useState2(0);
1540
1598
  const getCurrentView = useCallback2(() => {
1541
1599
  if (currentTab === "projects") {
1542
1600
  return selectedProjectPath ? "project-sessions" : "projects";
@@ -1581,34 +1639,50 @@ var App = ({ onResume, projectFilter: initialProjectFilter }) => {
1581
1639
  const session = currentSessions2[selectedIndex];
1582
1640
  if (!session) return;
1583
1641
  if (session.sourceFile) {
1584
- const filename = basename4(session.sourceFile);
1642
+ if (!existsSync6(session.sourceFile)) {
1643
+ if (hasBackup(session.sourceFile)) {
1644
+ const restored = restoreFromBackup(session.sourceFile);
1645
+ if (restored) {
1646
+ setStatusMessage("Restored session from backup");
1647
+ } else {
1648
+ setStatusMessage("Failed to restore session from backup");
1649
+ return;
1650
+ }
1651
+ } else {
1652
+ setStatusMessage("Session file deleted and no backup - cannot resume");
1653
+ return;
1654
+ }
1655
+ }
1656
+ const filename = basename5(session.sourceFile);
1585
1657
  const claudeSessionId = filename.replace(".jsonl", "");
1586
- const projectDirName = basename4(dirname2(session.sourceFile));
1587
- let projectPath = null;
1588
- if (projectDirName.startsWith("-")) {
1589
- const segments = projectDirName.substring(1).split("-");
1590
- let currentPath = "";
1591
- let remainingSegments = [...segments];
1592
- while (remainingSegments.length > 0) {
1593
- let found = false;
1594
- for (let i = remainingSegments.length; i > 0; i--) {
1595
- const testSegment = remainingSegments.slice(0, i).join("-");
1596
- const testPath = currentPath + "/" + testSegment;
1597
- if (existsSync5(testPath)) {
1598
- currentPath = testPath;
1599
- remainingSegments = remainingSegments.slice(i);
1600
- found = true;
1658
+ let projectPath = session.projectPath;
1659
+ if (!projectPath) {
1660
+ const projectDirName = basename5(dirname3(session.sourceFile));
1661
+ if (projectDirName.startsWith("-")) {
1662
+ const segments = projectDirName.substring(1).split("-");
1663
+ let currentPath = "";
1664
+ let remainingSegments = [...segments];
1665
+ while (remainingSegments.length > 0) {
1666
+ let found = false;
1667
+ for (let i = remainingSegments.length; i > 0; i--) {
1668
+ const testSegment = remainingSegments.slice(0, i).join("-");
1669
+ const testPath = currentPath + "/" + testSegment;
1670
+ if (existsSync6(testPath)) {
1671
+ currentPath = testPath;
1672
+ remainingSegments = remainingSegments.slice(i);
1673
+ found = true;
1674
+ break;
1675
+ }
1676
+ }
1677
+ if (!found) {
1678
+ currentPath = currentPath + "/" + remainingSegments.join("-");
1601
1679
  break;
1602
1680
  }
1603
1681
  }
1604
- if (!found) {
1605
- currentPath = currentPath + "/" + remainingSegments.join("-");
1606
- break;
1682
+ if (currentPath && existsSync6(currentPath)) {
1683
+ projectPath = currentPath;
1607
1684
  }
1608
1685
  }
1609
- if (currentPath && existsSync5(currentPath)) {
1610
- projectPath = currentPath;
1611
- }
1612
1686
  }
1613
1687
  if (onResume) {
1614
1688
  onResume(claudeSessionId, projectPath);
@@ -1621,13 +1695,13 @@ var App = ({ onResume, projectFilter: initialProjectFilter }) => {
1621
1695
  const handleEnterProject = useCallback2(() => {
1622
1696
  const project = projects[selectedIndex];
1623
1697
  if (project) {
1624
- clearScreen();
1698
+ setRenderKey((k) => k + 1);
1625
1699
  setSelectedProjectPath(project.path);
1626
1700
  setSelectedIndex(0);
1627
1701
  }
1628
1702
  }, [projects, selectedIndex]);
1629
1703
  const handleBackToProjects = useCallback2(() => {
1630
- clearScreen();
1704
+ setRenderKey((k) => k + 1);
1631
1705
  setSelectedProjectPath(null);
1632
1706
  setSelectedIndex(0);
1633
1707
  }, []);
@@ -1743,7 +1817,7 @@ var App = ({ onResume, projectFilter: initialProjectFilter }) => {
1743
1817
  }
1744
1818
  if (key.leftArrow || input === "h") {
1745
1819
  if (currentTab === "projects") {
1746
- clearScreen();
1820
+ setRenderKey((k) => k + 1);
1747
1821
  setCurrentTab("global");
1748
1822
  setSelectedProjectPath(null);
1749
1823
  }
@@ -1751,7 +1825,7 @@ var App = ({ onResume, projectFilter: initialProjectFilter }) => {
1751
1825
  }
1752
1826
  if (key.rightArrow || input === "l") {
1753
1827
  if (currentTab === "global") {
1754
- clearScreen();
1828
+ setRenderKey((k) => k + 1);
1755
1829
  setCurrentTab("projects");
1756
1830
  }
1757
1831
  return;
@@ -1821,7 +1895,7 @@ var App = ({ onResume, projectFilter: initialProjectFilter }) => {
1821
1895
  }
1822
1896
  ) : currentView === "project-sessions" && selectedProjectPath ? /* @__PURE__ */ jsxs6(Box6, { children: [
1823
1897
  /* @__PURE__ */ jsx6(Text6, { dimColor: true, children: "Projects \u2192 " }),
1824
- /* @__PURE__ */ jsx6(Text6, { color: "blue", children: basename4(selectedProjectPath) })
1898
+ /* @__PURE__ */ jsx6(Text6, { color: "blue", children: basename5(selectedProjectPath) })
1825
1899
  ] }) : /* @__PURE__ */ jsx6(Box6, { children: /* @__PURE__ */ jsx6(Text6, { dimColor: true, children: "Browse projects below" }) }),
1826
1900
  currentView === "projects" ? /* @__PURE__ */ jsx6(
1827
1901
  ProjectList,
@@ -1912,7 +1986,7 @@ var App = ({ onResume, projectFilter: initialProjectFilter }) => {
1912
1986
  ),
1913
1987
  /* @__PURE__ */ jsx6(Text6, { dimColor: true, children: " [\u2190\u2192] switch" })
1914
1988
  ] })
1915
- ] });
1989
+ ] }, renderKey);
1916
1990
  };
1917
1991
 
1918
1992
  // src/commands/save.ts
@@ -2210,7 +2284,7 @@ async function deleteCommand(id) {
2210
2284
 
2211
2285
  // src/commands/stats.ts
2212
2286
  import chalk6 from "chalk";
2213
- import { statSync as statSync2, existsSync as existsSync6 } from "fs";
2287
+ import { statSync as statSync2, existsSync as existsSync7 } from "fs";
2214
2288
  async function statsCommand() {
2215
2289
  console.log(chalk6.cyan("cmem Storage Statistics\n"));
2216
2290
  const stats = getStats();
@@ -2218,7 +2292,7 @@ async function statsCommand() {
2218
2292
  console.log(` Sessions: ${formatNumber(stats.sessionCount)}`);
2219
2293
  console.log(` Messages: ${formatNumber(stats.messageCount)}`);
2220
2294
  console.log(` Embeddings: ${formatNumber(stats.embeddingCount)}`);
2221
- if (existsSync6(DB_PATH)) {
2295
+ if (existsSync7(DB_PATH)) {
2222
2296
  const dbStats = statSync2(DB_PATH);
2223
2297
  console.log(` DB Size: ${formatBytes(dbStats.size)}`);
2224
2298
  }
@@ -2245,8 +2319,8 @@ async function statsCommand() {
2245
2319
  // src/commands/watch.ts
2246
2320
  import chalk7 from "chalk";
2247
2321
  import chokidar from "chokidar";
2248
- import { statSync as statSync3, existsSync as existsSync7, readFileSync as readFileSync2, readdirSync as readdirSync2 } from "fs";
2249
- import { join as join4, dirname as dirname3, basename as basename5 } from "path";
2322
+ import { statSync as statSync3, existsSync as existsSync8, readFileSync as readFileSync2, readdirSync as readdirSync2 } from "fs";
2323
+ import { join as join4, dirname as dirname4, basename as basename6 } from "path";
2250
2324
  var spinnerFrames = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
2251
2325
  var Spinner2 = class {
2252
2326
  interval = null;
@@ -2380,7 +2454,7 @@ async function watchCommand(options) {
2380
2454
  }
2381
2455
  function findAllSessionFiles(dir) {
2382
2456
  const files = [];
2383
- if (!existsSync7(dir)) return files;
2457
+ if (!existsSync8(dir)) return files;
2384
2458
  function scanDir(currentDir, depth = 0) {
2385
2459
  if (depth > 10) return;
2386
2460
  try {
@@ -2421,8 +2495,8 @@ async function processSessionFile(filePath, embeddingsReady, embedThreshold, ver
2421
2495
  const stats = statSync3(filePath);
2422
2496
  const fileMtime = stats.mtime.toISOString();
2423
2497
  const existingSession = getSessionBySourceFile(filePath);
2424
- const sessionId_from_file = basename5(filePath, ".jsonl");
2425
- const agentMessages = loadSubagentMessages2(dirname3(filePath), sessionId_from_file);
2498
+ const sessionId_from_file = basename6(filePath, ".jsonl");
2499
+ const agentMessages = loadSubagentMessages2(dirname4(filePath), sessionId_from_file);
2426
2500
  const allMessages = [...messages, ...agentMessages];
2427
2501
  const contentLength = allMessages.reduce((sum, m) => sum + m.content.length, 0);
2428
2502
  const firstUserMsg = messages.find((m) => m.role === "user");
@@ -2491,6 +2565,10 @@ async function processSessionFile(filePath, embeddingsReady, embedThreshold, ver
2491
2565
  }
2492
2566
  }
2493
2567
  }
2568
+ const backedUp = backupSessionFile(filePath);
2569
+ if (verbose && backedUp) {
2570
+ console.log(chalk7.dim(` Backed up: ${basename6(filePath)}`));
2571
+ }
2494
2572
  return isNew;
2495
2573
  } catch (err) {
2496
2574
  if (verbose) console.log(chalk7.red(`Error processing ${filePath}:`), err);
@@ -2499,7 +2577,7 @@ async function processSessionFile(filePath, embeddingsReady, embedThreshold, ver
2499
2577
  }
2500
2578
  function loadSubagentMessages2(projectDirPath, parentSessionId) {
2501
2579
  const subagentsDir = join4(projectDirPath, parentSessionId, "subagents");
2502
- if (!existsSync7(subagentsDir)) return [];
2580
+ if (!existsSync8(subagentsDir)) return [];
2503
2581
  const messages = [];
2504
2582
  try {
2505
2583
  const agentFiles = readdirSync2(subagentsDir).filter((f) => f.endsWith(".jsonl"));
@@ -2513,9 +2591,9 @@ function loadSubagentMessages2(projectDirPath, parentSessionId) {
2513
2591
  return messages;
2514
2592
  }
2515
2593
  function getProjectPathFromIndex(filePath, sessionId) {
2516
- const projectDir = dirname3(filePath);
2594
+ const projectDir = dirname4(filePath);
2517
2595
  const indexPath = join4(projectDir, "sessions-index.json");
2518
- if (!existsSync7(indexPath)) return null;
2596
+ if (!existsSync8(indexPath)) return null;
2519
2597
  try {
2520
2598
  const content = readFileSync2(indexPath, "utf-8");
2521
2599
  const index = JSON.parse(content);
@@ -3125,13 +3203,13 @@ init_install();
3125
3203
  // src/commands/setup.ts
3126
3204
  import chalk10 from "chalk";
3127
3205
  import { execSync as execSync3 } from "child_process";
3128
- import { existsSync as existsSync9, readFileSync as readFileSync3, writeFileSync as writeFileSync2, mkdirSync as mkdirSync3, readdirSync as readdirSync3, statSync as statSync4 } from "fs";
3206
+ import { existsSync as existsSync10, readFileSync as readFileSync3, writeFileSync as writeFileSync2, mkdirSync as mkdirSync3, readdirSync as readdirSync3, statSync as statSync4 } from "fs";
3129
3207
  import { homedir as homedir3 } from "os";
3130
- import { join as join6, dirname as dirname5, basename as basename6 } from "path";
3208
+ import { join as join6, dirname as dirname6, basename as basename7 } from "path";
3131
3209
  import { fileURLToPath } from "url";
3132
3210
  import { createInterface } from "readline";
3133
3211
  var __filename = fileURLToPath(import.meta.url);
3134
- var __dirname = dirname5(__filename);
3212
+ var __dirname = dirname6(__filename);
3135
3213
  var CLAUDE_JSON_PATH = join6(homedir3(), ".claude.json");
3136
3214
  var CLAUDE_SETTINGS_PATH = join6(homedir3(), ".claude", "settings.json");
3137
3215
  var CMEM_DIR2 = join6(homedir3(), ".cmem");
@@ -3255,7 +3333,7 @@ function isGloballyInstalled() {
3255
3333
  function getCmemVersion() {
3256
3334
  try {
3257
3335
  const packagePath = join6(__dirname, "..", "package.json");
3258
- if (existsSync9(packagePath)) {
3336
+ if (existsSync10(packagePath)) {
3259
3337
  const pkg = JSON.parse(readFileSync3(packagePath, "utf-8"));
3260
3338
  return pkg.version || "0.1.0";
3261
3339
  }
@@ -3341,7 +3419,7 @@ async function setupCommand() {
3341
3419
  }
3342
3420
  }
3343
3421
  console.log("");
3344
- const daemonInstalled = existsSync9(
3422
+ const daemonInstalled = existsSync10(
3345
3423
  join6(homedir3(), "Library", "LaunchAgents", "com.cmem.watch.plist")
3346
3424
  );
3347
3425
  const isUpdating = installedVersion && installedVersion !== currentVersion;
@@ -3406,7 +3484,7 @@ async function setupCommand() {
3406
3484
  console.log(chalk10.yellow(" Initial session indexing"));
3407
3485
  console.log(chalk10.dim(" Scanning and indexing your existing Claude Code conversations\n"));
3408
3486
  await indexExistingSessions();
3409
- if (!existsSync9(CMEM_DIR2)) {
3487
+ if (!existsSync10(CMEM_DIR2)) {
3410
3488
  mkdirSync3(CMEM_DIR2, { recursive: true });
3411
3489
  }
3412
3490
  writeFileSync2(SETUP_MARKER, (/* @__PURE__ */ new Date()).toISOString());
@@ -3423,7 +3501,7 @@ async function setupCommand() {
3423
3501
  console.log(chalk10.dim(" get_session Retrieve full history"));
3424
3502
  console.log(chalk10.dim(" list_sessions Browse recent sessions"));
3425
3503
  if (process.platform === "darwin") {
3426
- const plistExists = existsSync9(join6(homedir3(), "Library", "LaunchAgents", "com.cmem.watch.plist"));
3504
+ const plistExists = existsSync10(join6(homedir3(), "Library", "LaunchAgents", "com.cmem.watch.plist"));
3427
3505
  if (plistExists) {
3428
3506
  console.log(chalk10.green("\n \u{1F680} Daemon is now syncing your sessions in the background!"));
3429
3507
  }
@@ -3431,7 +3509,7 @@ async function setupCommand() {
3431
3509
  console.log("");
3432
3510
  }
3433
3511
  function isMcpConfigured() {
3434
- if (!existsSync9(CLAUDE_JSON_PATH)) {
3512
+ if (!existsSync10(CLAUDE_JSON_PATH)) {
3435
3513
  return false;
3436
3514
  }
3437
3515
  try {
@@ -3444,7 +3522,7 @@ function isMcpConfigured() {
3444
3522
  function configureMcpServer() {
3445
3523
  try {
3446
3524
  let claudeJson = {};
3447
- if (existsSync9(CLAUDE_JSON_PATH)) {
3525
+ if (existsSync10(CLAUDE_JSON_PATH)) {
3448
3526
  try {
3449
3527
  claudeJson = JSON.parse(readFileSync3(CLAUDE_JSON_PATH, "utf-8"));
3450
3528
  } catch {
@@ -3460,12 +3538,12 @@ function configureMcpServer() {
3460
3538
  args: ["mcp"]
3461
3539
  };
3462
3540
  writeFileSync2(CLAUDE_JSON_PATH, JSON.stringify(claudeJson, null, 2) + "\n");
3463
- const claudeDir = dirname5(CLAUDE_SETTINGS_PATH);
3464
- if (!existsSync9(claudeDir)) {
3541
+ const claudeDir = dirname6(CLAUDE_SETTINGS_PATH);
3542
+ if (!existsSync10(claudeDir)) {
3465
3543
  mkdirSync3(claudeDir, { recursive: true });
3466
3544
  }
3467
3545
  let settings = {};
3468
- if (existsSync9(CLAUDE_SETTINGS_PATH)) {
3546
+ if (existsSync10(CLAUDE_SETTINGS_PATH)) {
3469
3547
  try {
3470
3548
  settings = JSON.parse(readFileSync3(CLAUDE_SETTINGS_PATH, "utf-8"));
3471
3549
  } catch {
@@ -3492,7 +3570,7 @@ function configureMcpServer() {
3492
3570
  }
3493
3571
  function shouldRunSetup() {
3494
3572
  const isNpx = isRunningViaNpx();
3495
- const setupComplete = existsSync9(SETUP_MARKER);
3573
+ const setupComplete = existsSync10(SETUP_MARKER);
3496
3574
  const isGlobal = isGloballyInstalled();
3497
3575
  if (isNpx) return true;
3498
3576
  if (!isGlobal && !setupComplete) return true;
@@ -3505,7 +3583,7 @@ function shouldRunSetup() {
3505
3583
  }
3506
3584
  function findAllSessionFiles2(dir) {
3507
3585
  const files = [];
3508
- if (!existsSync9(dir)) return files;
3586
+ if (!existsSync10(dir)) return files;
3509
3587
  function scanDir(currentDir, depth = 0) {
3510
3588
  if (depth > 10) return;
3511
3589
  try {
@@ -3537,7 +3615,7 @@ async function indexSessionFile(filePath, embeddingsReady) {
3537
3615
  const title = firstUserMsg ? generateTitle(firstUserMsg.content) : "Untitled Session";
3538
3616
  const summary = generateSummary(messages);
3539
3617
  const rawData = JSON.stringify({ filePath, messages, mtime: fileMtime });
3540
- const sessionId_from_file = basename6(filePath, ".jsonl");
3618
+ const sessionId_from_file = basename7(filePath, ".jsonl");
3541
3619
  const sessionId = createSession({
3542
3620
  title,
3543
3621
  summary,
@@ -3603,9 +3681,9 @@ var sessionToResume = null;
3603
3681
  function getVersion() {
3604
3682
  try {
3605
3683
  const __filename2 = fileURLToPath2(import.meta.url);
3606
- const __dirname2 = dirname6(__filename2);
3684
+ const __dirname2 = dirname7(__filename2);
3607
3685
  const packagePath = join7(__dirname2, "..", "package.json");
3608
- if (existsSync10(packagePath)) {
3686
+ if (existsSync11(packagePath)) {
3609
3687
  const pkg = JSON.parse(readFileSync4(packagePath, "utf-8"));
3610
3688
  return pkg.version || "0.1.0";
3611
3689
  }