claude-recall 0.24.0 → 0.24.1

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.
@@ -261,6 +261,99 @@ class ClaudeRecallCLI {
261
261
  });
262
262
  this.logger.info('CLI', 'Failures displayed', { count: displayFailures.length });
263
263
  }
264
+ /**
265
+ * Find project-local node_modules/claude-recall/ installs that shadow the
266
+ * global one when invoked via `npx claude-recall`. npx walks up from cwd
267
+ * looking for node_modules/.bin/claude-recall and uses the first match,
268
+ * not the globally-installed binary. A stray ~/node_modules/claude-recall/
269
+ * (a common WSL/Windows accident) traps every npx invocation under $HOME.
270
+ *
271
+ * Returns dir + version for each stale install found. Skips the global
272
+ * install (heuristic: paths containing /lib/node_modules/ or /.nvm/).
273
+ * Walk stops at $HOME so we never report or touch system locations.
274
+ */
275
+ findStaleLocalInstalls() {
276
+ const found = [];
277
+ const seen = new Set();
278
+ const home = os.homedir();
279
+ const checkDir = (dir) => {
280
+ if (seen.has(dir))
281
+ return;
282
+ seen.add(dir);
283
+ const pkgPath = path.join(dir, 'node_modules', 'claude-recall', 'package.json');
284
+ if (!fs.existsSync(pkgPath))
285
+ return;
286
+ // Skip global install paths so we never propose nuking them.
287
+ if (pkgPath.includes('/lib/node_modules/'))
288
+ return;
289
+ if (pkgPath.includes('/.nvm/'))
290
+ return;
291
+ try {
292
+ const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf-8'));
293
+ if (pkg && typeof pkg.version === 'string') {
294
+ found.push({ dir, version: pkg.version });
295
+ }
296
+ }
297
+ catch {
298
+ // Unreadable manifest — skip silently.
299
+ }
300
+ };
301
+ // Walk cwd up to $HOME (don't traverse above the user's home).
302
+ let dir = process.cwd();
303
+ while (true) {
304
+ checkDir(dir);
305
+ if (dir === home)
306
+ break;
307
+ const parent = path.dirname(dir);
308
+ if (parent === dir)
309
+ break; // hit filesystem root
310
+ dir = parent;
311
+ }
312
+ // If cwd was outside the home tree, the walk skipped $HOME. Check it.
313
+ checkDir(home);
314
+ return found;
315
+ }
316
+ /**
317
+ * Print a warning about stale local installs. If `autoClean` is true,
318
+ * actually remove them. Otherwise print copy-paste rm commands.
319
+ */
320
+ warnOrCleanStaleLocals(globalVersion, autoClean) {
321
+ const stale = this.findStaleLocalInstalls();
322
+ if (stale.length === 0)
323
+ return;
324
+ console.log('\n⚠ Stale local claude-recall installs detected:');
325
+ for (const s of stale) {
326
+ const drift = s.version !== globalVersion
327
+ ? ` (drift: global is ${globalVersion})`
328
+ : '';
329
+ console.log(` ${path.join(s.dir, 'node_modules', 'claude-recall')} v${s.version}${drift}`);
330
+ }
331
+ console.log('');
332
+ console.log(' These shadow the global install. `npx claude-recall` walks up from cwd');
333
+ console.log(' looking for node_modules/.bin/claude-recall and uses the first match —');
334
+ console.log(` not the global v${globalVersion}.`);
335
+ if (autoClean) {
336
+ console.log('\n🧹 Removing...');
337
+ for (const s of stale) {
338
+ const target = path.join(s.dir, 'node_modules', 'claude-recall');
339
+ try {
340
+ fs.rmSync(target, { recursive: true, force: true });
341
+ console.log(` ✓ removed ${target}`);
342
+ }
343
+ catch (e) {
344
+ console.log(` ✗ failed to remove ${target}: ${e.message}`);
345
+ }
346
+ }
347
+ console.log('\n Verify with: npx claude-recall --version');
348
+ }
349
+ else {
350
+ console.log('\n To remove them all:');
351
+ for (const s of stale) {
352
+ console.log(` rm -rf ${path.join(s.dir, 'node_modules', 'claude-recall')}`);
353
+ }
354
+ console.log('\n Or re-run upgrade with auto-clean: claude-recall upgrade --clean-locals');
355
+ }
356
+ }
264
357
  /**
265
358
  * One-shot upgrade: check registry, install latest globally, clean up any
266
359
  * running MCP servers (so fresh 0.x spawns on next Claude Code tool call).
@@ -269,8 +362,9 @@ class ClaudeRecallCLI {
269
362
  * - EACCES on `/usr/lib/node_modules` → print sudo + permanent-prefix fix
270
363
  * - No npm in PATH → actionable error
271
364
  * - Registry unreachable → clear error, don't leave install half-done
365
+ * - Stale project-local installs shadowing the global → warn (or clean with --clean-locals)
272
366
  */
273
- async upgrade() {
367
+ async upgrade(opts = {}) {
274
368
  const { execSync, spawnSync } = require('child_process');
275
369
  // Current version from package.json shipped with the installed binary
276
370
  let current;
@@ -328,6 +422,9 @@ class ClaudeRecallCLI {
328
422
  catch {
329
423
  // Non-fatal — the user can restart Claude Code manually if this fails
330
424
  }
425
+ // Detect stale project-local installs that would shadow the just-installed
426
+ // global binary when invoked via `npx claude-recall`.
427
+ this.warnOrCleanStaleLocals(latest, opts.cleanLocals === true);
331
428
  console.log(`\n✓ Upgraded to ${latest}. No need to re-run \`claude mcp add\` — existing`);
332
429
  console.log(' registrations point at the `claude-recall` command and pick up the new');
333
430
  console.log(' binary automatically. Just run any tool in Claude Code.');
@@ -1707,9 +1804,10 @@ async function main() {
1707
1804
  program
1708
1805
  .command('upgrade')
1709
1806
  .description('Upgrade claude-recall to the latest version and clear stale MCP servers')
1710
- .action(async () => {
1807
+ .option('--clean-locals', 'Also remove stale project-local installs that shadow the global binary')
1808
+ .action(async (options) => {
1711
1809
  const cli = new ClaudeRecallCLI(program.opts());
1712
- await cli.upgrade();
1810
+ await cli.upgrade({ cleanLocals: options.cleanLocals === true });
1713
1811
  process.exit(0);
1714
1812
  });
1715
1813
  // Export command
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-recall",
3
- "version": "0.24.0",
3
+ "version": "0.24.1",
4
4
  "description": "Persistent memory for Claude Code and Pi with native Skills integration, automatic capture, failure learning, and project scoping",
5
5
  "main": "dist/index.js",
6
6
  "bin": {
@@ -49,6 +49,8 @@
49
49
  "search": "npm run claude-recall search",
50
50
  "preuninstall": "node scripts/uninstall.js",
51
51
  "prepare": "npm run build",
52
+ "prepublishOnly": "node scripts/check-publish.js",
53
+ "check:publish": "node scripts/check-publish.js",
52
54
  "mcp:start": "node dist/cli/claude-recall-cli.js mcp start",
53
55
  "mcp:dev": "ts-node src/cli/claude-recall-cli.ts mcp start",
54
56
  "mcp:debug": "NODE_ENV=development DEBUG=claude-recall:* ts-node src/cli/claude-recall-cli.ts mcp start",