specmem-hardwicksoftware 3.7.1 → 3.7.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -117,10 +117,10 @@ const SOURCE_COMMANDS_DIR = path.join(SPECMEM_ROOT, 'commands');
117
117
  const HOOK_ENV = {
118
118
  SPECMEM_HOME: path.join(HOME_DIR, '.specmem'), // Dynamic path using os.homedir()
119
119
  SPECMEM_PKG: SPECMEM_ROOT, // Use detected package root
120
- // Per-project socket paths - ${cwd} is expanded at runtime by Code
120
+ // Per-project socket paths - ${PWD} is expanded at MCP server startup
121
121
  SPECMEM_RUN_DIR: '${cwd}/specmem/sockets',
122
122
  SPECMEM_EMBEDDING_SOCKET: '${cwd}/specmem/sockets/embeddings.sock',
123
- SPECMEM_PROJECT_PATH: '${cwd}', // Dynamically set by Code
123
+ SPECMEM_PROJECT_PATH: '${PWD}', // Dynamically set by Code
124
124
  SPECMEM_SEARCH_LIMIT: '5',
125
125
  SPECMEM_THRESHOLD: '0.30',
126
126
  SPECMEM_MAX_CONTENT: '200'
@@ -213,7 +213,7 @@ export function isSpecmemMcpConfigured(projectPath) {
213
213
  if (projectPath) {
214
214
  const configuredPath = specmem.env?.SPECMEM_PROJECT_PATH;
215
215
  // ${PWD} and ${cwd} are expanded at runtime by Claude Code, so they're valid
216
- if (configuredPath && configuredPath !== '${PWD}' && configuredPath !== '${cwd}' && configuredPath !== projectPath) {
216
+ if (configuredPath && configuredPath !== '${PWD}' && configuredPath !== '${PWD}' && configuredPath !== projectPath) {
217
217
  return false;
218
218
  }
219
219
  }
@@ -236,17 +236,16 @@ function configureMcpServer() {
236
236
  config.mcpServers = {};
237
237
  }
238
238
  // Build the SpecMem MCP server config
239
- // CRITICAL: ${cwd} is expanded by Code at runtime to current working directory
240
- // NOTE: ${PWD} resolves at MCP server startup, ${cwd} resolves per-invocation
239
+ // CRITICAL: ${PWD} is expanded at MCP server startup to current working directory
241
240
  const specmemConfig = {
242
241
  command: 'node',
243
242
  args: ['--max-old-space-size=250', BOOTSTRAP_PATH],
244
243
  env: {
245
- // Core paths - ${cwd} gives us project isolation (dynamic per-directory)
244
+ // Core paths - ${PWD} gives us project isolation (dynamic per-directory)
246
245
  HOME: HOME_DIR,
247
- SPECMEM_PROJECT_PATH: '${cwd}',
248
- SPECMEM_WATCHER_ROOT_PATH: '${cwd}',
249
- SPECMEM_CODEBASE_PATH: '${cwd}',
246
+ SPECMEM_PROJECT_PATH: '${PWD}',
247
+ SPECMEM_WATCHER_ROOT_PATH: '${PWD}',
248
+ SPECMEM_CODEBASE_PATH: '${PWD}',
250
249
  // Database (use environment values or defaults)
251
250
  SPECMEM_DB_HOST: process.env.SPECMEM_DB_HOST || 'localhost',
252
251
  SPECMEM_DB_PORT: process.env.SPECMEM_DB_PORT || '5432',
@@ -332,7 +331,7 @@ function fixProjectMcpConfigs() {
332
331
  if (!specmem.env) {
333
332
  specmem.env = {};
334
333
  }
335
- if (!specmem.env.SPECMEM_PROJECT_PATH || specmem.env.SPECMEM_PROJECT_PATH === '${PWD}' || specmem.env.SPECMEM_PROJECT_PATH === '${cwd}') {
334
+ if (!specmem.env.SPECMEM_PROJECT_PATH || specmem.env.SPECMEM_PROJECT_PATH === '${PWD}' || specmem.env.SPECMEM_PROJECT_PATH === '${PWD}') {
336
335
  specmem.env.SPECMEM_PROJECT_PATH = projectPath;
337
336
  }
338
337
  logger.info({ projectPath, oldArgs: args, newArgs: updatedArgs }, '[ConfigInjector] Fixed outdated specmem path in project config');
@@ -349,9 +348,9 @@ function fixProjectMcpConfigs() {
349
348
  args: ['--max-old-space-size=250', BOOTSTRAP_PATH],
350
349
  env: {
351
350
  HOME: HOME_DIR,
352
- SPECMEM_PROJECT_PATH: '${cwd}',
353
- SPECMEM_WATCHER_ROOT_PATH: '${cwd}',
354
- SPECMEM_CODEBASE_PATH: '${cwd}',
351
+ SPECMEM_PROJECT_PATH: '${PWD}',
352
+ SPECMEM_WATCHER_ROOT_PATH: '${PWD}',
353
+ SPECMEM_CODEBASE_PATH: '${PWD}',
355
354
  SPECMEM_DB_HOST: process.env.SPECMEM_DB_HOST || 'localhost',
356
355
  SPECMEM_DB_PORT: process.env.SPECMEM_DB_PORT || '5432',
357
356
  SPECMEM_SESSION_WATCHER_ENABLED: 'true',
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "specmem-hardwicksoftware",
3
- "version": "3.7.1",
3
+ "version": "3.7.3",
4
4
  "type": "module",
5
5
  "description": "Persistent memory system for coding sessions - semantic search with pgvector, token compression, team coordination, file watching. Needs root: installs system-wide hooks, manages docker/PostgreSQL, writes global configs, handles screen sessions. justcalljon.pro",
6
6
  "main": "dist/index.js",
@@ -3596,7 +3596,8 @@ async function indexCodebase(projectPath, ui, embeddingResult) {
3596
3596
  // Revalidate socket path after failures (may have been restarted externally)
3597
3597
  async function revalidateSocket() {
3598
3598
  const projectSocketPath = path.join(projectPath, 'specmem', 'sockets', 'embeddings.sock');
3599
- const sharedSocketPath = path.join(os.homedir(), '.specmem', projectName.replace(/[^a-zA-Z0-9_-]/g, '_'), 'sockets', 'embeddings.sock');
3599
+ const projDirName = path.basename(projectPath).replace(/[^a-zA-Z0-9_-]/g, '_');
3600
+ const sharedSocketPath = path.join(os.homedir(), '.specmem', projDirName, 'sockets', 'embeddings.sock');
3600
3601
 
3601
3602
  // Check project socket first (preferred)
3602
3603
  if (fs.existsSync(projectSocketPath)) {
@@ -3609,7 +3610,7 @@ async function indexCodebase(projectPath, ui, embeddingResult) {
3609
3610
  }
3610
3611
  }
3611
3612
 
3612
- // Fallback to shared socket
3613
+ // Fallback to shared socket (home dir)
3613
3614
  if (fs.existsSync(sharedSocketPath)) {
3614
3615
  activeSocketPath = sharedSocketPath;
3615
3616
  const healthy = await checkSocketHealth();
@@ -3620,6 +3621,70 @@ async function indexCodebase(projectPath, ui, embeddingResult) {
3620
3621
  }
3621
3622
  }
3622
3623
 
3624
+ // Fallback to /tmp shared sockets
3625
+ for (let i = 0; i < 5; i++) {
3626
+ const tmpSocket = `/tmp/specmem-embed-${i}.sock`;
3627
+ if (fs.existsSync(tmpSocket)) {
3628
+ activeSocketPath = tmpSocket;
3629
+ const healthy = await checkSocketHealth();
3630
+ if (healthy) {
3631
+ initLog(`Revalidated socket: using /tmp shared socket at ${tmpSocket}`);
3632
+ consecutiveEmbeddingFailures = 0;
3633
+ return true;
3634
+ }
3635
+ }
3636
+ }
3637
+
3638
+ // AUTO-RESTART: If no socket found, try restarting the embedding server
3639
+ initLog('Socket revalidation: no socket found, attempting auto-restart of embedding server...');
3640
+ try {
3641
+ const { spawn } = require('child_process');
3642
+ const possiblePythonPaths = [
3643
+ path.join(__dirname, '..', 'embedding-sandbox', 'frankenstein-embeddings.py'),
3644
+ path.join(projectPath, 'embedding-sandbox', 'frankenstein-embeddings.py'),
3645
+ '/opt/specmem/embedding-sandbox/frankenstein-embeddings.py'
3646
+ ];
3647
+ let embeddingScript = null;
3648
+ for (const p of possiblePythonPaths) {
3649
+ if (fs.existsSync(p)) { embeddingScript = p; break; }
3650
+ }
3651
+ if (embeddingScript) {
3652
+ const socketsDir = path.join(projectPath, 'specmem', 'sockets');
3653
+ if (!fs.existsSync(socketsDir)) fs.mkdirSync(socketsDir, { recursive: true });
3654
+ // Clean stale socket
3655
+ if (fs.existsSync(projectSocketPath)) {
3656
+ try { fs.unlinkSync(projectSocketPath); } catch { /* ignore */ }
3657
+ }
3658
+ const pythonPath = getPythonPath();
3659
+ const proc = spawn(pythonPath, [embeddingScript], {
3660
+ cwd: path.dirname(embeddingScript),
3661
+ env: { ...process.env, SPECMEM_SOCKET_PATH: projectSocketPath, SPECMEM_PROJECT_PATH: projectPath },
3662
+ detached: true,
3663
+ stdio: ['ignore', 'pipe', 'pipe']
3664
+ });
3665
+ proc.stdout.on('data', () => {});
3666
+ proc.stderr.on('data', () => {});
3667
+ proc.on('error', () => {});
3668
+ proc.unref();
3669
+ // Wait up to 15s for socket to appear
3670
+ for (let i = 0; i < 30; i++) {
3671
+ await new Promise(r => setTimeout(r, 500));
3672
+ if (fs.existsSync(projectSocketPath)) {
3673
+ activeSocketPath = projectSocketPath;
3674
+ const healthy = await checkSocketHealth();
3675
+ if (healthy) {
3676
+ initLog(`Embedding server auto-restarted successfully, socket at ${projectSocketPath}`);
3677
+ consecutiveEmbeddingFailures = 0;
3678
+ return true;
3679
+ }
3680
+ }
3681
+ }
3682
+ initLog('Embedding server auto-restart: socket did not appear within 15s');
3683
+ }
3684
+ } catch (restartErr) {
3685
+ initLog(`Embedding server auto-restart failed: ${restartErr.message || restartErr}`);
3686
+ }
3687
+
3623
3688
  initLog('Socket revalidation failed: no healthy socket found');
3624
3689
  return false;
3625
3690
  }