* docs: add investigation reports for 5 open GitHub issues Comprehensive analysis of issues #543, #544, #545, #555, and #557: - #557: settings.json not generated, module loader error (node/bun mismatch) - #555: Windows hooks not executing, hasIpc always false - #545: formatTool crashes on non-JSON tool_input strings - #544: mem-search skill hint shown incorrectly to Claude Code users - #543: /claude-mem slash command unavailable despite installation Each report includes root cause analysis, affected files, and proposed fixes. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * fix(logger): handle non-JSON tool_input in formatTool (#545) Wrap JSON.parse in try-catch to handle raw string inputs (e.g., Bash commands) that aren't valid JSON. Falls back to using the string as-is. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * fix(context): update mem-search hint to reference MCP tools (#544) Update hint messages to reference MCP tools (search, get_observations) instead of the deprecated "mem-search skill" terminology. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * fix(settings): auto-create settings.json on first load (#557, #543) When settings.json doesn't exist, create it with defaults instead of returning in-memory defaults. Creates parent directory if needed. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * fix(hooks): use bun runtime for hooks except smart-install (#557) Change hook commands from node to bun since hooks use bun:sqlite. Keep smart-install.js on node since it bootstraps bun installation. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * chore: rebuild plugin scripts * docs: clarify that build artifacts must be committed * fix(docs): update build artifacts directory reference in CLAUDE.md * test: add test coverage for PR #558 fixes - Fix 2 failing tests: update "mem-search skill" → "MCP tools" expectations - Add 56 tests for formatTool() JSON.parse crash fix (Issue #545) - Add 27 tests for settings.json auto-creation (Issue #543) Test coverage includes: - formatTool: JSON parsing, raw strings, objects, null/undefined, all tool types - Settings: file creation, directory creation, schema migration, edge cases 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * fix(tests): clean up flaky tests and fix circular dependency Phase 1 of test quality improvements: - Delete 6 harmful/worthless test files that used problematic mock.module() patterns or tested implementation details rather than behavior: - context-builder.test.ts (tested internal implementation) - export-types.test.ts (fragile mock patterns) - smart-install.test.ts (shell script testing antipattern) - session_id_refactor.test.ts (outdated, tested refactoring itself) - validate_sql_update.test.ts (one-time migration validation) - observation-broadcaster.test.ts (excessive mocking) - Fix circular dependency between logger.ts and SettingsDefaultsManager.ts by using late binding pattern - logger now lazily loads settings - Refactor mock.module() to spyOn() in several test files for more maintainable and less brittle tests: - observation-compiler.test.ts - gemini_agent.test.ts - error-handler.test.ts - server.test.ts - response-processor.test.ts All 649 tests pass. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * refactor(tests): phase 2 - reduce mock-heavy tests and improve focus - Remove mock-heavy query tests from observation-compiler.test.ts, keep real buildTimeline tests - Convert session_id_usage_validation.test.ts from 477 to 178 lines of focused smoke tests - Remove tests for language built-ins from worker-spawn.test.ts (JSON.parse, array indexing) - Rename logger-coverage.test.ts to logger-usage-standards.test.ts for clarity 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * docs(tests): phase 3 - add JSDoc mock justification to test files Document mock usage rationale in 5 test files to improve maintainability: - error-handler.test.ts: Express req/res mocks, logger spies (~11%) - fallback-error-handler.test.ts: Zero mocks, pure function tests - session-cleanup-helper.test.ts: Session fixtures, worker mocks (~19%) - hook-constants.test.ts: process.platform mock for Windows tests (~12%) - session_store.test.ts: Zero mocks, real SQLite :memory: database Part of ongoing effort to document mock justifications per TESTING.md guidelines. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * test(integration): phase 5 - add 72 tests for critical coverage gaps Add comprehensive test coverage for previously untested areas: - tests/integration/hook-execution-e2e.test.ts (10 tests) Tests lifecycle hooks execution flow and context propagation - tests/integration/worker-api-endpoints.test.ts (19 tests) Tests all worker service HTTP endpoints without heavy mocking - tests/integration/chroma-vector-sync.test.ts (16 tests) Tests vector embedding synchronization with ChromaDB - tests/utils/tag-stripping.test.ts (27 tests) Tests privacy tag stripping utilities for both <private> and <meta-observation> tags All tests use real implementations where feasible, following the project's testing philosophy of preferring integration-style tests over unit tests with extensive mocking. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * context update * docs: add comment linking DEFAULT_DATA_DIR locations Added NOTE comment in logger.ts pointing to the canonical DEFAULT_DATA_DIR in SettingsDefaultsManager.ts. This addresses PR reviewer feedback about the fragility of having the default defined in two places to avoid circular dependencies. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
11 KiB
Investigation Report: Issue #557 - Plugin Fails to Start
Date: January 5, 2026 Issue: #557 - Plugin fails to start: settings.json not generated, worker throws module loader error Author: Sheikh Abdur Raheem Ali (@sheikheddy) Investigator: Claude (Opus 4.5)
Executive Summary
The plugin fails to start during the SessionStart hook with a Node.js module loader error. This investigation identifies two separate but related issues:
- Primary Issue: Runtime mismatch - hooks are built for Bun but invoked with Node.js
- Secondary Issue: settings.json auto-creation only happens via HTTP API, not during initialization
The root cause appears to be that Claude Code 2.0.76 is invoking hooks with Node.js despite hooks having #!/usr/bin/env bun shebangs, and Node.js v25.2.1 cannot execute code with bun:sqlite imports (an external module reference that doesn't exist in Node.js).
Environment Details
| Component | Version |
|---|---|
| claude-mem | 8.1.0 |
| Claude Code | 2.0.76 |
| Node.js | v25.2.1 |
| Bun | 1.3.5 |
| OS | macOS 26.2 (arm64) |
| Database Size | 17.9 MB (existing data) |
Issue Analysis
Error Location
The error occurs at:
node:internal/modules/cjs/loader:1423
throw err;
^
This error signature indicates Node.js (not Bun) is attempting to load a CommonJS module that has unresolvable dependencies.
Hook Configuration Analysis
From /Users/alexnewman/Scripts/claude-mem/plugin/hooks/hooks.json:
{
"SessionStart": [
{
"matcher": "startup|clear|compact",
"hooks": [
{
"type": "command",
"command": "node \"${CLAUDE_PLUGIN_ROOT}/scripts/smart-install.js\"",
"timeout": 300
},
{
"type": "command",
"command": "bun \"${CLAUDE_PLUGIN_ROOT}/scripts/worker-service.cjs\" start",
"timeout": 60
},
{
"type": "command",
"command": "node \"${CLAUDE_PLUGIN_ROOT}/scripts/context-hook.js\"",
"timeout": 60
},
{
"type": "command",
"command": "node \"${CLAUDE_PLUGIN_ROOT}/scripts/user-message-hook.js\"",
"timeout": 60
}
]
}
]
}
Key Observation: Hooks are explicitly invoked with node but are built as ESM bundles with Bun-specific features.
Build Configuration Analysis
From /Users/alexnewman/Scripts/claude-mem/scripts/build-hooks.js:
-
Hooks are built with:
format: 'esm'(ES modules)external: ['bun:sqlite'](Bun-specific SQLite binding)- Shebang:
#!/usr/bin/env bun
-
Worker Service is built with:
format: 'cjs'(CommonJS)external: ['bun:sqlite']- Shebang:
#!/usr/bin/env bun
The bun:sqlite external dependency is the critical issue. When Node.js tries to load these files, it cannot resolve bun:sqlite as it's a Bun-specific built-in module.
Settings.json Auto-Creation Analysis
From /Users/alexnewman/Scripts/claude-mem/src/services/worker/http/routes/SettingsRoutes.ts:
private ensureSettingsFile(settingsPath: string): void {
if (!existsSync(settingsPath)) {
const defaults = SettingsDefaultsManager.getAllDefaults();
const dir = path.dirname(settingsPath);
if (!existsSync(dir)) {
mkdirSync(dir, { recursive: true });
}
writeFileSync(settingsPath, JSON.stringify(defaults, null, 2), 'utf-8');
logger.info('SETTINGS', 'Created settings file with defaults', { settingsPath });
}
}
This method is only called when:
GET /api/settingsis requestedPOST /api/settingsis requested
Problem: If the worker service fails to start (due to the module loader error), the HTTP API never becomes available, so ensureSettingsFile is never called.
SettingsDefaultsManager Behavior
From /Users/alexnewman/Scripts/claude-mem/src/shared/SettingsDefaultsManager.ts:
static loadFromFile(settingsPath: string): SettingsDefaults {
try {
if (!existsSync(settingsPath)) {
return this.getAllDefaults(); // Returns defaults, doesn't create file
}
// ... rest of loading logic
} catch (error) {
return this.getAllDefaults(); // Fallback to defaults on any error
}
}
Behavior: When settings.json doesn't exist, loadFromFile returns in-memory defaults but does NOT create the file. This is defensive programming (fail-safe) but means the file is never auto-created during worker startup.
Root Cause Analysis
Primary Root Cause: Runtime Mismatch
The hooks are designed to run under Bun (as indicated by their shebangs and bun:sqlite dependency), but hooks.json explicitly invokes them with node:
"command": "node \"${CLAUDE_PLUGIN_ROOT}/scripts/context-hook.js\""
When Node.js v25.2.1 attempts to load these ESM bundles:
- It parses the JavaScript successfully (ESM is valid)
- It encounters
import ... from 'bun:sqlite' - Node.js cannot resolve
bun:sqlite(not a valid Node.js specifier) - CJS loader throws the error at line 1423
Why This Worked Before (Potential Regression Paths)
- Bun Availability: The smart-install.js script auto-installs Bun, but the PATH may not be updated within the same shell session
- Claude Code Change: Claude Code 2.0.76 may have changed how it invokes hooks (not honoring shebangs, using explicit
nodecommand) - Node.js v25 Change: Node.js v25 may handle ESM/CJS boundaries differently than earlier versions
Secondary Root Cause: Settings Not Auto-Created at Startup
The worker service's background initialization (initializeBackground()) loads settings but doesn't create the file:
const settings = SettingsDefaultsManager.loadFromFile(USER_SETTINGS_PATH);
const modeId = settings.CLAUDE_MEM_MODE;
ModeManager.getInstance().loadMode(modeId);
loadFromFile returns defaults when the file is missing but doesn't write them to disk.
Affected Files
| File | Role | Issue |
|---|---|---|
/plugin/hooks/hooks.json |
Hook configuration | Explicitly uses node instead of bun |
/plugin/scripts/context-hook.js |
SessionStart hook | ESM with bun:sqlite dependency |
/plugin/scripts/user-message-hook.js |
SessionStart hook | ESM with bun:sqlite dependency |
/plugin/scripts/worker-service.cjs |
Worker service | CJS with bun:sqlite dependency |
/src/shared/SettingsDefaultsManager.ts |
Settings manager | Doesn't auto-create file |
/src/services/worker/http/routes/SettingsRoutes.ts |
HTTP routes | Only creates file on API access |
/scripts/build-hooks.js |
Build script | Marks bun:sqlite as external |
Proposed Fixes
Fix 1: Update hooks.json to Use Bun (Recommended)
Change all hook commands from node to bun:
{
"type": "command",
"command": "bun \"${CLAUDE_PLUGIN_ROOT}/scripts/context-hook.js\"",
"timeout": 60
}
Rationale: Hooks depend on bun:sqlite, so they must run under Bun.
Fix 2: Create Settings File During Startup
Add file creation to SettingsDefaultsManager.loadFromFile:
static loadFromFile(settingsPath: string): SettingsDefaults {
try {
if (!existsSync(settingsPath)) {
const defaults = this.getAllDefaults();
// Create directory if needed
const dir = path.dirname(settingsPath);
if (!existsSync(dir)) {
mkdirSync(dir, { recursive: true });
}
// Write defaults to file
writeFileSync(settingsPath, JSON.stringify(defaults, null, 2), 'utf-8');
logger.info('SETTINGS', 'Created settings file with defaults', { settingsPath });
return defaults;
}
// ... existing logic
} catch (error) {
logger.warn('SETTINGS', 'Failed to load/create settings, using defaults', { settingsPath }, error);
return this.getAllDefaults();
}
}
Rationale: This ensures settings.json always exists after first access, regardless of how the plugin starts.
Fix 3: Build Hooks Without bun:sqlite Dependency (Alternative)
Modify the build to inline SQLite operations or use a Node.js-compatible SQLite library:
// In build-hooks.js
external: [], // Remove bun:sqlite from externals
This would require using better-sqlite3 or similar, which has been deliberately avoided due to native module compilation issues.
Fix 4: Add Fallback Logic in Hooks (Defensive)
Add runtime detection to hooks to provide better error messages:
if (typeof Bun === 'undefined') {
console.error('This hook requires Bun runtime. Please ensure Bun is installed.');
process.exit(1);
}
Verification Steps
-
Confirm Bun is installed and in PATH:
which bun bun --version -
Manually test context-hook with Bun:
bun ~/.claude/plugins/marketplaces/thedotmack/plugin/scripts/context-hook.js -
Manually test context-hook with Node (should fail):
node ~/.claude/plugins/marketplaces/thedotmack/plugin/scripts/context-hook.js -
Check if settings.json exists:
cat ~/.claude-mem/settings.json -
Verify worker can start:
bun ~/.claude/plugins/marketplaces/thedotmack/plugin/scripts/worker-service.cjs start
Related Issues
- Issue #290:
refactor: simplify hook execution - use Node directly instead of Bun- This commit changed hooks to use Node, potentially introducing this regression - Issue #265:
fix: add npm fallback when bun install fails with alias packages- Related to Bun/npm installation issues - Issue #527:
uv-homebrew-analysis- Related to dependency installation issues
Workaround for Users
Until a fix is released, users can manually:
-
Ensure Bun is installed:
curl -fsSL https://bun.sh/install | bash source ~/.bashrc # or ~/.zshrc -
Create settings.json manually:
mkdir -p ~/.claude-mem cat > ~/.claude-mem/settings.json << 'EOF' { "CLAUDE_MEM_MODEL": "claude-sonnet-4-5", "CLAUDE_MEM_CONTEXT_OBSERVATIONS": "50", "CLAUDE_MEM_WORKER_PORT": "37777", "CLAUDE_MEM_WORKER_HOST": "127.0.0.1", "CLAUDE_MEM_PROVIDER": "claude", "CLAUDE_MEM_DATA_DIR": "$HOME/.claude-mem", "CLAUDE_MEM_LOG_LEVEL": "INFO", "CLAUDE_MEM_MODE": "code" } EOF -
Start worker manually:
bun ~/.claude/plugins/marketplaces/thedotmack/plugin/scripts/worker-service.cjs start
Conclusion
This issue is a runtime mismatch regression where hooks built for Bun are being invoked with Node.js. The fix requires updating hooks.json to use Bun for all hook commands that depend on bun:sqlite. The settings.json creation is a secondary issue that should be addressed by ensuring the file is created during first access in SettingsDefaultsManager.loadFromFile.
Priority: High (blocks plugin startup) Severity: Critical (plugin completely non-functional) Effort: Low (configuration change + minor code addition)