diff --git a/plugin/package.json b/plugin/package.json new file mode 100644 index 00000000..519346e8 --- /dev/null +++ b/plugin/package.json @@ -0,0 +1,13 @@ +{ + "name": "claude-mem-plugin", + "version": "7.0.3", + "private": true, + "description": "Runtime dependencies for claude-mem bundled hooks", + "type": "module", + "dependencies": { + "better-sqlite3": "^12.5.0" + }, + "engines": { + "node": ">=18.0.0" + } +} diff --git a/plugin/scripts/smart-install.js b/plugin/scripts/smart-install.js index ee79f332..87872141 100644 --- a/plugin/scripts/smart-install.js +++ b/plugin/scripts/smart-install.js @@ -4,6 +4,8 @@ * Smart Install Script for claude-mem * * Features: + * - Detects execution context (cache vs marketplace directory) + * - Installs dependencies where the hooks actually run (cache directory) * - Only runs npm install when necessary (version change or missing deps) * - Caches installation state with version marker * - Provides helpful Windows-specific error messages @@ -12,20 +14,30 @@ */ import { existsSync, readFileSync, writeFileSync } from 'fs'; -import { execSync, spawnSync, spawn } from 'child_process'; -import { join } from 'path'; +import { execSync } from 'child_process'; +import { join, dirname } from 'path'; import { homedir } from 'os'; import { createRequire } from 'module'; +import { fileURLToPath } from 'url'; -// CRITICAL: Always use marketplace directory for ALL operations -// This script may run from the cache directory (plugin/scripts/) but must -// operate on the marketplace directory where package.json and node_modules live. -// This ensures cross-platform compatibility and avoids cache directory confusion. -const MARKETPLACE_ROOT = join(homedir(), '.claude', 'plugins', 'marketplaces', 'thedotmack'); +// Determine the directory where THIS script is running from +// This could be either: +// 1. Cache: ~/.claude/plugins/cache/thedotmack/claude-mem/X.X.X/scripts/ +// 2. Marketplace: ~/.claude/plugins/marketplaces/thedotmack/plugin/scripts/ +const __dirname = dirname(fileURLToPath(import.meta.url)); +const SCRIPT_ROOT = dirname(__dirname); // Parent of scripts/ directory + +// Detect if running from cache directory (has version number in path) +const CACHE_PATTERN = /[/\\]cache[/\\]thedotmack[/\\]claude-mem[/\\]\d+\.\d+\.\d+/; +const IS_RUNNING_FROM_CACHE = CACHE_PATTERN.test(__dirname); + +// Set PLUGIN_ROOT based on where we're running +// If from cache, install dependencies IN the cache directory (where hooks run) +// If from marketplace, use marketplace directory +const PLUGIN_ROOT = IS_RUNNING_FROM_CACHE + ? SCRIPT_ROOT // Cache directory (e.g., ~/.claude/plugins/cache/thedotmack/claude-mem/7.0.3/) + : join(homedir(), '.claude', 'plugins', 'marketplaces', 'thedotmack'); -// Use MARKETPLACE_ROOT for all paths - this script can be deployed anywhere -// but always operates on the marketplace directory -const PLUGIN_ROOT = MARKETPLACE_ROOT; const PACKAGE_JSON_PATH = join(PLUGIN_ROOT, 'package.json'); const VERSION_MARKER_PATH = join(PLUGIN_ROOT, '.install-version'); const NODE_MODULES_PATH = join(PLUGIN_ROOT, 'node_modules'); @@ -102,6 +114,12 @@ function setInstalledVersion(packageVersion, nodeVersion) { } function needsInstall() { + // Check if package.json exists (required for npm install) + if (!existsSync(PACKAGE_JSON_PATH)) { + log(`āš ļø No package.json found at ${PLUGIN_ROOT}`, colors.yellow); + return false; // Can't install without package.json + } + // Check if node_modules exists if (!existsSync(NODE_MODULES_PATH)) { log('šŸ“¦ Dependencies not found - first time setup', colors.cyan); @@ -155,9 +173,8 @@ async function verifyNativeModules() { try { log('šŸ” Verifying native modules...', colors.dim); - // CRITICAL: Use createRequire() to resolve from MARKETPLACE_ROOT - // This script may run from cache but must load modules from marketplace's node_modules - const require = createRequire(join(MARKETPLACE_ROOT, 'package.json')); + // Use createRequire() to resolve from PLUGIN_ROOT's node_modules + const require = createRequire(join(PLUGIN_ROOT, 'package.json')); const Database = require('better-sqlite3'); // Try to create a test in-memory database @@ -247,7 +264,8 @@ async function runNpmInstall() { const isWindows = process.platform === 'win32'; log('', colors.cyan); - log('šŸ”Ø Installing dependencies...', colors.bright); + log(`šŸ”Ø Installing dependencies in ${IS_RUNNING_FROM_CACHE ? 'cache' : 'marketplace'}...`, colors.bright); + log(` ${PLUGIN_ROOT}`, colors.dim); log('', colors.reset); // Try normal install first, then retry with force if it fails @@ -274,7 +292,7 @@ async function runNpmInstall() { throw new Error('better-sqlite3 installation verification failed'); } - // NEW: Verify native modules actually work + // Verify native modules actually work const nativeModulesWork = await verifyNativeModules(); if (!nativeModulesWork) { throw new Error('Native modules failed to load after install'); @@ -331,16 +349,15 @@ async function runNpmInstall() { return false; } -/** - * Check if we should fail when worker startup fails - * Returns true if worker failed AND dependencies are missing - */ -function shouldFailOnWorkerStartup(workerStarted) { - return !workerStarted && !existsSync(NODE_MODULES_PATH); -} - async function main() { try { + // Log execution context for debugging + if (IS_RUNNING_FROM_CACHE) { + log('šŸ“ Running from cache directory', colors.dim); + } else { + log('šŸ“ Running from marketplace directory', colors.dim); + } + // Check if we need to install dependencies const installNeeded = needsInstall(); @@ -355,7 +372,7 @@ async function main() { process.exit(1); } } else { - // NEW: Even if install not needed, verify native modules work + // Even if install not needed, verify native modules work const nativeModulesWork = await verifyNativeModules(); if (!nativeModulesWork) { @@ -371,10 +388,10 @@ async function main() { } } - // NOTE: Worker auto-start disabled in smart-install.js - // The context-hook.js calls ensureWorkerRunning() which handles worker startup - // This avoids potential process management conflicts during plugin initialization - log('āœ… Installation complete', colors.green); + // NOTE: Worker auto-start disabled in smart-install.js + // The context-hook.js calls ensureWorkerRunning() which handles worker startup + // This avoids potential process management conflicts during plugin initialization + log('āœ… Installation complete', colors.green); // Success - dependencies installed (if needed) process.exit(0); diff --git a/scripts/build-hooks.js b/scripts/build-hooks.js index 5b49fc26..224f64a0 100644 --- a/scripts/build-hooks.js +++ b/scripts/build-hooks.js @@ -58,6 +58,26 @@ async function buildHooks() { } console.log('āœ“ Output directories ready'); + // Generate plugin/package.json for cache directory dependency installation + // The bundled hooks use `external: ['better-sqlite3']` so dependencies must be + // installed at runtime. This package.json enables npm install in the cache directory. + console.log('\nšŸ“¦ Generating plugin package.json...'); + const pluginPackageJson = { + name: 'claude-mem-plugin', + version: version, + private: true, + description: 'Runtime dependencies for claude-mem bundled hooks', + type: 'module', + dependencies: { + 'better-sqlite3': packageJson.dependencies['better-sqlite3'] + }, + engines: { + node: '>=18.0.0' + } + }; + fs.writeFileSync('plugin/package.json', JSON.stringify(pluginPackageJson, null, 2) + '\n'); + console.log('āœ“ plugin/package.json generated'); + // Build React viewer console.log('\nšŸ“‹ Building React viewer...'); const { spawn } = await import('child_process');