Compare commits

...

11 Commits

Author SHA1 Message Date
Alex Newman 4c1f7b4ff1 Release v5.2.3: Added troubleshooting skill
Improvements:
- Added troubleshooting slash command skill for diagnosing claude-mem installation issues
- Comprehensive diagnostic workflow covering PM2, worker health, database, dependencies, logs, and viewer UI
- Automated fix sequences and common issue resolutions
- Full system diagnostic report generation

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-08 19:29:26 -05:00
claude[bot] 35b98c53f0 Remove unnecessary troubleshooting checks from skill
Simplified troubleshooting diagnostics by removing checks that are:
- Too brittle (300+ packages requirement)
- Not relevant to v5.2.2 issues (version cache)
- Redundant (Node.js version checks)

Changes:
- Removed "300+ packages installed" expectation
- Removed entire "Step 5: Check Version Cache" section
- Removed dependency count check (ls node_modules/ | wc -l)
- Removed Node.js version checks from multiple sections
- Renumbered steps from 8 to 7

Result: More focused diagnostics for reported issues (memory persistence,
viewer state, observation freshness) without false positives.

Co-authored-by: Alex Newman <thedotmack@users.noreply.github.com>
2025-11-08 23:21:52 +00:00
copilot-swe-agent[bot] e029903a66 Add troubleshooting slash command skill
Co-authored-by: thedotmack <683968+thedotmack@users.noreply.github.com>
2025-11-08 21:47:03 +00:00
copilot-swe-agent[bot] 5666f7dd1c Initial exploration and planning for troubleshooting slash command
Co-authored-by: thedotmack <683968+thedotmack@users.noreply.github.com>
2025-11-08 21:43:06 +00:00
copilot-swe-agent[bot] 57f0c9fd92 Initial plan 2025-11-08 21:39:44 +00:00
Alex Newman 5482052c16 Release v5.2.2: Context hook now displays investigated and learned fields
Improvements:
- Context hook now displays 'investigated' and 'learned' fields from session summaries
- Enhanced SQL query to SELECT these fields from database
- Added color-coded display formatting (blue for investigated, yellow for learned)
- Updated TypeScript types to include nullable investigated and learned fields

Technical changes:
- Updated src/hooks/context-hook.ts to query and display new fields
- Updated built plugin/scripts/context-hook.js
- Bumped version to 5.2.2 in all metadata files

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-08 12:55:53 -05:00
Alex Newman ccb7001ff0 fix: update README to enhance image linking and improve layout consistency 2025-11-07 22:02:17 -05:00
Alex Newman edc20a12a1 fix: remove unnecessary horizontal rule from README for cleaner layout 2025-11-07 22:00:49 -05:00
Alex Newman 45c35c6bdd fix: adjust spacing and alignment in README for improved readability 2025-11-07 22:00:28 -05:00
Alex Newman 47ee0838e3 fix: update README to correct image placement for Claude-Mem preview 2025-11-07 21:52:14 -05:00
Alex Newman 809c9e6639 Implement code changes to enhance functionality and improve performance 2025-11-07 21:51:04 -05:00
11 changed files with 463 additions and 27 deletions
+1 -1
View File
@@ -10,7 +10,7 @@
"plugins": [ "plugins": [
{ {
"name": "claude-mem", "name": "claude-mem",
"version": "5.2.1", "version": "5.2.3",
"source": "./plugin", "source": "./plugin",
"description": "Persistent memory system for Claude Code - context compression across sessions" "description": "Persistent memory system for Claude Code - context compression across sessions"
} }
+363
View File
@@ -0,0 +1,363 @@
---
name: troubleshoot
description: Diagnose and fix claude-mem installation issues. Checks PM2 worker status, database integrity, service health, dependencies, and provides automated fixes for common problems.
---
# Claude-Mem Troubleshooting Skill
This skill diagnoses and resolves common installation and operational issues with the claude-mem plugin.
## Quick Reference
**Common Issues:**
- Memory not persisting after `/clear`
- Viewer UI empty or not loading
- Worker service not running
- Database missing or corrupted
- Port conflicts
- Missing dependencies
## Diagnostic Workflow
When invoked, follow these steps systematically:
### 1. Check PM2 Worker Status
First, verify if the worker service is running:
```bash
# Check if PM2 is available
which pm2 || echo "PM2 not found in PATH"
# List PM2 processes
pm2 jlist 2>&1
# If pm2 is not found, try the local installation
~/.claude/plugins/marketplaces/thedotmack/node_modules/.bin/pm2 jlist 2>&1
```
**Expected output:** JSON array with `claude-mem-worker` process showing `"status": "online"`
**If worker not running or status is not "online":**
```bash
cd ~/.claude/plugins/marketplaces/thedotmack/
pm2 start ecosystem.config.cjs
# Or use local pm2:
node_modules/.bin/pm2 start ecosystem.config.cjs
```
### 2. Check Worker Service Health
Test if the worker service responds to HTTP requests:
```bash
# Default port is 37777
curl -s http://127.0.0.1:37777/health
# Check custom port from settings
PORT=$(cat ~/.claude-mem/settings.json 2>/dev/null | grep CLAUDE_MEM_WORKER_PORT | grep -o '[0-9]\+' || echo "37777")
curl -s http://127.0.0.1:$PORT/health
```
**Expected output:** `{"status":"ok"}`
**If connection refused:**
- Worker not running → Go back to step 1
- Port conflict → Check what's using the port:
```bash
lsof -i :37777 || netstat -tlnp | grep 37777
```
### 3. Check Database
Verify the database exists and contains data:
```bash
# Check if database file exists
ls -lh ~/.claude-mem/claude-mem.db
# Check database size (should be > 0 bytes)
du -h ~/.claude-mem/claude-mem.db
# Query database for observation count (requires sqlite3)
sqlite3 ~/.claude-mem/claude-mem.db "SELECT COUNT(*) as observation_count FROM observations;" 2>&1
# Query for session count
sqlite3 ~/.claude-mem/claude-mem.db "SELECT COUNT(*) as session_count FROM sessions;" 2>&1
# Check recent observations
sqlite3 ~/.claude-mem/claude-mem.db "SELECT created_at, type, title FROM observations ORDER BY created_at DESC LIMIT 5;" 2>&1
```
**Expected:**
- Database file exists (typically 100KB - 10MB+)
- Contains observations and sessions
- Recent observations visible
**If database missing or empty:**
- New installation - this is normal, database will populate as you work
- After `/clear` - sessions are marked complete but not deleted, data should persist
- Corrupted database - backup and recreate:
```bash
cp ~/.claude-mem/claude-mem.db ~/.claude-mem/claude-mem.db.backup
# Worker will recreate on next observation
```
### 4. Check Dependencies Installation
Verify all required npm packages are installed:
```bash
cd ~/.claude/plugins/marketplaces/thedotmack/
# Check for critical packages
ls node_modules/@anthropic-ai/claude-agent-sdk 2>&1 | head -1
ls node_modules/better-sqlite3 2>&1 | head -1
ls node_modules/express 2>&1 | head -1
ls node_modules/pm2 2>&1 | head -1
```
**Expected:** All critical packages present
**If dependencies missing:**
```bash
cd ~/.claude/plugins/marketplaces/thedotmack/
npm install
```
### 5. Check Worker Logs
Review recent worker logs for errors:
```bash
# View last 50 lines of worker logs
pm2 logs claude-mem-worker --lines 50 --nostream
# Or use local pm2:
cd ~/.claude/plugins/marketplaces/thedotmack/
node_modules/.bin/pm2 logs claude-mem-worker --lines 50 --nostream
# Check for specific errors
pm2 logs claude-mem-worker --lines 100 --nostream | grep -i "error\|exception\|failed"
```
### 6. Test Viewer UI
Check if the web viewer is accessible:
```bash
# Test viewer endpoint
curl -s http://127.0.0.1:37777/ | head -20
# Test stats endpoint
curl -s http://127.0.0.1:37777/api/stats
```
**Expected:**
- `/` returns HTML page with React viewer
- `/api/stats` returns JSON with database counts
### 7. Check Port Configuration
Verify port settings and availability:
```bash
# Check if custom port is configured
cat ~/.claude-mem/settings.json 2>/dev/null
cat ~/.claude/settings.json 2>/dev/null
# Check what's listening on default port
lsof -i :37777 2>&1 || netstat -tlnp 2>&1 | grep 37777
# Test connectivity
nc -zv 127.0.0.1 37777 2>&1
```
## Automated Fix Sequence
If you're seeing issues, try this automated fix sequence:
```bash
# 1. Stop the worker
pm2 delete claude-mem-worker 2>/dev/null || true
# 2. Navigate to plugin directory
cd ~/.claude/plugins/marketplaces/thedotmack/
# 3. Ensure dependencies are installed
npm install
# 4. Start worker with local pm2
node_modules/.bin/pm2 start ecosystem.config.cjs
# 5. Wait for health check
sleep 3
curl -s http://127.0.0.1:37777/health
# 6. Check logs for any errors
node_modules/.bin/pm2 logs claude-mem-worker --lines 20 --nostream
```
## Common Issue Resolutions
### Issue: "Nothing is remembered after /clear"
**Root cause:** Sessions are marked complete but data should persist. This suggests:
- Worker not processing observations
- Database not being written to
- Context hook not reading from database
**Fix:**
1. Verify worker is running (Step 1)
2. Check database has recent observations (Step 3)
3. Restart worker and start new session
4. Create a test observation: `/skill version-bump` then cancel
5. Check if observation appears in viewer: http://127.0.0.1:37777
### Issue: "Viewer empty after every Claude restart"
**Root cause:**
- Database being recreated on startup (shouldn't happen)
- Worker reading from wrong database location
- Database permissions issue
**Fix:**
1. Check database file exists and has data (Step 3)
2. Check file permissions:
```bash
ls -la ~/.claude-mem/claude-mem.db
# Should be readable/writable by your user
```
3. Verify worker is using correct database path in logs
4. Test viewer connection manually
### Issue: "Old memory in Claude"
**Root cause:** Context hook injecting stale observations
**Fix:**
1. Check the observation count setting:
```bash
grep CLAUDE_MEM_CONTEXT_OBSERVATIONS ~/.claude/settings.json
```
2. Default is 50 observations - you can adjust this
3. Check database for actual observation dates:
```bash
sqlite3 ~/.claude-mem/claude-mem.db "SELECT created_at, project, title FROM observations ORDER BY created_at DESC LIMIT 10;"
```
### Issue: "Worker not starting"
**Root cause:**
- Port already in use
- PM2 not installed or not in PATH
- Missing dependencies
**Fix:**
1. Try manual worker start:
```bash
cd ~/.claude/plugins/marketplaces/thedotmack/
node plugin/scripts/worker-service.cjs
# Should start server on port 37777
```
2. If port in use, change it:
```bash
echo '{"env":{"CLAUDE_MEM_WORKER_PORT":"37778"}}' > ~/.claude-mem/settings.json
```
## Full System Diagnosis
Run this comprehensive diagnostic script:
```bash
#!/bin/bash
echo "=== Claude-Mem Troubleshooting Report ==="
echo ""
echo "1. Environment"
echo " OS: $(uname -s)"
echo ""
echo "2. Plugin Installation"
echo " Plugin directory exists: $([ -d ~/.claude/plugins/marketplaces/thedotmack ] && echo 'YES' || echo 'NO')"
echo " Package version: $(grep '"version"' ~/.claude/plugins/marketplaces/thedotmack/package.json 2>/dev/null | head -1)"
echo ""
echo "3. Database"
echo " Database exists: $([ -f ~/.claude-mem/claude-mem.db ] && echo 'YES' || echo 'NO')"
echo " Database size: $(du -h ~/.claude-mem/claude-mem.db 2>/dev/null | cut -f1)"
echo " Observation count: $(sqlite3 ~/.claude-mem/claude-mem.db 'SELECT COUNT(*) FROM observations;' 2>/dev/null || echo 'N/A')"
echo " Session count: $(sqlite3 ~/.claude-mem/claude-mem.db 'SELECT COUNT(*) FROM sessions;' 2>/dev/null || echo 'N/A')"
echo ""
echo "4. Worker Service"
PM2_PATH=$(which pm2 2>/dev/null || echo "~/.claude/plugins/marketplaces/thedotmack/node_modules/.bin/pm2")
echo " PM2 path: $PM2_PATH"
WORKER_STATUS=$($PM2_PATH jlist 2>/dev/null | grep -o '"name":"claude-mem-worker".*"status":"[^"]*"' | grep -o 'status":"[^"]*"' | cut -d'"' -f3 || echo 'not running')
echo " Worker status: $WORKER_STATUS"
echo " Health check: $(curl -s http://127.0.0.1:37777/health 2>/dev/null || echo 'FAILED')"
echo ""
echo "5. Configuration"
echo " Port setting: $(cat ~/.claude-mem/settings.json 2>/dev/null | grep CLAUDE_MEM_WORKER_PORT || echo 'default (37777)')"
echo " Observation count: $(cat ~/.claude/settings.json 2>/dev/null | grep CLAUDE_MEM_CONTEXT_OBSERVATIONS || echo 'default (50)')"
echo ""
echo "6. Recent Activity"
echo " Latest observation: $(sqlite3 ~/.claude-mem/claude-mem.db 'SELECT created_at FROM observations ORDER BY created_at DESC LIMIT 1;' 2>/dev/null || echo 'N/A')"
echo " Latest session: $(sqlite3 ~/.claude-mem/claude-mem.db 'SELECT created_at FROM sessions ORDER BY created_at DESC LIMIT 1;' 2>/dev/null || echo 'N/A')"
echo ""
echo "=== End Report ==="
```
Save this as `/tmp/claude-mem-diagnostics.sh` and run:
```bash
bash /tmp/claude-mem-diagnostics.sh
```
## Reporting Issues
If troubleshooting doesn't resolve the issue, collect this information for a bug report:
1. Full diagnostic report (run script above)
2. Worker logs: `pm2 logs claude-mem-worker --lines 100 --nostream`
3. Your setup:
- Claude version: Check with Claude
- OS: `uname -a`
- Node version: `node --version`
- Plugin version: In package.json
4. Steps to reproduce the issue
5. Expected vs actual behavior
Post to: https://github.com/thedotmack/claude-mem/issues
## Prevention Tips
**Keep claude-mem healthy:**
- Regularly check viewer UI to see if observations are being captured
- Monitor database size (shouldn't grow unbounded)
- Update plugin when new versions are released
- Keep Claude Code updated
**Performance tuning:**
- Adjust `CLAUDE_MEM_CONTEXT_OBSERVATIONS` if context is too large/small
- Use `/clear` to mark sessions complete and start fresh
- Use MCP search tools to query specific memories instead of loading everything
## Quick Commands Reference
```bash
# Restart worker
pm2 restart claude-mem-worker
# View logs
pm2 logs claude-mem-worker
# Check health
curl http://127.0.0.1:37777/health
# View database stats
curl http://127.0.0.1:37777/api/stats
# Open viewer
open http://127.0.0.1:37777
# Delete and reinstall worker
pm2 delete claude-mem-worker
cd ~/.claude/plugins/marketplaces/thedotmack/
pm2 start ecosystem.config.cjs
```
+1 -1
View File
@@ -6,7 +6,7 @@ Claude-mem is a Claude Code plugin providing persistent memory across sessions.
**Your Role**: You are working on the plugin itself. When users interact with Claude Code with this plugin installed, your observations get captured and become their persistent memory. **Your Role**: You are working on the plugin itself. When users interact with Claude Code with this plugin installed, your observations get captured and become their persistent memory.
**Current Version**: 5.2.1 **Current Version**: 5.2.3
## Critical Architecture Knowledge ## Critical Architecture Knowledge
+39
View File
@@ -27,6 +27,16 @@
</a> </a>
</p> </p>
<br>
<p align="center">
<a href="https://github.com/thedotmack/claude-mem">
<picture>
<img src="docs/cm-preview.gif" alt="Claude-Mem Preview" width="800">
</picture>
</a>
</p>
<p align="center"> <p align="center">
<a href="#quick-start">Quick Start</a> • <a href="#quick-start">Quick Start</a> •
<a href="#how-it-works">How It Works</a> • <a href="#how-it-works">How It Works</a> •
@@ -56,6 +66,7 @@ Start a new Claude Code session in the terminal and enter the following commands
Restart Claude Code. Context from previous sessions will automatically appear in new sessions. Restart Claude Code. Context from previous sessions will automatically appear in new sessions.
**Key Features:** **Key Features:**
- 🧠 **Persistent Memory** - Context survives across sessions - 🧠 **Persistent Memory** - Context survives across sessions
- 📊 **Progressive Disclosure** - Layered memory retrieval with token cost visibility - 📊 **Progressive Disclosure** - Layered memory retrieval with token cost visibility
- 🔍 **9 Search Tools** - Query your project history via MCP - 🔍 **9 Search Tools** - Query your project history via MCP
@@ -70,21 +81,25 @@ Restart Claude Code. Context from previous sessions will automatically appear in
📚 **[View Full Documentation](docs/)** - Browse markdown docs on GitHub 📚 **[View Full Documentation](docs/)** - Browse markdown docs on GitHub
💻 **Local Preview**: Run Mintlify docs locally: 💻 **Local Preview**: Run Mintlify docs locally:
```bash ```bash
cd docs cd docs
npx mintlify dev npx mintlify dev
``` ```
### Getting Started ### Getting Started
- **[Installation Guide](docs/installation.mdx)** - Quick start & advanced installation - **[Installation Guide](docs/installation.mdx)** - Quick start & advanced installation
- **[Usage Guide](docs/usage/getting-started.mdx)** - How Claude-Mem works automatically - **[Usage Guide](docs/usage/getting-started.mdx)** - How Claude-Mem works automatically
- **[MCP Search Tools](docs/usage/search-tools.mdx)** - Query your project history - **[MCP Search Tools](docs/usage/search-tools.mdx)** - Query your project history
### Best Practices ### Best Practices
- **[Context Engineering](docs/context-engineering.mdx)** - AI agent context optimization principles - **[Context Engineering](docs/context-engineering.mdx)** - AI agent context optimization principles
- **[Progressive Disclosure](docs/progressive-disclosure.mdx)** - Philosophy behind Claude-Mem's context priming strategy - **[Progressive Disclosure](docs/progressive-disclosure.mdx)** - Philosophy behind Claude-Mem's context priming strategy
### Architecture ### Architecture
- **[Overview](docs/architecture/overview.mdx)** - System components & data flow - **[Overview](docs/architecture/overview.mdx)** - System components & data flow
- **[Architecture Evolution](docs/architecture-evolution.mdx)** - The journey from v3 to v5 - **[Architecture Evolution](docs/architecture-evolution.mdx)** - The journey from v3 to v5
- **[Hooks Architecture](docs/hooks-architecture.mdx)** - How Claude-Mem uses lifecycle hooks - **[Hooks Architecture](docs/hooks-architecture.mdx)** - How Claude-Mem uses lifecycle hooks
@@ -95,6 +110,7 @@ npx mintlify dev
- **[Viewer UI](docs/VIEWER.md)** - Web-based memory stream visualization - **[Viewer UI](docs/VIEWER.md)** - Web-based memory stream visualization
### Configuration & Development ### Configuration & Development
- **[Configuration](docs/configuration.mdx)** - Environment variables & settings - **[Configuration](docs/configuration.mdx)** - Environment variables & settings
- **[Development](docs/development.mdx)** - Building, testing, contributing - **[Development](docs/development.mdx)** - Building, testing, contributing
- **[Troubleshooting](docs/troubleshooting.mdx)** - Common issues & solutions - **[Troubleshooting](docs/troubleshooting.mdx)** - Common issues & solutions
@@ -126,6 +142,7 @@ npx mintlify dev
``` ```
**Core Components:** **Core Components:**
1. **7 Lifecycle Hook Scripts** - smart-install, context-hook, user-message-hook, new-hook, save-hook, summary-hook, cleanup-hook 1. **7 Lifecycle Hook Scripts** - smart-install, context-hook, user-message-hook, new-hook, save-hook, summary-hook, cleanup-hook
2. **Worker Service** - HTTP API on port 37777 with web viewer UI, managed by PM2 2. **Worker Service** - HTTP API on port 37777 with web viewer UI, managed by PM2
3. **SQLite Database** - Stores sessions, observations, summaries with FTS5 full-text search 3. **SQLite Database** - Stores sessions, observations, summaries with FTS5 full-text search
@@ -151,6 +168,7 @@ Claude-Mem provides 9 specialized search tools:
9. **get_timeline_by_query** - Search for observations and get timeline context around best match 9. **get_timeline_by_query** - Search for observations and get timeline context around best match
**Example Queries:** **Example Queries:**
``` ```
search_observations with query="authentication" and type="decision" search_observations with query="authentication" and type="decision"
find_by_file with filePath="worker-service.ts" find_by_file with filePath="worker-service.ts"
@@ -167,12 +185,14 @@ See [MCP Search Tools Guide](docs/usage/search-tools.mdx) for detailed examples.
## What's New in v5.1.2 ## What's New in v5.1.2
**🎨 Theme Toggle (v5.1.2):** **🎨 Theme Toggle (v5.1.2):**
- Light/dark mode support in viewer UI - Light/dark mode support in viewer UI
- System preference detection - System preference detection
- Persistent theme settings across sessions - Persistent theme settings across sessions
- Smooth transitions between themes - Smooth transitions between themes
**🖥️ Web-Based Viewer UI (v5.1.0):** **🖥️ Web-Based Viewer UI (v5.1.0):**
- Real-time memory stream visualization at http://localhost:37777 - Real-time memory stream visualization at http://localhost:37777
- Server-Sent Events (SSE) for instant updates - Server-Sent Events (SSE) for instant updates
- Infinite scroll pagination with automatic deduplication - Infinite scroll pagination with automatic deduplication
@@ -181,11 +201,13 @@ See [MCP Search Tools Guide](docs/usage/search-tools.mdx) for detailed examples.
- Auto-reconnection with exponential backoff - Auto-reconnection with exponential backoff
**⚡ Smart Install Caching (v5.0.3):** **⚡ Smart Install Caching (v5.0.3):**
- Eliminated redundant npm installs on every session (2-5s → 10ms) - Eliminated redundant npm installs on every session (2-5s → 10ms)
- Caches version in `.install-version` file - Caches version in `.install-version` file
- Only runs npm install when needed (first time, version change, missing deps) - Only runs npm install when needed (first time, version change, missing deps)
**🔍 Hybrid Search Architecture (v5.0.0):** **🔍 Hybrid Search Architecture (v5.0.0):**
- Chroma vector database for semantic search - Chroma vector database for semantic search
- Combined with FTS5 keyword search - Combined with FTS5 keyword search
- Intelligent context retrieval with 90-day recency filtering - Intelligent context retrieval with 90-day recency filtering
@@ -206,6 +228,7 @@ See [CHANGELOG.md](CHANGELOG.md) for complete version history.
## Key Benefits ## Key Benefits
### Progressive Disclosure Context ### Progressive Disclosure Context
- **Layered memory retrieval** mirrors human memory patterns - **Layered memory retrieval** mirrors human memory patterns
- **Layer 1 (Index)**: See what observations exist with token costs at session start - **Layer 1 (Index)**: See what observations exist with token costs at session start
- **Layer 2 (Details)**: Fetch full narratives on-demand via MCP search - **Layer 2 (Details)**: Fetch full narratives on-demand via MCP search
@@ -214,21 +237,25 @@ See [CHANGELOG.md](CHANGELOG.md) for complete version history.
- **Type indicators**: Visual cues (🔴 critical, 🟤 decision, 🔵 informational) highlight observation importance - **Type indicators**: Visual cues (🔴 critical, 🟤 decision, 🔵 informational) highlight observation importance
### Automatic Memory ### Automatic Memory
- Context automatically injected when Claude starts - Context automatically injected when Claude starts
- No manual commands or configuration needed - No manual commands or configuration needed
- Works transparently in the background - Works transparently in the background
### Full History Search ### Full History Search
- Search across all sessions and observations - Search across all sessions and observations
- FTS5 full-text search for fast queries - FTS5 full-text search for fast queries
- Citations link back to specific observations - Citations link back to specific observations
### Structured Observations ### Structured Observations
- AI-powered extraction of learnings - AI-powered extraction of learnings
- Categorized by type (decision, bugfix, feature, etc.) - Categorized by type (decision, bugfix, feature, etc.)
- Tagged with concepts and file references - Tagged with concepts and file references
### Multi-Prompt Sessions ### Multi-Prompt Sessions
- Sessions span multiple user prompts - Sessions span multiple user prompts
- Context preserved across `/clear` commands - Context preserved across `/clear` commands
- Track entire conversation threads - Track entire conversation threads
@@ -238,11 +265,13 @@ See [CHANGELOG.md](CHANGELOG.md) for complete version history.
## Configuration ## Configuration
**Model Selection:** **Model Selection:**
```bash ```bash
./claude-mem-settings.sh ./claude-mem-settings.sh
``` ```
**Environment Variables:** **Environment Variables:**
- `CLAUDE_MEM_MODEL` - AI model for processing (default: claude-sonnet-4-5) - `CLAUDE_MEM_MODEL` - AI model for processing (default: claude-sonnet-4-5)
- `CLAUDE_MEM_WORKER_PORT` - Worker port (default: 37777) - `CLAUDE_MEM_WORKER_PORT` - Worker port (default: 37777)
- `CLAUDE_MEM_DATA_DIR` - Data directory override (dev only) - `CLAUDE_MEM_DATA_DIR` - Data directory override (dev only)
@@ -276,7 +305,16 @@ See [Development Guide](docs/development.mdx) for detailed instructions.
## Troubleshooting ## Troubleshooting
**Quick Diagnostic:**
Run the troubleshooting skill for automated diagnosis and fixes:
```
/skill troubleshoot
```
**Common Issues:** **Common Issues:**
- Worker not starting → `npm run worker:restart` - Worker not starting → `npm run worker:restart`
- No context appearing → `npm run test:context` - No context appearing → `npm run test:context`
- Database issues → `sqlite3 ~/.claude-mem/claude-mem.db "PRAGMA integrity_check;"` - Database issues → `sqlite3 ~/.claude-mem/claude-mem.db "PRAGMA integrity_check;"`
@@ -309,6 +347,7 @@ Copyright (C) 2025 Alex Newman (@thedotmack). All rights reserved.
See the [LICENSE](LICENSE) file for full details. See the [LICENSE](LICENSE) file for full details.
**What This Means:** **What This Means:**
- You can use, modify, and distribute this software freely - You can use, modify, and distribute this software freely
- If you modify and deploy on a network server, you must make your source code available - If you modify and deploy on a network server, you must make your source code available
- Derivative works must also be licensed under AGPL-3.0 - Derivative works must also be licensed under AGPL-3.0
Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 MiB

+20
View File
@@ -5,6 +5,26 @@ description: "Common issues and solutions for Claude-Mem"
# Troubleshooting Guide # Troubleshooting Guide
## Quick Diagnostic Tool
**NEW:** Use the automated troubleshooting skill for instant diagnosis:
```
/skill troubleshoot
```
This skill will:
- ✅ Check PM2 worker status and health
- ✅ Verify database existence and integrity
- ✅ Test worker service connectivity
- ✅ Validate dependencies installation
- ✅ Check port configuration and availability
- ✅ Provide automated fixes for common issues
The skill includes comprehensive diagnostics, automated repair sequences, and detailed troubleshooting workflows for all common issues. Use it before manually troubleshooting below.
---
## v5.x Specific Issues ## v5.x Specific Issues
### Viewer UI Not Loading ### Viewer UI Not Loading
+2 -6
View File
@@ -1,12 +1,12 @@
{ {
"name": "claude-mem", "name": "claude-mem",
"version": "5.1.4", "version": "5.2.2",
"lockfileVersion": 3, "lockfileVersion": 3,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "claude-mem", "name": "claude-mem",
"version": "5.1.4", "version": "5.2.2",
"license": "AGPL-3.0", "license": "AGPL-3.0",
"dependencies": { "dependencies": {
"@anthropic-ai/claude-agent-sdk": "^0.1.27", "@anthropic-ai/claude-agent-sdk": "^0.1.27",
@@ -1484,7 +1484,6 @@
"integrity": "sha512-RFA/bURkcKzx/X9oumPG9Vp3D3JUgus/d0b67KB0t5S/raciymilkOa66olh78MUI92QLbEJevO7rvqU/kjwKA==", "integrity": "sha512-RFA/bURkcKzx/X9oumPG9Vp3D3JUgus/d0b67KB0t5S/raciymilkOa66olh78MUI92QLbEJevO7rvqU/kjwKA==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"peer": true,
"dependencies": { "dependencies": {
"@types/prop-types": "*", "@types/prop-types": "*",
"csstype": "^3.0.2" "csstype": "^3.0.2"
@@ -2338,7 +2337,6 @@
"resolved": "https://registry.npmjs.org/express/-/express-4.21.2.tgz", "resolved": "https://registry.npmjs.org/express/-/express-4.21.2.tgz",
"integrity": "sha512-28HqgMZAmih1Czt9ny7qr6ek2qddF4FclbMzwhCREB6OFfH+rXAnuNCwo1/wFvrtbgsQDb4kSbX9de9lFbrXnA==", "integrity": "sha512-28HqgMZAmih1Czt9ny7qr6ek2qddF4FclbMzwhCREB6OFfH+rXAnuNCwo1/wFvrtbgsQDb4kSbX9de9lFbrXnA==",
"license": "MIT", "license": "MIT",
"peer": true,
"dependencies": { "dependencies": {
"accepts": "~1.3.8", "accepts": "~1.3.8",
"array-flatten": "1.1.1", "array-flatten": "1.1.1",
@@ -3851,7 +3849,6 @@
"resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz", "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz",
"integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==", "integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==",
"license": "MIT", "license": "MIT",
"peer": true,
"dependencies": { "dependencies": {
"loose-envify": "^1.1.0" "loose-envify": "^1.1.0"
}, },
@@ -4853,7 +4850,6 @@
"node_modules/zod": { "node_modules/zod": {
"version": "3.25.76", "version": "3.25.76",
"license": "MIT", "license": "MIT",
"peer": true,
"funding": { "funding": {
"url": "https://github.com/sponsors/colinhacks" "url": "https://github.com/sponsors/colinhacks"
} }
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "claude-mem", "name": "claude-mem",
"version": "5.2.1", "version": "5.2.3",
"description": "Memory compression system for Claude Code - persist context across sessions", "description": "Memory compression system for Claude Code - persist context across sessions",
"keywords": [ "keywords": [
"claude", "claude",
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "claude-mem", "name": "claude-mem",
"version": "5.2.1", "version": "5.2.3",
"description": "Persistent memory system for Claude Code - seamlessly preserve context across sessions", "description": "Persistent memory system for Claude Code - seamlessly preserve context across sessions",
"author": { "author": {
"name": "Alex Newman" "name": "Alex Newman"
+14 -14
View File
@@ -1,7 +1,7 @@
#!/usr/bin/env node #!/usr/bin/env node
import X from"path";import{stdin as w}from"process";import se from"better-sqlite3";import{join as S,dirname as Q,basename as _e}from"path";import{homedir as j}from"os";import{existsSync as Ee,mkdirSync as z}from"fs";import{fileURLToPath as Z}from"url";function ee(){return typeof __dirname<"u"?__dirname:Q(Z(import.meta.url))}var ge=ee(),I=process.env.CLAUDE_MEM_DATA_DIR||S(j(),".claude-mem"),$=process.env.CLAUDE_CONFIG_DIR||S(j(),".claude"),he=S(I,"archives"),be=S(I,"logs"),Se=S(I,"trash"),Re=S(I,"backups"),fe=S(I,"settings.json"),P=S(I,"claude-mem.db"),Ne=S(I,"vector-db"),Oe=S($,"settings.json"),Ie=S($,"commands"),Le=S($,"CLAUDE.md");function H(p){z(p,{recursive:!0})}var U=(i=>(i[i.DEBUG=0]="DEBUG",i[i.INFO=1]="INFO",i[i.WARN=2]="WARN",i[i.ERROR=3]="ERROR",i[i.SILENT=4]="SILENT",i))(U||{}),M=class{level;useColor;constructor(){let e=process.env.CLAUDE_MEM_LOG_LEVEL?.toUpperCase()||"INFO";this.level=U[e]??1,this.useColor=process.stdout.isTTY??!1}correlationId(e,s){return`obs-${e}-${s}`}sessionId(e){return`session-${e}`}formatData(e){if(e==null)return"";if(typeof e=="string")return e;if(typeof e=="number"||typeof e=="boolean")return e.toString();if(typeof e=="object"){if(e instanceof Error)return this.level===0?`${e.message} import X from"path";import{stdin as w}from"process";import se from"better-sqlite3";import{join as f,dirname as Q,basename as _e}from"path";import{homedir as j}from"os";import{existsSync as Ee,mkdirSync as z}from"fs";import{fileURLToPath as Z}from"url";function ee(){return typeof __dirname<"u"?__dirname:Q(Z(import.meta.url))}var ge=ee(),I=process.env.CLAUDE_MEM_DATA_DIR||f(j(),".claude-mem"),$=process.env.CLAUDE_CONFIG_DIR||f(j(),".claude"),he=f(I,"archives"),be=f(I,"logs"),Se=f(I,"trash"),fe=f(I,"backups"),Re=f(I,"settings.json"),P=f(I,"claude-mem.db"),Ne=f(I,"vector-db"),Oe=f($,"settings.json"),Ie=f($,"commands"),Le=f($,"CLAUDE.md");function H(p){z(p,{recursive:!0})}var U=(i=>(i[i.DEBUG=0]="DEBUG",i[i.INFO=1]="INFO",i[i.WARN=2]="WARN",i[i.ERROR=3]="ERROR",i[i.SILENT=4]="SILENT",i))(U||{}),M=class{level;useColor;constructor(){let e=process.env.CLAUDE_MEM_LOG_LEVEL?.toUpperCase()||"INFO";this.level=U[e]??1,this.useColor=process.stdout.isTTY??!1}correlationId(e,s){return`obs-${e}-${s}`}sessionId(e){return`session-${e}`}formatData(e){if(e==null)return"";if(typeof e=="string")return e;if(typeof e=="number"||typeof e=="boolean")return e.toString();if(typeof e=="object"){if(e instanceof Error)return this.level===0?`${e.message}
${e.stack}`:e.message;if(Array.isArray(e))return`[${e.length} items]`;let s=Object.keys(e);return s.length===0?"{}":s.length<=3?JSON.stringify(e):`{${s.length} keys: ${s.slice(0,3).join(", ")}...}`}return String(e)}formatTool(e,s){if(!s)return e;try{let t=typeof s=="string"?JSON.parse(s):s;if(e==="Bash"&&t.command){let r=t.command.length>50?t.command.substring(0,50)+"...":t.command;return`${e}(${r})`}if(e==="Read"&&t.file_path){let r=t.file_path.split("/").pop()||t.file_path;return`${e}(${r})`}if(e==="Edit"&&t.file_path){let r=t.file_path.split("/").pop()||t.file_path;return`${e}(${r})`}if(e==="Write"&&t.file_path){let r=t.file_path.split("/").pop()||t.file_path;return`${e}(${r})`}return e}catch{return e}}log(e,s,t,r,i){if(e<this.level)return;let d=new Date().toISOString().replace("T"," ").substring(0,23),a=U[e].padEnd(5),_=s.padEnd(6),E="";r?.correlationId?E=`[${r.correlationId}] `:r?.sessionId&&(E=`[session-${r.sessionId}] `);let b="";i!=null&&(this.level===0&&typeof i=="object"?b=` ${e.stack}`:e.message;if(Array.isArray(e))return`[${e.length} items]`;let s=Object.keys(e);return s.length===0?"{}":s.length<=3?JSON.stringify(e):`{${s.length} keys: ${s.slice(0,3).join(", ")}...}`}return String(e)}formatTool(e,s){if(!s)return e;try{let t=typeof s=="string"?JSON.parse(s):s;if(e==="Bash"&&t.command){let r=t.command.length>50?t.command.substring(0,50)+"...":t.command;return`${e}(${r})`}if(e==="Read"&&t.file_path){let r=t.file_path.split("/").pop()||t.file_path;return`${e}(${r})`}if(e==="Edit"&&t.file_path){let r=t.file_path.split("/").pop()||t.file_path;return`${e}(${r})`}if(e==="Write"&&t.file_path){let r=t.file_path.split("/").pop()||t.file_path;return`${e}(${r})`}return e}catch{return e}}log(e,s,t,r,i){if(e<this.level)return;let d=new Date().toISOString().replace("T"," ").substring(0,23),a=U[e].padEnd(5),_=s.padEnd(6),T="";r?.correlationId?T=`[${r.correlationId}] `:r?.sessionId&&(T=`[session-${r.sessionId}] `);let S="";i!=null&&(this.level===0&&typeof i=="object"?S=`
`+JSON.stringify(i,null,2):b=" "+this.formatData(i));let n="";if(r){let{sessionId:R,sdkSessionId:N,correlationId:l,...c}=r;Object.keys(c).length>0&&(n=` {${Object.entries(c).map(([u,T])=>`${u}=${T}`).join(", ")}}`)}let A=`[${d}] [${a}] [${_}] ${E}${t}${n}${b}`;e===3?console.error(A):console.log(A)}debug(e,s,t,r){this.log(0,e,s,t,r)}info(e,s,t,r){this.log(1,e,s,t,r)}warn(e,s,t,r){this.log(2,e,s,t,r)}error(e,s,t,r){this.log(3,e,s,t,r)}dataIn(e,s,t,r){this.info(e,`\u2192 ${s}`,t,r)}dataOut(e,s,t,r){this.info(e,`\u2190 ${s}`,t,r)}success(e,s,t,r){this.info(e,`\u2713 ${s}`,t,r)}failure(e,s,t,r){this.error(e,`\u2717 ${s}`,t,r)}timing(e,s,t,r){this.info(e,`\u23F1 ${s}`,r,{duration:`${t}ms`})}},G=new M;var D=class{db;constructor(){H(I),this.db=new se(P),this.db.pragma("journal_mode = WAL"),this.db.pragma("synchronous = NORMAL"),this.db.pragma("foreign_keys = ON"),this.initializeSchema(),this.ensureWorkerPortColumn(),this.ensurePromptTrackingColumns(),this.removeSessionSummariesUniqueConstraint(),this.addObservationHierarchicalFields(),this.makeObservationsTextNullable(),this.createUserPromptsTable()}initializeSchema(){try{this.db.exec(` `+JSON.stringify(i,null,2):S=" "+this.formatData(i));let n="";if(r){let{sessionId:R,sdkSessionId:N,correlationId:l,...c}=r;Object.keys(c).length>0&&(n=` {${Object.entries(c).map(([u,g])=>`${u}=${g}`).join(", ")}}`)}let v=`[${d}] [${a}] [${_}] ${T}${t}${n}${S}`;e===3?console.error(v):console.log(v)}debug(e,s,t,r){this.log(0,e,s,t,r)}info(e,s,t,r){this.log(1,e,s,t,r)}warn(e,s,t,r){this.log(2,e,s,t,r)}error(e,s,t,r){this.log(3,e,s,t,r)}dataIn(e,s,t,r){this.info(e,`\u2192 ${s}`,t,r)}dataOut(e,s,t,r){this.info(e,`\u2190 ${s}`,t,r)}success(e,s,t,r){this.info(e,`\u2713 ${s}`,t,r)}failure(e,s,t,r){this.error(e,`\u2717 ${s}`,t,r)}timing(e,s,t,r){this.info(e,`\u23F1 ${s}`,r,{duration:`${t}ms`})}},G=new M;var D=class{db;constructor(){H(I),this.db=new se(P),this.db.pragma("journal_mode = WAL"),this.db.pragma("synchronous = NORMAL"),this.db.pragma("foreign_keys = ON"),this.initializeSchema(),this.ensureWorkerPortColumn(),this.ensurePromptTrackingColumns(),this.removeSessionSummariesUniqueConstraint(),this.addObservationHierarchicalFields(),this.makeObservationsTextNullable(),this.createUserPromptsTable()}initializeSchema(){try{this.db.exec(`
CREATE TABLE IF NOT EXISTS schema_versions ( CREATE TABLE IF NOT EXISTS schema_versions (
id INTEGER PRIMARY KEY, id INTEGER PRIMARY KEY,
version INTEGER UNIQUE NOT NULL, version INTEGER UNIQUE NOT NULL,
@@ -318,23 +318,23 @@ ${e.stack}`:e.message;if(Array.isArray(e))return`[${e.length} items]`;let s=Obje
INSERT INTO sdk_sessions INSERT INTO sdk_sessions
(claude_session_id, sdk_session_id, project, started_at, started_at_epoch, status) (claude_session_id, sdk_session_id, project, started_at, started_at_epoch, status)
VALUES (?, ?, ?, ?, ?, 'active') VALUES (?, ?, ?, ?, ?, 'active')
`).run(e,e,s,i.toISOString(),d),console.error(`[SessionStore] Auto-created session record for session_id: ${e}`));let b=this.db.prepare(` `).run(e,e,s,i.toISOString(),d),console.error(`[SessionStore] Auto-created session record for session_id: ${e}`));let S=this.db.prepare(`
INSERT INTO observations INSERT INTO observations
(sdk_session_id, project, type, title, subtitle, facts, narrative, concepts, (sdk_session_id, project, type, title, subtitle, facts, narrative, concepts,
files_read, files_modified, prompt_number, created_at, created_at_epoch) files_read, files_modified, prompt_number, created_at, created_at_epoch)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
`).run(e,s,t.type,t.title,t.subtitle,JSON.stringify(t.facts),t.narrative,JSON.stringify(t.concepts),JSON.stringify(t.files_read),JSON.stringify(t.files_modified),r||null,i.toISOString(),d);return{id:Number(b.lastInsertRowid),createdAtEpoch:d}}storeSummary(e,s,t,r){let i=new Date,d=i.getTime();this.db.prepare(` `).run(e,s,t.type,t.title,t.subtitle,JSON.stringify(t.facts),t.narrative,JSON.stringify(t.concepts),JSON.stringify(t.files_read),JSON.stringify(t.files_modified),r||null,i.toISOString(),d);return{id:Number(S.lastInsertRowid),createdAtEpoch:d}}storeSummary(e,s,t,r){let i=new Date,d=i.getTime();this.db.prepare(`
SELECT id FROM sdk_sessions WHERE sdk_session_id = ? SELECT id FROM sdk_sessions WHERE sdk_session_id = ?
`).get(e)||(this.db.prepare(` `).get(e)||(this.db.prepare(`
INSERT INTO sdk_sessions INSERT INTO sdk_sessions
(claude_session_id, sdk_session_id, project, started_at, started_at_epoch, status) (claude_session_id, sdk_session_id, project, started_at, started_at_epoch, status)
VALUES (?, ?, ?, ?, ?, 'active') VALUES (?, ?, ?, ?, ?, 'active')
`).run(e,e,s,i.toISOString(),d),console.error(`[SessionStore] Auto-created session record for session_id: ${e}`));let b=this.db.prepare(` `).run(e,e,s,i.toISOString(),d),console.error(`[SessionStore] Auto-created session record for session_id: ${e}`));let S=this.db.prepare(`
INSERT INTO session_summaries INSERT INTO session_summaries
(sdk_session_id, project, request, investigated, learned, completed, (sdk_session_id, project, request, investigated, learned, completed,
next_steps, notes, prompt_number, created_at, created_at_epoch) next_steps, notes, prompt_number, created_at, created_at_epoch)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
`).run(e,s,t.request,t.investigated,t.learned,t.completed,t.next_steps,t.notes,r||null,i.toISOString(),d);return{id:Number(b.lastInsertRowid),createdAtEpoch:d}}markSessionCompleted(e){let s=new Date,t=s.getTime();this.db.prepare(` `).run(e,s,t.request,t.investigated,t.learned,t.completed,t.next_steps,t.notes,r||null,i.toISOString(),d);return{id:Number(S.lastInsertRowid),createdAtEpoch:d}}markSessionCompleted(e){let s=new Date,t=s.getTime();this.db.prepare(`
UPDATE sdk_sessions UPDATE sdk_sessions
SET status = 'completed', completed_at = ?, completed_at_epoch = ? SET status = 'completed', completed_at = ?, completed_at_epoch = ?
WHERE id = ? WHERE id = ?
@@ -357,7 +357,7 @@ ${e.stack}`:e.message;if(Array.isArray(e))return`[${e.length} items]`;let s=Obje
WHERE up.id IN (${a}) WHERE up.id IN (${a})
ORDER BY up.created_at_epoch ${i} ORDER BY up.created_at_epoch ${i}
${d} ${d}
`).all(...e)}getTimelineAroundTimestamp(e,s=10,t=10,r){return this.getTimelineAroundObservation(null,e,s,t,r)}getTimelineAroundObservation(e,s,t=10,r=10,i){let d=i?"AND project = ?":"",a=i?[i]:[],_,E;if(e!==null){let R=` `).all(...e)}getTimelineAroundTimestamp(e,s=10,t=10,r){return this.getTimelineAroundObservation(null,e,s,t,r)}getTimelineAroundObservation(e,s,t=10,r=10,i){let d=i?"AND project = ?":"",a=i?[i]:[],_,T;if(e!==null){let R=`
SELECT id, created_at_epoch SELECT id, created_at_epoch
FROM observations FROM observations
WHERE id <= ? ${d} WHERE id <= ? ${d}
@@ -369,7 +369,7 @@ ${e.stack}`:e.message;if(Array.isArray(e))return`[${e.length} items]`;let s=Obje
WHERE id >= ? ${d} WHERE id >= ? ${d}
ORDER BY id ASC ORDER BY id ASC
LIMIT ? LIMIT ?
`;try{let l=this.db.prepare(R).all(e,...a,t+1),c=this.db.prepare(N).all(e,...a,r+1);if(l.length===0&&c.length===0)return{observations:[],sessions:[],prompts:[]};_=l.length>0?l[l.length-1].created_at_epoch:s,E=c.length>0?c[c.length-1].created_at_epoch:s}catch(l){return console.error("[SessionStore] Error getting boundary observations:",l.message),{observations:[],sessions:[],prompts:[]}}}else{let R=` `;try{let l=this.db.prepare(R).all(e,...a,t+1),c=this.db.prepare(N).all(e,...a,r+1);if(l.length===0&&c.length===0)return{observations:[],sessions:[],prompts:[]};_=l.length>0?l[l.length-1].created_at_epoch:s,T=c.length>0?c[c.length-1].created_at_epoch:s}catch(l){return console.error("[SessionStore] Error getting boundary observations:",l.message),{observations:[],sessions:[],prompts:[]}}}else{let R=`
SELECT created_at_epoch SELECT created_at_epoch
FROM observations FROM observations
WHERE created_at_epoch <= ? ${d} WHERE created_at_epoch <= ? ${d}
@@ -381,7 +381,7 @@ ${e.stack}`:e.message;if(Array.isArray(e))return`[${e.length} items]`;let s=Obje
WHERE created_at_epoch >= ? ${d} WHERE created_at_epoch >= ? ${d}
ORDER BY created_at_epoch ASC ORDER BY created_at_epoch ASC
LIMIT ? LIMIT ?
`;try{let l=this.db.prepare(R).all(s,...a,t),c=this.db.prepare(N).all(s,...a,r+1);if(l.length===0&&c.length===0)return{observations:[],sessions:[],prompts:[]};_=l.length>0?l[l.length-1].created_at_epoch:s,E=c.length>0?c[c.length-1].created_at_epoch:s}catch(l){return console.error("[SessionStore] Error getting boundary timestamps:",l.message),{observations:[],sessions:[],prompts:[]}}}let b=` `;try{let l=this.db.prepare(R).all(s,...a,t),c=this.db.prepare(N).all(s,...a,r+1);if(l.length===0&&c.length===0)return{observations:[],sessions:[],prompts:[]};_=l.length>0?l[l.length-1].created_at_epoch:s,T=c.length>0?c[c.length-1].created_at_epoch:s}catch(l){return console.error("[SessionStore] Error getting boundary timestamps:",l.message),{observations:[],sessions:[],prompts:[]}}}let S=`
SELECT * SELECT *
FROM observations FROM observations
WHERE created_at_epoch >= ? AND created_at_epoch <= ? ${d} WHERE created_at_epoch >= ? AND created_at_epoch <= ? ${d}
@@ -391,13 +391,13 @@ ${e.stack}`:e.message;if(Array.isArray(e))return`[${e.length} items]`;let s=Obje
FROM session_summaries FROM session_summaries
WHERE created_at_epoch >= ? AND created_at_epoch <= ? ${d} WHERE created_at_epoch >= ? AND created_at_epoch <= ? ${d}
ORDER BY created_at_epoch ASC ORDER BY created_at_epoch ASC
`,A=` `,v=`
SELECT up.*, s.project, s.sdk_session_id SELECT up.*, s.project, s.sdk_session_id
FROM user_prompts up FROM user_prompts up
JOIN sdk_sessions s ON up.claude_session_id = s.claude_session_id JOIN sdk_sessions s ON up.claude_session_id = s.claude_session_id
WHERE up.created_at_epoch >= ? AND up.created_at_epoch <= ? ${d.replace("project","s.project")} WHERE up.created_at_epoch >= ? AND up.created_at_epoch <= ? ${d.replace("project","s.project")}
ORDER BY up.created_at_epoch ASC ORDER BY up.created_at_epoch ASC
`;try{let R=this.db.prepare(b).all(_,E,...a),N=this.db.prepare(n).all(_,E,...a),l=this.db.prepare(A).all(_,E,...a);return{observations:R,sessions:N.map(c=>({id:c.id,sdk_session_id:c.sdk_session_id,project:c.project,request:c.request,completed:c.completed,next_steps:c.next_steps,created_at:c.created_at,created_at_epoch:c.created_at_epoch})),prompts:l.map(c=>({id:c.id,claude_session_id:c.claude_session_id,project:c.project,prompt:c.prompt_text,created_at:c.created_at,created_at_epoch:c.created_at_epoch}))}}catch(R){return console.error("[SessionStore] Error querying timeline records:",R.message),{observations:[],sessions:[],prompts:[]}}}close(){this.db.close()}};var te=parseInt(process.env.CLAUDE_MEM_CONTEXT_OBSERVATIONS||"50",10),W=10,o={reset:"\x1B[0m",bright:"\x1B[1m",dim:"\x1B[2m",cyan:"\x1B[36m",green:"\x1B[32m",yellow:"\x1B[33m",blue:"\x1B[34m",magenta:"\x1B[35m",gray:"\x1B[90m",red:"\x1B[31m"};function re(p){if(!p)return[];let e=JSON.parse(p);return Array.isArray(e)?e:[]}function ne(p){return new Date(p).toLocaleString("en-US",{month:"short",day:"numeric",hour:"numeric",minute:"2-digit",hour12:!0})}function ie(p){return new Date(p).toLocaleString("en-US",{hour:"numeric",minute:"2-digit",hour12:!0})}function oe(p){return new Date(p).toLocaleString("en-US",{month:"short",day:"numeric",year:"numeric"})}function ae(p){return p?Math.ceil(p.length/4):0}function de(p,e){return X.isAbsolute(p)?X.relative(e,p):p}async function Y(p,e=!1,s=!1){let t=p?.cwd??process.cwd(),r=t?X.basename(t):"unknown-project",i=new D,d=i.db.prepare(` `;try{let R=this.db.prepare(S).all(_,T,...a),N=this.db.prepare(n).all(_,T,...a),l=this.db.prepare(v).all(_,T,...a);return{observations:R,sessions:N.map(c=>({id:c.id,sdk_session_id:c.sdk_session_id,project:c.project,request:c.request,completed:c.completed,next_steps:c.next_steps,created_at:c.created_at,created_at_epoch:c.created_at_epoch})),prompts:l.map(c=>({id:c.id,claude_session_id:c.claude_session_id,project:c.project,prompt:c.prompt_text,created_at:c.created_at,created_at_epoch:c.created_at_epoch}))}}catch(R){return console.error("[SessionStore] Error querying timeline records:",R.message),{observations:[],sessions:[],prompts:[]}}}close(){this.db.close()}};var te=parseInt(process.env.CLAUDE_MEM_CONTEXT_OBSERVATIONS||"50",10),W=10,o={reset:"\x1B[0m",bright:"\x1B[1m",dim:"\x1B[2m",cyan:"\x1B[36m",green:"\x1B[32m",yellow:"\x1B[33m",blue:"\x1B[34m",magenta:"\x1B[35m",gray:"\x1B[90m",red:"\x1B[31m"};function re(p){if(!p)return[];let e=JSON.parse(p);return Array.isArray(e)?e:[]}function ne(p){return new Date(p).toLocaleString("en-US",{month:"short",day:"numeric",hour:"numeric",minute:"2-digit",hour12:!0})}function ie(p){return new Date(p).toLocaleString("en-US",{hour:"numeric",minute:"2-digit",hour12:!0})}function oe(p){return new Date(p).toLocaleString("en-US",{month:"short",day:"numeric",year:"numeric"})}function ae(p){return p?Math.ceil(p.length/4):0}function de(p,e){return X.isAbsolute(p)?X.relative(e,p):p}async function Y(p,e=!1,s=!1){let t=p?.cwd??process.cwd(),r=t?X.basename(t):"unknown-project",i=new D,d=i.db.prepare(`
SELECT SELECT
id, sdk_session_id, type, title, subtitle, narrative, id, sdk_session_id, type, title, subtitle, narrative,
facts, concepts, files_read, files_modified, facts, concepts, files_read, files_modified,
@@ -407,7 +407,7 @@ ${e.stack}`:e.message;if(Array.isArray(e))return`[${e.length} items]`;let s=Obje
ORDER BY created_at_epoch DESC ORDER BY created_at_epoch DESC
LIMIT ? LIMIT ?
`).all(r,te),a=i.db.prepare(` `).all(r,te),a=i.db.prepare(`
SELECT id, sdk_session_id, request, completed, next_steps, created_at, created_at_epoch SELECT id, sdk_session_id, request, investigated, learned, completed, next_steps, created_at, created_at_epoch
FROM session_summaries FROM session_summaries
WHERE project = ? WHERE project = ?
ORDER BY created_at_epoch DESC ORDER BY created_at_epoch DESC
@@ -419,5 +419,5 @@ ${o.gray}${"\u2500".repeat(60)}${o.reset}
${o.dim}No previous sessions found for this project yet.${o.reset} ${o.dim}No previous sessions found for this project yet.${o.reset}
`:`# [${r}] recent context `:`# [${r}] recent context
No previous sessions found for this project yet.`;let _=d,E=a.slice(0,W),b=_,n=[];if(e?(n.push(""),n.push(`${o.bright}${o.cyan}\u{1F4DD} [${r}] recent context${o.reset}`),n.push(`${o.gray}${"\u2500".repeat(60)}${o.reset}`),n.push("")):(n.push(`# [${r}] recent context`),n.push("")),b.length>0){e?(n.push(`${o.dim}Legend: \u{1F3AF} session-request | \u{1F534} bugfix | \u{1F7E3} feature | \u{1F504} refactor | \u2705 change | \u{1F535} discovery | \u{1F9E0} decision${o.reset}`),n.push("")):(n.push("**Legend:** \u{1F3AF} session-request | \u{1F534} bugfix | \u{1F7E3} feature | \u{1F504} refactor | \u2705 change | \u{1F535} discovery | \u{1F9E0} decision"),n.push("")),e?(n.push(`${o.dim}\u{1F4A1} Progressive Disclosure: This index shows WHAT exists (titles) and retrieval COST (token counts).${o.reset}`),n.push(`${o.dim} \u2192 Use MCP search tools to fetch full observation details on-demand (Layer 2)${o.reset}`),n.push(`${o.dim} \u2192 Prefer searching observations over re-reading code for past decisions and learnings${o.reset}`),n.push(`${o.dim} \u2192 Critical types (\u{1F534} bugfix, \u{1F9E0} decision) often worth fetching immediately${o.reset}`),n.push("")):(n.push("\u{1F4A1} **Progressive Disclosure:** This index shows WHAT exists (titles) and retrieval COST (token counts)."),n.push("- Use MCP search tools to fetch full observation details on-demand (Layer 2)"),n.push("- Prefer searching observations over re-reading code for past decisions and learnings"),n.push("- Critical types (\u{1F534} bugfix, \u{1F9E0} decision) often worth fetching immediately"),n.push(""));let A=a[0]?.id,R=E.map((u,T)=>{let m=T===0?null:a[T+1];return{...u,displayEpoch:m?m.created_at_epoch:u.created_at_epoch,displayTime:m?m.created_at:u.created_at,isMostRecent:u.id===A}}),N=[...b.map(u=>({type:"observation",data:u})),...R.map(u=>({type:"summary",data:u}))];N.sort((u,T)=>{let m=u.type==="observation"?u.data.created_at_epoch:u.data.displayEpoch,L=T.type==="observation"?T.data.created_at_epoch:T.data.displayEpoch;return m-L});let l=new Map;for(let u of N){let T=u.type==="observation"?u.data.created_at:u.data.displayTime,m=oe(T);l.has(m)||l.set(m,[]),l.get(m).push(u)}let c=Array.from(l.entries()).sort((u,T)=>{let m=new Date(u[0]).getTime(),L=new Date(T[0]).getTime();return m-L});for(let[u,T]of c){e?(n.push(`${o.bright}${o.cyan}${u}${o.reset}`),n.push("")):(n.push(`### ${u}`),n.push(""));let m=null,L="",v=!1;for(let x of T)if(x.type==="summary"){v&&(n.push(""),v=!1,m=null,L="");let g=x.data,y=`${g.request||"Session started"} (${ne(g.displayTime)})`,O=g.isMostRecent?"":`claude-mem://session-summary/${g.id}`;if(e){let h=O?`${o.dim}[${O}]${o.reset}`:"";n.push(`\u{1F3AF} ${o.yellow}#S${g.id}${o.reset} ${y} ${h}`)}else{let h=O?` [\u2192](${O})`:"";n.push(`**\u{1F3AF} #S${g.id}** ${y}${h}`)}n.push("")}else{let g=x.data,y=re(g.files_modified),O=y.length>0?de(y[0],t):"General";O!==m&&(v&&n.push(""),e?n.push(`${o.dim}${O}${o.reset}`):n.push(`**${O}**`),e||(n.push("| ID | Time | T | Title | Tokens |"),n.push("|----|------|---|-------|--------|")),m=O,v=!0,L="");let h="\u2022";switch(g.type){case"bugfix":h="\u{1F534}";break;case"feature":h="\u{1F7E3}";break;case"refactor":h="\u{1F504}";break;case"change":h="\u2705";break;case"discovery":h="\u{1F535}";break;case"decision":h="\u{1F9E0}";break;default:h="\u2022"}let C=ie(g.created_at),F=g.title||"Untitled",k=ae(g.narrative),B=C!==L,q=B?C:"";if(L=C,e){let K=B?`${o.dim}${C}${o.reset}`:" ".repeat(C.length),J=k>0?`${o.dim}(~${k}t)${o.reset}`:"";n.push(` ${o.dim}#${g.id}${o.reset} ${K} ${h} ${F} ${J}`)}else n.push(`| #${g.id} | ${q||"\u2033"} | ${h} | ${F} | ~${k} |`)}v&&n.push("")}let f=a[0];f&&(f.completed||f.next_steps)&&(f.completed&&(e?n.push(`${o.green}Completed:${o.reset} ${f.completed}`):n.push(`**Completed**: ${f.completed}`),n.push("")),f.next_steps&&(e?n.push(`${o.magenta}Next Steps:${o.reset} ${f.next_steps}`):n.push(`**Next Steps**: ${f.next_steps}`),n.push(""))),e?n.push(`${o.dim}Use claude-mem MCP search to access records with the given ID${o.reset}`):n.push("*Use claude-mem MCP search to access records with the given ID*")}return i.close(),n.join(` No previous sessions found for this project yet.`;let _=d,T=a.slice(0,W),S=_,n=[];if(e?(n.push(""),n.push(`${o.bright}${o.cyan}\u{1F4DD} [${r}] recent context${o.reset}`),n.push(`${o.gray}${"\u2500".repeat(60)}${o.reset}`),n.push("")):(n.push(`# [${r}] recent context`),n.push("")),S.length>0){e?(n.push(`${o.dim}Legend: \u{1F3AF} session-request | \u{1F534} bugfix | \u{1F7E3} feature | \u{1F504} refactor | \u2705 change | \u{1F535} discovery | \u{1F9E0} decision${o.reset}`),n.push("")):(n.push("**Legend:** \u{1F3AF} session-request | \u{1F534} bugfix | \u{1F7E3} feature | \u{1F504} refactor | \u2705 change | \u{1F535} discovery | \u{1F9E0} decision"),n.push("")),e?(n.push(`${o.dim}\u{1F4A1} Progressive Disclosure: This index shows WHAT exists (titles) and retrieval COST (token counts).${o.reset}`),n.push(`${o.dim} \u2192 Use MCP search tools to fetch full observation details on-demand (Layer 2)${o.reset}`),n.push(`${o.dim} \u2192 Prefer searching observations over re-reading code for past decisions and learnings${o.reset}`),n.push(`${o.dim} \u2192 Critical types (\u{1F534} bugfix, \u{1F9E0} decision) often worth fetching immediately${o.reset}`),n.push("")):(n.push("\u{1F4A1} **Progressive Disclosure:** This index shows WHAT exists (titles) and retrieval COST (token counts)."),n.push("- Use MCP search tools to fetch full observation details on-demand (Layer 2)"),n.push("- Prefer searching observations over re-reading code for past decisions and learnings"),n.push("- Critical types (\u{1F534} bugfix, \u{1F9E0} decision) often worth fetching immediately"),n.push(""));let v=a[0]?.id,R=T.map((u,g)=>{let E=g===0?null:a[g+1];return{...u,displayEpoch:E?E.created_at_epoch:u.created_at_epoch,displayTime:E?E.created_at:u.created_at,isMostRecent:u.id===v}}),N=[...S.map(u=>({type:"observation",data:u})),...R.map(u=>({type:"summary",data:u}))];N.sort((u,g)=>{let E=u.type==="observation"?u.data.created_at_epoch:u.data.displayEpoch,L=g.type==="observation"?g.data.created_at_epoch:g.data.displayEpoch;return E-L});let l=new Map;for(let u of N){let g=u.type==="observation"?u.data.created_at:u.data.displayTime,E=oe(g);l.has(E)||l.set(E,[]),l.get(E).push(u)}let c=Array.from(l.entries()).sort((u,g)=>{let E=new Date(u[0]).getTime(),L=new Date(g[0]).getTime();return E-L});for(let[u,g]of c){e?(n.push(`${o.bright}${o.cyan}${u}${o.reset}`),n.push("")):(n.push(`### ${u}`),n.push(""));let E=null,L="",A=!1;for(let x of g)if(x.type==="summary"){A&&(n.push(""),A=!1,E=null,L="");let h=x.data,y=`${h.request||"Session started"} (${ne(h.displayTime)})`,O=h.isMostRecent?"":`claude-mem://session-summary/${h.id}`;if(e){let b=O?`${o.dim}[${O}]${o.reset}`:"";n.push(`\u{1F3AF} ${o.yellow}#S${h.id}${o.reset} ${y} ${b}`)}else{let b=O?` [\u2192](${O})`:"";n.push(`**\u{1F3AF} #S${h.id}** ${y}${b}`)}n.push("")}else{let h=x.data,y=re(h.files_modified),O=y.length>0?de(y[0],t):"General";O!==E&&(A&&n.push(""),e?n.push(`${o.dim}${O}${o.reset}`):n.push(`**${O}**`),e||(n.push("| ID | Time | T | Title | Tokens |"),n.push("|----|------|---|-------|--------|")),E=O,A=!0,L="");let b="\u2022";switch(h.type){case"bugfix":b="\u{1F534}";break;case"feature":b="\u{1F7E3}";break;case"refactor":b="\u{1F504}";break;case"change":b="\u2705";break;case"discovery":b="\u{1F535}";break;case"decision":b="\u{1F9E0}";break;default:b="\u2022"}let C=ie(h.created_at),F=h.title||"Untitled",k=ae(h.narrative),B=C!==L,q=B?C:"";if(L=C,e){let K=B?`${o.dim}${C}${o.reset}`:" ".repeat(C.length),J=k>0?`${o.dim}(~${k}t)${o.reset}`:"";n.push(` ${o.dim}#${h.id}${o.reset} ${K} ${b} ${F} ${J}`)}else n.push(`| #${h.id} | ${q||"\u2033"} | ${b} | ${F} | ~${k} |`)}A&&n.push("")}let m=a[0];m&&(m.investigated||m.learned||m.completed||m.next_steps)&&(m.investigated&&(e?n.push(`${o.blue}Investigated:${o.reset} ${m.investigated}`):n.push(`**Investigated**: ${m.investigated}`),n.push("")),m.learned&&(e?n.push(`${o.yellow}Learned:${o.reset} ${m.learned}`):n.push(`**Learned**: ${m.learned}`),n.push("")),m.completed&&(e?n.push(`${o.green}Completed:${o.reset} ${m.completed}`):n.push(`**Completed**: ${m.completed}`),n.push("")),m.next_steps&&(e?n.push(`${o.magenta}Next Steps:${o.reset} ${m.next_steps}`):n.push(`**Next Steps**: ${m.next_steps}`),n.push(""))),e?n.push(`${o.dim}Use claude-mem MCP search to access records with the given ID${o.reset}`):n.push("*Use claude-mem MCP search to access records with the given ID*")}return i.close(),n.join(`
`).trimEnd()}var V=process.argv.includes("--index"),ce=process.argv.includes("--colors");if(w.isTTY||ce)Y(void 0,!0,V).then(p=>{console.log(p),process.exit(0)});else{let p="";w.on("data",e=>p+=e),w.on("end",async()=>{let e=p.trim()?JSON.parse(p):void 0,t={hookSpecificOutput:{hookEventName:"SessionStart",additionalContext:await Y(e,!1,V)}};console.log(JSON.stringify(t)),process.exit(0)})} `).trimEnd()}var V=process.argv.includes("--index"),ce=process.argv.includes("--colors");if(w.isTTY||ce)Y(void 0,!0,V).then(p=>{console.log(p),process.exit(0)});else{let p="";w.on("data",e=>p+=e),w.on("end",async()=>{let e=p.trim()?JSON.parse(p):void 0,t={hookSpecificOutput:{hookEventName:"SessionStart",additionalContext:await Y(e,!1,V)}};console.log(JSON.stringify(t)),process.exit(0)})}
+21 -3
View File
@@ -147,12 +147,12 @@ async function contextHook(input?: SessionStartInput, useColors: boolean = false
// Get recent summaries (optional - may not exist for recent sessions) // Get recent summaries (optional - may not exist for recent sessions)
const recentSummaries = db.db.prepare(` const recentSummaries = db.db.prepare(`
SELECT id, sdk_session_id, request, completed, next_steps, created_at, created_at_epoch SELECT id, sdk_session_id, request, investigated, learned, completed, next_steps, created_at, created_at_epoch
FROM session_summaries FROM session_summaries
WHERE project = ? WHERE project = ?
ORDER BY created_at_epoch DESC ORDER BY created_at_epoch DESC
LIMIT ? LIMIT ?
`).all(project, DISPLAY_SESSION_COUNT + 1) as Array<{ id: number; sdk_session_id: string; request: string | null; completed: string | null; next_steps: string | null; created_at: string; created_at_epoch: number }>; `).all(project, DISPLAY_SESSION_COUNT + 1) as Array<{ id: number; sdk_session_id: string; request: string | null; investigated: string | null; learned: string | null; completed: string | null; next_steps: string | null; created_at: string; created_at_epoch: number }>;
// If we have neither observations nor summaries, show empty state // If we have neither observations nor summaries, show empty state
if (allObservations.length === 0 && recentSummaries.length === 0) { if (allObservations.length === 0 && recentSummaries.length === 0) {
@@ -382,7 +382,25 @@ async function contextHook(input?: SessionStartInput, useColors: boolean = false
// Add full summary details for most recent session // Add full summary details for most recent session
const mostRecentSummary = recentSummaries[0]; const mostRecentSummary = recentSummaries[0];
if (mostRecentSummary && (mostRecentSummary.completed || mostRecentSummary.next_steps)) { if (mostRecentSummary && (mostRecentSummary.investigated || mostRecentSummary.learned || mostRecentSummary.completed || mostRecentSummary.next_steps)) {
if (mostRecentSummary.investigated) {
if (useColors) {
output.push(`${colors.blue}Investigated:${colors.reset} ${mostRecentSummary.investigated}`);
} else {
output.push(`**Investigated**: ${mostRecentSummary.investigated}`);
}
output.push('');
}
if (mostRecentSummary.learned) {
if (useColors) {
output.push(`${colors.yellow}Learned:${colors.reset} ${mostRecentSummary.learned}`);
} else {
output.push(`**Learned**: ${mostRecentSummary.learned}`);
}
output.push('');
}
if (mostRecentSummary.completed) { if (mostRecentSummary.completed) {
if (useColors) { if (useColors) {
output.push(`${colors.green}Completed:${colors.reset} ${mostRecentSummary.completed}`); output.push(`${colors.green}Completed:${colors.reset} ${mostRecentSummary.completed}`);