claude-code-memory-explorer 0.4.0 → 0.4.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.
Files changed (2) hide show
  1. package/package.json +1 -1
  2. package/server.js +25 -14
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-code-memory-explorer",
3
- "version": "0.4.0",
3
+ "version": "0.4.1",
4
4
  "description": "Memory explorer dashboard for Claude Code",
5
5
  "main": "server.js",
6
6
  "bin": {
package/server.js CHANGED
@@ -17,21 +17,19 @@ function getArg(name) {
17
17
  return process.argv[eqIdx + 1] || null;
18
18
  }
19
19
 
20
+ const expandHome = (p) => (p || '').replace(/^~/, os.homedir());
21
+
20
22
  const PORT = getArg('port') || process.env.PORT || 3459;
21
23
  const AUTO_OPEN = process.argv.includes('--open');
22
24
  const claudeDirArg = getArg('dir');
23
- const CLAUDE_DIR = claudeDirArg
24
- ? claudeDirArg.replace(/^~/, os.homedir())
25
- : path.join(os.homedir(), '.claude');
25
+ const CLAUDE_DIR = claudeDirArg ? expandHome(claudeDirArg) : path.join(os.homedir(), '.claude');
26
26
  const projectDirArg = getArg('project');
27
27
 
28
28
  // #endregion CLI_ARGS
29
29
 
30
30
  // #region STATE
31
31
 
32
- let currentProjectPath = projectDirArg
33
- ? path.resolve(projectDirArg.replace(/^~/, os.homedir()))
34
- : process.cwd();
32
+ let currentProjectPath = projectDirArg ? path.resolve(expandHome(projectDirArg)) : process.cwd();
35
33
 
36
34
  const cache = {};
37
35
  const CACHE_TTL = 30_000;
@@ -129,7 +127,7 @@ function parseImports(content) {
129
127
  function resolveImport(importPath, fromFile) {
130
128
  let resolved = importPath;
131
129
  if (resolved.startsWith('~')) {
132
- resolved = resolved.replace(/^~/, os.homedir());
130
+ resolved = expandHome(resolved);
133
131
  } else {
134
132
  resolved = path.resolve(path.dirname(fromFile), resolved);
135
133
  }
@@ -325,7 +323,7 @@ function discoverMemorySources(projectPath) {
325
323
  }
326
324
  }
327
325
 
328
- // 6. Auto memory
326
+ // 6. Auto memory (projects base honors `autoMemoryDirectory` user setting)
329
327
  const memoryDir = findMemoryDir(projectPath);
330
328
  if (memoryDir && fs.existsSync(memoryDir)) {
331
329
  pushMemoryDir(memoryDir, 'memory', 'memory');
@@ -428,7 +426,7 @@ function getAncestorDirs(projectPath) {
428
426
  }
429
427
 
430
428
  function findMemoryDir(projectPath) {
431
- const projectsDir = path.join(CLAUDE_DIR, 'projects');
429
+ const projectsDir = getProjectsBaseDir();
432
430
  if (!fs.existsSync(projectsDir)) return null;
433
431
  const encoded = encodeProjectPath(projectPath);
434
432
  const memDir = path.join(projectsDir, encoded, 'memory');
@@ -467,8 +465,17 @@ function encodeProjectPath(projectPath) {
467
465
  .replace(/\//g, '-');
468
466
  }
469
467
 
468
+ // User-scope `autoMemoryDirectory` only — Claude Code rejects this key from project/local settings for security.
469
+ function getProjectsBaseDir() {
470
+ let settings = {};
471
+ try { settings = JSON.parse(fs.readFileSync(path.join(CLAUDE_DIR, 'settings.json'), 'utf-8')); } catch { /* no settings */ }
472
+ const raw = settings.autoMemoryDirectory;
473
+ if (typeof raw === 'string' && raw.trim()) return path.resolve(expandHome(raw));
474
+ return path.join(CLAUDE_DIR, 'projects');
475
+ }
476
+
470
477
  function findMemoryDirBySubstring(projectPath) {
471
- const projectsDir = path.join(CLAUDE_DIR, 'projects');
478
+ const projectsDir = getProjectsBaseDir();
472
479
  if (!fs.existsSync(projectsDir)) return null;
473
480
  // Match by last 2-3 path segments
474
481
  const segments = projectPath.replace(/\\/g, '/').split('/').filter(Boolean);
@@ -525,7 +532,7 @@ app.get('/api/project', (_req, res) => {
525
532
  app.put('/api/project', (req, res) => {
526
533
  const { path: dirPath } = req.body;
527
534
  if (!dirPath) return res.status(400).json({ error: 'path required' });
528
- const resolved = path.resolve(dirPath.replace(/^~/, os.homedir()));
535
+ const resolved = path.resolve(expandHome(dirPath));
529
536
  if (!fs.existsSync(resolved)) return res.status(404).json({ error: 'directory not found' });
530
537
  if (resolved !== path.resolve(currentProjectPath)) {
531
538
  currentProjectPath = resolved;
@@ -541,8 +548,12 @@ app.post('/api/refresh', (_req, res) => {
541
548
 
542
549
  function resolveAllowedPath(filePath) {
543
550
  if (!filePath) return { error: { status: 400, message: 'path required' } };
544
- const resolved = path.resolve(filePath.replace(/^~/, os.homedir()));
545
- const roots = [path.resolve(CLAUDE_DIR), path.resolve(currentProjectPath)];
551
+ const resolved = path.resolve(expandHome(filePath));
552
+ const roots = [
553
+ path.resolve(CLAUDE_DIR),
554
+ path.resolve(currentProjectPath),
555
+ getProjectsBaseDir(),
556
+ ];
546
557
  const inRoot = roots.some((r) => resolved === r || resolved.startsWith(r + path.sep));
547
558
  if (!inRoot) return { error: { status: 403, message: 'path outside allowed roots' } };
548
559
  return { resolved };
@@ -597,7 +608,7 @@ app.post('/api/memory/cleanup-orphans', (req, res) => {
597
608
  app.post('/api/open-in-editor', (req, res) => {
598
609
  const { path: filePath } = req.body;
599
610
  if (!filePath) return res.status(400).json({ error: 'path required' });
600
- const resolved = filePath.replace(/^~/, os.homedir());
611
+ const resolved = expandHome(filePath);
601
612
  if (!fs.existsSync(resolved)) return res.status(404).json({ error: 'file not found' });
602
613
  const { exec } = require('child_process');
603
614
  exec(`code "${resolved}"`, (err) => {