@colbymchenry/cmem 0.2.33 → 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 +3 -0
- package/dist/cli.js +174 -94
- package/dist/cli.js.map +1 -1
- package/package.json +1 -1
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
|
|
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
|
|
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
|
|
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 (!
|
|
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 (!
|
|
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 (
|
|
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 (!
|
|
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 (!
|
|
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
|
|
199
|
-
import { join as join7, dirname as
|
|
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
|
|
209
|
-
import { existsSync as
|
|
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
|
-
|
|
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
|
|
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(
|
|
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(
|
|
1441
|
+
const sorted = smartSort(recoverable, favIds);
|
|
1382
1442
|
setAllSessions(sorted);
|
|
1383
1443
|
setSessions(sorted);
|
|
1384
1444
|
if (!projectFilter) {
|
|
@@ -1579,34 +1639,50 @@ var App = ({ onResume, projectFilter: initialProjectFilter }) => {
|
|
|
1579
1639
|
const session = currentSessions2[selectedIndex];
|
|
1580
1640
|
if (!session) return;
|
|
1581
1641
|
if (session.sourceFile) {
|
|
1582
|
-
|
|
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);
|
|
1583
1657
|
const claudeSessionId = filename.replace(".jsonl", "");
|
|
1584
|
-
|
|
1585
|
-
|
|
1586
|
-
|
|
1587
|
-
|
|
1588
|
-
|
|
1589
|
-
|
|
1590
|
-
|
|
1591
|
-
|
|
1592
|
-
|
|
1593
|
-
|
|
1594
|
-
|
|
1595
|
-
|
|
1596
|
-
|
|
1597
|
-
|
|
1598
|
-
|
|
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("-");
|
|
1599
1679
|
break;
|
|
1600
1680
|
}
|
|
1601
1681
|
}
|
|
1602
|
-
if (
|
|
1603
|
-
|
|
1604
|
-
break;
|
|
1682
|
+
if (currentPath && existsSync6(currentPath)) {
|
|
1683
|
+
projectPath = currentPath;
|
|
1605
1684
|
}
|
|
1606
1685
|
}
|
|
1607
|
-
if (currentPath && existsSync5(currentPath)) {
|
|
1608
|
-
projectPath = currentPath;
|
|
1609
|
-
}
|
|
1610
1686
|
}
|
|
1611
1687
|
if (onResume) {
|
|
1612
1688
|
onResume(claudeSessionId, projectPath);
|
|
@@ -1819,7 +1895,7 @@ var App = ({ onResume, projectFilter: initialProjectFilter }) => {
|
|
|
1819
1895
|
}
|
|
1820
1896
|
) : currentView === "project-sessions" && selectedProjectPath ? /* @__PURE__ */ jsxs6(Box6, { children: [
|
|
1821
1897
|
/* @__PURE__ */ jsx6(Text6, { dimColor: true, children: "Projects \u2192 " }),
|
|
1822
|
-
/* @__PURE__ */ jsx6(Text6, { color: "blue", children:
|
|
1898
|
+
/* @__PURE__ */ jsx6(Text6, { color: "blue", children: basename5(selectedProjectPath) })
|
|
1823
1899
|
] }) : /* @__PURE__ */ jsx6(Box6, { children: /* @__PURE__ */ jsx6(Text6, { dimColor: true, children: "Browse projects below" }) }),
|
|
1824
1900
|
currentView === "projects" ? /* @__PURE__ */ jsx6(
|
|
1825
1901
|
ProjectList,
|
|
@@ -2208,7 +2284,7 @@ async function deleteCommand(id) {
|
|
|
2208
2284
|
|
|
2209
2285
|
// src/commands/stats.ts
|
|
2210
2286
|
import chalk6 from "chalk";
|
|
2211
|
-
import { statSync as statSync2, existsSync as
|
|
2287
|
+
import { statSync as statSync2, existsSync as existsSync7 } from "fs";
|
|
2212
2288
|
async function statsCommand() {
|
|
2213
2289
|
console.log(chalk6.cyan("cmem Storage Statistics\n"));
|
|
2214
2290
|
const stats = getStats();
|
|
@@ -2216,7 +2292,7 @@ async function statsCommand() {
|
|
|
2216
2292
|
console.log(` Sessions: ${formatNumber(stats.sessionCount)}`);
|
|
2217
2293
|
console.log(` Messages: ${formatNumber(stats.messageCount)}`);
|
|
2218
2294
|
console.log(` Embeddings: ${formatNumber(stats.embeddingCount)}`);
|
|
2219
|
-
if (
|
|
2295
|
+
if (existsSync7(DB_PATH)) {
|
|
2220
2296
|
const dbStats = statSync2(DB_PATH);
|
|
2221
2297
|
console.log(` DB Size: ${formatBytes(dbStats.size)}`);
|
|
2222
2298
|
}
|
|
@@ -2243,8 +2319,8 @@ async function statsCommand() {
|
|
|
2243
2319
|
// src/commands/watch.ts
|
|
2244
2320
|
import chalk7 from "chalk";
|
|
2245
2321
|
import chokidar from "chokidar";
|
|
2246
|
-
import { statSync as statSync3, existsSync as
|
|
2247
|
-
import { join as join4, dirname as
|
|
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";
|
|
2248
2324
|
var spinnerFrames = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
|
|
2249
2325
|
var Spinner2 = class {
|
|
2250
2326
|
interval = null;
|
|
@@ -2378,7 +2454,7 @@ async function watchCommand(options) {
|
|
|
2378
2454
|
}
|
|
2379
2455
|
function findAllSessionFiles(dir) {
|
|
2380
2456
|
const files = [];
|
|
2381
|
-
if (!
|
|
2457
|
+
if (!existsSync8(dir)) return files;
|
|
2382
2458
|
function scanDir(currentDir, depth = 0) {
|
|
2383
2459
|
if (depth > 10) return;
|
|
2384
2460
|
try {
|
|
@@ -2419,8 +2495,8 @@ async function processSessionFile(filePath, embeddingsReady, embedThreshold, ver
|
|
|
2419
2495
|
const stats = statSync3(filePath);
|
|
2420
2496
|
const fileMtime = stats.mtime.toISOString();
|
|
2421
2497
|
const existingSession = getSessionBySourceFile(filePath);
|
|
2422
|
-
const sessionId_from_file =
|
|
2423
|
-
const agentMessages = loadSubagentMessages2(
|
|
2498
|
+
const sessionId_from_file = basename6(filePath, ".jsonl");
|
|
2499
|
+
const agentMessages = loadSubagentMessages2(dirname4(filePath), sessionId_from_file);
|
|
2424
2500
|
const allMessages = [...messages, ...agentMessages];
|
|
2425
2501
|
const contentLength = allMessages.reduce((sum, m) => sum + m.content.length, 0);
|
|
2426
2502
|
const firstUserMsg = messages.find((m) => m.role === "user");
|
|
@@ -2489,6 +2565,10 @@ async function processSessionFile(filePath, embeddingsReady, embedThreshold, ver
|
|
|
2489
2565
|
}
|
|
2490
2566
|
}
|
|
2491
2567
|
}
|
|
2568
|
+
const backedUp = backupSessionFile(filePath);
|
|
2569
|
+
if (verbose && backedUp) {
|
|
2570
|
+
console.log(chalk7.dim(` Backed up: ${basename6(filePath)}`));
|
|
2571
|
+
}
|
|
2492
2572
|
return isNew;
|
|
2493
2573
|
} catch (err) {
|
|
2494
2574
|
if (verbose) console.log(chalk7.red(`Error processing ${filePath}:`), err);
|
|
@@ -2497,7 +2577,7 @@ async function processSessionFile(filePath, embeddingsReady, embedThreshold, ver
|
|
|
2497
2577
|
}
|
|
2498
2578
|
function loadSubagentMessages2(projectDirPath, parentSessionId) {
|
|
2499
2579
|
const subagentsDir = join4(projectDirPath, parentSessionId, "subagents");
|
|
2500
|
-
if (!
|
|
2580
|
+
if (!existsSync8(subagentsDir)) return [];
|
|
2501
2581
|
const messages = [];
|
|
2502
2582
|
try {
|
|
2503
2583
|
const agentFiles = readdirSync2(subagentsDir).filter((f) => f.endsWith(".jsonl"));
|
|
@@ -2511,9 +2591,9 @@ function loadSubagentMessages2(projectDirPath, parentSessionId) {
|
|
|
2511
2591
|
return messages;
|
|
2512
2592
|
}
|
|
2513
2593
|
function getProjectPathFromIndex(filePath, sessionId) {
|
|
2514
|
-
const projectDir =
|
|
2594
|
+
const projectDir = dirname4(filePath);
|
|
2515
2595
|
const indexPath = join4(projectDir, "sessions-index.json");
|
|
2516
|
-
if (!
|
|
2596
|
+
if (!existsSync8(indexPath)) return null;
|
|
2517
2597
|
try {
|
|
2518
2598
|
const content = readFileSync2(indexPath, "utf-8");
|
|
2519
2599
|
const index = JSON.parse(content);
|
|
@@ -3123,13 +3203,13 @@ init_install();
|
|
|
3123
3203
|
// src/commands/setup.ts
|
|
3124
3204
|
import chalk10 from "chalk";
|
|
3125
3205
|
import { execSync as execSync3 } from "child_process";
|
|
3126
|
-
import { existsSync as
|
|
3206
|
+
import { existsSync as existsSync10, readFileSync as readFileSync3, writeFileSync as writeFileSync2, mkdirSync as mkdirSync3, readdirSync as readdirSync3, statSync as statSync4 } from "fs";
|
|
3127
3207
|
import { homedir as homedir3 } from "os";
|
|
3128
|
-
import { join as join6, dirname as
|
|
3208
|
+
import { join as join6, dirname as dirname6, basename as basename7 } from "path";
|
|
3129
3209
|
import { fileURLToPath } from "url";
|
|
3130
3210
|
import { createInterface } from "readline";
|
|
3131
3211
|
var __filename = fileURLToPath(import.meta.url);
|
|
3132
|
-
var __dirname =
|
|
3212
|
+
var __dirname = dirname6(__filename);
|
|
3133
3213
|
var CLAUDE_JSON_PATH = join6(homedir3(), ".claude.json");
|
|
3134
3214
|
var CLAUDE_SETTINGS_PATH = join6(homedir3(), ".claude", "settings.json");
|
|
3135
3215
|
var CMEM_DIR2 = join6(homedir3(), ".cmem");
|
|
@@ -3253,7 +3333,7 @@ function isGloballyInstalled() {
|
|
|
3253
3333
|
function getCmemVersion() {
|
|
3254
3334
|
try {
|
|
3255
3335
|
const packagePath = join6(__dirname, "..", "package.json");
|
|
3256
|
-
if (
|
|
3336
|
+
if (existsSync10(packagePath)) {
|
|
3257
3337
|
const pkg = JSON.parse(readFileSync3(packagePath, "utf-8"));
|
|
3258
3338
|
return pkg.version || "0.1.0";
|
|
3259
3339
|
}
|
|
@@ -3339,7 +3419,7 @@ async function setupCommand() {
|
|
|
3339
3419
|
}
|
|
3340
3420
|
}
|
|
3341
3421
|
console.log("");
|
|
3342
|
-
const daemonInstalled =
|
|
3422
|
+
const daemonInstalled = existsSync10(
|
|
3343
3423
|
join6(homedir3(), "Library", "LaunchAgents", "com.cmem.watch.plist")
|
|
3344
3424
|
);
|
|
3345
3425
|
const isUpdating = installedVersion && installedVersion !== currentVersion;
|
|
@@ -3404,7 +3484,7 @@ async function setupCommand() {
|
|
|
3404
3484
|
console.log(chalk10.yellow(" Initial session indexing"));
|
|
3405
3485
|
console.log(chalk10.dim(" Scanning and indexing your existing Claude Code conversations\n"));
|
|
3406
3486
|
await indexExistingSessions();
|
|
3407
|
-
if (!
|
|
3487
|
+
if (!existsSync10(CMEM_DIR2)) {
|
|
3408
3488
|
mkdirSync3(CMEM_DIR2, { recursive: true });
|
|
3409
3489
|
}
|
|
3410
3490
|
writeFileSync2(SETUP_MARKER, (/* @__PURE__ */ new Date()).toISOString());
|
|
@@ -3421,7 +3501,7 @@ async function setupCommand() {
|
|
|
3421
3501
|
console.log(chalk10.dim(" get_session Retrieve full history"));
|
|
3422
3502
|
console.log(chalk10.dim(" list_sessions Browse recent sessions"));
|
|
3423
3503
|
if (process.platform === "darwin") {
|
|
3424
|
-
const plistExists =
|
|
3504
|
+
const plistExists = existsSync10(join6(homedir3(), "Library", "LaunchAgents", "com.cmem.watch.plist"));
|
|
3425
3505
|
if (plistExists) {
|
|
3426
3506
|
console.log(chalk10.green("\n \u{1F680} Daemon is now syncing your sessions in the background!"));
|
|
3427
3507
|
}
|
|
@@ -3429,7 +3509,7 @@ async function setupCommand() {
|
|
|
3429
3509
|
console.log("");
|
|
3430
3510
|
}
|
|
3431
3511
|
function isMcpConfigured() {
|
|
3432
|
-
if (!
|
|
3512
|
+
if (!existsSync10(CLAUDE_JSON_PATH)) {
|
|
3433
3513
|
return false;
|
|
3434
3514
|
}
|
|
3435
3515
|
try {
|
|
@@ -3442,7 +3522,7 @@ function isMcpConfigured() {
|
|
|
3442
3522
|
function configureMcpServer() {
|
|
3443
3523
|
try {
|
|
3444
3524
|
let claudeJson = {};
|
|
3445
|
-
if (
|
|
3525
|
+
if (existsSync10(CLAUDE_JSON_PATH)) {
|
|
3446
3526
|
try {
|
|
3447
3527
|
claudeJson = JSON.parse(readFileSync3(CLAUDE_JSON_PATH, "utf-8"));
|
|
3448
3528
|
} catch {
|
|
@@ -3458,12 +3538,12 @@ function configureMcpServer() {
|
|
|
3458
3538
|
args: ["mcp"]
|
|
3459
3539
|
};
|
|
3460
3540
|
writeFileSync2(CLAUDE_JSON_PATH, JSON.stringify(claudeJson, null, 2) + "\n");
|
|
3461
|
-
const claudeDir =
|
|
3462
|
-
if (!
|
|
3541
|
+
const claudeDir = dirname6(CLAUDE_SETTINGS_PATH);
|
|
3542
|
+
if (!existsSync10(claudeDir)) {
|
|
3463
3543
|
mkdirSync3(claudeDir, { recursive: true });
|
|
3464
3544
|
}
|
|
3465
3545
|
let settings = {};
|
|
3466
|
-
if (
|
|
3546
|
+
if (existsSync10(CLAUDE_SETTINGS_PATH)) {
|
|
3467
3547
|
try {
|
|
3468
3548
|
settings = JSON.parse(readFileSync3(CLAUDE_SETTINGS_PATH, "utf-8"));
|
|
3469
3549
|
} catch {
|
|
@@ -3490,7 +3570,7 @@ function configureMcpServer() {
|
|
|
3490
3570
|
}
|
|
3491
3571
|
function shouldRunSetup() {
|
|
3492
3572
|
const isNpx = isRunningViaNpx();
|
|
3493
|
-
const setupComplete =
|
|
3573
|
+
const setupComplete = existsSync10(SETUP_MARKER);
|
|
3494
3574
|
const isGlobal = isGloballyInstalled();
|
|
3495
3575
|
if (isNpx) return true;
|
|
3496
3576
|
if (!isGlobal && !setupComplete) return true;
|
|
@@ -3503,7 +3583,7 @@ function shouldRunSetup() {
|
|
|
3503
3583
|
}
|
|
3504
3584
|
function findAllSessionFiles2(dir) {
|
|
3505
3585
|
const files = [];
|
|
3506
|
-
if (!
|
|
3586
|
+
if (!existsSync10(dir)) return files;
|
|
3507
3587
|
function scanDir(currentDir, depth = 0) {
|
|
3508
3588
|
if (depth > 10) return;
|
|
3509
3589
|
try {
|
|
@@ -3535,7 +3615,7 @@ async function indexSessionFile(filePath, embeddingsReady) {
|
|
|
3535
3615
|
const title = firstUserMsg ? generateTitle(firstUserMsg.content) : "Untitled Session";
|
|
3536
3616
|
const summary = generateSummary(messages);
|
|
3537
3617
|
const rawData = JSON.stringify({ filePath, messages, mtime: fileMtime });
|
|
3538
|
-
const sessionId_from_file =
|
|
3618
|
+
const sessionId_from_file = basename7(filePath, ".jsonl");
|
|
3539
3619
|
const sessionId = createSession({
|
|
3540
3620
|
title,
|
|
3541
3621
|
summary,
|
|
@@ -3601,9 +3681,9 @@ var sessionToResume = null;
|
|
|
3601
3681
|
function getVersion() {
|
|
3602
3682
|
try {
|
|
3603
3683
|
const __filename2 = fileURLToPath2(import.meta.url);
|
|
3604
|
-
const __dirname2 =
|
|
3684
|
+
const __dirname2 = dirname7(__filename2);
|
|
3605
3685
|
const packagePath = join7(__dirname2, "..", "package.json");
|
|
3606
|
-
if (
|
|
3686
|
+
if (existsSync11(packagePath)) {
|
|
3607
3687
|
const pkg = JSON.parse(readFileSync4(packagePath, "utf-8"));
|
|
3608
3688
|
return pkg.version || "0.1.0";
|
|
3609
3689
|
}
|