Skip to main content
Historical Migration DocumentationThis 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.

PM2 to Bun Migration: Complete Technical Documentation

Version: 7.1.0 Date: December 2025 Migration Type: Process Management (PM2 → Bun) + Database Driver (better-sqlite3 → bun:sqlite)

Executive Summary

Claude-mem version 7.1.0 introduces two major architectural migrations:
  1. Process Management: PM2 → Custom Bun-based ProcessManager
  2. Database Driver: better-sqlite3 npm package → bun:sqlite runtime module
Both migrations are automatic and transparent to end users. The first time a hook fires after updating to 7.1.0+, the system performs a one-time cleanup of legacy PM2 processes and transitions to the new architecture.

Key Benefits

  • Simplified Dependencies: Removes PM2 and better-sqlite3 npm packages
  • Improved Cross-Platform Support: Better Windows compatibility
  • Faster Installation: No native module compilation required
  • Built-in Runtime: Leverages Bun’s built-in process management and SQLite
  • Reduced Complexity: Custom ProcessManager is simpler than PM2 integration

Migration Impact

  • Data Preservation: User data, settings, and database remain unchanged
  • Automatic Cleanup: Old PM2 processes automatically terminated (all platforms)
  • No User Action Required: Migration happens automatically on first hook trigger
  • Backward Compatible: SQLite database format unchanged (only driver changed)

Architecture Comparison

Old System (PM2-based)

Component: PM2 (Process Manager 2)
  • Package: pm2 npm dependency
  • Process Name: claude-mem-worker
  • Management: External PM2 daemon manages lifecycle
  • Discovery: pm2 list, pm2 describe commands
  • Auto-restart: PM2 automatically restarts on crash
  • Logs: ~/.pm2/logs/claude-mem-worker-*.log
  • PID File: ~/.pm2/pids/claude-mem-worker.pid
Lifecycle Commands:
pm2 start <script>           # Start worker
pm2 stop claude-mem-worker   # Stop worker
pm2 restart claude-mem-worker # Restart worker
pm2 delete claude-mem-worker  # Remove from PM2
pm2 logs claude-mem-worker    # View logs
Pain Points:
  • Additional npm dependency required
  • PM2 daemon must be running
  • Potential conflicts with other PM2 processes
  • Windows compatibility issues
  • Complex configuration for simple use case
Component: better-sqlite3
  • Package: better-sqlite3 npm package (native module)
  • Installation: Requires native compilation (node-gyp)
  • Windows: Requires Visual Studio build tools + Python
  • Import: import Database from 'better-sqlite3'
Installation Requirements:
  • Node.js development headers
  • C++ compiler (gcc/clang on Mac/Linux, MSVC on Windows)
  • Python (for node-gyp)
  • Windows: Visual Studio Build Tools

New System (Bun-based)

Component: Custom ProcessManager (src/services/process/ProcessManager.ts)
  • Package: Built-in Bun APIs (no external dependency)
  • Process Spawn: Bun.spawn() with detached mode
  • Management: Direct process control via PID file
  • Discovery: PID file + process existence check + HTTP health check
  • Auto-restart: Hook-triggered restart on failure detection
  • Logs: ~/.claude-mem/logs/worker-YYYY-MM-DD.log
  • PID File: ~/.claude-mem/.worker.pid
  • Port File: ~/.claude-mem/.worker.port (new)
Lifecycle Commands:
npm run worker:start    # Start worker
npm run worker:stop     # Stop worker
npm run worker:restart  # Restart worker
npm run worker:status   # Check status
npm run worker:logs     # View logs
Core Mechanisms:
  1. PID File Management:
    • File: ~/.claude-mem/.worker.pid
    • Content: Process ID (e.g., “35557”)
    • Validation: Process existence via kill(pid, 0) signal
  2. Port File Management:
    • File: ~/.claude-mem/.worker.port
    • Content: Two lines (port number, PID)
    • Purpose: Track port binding and validate PID match
  3. Health Checking:
    • Layer 1: PID file exists?
    • Layer 2: Process alive? (kill(pid, 0))
    • Layer 3: HTTP health check (GET /health)
    • All three must pass for “healthy” status
Advantages:
  • No external dependencies
  • Simpler codebase (direct control)
  • Better error handling and validation
  • Platform-agnostic (Bun handles platform differences)
Component: bun:sqlite
  • Package: Built into Bun runtime (no npm package)
  • Installation: None required (comes with Bun ≥1.0)
  • Platform: Works anywhere Bun works
  • Import: import { Database } from 'bun:sqlite'
  • API: Similar to better-sqlite3 (synchronous)
Installation Requirements:
  • Bun ≥1.0 (automatically installed if missing)
  • No native compilation required
  • No platform-specific build tools needed
Compatibility:
  • SQLite database format: Unchanged
  • Database file: ~/.claude-mem/claude-mem.db (same location)
  • Query syntax: Identical (both use SQLite SQL)

Migration Mechanics

One-Time PM2 Cleanup

The migration system uses a marker-based approach to perform PM2 cleanup exactly once. Implementation: src/shared/worker-utils.ts:73-86
// Clean up legacy PM2 (one-time migration)
const pm2MigratedMarker = join(DATA_DIR, '.pm2-migrated');

if (!existsSync(pm2MigratedMarker)) {
  try {
    spawnSync('pm2', ['delete', 'claude-mem-worker'], { stdio: 'ignore' });
    // Mark migration as complete
    writeFileSync(pm2MigratedMarker, new Date().toISOString(), 'utf-8');
    logger.debug('SYSTEM', 'PM2 cleanup completed and marked');
  } catch {
    // PM2 not installed or process doesn't exist - still mark as migrated
    writeFileSync(pm2MigratedMarker, new Date().toISOString(), 'utf-8');
  }
}

Migration Trigger Points

1

Hook Execution

SessionStart, UserPromptSubmit, or PostToolUse hooks execute using new 7.1.0 code
2

Worker Status Check

ensureWorkerRunning() checks if ~/.claude-mem/.worker.pid exists (it doesn’t for first run after update)
3

Start Worker Decision

Worker not running → Call startWorker()
4

Migration Check

Check if ~/.claude-mem/.pm2-migrated exists
5

PM2 Cleanup

Execute pm2 delete claude-mem-worker (errors ignored), create marker file
6

New Worker Start

Spawn new Bun-managed worker process with PID and port files

Marker File

Location: ~/.claude-mem/.pm2-migrated Content: ISO 8601 timestamp
2025-12-13T00:18:39.673Z
Purpose:
  • One-time migration flag
  • Prevents repeated PM2 cleanup on every start
  • Persists across restarts and reboots
Lifecycle:
  • Created: First hook trigger after update to 7.1.0+ (all platforms)
  • Updated: Never
  • Deleted: Never (user could manually delete to force re-migration)

User Experience Timeline

First Session After Update

This is the critical migration moment. The process takes approximately 2-5 seconds.
Step-by-Step Execution:
  1. Hook fires (SessionStart most common)
  2. Worker status check: No PID file → worker not running
  3. Migration check: No marker file → run PM2 cleanup
  4. PM2 cleanup: pm2 delete claude-mem-worker (old worker terminated)
  5. Marker creation: ~/.claude-mem/.pm2-migrated with timestamp
  6. New worker start: Bun process spawned, PID/port files created
  7. Verification: Process check + HTTP health check
  8. Hook completes: Claude Code session starts normally
User Observable Behavior:
  • Slight delay on first startup (PM2 cleanup + new worker spawn)
  • No error messages (cleanup failures silently handled)
  • Worker appears running via npm run worker:status
  • Old PM2 worker no longer in pm2 list

Subsequent Sessions

After migration completes, every hook trigger follows the fast path:
  1. PID file exists? YES
  2. Process alive? YES
  3. HTTP health check? SUCCESS
  4. Result: Worker already running, done (~50ms)
No migration logic runs on subsequent sessions.

Platform-Specific Behavior

Platform Comparison

FeaturemacOSLinuxWindows
PM2 CleanupAttemptedAttemptedAttempted
Marker FileCreatedCreatedCreated
Process SignalsPOSIX (native)POSIX (native)Bun abstraction
Bun SupportFullFullFull
PID FileYesYesYes
Port FileYesYesYes
Health CheckHTTPHTTPHTTP
Migration Delay~2-5s first time~2-5s first time~2-5s first time

Platform Notes

  • POSIX signal handling works natively
  • Bun fully supported
  • No platform-specific workarounds needed

Observable Changes

Command Changes

Old (PM2)New (Bun)Notes
pm2 listnpm run worker:statusShows worker status
pm2 start <script>npm run worker:startStart worker
pm2 stop claude-mem-workernpm run worker:stopStop worker
pm2 restart claude-mem-workernpm run worker:restartRestart worker
pm2 delete claude-mem-workernpm run worker:stopRemove worker
pm2 logs claude-mem-workernpm run worker:logsView logs
pm2 describe claude-mem-workernpm run worker:statusDetailed status
pm2 monitNo equivalentPM2-specific monitoring

File Location Changes

Logs:
Old: ~/.pm2/logs/claude-mem-worker-out.log
     ~/.pm2/logs/claude-mem-worker-error.log

New: ~/.claude-mem/logs/worker-YYYY-MM-DD.log
PID Files:
Old: ~/.pm2/pids/claude-mem-worker.pid

New: ~/.claude-mem/.worker.pid
Process State:
Old: PM2 daemon memory (pm2 save)

New: ~/.claude-mem/.worker.pid
     ~/.claude-mem/.worker.port
     ~/.claude-mem/.pm2-migrated (all platforms)
Database (unchanged):
Same: ~/.claude-mem/claude-mem.db

User-Visible Changes

Before Update:
$ pm2 list
┌────┬────────────────────┬─────────┬─────────┬──────────┐
 id name status restart uptime
├────┼────────────────────┼─────────┼─────────┼──────────┤
 0 claude-mem-worker online 0 2d 5h
└────┴────────────────────┴─────────┴─────────┴──────────┘
After Update:
$ pm2 list
# Empty - worker no longer managed by PM2

$ npm run worker:status
Worker is running
PID: 35557
Port: 37777
Uptime: 2h 15m

Orphaned Files

After migration, these PM2 files may remain (safe to delete):
~/.pm2/                    # Entire PM2 directory
~/.pm2/logs/               # Old logs
~/.pm2/pids/               # Old PID files
~/.pm2/pm2.log             # PM2 daemon log
~/.pm2/dump.pm2            # PM2 process dump
Cleanup (optional):
# Remove PM2 entirely (if not used for other processes)
pm2 kill
rm -rf ~/.pm2

# Or just remove claude-mem logs
rm -f ~/.pm2/logs/claude-mem-worker-*.log
rm -f ~/.pm2/pids/claude-mem-worker.pid

File System State

State Directory Structure

Before Migration (PM2 system):
~/.claude-mem/
├── claude-mem.db          # Database (unchanged)
├── chroma/                # Vector embeddings (unchanged)
├── logs/                  # Application logs (unchanged)
└── settings.json          # User settings (unchanged)

~/.pm2/
├── logs/
│   ├── claude-mem-worker-out.log
│   └── claude-mem-worker-error.log
├── pids/
│   └── claude-mem-worker.pid
└── pm2.log
After Migration (Bun system):
~/.claude-mem/
├── claude-mem.db          # Database (same file)
├── chroma/                # Vector embeddings (unchanged)
├── logs/
│   └── worker-2025-12-13.log  # New log format
├── settings.json          # User settings (unchanged)
├── .worker.pid            # NEW: Process ID
├── .worker.port           # NEW: Port + PID
└── .pm2-migrated          # NEW: Migration marker (all platforms)

~/.pm2/                    # Orphaned (safe to delete)
├── logs/                  # Old logs (no longer written)
├── pids/                  # Old PID (no longer updated)
└── pm2.log                # PM2 daemon log (not used)

Edge Cases and Troubleshooting

Scenario 1: Migration Fails (PM2 Still Running)

This is rare but can happen if PM2 has watch mode enabled or the process is manually restarted.
Symptoms:
  • pm2 list still shows claude-mem-worker
  • Port conflict errors in logs
  • Worker fails to start
Resolution:
# Manual cleanup
pm2 delete claude-mem-worker
pm2 save  # Persist the deletion

# Force re-migration (optional)
rm ~/.claude-mem/.pm2-migrated

# Restart worker
npm run worker:restart

Scenario 2: Stale PID File (Process Dead)

Symptoms:
  • npm run worker:status shows “not running”
  • .worker.pid file exists
  • Process ID doesn’t exist
Automatic Recovery: Next hook trigger detects dead process and starts a fresh worker. Manual Resolution:
rm ~/.claude-mem/.worker.pid
rm ~/.claude-mem/.worker.port
npm run worker:start

Scenario 3: Port Already in Use

Error: EADDRINUSE: address already in use Resolution:
# Check what's using the port
lsof -i :37777

# Kill the process
kill -9 <PID>

# Restart worker
npm run worker:restart

Common Error Messages

ErrorCauseResolution
EADDRINUSEPort already in uselsof -i :37777 then kill conflicting process
No such processStale PID fileAutomatic cleanup on next hook trigger
pm2: command not foundPM2 not installedNone needed (error is caught and ignored)
Invalid port XPort validation failedUpdate CLAUDE_MEM_WORKER_PORT in settings

Developer Notes

Testing the Migration

# 1. Install old version (with PM2)
git checkout <pre-7.1.0-tag>
npm install && npm run build && npm run sync-marketplace

# 2. Start PM2 worker
pm2 start plugin/scripts/worker-cli.js --name claude-mem-worker

# 3. Update to new version
git checkout main
npm install && npm run build && npm run sync-marketplace

# 4. Trigger hook
node plugin/scripts/session-start-hook.js

# 5. Verify migration
pm2 list  # Should NOT show claude-mem-worker
cat ~/.claude-mem/.pm2-migrated  # Should exist
npm run worker:status  # Should show Bun worker running

Architecture Decisions

Why Custom ProcessManager Instead of PM2?
  1. Simplicity: Direct control, no external daemon
  2. Dependencies: Remove npm dependency
  3. Cross-platform: Bun handles platform differences
  4. Bundle Size: Reduce plugin package size
  5. Control: Fine-grained error handling and validation
Why One-Time Marker Instead of Always Running PM2 Delete?
  1. Performance: Avoid unnecessary process spawning
  2. Idempotency: Migration runs exactly once
  3. Debugging: Timestamp shows when migration occurred
  4. Simplicity: Clear migration state
Why Run PM2 Cleanup on All Platforms?
  1. Quality Migration: Clean up orphaned processes
  2. Consistency: Same behavior across all platforms
  3. Safety: Error handling already in place (try/catch)
  4. No Downside: If PM2 not installed, error is caught and ignored

Summary

The migration from PM2 to Bun-based ProcessManager is a one-time, automatic, transparent transition that:
  1. Removes external dependencies (PM2, better-sqlite3)
  2. Simplifies architecture (direct process control)
  3. Improves cross-platform support (especially Windows)
  4. Preserves user data (database, settings, logs unchanged)
  5. Requires no user action (automatic on first hook trigger)
Key Migration Moment: First hook trigger after update to 7.1.0+ Duration: ~2-5 seconds (one-time delay) Impact: Seamless transition, user-invisible Rollback: Not needed (migration is forward-only, safe) For most users, the migration will be completely transparent - they’ll see no errors, no data loss, and experience improved reliability and simpler troubleshooting going forward.