Adds Cursor (live transcripts + opt-in state.vscdb backfill) and opencode (DB->JSONL export) as harnesses, following the existing Codex pattern. opencode summarization is fixed to route via transcript text instead of a doomed claude --resume, and hasConversationContent recognizes opencode_message lines. Harness union is 'claude'|'codex'|'cursor'|'opencode'. Also pins conversation timestamps to en-US/UTC in every renderer (#130) and restores valid YAML in the search agent frontmatter (#153).
Co-authored-by: vicnaum <vicnaum@users.noreply.github.com>
Co-authored-by: slandau3 <slandau3@users.noreply.github.com>
Co-authored-by: arimu1 <arimu1@users.noreply.github.com>
Claude-Session: https://claude.ai/code/session_0112vdwZphiWzfCYfaXMes4C
Independent SessionStart events from multiple Claude Code sessions
each fire `episodic-memory sync --background`, which forks a detached
worker. Without coordination, N parents trigger N concurrent worker
processes racing the same archive and SQLite database.
The reentrancy guard (#87) covers same-process recursion, but it
can't stop SessionStart events from independent sessions whose envs
don't carry EPISODIC_MEMORY_SUMMARIZER_GUARD.
Reproduced locally on macOS: 3 parallel `node dist/sync-cli.js`
processes against the same TEST_ARCHIVE_DIR — worker 1 completes;
workers 2 and 3 crash with `SqliteError: database is locked`
(SQLITE_BUSY) trying to init the DB. On Windows, the reporter's
setup with ~67 worktrees instead piles up enough claude.exe children
to exhaust the desktop heap and crash with STATUS_DLL_INIT_FAILED
(0xC0000142). Same root cause; different blast radius depending on
how far the workers get before stepping on each other.
Fix: a single-instance lock around the sync worker, implemented as a
thin wrapper in src/file-lock.ts over the `proper-lockfile` package.
A first attempt at a hand-rolled openSync('wx') + PID-file protocol
had a residual race under concurrent stale-stealers that pure file
primitives cannot fully close without advisory locking.
proper-lockfile uses an atomic-mkdir + mtime-heartbeat protocol that
is race-free under that contention shape — the same approach npm
itself uses.
sync-cli.ts acquires <log-dir>/episodic-memory-sync.lock after the
source-dir check and before initDatabase(); if another live process
holds it, the worker prints "sync already running (pid X); skipping"
to stderr and exits 0. The lock releases on normal exit and on the
common signals (SIGINT/SIGTERM/SIGHUP).
Embedding migration's own lock now delegates to the generic helper;
its old export names (acquireMigrationLock, releaseMigrationLock,
MigrationLockHandle) stay for back-compat.
proper-lockfile is excluded from the esbuild bundle (runtime dep on
the same level as better-sqlite3/transformers/etc.) so the MCP
server bundle size is unchanged. The wrapper's install-health probe
(#95 Bug 1) gains proper-lockfile as a required package so a partial
extraction surfaces a useful diagnostic.
Tests:
- test/file-lock.test.ts: acquire/release, contention,
parent-dir creation, garbage diagnostic content, I/O error
propagation, N-concurrent-acquirers stress test, mtime-based
stale recovery, fresh-lock-not-reclaimable. Subprocess imports
use pathToFileURL for Windows compatibility.
- test/sync-cli-single-instance.test.ts: integration via real
child-process spawn — two concurrent workers (one completes,
one skips), single sequential run still works, lock released
on normal exit, stale lock from a dead PID is reclaimable.
- Existing test/embedding-migration.test.ts continues to pass.
Closes#97.
cli/mcp-server-wrapper.js previously checked only `existsSync(node_modules)`
to decide whether to run `npm install`. A partial extraction — the
directory exists, but a package is missing its package.json and lib/ —
slips past that check, hands off to dist/mcp-server.js, and crashes with
a confusing `ERR_MODULE_NOT_FOUND` after the wrapper has already declared
deps healthy. The reporter on Windows 11 saw exactly this for
better-sqlite3 (folder contained only `deps/` and `LICENSE`).
New module cli/install-check.js exports findMissingDeps(pluginRoot) which
returns the list of runtime-required packages whose package.json is
missing under node_modules. The wrapper now logs the missing packages
before re-running `npm install`, giving the user a useful diagnostic when
the partial-extract case strikes again.
Probing each package's manifest — not just the directory — catches the
specific failure shape that originally motivated the issue. Optional and
OS-specific externals (sharp, fsevents) are deliberately excluded from
the check.
Tests in test/install-check.test.ts cover the no-node_modules case, the
empty-node_modules case, the all-present happy path, the
partial-extraction case (better-sqlite3 dir present but manifest gone),
multi-missing reporting, and the optional-deps-excluded behavior.
Addresses Bug 1 of #95; Bug 2 (onnxruntime-common hoisting) is deferred
pending a Windows reproduction.
The --prefer-offline flag instructs npm to skip the registry when local
metadata exists, even if it's stale. On a fresh plugin install where the
user's npm cache is older than the plugin's better-sqlite3 dependency
range (^12.4.1), npm fails with ETARGET because the cached metadata still
points at older versions, and the MCP server never starts.
Dropping the flag lets npm refresh metadata as needed. The other two
flags (--no-audit --no-fund) are kept; they suppress noise but do not
gate registry access.
Fixes#76
Forward terminal hangup signal to child process, and detect parent
death via stdin close to catch kill -9 and crashes.
Fixes#53
Co-authored-by: Zohaib Shahab <57931096+shahabzohaib@users.noreply.github.com>
Stop shipping package-lock.json (was tracked despite .gitignore) and
remove the code that deleted it on first run. This was a sledgehammer
fix for cross-platform optional dependencies - npm handles this fine
without a lockfile present.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Route all CLI commands through dist/*.js instead of npx tsx src/*.ts
- Fixes background sync silently failing (#25 root cause)
- Faster startup, lighter runtime dependencies
- tsx is now dev-only
This properly fixes the issue @stromseth identified in PR #25 by
addressing the root cause rather than just the sync command.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Convert all CLI entry points (episodic-memory, index-conversations,
search-conversations, mcp-server) from bash to Node.js
- Add search-conversations.js to complete Node.js CLI coverage
- Update SessionStart hook to call node directly
- Eliminates bash dependency for full cross-platform support
(Windows, NixOS, etc.)
Obsoletes PRs #29, #17, #11 which all modified bash scripts.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Fix: Add Windows compatibility for spawn() in runCommand()
Fixes spawn ENOENT error on Windows when running npx commands.
On Windows, npx is a .cmd file, not an executable. The spawn() function
requires shell:true to execute .cmd files, otherwise it fails with ENOENT.
This fix adds shell: process.platform === 'win32' to the spawn options,
matching the pattern already used in mcp-server-wrapper.js:43.
Before this fix:
- spawn('npx', ...) → Error: spawn npx ENOENT
- Plugin fails to initialize on Windows
- SessionStart hook fails silently
After this fix:
- spawn('npx', ..., {shell: true}) → Works correctly
- Plugin initializes and syncs successfully
- All features work as expected on Windows
Tested on:
- Windows 10/11
- Node.js v22.14.0
- Verified sync command executes successfully
* Fix: Apply Windows spawn fix to index-conversations.js
Addresses CodeRabbit's review feedback - the same Windows spawn issue
affects index-conversations.js.
This file also spawns npx without the shell option, causing the same
ENOENT error on Windows. Applied the identical fix for consistency.
Changes:
- Added shell: process.platform === 'win32' to spawn() options in runTsxCommand()
- Matches the pattern in episodic-memory.js and mcp-server-wrapper.js
This ensures all npx spawn calls work correctly on Windows.
The bash script cli/mcp-server-wrapper is no longer needed since we now use
the cross-platform Node.js version cli/mcp-server-wrapper.js for Windows
compatibility.
This eliminates dead code and prevents confusion about which wrapper to use.
All MCP server functionality now goes through the Node.js wrapper.
This release resolves Windows compatibility issues with the MCP server by replacing
the bash script wrapper with a cross-platform Node.js implementation.
### Key Changes:
- Replace bash script `mcp-server-wrapper` with Node.js version `mcp-server-wrapper.js`
- Update plugin.json to use `node cli/mcp-server-wrapper.js` instead of bash script
- Add Windows-compatible npm command detection (npm.cmd vs npm)
- Improve signal forwarding and error handling in wrapper
### Files Updated:
- Created: cli/mcp-server-wrapper.js (cross-platform Node.js wrapper)
- Modified: .claude-plugin/plugin.json (MCP server command)
- Updated: CHANGELOG.md, package.json, marketplace.json files
- Version: 1.0.7 → 1.0.8
### Windows Support:
- MCP server now works with Claude Code native install on Windows
- Resolves "/bin/bash: No such file or directory" errors
- Proper dependency installation across all platforms
Closes#7
Fix MCP server startup error by ensuring platform-specific sqlite-vec
packages are installed correctly. The issue was that npm would use an
existing package-lock.json that didn't include the platform-specific
optional dependencies.
Changes:
- Wrapper script now deletes package-lock.json before npm install
- Add package-lock.json to .gitignore to prevent cross-platform issues
- Bump version to 1.0.5
- Update CHANGELOG.md
Testing:
- Reproduced error by removing sqlite-vec-darwin-arm64 package
- Confirmed fix: deleting package-lock.json ensures correct installation
- All 71 tests pass
Fixes: Error "Loadable extension for sqlite-vec not found. Was the
sqlite-vec-darwin-arm64 package installed?"
Fix MCP server startup errors by implementing a wrapper script that
automatically runs npm install before starting the server. This solves
the "Cannot find module" errors that occurred because MCP servers start
before SessionStart hooks can run.
Changes:
- Add cli/mcp-server-wrapper that checks for node_modules and runs npm install
- Update plugin.json to use wrapper script instead of direct node execution
- Add esbuild bundling to reduce dependency size
- Remove cli/ensure-dependencies (no longer needed)
- Remove ensure-dependencies from SessionStart hook
- Bump version to 1.0.3 in package.json, plugin.json, marketplace.json
- Update CHANGELOG.md
Fixes: Error [ERR_MODULE_NOT_FOUND]: Cannot find package '@modelcontextprotocol/sdk'
Add automatic npm install on plugin installation via SessionStart hook.
This ensures dependencies are available after plugin install without
requiring manual intervention.
Changes:
- Add ensure-dependencies script to check and install npm packages
- Update SessionStart hook to run dependency check before sync
- Bump version to 1.0.1 in package.json and plugin.json
- Add CHANGELOG.md to track releases
Fixes the issue where plugin dependencies were not automatically
installed after running /plugin install.
- Created MCP server (src/mcp-server.ts) with 2 tools:
- episodic_memory_search: Unified single/multi-concept search
- episodic_memory_show: Display full conversations in markdown
- Added Claude Code plugin structure (.claude-plugin/, hooks/, commands/)
- Implemented resume-based summarization using SDK resume option
- Updated sync to generate summaries for all conversations (max 10/run)
- Consolidated plugin config to top level, removed old claude-code-plugin/ dir
- Added MCP dependencies (@modelcontextprotocol/sdk, zod)
- SessionEnd hook automatically syncs and indexes conversations
- Improved summarization prompt with context about search usage
Implements episodic-memory sync to copy and index new conversations atomically.
Features:
- Copies .jsonl files from ~/.claude/projects to archive
- Only copies new or modified files (mtime check)
- Generates embeddings and indexes copied files
- Idempotent - safe to run repeatedly
- No explicit locking - uses atomic operations + SQLite WAL
Race condition handling:
- Atomic file copy (temp + rename)
- SQLite WAL mode for concurrent database writes
- mtime checks prevent duplicate copies
- last_indexed prevents duplicate indexing
Designed for Claude Code session-end hooks to automatically index
conversations without blocking or causing conflicts.
Tests: 66/66 passing including 5 new sync tests
- Implements episodic-memory stats to show index statistics
- Displays conversation counts, date ranges, project breakdowns
- Shows summary coverage (important for search result quality)
- Fixes unified CLI to resolve symlinks properly for npm link
- Updates search routing to use TypeScript directly
Stats output includes:
- Total conversations and exchanges
- Summary coverage percentage
- Date range (earliest to latest)
- Top 10 projects by conversation count
Implements episodic-memory show command to display conversations in human-readable formats.
New features:
- Unified CLI: episodic-memory <command> (index, search, show)
- Show command with markdown and HTML output formats
- Markdown rendering for rich assistant responses
- Sidechain grouping with visual indicators
- Inline tool results paired with tool calls
- Linkable message anchors
- Compact token usage display
- Role labels (User/Agent in main, Agent/Subagent in sidechains)
- Clean Apple-inspired design for HTML output
- Filters out system messages (file-history-snapshot, etc.)
The show command makes conversations readable in terminal (markdown) or browser (HTML),
preserving all conversation details including tool calls, results, and parallel execution.