mirror of
https://github.com/thedotmack/claude-mem
synced 2026-04-25 17:15:04 +02:00
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:
@@ -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.
@@ -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:
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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` |
|
||||
|
||||
@@ -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
|
||||
```
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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"
|
||||
```
|
||||
|
||||
@@ -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 |
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -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`);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 };
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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: [{
|
||||
|
||||
@@ -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
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user