> ## Documentation Index
> Fetch the complete documentation index at: https://docs.claude-mem.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# PM2 to Bun Migration

> 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
**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)

<AccordionGroup>
  <Accordion title="Process Management (PM2)">
    **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**:

    ```bash theme={null}
    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
  </Accordion>

  <Accordion title="Database Driver (better-sqlite3)">
    **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
  </Accordion>
</AccordionGroup>

### New System (Bun-based)

<AccordionGroup>
  <Accordion title="Process Management (Custom ProcessManager)">
    **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**:

    ```bash theme={null}
    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)
  </Accordion>

  <Accordion title="Database Driver (bun:sqlite)">
    **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)
  </Accordion>
</AccordionGroup>

## 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`

```typescript theme={null}
// 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

<Steps>
  <Step title="Hook Execution">
    SessionStart, UserPromptSubmit, or PostToolUse hooks execute using new 7.1.0 code
  </Step>

  <Step title="Worker Status Check">
    `ensureWorkerRunning()` checks if `~/.claude-mem/.worker.pid` exists (it doesn't for first run after update)
  </Step>

  <Step title="Start Worker Decision">
    Worker not running → Call `startWorker()`
  </Step>

  <Step title="Migration Check">
    Check if `~/.claude-mem/.pm2-migrated` exists
  </Step>

  <Step title="PM2 Cleanup">
    Execute `pm2 delete claude-mem-worker` (errors ignored), create marker file
  </Step>

  <Step title="New Worker Start">
    Spawn new Bun-managed worker process with PID and port files
  </Step>
</Steps>

### 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

<Note>
  This is the critical migration moment. The process takes approximately 2-5 seconds.
</Note>

**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

| Feature         | macOS             | Linux             | Windows           |
| --------------- | ----------------- | ----------------- | ----------------- |
| PM2 Cleanup     | Attempted         | Attempted         | Attempted         |
| Marker File     | Created           | Created           | Created           |
| Process Signals | POSIX (native)    | POSIX (native)    | Bun abstraction   |
| Bun Support     | Full              | Full              | Full              |
| PID File        | Yes               | Yes               | Yes               |
| Port File       | Yes               | Yes               | Yes               |
| Health Check    | HTTP              | HTTP              | HTTP              |
| Migration Delay | \~2-5s first time | \~2-5s first time | \~2-5s first time |

### Platform Notes

<Tabs>
  <Tab title="macOS">
    * POSIX signal handling works natively
    * Bun fully supported
    * No platform-specific workarounds needed
  </Tab>

  <Tab title="Linux">
    * Identical behavior to macOS
    * POSIX signal handling
    * Works on Ubuntu, Debian, RHEL, CentOS, Arch
    * Alpine may require glibc (not musl)
  </Tab>

  <Tab title="Windows">
    * PM2 cleanup now runs (safe due to try/catch)
    * Bun abstracts signal handling differences
    * Path module handles Windows separators
    * File locking handled by SQLite
  </Tab>
</Tabs>

## Observable Changes

### Command Changes

| Old (PM2)                        | New (Bun)                | Notes                   |
| -------------------------------- | ------------------------ | ----------------------- |
| `pm2 list`                       | `npm run worker:status`  | Shows worker status     |
| `pm2 start <script>`             | `npm run worker:start`   | Start worker            |
| `pm2 stop claude-mem-worker`     | `npm run worker:stop`    | Stop worker             |
| `pm2 restart claude-mem-worker`  | `npm run worker:restart` | Restart worker          |
| `pm2 delete claude-mem-worker`   | `npm run worker:stop`    | Remove worker           |
| `pm2 logs claude-mem-worker`     | `npm run worker:logs`    | View logs               |
| `pm2 describe claude-mem-worker` | `npm run worker:status`  | Detailed status         |
| `pm2 monit`                      | No equivalent            | PM2-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**:

```bash theme={null}
$ pm2 list
┌────┬────────────────────┬─────────┬─────────┬──────────┐
│ id │ name               │ status  │ restart │ uptime   │
├────┼────────────────────┼─────────┼─────────┼──────────┤
│ 0  │ claude-mem-worker  │ online  │ 0       │ 2d 5h    │
└────┴────────────────────┴─────────┴─────────┴──────────┘
```

**After Update**:

```bash theme={null}
$ 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)**:

```bash theme={null}
# 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)

<Warning>
  This is rare but can happen if PM2 has watch mode enabled or the process is manually restarted.
</Warning>

**Symptoms**:

* `pm2 list` still shows `claude-mem-worker`
* Port conflict errors in logs
* Worker fails to start

**Resolution**:

```bash theme={null}
# 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**:

```bash theme={null}
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**:

```bash theme={null}
# Check what's using the port
lsof -i :37777

# Kill the process
kill -9 <PID>

# Restart worker
npm run worker:restart
```

### Common Error Messages

| Error                    | Cause                  | Resolution                                     |
| ------------------------ | ---------------------- | ---------------------------------------------- |
| `EADDRINUSE`             | Port already in use    | `lsof -i :37777` then kill conflicting process |
| `No such process`        | Stale PID file         | Automatic cleanup on next hook trigger         |
| `pm2: command not found` | PM2 not installed      | None needed (error is caught and ignored)      |
| `Invalid port X`         | Port validation failed | Update `CLAUDE_MEM_WORKER_PORT` in settings    |

## Developer Notes

### Testing the Migration

```bash theme={null}
# 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.
