chore: remove all better-sqlite3 references from codebase (#357)

* fix: export/import scripts now use API instead of direct DB access

Export script fix:
- Add format=json parameter to SearchManager for raw data output
- Add getSdkSessionsBySessionIds method to SessionStore
- Add POST /api/sdk-sessions/batch endpoint to DataRoutes
- Refactor export-memories.ts to use HTTP API

Import script fix:
- Add import methods to SessionStore with duplicate detection
- Add POST /api/import endpoint to DataRoutes
- Refactor import-memories.ts to use HTTP API

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix: update analyze-transformations-smart.js to use bun:sqlite

Replace better-sqlite3 import with bun:sqlite to align with v7.1.0 migration.

* chore: remove all better-sqlite3 references from codebase

- Updated scripts/analyze-transformations-smart.js to use bun:sqlite
- Merged PR #332: Refactored import/export scripts to use worker API instead of direct DB access
- Updated PM2-to-Bun migration documentation

All better-sqlite3 references have been removed from source code.
Documentation references remain as appropriate historical context.

* build: update plugin artifacts with merged changes

Include built artifacts from PR #332 merge and analyze-transformations-smart.js update.

---------

Co-authored-by: lee <loyalpartner@163.com>
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Alex Newman
2025-12-16 17:57:40 -05:00
committed by GitHub
parent 78cb5c38dc
commit db3794762f
20 changed files with 988 additions and 508 deletions

View File

@@ -3,6 +3,14 @@ title: "PM2 to Bun Migration"
description: "Complete technical documentation for the process management and database driver migration in v7.1.0"
---
<Note>
**Historical Migration Documentation**
This document describes the PM2 to Bun migration that occurred in v7.1.0 (December 2025). If you're installing claude-mem for the first time, this migration has already been completed and you can use the current Bun-based system documented in the main guides.
This documentation is preserved for users upgrading from versions older than v7.1.0.
</Note>
# PM2 to Bun Migration: Complete Technical Documentation
**Version**: 7.1.0

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

Binary file not shown.

View File

@@ -282,7 +282,7 @@ No results found for "{query}". Try:
The search service isn't available. Check if the worker is running:
```bash
pm2 list
npm run worker:status
```
If the worker is stopped, restart it:

View File

@@ -113,7 +113,7 @@ Many endpoints share these parameters:
## Error Handling
**Worker not running:**
Connection refused error. Response: "The search API isn't available. Check if worker is running: `pm2 list`"
Connection refused error. Response: "The search API isn't available. Check if worker is running: `npm run worker:status`"
**Invalid endpoint:**
```json

View File

@@ -93,7 +93,7 @@ curl -s "http://localhost:37777/api/context/recent?limit=3"
Response: "No recent sessions found for 'new-project'. This might be a new project."
**Worker not running:**
Connection refused error. Inform user to check if worker is running: `pm2 list`
Connection refused error. Inform user to check if worker is running: `npm run worker:status`
## Tips

View File

@@ -1,6 +1,6 @@
---
name: troubleshoot
description: Diagnose and fix claude-mem installation issues. Checks PM2 worker status, database integrity, service health, dependencies, and provides automated fixes for common problems.
description: Diagnose and fix claude-mem installation issues. Checks worker status, database integrity, service health, dependencies, and provides automated fixes for common problems.
---
# Claude-Mem Troubleshooting Skill
@@ -39,7 +39,7 @@ Choose the appropriate operation file for detailed instructions:
### Diagnostic Workflows
1. **[Full System Diagnostics](operations/diagnostics.md)** - Comprehensive step-by-step diagnostic workflow
2. **[Worker Diagnostics](operations/worker.md)** - PM2 worker-specific troubleshooting
2. **[Worker Diagnostics](operations/worker.md)** - Bun worker-specific troubleshooting
3. **[Database Diagnostics](operations/database.md)** - Database integrity and data checks
### Issue Resolution
@@ -54,9 +54,9 @@ Choose the appropriate operation file for detailed instructions:
**Fast automated fix (try this first):**
```bash
cd ~/.claude/plugins/marketplaces/thedotmack/ && \
pm2 delete claude-mem-worker 2>/dev/null; \
npm run worker:stop; \
npm install && \
node_modules/.bin/pm2 start ecosystem.config.cjs && \
npm run worker:start && \
sleep 3 && \
curl -s http://127.0.0.1:37777/health
```
@@ -79,7 +79,7 @@ When troubleshooting:
- **Worker port:** Default 37777 (configurable via `CLAUDE_MEM_WORKER_PORT`)
- **Database location:** `~/.claude-mem/claude-mem.db`
- **Plugin location:** `~/.claude/plugins/marketplaces/thedotmack/`
- **PM2 process name:** `claude-mem-worker`
- **Worker PID file:** `~/.claude-mem/worker.pid`
## Error Reporting

View File

@@ -8,9 +8,9 @@ One-command fix sequences for common claude-mem issues.
```bash
cd ~/.claude/plugins/marketplaces/thedotmack/ && \
pm2 delete claude-mem-worker 2>/dev/null; \
npm run worker:stop; \
npm install && \
node_modules/.bin/pm2 start ecosystem.config.cjs && \
npm run worker:start && \
sleep 3 && \
curl -s http://127.0.0.1:37777/health
```
@@ -20,22 +20,22 @@ curl -s http://127.0.0.1:37777/health
**What it does:**
1. Stops the worker (if running)
2. Ensures dependencies are installed
3. Starts worker with local PM2
3. Starts worker
4. Waits for startup
5. Verifies health
## Fix: Worker Not Running
**Use when:** PM2 shows worker as stopped or not listed
**Use when:** Worker status shows it's not running
```bash
cd ~/.claude/plugins/marketplaces/thedotmack/ && \
node_modules/.bin/pm2 start ecosystem.config.cjs && \
npm run worker:start && \
sleep 2 && \
pm2 status
npm run worker:status
```
**Expected output:** Worker shows as "online"
**Expected output:** Worker running with PID and health OK
## Fix: Dependencies Missing
@@ -44,9 +44,23 @@ pm2 status
```bash
cd ~/.claude/plugins/marketplaces/thedotmack/ && \
npm install && \
pm2 restart claude-mem-worker
npm run worker:restart
```
## Fix: Stale PID File
**Use when:** Worker reports running but health check fails
```bash
rm -f ~/.claude-mem/worker.pid && \
cd ~/.claude/plugins/marketplaces/thedotmack/ && \
npm run worker:start && \
sleep 2 && \
curl -s http://127.0.0.1:37777/health
```
**Expected output:** `{"status":"ok"}`
## Fix: Port Conflict
**Use when:** Error shows port already in use
@@ -54,8 +68,9 @@ pm2 restart claude-mem-worker
```bash
# Change to port 37778
mkdir -p ~/.claude-mem && \
echo '{"env":{"CLAUDE_MEM_WORKER_PORT":"37778"}}' > ~/.claude-mem/settings.json && \
pm2 restart claude-mem-worker && \
echo '{"CLAUDE_MEM_WORKER_PORT":"37778"}' > ~/.claude-mem/settings.json && \
cd ~/.claude/plugins/marketplaces/thedotmack/ && \
npm run worker:restart && \
sleep 2 && \
curl -s http://127.0.0.1:37778/health
```
@@ -70,14 +85,16 @@ curl -s http://127.0.0.1:37778/health
# Backup and test integrity
cp ~/.claude-mem/claude-mem.db ~/.claude-mem/claude-mem.db.backup && \
sqlite3 ~/.claude-mem/claude-mem.db "PRAGMA integrity_check;" && \
pm2 restart claude-mem-worker
cd ~/.claude/plugins/marketplaces/thedotmack/ && \
npm run worker:restart
```
**If integrity check fails, recreate database:**
```bash
# WARNING: This deletes all memory data
mv ~/.claude-mem/claude-mem.db ~/.claude-mem/claude-mem.db.old && \
pm2 restart claude-mem-worker
cd ~/.claude/plugins/marketplaces/thedotmack/ && \
npm run worker:restart
```
## Fix: Clean Reinstall
@@ -88,36 +105,49 @@ pm2 restart claude-mem-worker
# Backup data first
cp ~/.claude-mem/claude-mem.db ~/.claude-mem/claude-mem.db.backup 2>/dev/null
# Stop and remove worker
pm2 delete claude-mem-worker 2>/dev/null
# Stop worker
cd ~/.claude/plugins/marketplaces/thedotmack/
npm run worker:stop
# Clean PID file
rm -f ~/.claude-mem/worker.pid
# Reinstall dependencies
cd ~/.claude/plugins/marketplaces/thedotmack/ && \
rm -rf node_modules && \
npm install
# Start worker
node_modules/.bin/pm2 start ecosystem.config.cjs && \
npm run worker:start && \
sleep 3 && \
curl -s http://127.0.0.1:37777/health
```
## Fix: Clear PM2 Logs
## Fix: Clear Old Logs
**Use when:** Logs are too large, want fresh start
**Use when:** Want to start with fresh logs
```bash
pm2 flush claude-mem-worker && \
pm2 restart claude-mem-worker
# Archive old logs
tar -czf ~/.claude-mem/logs-archive-$(date +%Y-%m-%d).tar.gz ~/.claude-mem/logs/*.log 2>/dev/null
# Remove logs older than 7 days
find ~/.claude-mem/logs/ -name "worker-*.log" -mtime +7 -delete
# Restart worker for fresh log
cd ~/.claude/plugins/marketplaces/thedotmack/
npm run worker:restart
```
**Note:** Logs auto-rotate daily, manual cleanup rarely needed.
## Verification Commands
**After running any fix, verify with these:**
```bash
# Check worker status
pm2 status | grep claude-mem-worker
cd ~/.claude/plugins/marketplaces/thedotmack/
npm run worker:status
# Check health
curl -s http://127.0.0.1:37777/health
@@ -129,23 +159,48 @@ sqlite3 ~/.claude-mem/claude-mem.db "SELECT COUNT(*) FROM observations;"
curl -s http://127.0.0.1:37777/api/stats
# Check logs for errors
pm2 logs claude-mem-worker --lines 20 --nostream | grep -i error
grep -i "error" ~/.claude-mem/logs/worker-$(date +%Y-%m-%d).log | tail -20
```
**All checks should pass:**
- Worker status: "online"
- Health: `{"status":"ok"}`
- Worker status: Shows PID and "Health: OK"
- Health endpoint: `{"status":"ok"}`
- Database: Shows count (may be 0 if new)
- Stats: Returns JSON with counts
- Logs: No recent errors
## One-Line Complete Diagnostic
**Quick health check:**
```bash
cd ~/.claude/plugins/marketplaces/thedotmack/ && npm run worker:status && curl -s http://127.0.0.1:37777/health && echo " ✓ All systems OK"
```
## Troubleshooting the Fixes
**If automated fix fails:**
1. Run the diagnostic script from [diagnostics.md](diagnostics.md)
2. Check specific error in PM2 logs
2. Check specific error in worker logs:
```bash
tail -50 ~/.claude-mem/logs/worker-$(date +%Y-%m-%d).log
```
3. Try manual worker start to see detailed error:
```bash
cd ~/.claude/plugins/marketplaces/thedotmack/
node plugin/scripts/worker-service.cjs
bun plugin/scripts/worker-service.js
```
4. Use the bug report tool:
```bash
npm run bug-report
```
## Common Error Patterns and Fixes
| Error Pattern | Likely Cause | Quick Fix |
|---------------|--------------|-----------|
| `EADDRINUSE` | Port conflict | Change port in settings.json |
| `SQLITE_ERROR` | Database corruption | Run integrity check, recreate if needed |
| `ENOENT` | Missing files | Run `npm install` |
| `Module not found` | Dependency issue | Clean reinstall |
| Connection refused | Worker not running | `npm run worker:start` |
| Stale PID | Old PID file | Remove `~/.claude-mem/worker.pid` |

View File

@@ -17,7 +17,8 @@ Quick fixes for frequently encountered claude-mem problems.
**Fix:**
1. Verify worker is running:
```bash
pm2 jlist | grep claude-mem-worker
cd ~/.claude/plugins/marketplaces/thedotmack/
npm run worker:status
```
2. Check database has recent observations:
@@ -27,7 +28,8 @@ Quick fixes for frequently encountered claude-mem problems.
3. Restart worker and start new session:
```bash
pm2 restart claude-mem-worker
cd ~/.claude/plugins/marketplaces/thedotmack/
npm run worker:restart
```
4. Create a test observation: `/skill version-bump` then cancel
@@ -66,7 +68,7 @@ Quick fixes for frequently encountered claude-mem problems.
3. Verify worker is using correct database path in logs:
```bash
pm2 logs claude-mem-worker --lines 50 --nostream | grep "Database"
grep "Database" ~/.claude-mem/logs/worker-$(date +%Y-%m-%d).log
```
4. Test viewer connection manually:
@@ -109,34 +111,34 @@ Quick fixes for frequently encountered claude-mem problems.
## Issue: Worker Not Starting {#worker-not-starting}
**Symptoms:**
- PM2 shows worker as "stopped" or "errored"
- Worker status shows not running or error
- Health check fails
- Viewer not accessible
**Root cause:**
- Port already in use
- PM2 not installed or not in PATH
- Bun not installed
- Missing dependencies
**Fix:**
1. Try manual worker start to see error:
```bash
cd ~/.claude/plugins/marketplaces/thedotmack/
node plugin/scripts/worker-service.cjs
bun plugin/scripts/worker-service.js
# Should start server on port 37777 or show error
```
2. If port in use, change it:
```bash
mkdir -p ~/.claude-mem
echo '{"env":{"CLAUDE_MEM_WORKER_PORT":"37778"}}' > ~/.claude-mem/settings.json
echo '{"CLAUDE_MEM_WORKER_PORT":"37778"}' > ~/.claude-mem/settings.json
```
3. If dependencies missing:
```bash
cd ~/.claude/plugins/marketplaces/thedotmack/
npm install
pm2 start ecosystem.config.cjs
npm run worker:start
```
## Issue: Search Results Empty
@@ -170,7 +172,8 @@ Quick fixes for frequently encountered claude-mem problems.
4. If FTS5 out of sync, restart worker (triggers reindex):
```bash
pm2 restart claude-mem-worker
cd ~/.claude/plugins/marketplaces/thedotmack/
npm run worker:restart
```
## Issue: Port Conflicts
@@ -189,8 +192,9 @@ Quick fixes for frequently encountered claude-mem problems.
2. Either kill the conflicting process or change claude-mem port:
```bash
mkdir -p ~/.claude-mem
echo '{"env":{"CLAUDE_MEM_WORKER_PORT":"37778"}}' > ~/.claude-mem/settings.json
pm2 restart claude-mem-worker
echo '{"CLAUDE_MEM_WORKER_PORT":"37778"}' > ~/.claude-mem/settings.json
cd ~/.claude/plugins/marketplaces/thedotmack/
npm run worker:restart
```
## Issue: Database Corrupted
@@ -214,7 +218,8 @@ Quick fixes for frequently encountered claude-mem problems.
3. If repair fails, recreate (loses data):
```bash
rm ~/.claude-mem/claude-mem.db
pm2 restart claude-mem-worker
cd ~/.claude/plugins/marketplaces/thedotmack/
npm run worker:restart
# Worker will create new database
```

View File

@@ -172,7 +172,8 @@ SELECT
If FTS5 counts don't match, triggers may have failed. Restart worker to rebuild:
```bash
pm2 restart claude-mem-worker
cd ~/.claude/plugins/marketplaces/thedotmack/
npm run worker:restart
```
The worker will rebuild FTS5 indexes on startup if they're out of sync.
@@ -196,7 +197,7 @@ The worker will rebuild FTS5 indexes on startup if they're out of sync.
1. Create test observation (use any skill and cancel)
2. Check worker logs for errors:
```bash
pm2 logs claude-mem-worker --lines 50 --nostream
tail -50 ~/.claude-mem/logs/worker-$(date +%Y-%m-%d).log
```
3. Verify observation appears in database
@@ -228,9 +229,10 @@ ls -la ~/.claude-mem/claude-mem.db-wal
ls -la ~/.claude-mem/claude-mem.db-shm
# Remove lock files (only if worker is stopped!)
pm2 stop claude-mem-worker
cd ~/.claude/plugins/marketplaces/thedotmack/
npm run worker:stop
rm ~/.claude-mem/claude-mem.db-wal ~/.claude-mem/claude-mem.db-shm
pm2 start claude-mem-worker
npm run worker:start
```
### Issue: Database Growing Too Large
@@ -260,7 +262,8 @@ sqlite3 ~/.claude-mem/claude-mem.db "SELECT COUNT(*) FROM observations;"
3. Archive and start fresh:
```bash
mv ~/.claude-mem/claude-mem.db ~/.claude-mem/claude-mem.db.archive
pm2 restart claude-mem-worker
cd ~/.claude/plugins/marketplaces/thedotmack/
npm run worker:restart
```
## Database Recovery
@@ -275,9 +278,10 @@ cp ~/.claude-mem/claude-mem.db ~/.claude-mem/claude-mem.db.backup
### Restore from Backup
```bash
pm2 stop claude-mem-worker
cd ~/.claude/plugins/marketplaces/thedotmack/
npm run worker:stop
cp ~/.claude-mem/claude-mem.db.backup ~/.claude-mem/claude-mem.db
pm2 start claude-mem-worker
npm run worker:start
```
### Export Data
@@ -300,8 +304,10 @@ sqlite3 ~/.claude-mem/claude-mem.db -json "SELECT * FROM user_prompts;" > prompt
**WARNING: Data loss. Backup first!**
```bash
cd ~/.claude/plugins/marketplaces/thedotmack/
# Stop worker
pm2 stop claude-mem-worker
npm run worker:stop
# Backup current database
cp ~/.claude-mem/claude-mem.db ~/.claude-mem/claude-mem.db.old
@@ -310,7 +316,7 @@ cp ~/.claude-mem/claude-mem.db ~/.claude-mem/claude-mem.db.old
rm ~/.claude-mem/claude-mem.db
# Start worker (creates new database)
pm2 start claude-mem-worker
npm run worker:start
```
## Database Statistics

View File

@@ -6,29 +6,42 @@ Comprehensive step-by-step diagnostic workflow for claude-mem issues.
Run these checks systematically to identify the root cause:
### 1. Check PM2 Worker Status
### 1. Check Worker Status
First, verify if the worker service is running:
```bash
# Check if PM2 is available
which pm2 || echo "PM2 not found in PATH"
# Check worker status using npm script
cd ~/.claude/plugins/marketplaces/thedotmack/
npm run worker:status
# List PM2 processes
pm2 jlist 2>&1
# If pm2 is not found, try the local installation
~/.claude/plugins/marketplaces/thedotmack/node_modules/.bin/pm2 jlist 2>&1
# Or check health endpoint directly
curl -s http://127.0.0.1:37777/health
```
**Expected output:** JSON array with `claude-mem-worker` process showing `"status": "online"`
**Expected output from npm run worker:status:**
```
✓ Worker is running (PID: 12345)
Port: 37777
Uptime: 45m
Health: OK
```
**If worker not running or status is not "online":**
**Expected output from health endpoint:** `{"status":"ok"}`
**If worker not running:**
```bash
cd ~/.claude/plugins/marketplaces/thedotmack/
pm2 start ecosystem.config.cjs
# Or use local pm2:
node_modules/.bin/pm2 start ecosystem.config.cjs
npm run worker:start
```
**If health endpoint fails but worker reports running:**
Check for stale PID file:
```bash
cat ~/.claude-mem/worker.pid
ps -p $(cat ~/.claude-mem/worker.pid 2>/dev/null | grep -o '"pid":[0-9]*' | grep -o '[0-9]*') 2>/dev/null || echo "Stale PID - worker not actually running"
rm ~/.claude-mem/worker.pid
npm run worker:start
```
### 2. Check Worker Service Health
@@ -98,10 +111,12 @@ cd ~/.claude/plugins/marketplaces/thedotmack/
# Check for critical packages
ls node_modules/@anthropic-ai/claude-agent-sdk 2>&1 | head -1
ls node_modules/express 2>&1 | head -1
ls node_modules/pm2 2>&1 | head -1
# Check if Bun is available
bun --version 2>&1
```
**Expected:** All critical packages present
**Expected:** All critical packages present, Bun installed
**If dependencies missing:**
```bash
@@ -114,17 +129,26 @@ npm install
Review recent worker logs for errors:
```bash
# View last 50 lines of worker logs
pm2 logs claude-mem-worker --lines 50 --nostream
# Or use local pm2:
# View logs using npm script
cd ~/.claude/plugins/marketplaces/thedotmack/
node_modules/.bin/pm2 logs claude-mem-worker --lines 50 --nostream
npm run worker:logs
# View today's log file directly
cat ~/.claude-mem/logs/worker-$(date +%Y-%m-%d).log
# Last 50 lines
tail -50 ~/.claude-mem/logs/worker-$(date +%Y-%m-%d).log
# Check for specific errors
pm2 logs claude-mem-worker --lines 100 --nostream | grep -i "error\|exception\|failed"
grep -iE "error|exception|failed" ~/.claude-mem/logs/worker-$(date +%Y-%m-%d).log | tail -20
```
**Common error patterns to look for:**
- `SQLITE_ERROR` - Database issues
- `EADDRINUSE` - Port conflict
- `ENOENT` - Missing files
- `Module not found` - Dependency issues
### 6. Test Viewer UI
Check if the web viewer is accessible:
@@ -167,6 +191,8 @@ echo "=== Claude-Mem Troubleshooting Report ==="
echo ""
echo "1. Environment"
echo " OS: $(uname -s)"
echo " Node version: $(node --version 2>/dev/null || echo 'N/A')"
echo " Bun version: $(bun --version 2>/dev/null || echo 'N/A')"
echo ""
echo "2. Plugin Installation"
echo " Plugin directory exists: $([ -d ~/.claude/plugins/marketplaces/thedotmack ] && echo 'YES' || echo 'NO')"
@@ -179,20 +205,28 @@ echo " Observation count: $(sqlite3 ~/.claude-mem/claude-mem.db 'SELECT COUNT(
echo " Session count: $(sqlite3 ~/.claude-mem/claude-mem.db 'SELECT COUNT(*) FROM sessions;' 2>/dev/null || echo 'N/A')"
echo ""
echo "4. Worker Service"
PM2_PATH=$(which pm2 2>/dev/null || echo "~/.claude/plugins/marketplaces/thedotmack/node_modules/.bin/pm2")
echo " PM2 path: $PM2_PATH"
WORKER_STATUS=$($PM2_PATH jlist 2>/dev/null | grep -o '"name":"claude-mem-worker".*"status":"[^"]*"' | grep -o 'status":"[^"]*"' | cut -d'"' -f3 || echo 'not running')
echo " Worker status: $WORKER_STATUS"
echo " Worker PID file: $([ -f ~/.claude-mem/worker.pid ] && echo 'EXISTS' || echo 'MISSING')"
if [ -f ~/.claude-mem/worker.pid ]; then
WORKER_PID=$(cat ~/.claude-mem/worker.pid 2>/dev/null | grep -o '"pid":[0-9]*' | grep -o '[0-9]*')
echo " Worker PID: $WORKER_PID"
echo " Process running: $(ps -p $WORKER_PID >/dev/null 2>&1 && echo 'YES' || echo 'NO (stale PID)')"
fi
echo " Health check: $(curl -s http://127.0.0.1:37777/health 2>/dev/null || echo 'FAILED')"
echo ""
echo "5. Configuration"
echo " Port setting: $(cat ~/.claude-mem/settings.json 2>/dev/null | grep CLAUDE_MEM_WORKER_PORT || echo 'default (37777)')"
echo " Observation count: $(cat ~/.claude-mem/settings.json 2>/dev/null | grep CLAUDE_MEM_CONTEXT_OBSERVATIONS || echo 'default (50)')"
echo " Model: $(cat ~/.claude-mem/settings.json 2>/dev/null | grep CLAUDE_MEM_MODEL || echo 'default (claude-sonnet-4-5)')"
echo ""
echo "6. Recent Activity"
echo " Latest observation: $(sqlite3 ~/.claude-mem/claude-mem.db 'SELECT created_at FROM observations ORDER BY created_at DESC LIMIT 1;' 2>/dev/null || echo 'N/A')"
echo " Latest session: $(sqlite3 ~/.claude-mem/claude-mem.db 'SELECT created_at FROM sessions ORDER BY created_at DESC LIMIT 1;' 2>/dev/null || echo 'N/A')"
echo ""
echo "7. Logs"
echo " Today's log file: $([ -f ~/.claude-mem/logs/worker-$(date +%Y-%m-%d).log ] && echo 'EXISTS' || echo 'MISSING')"
echo " Log file size: $(du -h ~/.claude-mem/logs/worker-$(date +%Y-%m-%d).log 2>/dev/null | cut -f1 || echo 'N/A')"
echo " Recent errors: $(grep -c -i "error" ~/.claude-mem/logs/worker-$(date +%Y-%m-%d).log 2>/dev/null || echo '0')"
echo ""
echo "=== End Report ==="
```
@@ -201,18 +235,75 @@ Save this as `/tmp/claude-mem-diagnostics.sh` and run:
bash /tmp/claude-mem-diagnostics.sh
```
## Quick Diagnostic One-Liners
```bash
# Full status check
npm run worker:status && curl -s http://127.0.0.1:37777/health && echo " - All systems OK"
# Database stats
echo "DB: $(du -h ~/.claude-mem/claude-mem.db | cut -f1) | Obs: $(sqlite3 ~/.claude-mem/claude-mem.db 'SELECT COUNT(*) FROM observations;' 2>/dev/null) | Sessions: $(sqlite3 ~/.claude-mem/claude-mem.db 'SELECT COUNT(*) FROM sessions;' 2>/dev/null)"
# Recent errors
grep -i "error" ~/.claude-mem/logs/worker-$(date +%Y-%m-%d).log 2>/dev/null | tail -5 || echo "No recent errors"
# Port check
lsof -i :37777 || echo "Port 37777 is free"
# Worker process check
ps aux | grep -E "bun.*worker-service" | grep -v grep || echo "Worker not running"
```
## Automated Fix Sequence
If diagnostics show issues, run this automated fix sequence:
```bash
#!/bin/bash
echo "Running automated fix sequence..."
# 1. Stop worker if running
echo "1. Stopping worker..."
cd ~/.claude/plugins/marketplaces/thedotmack/
npm run worker:stop
# 2. Clean stale PID if exists
echo "2. Cleaning stale PID file..."
rm -f ~/.claude-mem/worker.pid
# 3. Reinstall dependencies
echo "3. Reinstalling dependencies..."
npm install
# 4. Start worker
echo "4. Starting worker..."
npm run worker:start
# 5. Wait for startup
echo "5. Waiting for worker to start..."
sleep 3
# 6. Verify health
echo "6. Verifying health..."
curl -s http://127.0.0.1:37777/health || echo "Worker health check FAILED"
echo "Fix sequence complete!"
```
## Reporting Issues
If troubleshooting doesn't resolve the issue, collect this information for a bug report:
If troubleshooting doesn't resolve the issue, run the built-in bug report tool:
1. Full diagnostic report (run script above)
2. Worker logs: `pm2 logs claude-mem-worker --lines 100 --nostream`
3. Your setup:
- Claude version: Check with Claude
- OS: `uname -a`
- Node version: `node --version`
- Plugin version: In package.json
4. Steps to reproduce the issue
5. Expected vs actual behavior
```bash
cd ~/.claude/plugins/marketplaces/thedotmack/
npm run bug-report
```
Post to: https://github.com/thedotmack/claude-mem/issues
This will collect:
1. Full diagnostic report
2. Worker logs
3. System information
4. Configuration details
5. Database stats
Post the generated report to: https://github.com/thedotmack/claude-mem/issues

View File

@@ -6,30 +6,29 @@ Essential commands for troubleshooting claude-mem.
```bash
# Check worker status
pm2 status | grep claude-mem-worker
pm2 jlist | grep claude-mem-worker # JSON format
cd ~/.claude/plugins/marketplaces/thedotmack/
npm run worker:status
# Start worker
cd ~/.claude/plugins/marketplaces/thedotmack/
pm2 start ecosystem.config.cjs
npm run worker:start
# Restart worker
pm2 restart claude-mem-worker
npm run worker:restart
# Stop worker
pm2 stop claude-mem-worker
# Delete worker (for clean restart)
pm2 delete claude-mem-worker
npm run worker:stop
# View logs
pm2 logs claude-mem-worker
npm run worker:logs
# View last N lines
pm2 logs claude-mem-worker --lines 50 --nostream
# View today's log file
cat ~/.claude-mem/logs/worker-$(date +%Y-%m-%d).log
# Clear logs
pm2 flush claude-mem-worker
# Last 50 lines
tail -50 ~/.claude-mem/logs/worker-$(date +%Y-%m-%d).log
# Follow logs in real-time
tail -f ~/.claude-mem/logs/worker-$(date +%Y-%m-%d).log
```
## Health Checks
@@ -82,21 +81,17 @@ cat ~/.claude-mem/settings.json
cat ~/.claude/settings.json
# Change worker port
echo '{"env":{"CLAUDE_MEM_WORKER_PORT":"37778"}}' > ~/.claude-mem/settings.json
echo '{"CLAUDE_MEM_WORKER_PORT":"37778"}' > ~/.claude-mem/settings.json
# Change context observation count
# Edit ~/.claude-mem/settings.json and add:
{
"env": {
"CLAUDE_MEM_CONTEXT_OBSERVATIONS": "25"
}
}
# Change AI model
{
"env": {
"CLAUDE_MEM_MODEL": "claude-sonnet-4-5"
}
}
```
@@ -132,16 +127,19 @@ curl -v http://127.0.0.1:37777/health
```bash
# Search logs for errors
pm2 logs claude-mem-worker --lines 100 --nostream | grep -i "error"
grep -i "error" ~/.claude-mem/logs/worker-$(date +%Y-%m-%d).log
# Search for specific keyword
pm2 logs claude-mem-worker --lines 100 --nostream | grep "keyword"
grep "keyword" ~/.claude-mem/logs/worker-$(date +%Y-%m-%d).log
# Search across all log files
grep -i "error" ~/.claude-mem/logs/worker-*.log
# Last 100 error lines
grep -i "error" ~/.claude-mem/logs/worker-$(date +%Y-%m-%d).log | tail -100
# Follow logs in real-time
pm2 logs claude-mem-worker
# Show only error logs
pm2 logs claude-mem-worker --err
tail -f ~/.claude-mem/logs/worker-$(date +%Y-%m-%d).log
```
## File Locations
@@ -160,11 +158,11 @@ pm2 logs claude-mem-worker --err
# Chroma vector database
~/.claude-mem/chroma/
# Usage logs
~/.claude-mem/usage-logs/
# Worker logs (daily rotation)
~/.claude-mem/logs/worker-*.log
# PM2 logs
~/.pm2/logs/
# Worker PID file
~/.claude-mem/worker.pid
```
## System Information
@@ -179,8 +177,8 @@ node --version
# NPM version
npm --version
# PM2 version
pm2 --version
# Bun version
bun --version
# SQLite version
sqlite3 --version
@@ -188,3 +186,22 @@ sqlite3 --version
# Check disk space
df -h ~/.claude-mem/
```
## One-Line Diagnostics
```bash
# Full worker status check
npm run worker:status && curl -s http://127.0.0.1:37777/health
# Quick health check
curl -s http://127.0.0.1:37777/health && echo " - Worker is healthy"
# Database stats
echo "Observations: $(sqlite3 ~/.claude-mem/claude-mem.db 'SELECT COUNT(*) FROM observations;')" && echo "Sessions: $(sqlite3 ~/.claude-mem/claude-mem.db 'SELECT COUNT(*) FROM sessions;')"
# Recent errors
grep -i "error" ~/.claude-mem/logs/worker-$(date +%Y-%m-%d).log | tail -10
# Port check
lsof -i :37777 || echo "Port 37777 is free"
```

View File

@@ -1,10 +1,10 @@
# Worker Service Diagnostics
PM2 worker-specific troubleshooting for claude-mem.
Bun worker-specific troubleshooting for claude-mem.
## PM2 Worker Overview
## Worker Overview
The claude-mem worker is a persistent background service managed by PM2. It:
The claude-mem worker is a persistent background service managed by Bun. It:
- Runs Express.js server on port 37777 (default)
- Processes observations asynchronously
- Serves the viewer UI
@@ -15,36 +15,41 @@ The claude-mem worker is a persistent background service managed by PM2. It:
### Basic Status Check
```bash
# List all PM2 processes
pm2 list
# Check worker status using npm script
cd ~/.claude/plugins/marketplaces/thedotmack/
npm run worker:status
# JSON format (parseable)
pm2 jlist
# Filter for claude-mem-worker
pm2 status | grep claude-mem-worker
# Or check health endpoint directly
curl -s http://127.0.0.1:37777/health
```
**Expected output:**
**Expected npm run worker:status output:**
```
│ claude-mem-worker │ online │ 12345 │ 0 │ 45m │ 0% │ 85.6mb │
✓ Worker is running (PID: 12345)
Port: 37777
Uptime: 45m
Health: OK
```
**Status meanings:**
- `online` - Worker running correctly
- `stopped` - Worker stopped (normal shutdown)
- `errored` - Worker crashed (check logs)
- `stopping` - Worker shutting down
- Not listed - Worker never started
**Expected health endpoint output:**
```json
{"status":"ok"}
```
**Status indicators:**
- `Worker is running` - Worker running correctly
- `Worker is not running` - Worker stopped or crashed
- Connection refused - Worker not running
- Timeout - Worker hung (restart needed)
### Detailed Worker Info
```bash
# Show detailed information
pm2 show claude-mem-worker
# View PID file
cat ~/.claude-mem/worker.pid
# JSON format
pm2 jlist | grep -A 20 '"name":"claude-mem-worker"'
# Check process details
ps aux | grep "bun.*worker-service"
```
## Worker Health Endpoint
@@ -72,30 +77,37 @@ curl -s http://127.0.0.1:$PORT/health
### View Recent Logs
```bash
# Last 50 lines
pm2 logs claude-mem-worker --lines 50 --nostream
# View logs using npm script
cd ~/.claude/plugins/marketplaces/thedotmack/
npm run worker:logs
# Last 200 lines
pm2 logs claude-mem-worker --lines 200 --nostream
# View today's log file directly
cat ~/.claude-mem/logs/worker-$(date +%Y-%m-%d).log
# Last 50 lines of today's log
tail -50 ~/.claude-mem/logs/worker-$(date +%Y-%m-%d).log
# Follow logs in real-time
pm2 logs claude-mem-worker
tail -f ~/.claude-mem/logs/worker-$(date +%Y-%m-%d).log
```
### Search Logs for Errors
```bash
# Find errors
pm2 logs claude-mem-worker --lines 500 --nostream | grep -i "error"
# Find errors in today's log
grep -i "error" ~/.claude-mem/logs/worker-$(date +%Y-%m-%d).log
# Find exceptions
pm2 logs claude-mem-worker --lines 500 --nostream | grep -i "exception"
grep -i "exception" ~/.claude-mem/logs/worker-$(date +%Y-%m-%d).log
# Find failed requests
pm2 logs claude-mem-worker --lines 500 --nostream | grep -i "failed"
grep -i "failed" ~/.claude-mem/logs/worker-$(date +%Y-%m-%d).log
# All error patterns
pm2 logs claude-mem-worker --lines 500 --nostream | grep -iE "error|exception|failed|crash"
grep -iE "error|exception|failed|crash" ~/.claude-mem/logs/worker-$(date +%Y-%m-%d).log
# Search across all log files
grep -iE "error|exception|failed|crash" ~/.claude-mem/logs/worker-*.log
```
### Common Log Patterns
@@ -122,8 +134,8 @@ Port 37777 already in use
**Crashes:**
```
PM2 | App [claude-mem-worker] exited with code [1]
PM2 | App [claude-mem-worker] will restart in 100ms
Worker process exited with code 1
Worker restarting...
```
## Starting the Worker
@@ -132,37 +144,26 @@ PM2 | App [claude-mem-worker] will restart in 100ms
```bash
cd ~/.claude/plugins/marketplaces/thedotmack/
pm2 start ecosystem.config.cjs
```
### Start with Local PM2
If `pm2` command not in PATH:
```bash
cd ~/.claude/plugins/marketplaces/thedotmack/
node_modules/.bin/pm2 start ecosystem.config.cjs
npm run worker:start
```
### Force Restart
```bash
# Restart if already running
pm2 restart claude-mem-worker
# Restart worker (stops and starts)
cd ~/.claude/plugins/marketplaces/thedotmack/
npm run worker:restart
# Delete and start fresh
pm2 delete claude-mem-worker
pm2 start ecosystem.config.cjs
# Or manually stop and start
npm run worker:stop
npm run worker:start
```
## Stopping the Worker
```bash
# Graceful stop
pm2 stop claude-mem-worker
# Delete completely (also removes from PM2 list)
pm2 delete claude-mem-worker
cd ~/.claude/plugins/marketplaces/thedotmack/
npm run worker:stop
```
## Worker Not Starting
@@ -172,23 +173,22 @@ pm2 delete claude-mem-worker
1. **Try manual start to see error:**
```bash
cd ~/.claude/plugins/marketplaces/thedotmack/
node plugin/scripts/worker-service.cjs
bun plugin/scripts/worker-service.js
```
This runs the worker directly without PM2, showing full error output.
This runs the worker directly, showing full error output.
2. **Check PM2 itself:**
2. **Check Bun installation:**
```bash
which pm2
pm2 --version
which bun
bun --version
```
If PM2 not found, dependencies not installed.
If Bun not found, run: `npm install` (auto-installs Bun)
3. **Check dependencies:**
```bash
cd ~/.claude/plugins/marketplaces/thedotmack/
ls node_modules/@anthropic-ai/claude-agent-sdk
ls node_modules/express
ls node_modules/pm2
```
4. **Check port availability:**
@@ -197,42 +197,57 @@ pm2 delete claude-mem-worker
```
If port in use, either kill that process or change claude-mem port.
5. **Check PID file:**
```bash
cat ~/.claude-mem/worker.pid
```
If worker PID exists but process is dead, remove stale PID:
```bash
rm ~/.claude-mem/worker.pid
npm run worker:start
```
### Common Fixes
**Dependencies missing:**
```bash
cd ~/.claude/plugins/marketplaces/thedotmack/
npm install
pm2 start ecosystem.config.cjs
npm run worker:start
```
**Port conflict:**
```bash
echo '{"env":{"CLAUDE_MEM_WORKER_PORT":"37778"}}' > ~/.claude-mem/settings.json
pm2 restart claude-mem-worker
echo '{"CLAUDE_MEM_WORKER_PORT":"37778"}' > ~/.claude-mem/settings.json
npm run worker:restart
```
**Corrupted PM2:**
**Stale PID file:**
```bash
pm2 kill # Stop PM2 daemon
cd ~/.claude/plugins/marketplaces/thedotmack/
pm2 start ecosystem.config.cjs
rm ~/.claude-mem/worker.pid
npm run worker:start
```
## Worker Crashing Repeatedly
If worker keeps restarting (check with `pm2 status` showing high restart count):
If worker keeps restarting (check logs for repeated startup messages):
### Find the Cause
1. **Check error logs:**
```bash
pm2 logs claude-mem-worker --err --lines 100 --nostream
grep -i "error" ~/.claude-mem/logs/worker-$(date +%Y-%m-%d).log | tail -100
```
2. **Look for crash pattern:**
```bash
pm2 logs claude-mem-worker --lines 200 --nostream | grep -A 5 "exited with code"
grep -A 5 "exited with code" ~/.claude-mem/logs/worker-$(date +%Y-%m-%d).log
```
3. **Run worker in foreground to see crashes:**
```bash
cd ~/.claude/plugins/marketplaces/thedotmack/
bun plugin/scripts/worker-service.js
```
### Common Crash Causes
@@ -246,43 +261,71 @@ If fails, backup and recreate database.
**Out of memory:**
Check if database is too large or memory leak. Restart:
```bash
pm2 restart claude-mem-worker
npm run worker:restart
```
**Port conflict race condition:**
Another process grabbing port intermittently. Change port:
```bash
echo '{"env":{"CLAUDE_MEM_WORKER_PORT":"37778"}}' > ~/.claude-mem/settings.json
pm2 restart claude-mem-worker
echo '{"CLAUDE_MEM_WORKER_PORT":"37778"}' > ~/.claude-mem/settings.json
npm run worker:restart
```
## PM2 Management Commands
## Worker Management Commands
```bash
# List processes
pm2 list
pm2 jlist # JSON format
# Check status
npm run worker:status
# Show detailed info
pm2 show claude-mem-worker
# Start worker
npm run worker:start
# Monitor resources
pm2 monit
# Stop worker
npm run worker:stop
# Clear logs
pm2 flush claude-mem-worker
# Restart worker
npm run worker:restart
# Restart PM2 daemon
pm2 kill
pm2 resurrect # Restore saved processes
# View logs
npm run worker:logs
# Save current process list
pm2 save
# Check health endpoint
curl -s http://127.0.0.1:37777/health
# Update PM2
npm install -g pm2
# View PID
cat ~/.claude-mem/worker.pid
# View today's log file
cat ~/.claude-mem/logs/worker-$(date +%Y-%m-%d).log
# List all log files
ls -lh ~/.claude-mem/logs/worker-*.log
```
## Log File Management
Worker logs are stored in `~/.claude-mem/logs/` with daily rotation:
```bash
# View today's log
cat ~/.claude-mem/logs/worker-$(date +%Y-%m-%d).log
# View yesterday's log
cat ~/.claude-mem/logs/worker-$(date -d "yesterday" +%Y-%m-%d).log # Linux
cat ~/.claude-mem/logs/worker-$(date -v-1d +%Y-%m-%d).log # macOS
# List all logs
ls -lh ~/.claude-mem/logs/
# Clean old logs (older than 7 days)
find ~/.claude-mem/logs/ -name "worker-*.log" -mtime +7 -delete
# Archive logs
tar -czf ~/claude-mem-logs-backup-$(date +%Y-%m-%d).tar.gz ~/.claude-mem/logs/
```
**Note:** Logs auto-rotate daily. No manual flush required.
## Testing Worker Endpoints
Once worker is running, test all endpoints:
@@ -298,10 +341,22 @@ curl -s http://127.0.0.1:37777/ | head -20
curl -s http://127.0.0.1:37777/api/stats
# Search API
curl -s "http://127.0.0.1:37777/api/search/observations?q=test&format=index"
curl -s "http://127.0.0.1:37777/api/search?query=test&limit=5"
# Prompts API
curl -s "http://127.0.0.1:37777/api/prompts?limit=5"
# Recent context
curl -s "http://127.0.0.1:37777/api/context/recent?limit=3"
```
All should return appropriate responses (HTML for viewer, JSON for APIs).
## Troubleshooting Quick Reference
| Problem | Command | Expected Result |
|---------|---------|----------------|
| Check if running | `npm run worker:status` | Shows PID and uptime |
| Worker not running | `npm run worker:start` | Worker starts successfully |
| Worker crashed | `npm run worker:restart` | Worker restarts |
| View recent errors | `grep -i error ~/.claude-mem/logs/worker-$(date +%Y-%m-%d).log \| tail -20` | Shows recent errors |
| Port in use | `lsof -i :37777` | Shows process using port |
| Stale PID | `rm ~/.claude-mem/worker.pid && npm run worker:start` | Removes stale PID and starts |
| Dependencies missing | `npm install && npm run worker:start` | Installs deps and starts |

View File

@@ -1,7 +1,7 @@
#!/usr/bin/env node
import fs from 'fs';
import Database from 'better-sqlite3';
import { Database } from 'bun:sqlite';
import readline from 'readline';
import path from 'path';
import { homedir } from 'os';

View File

@@ -5,8 +5,7 @@
* Example: npx tsx scripts/export-memories.ts "windows" windows-memories.json --project=claude-mem
*/
import Database from 'better-sqlite3';
import { existsSync, writeFileSync } from 'fs';
import { writeFileSync } from 'fs';
import { homedir } from 'os';
import { join } from 'path';
import { SettingsDefaultsManager } from '../src/shared/SettingsDefaultsManager';
@@ -127,33 +126,19 @@ async function exportMemories(query: string, outputFile: string, project?: strin
if (s.sdk_session_id) sdkSessionIds.add(s.sdk_session_id);
});
// Get SDK sessions metadata from database
// (We need this because the API doesn't expose sdk_sessions table directly)
// Get SDK sessions metadata via API
console.log('📡 Fetching SDK sessions metadata...');
const sessions: SdkSessionRecord[] = [];
let sessions: SdkSessionRecord[] = [];
if (sdkSessionIds.size > 0) {
// Read directly from database for sdk_sessions table
const Database = (await import('better-sqlite3')).default;
const dbPath = join(homedir(), '.claude-mem', 'claude-mem.db');
if (!existsSync(dbPath)) {
console.error(`❌ Database not found at: ${dbPath}`);
console.error('💡 Has claude-mem been initialized? Try running a session first.');
process.exit(1);
}
const db = new Database(dbPath, { readonly: true });
try {
const placeholders = Array.from(sdkSessionIds).map(() => '?').join(',');
const sessionQuery = `
SELECT * FROM sdk_sessions
WHERE sdk_session_id IN (${placeholders})
ORDER BY started_at_epoch DESC
`;
sessions.push(...db.prepare(sessionQuery).all(...Array.from(sdkSessionIds)));
} finally {
db.close();
const sessionsResponse = await fetch(`${baseUrl}/api/sdk-sessions/batch`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ sdkSessionIds: Array.from(sdkSessionIds) })
});
if (sessionsResponse.ok) {
sessions = await sessionsResponse.json();
} else {
console.warn(`⚠️ Failed to fetch SDK sessions: ${sessionsResponse.status}`);
}
}
console.log(`✅ Found ${sessions.length} SDK sessions`);

View File

@@ -3,37 +3,21 @@
* Import memories from a JSON export file with duplicate prevention
* Usage: npx tsx scripts/import-memories.ts <input-file>
* Example: npx tsx scripts/import-memories.ts windows-memories.json
*
* This script uses the worker API instead of direct database access.
*/
import Database from 'better-sqlite3';
import { existsSync, readFileSync } from 'fs';
import { homedir } from 'os';
import { join } from 'path';
interface ImportStats {
sessionsImported: number;
sessionsSkipped: number;
summariesImported: number;
summariesSkipped: number;
observationsImported: number;
observationsSkipped: number;
promptsImported: number;
promptsSkipped: number;
}
const WORKER_PORT = process.env.CLAUDE_MEM_WORKER_PORT || 37777;
const WORKER_URL = `http://127.0.0.1:${WORKER_PORT}`;
function importMemories(inputFile: string) {
async function importMemories(inputFile: string) {
if (!existsSync(inputFile)) {
console.error(`❌ Input file not found: ${inputFile}`);
process.exit(1);
}
const dbPath = join(homedir(), '.claude-mem', 'claude-mem.db');
if (!existsSync(dbPath)) {
console.error(`❌ Database not found at: ${dbPath}`);
process.exit(1);
}
// Read and parse export file
const exportData = JSON.parse(readFileSync(inputFile, 'utf-8'));
@@ -47,179 +31,43 @@ function importMemories(inputFile: string) {
console.log(`${exportData.totalPrompts} prompts`);
console.log('');
const db = new Database(dbPath);
const stats: ImportStats = {
sessionsImported: 0,
sessionsSkipped: 0,
summariesImported: 0,
summariesSkipped: 0,
observationsImported: 0,
observationsSkipped: 0,
promptsImported: 0,
promptsSkipped: 0
};
// Check if worker is running
try {
// Prepare statements for duplicate checking
const checkSession = db.prepare('SELECT id FROM sdk_sessions WHERE claude_session_id = ?');
const checkSummary = db.prepare('SELECT id FROM session_summaries WHERE sdk_session_id = ?');
const checkObservation = db.prepare(`
SELECT id FROM observations
WHERE sdk_session_id = ?
AND title = ?
AND created_at_epoch = ?
`);
const checkPrompt = db.prepare(`
SELECT id FROM user_prompts
WHERE claude_session_id = ?
AND prompt_number = ?
`);
// Prepare insert statements
const insertSession = db.prepare(`
INSERT INTO sdk_sessions (
claude_session_id, sdk_session_id, project, user_prompt,
started_at, started_at_epoch, completed_at, completed_at_epoch,
status
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
`);
const insertSummary = db.prepare(`
INSERT INTO session_summaries (
sdk_session_id, project, request, investigated, learned,
completed, next_steps, files_read, files_edited, notes,
prompt_number, discovery_tokens, created_at, created_at_epoch
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
`);
const insertObservation = db.prepare(`
INSERT INTO observations (
sdk_session_id, project, text, type, title, subtitle,
facts, narrative, concepts, files_read, files_modified,
prompt_number, discovery_tokens, created_at, created_at_epoch
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
`);
const insertPrompt = db.prepare(`
INSERT INTO user_prompts (
claude_session_id, prompt_number, prompt_text,
created_at, created_at_epoch
) VALUES (?, ?, ?, ?, ?)
`);
// Import in transaction
db.transaction(() => {
// 1. Import sessions first (dependency for everything else)
console.log('🔄 Importing sessions...');
for (const session of exportData.sessions) {
const exists = checkSession.get(session.claude_session_id);
if (exists) {
stats.sessionsSkipped++;
continue;
const healthCheck = await fetch(`${WORKER_URL}/api/stats`);
if (!healthCheck.ok) {
throw new Error('Worker not responding');
}
} catch (error) {
console.error(`❌ Worker not running at ${WORKER_URL}`);
console.error(' Please ensure the claude-mem worker is running.');
process.exit(1);
}
insertSession.run(
session.claude_session_id,
session.sdk_session_id,
session.project,
session.user_prompt,
session.started_at,
session.started_at_epoch,
session.completed_at,
session.completed_at_epoch,
session.status
);
stats.sessionsImported++;
}
console.log(` ✅ Imported: ${stats.sessionsImported}, Skipped: ${stats.sessionsSkipped}`);
console.log('🔄 Importing via worker API...');
// 2. Import summaries (depends on sessions)
console.log('🔄 Importing summaries...');
for (const summary of exportData.summaries) {
const exists = checkSummary.get(summary.sdk_session_id);
if (exists) {
stats.summariesSkipped++;
continue;
// Send import request to worker
const response = await fetch(`${WORKER_URL}/api/import`, {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
sessions: exportData.sessions || [],
summaries: exportData.summaries || [],
observations: exportData.observations || [],
prompts: exportData.prompts || []
})
});
if (!response.ok) {
const errorText = await response.text();
console.error(`❌ Import failed: ${response.status} ${response.statusText}`);
console.error(` ${errorText}`);
process.exit(1);
}
insertSummary.run(
summary.sdk_session_id,
summary.project,
summary.request,
summary.investigated,
summary.learned,
summary.completed,
summary.next_steps,
summary.files_read,
summary.files_edited,
summary.notes,
summary.prompt_number,
summary.discovery_tokens || 0,
summary.created_at,
summary.created_at_epoch
);
stats.summariesImported++;
}
console.log(` ✅ Imported: ${stats.summariesImported}, Skipped: ${stats.summariesSkipped}`);
// 3. Import observations (depends on sessions)
console.log('🔄 Importing observations...');
for (const obs of exportData.observations) {
const exists = checkObservation.get(
obs.sdk_session_id,
obs.title,
obs.created_at_epoch
);
if (exists) {
stats.observationsSkipped++;
continue;
}
insertObservation.run(
obs.sdk_session_id,
obs.project,
obs.text,
obs.type,
obs.title,
obs.subtitle,
obs.facts,
obs.narrative,
obs.concepts,
obs.files_read,
obs.files_modified,
obs.prompt_number,
obs.discovery_tokens || 0,
obs.created_at,
obs.created_at_epoch
);
stats.observationsImported++;
}
console.log(` ✅ Imported: ${stats.observationsImported}, Skipped: ${stats.observationsSkipped}`);
// 4. Import prompts (depends on sessions)
console.log('🔄 Importing prompts...');
for (const prompt of exportData.prompts) {
const exists = checkPrompt.get(
prompt.claude_session_id,
prompt.prompt_number
);
if (exists) {
stats.promptsSkipped++;
continue;
}
insertPrompt.run(
prompt.claude_session_id,
prompt.prompt_number,
prompt.prompt_text,
prompt.created_at,
prompt.created_at_epoch
);
stats.promptsImported++;
}
console.log(` ✅ Imported: ${stats.promptsImported}, Skipped: ${stats.promptsSkipped}`);
})();
const result = await response.json();
const stats = result.stats;
console.log('\n✅ Import complete!');
console.log('📊 Summary:');
@@ -227,10 +75,6 @@ function importMemories(inputFile: string) {
console.log(` Summaries: ${stats.summariesImported} imported, ${stats.summariesSkipped} skipped`);
console.log(` Observations: ${stats.observationsImported} imported, ${stats.observationsSkipped} skipped`);
console.log(` Prompts: ${stats.promptsImported} imported, ${stats.promptsSkipped} skipped`);
} finally {
db.close();
}
}
// CLI interface

View File

@@ -1039,6 +1039,36 @@ export class SessionStore {
return stmt.get(id) || null;
}
/**
* Get SDK sessions by SDK session IDs
* Used for exporting session metadata
*/
getSdkSessionsBySessionIds(sdkSessionIds: string[]): {
id: number;
claude_session_id: string;
sdk_session_id: string;
project: string;
user_prompt: string;
started_at: string;
started_at_epoch: number;
completed_at: string | null;
completed_at_epoch: number | null;
status: string;
}[] {
if (sdkSessionIds.length === 0) return [];
const placeholders = sdkSessionIds.map(() => '?').join(',');
const stmt = this.db.prepare(`
SELECT id, claude_session_id, sdk_session_id, project, user_prompt,
started_at, started_at_epoch, completed_at, completed_at_epoch, status
FROM sdk_sessions
WHERE sdk_session_id IN (${placeholders})
ORDER BY started_at_epoch DESC
`);
return stmt.all(...sdkSessionIds) as any[];
}
/**
* Find active SDK session for a Claude session
*/
@@ -1795,4 +1825,212 @@ export class SessionStore {
close(): void {
this.db.close();
}
// ===========================================
// Import Methods (for import-memories script)
// ===========================================
/**
* Import SDK session with duplicate checking
* Returns: { imported: boolean, id: number }
*/
importSdkSession(session: {
claude_session_id: string;
sdk_session_id: string;
project: string;
user_prompt: string;
started_at: string;
started_at_epoch: number;
completed_at: string | null;
completed_at_epoch: number | null;
status: string;
}): { imported: boolean; id: number } {
// Check if session already exists
const existing = this.db.prepare(
'SELECT id FROM sdk_sessions WHERE claude_session_id = ?'
).get(session.claude_session_id) as { id: number } | undefined;
if (existing) {
return { imported: false, id: existing.id };
}
const stmt = this.db.prepare(`
INSERT INTO sdk_sessions (
claude_session_id, sdk_session_id, project, user_prompt,
started_at, started_at_epoch, completed_at, completed_at_epoch, status
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
`);
const result = stmt.run(
session.claude_session_id,
session.sdk_session_id,
session.project,
session.user_prompt,
session.started_at,
session.started_at_epoch,
session.completed_at,
session.completed_at_epoch,
session.status
);
return { imported: true, id: result.lastInsertRowid as number };
}
/**
* Import session summary with duplicate checking
* Returns: { imported: boolean, id: number }
*/
importSessionSummary(summary: {
sdk_session_id: string;
project: string;
request: string | null;
investigated: string | null;
learned: string | null;
completed: string | null;
next_steps: string | null;
files_read: string | null;
files_edited: string | null;
notes: string | null;
prompt_number: number | null;
discovery_tokens: number;
created_at: string;
created_at_epoch: number;
}): { imported: boolean; id: number } {
// Check if summary already exists for this session
const existing = this.db.prepare(
'SELECT id FROM session_summaries WHERE sdk_session_id = ?'
).get(summary.sdk_session_id) as { id: number } | undefined;
if (existing) {
return { imported: false, id: existing.id };
}
const stmt = this.db.prepare(`
INSERT INTO session_summaries (
sdk_session_id, project, request, investigated, learned,
completed, next_steps, files_read, files_edited, notes,
prompt_number, discovery_tokens, created_at, created_at_epoch
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
`);
const result = stmt.run(
summary.sdk_session_id,
summary.project,
summary.request,
summary.investigated,
summary.learned,
summary.completed,
summary.next_steps,
summary.files_read,
summary.files_edited,
summary.notes,
summary.prompt_number,
summary.discovery_tokens || 0,
summary.created_at,
summary.created_at_epoch
);
return { imported: true, id: result.lastInsertRowid as number };
}
/**
* Import observation with duplicate checking
* Duplicates are identified by sdk_session_id + title + created_at_epoch
* Returns: { imported: boolean, id: number }
*/
importObservation(obs: {
sdk_session_id: string;
project: string;
text: string | null;
type: string;
title: string | null;
subtitle: string | null;
facts: string | null;
narrative: string | null;
concepts: string | null;
files_read: string | null;
files_modified: string | null;
prompt_number: number | null;
discovery_tokens: number;
created_at: string;
created_at_epoch: number;
}): { imported: boolean; id: number } {
// Check if observation already exists
const existing = this.db.prepare(`
SELECT id FROM observations
WHERE sdk_session_id = ? AND title = ? AND created_at_epoch = ?
`).get(obs.sdk_session_id, obs.title, obs.created_at_epoch) as { id: number } | undefined;
if (existing) {
return { imported: false, id: existing.id };
}
const stmt = this.db.prepare(`
INSERT INTO observations (
sdk_session_id, project, text, type, title, subtitle,
facts, narrative, concepts, files_read, files_modified,
prompt_number, discovery_tokens, created_at, created_at_epoch
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
`);
const result = stmt.run(
obs.sdk_session_id,
obs.project,
obs.text,
obs.type,
obs.title,
obs.subtitle,
obs.facts,
obs.narrative,
obs.concepts,
obs.files_read,
obs.files_modified,
obs.prompt_number,
obs.discovery_tokens || 0,
obs.created_at,
obs.created_at_epoch
);
return { imported: true, id: result.lastInsertRowid as number };
}
/**
* Import user prompt with duplicate checking
* Duplicates are identified by claude_session_id + prompt_number
* Returns: { imported: boolean, id: number }
*/
importUserPrompt(prompt: {
claude_session_id: string;
prompt_number: number;
prompt_text: string;
created_at: string;
created_at_epoch: number;
}): { imported: boolean; id: number } {
// Check if prompt already exists
const existing = this.db.prepare(`
SELECT id FROM user_prompts
WHERE claude_session_id = ? AND prompt_number = ?
`).get(prompt.claude_session_id, prompt.prompt_number) as { id: number } | undefined;
if (existing) {
return { imported: false, id: existing.id };
}
const stmt = this.db.prepare(`
INSERT INTO user_prompts (
claude_session_id, prompt_number, prompt_text,
created_at, created_at_epoch
) VALUES (?, ?, ?, ?, ?)
`);
const result = stmt.run(
prompt.claude_session_id,
prompt.prompt_number,
prompt.prompt_text,
prompt.created_at,
prompt.created_at_epoch
);
return { imported: true, id: result.lastInsertRowid as number };
}
}

View File

@@ -87,7 +87,7 @@ export class SearchManager {
try {
// Normalize URL-friendly params to internal format
const normalized = this.normalizeParams(args);
const { query, type, obs_type, concepts, files, ...options } = normalized;
const { query, type, obs_type, concepts, files, format, ...options } = normalized;
let observations: ObservationSearchResult[] = [];
let sessions: SessionSummarySearchResult[] = [];
let prompts: UserPromptSearchResult[] = [];
@@ -200,6 +200,17 @@ export class SearchManager {
const totalResults = observations.length + sessions.length + prompts.length;
// JSON format: return raw data for programmatic access (e.g., export scripts)
if (format === 'json') {
return {
observations,
sessions,
prompts,
totalResults,
query: query || ''
};
}
if (totalResults === 0) {
return {
content: [{

View File

@@ -40,6 +40,7 @@ export class DataRoutes extends BaseRouteHandler {
app.get('/api/observation/:id', this.handleGetObservationById.bind(this));
app.post('/api/observations/batch', this.handleGetObservationsByIds.bind(this));
app.get('/api/session/:id', this.handleGetSessionById.bind(this));
app.post('/api/sdk-sessions/batch', this.handleGetSdkSessionsByIds.bind(this));
app.get('/api/prompt/:id', this.handleGetPromptById.bind(this));
// Metadata endpoints
@@ -49,6 +50,9 @@ export class DataRoutes extends BaseRouteHandler {
// Processing status endpoints
app.get('/api/processing-status', this.handleGetProcessingStatus.bind(this));
app.post('/api/processing', this.handleSetProcessing.bind(this));
// Import endpoint
app.post('/api/import', this.handleImport.bind(this));
}
/**
@@ -146,6 +150,24 @@ export class DataRoutes extends BaseRouteHandler {
res.json(sessions[0]);
});
/**
* Get SDK sessions by SDK session IDs
* POST /api/sdk-sessions/batch
* Body: { sdkSessionIds: string[] }
*/
private handleGetSdkSessionsByIds = this.wrapHandler((req: Request, res: Response): void => {
const { sdkSessionIds } = req.body;
if (!Array.isArray(sdkSessionIds)) {
this.badRequest(res, 'sdkSessionIds must be an array');
return;
}
const store = this.dbManager.getSessionStore();
const sessions = store.getSdkSessionsBySessionIds(sdkSessionIds);
res.json(sessions);
});
/**
* Get user prompt by ID
* GET /api/prompt/:id
@@ -267,4 +289,79 @@ export class DataRoutes extends BaseRouteHandler {
return { offset, limit, project };
}
/**
* Import memories from export file
* POST /api/import
* Body: { sessions: [], summaries: [], observations: [], prompts: [] }
*/
private handleImport = this.wrapHandler((req: Request, res: Response): void => {
const { sessions, summaries, observations, prompts } = req.body;
const stats = {
sessionsImported: 0,
sessionsSkipped: 0,
summariesImported: 0,
summariesSkipped: 0,
observationsImported: 0,
observationsSkipped: 0,
promptsImported: 0,
promptsSkipped: 0
};
const store = this.dbManager.getSessionStore();
// Import sessions first (dependency for everything else)
if (Array.isArray(sessions)) {
for (const session of sessions) {
const result = store.importSdkSession(session);
if (result.imported) {
stats.sessionsImported++;
} else {
stats.sessionsSkipped++;
}
}
}
// Import summaries (depends on sessions)
if (Array.isArray(summaries)) {
for (const summary of summaries) {
const result = store.importSessionSummary(summary);
if (result.imported) {
stats.summariesImported++;
} else {
stats.summariesSkipped++;
}
}
}
// Import observations (depends on sessions)
if (Array.isArray(observations)) {
for (const obs of observations) {
const result = store.importObservation(obs);
if (result.imported) {
stats.observationsImported++;
} else {
stats.observationsSkipped++;
}
}
}
// Import prompts (depends on sessions)
if (Array.isArray(prompts)) {
for (const prompt of prompts) {
const result = store.importUserPrompt(prompt);
if (result.imported) {
stats.promptsImported++;
} else {
stats.promptsSkipped++;
}
}
}
res.json({
success: true,
stats
});
});
}