Compare commits
54 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| d4d6185bb4 | |||
| 2df50bcaa0 | |||
| 19ecc7845f | |||
| 142d6ae56f | |||
| 7db39bb482 | |||
| e600a0f702 | |||
| b87654d452 | |||
| a3b0b70a98 | |||
| 81f8aa7eef | |||
| 8f8649c2a8 | |||
| ab0938633a | |||
| d8d4d2464f | |||
| 9d32629f9d | |||
| 6a007a26fe | |||
| 6131f9694a | |||
| 22777bdcfe | |||
| d7946522e9 | |||
| 43262d7b53 | |||
| 80fc1588d3 | |||
| 70ba785364 | |||
| 7fb990f845 | |||
| 9f3bf55c76 | |||
| 83d9747627 | |||
| ae2c789781 | |||
| 81d7ab59ac | |||
| e82d9e075b | |||
| 99af5fdf13 | |||
| b88ce840fa | |||
| 516e136966 | |||
| 051bc8dd67 | |||
| 05ddf3540c | |||
| e18f02e2af | |||
| 77885db345 | |||
| 50d504715d | |||
| 28d9c43f85 | |||
| adc5853c73 | |||
| 81fdf28347 | |||
| b3a565c448 | |||
| 5b28c23b20 | |||
| f4217cb2b9 | |||
| e7252c8999 | |||
| 74637705d7 | |||
| 322cb94c43 | |||
| 21c7ab2929 | |||
| 3846d66ccc | |||
| 817a069323 | |||
| d7b9f68d80 | |||
| c48290a156 | |||
| 226a52f8b8 | |||
| 93b5f80168 | |||
| f6cf895847 | |||
| dba29de2a7 | |||
| c16f453017 | |||
| 8b4c962e62 |
@@ -10,7 +10,7 @@
|
||||
"plugins": [
|
||||
{
|
||||
"name": "claude-mem",
|
||||
"version": "4.2.3",
|
||||
"version": "4.3.0",
|
||||
"source": "./plugin",
|
||||
"description": "Persistent memory system for Claude Code - context compression across sessions"
|
||||
}
|
||||
|
||||
@@ -0,0 +1,222 @@
|
||||
---
|
||||
name: version-bump
|
||||
description: Manage semantic version updates for claude-mem project. Handles patch, minor, and major version increments following semantic versioning. Updates package.json, marketplace.json, and CLAUDE.md consistently.
|
||||
---
|
||||
|
||||
# Version Bump Skill
|
||||
|
||||
IMPORTANT: This skill manages semantic versioning across the claude-mem project. YOU MUST update all three version-tracked files consistently.
|
||||
|
||||
## Quick Reference
|
||||
|
||||
**Files requiring updates:**
|
||||
1. `package.json` (line 3)
|
||||
2. `.claude-plugin/marketplace.json` (line 13)
|
||||
3. `CLAUDE.md` (version history section)
|
||||
|
||||
**Semantic versioning:**
|
||||
- PATCH (x.y.Z): Bugfixes only
|
||||
- MINOR (x.Y.0): New features, backward compatible
|
||||
- MAJOR (X.0.0): Breaking changes
|
||||
|
||||
## Workflow
|
||||
|
||||
When invoked, follow this process:
|
||||
|
||||
### 1. Analyze Changes
|
||||
First, understand what changed:
|
||||
```bash
|
||||
git log --oneline -5
|
||||
git diff HEAD~1
|
||||
```
|
||||
|
||||
### 2. Determine Version Type
|
||||
Ask yourself:
|
||||
- Breaking changes? → MAJOR
|
||||
- New features? → MINOR
|
||||
- Bugfixes only? → PATCH
|
||||
|
||||
If unclear, ASK THE USER explicitly.
|
||||
|
||||
### 3. Calculate New Version
|
||||
From current version in `package.json`:
|
||||
```bash
|
||||
grep '"version"' package.json
|
||||
```
|
||||
|
||||
Apply semantic versioning rules:
|
||||
- Patch: increment Z (4.2.8 → 4.2.9)
|
||||
- Minor: increment Y, reset Z (4.2.8 → 4.3.0)
|
||||
- Major: increment X, reset Y and Z (4.2.8 → 5.0.0)
|
||||
|
||||
### 4. Preview Changes
|
||||
BEFORE making changes, show the user:
|
||||
```
|
||||
Current version: 4.2.8
|
||||
New version: 4.2.9 (PATCH)
|
||||
Reason: Fixed database query bug
|
||||
|
||||
Files to update:
|
||||
- package.json: "version": "4.2.9"
|
||||
- marketplace.json: "version": "4.2.9"
|
||||
- CLAUDE.md: Add v4.2.9 entry
|
||||
|
||||
Proceed? (yes/no)
|
||||
```
|
||||
|
||||
### 5. Update Files
|
||||
|
||||
**Update package.json:**
|
||||
```json
|
||||
{
|
||||
"name": "claude-mem",
|
||||
"version": "4.2.9",
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
**Update .claude-plugin/marketplace.json:**
|
||||
```json
|
||||
{
|
||||
"name": "claude-mem",
|
||||
"version": "4.2.9",
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
**Update CLAUDE.md:**
|
||||
Add entry at top of Version History section following the template below.
|
||||
|
||||
### 6. Verify Consistency
|
||||
```bash
|
||||
# Check all versions match
|
||||
grep -n '"version"' package.json .claude-plugin/marketplace.json
|
||||
# Should show same version in both files
|
||||
```
|
||||
|
||||
### 7. Test
|
||||
```bash
|
||||
# Verify the plugin loads correctly
|
||||
npm run build
|
||||
# Or whatever build command is appropriate
|
||||
```
|
||||
|
||||
## CLAUDE.md Templates
|
||||
|
||||
### PATCH Version Template
|
||||
```markdown
|
||||
### v4.2.9
|
||||
**Breaking Changes**: None (patch version)
|
||||
|
||||
**Fixes**:
|
||||
- [Specific bug fixed with file reference: src/db/query.ts:45]
|
||||
- [Impact: what this fixes for users]
|
||||
|
||||
**Technical Details**:
|
||||
- Modified: [file paths with line numbers]
|
||||
- Root cause: [brief explanation]
|
||||
```
|
||||
|
||||
### MINOR Version Template
|
||||
```markdown
|
||||
### v4.3.0
|
||||
**Breaking Changes**: None (minor version)
|
||||
|
||||
**Features**:
|
||||
- [Feature name and user benefit]
|
||||
- [How to use: command or API example]
|
||||
|
||||
**Improvements**:
|
||||
- [Enhancement description]
|
||||
|
||||
**Technical Details**:
|
||||
- New files: [paths]
|
||||
- Modified: [paths with line numbers]
|
||||
- Dependencies: [any new dependencies added]
|
||||
```
|
||||
|
||||
### MAJOR Version Template
|
||||
```markdown
|
||||
### v5.0.0
|
||||
**Breaking Changes**:
|
||||
⚠️ [Change 1: what breaks and why]
|
||||
⚠️ [Change 2: what breaks and why]
|
||||
|
||||
**Migration Guide**:
|
||||
1. [Step-by-step instructions]
|
||||
2. [Code examples showing old vs new]
|
||||
3. [Data migration commands if needed]
|
||||
|
||||
**Features**:
|
||||
- [New capabilities enabled by breaking changes]
|
||||
|
||||
**Technical Details**:
|
||||
- Architectural changes: [high-level overview]
|
||||
- Modified: [key files with line numbers]
|
||||
- Removed: [deprecated APIs or features]
|
||||
```
|
||||
|
||||
## Common Scenarios
|
||||
|
||||
**Scenario 1: Bug fix after testing**
|
||||
```
|
||||
User: "Fixed the memory leak in the search function"
|
||||
You: Determine → PATCH
|
||||
Calculate → 4.2.8 → 4.2.9
|
||||
Update all three files
|
||||
CLAUDE.md: Focus on the fix and impact
|
||||
```
|
||||
|
||||
**Scenario 2: New MCP tool added**
|
||||
```
|
||||
User: "Added web search MCP integration"
|
||||
You: Determine → MINOR (new feature)
|
||||
Calculate → 4.2.8 → 4.3.0
|
||||
Update all three files
|
||||
CLAUDE.md: Describe feature and usage
|
||||
```
|
||||
|
||||
**Scenario 3: Database schema redesign**
|
||||
```
|
||||
User: "Rewrote storage layer, old data needs migration"
|
||||
You: Determine → MAJOR (breaking change)
|
||||
Calculate → 4.2.8 → 5.0.0
|
||||
Update all three files
|
||||
CLAUDE.md: Include migration steps
|
||||
```
|
||||
|
||||
## Error Prevention
|
||||
|
||||
**ALWAYS verify:**
|
||||
- [ ] All three files have matching version numbers
|
||||
- [ ] CLAUDE.md entry matches version type (patch/minor/major)
|
||||
- [ ] Breaking changes are clearly marked with ⚠️
|
||||
- [ ] File references use format: `path/to/file.ts:line_number`
|
||||
- [ ] CLAUDE.md entry is added at TOP of version history
|
||||
|
||||
**NEVER:**
|
||||
- Update only one or two files
|
||||
- Skip the verification step
|
||||
- Forget to ask user if version type is unclear
|
||||
- Use vague descriptions in CLAUDE.md
|
||||
|
||||
## Best Practices
|
||||
|
||||
1. **Be explicit about breaking changes** - Users need clear migration paths[(2)](https://docs.claude.com/en/docs/claude-code/plugins-reference#plugin-manifest-schema)
|
||||
2. **Include file references** - Makes debugging easier later[(1)](https://www.anthropic.com/engineering/claude-code-best-practices)
|
||||
3. **Test after bumping** - Ensure version displays correctly[(3)](https://www.anthropic.com/engineering/claude-code-best-practices)
|
||||
4. **Keep CLAUDE.md concise** - Focus on user impact, not implementation minutiae[(1)](https://www.anthropic.com/engineering/claude-code-best-practices)
|
||||
5. **Use consistent formatting** - Follow existing CLAUDE.md style[(1)](https://www.anthropic.com/engineering/claude-code-best-practices)
|
||||
|
||||
## Reference Commands
|
||||
|
||||
```bash
|
||||
# View current version
|
||||
cat package.json | grep version
|
||||
|
||||
# Check version history
|
||||
head -50 CLAUDE.md | grep "^###"
|
||||
|
||||
# Verify consistency
|
||||
diff <(jq -r .version package.json) <(jq -r .version .claude-plugin/marketplace.json)
|
||||
```
|
||||
+2
-1
@@ -7,4 +7,5 @@ node_modules/
|
||||
*.temp
|
||||
.claude/settings.local.json
|
||||
plugin/data/
|
||||
plugin/data.backup/
|
||||
plugin/data.backup/
|
||||
package-lock.json
|
||||
@@ -8,6 +8,61 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
||||
## [Unreleased]
|
||||
|
||||
|
||||
## [4.3.0] - 2025-10-25
|
||||
|
||||
### Added
|
||||
- **Progressive Disclosure Context**: Enhanced context hook with layered memory retrieval system
|
||||
- Layer 1 (Index): Observation titles, token costs, and type indicators at session start
|
||||
- Layer 2 (Details): Full narratives retrieved on-demand via MCP search
|
||||
- Layer 3 (Perfect Recall): Source code and original transcripts
|
||||
- Context hook now displays observations in table format with ID, timestamp, type indicator, title, and token count
|
||||
- Type indicators: 🔴 (critical/gotcha), 🟤 (decision), 🔵 (informational/how-it-works)
|
||||
- Progressive disclosure instructions guide Claude on when to fetch full observation details vs. reading code
|
||||
- Token counts (~200-500 per observation) help Claude make informed retrieval decisions
|
||||
- **Agent Skills documentation**: Added comprehensive documentation on creating and using Claude Code agent skills
|
||||
- **Version bump skill**: Added automated version bump management skill for streamlined releases
|
||||
- **Memory toggle feature planning**: Added design document for future pause/resume recording capability
|
||||
|
||||
### Changed
|
||||
- **Enhanced session summary handling**: Improved timeline rendering and summary organization
|
||||
- **Improved context hook output**: Added structured timeline with session grouping and observation details
|
||||
- **Context token cost**: Increased from ~800 tokens to ~2,500 tokens for richer observation index
|
||||
|
||||
### Fixed
|
||||
- **Cross-platform path detection**: Removed hardcoded macOS-specific paths for project and Claude Code executable (fixes #23)
|
||||
- Removed hardcoded paths in context hook, worker service, and SDK integration
|
||||
- Now uses dynamic path resolution for cross-platform compatibility
|
||||
- Affects: `src/hooks/context.ts`, `src/services/worker-service.ts`, `src/sdk/worker.ts`
|
||||
|
||||
|
||||
## [4.2.11] - 2025-10-25
|
||||
|
||||
### Fixed
|
||||
- **Cross-platform Claude path detection**: Fixed SDK auto-detection failure by implementing explicit `which`/`where` command execution
|
||||
- SDK's automatic Claude path detection was returning undefined
|
||||
- Unix/macOS: Uses `which claude` command to find executable
|
||||
- Windows: Uses `where claude` command (works in both CMD and PowerShell)
|
||||
- Fallback to `CLAUDE_CODE_PATH` environment variable if set
|
||||
- Handles Windows multiple results by taking first match
|
||||
- Worker now logs discovered path for debugging: "Found Claude executable: /path/to/claude"
|
||||
|
||||
### Technical Details
|
||||
- Added `findClaudePath()` helper function using `child_process.execSync`
|
||||
- Platform detection via `process.platform === 'win32'` to choose appropriate command
|
||||
- Updated `src/sdk/worker.ts` and `src/services/worker-service.ts` with explicit path detection
|
||||
- Both files now pass `pathToClaudeCodeExecutable: claudePath` to SDK query
|
||||
|
||||
|
||||
## [4.2.10] - 2025-10-25
|
||||
|
||||
### Fixed
|
||||
- **Windows compatibility**: Removed hardcoded macOS-specific Claude executable path that prevented worker service from running on Windows
|
||||
- Removed hardcoded path: `/Users/alexnewman/.nvm/versions/node/v24.5.0/bin/claude`
|
||||
- Removed `pathToClaudeCodeExecutable` parameter from SDK query() calls
|
||||
- SDK now automatically detects Claude Code executable path on all platforms
|
||||
- Affects: `src/sdk/worker.ts`, `src/services/worker-service.ts`, `plugin/scripts/worker-service.cjs`
|
||||
|
||||
|
||||
## [4.2.3] - 2025-10-23
|
||||
|
||||
### Security
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
Claude-mem is a persistent memory compression system that preserves context across Claude Code sessions. It automatically captures tool usage observations, processes them through the Claude Agent SDK, and makes summaries available to future sessions.
|
||||
|
||||
**Current Version**: 4.2.3
|
||||
**Current Version**: 4.3.0
|
||||
**License**: AGPL-3.0
|
||||
**Author**: Alex Newman (@thedotmack)
|
||||
|
||||
@@ -210,86 +210,34 @@ npm run build && git commit -a -m "Build and update" && git push && cd ~/.claude
|
||||
|
||||
## Version History
|
||||
|
||||
### v4.2.3 (Current)
|
||||
**Breaking Changes**: None (patch version)
|
||||
For detailed version history and changelog, see [CHANGELOG.md](CHANGELOG.md).
|
||||
|
||||
**Security**:
|
||||
- Fixed FTS5 injection vulnerability in search functions
|
||||
- Implemented proper double-quote escaping for FTS5 queries
|
||||
- Added comprehensive test suite with 332 injection attack tests
|
||||
- Affects: `search_observations`, `search_sessions`, `search_user_prompts` MCP tools
|
||||
**Current Version**: 4.3.0
|
||||
|
||||
**Fixes**:
|
||||
- Fixed ESM/CJS compatibility for getDirname function in src/shared/paths.ts
|
||||
- Detects context using `typeof __dirname !== 'undefined'`
|
||||
- Falls back to `fileURLToPath(import.meta.url)` for ESM modules
|
||||
- Resolves path resolution issues across hook (ESM) and worker (CJS) contexts
|
||||
- Fixed Windows PowerShell compatibility issue with SessionStart hook
|
||||
- Replaced bash-specific test command `[` with cross-platform npm install command
|
||||
- Hook now runs `npm install` with quiet flags (fast and idempotent when dependencies exist)
|
||||
### Recent Highlights
|
||||
|
||||
**Technical Details**:
|
||||
- SessionSearch.ts now escapes double quotes in FTS5 queries: `query.replace(/"/g, '""')`
|
||||
- Updated `plugin/hooks/hooks.json` SessionStart command to use standard shell syntax
|
||||
- Changed from: `[ ! -d ... ] && cd ... && npm install && node ... || node ...`
|
||||
- Changed to: `cd ... && npm install --prefer-offline --no-audit --no-fund --loglevel=error && node ...`
|
||||
- Dependencies are installed in marketplace folder (parent of CLAUDE_PLUGIN_ROOT) where root package.json exists
|
||||
- getDirname function now properly handles both CommonJS (__dirname) and ES modules (import.meta.url)
|
||||
#### v4.3.0 (2025-10-25)
|
||||
- Progressive Disclosure Context: Enhanced context hook with observation timeline and token cost visibility
|
||||
- Session observations now display in table format showing ID, timestamp, type indicators, title, and token counts
|
||||
- Added progressive disclosure usage instructions to guide Claude on when to fetch full observation details vs. reading code
|
||||
- Added Agent Skills documentation and version bump management skill
|
||||
- Cross-platform path improvements: Removed hardcoded paths for project and Claude Code executable (fixes #23)
|
||||
|
||||
### v4.2.0
|
||||
**Breaking Changes**: None (minor version)
|
||||
#### v4.2.11 (2025-10-25)
|
||||
- Fixed cross-platform Claude executable path detection using `which`/`where` commands
|
||||
- Full Windows, macOS, and Linux compatibility
|
||||
|
||||
**Features**:
|
||||
- User prompt storage with FTS5 full-text search
|
||||
- New `user_prompts` table stores raw user input for every prompt
|
||||
- New `search_user_prompts` MCP tool enables searching actual user requests
|
||||
- Automatic FTS5 indexing of all user prompts for fast retrieval
|
||||
#### v4.2.8 (2025-10-25)
|
||||
- Fixed NOT NULL constraint violation for claude_session_id
|
||||
|
||||
**Benefits**:
|
||||
- Full context reconstruction from user intent → Claude actions → outcomes
|
||||
- Pattern detection for repeated requests (identify when Claude isn't listening)
|
||||
- Improved debugging by tracing from original user words to final implementation
|
||||
- Historical search: "How many times did user ask for X feature?"
|
||||
#### v4.2.3 (2025-10-23)
|
||||
- Fixed FTS5 injection vulnerability
|
||||
- Fixed Windows PowerShell compatibility
|
||||
|
||||
**Implementation**:
|
||||
- Migration 10: Creates user_prompts table with FTS5 virtual table and sync triggers
|
||||
- UserPromptSubmit hook now saves prompts using claude_session_id (available immediately)
|
||||
- Citations use `claude-mem://user-prompt/{id}` URI scheme
|
||||
|
||||
### v4.1.0
|
||||
**Breaking Changes**: None (minor version)
|
||||
|
||||
**Features**:
|
||||
- Graceful session cleanup (marks complete instead of DELETE)
|
||||
- Restored MCP search server from backup
|
||||
- Updated dependencies (claude-agent-sdk 0.1.23, MCP SDK 1.20.1)
|
||||
|
||||
**Fixes**:
|
||||
- `/clear` command now skips cleanup to prevent session interruption
|
||||
- Session workers can finish pending operations naturally
|
||||
|
||||
### v4.0.0
|
||||
**Breaking Changes**:
|
||||
- Data directory relocated to `${CLAUDE_PLUGIN_ROOT}/data/`
|
||||
- Fresh start required (no migration from v3.x)
|
||||
- Worker auto-starts in SessionStart hook
|
||||
|
||||
**Features**:
|
||||
- MCP Search Server with 8 specialized search tools
|
||||
- FTS5 full-text search across observations, sessions, and user prompts
|
||||
- Citation support with `claude-mem://` URIs
|
||||
- HTTP REST API architecture with PM2 management
|
||||
#### v4.0.0 (2025-10-18)
|
||||
- MCP Search Server with FTS5 full-text search
|
||||
- Plugin data directory integration
|
||||
|
||||
**Changes**:
|
||||
- Improved session continuity
|
||||
- Enhanced error handling
|
||||
- Better process cleanup
|
||||
|
||||
### Earlier Versions (v3.x)
|
||||
- v3.9.17: MCP integration, hookSpecificOutput JSON format
|
||||
- v3.7.1: SQLite storage backend
|
||||
- Earlier: Mintlify documentation, statusline support
|
||||
- HTTP REST API architecture with PM2
|
||||
|
||||
## Key Design Decisions
|
||||
|
||||
|
||||
@@ -0,0 +1,331 @@
|
||||
# Experimental Release: Progressive Disclosure Context System
|
||||
|
||||
## 🧪 Branch: `feature/context-with-observations`
|
||||
|
||||
**Status:** Seeking user feedback before merging to main
|
||||
|
||||
**We'd love your testing and feedback!** This experimental branch reimagines how Claude-Mem presents context at session startup, using a progressive disclosure approach that could significantly improve Claude's ability to leverage past learnings.
|
||||
|
||||
---
|
||||
|
||||
## What is Progressive Disclosure?
|
||||
|
||||
Progressive disclosure is a **layered memory retrieval system** inspired by how humans remember information:
|
||||
|
||||
### Layer 1: Index (The "Table of Contents")
|
||||
**Frontloaded at session start** - Claude sees:
|
||||
- **What exists**: Titles of all recent observations and session summaries
|
||||
- **Retrieval cost**: Token counts for each observation
|
||||
- **Priority signals**: Type indicators (🔴 critical gotcha, 🟤 architectural decision, 🔵 explanatory)
|
||||
|
||||
### Layer 2: Details (On-Demand Retrieval)
|
||||
**Retrieved via MCP search** - Claude fetches:
|
||||
- Full observation narratives when deeper context is needed
|
||||
- Search by concept, file path, type, or keywords
|
||||
- Only loads what's relevant to the current task
|
||||
|
||||
### Layer 3: Perfect Recall (Source of Truth)
|
||||
**Direct code access** - When needed:
|
||||
- Read actual source files for implementation details
|
||||
- Access original transcripts for exact quotes
|
||||
- Full context without compression artifacts
|
||||
|
||||
---
|
||||
|
||||
## The Problem This Solves
|
||||
|
||||
### Current Version (v4.2.x) Limitation
|
||||
|
||||
The current context hook shows **only session summaries** at startup:
|
||||
|
||||
```markdown
|
||||
**Session #312**: Put date/time at end of session titles
|
||||
Completed: Added date/time to session list with proper formatting
|
||||
Next Steps: Test edge cases with long dates
|
||||
```
|
||||
|
||||
**Strengths:**
|
||||
- ✅ Minimal token overhead (~800 tokens)
|
||||
- ✅ Clean, readable summaries
|
||||
|
||||
**Weaknesses:**
|
||||
- ❌ Claude doesn't know **what** detailed observations exist
|
||||
- ❌ Can't make informed decisions about whether to search vs read code
|
||||
- ❌ Often re-reads code to understand decisions that were already documented
|
||||
|
||||
### Experimental Version Enhancement
|
||||
|
||||
The experimental hook shows an **observation index** alongside session summaries:
|
||||
|
||||
```markdown
|
||||
**src/hooks/context.ts**
|
||||
| ID | Time | T | Title | Tokens |
|
||||
|----|------|---|-------|--------|
|
||||
| #2332 | 1:07 AM | 🔴 | Critical Bugfix: Session ID NULL Constraint | ~201 |
|
||||
| #2340 | 1:10 AM | 🟠 | Remove Redundant Summary Section | ~280 |
|
||||
| #2344 | 1:34 AM | 🔵 | Added progressive disclosure usage instructions | ~149 |
|
||||
```
|
||||
|
||||
**Benefits:**
|
||||
- ✅ Claude knows **what** learnings exist (titles/types)
|
||||
- ✅ Token counts inform **cost-benefit** decisions (fetch ~200 tokens vs re-read 2000-line file)
|
||||
- ✅ Progressive disclosure instructions **teach Claude** how to use the system
|
||||
- ✅ Type indicators help prioritize (critical gotchas > explanatory notes)
|
||||
|
||||
**Trade-offs:**
|
||||
- ⚠️ Higher initial token cost (~2,500 tokens vs ~800)
|
||||
- ⚠️ More visual noise in the context output
|
||||
- ❓ Unknown: Does this actually improve Claude's behavior enough to justify the cost?
|
||||
|
||||
---
|
||||
|
||||
## What's New in This Branch
|
||||
|
||||
### 1. Observation Index Display
|
||||
|
||||
Full table view of recent observations grouped by file:
|
||||
|
||||
```markdown
|
||||
### Oct 25, 2025
|
||||
|
||||
**src/hooks/context.ts**
|
||||
| ID | Time | T | Title | Tokens |
|
||||
|----|------|---|-------|--------|
|
||||
| #2296 | 12:12 AM | 🟢 | Session summaries now display date and time | ~141 |
|
||||
| #2298 | 12:44 AM | 🔵 | Timeline rendering refactored | ~231 |
|
||||
|
||||
**General**
|
||||
| ID | Time | T | Title | Tokens |
|
||||
|----|------|---|-------|--------|
|
||||
| #2301 | 12:50 AM | 🟢 | Development Task Breakdown Created | ~128 |
|
||||
```
|
||||
|
||||
### 2. Token Cost Metadata
|
||||
|
||||
Every observation shows estimated token count:
|
||||
- Helps Claude decide: "Is it worth fetching this 500-token explanation, or should I just read the code?"
|
||||
- Makes cost-benefit analysis explicit
|
||||
|
||||
### 3. Progressive Disclosure Instructions
|
||||
|
||||
New guidance section teaches Claude how to use the system:
|
||||
|
||||
```markdown
|
||||
💡 Progressive Disclosure: This index shows WHAT exists (titles) and retrieval COST (token counts).
|
||||
- Use MCP search tools to fetch full observation details on-demand (Layer 2)
|
||||
- Prefer searching observations over re-reading code for past decisions and learnings
|
||||
- Critical types (🔴 gotcha, 🟤 decision, ⚖️ trade-off) often worth fetching immediately
|
||||
```
|
||||
|
||||
### 4. Type-Based Priority System
|
||||
|
||||
Observations categorized by importance:
|
||||
- 🔴 **gotcha** - Critical bugs/blockers (fetch immediately)
|
||||
- 🟤 **decision** - Architectural choices (high value)
|
||||
- ⚖️ **trade-off** - Design considerations (prevents re-debating)
|
||||
- 🟠 **why-it-exists** - Rationale documentation
|
||||
- 🟡 **problem-solution** - How issues were solved
|
||||
- 🟣 **discovery** - Important learnings
|
||||
- 🔵 **how-it-works** - Explanatory/educational
|
||||
- 🟢 **what-changed** - Implementation details
|
||||
|
||||
---
|
||||
|
||||
## Testing Instructions
|
||||
|
||||
### Option 1: Quick Test (No Installation)
|
||||
|
||||
```bash
|
||||
# Clone and checkout experimental branch
|
||||
git clone https://github.com/thedotmack/claude-mem.git
|
||||
cd claude-mem
|
||||
git checkout feature/context-with-observations
|
||||
|
||||
# Build the experimental version
|
||||
npm install
|
||||
npm run build
|
||||
|
||||
# Navigate to YOUR project directory
|
||||
cd /path/to/your/project
|
||||
|
||||
# Run the experimental context hook with full path
|
||||
node /path/to/claude-mem/plugin/scripts/context-hook.js
|
||||
|
||||
# Example:
|
||||
# cd ~/my-app
|
||||
# node ~/Downloads/claude-mem/plugin/scripts/context-hook.js
|
||||
```
|
||||
|
||||
**Important:** The context hook reads from the current working directory (cwd). You must run it from your project's root folder to see context for that specific project.
|
||||
|
||||
This shows you the new context format without installing the plugin.
|
||||
|
||||
### Option 2: Full Testing (Install Locally)
|
||||
|
||||
If you're already using claude-mem and want to test the experimental version:
|
||||
|
||||
```bash
|
||||
# Navigate to your local claude-mem plugin directory
|
||||
cd ~/.claude/plugins/marketplaces/thedotmack
|
||||
|
||||
# Checkout experimental branch
|
||||
git fetch origin
|
||||
git checkout feature/context-with-observations
|
||||
|
||||
# Rebuild
|
||||
npm install
|
||||
npm run build
|
||||
|
||||
# Restart Claude Code to see the new context injection
|
||||
```
|
||||
|
||||
**⚠️ Warning:** This will replace your current context hook. To revert:
|
||||
```bash
|
||||
git checkout main
|
||||
npm run build
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## What We Want to Know
|
||||
|
||||
Please test the experimental branch and share your feedback on these questions:
|
||||
|
||||
### 1. Behavioral Impact
|
||||
- ✅ **Does Claude use MCP search more effectively?**
|
||||
- Does it fetch observation details more often?
|
||||
- Does it make better decisions about when to search vs read code?
|
||||
|
||||
### 2. Token Cost Analysis
|
||||
- 💰 **Do token counts influence Claude's retrieval decisions?**
|
||||
- Does Claude reference the token counts when deciding whether to fetch?
|
||||
- Example: "This observation is 500 tokens, so I'll read the code instead"
|
||||
|
||||
### 3. Instruction Effectiveness
|
||||
- 📖 **Is the progressive disclosure guidance helpful or noisy?**
|
||||
- Does Claude seem to understand the layered retrieval concept?
|
||||
- Do the instructions clutter the context or improve clarity?
|
||||
|
||||
### 4. Efficiency Gains
|
||||
- 🚀 **Does it reduce redundant code reading?**
|
||||
- Does Claude fetch learnings instead of re-reading entire files?
|
||||
- Overall: Is it faster/smarter despite the higher initial token cost?
|
||||
|
||||
### 5. User Experience
|
||||
- 👤 **Is the observation table too cluttered?**
|
||||
- Does the table format help or hurt readability?
|
||||
- Would you prefer a different presentation?
|
||||
|
||||
---
|
||||
|
||||
## How to Provide Feedback
|
||||
|
||||
### 📣 GitHub Issues (Please Use This!)
|
||||
|
||||
**[→ Click here to open a new issue](https://github.com/thedotmack/claude-mem/issues/new)**
|
||||
|
||||
Add the label `feedback: progressive-disclosure` and use this template:
|
||||
|
||||
```markdown
|
||||
## Progressive Disclosure Feedback
|
||||
|
||||
**Branch tested:** feature/context-with-observations
|
||||
**Test duration:** [e.g., 2 days, 10 sessions]
|
||||
**Project type:** [e.g., TypeScript library, React app, Python backend]
|
||||
|
||||
### What worked well:
|
||||
- [Your positive observations]
|
||||
|
||||
### What didn't work:
|
||||
- [Issues or concerns]
|
||||
|
||||
### Specific answers:
|
||||
1. **Claude's MCP search usage:** [Improved/Same/Worse]
|
||||
2. **Token count influence:** [Yes/No/Unclear]
|
||||
3. **Instructions helpful:** [Yes/No/Too verbose]
|
||||
4. **Code reading reduction:** [Yes/No/Hard to tell]
|
||||
5. **Overall impression:** [Worth merging/Needs work/Not useful]
|
||||
|
||||
### Additional notes:
|
||||
[Any other feedback, screenshots, or examples]
|
||||
```
|
||||
|
||||
**Why issues?** It keeps all feedback in one searchable place and lets other users see what's being discussed. Please don't hesitate to open an issue - all feedback is valuable, positive or negative!
|
||||
|
||||
---
|
||||
|
||||
## Next Steps
|
||||
|
||||
Based on feedback, we'll decide:
|
||||
|
||||
### ✅ If Successful:
|
||||
- Merge to `main` branch
|
||||
- Release as v4.3.0
|
||||
- Make progressive disclosure the default
|
||||
- Potentially add verbosity settings (minimal/standard/detailed)
|
||||
|
||||
### ⚠️ If Mixed Results:
|
||||
- Make it opt-in via settings: `CLAUDE_MEM_VERBOSE_CONTEXT=true`
|
||||
- Default to current minimal approach
|
||||
- Allow users to choose their preference
|
||||
|
||||
### ❌ If Unsuccessful:
|
||||
- Keep as experimental branch
|
||||
- Continue iterating on the approach
|
||||
- May explore alternative presentation formats
|
||||
|
||||
---
|
||||
|
||||
## Technical Details
|
||||
|
||||
### Files Changed
|
||||
|
||||
- **src/hooks/context.ts** (lines 227-240)
|
||||
- Added progressive disclosure instructions
|
||||
- Enhanced observation table rendering
|
||||
- Token count display for each observation
|
||||
|
||||
### Token Cost Breakdown
|
||||
|
||||
**Current version (v4.2.x):**
|
||||
- Session summaries only: ~800 tokens
|
||||
- 3 sessions × ~250 tokens each
|
||||
- Minimal overhead
|
||||
|
||||
**Experimental version:**
|
||||
- Progressive disclosure instructions: ~150 tokens
|
||||
- Observation index: ~2,000 tokens
|
||||
- 50 observations × ~40 tokens per row
|
||||
- Session summaries: ~800 tokens
|
||||
- **Total: ~2,950 tokens**
|
||||
|
||||
**ROI Analysis:**
|
||||
- If this prevents even ONE 2,000-token file read per session, it pays for itself
|
||||
- If Claude makes smarter retrieval decisions, overall token usage could be lower
|
||||
|
||||
---
|
||||
|
||||
## Acknowledgments
|
||||
|
||||
This experimental feature was inspired by:
|
||||
- Anthropic's "Effective context engineering for AI agents" (Sept 2025)
|
||||
- Claude Skills' progressive disclosure architecture (Oct 2025)
|
||||
- Real-world usage patterns from 200+ GitHub stars in 36 hours
|
||||
|
||||
Special thanks to our early adopters for pushing the boundaries of what's possible with persistent memory!
|
||||
|
||||
---
|
||||
|
||||
## Questions?
|
||||
|
||||
- 📖 **Docs:** [docs/](docs/)
|
||||
- 🐛 **Issues:** [GitHub Issues](https://github.com/thedotmack/claude-mem/issues)
|
||||
- 💬 **Discussion:** [GitHub Discussions](https://github.com/thedotmack/claude-mem/discussions)
|
||||
|
||||
---
|
||||
|
||||
**Happy Testing!** 🧪
|
||||
|
||||
We're excited to hear what you discover with progressive disclosure. This could be a game-changer for how Claude leverages long-term memory, but we need your real-world testing to validate the approach.
|
||||
|
||||
— Alex Newman ([@thedotmack](https://github.com/thedotmack))
|
||||
@@ -0,0 +1,83 @@
|
||||
# 🧪 Experimental: Progressive Disclosure Context System
|
||||
|
||||
> **We'd love your feedback!** Test the new context injection approach and share your experience.
|
||||
|
||||
## What is Progressive Disclosure?
|
||||
|
||||
A **layered memory retrieval system** that shows Claude:
|
||||
1. **Index** (frontloaded): What observations exist + token costs
|
||||
2. **Details** (on-demand): Full narratives via MCP search
|
||||
3. **Perfect recall**: Source code when needed
|
||||
|
||||
**The idea:** Instead of hiding observations completely, show an index so Claude can make informed decisions about what to fetch.
|
||||
|
||||
## Try It Out
|
||||
|
||||
```bash
|
||||
# Clone and build experimental version
|
||||
git clone https://github.com/thedotmack/claude-mem.git
|
||||
cd claude-mem
|
||||
git checkout feature/context-with-observations
|
||||
npm install && npm run build
|
||||
|
||||
# Navigate to YOUR project and run the hook
|
||||
cd /path/to/your/project
|
||||
node /path/to/claude-mem/plugin/scripts/context-hook.js
|
||||
```
|
||||
|
||||
**Important:** Run from your project's root directory to see context for that project.
|
||||
|
||||
## What's Different?
|
||||
|
||||
**Current (v4.2.x):** Session summaries only (~800 tokens)
|
||||
```markdown
|
||||
Session #312: Put date/time at end of session titles
|
||||
Completed: Added formatting
|
||||
Next: Test edge cases
|
||||
```
|
||||
|
||||
**Experimental:** Observation index + summaries (~2,500 tokens)
|
||||
```markdown
|
||||
**src/hooks/context.ts**
|
||||
| ID | Time | T | Title | Tokens |
|
||||
|----|------|---|-------|--------|
|
||||
| #2332 | 1:07 AM | 🔴 | Critical Bugfix: Session ID NULL | ~201 |
|
||||
| #2340 | 1:10 AM | 🟠 | Remove Redundant Summary Section | ~280 |
|
||||
```
|
||||
|
||||
Now Claude knows:
|
||||
- What learnings exist (without loading them)
|
||||
- Cost to fetch details (~200 tokens)
|
||||
- Priority (🔴 critical vs 🔵 informational)
|
||||
|
||||
## We Want Your Feedback
|
||||
|
||||
Test the experimental branch and tell us:
|
||||
|
||||
✅ **Does Claude use MCP search more effectively?**
|
||||
💰 **Do token counts influence retrieval decisions?**
|
||||
📖 **Are the instructions helpful or noisy?**
|
||||
🚀 **Does it reduce redundant code reading?**
|
||||
|
||||
### 📣 [Please Open a GitHub Issue](https://github.com/thedotmack/claude-mem/issues/new) With Your Experience!
|
||||
|
||||
Use the label `feedback: progressive-disclosure` - all feedback is valuable, positive or negative!
|
||||
|
||||
## Files Changed
|
||||
|
||||
- Updated `README.md` with experimental feature section
|
||||
- Enhanced `src/hooks/context.ts` with progressive disclosure instructions
|
||||
- New docs: `EXPERIMENTAL_RELEASE_NOTES.md` (full details)
|
||||
|
||||
## Next Steps
|
||||
|
||||
Based on your feedback:
|
||||
- ✅ **If successful:** Merge to main, release as v4.3.0
|
||||
- ⚠️ **If mixed:** Make opt-in via settings
|
||||
- ❌ **If unsuccessful:** Keep iterating as experimental
|
||||
|
||||
---
|
||||
|
||||
**Full details:** See [EXPERIMENTAL_RELEASE_NOTES.md](EXPERIMENTAL_RELEASE_NOTES.md)
|
||||
|
||||
**Questions?** Join the discussion or open an issue!
|
||||
File diff suppressed because it is too large
Load Diff
-297
@@ -1,297 +0,0 @@
|
||||
# Claude Never Forgets
|
||||
|
||||
**Give Claude a memory that spans your entire project.**
|
||||
|
||||
---
|
||||
|
||||
## The Problem
|
||||
|
||||
```
|
||||
You: "Remember that bug we fixed last Tuesday with the auth flow?"
|
||||
Claude: "I don't have access to previous conversations..."
|
||||
```
|
||||
|
||||
Every `/clear` wipes Claude's memory. Every new session starts from zero. You repeat yourself constantly.
|
||||
|
||||
**Until now.**
|
||||
|
||||
---
|
||||
|
||||
## What Changes
|
||||
|
||||
### Before claude-mem
|
||||
```typescript
|
||||
// Monday: You debug an issue
|
||||
You: "Why is the database connection failing?"
|
||||
Claude: [Helps you fix it]
|
||||
|
||||
// Wednesday: Similar issue appears
|
||||
You: "The database is timing out again"
|
||||
Claude: "Let me investigate..." [Starts from scratch]
|
||||
```
|
||||
|
||||
### After claude-mem
|
||||
```typescript
|
||||
// Monday: You debug an issue
|
||||
You: "Why is the database connection failing?"
|
||||
Claude: [Helps you fix it]
|
||||
✓ Remembers: connection pool exhaustion pattern
|
||||
|
||||
// Wednesday: Similar issue appears
|
||||
You: "The database is timing out again"
|
||||
Claude: "Based on Monday's session, this looks like the same
|
||||
connection pool issue. Let me check the pool size config..."
|
||||
```
|
||||
|
||||
Claude **remembers**. Claude **learns**. Claude gets **better** over time.
|
||||
|
||||
---
|
||||
|
||||
## Real Examples
|
||||
|
||||
### 1. **Context Across Sessions**
|
||||
|
||||
**Without claude-mem:**
|
||||
```
|
||||
Session 1: "We use Redux for state management"
|
||||
Session 2: "What state management do you use?" ❌
|
||||
```
|
||||
|
||||
**With claude-mem:**
|
||||
```
|
||||
Session 1: "We use Redux for state management"
|
||||
Session 2: Claude already knows you use Redux ✓
|
||||
Suggests Redux patterns automatically ✓
|
||||
References your store structure ✓
|
||||
```
|
||||
|
||||
### 2. **Architectural Memory**
|
||||
|
||||
**Your third session of the day:**
|
||||
```
|
||||
You: "Add a new API endpoint for user preferences"
|
||||
|
||||
Claude: "I see from previous sessions that:
|
||||
- Your API follows REST conventions in src/api/
|
||||
- You use Zod for validation
|
||||
- Auth middleware is required for user routes
|
||||
- You prefer async/await over promises
|
||||
|
||||
I'll create the endpoint following these patterns..."
|
||||
```
|
||||
|
||||
**No explaining. No repeating. Just building.**
|
||||
|
||||
### 3. **Bug Pattern Recognition**
|
||||
|
||||
```
|
||||
Week 1: Fixed race condition in webhook handler
|
||||
Week 2: Different race condition in event processor
|
||||
|
||||
Claude: "This looks similar to the webhook race condition
|
||||
we fixed last week. The same solution should work..."
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## How It Works
|
||||
|
||||
```
|
||||
┌─────────────────┐
|
||||
│ You code with │
|
||||
│ Claude today │
|
||||
└────────┬────────┘
|
||||
│
|
||||
▼
|
||||
┌─────────────────────────────┐
|
||||
│ claude-mem captures & │
|
||||
│ compresses everything │
|
||||
│ into structured memories │
|
||||
└────────┬────────────────────┘
|
||||
│
|
||||
▼
|
||||
┌─────────────────────────────┐
|
||||
│ Tomorrow, Claude starts │
|
||||
│ with full context of │
|
||||
│ your project history │
|
||||
└─────────────────────────────┘
|
||||
```
|
||||
|
||||
**Automatic. Zero effort. Always on.**
|
||||
|
||||
---
|
||||
|
||||
## What Gets Remembered
|
||||
|
||||
✓ **Decisions**: "Why did we choose this architecture?"
|
||||
✓ **Bugs Fixed**: "How did we solve this before?"
|
||||
✓ **Code Patterns**: "What's our convention for this?"
|
||||
✓ **File Changes**: "What did we modify last session?"
|
||||
✓ **Refactorings**: "What was the old implementation?"
|
||||
✓ **Dependencies**: "Which libraries are we using?"
|
||||
|
||||
Everything Claude does with you gets compressed into **searchable, reusable memory**.
|
||||
|
||||
---
|
||||
|
||||
## Powerful Search
|
||||
|
||||
Ask Claude to search your project history:
|
||||
|
||||
```
|
||||
You: "Find all the database migrations we did"
|
||||
Claude: [Searches across all sessions]
|
||||
"I found 7 database-related changes:
|
||||
- March 15: Added user_preferences table
|
||||
- March 12: Migration for OAuth tokens
|
||||
- March 8: Index optimization on sessions
|
||||
..."
|
||||
|
||||
You: "What decisions did we make about authentication?"
|
||||
Claude: [Retrieves decision observations]
|
||||
"We decided to use JWT tokens because..."
|
||||
```
|
||||
|
||||
7 specialized search tools. Instant recall. Full project history.
|
||||
|
||||
---
|
||||
|
||||
## The Numbers
|
||||
|
||||
| Metric | Before | After |
|
||||
|--------|--------|-------|
|
||||
| Context repetition | Every session | Never |
|
||||
| Onboarding time | 5-10 min per session | 0 seconds |
|
||||
| Bug re-investigation | Common | Rare |
|
||||
| Architectural questions | "What did we decide?" | Claude already knows |
|
||||
| Code pattern consistency | Manual enforcement | Automatic |
|
||||
|
||||
---
|
||||
|
||||
## Installation
|
||||
|
||||
### Quick Start (2 minutes)
|
||||
|
||||
```bash
|
||||
# 1. Clone and install
|
||||
git clone https://github.com/thedotmack/claude-mem.git
|
||||
cd claude-mem
|
||||
|
||||
# 2. Add to Claude Code
|
||||
/plugin marketplace add .claude-plugin/marketplace.json
|
||||
|
||||
# 3. Install
|
||||
/plugin install claude-mem
|
||||
```
|
||||
|
||||
**Done.** Claude now has memory.
|
||||
|
||||
---
|
||||
|
||||
## Configuration
|
||||
|
||||
Choose your AI model (controls cost vs. quality of memory compression):
|
||||
|
||||
```bash
|
||||
./claude-mem-settings.sh
|
||||
```
|
||||
|
||||
**Models:**
|
||||
- `claude-haiku-4-5` - Fast & cheap
|
||||
- `claude-sonnet-4-5` - Balanced (default) ✓
|
||||
- `claude-opus-4` - Maximum quality
|
||||
|
||||
---
|
||||
|
||||
## Under The Hood
|
||||
|
||||
**Simple architecture, powerful results:**
|
||||
|
||||
1. **Hooks** capture every tool Claude uses
|
||||
2. **Worker service** compresses observations with AI
|
||||
3. **SQLite database** stores structured memories
|
||||
4. **MCP server** makes everything searchable
|
||||
5. **Context injection** gives Claude the right memories at the right time
|
||||
|
||||
**Zero maintenance. Runs in the background. Just works.**
|
||||
|
||||
---
|
||||
|
||||
## Use Cases
|
||||
|
||||
### Solo Developers
|
||||
- Never lose context between coding sessions
|
||||
- Build on past decisions automatically
|
||||
- Remember why you made each choice
|
||||
|
||||
### Team Projects
|
||||
- Share architectural knowledge across sessions
|
||||
- Maintain consistency in code patterns
|
||||
- Document decisions as they happen
|
||||
|
||||
### Learning & Experiments
|
||||
- Track what you tried and what worked
|
||||
- Build a personal knowledge base
|
||||
- Learn from past mistakes
|
||||
|
||||
### Large Refactors
|
||||
- Remember what you changed across multiple sessions
|
||||
- Track progress on multi-day tasks
|
||||
- Maintain context through interruptions
|
||||
|
||||
---
|
||||
|
||||
## What Developers Say
|
||||
|
||||
> *"I used to spend 10 minutes every morning explaining my project to Claude. Now it just knows."*
|
||||
|
||||
> *"It's like having a teammate who was actually there for every line of code."*
|
||||
|
||||
> *"The search is incredible. I can ask about decisions we made weeks ago."*
|
||||
|
||||
---
|
||||
|
||||
## FAQ
|
||||
|
||||
**Does this slow down Claude?**
|
||||
No. Memory processing happens in the background. Claude responds instantly.
|
||||
|
||||
**How much does it cost?**
|
||||
Minimal. Memory compression uses your chosen model (default: Sonnet 4.5). Typical cost: $0.01-0.05 per coding session.
|
||||
|
||||
**Where is data stored?**
|
||||
Locally in `~/.claude-mem/claude-mem.db`. Fully private. Never leaves your machine.
|
||||
|
||||
**Can I search my memories?**
|
||||
Yes. 7 specialized search tools available through Claude.
|
||||
|
||||
**Does it work with existing projects?**
|
||||
Yes. Starts learning immediately when installed.
|
||||
|
||||
**What if I want to forget something?**
|
||||
Delete observations directly from the SQLite database, or start fresh by removing the DB file.
|
||||
|
||||
---
|
||||
|
||||
## Get Started
|
||||
|
||||
```bash
|
||||
git clone https://github.com/thedotmack/claude-mem.git
|
||||
cd claude-mem
|
||||
/plugin marketplace add .claude-plugin/marketplace.json
|
||||
/plugin install claude-mem
|
||||
```
|
||||
|
||||
**Give Claude a memory. Transform how you code.**
|
||||
|
||||
---
|
||||
|
||||
## Learn More
|
||||
|
||||
- [Technical Documentation](./CLAUDE.md)
|
||||
- [GitHub Repository](https://github.com/thedotmack/claude-mem)
|
||||
- [Report Issues](https://github.com/thedotmack/claude-mem/issues)
|
||||
|
||||
**License**: AGPL-3.0
|
||||
**Version**: 4.1.0
|
||||
**Author**: Alex Newman ([@thedotmack](https://github.com/thedotmack))
|
||||
@@ -1,246 +0,0 @@
|
||||
# Plan: Store Raw User Prompts for Full Context Search
|
||||
|
||||
## Problem
|
||||
|
||||
Claude-mem currently only captures tool executions (via PostToolUse hook), not the user's actual instructions and requests. This creates a gap:
|
||||
|
||||
- We can see WHAT Claude did (observations)
|
||||
- We can see SUMMARIES of what happened (session_summaries)
|
||||
- We CANNOT see what the user actually said/requested
|
||||
|
||||
**Real Example:**
|
||||
User repeatedly asked to "remove session validation" with increasing frustration over multiple prompts. The memory system captured the final implementation but not the conversation where the user had to convince Claude 3-4 times.
|
||||
|
||||
## Solution
|
||||
|
||||
Store ALL raw user prompts in a dedicated table with full-text search capability.
|
||||
|
||||
## Database Schema Changes
|
||||
|
||||
### 1. Create `user_prompts` table
|
||||
|
||||
```sql
|
||||
CREATE TABLE user_prompts (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
sdk_session_id TEXT NOT NULL,
|
||||
prompt_number INTEGER NOT NULL,
|
||||
prompt_text TEXT NOT NULL,
|
||||
created_at TEXT NOT NULL,
|
||||
created_at_epoch INTEGER NOT NULL,
|
||||
FOREIGN KEY(sdk_session_id) REFERENCES sdk_sessions(sdk_session_id) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
CREATE INDEX idx_user_prompts_sdk_session ON user_prompts(sdk_session_id);
|
||||
CREATE INDEX idx_user_prompts_created ON user_prompts(created_at_epoch DESC);
|
||||
CREATE INDEX idx_user_prompts_prompt_number ON user_prompts(prompt_number);
|
||||
```
|
||||
|
||||
### 2. Create FTS5 virtual table for full-text search
|
||||
|
||||
```sql
|
||||
CREATE VIRTUAL TABLE user_prompts_fts USING fts5(
|
||||
prompt_text,
|
||||
content='user_prompts',
|
||||
content_rowid='id'
|
||||
);
|
||||
```
|
||||
|
||||
### 3. Create triggers to sync FTS5
|
||||
|
||||
```sql
|
||||
CREATE TRIGGER user_prompts_ai AFTER INSERT ON user_prompts BEGIN
|
||||
INSERT INTO user_prompts_fts(rowid, prompt_text)
|
||||
VALUES (new.id, new.prompt_text);
|
||||
END;
|
||||
|
||||
CREATE TRIGGER user_prompts_ad AFTER DELETE ON user_prompts BEGIN
|
||||
INSERT INTO user_prompts_fts(user_prompts_fts, rowid, prompt_text)
|
||||
VALUES('delete', old.id, old.prompt_text);
|
||||
END;
|
||||
|
||||
CREATE TRIGGER user_prompts_au AFTER UPDATE ON user_prompts BEGIN
|
||||
INSERT INTO user_prompts_fts(user_prompts_fts, rowid, prompt_text)
|
||||
VALUES('delete', old.id, old.prompt_text);
|
||||
INSERT INTO user_prompts_fts(rowid, prompt_text)
|
||||
VALUES (new.id, new.prompt_text);
|
||||
END;
|
||||
```
|
||||
|
||||
## Code Changes
|
||||
|
||||
### 1. Update schema migration
|
||||
|
||||
**File:** `src/services/sqlite/SessionStore.ts`
|
||||
|
||||
Add the user_prompts table creation to the schema initialization (look for the `CREATE TABLE` statements in the constructor).
|
||||
|
||||
### 2. Add method to save user prompts
|
||||
|
||||
**File:** `src/services/sqlite/SessionStore.ts`
|
||||
|
||||
```typescript
|
||||
/**
|
||||
* Save a user prompt
|
||||
*/
|
||||
saveUserPrompt(sdkSessionId: string, promptNumber: number, promptText: string): number {
|
||||
const now = new Date();
|
||||
const nowEpoch = now.getTime();
|
||||
|
||||
const stmt = this.db.prepare(`
|
||||
INSERT INTO user_prompts
|
||||
(sdk_session_id, prompt_number, prompt_text, created_at, created_at_epoch)
|
||||
VALUES (?, ?, ?, ?, ?)
|
||||
`);
|
||||
|
||||
const result = stmt.run(sdkSessionId, promptNumber, promptText, now.toISOString(), nowEpoch);
|
||||
return result.lastInsertRowid as number;
|
||||
}
|
||||
```
|
||||
|
||||
### 3. Update new-hook to save user prompts
|
||||
|
||||
**File:** `src/hooks/new.ts`
|
||||
|
||||
After creating/getting the session and incrementing prompt counter, save the raw prompt:
|
||||
|
||||
```typescript
|
||||
// Around line 37, after incrementPromptCounter
|
||||
const sessionDbId = db.createSDKSession(session_id, project, prompt);
|
||||
const promptNumber = db.incrementPromptCounter(sessionDbId);
|
||||
|
||||
// Get sdk_session_id for foreign key
|
||||
const session = db.findAnySDKSession(session_id);
|
||||
if (session?.sdk_session_id) {
|
||||
db.saveUserPrompt(session.sdk_session_id, promptNumber, prompt);
|
||||
}
|
||||
|
||||
console.error(`[new-hook] Session ${sessionDbId}, prompt #${promptNumber}`);
|
||||
```
|
||||
|
||||
**Note:** We need the sdk_session_id (not just sessionDbId) for the foreign key. May need to adjust the logic to handle prompts before SDK session is fully initialized.
|
||||
|
||||
### 4. Add SessionSearch methods
|
||||
|
||||
**File:** `src/services/sqlite/SessionSearch.ts`
|
||||
|
||||
```typescript
|
||||
/**
|
||||
* Search user prompts with full-text search
|
||||
*/
|
||||
searchUserPrompts(query: string, options: SearchOptions = {}): UserPrompt[] {
|
||||
const { limit = 20, offset = 0 } = options;
|
||||
|
||||
const stmt = this.db.prepare(`
|
||||
SELECT
|
||||
up.id,
|
||||
up.sdk_session_id,
|
||||
up.prompt_number,
|
||||
up.prompt_text,
|
||||
up.created_at,
|
||||
up.created_at_epoch
|
||||
FROM user_prompts_fts fts
|
||||
JOIN user_prompts up ON fts.rowid = up.id
|
||||
WHERE user_prompts_fts MATCH ?
|
||||
ORDER BY rank
|
||||
LIMIT ? OFFSET ?
|
||||
`);
|
||||
|
||||
return stmt.all(query, limit, offset) as UserPrompt[];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all prompts for a session
|
||||
*/
|
||||
getUserPromptsBySession(sdkSessionId: string): UserPrompt[] {
|
||||
const stmt = this.db.prepare(`
|
||||
SELECT
|
||||
id,
|
||||
sdk_session_id,
|
||||
prompt_number,
|
||||
prompt_text,
|
||||
created_at,
|
||||
created_at_epoch
|
||||
FROM user_prompts
|
||||
WHERE sdk_session_id = ?
|
||||
ORDER BY prompt_number ASC
|
||||
`);
|
||||
|
||||
return stmt.all(sdkSessionId) as UserPrompt[];
|
||||
}
|
||||
```
|
||||
|
||||
Add the `UserPrompt` type:
|
||||
|
||||
```typescript
|
||||
interface UserPrompt {
|
||||
id: number;
|
||||
sdk_session_id: string;
|
||||
prompt_number: number;
|
||||
prompt_text: string;
|
||||
created_at: string;
|
||||
created_at_epoch: number;
|
||||
}
|
||||
```
|
||||
|
||||
### 5. Add MCP search tool
|
||||
|
||||
**File:** `src/servers/search-server.ts`
|
||||
|
||||
Add a new tool `search_user_prompts`:
|
||||
|
||||
```typescript
|
||||
{
|
||||
name: 'search_user_prompts',
|
||||
description: 'Search raw user prompts with full-text search. Use this to find what the user actually said/requested across all sessions.',
|
||||
inputSchema: zodToJsonSchema(z.object({
|
||||
query: z.string().describe('Search query for FTS5 full-text search'),
|
||||
limit: z.number().optional().default(20).describe('Maximum results'),
|
||||
offset: z.number().optional().default(0).describe('Results to skip'),
|
||||
}))
|
||||
}
|
||||
```
|
||||
|
||||
And implement the handler.
|
||||
|
||||
## Benefits
|
||||
|
||||
1. **Full context reconstruction:** See exact user language, frustration level, repeated requests
|
||||
2. **Pattern detection:** Identify when Claude isn't listening (user repeats same request)
|
||||
3. **Improved summaries:** AI can reference actual user words, not just observations
|
||||
4. **Debugging:** Trace from user request → Claude actions → outcomes
|
||||
5. **Search across time:** "How many times did user ask for X feature?"
|
||||
|
||||
## Testing
|
||||
|
||||
1. Create a new session and submit several prompts
|
||||
2. Query `user_prompts` table to verify they're saved
|
||||
3. Test FTS5 search: `SELECT * FROM user_prompts_fts WHERE user_prompts_fts MATCH 'validation'`
|
||||
4. Test MCP tool: `/claude-mem search_user_prompts "remove validation"`
|
||||
5. Verify prompt_number increments correctly
|
||||
6. Test cascade delete: delete a session, verify prompts are deleted
|
||||
|
||||
## Migration Notes
|
||||
|
||||
- This is a NEW table, no data migration needed
|
||||
- Existing sessions won't have historical prompts (that's fine)
|
||||
- FTS5 triggers will auto-populate as new prompts arrive
|
||||
|
||||
## Open Questions
|
||||
|
||||
1. **sdk_session_id timing:** New-hook runs BEFORE worker initializes SDK session. Need to either:
|
||||
- Save prompts with sessionDbId initially, update with sdk_session_id later
|
||||
- OR use sessionDbId as the foreign key instead
|
||||
|
||||
2. **Storage size:** User prompts can be large. Consider max length or compression?
|
||||
|
||||
3. **Privacy:** User prompts may contain sensitive info. Document this clearly.
|
||||
|
||||
## Implementation Order
|
||||
|
||||
1. Add schema to SessionStore constructor
|
||||
2. Add saveUserPrompt method to SessionStore
|
||||
3. Add search methods to SessionSearch
|
||||
4. Update new-hook to save prompts
|
||||
5. Add MCP tool to search-server
|
||||
6. Test end-to-end
|
||||
7. Update CLAUDE.md documentation
|
||||
@@ -1,12 +1,45 @@
|
||||
# Claude-Mem
|
||||
<h1 align="center">
|
||||
<br>
|
||||
<a href="https://github.com/thedotmack/claude-mem">
|
||||
<picture>
|
||||
<source media="(prefers-color-scheme: dark)" srcset="docs/claude-mem-logo-for-dark-mode.webp">
|
||||
<source media="(prefers-color-scheme: light)" srcset="docs/claude-mem-logo-for-light-mode.webp">
|
||||
<img src="docs/claude-mem-logo-for-light-mode.webp" alt="Claude-Mem" width="400">
|
||||
</picture>
|
||||
</a>
|
||||
<br>
|
||||
</h1>
|
||||
|
||||
**Persistent memory compression system for Claude Code**
|
||||
<h4 align="center">Persistent memory compression system built for <a href="https://claude.com/claude-code" target="_blank">Claude Code</a>.</h4>
|
||||
|
||||
Claude-Mem seamlessly preserves context across sessions by automatically capturing tool usage observations, generating semantic summaries, and making them available to future sessions. This enables Claude to maintain continuity of knowledge about projects even after sessions end or reconnect.
|
||||
<p align="center">
|
||||
<a href="LICENSE">
|
||||
<img src="https://img.shields.io/badge/License-AGPL%203.0-blue.svg" alt="License">
|
||||
</a>
|
||||
<a href="package.json">
|
||||
<img src="https://img.shields.io/badge/version-4.3.0-green.svg" alt="Version">
|
||||
</a>
|
||||
<a href="package.json">
|
||||
<img src="https://img.shields.io/badge/node-%3E%3D18.0.0-brightgreen.svg" alt="Node">
|
||||
</a>
|
||||
<a href="https://github.com/hesreallyhim/awesome-claude-code">
|
||||
<img src="https://awesome.re/mentioned-badge.svg" alt="Mentioned in Awesome Claude Code">
|
||||
</a>
|
||||
</p>
|
||||
|
||||
[](LICENSE)
|
||||
[](package.json)
|
||||
[](package.json)
|
||||
<p align="center">
|
||||
<a href="#quick-start">Quick Start</a> •
|
||||
<a href="#how-it-works">How It Works</a> •
|
||||
<a href="#mcp-search-tools">Search Tools</a> •
|
||||
<a href="#documentation">Documentation</a> •
|
||||
<a href="#configuration">Configuration</a> •
|
||||
<a href="#troubleshooting">Troubleshooting</a> •
|
||||
<a href="#license">License</a>
|
||||
</p>
|
||||
|
||||
<p align="center">
|
||||
Claude-Mem seamlessly preserves context across sessions by automatically capturing tool usage observations, generating semantic summaries, and making them available to future sessions. This enables Claude to maintain continuity of knowledge about projects even after sessions end or reconnect.
|
||||
</p>
|
||||
|
||||
---
|
||||
|
||||
@@ -24,31 +57,39 @@ Restart Claude Code. Context from previous sessions will automatically appear in
|
||||
|
||||
**Key Features:**
|
||||
- 🧠 **Persistent Memory** - Context survives across sessions
|
||||
- 📊 **Progressive Disclosure** - Layered memory retrieval with token cost visibility
|
||||
- 🔍 **7 Search Tools** - Query your project history via MCP
|
||||
- 🤖 **Automatic Operation** - No manual intervention required
|
||||
- 📊 **FTS5 Search** - Fast full-text search across observations
|
||||
- 🔗 **Citations** - Reference past decisions with `claude-mem://` URIs
|
||||
|
||||
---
|
||||
|
||||
## Documentation
|
||||
|
||||
📚 **[View Full Documentation](docs/)** - Browse markdown docs on GitHub
|
||||
|
||||
💻 **Local Preview**: Run Mintlify docs locally:
|
||||
```bash
|
||||
cd docs
|
||||
npx mintlify dev
|
||||
```
|
||||
|
||||
### Getting Started
|
||||
- **[Installation Guide](docs/installation.md)** - Quick start & advanced installation
|
||||
- **[Usage Guide](docs/usage/getting-started.md)** - How Claude-Mem works automatically
|
||||
- **[MCP Search Tools](docs/usage/search-tools.md)** - Query your project history
|
||||
- **[Installation Guide](docs/installation.mdx)** - Quick start & advanced installation
|
||||
- **[Usage Guide](docs/usage/getting-started.mdx)** - How Claude-Mem works automatically
|
||||
- **[MCP Search Tools](docs/usage/search-tools.mdx)** - Query your project history
|
||||
|
||||
### Architecture
|
||||
- **[Overview](docs/architecture/overview.md)** - System components & data flow
|
||||
- **[Hooks](docs/architecture/hooks.md)** - 5 lifecycle hooks explained
|
||||
- **[Worker Service](docs/architecture/worker-service.md)** - HTTP API & PM2 management
|
||||
- **[Database](docs/architecture/database.md)** - SQLite schema & FTS5 search
|
||||
- **[MCP Search](docs/architecture/mcp-search.md)** - 7 search tools & examples
|
||||
- **[Overview](docs/architecture/overview.mdx)** - System components & data flow
|
||||
- **[Hooks](docs/architecture/hooks.mdx)** - 5 lifecycle hooks explained
|
||||
- **[Worker Service](docs/architecture/worker-service.mdx)** - HTTP API & PM2 management
|
||||
- **[Database](docs/architecture/database.mdx)** - SQLite schema & FTS5 search
|
||||
- **[MCP Search](docs/architecture/mcp-search.mdx)** - 7 search tools & examples
|
||||
|
||||
### Configuration & Development
|
||||
- **[Configuration](docs/configuration.md)** - Environment variables & settings
|
||||
- **[Development](docs/development.md)** - Building, testing, contributing
|
||||
- **[Troubleshooting](docs/troubleshooting.md)** - Common issues & solutions
|
||||
- **[Configuration](docs/configuration.mdx)** - Environment variables & settings
|
||||
- **[Development](docs/development.mdx)** - Building, testing, contributing
|
||||
- **[Troubleshooting](docs/troubleshooting.mdx)** - Common issues & solutions
|
||||
|
||||
---
|
||||
|
||||
@@ -82,7 +123,7 @@ Restart Claude Code. Context from previous sessions will automatically appear in
|
||||
3. **SQLite Database** - Stores sessions, observations, summaries with FTS5 search
|
||||
4. **7 MCP Search Tools** - Query historical context with citations
|
||||
|
||||
See [Architecture Overview](docs/architecture/overview.md) for details.
|
||||
See [Architecture Overview](docs/architecture/overview.mdx) for details.
|
||||
|
||||
---
|
||||
|
||||
@@ -106,20 +147,22 @@ search_user_prompts with query="add dark mode"
|
||||
get_recent_context with limit=5
|
||||
```
|
||||
|
||||
See [MCP Search Tools Guide](docs/usage/search-tools.md) for detailed examples.
|
||||
See [MCP Search Tools Guide](docs/usage/search-tools.mdx) for detailed examples.
|
||||
|
||||
---
|
||||
|
||||
## What's New in v4.2.3
|
||||
## What's New in v4.3.0
|
||||
|
||||
**Security:**
|
||||
- Fixed FTS5 injection vulnerability in search functions
|
||||
- Added comprehensive test suite with 332 injection attack tests
|
||||
**Progressive Disclosure Context:**
|
||||
- Enhanced context hook displays observation timeline with token cost visibility
|
||||
- Table format shows ID, timestamp, type indicators (🔴 critical, 🟤 decision, 🔵 informational), title, and token counts
|
||||
- Progressive disclosure instructions guide Claude on when to fetch full observation details vs. reading code
|
||||
- Layered memory retrieval: Index → Details → Perfect Recall (code/transcripts)
|
||||
|
||||
**Fixes:**
|
||||
- Fixed ESM/CJS compatibility for getDirname function
|
||||
- Fixed Windows PowerShell compatibility in SessionStart hook
|
||||
- Cross-platform dependency installation now works on Windows, macOS, and Linux
|
||||
**Improvements:**
|
||||
- Added Agent Skills documentation and version bump management skill
|
||||
- Removed hardcoded paths for project and Claude Code executable (fixes #23)
|
||||
- Enhanced session summary handling and timeline rendering
|
||||
|
||||
See [CHANGELOG.md](CHANGELOG.md) for complete version history.
|
||||
|
||||
@@ -136,6 +179,14 @@ See [CHANGELOG.md](CHANGELOG.md) for complete version history.
|
||||
|
||||
## Key Benefits
|
||||
|
||||
### Progressive Disclosure Context
|
||||
- **Layered memory retrieval** mirrors human memory patterns
|
||||
- **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 3 (Perfect Recall)**: Access source code and original transcripts
|
||||
- **Smart decision-making**: Token counts help Claude choose between fetching details or reading code
|
||||
- **Type indicators**: Visual cues (🔴 critical, 🟤 decision, 🔵 informational) highlight observation importance
|
||||
|
||||
### Automatic Memory
|
||||
- Context automatically injected when Claude starts
|
||||
- No manual commands or configuration needed
|
||||
@@ -170,7 +221,7 @@ See [CHANGELOG.md](CHANGELOG.md) for complete version history.
|
||||
- `CLAUDE_MEM_WORKER_PORT` - Worker port (default: 37777)
|
||||
- `CLAUDE_MEM_DATA_DIR` - Data directory override (dev only)
|
||||
|
||||
See [Configuration Guide](docs/configuration.md) for details.
|
||||
See [Configuration Guide](docs/configuration.mdx) for details.
|
||||
|
||||
---
|
||||
|
||||
@@ -193,7 +244,7 @@ npm run worker:start
|
||||
npm run worker:logs
|
||||
```
|
||||
|
||||
See [Development Guide](docs/development.md) for detailed instructions.
|
||||
See [Development Guide](docs/development.mdx) for detailed instructions.
|
||||
|
||||
---
|
||||
|
||||
@@ -205,7 +256,7 @@ See [Development Guide](docs/development.md) for detailed instructions.
|
||||
- Database issues → `sqlite3 ~/.claude-mem/claude-mem.db "PRAGMA integrity_check;"`
|
||||
- Search not working → Check FTS5 tables exist
|
||||
|
||||
See [Troubleshooting Guide](docs/troubleshooting.md) for complete solutions.
|
||||
See [Troubleshooting Guide](docs/troubleshooting.mdx) for complete solutions.
|
||||
|
||||
---
|
||||
|
||||
@@ -219,7 +270,7 @@ Contributions are welcome! Please:
|
||||
4. Update documentation
|
||||
5. Submit a Pull Request
|
||||
|
||||
See [Development Guide](docs/development.md) for contribution workflow.
|
||||
See [Development Guide](docs/development.mdx) for contribution workflow.
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -1,260 +0,0 @@
|
||||
{
|
||||
"lockfileVersion": 1,
|
||||
"workspaces": {
|
||||
"": {
|
||||
"name": "claude-mem",
|
||||
"dependencies": {
|
||||
"@anthropic-ai/claude-agent-sdk": "^0.1.0",
|
||||
"@clack/prompts": "^0.11.0",
|
||||
"better-sqlite3": "^11.8.1",
|
||||
"boxen": "^8.0.1",
|
||||
"chalk": "^5.6.0",
|
||||
"commander": "^14.0.0",
|
||||
"glob": "^11.0.3",
|
||||
"gradient-string": "^3.0.0",
|
||||
"handlebars": "^4.7.8",
|
||||
},
|
||||
},
|
||||
},
|
||||
"packages": {
|
||||
"@anthropic-ai/claude-agent-sdk": ["@anthropic-ai/claude-agent-sdk@0.1.15", "", { "optionalDependencies": { "@img/sharp-darwin-arm64": "^0.33.5", "@img/sharp-darwin-x64": "^0.33.5", "@img/sharp-linux-arm": "^0.33.5", "@img/sharp-linux-arm64": "^0.33.5", "@img/sharp-linux-x64": "^0.33.5", "@img/sharp-win32-x64": "^0.33.5" }, "peerDependencies": { "zod": "^3.24.1" } }, "sha512-x0UR/YW87lRel3wYVimWjAkUOEGapg/nXx2GYNLbUD1ORsetRHeXZGFdJMuhRkk1Jt9sbn5m/RpT42b5R0pgYg=="],
|
||||
|
||||
"@clack/core": ["@clack/core@0.5.0", "", { "dependencies": { "picocolors": "^1.0.0", "sisteransi": "^1.0.5" } }, "sha512-p3y0FIOwaYRUPRcMO7+dlmLh8PSRcrjuTndsiA0WAFbWES0mLZlrjVoBRZ9DzkPFJZG6KGkJmoEAY0ZcVWTkow=="],
|
||||
|
||||
"@clack/prompts": ["@clack/prompts@0.11.0", "", { "dependencies": { "@clack/core": "0.5.0", "picocolors": "^1.0.0", "sisteransi": "^1.0.5" } }, "sha512-pMN5FcrEw9hUkZA4f+zLlzivQSeQf5dRGJjSUbvVYDLvpKCdQx5OaknvKzgbtXOizhP+SJJJjqEbOe55uKKfAw=="],
|
||||
|
||||
"@img/sharp-darwin-arm64": ["@img/sharp-darwin-arm64@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-darwin-arm64": "1.0.4" }, "os": "darwin", "cpu": "arm64" }, "sha512-UT4p+iz/2H4twwAoLCqfA9UH5pI6DggwKEGuaPy7nCVQ8ZsiY5PIcrRvD1DzuY3qYL07NtIQcWnBSY/heikIFQ=="],
|
||||
|
||||
"@img/sharp-darwin-x64": ["@img/sharp-darwin-x64@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-darwin-x64": "1.0.4" }, "os": "darwin", "cpu": "x64" }, "sha512-fyHac4jIc1ANYGRDxtiqelIbdWkIuQaI84Mv45KvGRRxSAa7o7d1ZKAOBaYbnepLC1WqxfpimdeWfvqqSGwR2Q=="],
|
||||
|
||||
"@img/sharp-libvips-darwin-arm64": ["@img/sharp-libvips-darwin-arm64@1.0.4", "", { "os": "darwin", "cpu": "arm64" }, "sha512-XblONe153h0O2zuFfTAbQYAX2JhYmDHeWikp1LM9Hul9gVPjFY427k6dFEcOL72O01QxQsWi761svJ/ev9xEDg=="],
|
||||
|
||||
"@img/sharp-libvips-darwin-x64": ["@img/sharp-libvips-darwin-x64@1.0.4", "", { "os": "darwin", "cpu": "x64" }, "sha512-xnGR8YuZYfJGmWPvmlunFaWJsb9T/AO2ykoP3Fz/0X5XV2aoYBPkX6xqCQvUTKKiLddarLaxpzNe+b1hjeWHAQ=="],
|
||||
|
||||
"@img/sharp-libvips-linux-arm": ["@img/sharp-libvips-linux-arm@1.0.5", "", { "os": "linux", "cpu": "arm" }, "sha512-gvcC4ACAOPRNATg/ov8/MnbxFDJqf/pDePbBnuBDcjsI8PssmjoKMAz4LtLaVi+OnSb5FK/yIOamqDwGmXW32g=="],
|
||||
|
||||
"@img/sharp-libvips-linux-arm64": ["@img/sharp-libvips-linux-arm64@1.0.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-9B+taZ8DlyyqzZQnoeIvDVR/2F4EbMepXMc/NdVbkzsJbzkUjhXv/70GQJ7tdLA4YJgNP25zukcxpX2/SueNrA=="],
|
||||
|
||||
"@img/sharp-libvips-linux-x64": ["@img/sharp-libvips-linux-x64@1.0.4", "", { "os": "linux", "cpu": "x64" }, "sha512-MmWmQ3iPFZr0Iev+BAgVMb3ZyC4KeFc3jFxnNbEPas60e1cIfevbtuyf9nDGIzOaW9PdnDciJm+wFFaTlj5xYw=="],
|
||||
|
||||
"@img/sharp-linux-arm": ["@img/sharp-linux-arm@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-arm": "1.0.5" }, "os": "linux", "cpu": "arm" }, "sha512-JTS1eldqZbJxjvKaAkxhZmBqPRGmxgu+qFKSInv8moZ2AmT5Yib3EQ1c6gp493HvrvV8QgdOXdyaIBrhvFhBMQ=="],
|
||||
|
||||
"@img/sharp-linux-arm64": ["@img/sharp-linux-arm64@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-arm64": "1.0.4" }, "os": "linux", "cpu": "arm64" }, "sha512-JMVv+AMRyGOHtO1RFBiJy/MBsgz0x4AWrT6QoEVVTyh1E39TrCUpTRI7mx9VksGX4awWASxqCYLCV4wBZHAYxA=="],
|
||||
|
||||
"@img/sharp-linux-x64": ["@img/sharp-linux-x64@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-x64": "1.0.4" }, "os": "linux", "cpu": "x64" }, "sha512-opC+Ok5pRNAzuvq1AG0ar+1owsu842/Ab+4qvU879ippJBHvyY5n2mxF1izXqkPYlGuP/M556uh53jRLJmzTWA=="],
|
||||
|
||||
"@img/sharp-win32-x64": ["@img/sharp-win32-x64@0.33.5", "", { "os": "win32", "cpu": "x64" }, "sha512-MpY/o8/8kj+EcnxwvrP4aTJSWw/aZ7JIGR4aBeZkZw5B7/Jn+tY9/VNwtcoGmdT7GfggGIU4kygOMSbYnOrAbg=="],
|
||||
|
||||
"@isaacs/balanced-match": ["@isaacs/balanced-match@4.0.1", "", {}, "sha512-yzMTt9lEb8Gv7zRioUilSglI0c0smZ9k5D65677DLWLtWJaXIS3CqcGyUFByYKlnUj6TkjLVs54fBl6+TiGQDQ=="],
|
||||
|
||||
"@isaacs/brace-expansion": ["@isaacs/brace-expansion@5.0.0", "", { "dependencies": { "@isaacs/balanced-match": "^4.0.1" } }, "sha512-ZT55BDLV0yv0RBm2czMiZ+SqCGO7AvmOM3G/w2xhVPH+te0aKgFjmBvGlL1dH+ql2tgGO3MVrbb3jCKyvpgnxA=="],
|
||||
|
||||
"@isaacs/cliui": ["@isaacs/cliui@8.0.2", "", { "dependencies": { "string-width": "^5.1.2", "string-width-cjs": "npm:string-width@^4.2.0", "strip-ansi": "^7.0.1", "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", "wrap-ansi": "^8.1.0", "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" } }, "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA=="],
|
||||
|
||||
"@types/tinycolor2": ["@types/tinycolor2@1.4.6", "", {}, "sha512-iEN8J0BoMnsWBqjVbWH/c0G0Hh7O21lpR2/+PrvAVgWdzL7eexIFm4JN/Wn10PTcmNdtS6U67r499mlWMXOxNw=="],
|
||||
|
||||
"ansi-align": ["ansi-align@3.0.1", "", { "dependencies": { "string-width": "^4.1.0" } }, "sha512-IOfwwBF5iczOjp/WeY4YxyjqAFMQoZufdQWDd19SEExbVLNXqvpzSJ/M7Za4/sCPmQ0+GRquoA7bGcINcxew6w=="],
|
||||
|
||||
"ansi-regex": ["ansi-regex@6.2.2", "", {}, "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg=="],
|
||||
|
||||
"ansi-styles": ["ansi-styles@6.2.3", "", {}, "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg=="],
|
||||
|
||||
"base64-js": ["base64-js@1.5.1", "", {}, "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA=="],
|
||||
|
||||
"better-sqlite3": ["better-sqlite3@11.10.0", "", { "dependencies": { "bindings": "^1.5.0", "prebuild-install": "^7.1.1" } }, "sha512-EwhOpyXiOEL/lKzHz9AW1msWFNzGc/z+LzeB3/jnFJpxu+th2yqvzsSWas1v9jgs9+xiXJcD5A8CJxAG2TaghQ=="],
|
||||
|
||||
"bindings": ["bindings@1.5.0", "", { "dependencies": { "file-uri-to-path": "1.0.0" } }, "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ=="],
|
||||
|
||||
"bl": ["bl@4.1.0", "", { "dependencies": { "buffer": "^5.5.0", "inherits": "^2.0.4", "readable-stream": "^3.4.0" } }, "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w=="],
|
||||
|
||||
"boxen": ["boxen@8.0.1", "", { "dependencies": { "ansi-align": "^3.0.1", "camelcase": "^8.0.0", "chalk": "^5.3.0", "cli-boxes": "^3.0.0", "string-width": "^7.2.0", "type-fest": "^4.21.0", "widest-line": "^5.0.0", "wrap-ansi": "^9.0.0" } }, "sha512-F3PH5k5juxom4xktynS7MoFY+NUWH5LC4CnH11YB8NPew+HLpmBLCybSAEyb2F+4pRXhuhWqFesoQd6DAyc2hw=="],
|
||||
|
||||
"buffer": ["buffer@5.7.1", "", { "dependencies": { "base64-js": "^1.3.1", "ieee754": "^1.1.13" } }, "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ=="],
|
||||
|
||||
"camelcase": ["camelcase@8.0.0", "", {}, "sha512-8WB3Jcas3swSvjIeA2yvCJ+Miyz5l1ZmB6HFb9R1317dt9LCQoswg/BGrmAmkWVEszSrrg4RwmO46qIm2OEnSA=="],
|
||||
|
||||
"chalk": ["chalk@5.6.2", "", {}, "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA=="],
|
||||
|
||||
"chownr": ["chownr@1.1.4", "", {}, "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg=="],
|
||||
|
||||
"cli-boxes": ["cli-boxes@3.0.0", "", {}, "sha512-/lzGpEWL/8PfI0BmBOPRwp0c/wFNX1RdUML3jK/RcSBA9T8mZDdQpqYBKtCFTOfQbwPqWEOpjqW+Fnayc0969g=="],
|
||||
|
||||
"color-convert": ["color-convert@2.0.1", "", { "dependencies": { "color-name": "~1.1.4" } }, "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ=="],
|
||||
|
||||
"color-name": ["color-name@1.1.4", "", {}, "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="],
|
||||
|
||||
"commander": ["commander@14.0.1", "", {}, "sha512-2JkV3gUZUVrbNA+1sjBOYLsMZ5cEEl8GTFP2a4AVz5hvasAMCQ1D2l2le/cX+pV4N6ZU17zjUahLpIXRrnWL8A=="],
|
||||
|
||||
"cross-spawn": ["cross-spawn@7.0.6", "", { "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", "which": "^2.0.1" } }, "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA=="],
|
||||
|
||||
"decompress-response": ["decompress-response@6.0.0", "", { "dependencies": { "mimic-response": "^3.1.0" } }, "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ=="],
|
||||
|
||||
"deep-extend": ["deep-extend@0.6.0", "", {}, "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA=="],
|
||||
|
||||
"detect-libc": ["detect-libc@2.1.2", "", {}, "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ=="],
|
||||
|
||||
"eastasianwidth": ["eastasianwidth@0.2.0", "", {}, "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA=="],
|
||||
|
||||
"emoji-regex": ["emoji-regex@10.6.0", "", {}, "sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A=="],
|
||||
|
||||
"end-of-stream": ["end-of-stream@1.4.5", "", { "dependencies": { "once": "^1.4.0" } }, "sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg=="],
|
||||
|
||||
"expand-template": ["expand-template@2.0.3", "", {}, "sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg=="],
|
||||
|
||||
"file-uri-to-path": ["file-uri-to-path@1.0.0", "", {}, "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw=="],
|
||||
|
||||
"foreground-child": ["foreground-child@3.3.1", "", { "dependencies": { "cross-spawn": "^7.0.6", "signal-exit": "^4.0.1" } }, "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw=="],
|
||||
|
||||
"fs-constants": ["fs-constants@1.0.0", "", {}, "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow=="],
|
||||
|
||||
"get-east-asian-width": ["get-east-asian-width@1.4.0", "", {}, "sha512-QZjmEOC+IT1uk6Rx0sX22V6uHWVwbdbxf1faPqJ1QhLdGgsRGCZoyaQBm/piRdJy/D2um6hM1UP7ZEeQ4EkP+Q=="],
|
||||
|
||||
"github-from-package": ["github-from-package@0.0.0", "", {}, "sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw=="],
|
||||
|
||||
"glob": ["glob@11.0.3", "", { "dependencies": { "foreground-child": "^3.3.1", "jackspeak": "^4.1.1", "minimatch": "^10.0.3", "minipass": "^7.1.2", "package-json-from-dist": "^1.0.0", "path-scurry": "^2.0.0" }, "bin": { "glob": "dist/esm/bin.mjs" } }, "sha512-2Nim7dha1KVkaiF4q6Dj+ngPPMdfvLJEOpZk/jKiUAkqKebpGAWQXAq9z1xu9HKu5lWfqw/FASuccEjyznjPaA=="],
|
||||
|
||||
"gradient-string": ["gradient-string@3.0.0", "", { "dependencies": { "chalk": "^5.3.0", "tinygradient": "^1.1.5" } }, "sha512-frdKI4Qi8Ihp4C6wZNB565de/THpIaw3DjP5ku87M+N9rNSGmPTjfkq61SdRXB7eCaL8O1hkKDvf6CDMtOzIAg=="],
|
||||
|
||||
"handlebars": ["handlebars@4.7.8", "", { "dependencies": { "minimist": "^1.2.5", "neo-async": "^2.6.2", "source-map": "^0.6.1", "wordwrap": "^1.0.0" }, "optionalDependencies": { "uglify-js": "^3.1.4" }, "bin": { "handlebars": "bin/handlebars" } }, "sha512-vafaFqs8MZkRrSX7sFVUdo3ap/eNiLnb4IakshzvP56X5Nr1iGKAIqdX6tMlm6HcNRIkr6AxO5jFEoJzzpT8aQ=="],
|
||||
|
||||
"ieee754": ["ieee754@1.2.1", "", {}, "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA=="],
|
||||
|
||||
"inherits": ["inherits@2.0.4", "", {}, "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="],
|
||||
|
||||
"ini": ["ini@1.3.8", "", {}, "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew=="],
|
||||
|
||||
"is-fullwidth-code-point": ["is-fullwidth-code-point@3.0.0", "", {}, "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg=="],
|
||||
|
||||
"isexe": ["isexe@2.0.0", "", {}, "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw=="],
|
||||
|
||||
"jackspeak": ["jackspeak@4.1.1", "", { "dependencies": { "@isaacs/cliui": "^8.0.2" } }, "sha512-zptv57P3GpL+O0I7VdMJNBZCu+BPHVQUk55Ft8/QCJjTVxrnJHuVuX/0Bl2A6/+2oyR/ZMEuFKwmzqqZ/U5nPQ=="],
|
||||
|
||||
"lru-cache": ["lru-cache@11.2.2", "", {}, "sha512-F9ODfyqML2coTIsQpSkRHnLSZMtkU8Q+mSfcaIyKwy58u+8k5nvAYeiNhsyMARvzNcXJ9QfWVrcPsC9e9rAxtg=="],
|
||||
|
||||
"mimic-response": ["mimic-response@3.1.0", "", {}, "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ=="],
|
||||
|
||||
"minimatch": ["minimatch@10.0.3", "", { "dependencies": { "@isaacs/brace-expansion": "^5.0.0" } }, "sha512-IPZ167aShDZZUMdRk66cyQAW3qr0WzbHkPdMYa8bzZhlHhO3jALbKdxcaak7W9FfT2rZNpQuUu4Od7ILEpXSaw=="],
|
||||
|
||||
"minimist": ["minimist@1.2.8", "", {}, "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA=="],
|
||||
|
||||
"minipass": ["minipass@7.1.2", "", {}, "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw=="],
|
||||
|
||||
"mkdirp-classic": ["mkdirp-classic@0.5.3", "", {}, "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A=="],
|
||||
|
||||
"napi-build-utils": ["napi-build-utils@2.0.0", "", {}, "sha512-GEbrYkbfF7MoNaoh2iGG84Mnf/WZfB0GdGEsM8wz7Expx/LlWf5U8t9nvJKXSp3qr5IsEbK04cBGhol/KwOsWA=="],
|
||||
|
||||
"neo-async": ["neo-async@2.6.2", "", {}, "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw=="],
|
||||
|
||||
"node-abi": ["node-abi@3.78.0", "", { "dependencies": { "semver": "^7.3.5" } }, "sha512-E2wEyrgX/CqvicaQYU3Ze1PFGjc4QYPGsjUrlYkqAE0WjHEZwgOsGMPMzkMse4LjJbDmaEuDX3CM036j5K2DSQ=="],
|
||||
|
||||
"once": ["once@1.4.0", "", { "dependencies": { "wrappy": "1" } }, "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w=="],
|
||||
|
||||
"package-json-from-dist": ["package-json-from-dist@1.0.1", "", {}, "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw=="],
|
||||
|
||||
"path-key": ["path-key@3.1.1", "", {}, "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q=="],
|
||||
|
||||
"path-scurry": ["path-scurry@2.0.0", "", { "dependencies": { "lru-cache": "^11.0.0", "minipass": "^7.1.2" } }, "sha512-ypGJsmGtdXUOeM5u93TyeIEfEhM6s+ljAhrk5vAvSx8uyY/02OvrZnA0YNGUrPXfpJMgI1ODd3nwz8Npx4O4cg=="],
|
||||
|
||||
"picocolors": ["picocolors@1.1.1", "", {}, "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="],
|
||||
|
||||
"prebuild-install": ["prebuild-install@7.1.3", "", { "dependencies": { "detect-libc": "^2.0.0", "expand-template": "^2.0.3", "github-from-package": "0.0.0", "minimist": "^1.2.3", "mkdirp-classic": "^0.5.3", "napi-build-utils": "^2.0.0", "node-abi": "^3.3.0", "pump": "^3.0.0", "rc": "^1.2.7", "simple-get": "^4.0.0", "tar-fs": "^2.0.0", "tunnel-agent": "^0.6.0" }, "bin": { "prebuild-install": "bin.js" } }, "sha512-8Mf2cbV7x1cXPUILADGI3wuhfqWvtiLA1iclTDbFRZkgRQS0NqsPZphna9V+HyTEadheuPmjaJMsbzKQFOzLug=="],
|
||||
|
||||
"pump": ["pump@3.0.3", "", { "dependencies": { "end-of-stream": "^1.1.0", "once": "^1.3.1" } }, "sha512-todwxLMY7/heScKmntwQG8CXVkWUOdYxIvY2s0VWAAMh/nd8SoYiRaKjlr7+iCs984f2P8zvrfWcDDYVb73NfA=="],
|
||||
|
||||
"rc": ["rc@1.2.8", "", { "dependencies": { "deep-extend": "^0.6.0", "ini": "~1.3.0", "minimist": "^1.2.0", "strip-json-comments": "~2.0.1" }, "bin": { "rc": "./cli.js" } }, "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw=="],
|
||||
|
||||
"readable-stream": ["readable-stream@3.6.2", "", { "dependencies": { "inherits": "^2.0.3", "string_decoder": "^1.1.1", "util-deprecate": "^1.0.1" } }, "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA=="],
|
||||
|
||||
"safe-buffer": ["safe-buffer@5.2.1", "", {}, "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ=="],
|
||||
|
||||
"semver": ["semver@7.7.3", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q=="],
|
||||
|
||||
"shebang-command": ["shebang-command@2.0.0", "", { "dependencies": { "shebang-regex": "^3.0.0" } }, "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA=="],
|
||||
|
||||
"shebang-regex": ["shebang-regex@3.0.0", "", {}, "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A=="],
|
||||
|
||||
"signal-exit": ["signal-exit@4.1.0", "", {}, "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw=="],
|
||||
|
||||
"simple-concat": ["simple-concat@1.0.1", "", {}, "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q=="],
|
||||
|
||||
"simple-get": ["simple-get@4.0.1", "", { "dependencies": { "decompress-response": "^6.0.0", "once": "^1.3.1", "simple-concat": "^1.0.0" } }, "sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA=="],
|
||||
|
||||
"sisteransi": ["sisteransi@1.0.5", "", {}, "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg=="],
|
||||
|
||||
"source-map": ["source-map@0.6.1", "", {}, "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="],
|
||||
|
||||
"string-width": ["string-width@7.2.0", "", { "dependencies": { "emoji-regex": "^10.3.0", "get-east-asian-width": "^1.0.0", "strip-ansi": "^7.1.0" } }, "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ=="],
|
||||
|
||||
"string-width-cjs": ["string-width@4.2.3", "", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="],
|
||||
|
||||
"string_decoder": ["string_decoder@1.3.0", "", { "dependencies": { "safe-buffer": "~5.2.0" } }, "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA=="],
|
||||
|
||||
"strip-ansi": ["strip-ansi@7.1.2", "", { "dependencies": { "ansi-regex": "^6.0.1" } }, "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA=="],
|
||||
|
||||
"strip-ansi-cjs": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="],
|
||||
|
||||
"strip-json-comments": ["strip-json-comments@2.0.1", "", {}, "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ=="],
|
||||
|
||||
"tar-fs": ["tar-fs@2.1.4", "", { "dependencies": { "chownr": "^1.1.1", "mkdirp-classic": "^0.5.2", "pump": "^3.0.0", "tar-stream": "^2.1.4" } }, "sha512-mDAjwmZdh7LTT6pNleZ05Yt65HC3E+NiQzl672vQG38jIrehtJk/J3mNwIg+vShQPcLF/LV7CMnDW6vjj6sfYQ=="],
|
||||
|
||||
"tar-stream": ["tar-stream@2.2.0", "", { "dependencies": { "bl": "^4.0.3", "end-of-stream": "^1.4.1", "fs-constants": "^1.0.0", "inherits": "^2.0.3", "readable-stream": "^3.1.1" } }, "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ=="],
|
||||
|
||||
"tinycolor2": ["tinycolor2@1.6.0", "", {}, "sha512-XPaBkWQJdsf3pLKJV9p4qN/S+fm2Oj8AIPo1BTUhg5oxkvm9+SVEGFdhyOz7tTdUTfvxMiAs4sp6/eZO2Ew+pw=="],
|
||||
|
||||
"tinygradient": ["tinygradient@1.1.5", "", { "dependencies": { "@types/tinycolor2": "^1.4.0", "tinycolor2": "^1.0.0" } }, "sha512-8nIfc2vgQ4TeLnk2lFj4tRLvvJwEfQuabdsmvDdQPT0xlk9TaNtpGd6nNRxXoK6vQhN6RSzj+Cnp5tTQmpxmbw=="],
|
||||
|
||||
"tunnel-agent": ["tunnel-agent@0.6.0", "", { "dependencies": { "safe-buffer": "^5.0.1" } }, "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w=="],
|
||||
|
||||
"type-fest": ["type-fest@4.41.0", "", {}, "sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA=="],
|
||||
|
||||
"uglify-js": ["uglify-js@3.19.3", "", { "bin": { "uglifyjs": "bin/uglifyjs" } }, "sha512-v3Xu+yuwBXisp6QYTcH4UbH+xYJXqnq2m/LtQVWKWzYc1iehYnLixoQDN9FH6/j9/oybfd6W9Ghwkl8+UMKTKQ=="],
|
||||
|
||||
"util-deprecate": ["util-deprecate@1.0.2", "", {}, "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw=="],
|
||||
|
||||
"which": ["which@2.0.2", "", { "dependencies": { "isexe": "^2.0.0" }, "bin": { "node-which": "./bin/node-which" } }, "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA=="],
|
||||
|
||||
"widest-line": ["widest-line@5.0.0", "", { "dependencies": { "string-width": "^7.0.0" } }, "sha512-c9bZp7b5YtRj2wOe6dlj32MK+Bx/M/d+9VB2SHM1OtsUHR0aV0tdP6DWh/iMt0kWi1t5g1Iudu6hQRNd1A4PVA=="],
|
||||
|
||||
"wordwrap": ["wordwrap@1.0.0", "", {}, "sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q=="],
|
||||
|
||||
"wrap-ansi": ["wrap-ansi@9.0.2", "", { "dependencies": { "ansi-styles": "^6.2.1", "string-width": "^7.0.0", "strip-ansi": "^7.1.0" } }, "sha512-42AtmgqjV+X1VpdOfyTGOYRi0/zsoLqtXQckTmqTeybT+BDIbM/Guxo7x3pE2vtpr1ok6xRqM9OpBe+Jyoqyww=="],
|
||||
|
||||
"wrap-ansi-cjs": ["wrap-ansi@7.0.0", "", { "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", "strip-ansi": "^6.0.0" } }, "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q=="],
|
||||
|
||||
"wrappy": ["wrappy@1.0.2", "", {}, "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ=="],
|
||||
|
||||
"zod": ["zod@3.25.76", "", {}, "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ=="],
|
||||
|
||||
"@isaacs/cliui/string-width": ["string-width@5.1.2", "", { "dependencies": { "eastasianwidth": "^0.2.0", "emoji-regex": "^9.2.2", "strip-ansi": "^7.0.1" } }, "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA=="],
|
||||
|
||||
"@isaacs/cliui/wrap-ansi": ["wrap-ansi@8.1.0", "", { "dependencies": { "ansi-styles": "^6.1.0", "string-width": "^5.0.1", "strip-ansi": "^7.0.1" } }, "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ=="],
|
||||
|
||||
"ansi-align/string-width": ["string-width@4.2.3", "", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="],
|
||||
|
||||
"string-width-cjs/emoji-regex": ["emoji-regex@8.0.0", "", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="],
|
||||
|
||||
"string-width-cjs/strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="],
|
||||
|
||||
"strip-ansi-cjs/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="],
|
||||
|
||||
"wrap-ansi-cjs/ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="],
|
||||
|
||||
"wrap-ansi-cjs/string-width": ["string-width@4.2.3", "", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="],
|
||||
|
||||
"wrap-ansi-cjs/strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="],
|
||||
|
||||
"@isaacs/cliui/string-width/emoji-regex": ["emoji-regex@9.2.2", "", {}, "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg=="],
|
||||
|
||||
"ansi-align/string-width/emoji-regex": ["emoji-regex@8.0.0", "", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="],
|
||||
|
||||
"ansi-align/string-width/strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="],
|
||||
|
||||
"string-width-cjs/strip-ansi/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="],
|
||||
|
||||
"wrap-ansi-cjs/string-width/emoji-regex": ["emoji-regex@8.0.0", "", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="],
|
||||
|
||||
"wrap-ansi-cjs/strip-ansi/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="],
|
||||
|
||||
"ansi-align/string-width/strip-ansi/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="],
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,6 @@
|
||||
# Claude-Mem Architecture Refactor Plan
|
||||
# Claude-Mem Architecture v3 to v4 Plan (✅ Completed)
|
||||
|
||||
This file exists as a reference to explain the path forward from v3 to v4.
|
||||
|
||||
## Core Purpose
|
||||
|
||||
@@ -0,0 +1,607 @@
|
||||
# Agent Skills
|
||||
|
||||
> Create, manage, and share Skills to extend Claude's capabilities in Claude Code.
|
||||
|
||||
This guide shows you how to create, use, and manage Agent Skills in Claude Code. Skills are modular capabilities that extend Claude's functionality through organized folders containing instructions, scripts, and resources.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
* Claude Code version 1.0 or later
|
||||
* Basic familiarity with [Claude Code](/en/docs/claude-code/quickstart)
|
||||
|
||||
## What are Agent Skills?
|
||||
|
||||
Agent Skills package expertise into discoverable capabilities. Each Skill consists of a `SKILL.md` file with instructions that Claude reads when relevant, plus optional supporting files like scripts and templates.
|
||||
|
||||
**How Skills are invoked**: Skills are **model-invoked**—Claude autonomously decides when to use them based on your request and the Skill's description. This is different from slash commands, which are **user-invoked** (you explicitly type `/command` to trigger them).
|
||||
|
||||
**Benefits**:
|
||||
|
||||
* Extend Claude's capabilities for your specific workflows
|
||||
* Share expertise across your team via git
|
||||
* Reduce repetitive prompting
|
||||
* Compose multiple Skills for complex tasks
|
||||
|
||||
Learn more in the [Agent Skills overview](/en/docs/agents-and-tools/agent-skills/overview).
|
||||
|
||||
<Note>
|
||||
For a deep dive into the architecture and real-world applications of Agent Skills, read our engineering blog: [Equipping agents for the real world with Agent Skills](https://www.anthropic.com/engineering/equipping-agents-for-the-real-world-with-agent-skills).
|
||||
</Note>
|
||||
|
||||
## Create a Skill
|
||||
|
||||
Skills are stored as directories containing a `SKILL.md` file.
|
||||
|
||||
### Personal Skills
|
||||
|
||||
Personal Skills are available across all your projects. Store them in `~/.claude/skills/`:
|
||||
|
||||
```bash theme={null}
|
||||
mkdir -p ~/.claude/skills/my-skill-name
|
||||
```
|
||||
|
||||
**Use personal Skills for**:
|
||||
|
||||
* Your individual workflows and preferences
|
||||
* Experimental Skills you're developing
|
||||
* Personal productivity tools
|
||||
|
||||
### Project Skills
|
||||
|
||||
Project Skills are shared with your team. Store them in `.claude/skills/` within your project:
|
||||
|
||||
```bash theme={null}
|
||||
mkdir -p .claude/skills/my-skill-name
|
||||
```
|
||||
|
||||
**Use project Skills for**:
|
||||
|
||||
* Team workflows and conventions
|
||||
* Project-specific expertise
|
||||
* Shared utilities and scripts
|
||||
|
||||
Project Skills are checked into git and automatically available to team members.
|
||||
|
||||
### Plugin Skills
|
||||
|
||||
Skills can also come from [Claude Code plugins](/en/docs/claude-code/plugins). Plugins may bundle Skills that are automatically available when the plugin is installed. These Skills work the same way as personal and project Skills.
|
||||
|
||||
## Write SKILL.md
|
||||
|
||||
Create a `SKILL.md` file with YAML frontmatter and Markdown content:
|
||||
|
||||
```yaml theme={null}
|
||||
---
|
||||
name: your-skill-name
|
||||
description: Brief description of what this Skill does and when to use it
|
||||
---
|
||||
|
||||
# Your Skill Name
|
||||
|
||||
## Instructions
|
||||
Provide clear, step-by-step guidance for Claude.
|
||||
|
||||
## Examples
|
||||
Show concrete examples of using this Skill.
|
||||
```
|
||||
|
||||
**Field requirements**:
|
||||
|
||||
* `name`: Must use lowercase letters, numbers, and hyphens only (max 64 characters)
|
||||
* `description`: Brief description of what the Skill does and when to use it (max 1024 characters)
|
||||
|
||||
The `description` field is critical for Claude to discover when to use your Skill. It should include both what the Skill does and when Claude should use it.
|
||||
|
||||
See the [best practices guide](/en/docs/agents-and-tools/agent-skills/best-practices) for complete authoring guidance including validation rules.
|
||||
|
||||
## Add supporting files
|
||||
|
||||
Create additional files alongside SKILL.md:
|
||||
|
||||
```
|
||||
my-skill/
|
||||
├── SKILL.md (required)
|
||||
├── reference.md (optional documentation)
|
||||
├── examples.md (optional examples)
|
||||
├── scripts/
|
||||
│ └── helper.py (optional utility)
|
||||
└── templates/
|
||||
└── template.txt (optional template)
|
||||
```
|
||||
|
||||
Reference these files from SKILL.md:
|
||||
|
||||
````markdown theme={null}
|
||||
For advanced usage, see [reference.md](reference.md).
|
||||
|
||||
Run the helper script:
|
||||
```bash
|
||||
python scripts/helper.py input.txt
|
||||
```
|
||||
````
|
||||
|
||||
Claude reads these files only when needed, using progressive disclosure to manage context efficiently.
|
||||
|
||||
## Restrict tool access with allowed-tools
|
||||
|
||||
Use the `allowed-tools` frontmatter field to limit which tools Claude can use when a Skill is active:
|
||||
|
||||
```yaml theme={null}
|
||||
---
|
||||
name: safe-file-reader
|
||||
description: Read files without making changes. Use when you need read-only file access.
|
||||
allowed-tools: Read, Grep, Glob
|
||||
---
|
||||
|
||||
# Safe File Reader
|
||||
|
||||
This Skill provides read-only file access.
|
||||
|
||||
## Instructions
|
||||
1. Use Read to view file contents
|
||||
2. Use Grep to search within files
|
||||
3. Use Glob to find files by pattern
|
||||
```
|
||||
|
||||
When this Skill is active, Claude can only use the specified tools (Read, Grep, Glob) without needing to ask for permission. This is useful for:
|
||||
|
||||
* Read-only Skills that shouldn't modify files
|
||||
* Skills with limited scope (e.g., only data analysis, no file writing)
|
||||
* Security-sensitive workflows where you want to restrict capabilities
|
||||
|
||||
If `allowed-tools` is not specified, Claude will ask for permission to use tools as normal, following the standard permission model.
|
||||
|
||||
<Note>
|
||||
`allowed-tools` is only supported for Skills in Claude Code.
|
||||
</Note>
|
||||
|
||||
## View available Skills
|
||||
|
||||
Skills are automatically discovered by Claude from three sources:
|
||||
|
||||
* Personal Skills: `~/.claude/skills/`
|
||||
* Project Skills: `.claude/skills/`
|
||||
* Plugin Skills: bundled with installed plugins
|
||||
|
||||
**To view all available Skills**, ask Claude directly:
|
||||
|
||||
```
|
||||
What Skills are available?
|
||||
```
|
||||
|
||||
or
|
||||
|
||||
```
|
||||
List all available Skills
|
||||
```
|
||||
|
||||
This will show all Skills from all sources, including plugin Skills.
|
||||
|
||||
**To inspect a specific Skill**, you can also check the filesystem:
|
||||
|
||||
```bash theme={null}
|
||||
# List personal Skills
|
||||
ls ~/.claude/skills/
|
||||
|
||||
# List project Skills (if in a project directory)
|
||||
ls .claude/skills/
|
||||
|
||||
# View a specific Skill's content
|
||||
cat ~/.claude/skills/my-skill/SKILL.md
|
||||
```
|
||||
|
||||
## Test a Skill
|
||||
|
||||
After creating a Skill, test it by asking questions that match your description.
|
||||
|
||||
**Example**: If your description mentions "PDF files":
|
||||
|
||||
```
|
||||
Can you help me extract text from this PDF?
|
||||
```
|
||||
|
||||
Claude autonomously decides to use your Skill if it matches the request—you don't need to explicitly invoke it. The Skill activates automatically based on the context of your question.
|
||||
|
||||
## Debug a Skill
|
||||
|
||||
If Claude doesn't use your Skill, check these common issues:
|
||||
|
||||
### Make description specific
|
||||
|
||||
**Too vague**:
|
||||
|
||||
```yaml theme={null}
|
||||
description: Helps with documents
|
||||
```
|
||||
|
||||
**Specific**:
|
||||
|
||||
```yaml theme={null}
|
||||
description: Extract text and tables from PDF files, fill forms, merge documents. Use when working with PDF files or when the user mentions PDFs, forms, or document extraction.
|
||||
```
|
||||
|
||||
Include both what the Skill does and when to use it in the description.
|
||||
|
||||
### Verify file path
|
||||
|
||||
**Personal Skills**: `~/.claude/skills/skill-name/SKILL.md`
|
||||
**Project Skills**: `.claude/skills/skill-name/SKILL.md`
|
||||
|
||||
Check the file exists:
|
||||
|
||||
```bash theme={null}
|
||||
# Personal
|
||||
ls ~/.claude/skills/my-skill/SKILL.md
|
||||
|
||||
# Project
|
||||
ls .claude/skills/my-skill/SKILL.md
|
||||
```
|
||||
|
||||
### Check YAML syntax
|
||||
|
||||
Invalid YAML prevents the Skill from loading. Verify the frontmatter:
|
||||
|
||||
```bash theme={null}
|
||||
cat SKILL.md | head -n 10
|
||||
```
|
||||
|
||||
Ensure:
|
||||
|
||||
* Opening `---` on line 1
|
||||
* Closing `---` before Markdown content
|
||||
* Valid YAML syntax (no tabs, correct indentation)
|
||||
|
||||
### View errors
|
||||
|
||||
Run Claude Code with debug mode to see Skill loading errors:
|
||||
|
||||
```bash theme={null}
|
||||
claude --debug
|
||||
```
|
||||
|
||||
## Share Skills with your team
|
||||
|
||||
**Recommended approach**: Distribute Skills through [plugins](/en/docs/claude-code/plugins).
|
||||
|
||||
To share Skills via plugin:
|
||||
|
||||
1. Create a plugin with Skills in the `skills/` directory
|
||||
2. Add the plugin to a marketplace
|
||||
3. Team members install the plugin
|
||||
|
||||
For complete instructions, see [Add Skills to your plugin](/en/docs/claude-code/plugins#add-skills-to-your-plugin).
|
||||
|
||||
You can also share Skills directly through project repositories:
|
||||
|
||||
### Step 1: Add Skill to your project
|
||||
|
||||
Create a project Skill:
|
||||
|
||||
```bash theme={null}
|
||||
mkdir -p .claude/skills/team-skill
|
||||
# Create SKILL.md
|
||||
```
|
||||
|
||||
### Step 2: Commit to git
|
||||
|
||||
```bash theme={null}
|
||||
git add .claude/skills/
|
||||
git commit -m "Add team Skill for PDF processing"
|
||||
git push
|
||||
```
|
||||
|
||||
### Step 3: Team members get Skills automatically
|
||||
|
||||
When team members pull the latest changes, Skills are immediately available:
|
||||
|
||||
```bash theme={null}
|
||||
git pull
|
||||
claude # Skills are now available
|
||||
```
|
||||
|
||||
## Update a Skill
|
||||
|
||||
Edit SKILL.md directly:
|
||||
|
||||
```bash theme={null}
|
||||
# Personal Skill
|
||||
code ~/.claude/skills/my-skill/SKILL.md
|
||||
|
||||
# Project Skill
|
||||
code .claude/skills/my-skill/SKILL.md
|
||||
```
|
||||
|
||||
Changes take effect the next time you start Claude Code. If Claude Code is already running, restart it to load the updates.
|
||||
|
||||
## Remove a Skill
|
||||
|
||||
Delete the Skill directory:
|
||||
|
||||
```bash theme={null}
|
||||
# Personal
|
||||
rm -rf ~/.claude/skills/my-skill
|
||||
|
||||
# Project
|
||||
rm -rf .claude/skills/my-skill
|
||||
git commit -m "Remove unused Skill"
|
||||
```
|
||||
|
||||
## Best practices
|
||||
|
||||
### Keep Skills focused
|
||||
|
||||
One Skill should address one capability:
|
||||
|
||||
**Focused**:
|
||||
|
||||
* "PDF form filling"
|
||||
* "Excel data analysis"
|
||||
* "Git commit messages"
|
||||
|
||||
**Too broad**:
|
||||
|
||||
* "Document processing" (split into separate Skills)
|
||||
* "Data tools" (split by data type or operation)
|
||||
|
||||
### Write clear descriptions
|
||||
|
||||
Help Claude discover when to use Skills by including specific triggers in your description:
|
||||
|
||||
**Clear**:
|
||||
|
||||
```yaml theme={null}
|
||||
description: Analyze Excel spreadsheets, create pivot tables, and generate charts. Use when working with Excel files, spreadsheets, or analyzing tabular data in .xlsx format.
|
||||
```
|
||||
|
||||
**Vague**:
|
||||
|
||||
```yaml theme={null}
|
||||
description: For files
|
||||
```
|
||||
|
||||
### Test with your team
|
||||
|
||||
Have teammates use Skills and provide feedback:
|
||||
|
||||
* Does the Skill activate when expected?
|
||||
* Are the instructions clear?
|
||||
* Are there missing examples or edge cases?
|
||||
|
||||
### Document Skill versions
|
||||
|
||||
You can document Skill versions in your SKILL.md content to track changes over time. Add a version history section:
|
||||
|
||||
```markdown theme={null}
|
||||
# My Skill
|
||||
|
||||
## Version History
|
||||
- v2.0.0 (2025-10-01): Breaking changes to API
|
||||
- v1.1.0 (2025-09-15): Added new features
|
||||
- v1.0.0 (2025-09-01): Initial release
|
||||
```
|
||||
|
||||
This helps team members understand what changed between versions.
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Claude doesn't use my Skill
|
||||
|
||||
**Symptom**: You ask a relevant question but Claude doesn't use your Skill.
|
||||
|
||||
**Check**: Is the description specific enough?
|
||||
|
||||
Vague descriptions make discovery difficult. Include both what the Skill does and when to use it, with key terms users would mention.
|
||||
|
||||
**Too generic**:
|
||||
|
||||
```yaml theme={null}
|
||||
description: Helps with data
|
||||
```
|
||||
|
||||
**Specific**:
|
||||
|
||||
```yaml theme={null}
|
||||
description: Analyze Excel spreadsheets, generate pivot tables, create charts. Use when working with Excel files, spreadsheets, or .xlsx files.
|
||||
```
|
||||
|
||||
**Check**: Is the YAML valid?
|
||||
|
||||
Run validation to check for syntax errors:
|
||||
|
||||
```bash theme={null}
|
||||
# View frontmatter
|
||||
cat .claude/skills/my-skill/SKILL.md | head -n 15
|
||||
|
||||
# Check for common issues
|
||||
# - Missing opening or closing ---
|
||||
# - Tabs instead of spaces
|
||||
# - Unquoted strings with special characters
|
||||
```
|
||||
|
||||
**Check**: Is the Skill in the correct location?
|
||||
|
||||
```bash theme={null}
|
||||
# Personal Skills
|
||||
ls ~/.claude/skills/*/SKILL.md
|
||||
|
||||
# Project Skills
|
||||
ls .claude/skills/*/SKILL.md
|
||||
```
|
||||
|
||||
### Skill has errors
|
||||
|
||||
**Symptom**: The Skill loads but doesn't work correctly.
|
||||
|
||||
**Check**: Are dependencies available?
|
||||
|
||||
Claude will automatically install required dependencies (or ask for permission to install them) when it needs them.
|
||||
|
||||
**Check**: Do scripts have execute permissions?
|
||||
|
||||
```bash theme={null}
|
||||
chmod +x .claude/skills/my-skill/scripts/*.py
|
||||
```
|
||||
|
||||
**Check**: Are file paths correct?
|
||||
|
||||
Use forward slashes (Unix style) in all paths:
|
||||
|
||||
**Correct**: `scripts/helper.py`
|
||||
**Wrong**: `scripts\helper.py` (Windows style)
|
||||
|
||||
### Multiple Skills conflict
|
||||
|
||||
**Symptom**: Claude uses the wrong Skill or seems confused between similar Skills.
|
||||
|
||||
**Be specific in descriptions**: Help Claude choose the right Skill by using distinct trigger terms in your descriptions.
|
||||
|
||||
Instead of:
|
||||
|
||||
```yaml theme={null}
|
||||
# Skill 1
|
||||
description: For data analysis
|
||||
|
||||
# Skill 2
|
||||
description: For analyzing data
|
||||
```
|
||||
|
||||
Use:
|
||||
|
||||
```yaml theme={null}
|
||||
# Skill 1
|
||||
description: Analyze sales data in Excel files and CRM exports. Use for sales reports, pipeline analysis, and revenue tracking.
|
||||
|
||||
# Skill 2
|
||||
description: Analyze log files and system metrics data. Use for performance monitoring, debugging, and system diagnostics.
|
||||
```
|
||||
|
||||
## Examples
|
||||
|
||||
### Simple Skill (single file)
|
||||
|
||||
```
|
||||
commit-helper/
|
||||
└── SKILL.md
|
||||
```
|
||||
|
||||
```yaml theme={null}
|
||||
---
|
||||
name: generating-commit-messages
|
||||
description: Generates clear commit messages from git diffs. Use when writing commit messages or reviewing staged changes.
|
||||
---
|
||||
|
||||
# Generating Commit Messages
|
||||
|
||||
## Instructions
|
||||
|
||||
1. Run `git diff --staged` to see changes
|
||||
2. I'll suggest a commit message with:
|
||||
- Summary under 50 characters
|
||||
- Detailed description
|
||||
- Affected components
|
||||
|
||||
## Best practices
|
||||
|
||||
- Use present tense
|
||||
- Explain what and why, not how
|
||||
```
|
||||
|
||||
### Skill with tool permissions
|
||||
|
||||
```
|
||||
code-reviewer/
|
||||
└── SKILL.md
|
||||
```
|
||||
|
||||
```yaml theme={null}
|
||||
---
|
||||
name: code-reviewer
|
||||
description: Review code for best practices and potential issues. Use when reviewing code, checking PRs, or analyzing code quality.
|
||||
allowed-tools: Read, Grep, Glob
|
||||
---
|
||||
|
||||
# Code Reviewer
|
||||
|
||||
## Review checklist
|
||||
|
||||
1. Code organization and structure
|
||||
2. Error handling
|
||||
3. Performance considerations
|
||||
4. Security concerns
|
||||
5. Test coverage
|
||||
|
||||
## Instructions
|
||||
|
||||
1. Read the target files using Read tool
|
||||
2. Search for patterns using Grep
|
||||
3. Find related files using Glob
|
||||
4. Provide detailed feedback on code quality
|
||||
```
|
||||
|
||||
### Multi-file Skill
|
||||
|
||||
```
|
||||
pdf-processing/
|
||||
├── SKILL.md
|
||||
├── FORMS.md
|
||||
├── REFERENCE.md
|
||||
└── scripts/
|
||||
├── fill_form.py
|
||||
└── validate.py
|
||||
```
|
||||
|
||||
**SKILL.md**:
|
||||
|
||||
````yaml theme={null}
|
||||
---
|
||||
name: pdf-processing
|
||||
description: Extract text, fill forms, merge PDFs. Use when working with PDF files, forms, or document extraction. Requires pypdf and pdfplumber packages.
|
||||
---
|
||||
|
||||
# PDF Processing
|
||||
|
||||
## Quick start
|
||||
|
||||
Extract text:
|
||||
```python
|
||||
import pdfplumber
|
||||
with pdfplumber.open("doc.pdf") as pdf:
|
||||
text = pdf.pages[0].extract_text()
|
||||
```
|
||||
|
||||
For form filling, see [FORMS.md](FORMS.md).
|
||||
For detailed API reference, see [REFERENCE.md](REFERENCE.md).
|
||||
|
||||
## Requirements
|
||||
|
||||
Packages must be installed in your environment:
|
||||
```bash
|
||||
pip install pypdf pdfplumber
|
||||
```
|
||||
````
|
||||
|
||||
<Note>
|
||||
List required packages in the description. Packages must be installed in your environment before Claude can use them.
|
||||
</Note>
|
||||
|
||||
Claude loads additional files only when needed.
|
||||
|
||||
## Next steps
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card title="Authoring best practices" icon="lightbulb" href="/en/docs/agents-and-tools/agent-skills/best-practices">
|
||||
Write Skills that Claude can use effectively
|
||||
</Card>
|
||||
|
||||
<Card title="Agent Skills overview" icon="book" href="/en/docs/agents-and-tools/agent-skills/overview">
|
||||
Learn how Skills work across Claude products
|
||||
</Card>
|
||||
|
||||
<Card title="Use Skills in the Agent SDK" icon="cube" href="/en/api/agent-sdk/skills">
|
||||
Use Skills programmatically with TypeScript and Python
|
||||
</Card>
|
||||
|
||||
<Card title="Get started with Agent Skills" icon="rocket" href="/en/docs/agents-and-tools/agent-skills/quickstart">
|
||||
Create your first Skill
|
||||
</Card>
|
||||
</CardGroup>
|
||||
@@ -1,283 +0,0 @@
|
||||
# Magic UI Components Catalog
|
||||
|
||||
Complete catalog of Magic UI components with descriptions and use cases.
|
||||
|
||||
## Animation Components
|
||||
|
||||
### 1. **Animated List**
|
||||
- **Purpose**: Animates list items sequentially with delay
|
||||
- **Use Case**: Showcasing events, notifications, feature lists
|
||||
- **Key Props**: `delay` (ms between items)
|
||||
- **Effect**: Staggered reveal animation
|
||||
|
||||
### 2. **Text Animate**
|
||||
- **Purpose**: Sophisticated text animation effects
|
||||
- **Animation Types**:
|
||||
- `blurIn` - Characters fade from blur
|
||||
- `slideUp` - Words slide up
|
||||
- `scaleUp` - Text scales up
|
||||
- `fadeIn` - Lines fade in
|
||||
- `slideLeft` - Characters slide from left
|
||||
- **Animate By**: `character`, `word`, `text`, `line`
|
||||
- **Use Case**: Hero headlines, feature announcements, storytelling
|
||||
- **Customization**: Delay, duration, custom motion variants
|
||||
|
||||
### 3. **Flip Text**
|
||||
- **Purpose**: Vertical flip animation for text
|
||||
- **Key Props**: `duration`, `delayMultiple`, custom variants
|
||||
- **Use Case**: Eye-catching headlines, call-to-actions
|
||||
|
||||
### 4. **Morphing Text**
|
||||
- **Purpose**: Dynamic text transitions between multiple strings
|
||||
- **Key Props**: `texts` array (strings to morph between)
|
||||
- **Use Case**: Dynamic value propositions, rotating benefits
|
||||
|
||||
### 5. **Word Rotate**
|
||||
- **Purpose**: Vertical rotation of words
|
||||
- **Key Props**: `words` (string array), `duration` (2500ms default)
|
||||
- **Use Case**: Rotating feature names, dynamic headlines
|
||||
|
||||
### 6. **Aurora Text**
|
||||
- **Purpose**: Beautiful aurora text effect
|
||||
- **Key Props**: `colors` array, `speed` multiplier
|
||||
- **Default Colors**: `["#FF0080", "#7928CA", "#0070F3", "#38bdf8"]`
|
||||
- **Use Case**: Premium headlines, brand emphasis
|
||||
|
||||
## Visual Effect Components
|
||||
|
||||
### 7. **Orbiting Circles**
|
||||
- **Purpose**: Circles moving in orbit along circular paths
|
||||
- **Key Props**:
|
||||
- `radius` (orbit size)
|
||||
- `duration` (animation speed)
|
||||
- `reverse` (direction)
|
||||
- `delay`, `path` (show orbit path)
|
||||
- `iconSize`, `speed`
|
||||
- **Use Case**: Technology visualization, ecosystem diagrams, feature satellites
|
||||
|
||||
### 8. **Particles**
|
||||
- **Purpose**: Animated particle background with depth and interactivity
|
||||
- **Use Case**: Hero sections, immersive backgrounds
|
||||
|
||||
### 9. **Confetti**
|
||||
- **Purpose**: Celebration confetti effect
|
||||
- **Key Props**:
|
||||
- `particleCount`, `angle`, `spread`
|
||||
- `startVelocity`, `decay`, `gravity`
|
||||
- `colors`, `shapes` (square, circle, star)
|
||||
- `origin` point
|
||||
- **Includes**: `ConfettiButton` wrapper component
|
||||
- **Use Case**: Success states, milestones, achievements
|
||||
|
||||
### 10. **Border Beam**
|
||||
- **Purpose**: Animated beam effect along borders
|
||||
- **Key Props**: `reverse`, spring animations
|
||||
- **Use Case**: Card highlights, section emphasis
|
||||
|
||||
### 11. **Shine Border**
|
||||
- **Purpose**: Animated shining border effect
|
||||
- **Key Props**: `color` array, `borderWidth`, `duration`
|
||||
- **Use Case**: Premium cards, CTAs, feature boxes
|
||||
|
||||
### 12. **Magic Card**
|
||||
- **Purpose**: Spotlight effect following mouse cursor with border highlights
|
||||
- **Key Props**:
|
||||
- `gradientSize` (200 default)
|
||||
- `gradientColor` (#262626 default)
|
||||
- `gradientOpacity` (0.8)
|
||||
- `gradientFrom`/`gradientTo` (border colors)
|
||||
- **Use Case**: Interactive feature cards, pricing tables
|
||||
|
||||
## Background Components
|
||||
|
||||
### 13. **Grid Beams**
|
||||
- **Purpose**: Dynamic grid background with animated light beams
|
||||
- **Key Props**:
|
||||
- `gridSize` (40px default)
|
||||
- `gridColor` (rgba)
|
||||
- `rayCount` (15 default)
|
||||
- `rayOpacity` (0.35)
|
||||
- `raySpeed`, `rayLength` (45vh)
|
||||
- `gridFadeStart`/`gridFadeEnd` (%)
|
||||
- `backgroundColor`
|
||||
- **Use Case**: Hero sections, feature backgrounds, immersive layouts
|
||||
|
||||
### 14. **Warp Background**
|
||||
- **Purpose**: Warped perspective grid effect
|
||||
- **Key Props**:
|
||||
- `perspective` (depth)
|
||||
- `beamsPerSide` (4 default)
|
||||
- `beamSize` (thickness)
|
||||
- `beamDuration` (speed)
|
||||
- `gridColor`
|
||||
- **Use Case**: Futuristic hero sections, tech-focused pages
|
||||
|
||||
### 15. **Dot Pattern**
|
||||
- **Purpose**: Customizable dotted background pattern (SVG)
|
||||
- **Key Props**: `width`, `height`, `cx`, `cy`, `cr` (dot radius)
|
||||
- **Effects**: Supports glow effects
|
||||
- **Use Case**: Subtle backgrounds, section dividers
|
||||
|
||||
## Interactive Components
|
||||
|
||||
### 16. **Dock**
|
||||
- **Purpose**: macOS-style dock with magnification effect
|
||||
- **Key Props**:
|
||||
- `iconMagnification` (zoom amount)
|
||||
- `iconDistance` (hover range)
|
||||
- `direction` (middle, start, end)
|
||||
- **Child Component**: `DockIcon`
|
||||
- **Use Case**: Navigation, tool showcases, social links
|
||||
|
||||
### 17. **Scratch To Reveal**
|
||||
- **Purpose**: Interactive scratch-off effect revealing hidden content
|
||||
- **Key Props**:
|
||||
- `width`, `height`
|
||||
- `minScratchPercentage` (50 default, completion threshold)
|
||||
- `onComplete` callback
|
||||
- `gradientColors`
|
||||
- **Use Case**: Interactive reveals, gamification, teasers
|
||||
|
||||
### 18. **Highlighter**
|
||||
- **Purpose**: Animated text highlighting and underlining
|
||||
- **Key Props**:
|
||||
- `color`
|
||||
- `strokeWidth`
|
||||
- `action` (underline, highlight)
|
||||
- `animationDuration`
|
||||
- `iterations`, `padding`
|
||||
- **Use Case**: Emphasis on key phrases, call-outs
|
||||
|
||||
## Layout/Display Components
|
||||
|
||||
### 19. **Marquee**
|
||||
- **Purpose**: Scrolling content (horizontal or vertical)
|
||||
- **Key Props**:
|
||||
- `reverse` (direction)
|
||||
- `pauseOnHover`
|
||||
- `vertical` (orientation)
|
||||
- `repeat` (count)
|
||||
- **Effects**: 3D perspective option
|
||||
- **Use Case**: Testimonials, logo clouds, infinite scrollers
|
||||
|
||||
### 20. **Safari**
|
||||
- **Purpose**: Safari browser mockup for showcasing
|
||||
- **Key Props**:
|
||||
- `url` (address bar)
|
||||
- `imageSrc` or `videoSrc`
|
||||
- `width` (1203 default), `height` (753)
|
||||
- `mode` (default, simple)
|
||||
- **Use Case**: Product demos, website previews
|
||||
|
||||
### 21. **Bento Grid**
|
||||
- **Purpose**: Grid layout for organizing content
|
||||
- **Use Case**: Feature showcases, portfolios
|
||||
|
||||
### 22. **Avatar Circles**
|
||||
- **Purpose**: Overlapping circles of avatars
|
||||
- **Key Props**: `numPeople` (99 default, shown in last circle)
|
||||
- **Use Case**: Social proof, team displays, user counts
|
||||
|
||||
## Timeline Components
|
||||
|
||||
### 23. **Arc Timeline**
|
||||
- **Purpose**: Curved timeline visualizing milestones
|
||||
- **Key Props**:
|
||||
- `data` (array of {time, title})
|
||||
- `arcConfig`:
|
||||
- `circleWidth` (5000 default)
|
||||
- `angleBetweenMinorSteps` (0.35)
|
||||
- `lineCountFillBetweenSteps` (10)
|
||||
- `boundaryPlaceholderLinesCount` (50)
|
||||
- `defaultActiveStep` ({time, stepIndex})
|
||||
- **Use Case**: Project roadmaps, product evolution, version history
|
||||
|
||||
## Button Components
|
||||
|
||||
### 24. **Shiny Button**
|
||||
- **Purpose**: Button with shiny effect
|
||||
- **Features**: Dark/light mode support
|
||||
- **Use Case**: Primary CTAs, important actions
|
||||
|
||||
### 25. **Pulsating Button**
|
||||
- **Purpose**: Button with pulsing wave animation
|
||||
- **Key Props**: `pulseColor` (RGB), `duration`
|
||||
- **Use Case**: Attention-grabbing CTAs, urgent actions
|
||||
|
||||
### 26. **Rainbow Button**
|
||||
- **Purpose**: Rainbow gradient button effect
|
||||
- **Variants**: Default, Outline
|
||||
- **Use Case**: Premium CTAs, playful actions
|
||||
|
||||
## Theme Components
|
||||
|
||||
### 27. **Animated Theme Toggler**
|
||||
- **Purpose**: Smooth animated light/dark mode toggle
|
||||
- **Built With**: Tailwind CSS
|
||||
- **Use Case**: Theme switching UI
|
||||
|
||||
---
|
||||
|
||||
## Installation
|
||||
|
||||
```bash
|
||||
# Via CLI (recommended)
|
||||
npx shadcn-ui@latest add [component-name]
|
||||
|
||||
# Manual installation
|
||||
npm install magicui
|
||||
# or
|
||||
yarn add magicui
|
||||
```
|
||||
|
||||
## Component Categories Summary
|
||||
|
||||
| Category | Components | Best For |
|
||||
|----------|-----------|----------|
|
||||
| **Text Animation** | Animated List, Text Animate, Flip Text, Morphing Text, Word Rotate, Aurora Text | Headlines, feature lists, dynamic content |
|
||||
| **Visual Effects** | Orbiting Circles, Particles, Confetti, Border Beam, Shine Border, Magic Card | Visual interest, interactivity, emphasis |
|
||||
| **Backgrounds** | Grid Beams, Warp Background, Dot Pattern | Hero sections, immersive layouts |
|
||||
| **Interactive** | Dock, Scratch To Reveal, Highlighter | User engagement, gamification |
|
||||
| **Layout** | Marquee, Safari, Bento Grid, Avatar Circles | Content organization, showcases |
|
||||
| **Timeline** | Arc Timeline | Roadmaps, history, progression |
|
||||
| **Buttons** | Shiny Button, Pulsating Button, Rainbow Button | CTAs, actions |
|
||||
| **Utility** | Animated Theme Toggler | UI controls |
|
||||
|
||||
## Design Philosophy
|
||||
|
||||
Magic UI components focus on:
|
||||
- **Delight**: Unexpected animations that create joy
|
||||
- **Fluidity**: Smooth, natural motion
|
||||
- **Performance**: Optimized for web performance
|
||||
- **Flexibility**: Highly customizable via props
|
||||
- **Modern**: Built with React, Tailwind CSS, Framer Motion
|
||||
|
||||
## Use Case Recommendations by Landing Page Section
|
||||
|
||||
### Hero Sections
|
||||
- Grid Beams or Warp Background (background)
|
||||
- Aurora Text or Morphing Text (headline)
|
||||
- Pulsating Button or Rainbow Button (CTA)
|
||||
- Orbiting Circles (tech visualization)
|
||||
|
||||
### Feature Showcases
|
||||
- Bento Grid (layout)
|
||||
- Magic Card (individual features)
|
||||
- Animated List (feature details)
|
||||
- Highlighter (emphasis)
|
||||
|
||||
### Social Proof
|
||||
- Marquee (testimonials/logos)
|
||||
- Avatar Circles (user counts)
|
||||
|
||||
### Product Demos
|
||||
- Safari (browser mockups)
|
||||
- Border Beam or Shine Border (emphasis)
|
||||
|
||||
### Roadmaps/Progress
|
||||
- Arc Timeline (milestones)
|
||||
|
||||
### Interactive Elements
|
||||
- Scratch To Reveal (teasers)
|
||||
- Dock (navigation/tools)
|
||||
- Confetti (celebrations)
|
||||
@@ -1,471 +0,0 @@
|
||||
# Magic UI Landing Page Creative Ideas
|
||||
|
||||
Creative applications of Magic UI components for each section of the claude-mem landing page.
|
||||
|
||||
## Section 1: HERO - "Claude Never Forgets"
|
||||
|
||||
### Idea 1: "Fading Memory" Effect ⭐ WINNER
|
||||
**Components**: Morphing Text, Grid Beams, Orbiting Circles, Scratch To Reveal
|
||||
|
||||
**Vision**:
|
||||
- **Headline**: Morphing Text rotating between:
|
||||
- "Claude Never Forgets"
|
||||
- "Claude Always Remembers"
|
||||
- "Claude Learns Forever"
|
||||
- **Background**: Grid Beams in blue/purple gradient representing the "memory grid"
|
||||
- **Central Visual**: Orbiting Circles around a central "brain/database" icon
|
||||
- Inner orbit: File icons (representing code files)
|
||||
- Middle orbit: Lightbulb icons (decisions)
|
||||
- Outer orbit: Bug icons (fixes)
|
||||
- **Problem Statement**: Scratch To Reveal overlay
|
||||
- User must scratch to reveal: "Every /clear wipes Claude's memory"
|
||||
- Makes the pain point visceral and interactive
|
||||
- **CTA**: Pulsating Button "Give Claude a Memory"
|
||||
|
||||
### Idea 2: "Memory Timeline" Hero
|
||||
**Components**: Warp Background, Arc Timeline, Aurora Text, Word Rotate
|
||||
|
||||
**Vision**:
|
||||
- Warp Background creating perspective depth
|
||||
- Arc Timeline showing "Session 1 → Session 2 → Session 3" with memories persisting across
|
||||
- Aurora Text for main headline with flowing gradient
|
||||
- Word Rotate cycling pain points: "Repeating" → "Explaining" → "Re-discovering" → "Forgetting"
|
||||
|
||||
### Idea 3: "Brain Storage" Visualization
|
||||
**Components**: Particles, Magic Card, Highlighter, Pulsating Button
|
||||
|
||||
**Vision**:
|
||||
- Particles background (subtle, neuron-like firing)
|
||||
- Central Magic Card with spotlight effect containing headline
|
||||
- Highlighter emphasizing "Never" and "Forgets" with animated underlines
|
||||
- Pulsating Button for CTA with urgency
|
||||
|
||||
---
|
||||
|
||||
## Section 2: BEFORE/AFTER Comparison
|
||||
|
||||
### Idea 1: "Split Screen Wipe" ⭐ WINNER
|
||||
**Components**: Safari, Text Animate, Border Beam, Scratch To Reveal
|
||||
|
||||
**Vision**:
|
||||
- Two Safari browser mockups side by side
|
||||
- **Left (Before)**: Conversation fades/blurs out showing memory loss
|
||||
- **Right (After)**: Sharp, persistent content with Border Beam highlighting
|
||||
- Text Animate with slideUp for conversations appearing sequentially
|
||||
- Optional: Scratch To Reveal over "Before" side to uncover the painful reality
|
||||
|
||||
### Idea 2: "Memory Decay Visualization"
|
||||
**Components**: Text Animate, Shine Border, Magic Card, Animated Theme Toggler
|
||||
|
||||
**Vision**:
|
||||
- Before side: Text with progressive fade-out animation (opacity decreasing)
|
||||
- After side: Text with Shine Border, glowing persistently
|
||||
- Magic Card on After side with spotlight following mouse
|
||||
- Animated Theme Toggler metaphorically switching "forgetting" → "remembering"
|
||||
|
||||
### Idea 3: "Conversation Replay"
|
||||
**Components**: Animated List, Border Beam, Highlighter
|
||||
|
||||
**Vision**:
|
||||
- Animated List showing conversation history
|
||||
- Before: List items fade out and disappear sequentially
|
||||
- After: List items persist, Border Beam appears on referenced items
|
||||
- Highlighter underlining key persistent information on After side
|
||||
|
||||
---
|
||||
|
||||
## Section 3: REAL EXAMPLES (3 Scenarios)
|
||||
|
||||
### Idea 1: "Tabbed Experience"
|
||||
**Components**: Bento Grid, Magic Card, Text Animate, Flip Text, Confetti
|
||||
|
||||
**Vision**:
|
||||
- Bento Grid layout with 3 Magic Cards (one per scenario)
|
||||
- Each card has spotlight effect on hover
|
||||
- Text Animate (blurIn) for code examples appearing
|
||||
- Flip Text for headings revealing scenario names
|
||||
- Confetti burst when clicking through to third example (pattern recognized!)
|
||||
|
||||
### Idea 2: "Timeline Story" ⭐ WINNER
|
||||
**Components**: Arc Timeline, Orbiting Circles, Highlighter, Animated List
|
||||
|
||||
**Vision**:
|
||||
- Arc Timeline showing progression: "Monday Session" → "Wednesday Session"
|
||||
- Each timeline node expands to show the scenario details
|
||||
- Orbiting Circles around active node showing:
|
||||
- Files involved (icons in orbit)
|
||||
- Decisions made (decision icons)
|
||||
- Bugs fixed (bug icons)
|
||||
- Highlighter emphasizing key remembered details in the expanded content
|
||||
- Animated List revealing the bulleted "Claude remembers" items
|
||||
|
||||
### Idea 3: "Memory Card Flip"
|
||||
**Components**: Scratch To Reveal, Magic Card, Pulsating Button, Word Rotate
|
||||
|
||||
**Vision**:
|
||||
- Three cards with Scratch To Reveal overlay
|
||||
- Scratch to reveal what Claude remembers from previous sessions
|
||||
- Magic Card underneath with gradient borders
|
||||
- Pulsating Button to cycle through scenarios
|
||||
- Word Rotate showing memory types: "Patterns" → "Decisions" → "Context" → "Architecture"
|
||||
|
||||
---
|
||||
|
||||
## Section 4: HOW IT WORKS (Pipeline)
|
||||
|
||||
### Idea 1: "Animated Data Flow"
|
||||
**Components**: Magic Card, Orbiting Circles, Border Beam, Text Animate, Particles
|
||||
|
||||
**Vision**:
|
||||
- Three connected Magic Cards showing pipeline stages:
|
||||
1. "You code with Claude today"
|
||||
2. "claude-mem captures & compresses"
|
||||
3. "Tomorrow, Claude starts with context"
|
||||
- Orbiting Circles representing data flowing between stages
|
||||
- Border Beam animating along connections between cards
|
||||
- Text Animate (slideUp) for each stage description on scroll
|
||||
- Particles flowing from one stage to the next
|
||||
|
||||
### Idea 2: "Arc Timeline as Process"
|
||||
**Components**: Arc Timeline, Orbiting Circles, Warp Background, Animated List, Shine Border
|
||||
|
||||
**Vision**:
|
||||
- Arc Timeline showing 5 steps of memory system
|
||||
- Each node has Orbiting Circles showing components (hooks, worker, DB, MCP, context)
|
||||
- Warp Background creating depth
|
||||
- Animated List revealing sub-bullets under each stage
|
||||
- Shine Border on active/hovered stage
|
||||
|
||||
### Idea 3: "Layered Depth Model" ⭐ WINNER
|
||||
**Components**: Grid Beams, Magic Card, Border Beam, Morphing Text, Highlighter
|
||||
|
||||
**Vision**:
|
||||
- Grid Beams background representing the "storage layer"
|
||||
- Three floating Magic Cards in z-space (perspective/depth):
|
||||
- Top layer: "Hooks capture"
|
||||
- Middle layer: "AI compresses"
|
||||
- Bottom layer: "Database stores"
|
||||
- Arrows with Border Beam animation showing data flow downward
|
||||
- Morphing Text showing state transformation: "Capturing" → "Compressing" → "Storing" → "Retrieving"
|
||||
- Highlighter emphasizing "Automatic. Zero effort. Always on."
|
||||
|
||||
---
|
||||
|
||||
## Section 5: WHAT GETS REMEMBERED (Feature List)
|
||||
|
||||
### Idea 1: "Checkbox Delight"
|
||||
**Components**: Animated List, Text Animate, Highlighter, Confetti, Magic Card
|
||||
|
||||
**Vision**:
|
||||
- Animated List of 6 checkmark items
|
||||
- Each item appears with Text Animate (blurIn by character)
|
||||
- Highlighter underlining key words: "Decisions", "Bugs Fixed", "Code Patterns", etc.
|
||||
- Confetti burst when all items are visible (celebration of completeness)
|
||||
- Magic Card container with subtle spotlight effect
|
||||
|
||||
### Idea 2: "Memory Bank Slots" ⭐ WINNER
|
||||
**Components**: Bento Grid, Scratch To Reveal, Shine Border, Orbiting Circles, Pulsating Button
|
||||
|
||||
**Vision**:
|
||||
- Bento Grid of 6 cards (one per memory type)
|
||||
- Each card has Scratch To Reveal to discover what gets saved (gamification!)
|
||||
- Shine Border appears around revealed cards
|
||||
- Orbiting Circles showing example icons around each card type
|
||||
- Pulsating indicator on most important memory types
|
||||
|
||||
### Idea 3: "Collector Animation"
|
||||
**Components**: Magic Card, Border Beam, Flip Text, Aurora Text, Dot Pattern
|
||||
|
||||
**Vision**:
|
||||
- Six Magic Cards arranged in grid
|
||||
- Border Beam cascading through cards in sequence
|
||||
- Each card flips (Flip Text) to reveal icon + description
|
||||
- Aurora Text for section heading
|
||||
- Dot Pattern background with subtle glow on active items
|
||||
|
||||
---
|
||||
|
||||
## Section 6: POWERFUL SEARCH
|
||||
|
||||
### Idea 1: "Live Search Demo" ⭐ WINNER
|
||||
**Components**: Safari, Text Animate, Animated List, Highlighter, Border Beam, Morphing Text
|
||||
|
||||
**Vision**:
|
||||
- Safari browser mockup showing search interface
|
||||
- Text Animate typing out search queries with realistic timing:
|
||||
- "Find all database migrations..."
|
||||
- "What decisions about authentication..."
|
||||
- Animated List revealing search results sequentially
|
||||
- Highlighter emphasizing matched keywords in results
|
||||
- Border Beam around result cards
|
||||
- Morphing Text cycling search types: "Migrations" → "Decisions" → "Patterns" → "Bugs"
|
||||
|
||||
### Idea 2: "Search Radar"
|
||||
**Components**: Orbiting Circles, Magic Card, Particles, Text Animate, Grid Beams
|
||||
|
||||
**Vision**:
|
||||
- Orbiting Circles representing different search dimensions:
|
||||
- Inner orbit: File search
|
||||
- Middle orbit: Concept search
|
||||
- Outer orbit: Type/date filters
|
||||
- Central Magic Card with search query
|
||||
- Particles radiating outward to show search happening
|
||||
- Results appear with Text Animate (slideUp)
|
||||
- Grid Beams background representing indexed database
|
||||
|
||||
### Idea 3: "Memory Retrieval Visualization"
|
||||
**Components**: Warp Background, Aurora Text, Magic Card, Shine Border, Dock, Confetti
|
||||
|
||||
**Vision**:
|
||||
- Warp Background creating depth into "memory storage"
|
||||
- Search query in Aurora Text
|
||||
- Seven Magic Cards (one per search tool) with Shine Border
|
||||
- Dock component at bottom showing 7 search tool icons
|
||||
- Confetti when "perfect match" is found
|
||||
|
||||
---
|
||||
|
||||
## Section 7: THE NUMBERS (Metrics Table)
|
||||
|
||||
### Idea 1: "Counting Animation"
|
||||
**Components**: Safari, Text Animate, Aurora Text, Confetti, Border Beam
|
||||
|
||||
**Vision**:
|
||||
- Safari mockup showing comparison table
|
||||
- Text Animate for each metric value counting up
|
||||
- "Before" values in faded text
|
||||
- "After" values with Aurora Text effect (glowing)
|
||||
- Confetti when hovering over dramatic improvements
|
||||
- Border Beam highlighting entire "After" column
|
||||
|
||||
### Idea 2: "Progress Bar Transformation" ⭐ WINNER
|
||||
**Components**: Magic Card, Morphing Text, Shine Border, Pulsating Button
|
||||
|
||||
**Vision**:
|
||||
- Five Magic Cards, one per metric
|
||||
- Animated progress bars showing transformation:
|
||||
- Before (low/red) → After (high/green)
|
||||
- Visual representation of improvement
|
||||
- Morphing Text cycling through metrics
|
||||
- Shine Border on cards with biggest improvements
|
||||
- Pulsating Button: "See the Difference"
|
||||
|
||||
### Idea 3: "Flip Cards Reveal"
|
||||
**Components**: Scratch To Reveal, Magic Card, Highlighter, Animated List
|
||||
|
||||
**Vision**:
|
||||
- Five cards with Scratch To Reveal
|
||||
- Scratch "Before" to reveal "After" metrics
|
||||
- Each card has Magic Card spotlight effect
|
||||
- Highlighter emphasizing: "Never", "0 seconds", "Rare"
|
||||
- Animated List showing additional benefits below table
|
||||
|
||||
---
|
||||
|
||||
## Section 8: INSTALLATION (Quick Start)
|
||||
|
||||
### Idea 1: "Copy-Paste Delight" ⭐ WINNER
|
||||
**Components**: Safari, Text Animate, Shiny Button, Confetti, Animated List, Highlighter
|
||||
|
||||
**Vision**:
|
||||
- Safari mockup showing terminal window
|
||||
- Text Animate typing out commands with realistic timing (typewriter effect)
|
||||
- Shiny Button next to each command for copy
|
||||
- Confetti celebration when installation completes
|
||||
- Animated List showing 3 steps with checkmarks appearing
|
||||
- Highlighter emphasizing "2 minutes"
|
||||
|
||||
### Idea 2: "Progress Stepper"
|
||||
**Components**: Arc Timeline, Border Beam, Magic Card, Pulsating Button, Word Rotate
|
||||
|
||||
**Vision**:
|
||||
- Arc Timeline with 3 nodes: Clone → Add → Install
|
||||
- Each step expands to show code when active
|
||||
- Border Beam connecting steps as they complete
|
||||
- Magic Card for code blocks with spotlight
|
||||
- Pulsating Button for "Get Started"
|
||||
- Word Rotate: "Simple" → "Fast" → "Easy" → "Done"
|
||||
|
||||
### Idea 3: "Interactive Terminal"
|
||||
**Components**: Grid Beams, Safari, Text Animate, Orbiting Circles, Rainbow Button, Shine Border
|
||||
|
||||
**Vision**:
|
||||
- Grid Beams background (tech aesthetic)
|
||||
- Safari terminal mockup
|
||||
- Text Animate simulating command execution
|
||||
- Orbiting Circles showing installed components appearing
|
||||
- Rainbow Button for final "Installation Complete" CTA
|
||||
- Shine Border around success message
|
||||
|
||||
---
|
||||
|
||||
## Section 9: UNDER THE HOOD (Architecture)
|
||||
|
||||
### Idea 1: "System Diagram"
|
||||
**Components**: Bento Grid, Magic Card, Orbiting Circles, Border Beam, Dot Pattern, Text Animate, Highlighter
|
||||
|
||||
**Vision**:
|
||||
- Bento Grid showing 5 architecture components:
|
||||
- Hooks, Worker, Database, MCP, Context Injection
|
||||
- Each grid cell is a Magic Card with spotlight
|
||||
- Orbiting Circles showing data flow between components
|
||||
- Border Beam animating along connections
|
||||
- Dot Pattern background
|
||||
- Text Animate for each component description
|
||||
- Highlighter on "Zero maintenance. Just works."
|
||||
|
||||
### Idea 2: "Layered Stack" ⭐ WINNER
|
||||
**Components**: Magic Card, Warp Background, Morphing Text, Particles, Shine Border
|
||||
|
||||
**Vision**:
|
||||
- Five Magic Cards stacked with perspective (z-depth)
|
||||
- Each layer slides out on scroll to reveal architecture:
|
||||
1. Hooks (top)
|
||||
2. Worker Service
|
||||
3. SQLite Database (center)
|
||||
4. MCP Server
|
||||
5. Context Injection (bottom)
|
||||
- Warp Background creating depth
|
||||
- Morphing Text showing active layer: "Hooks" → "Worker" → "SQLite" → "MCP" → "Context"
|
||||
- Particles flowing between layers
|
||||
- Shine Border on active layer
|
||||
|
||||
### Idea 3: "Technical Orbits"
|
||||
**Components**: Orbiting Circles, Grid Beams, Magic Card, Text Animate
|
||||
|
||||
**Vision**:
|
||||
- Central "SQLite DB" icon
|
||||
- Orbiting Circles representing different systems:
|
||||
- Inner orbit: 5 hooks (SessionStart, UserPrompt, PostTool, Summary, SessionEnd)
|
||||
- Middle orbit: Worker service (PM2 process)
|
||||
- Outer orbit: MCP server (7 search tools)
|
||||
- Farthest orbit: Context injection
|
||||
- Grid Beams background
|
||||
- Magic Card for each orbit explanation
|
||||
- Text Animate (slideUp) for technical details
|
||||
|
||||
---
|
||||
|
||||
## Section 10: USE CASES (User Types)
|
||||
|
||||
### Idea 1: "User Journey Cards"
|
||||
**Components**: Bento Grid, Magic Card, Avatar Circles, Animated List, Flip Text, Border Beam
|
||||
|
||||
**Vision**:
|
||||
- Four Magic Cards in Bento Grid layout
|
||||
- Each card has different gradient colors (gradientFrom/To)
|
||||
- Avatar Circles showing user type icon
|
||||
- Animated List of benefits per user type
|
||||
- Flip Text for headings revealing user types
|
||||
- Border Beam highlighting active/hovered card
|
||||
|
||||
### Idea 2: "Role Selector" ⭐ WINNER
|
||||
**Components**: Dock, Magic Card, Text Animate, Highlighter, Confetti
|
||||
|
||||
**Vision**:
|
||||
- Dock component with 4 icons representing user types:
|
||||
- Solo Developer icon
|
||||
- Team icon
|
||||
- Learning/Student icon
|
||||
- Large Refactor icon
|
||||
- Click icon to expand that use case
|
||||
- Magic Card expands with spotlight effect
|
||||
- Text Animate revealing use case details
|
||||
- Highlighter emphasizing key benefits
|
||||
- Confetti celebration when selecting "your" use case (engagement!)
|
||||
|
||||
### Idea 3: "Story Scenarios"
|
||||
**Components**: Arc Timeline, Safari, Orbiting Circles, Morphing Text, Shine Border, Animated List
|
||||
|
||||
**Vision**:
|
||||
- Arc Timeline showing progression for each user type
|
||||
- Safari mockup showing actual usage scenario
|
||||
- Orbiting Circles around timeline nodes showing features being used
|
||||
- Morphing Text cycling through user types
|
||||
- Shine Border on selected use case
|
||||
- Animated List showing specific benefits
|
||||
|
||||
---
|
||||
|
||||
## Section 11: FAQ
|
||||
|
||||
### Idea 1: "Expandable Cards"
|
||||
**Components**: Magic Card, Text Animate, Border Beam, Highlighter, Dot Pattern, Animated List
|
||||
|
||||
**Vision**:
|
||||
- Six Magic Cards with questions visible
|
||||
- Click to expand with Text Animate (blurIn)
|
||||
- Border Beam appears on expanded card
|
||||
- Highlighter emphasizing key answer points
|
||||
- Dot Pattern background
|
||||
- Animated List for multi-point answers
|
||||
|
||||
### Idea 2: "Scratch to Answer" ⭐ WINNER
|
||||
**Components**: Scratch To Reveal, Confetti, Magic Card, Morphing Text
|
||||
|
||||
**Vision**:
|
||||
- Questions visible, answers hidden under scratch surface
|
||||
- Scratch To Reveal to see answers (highly engaging!)
|
||||
- Confetti on revealing particularly important answers (e.g., "Fully private")
|
||||
- Magic Card container with spotlight
|
||||
- Morphing Text cycling through common concerns: "Cost?" → "Speed?" → "Privacy?" → "Storage?"
|
||||
|
||||
### Idea 3: "Interactive Q&A"
|
||||
**Components**: Bento Grid, Border Beam, Shine Border, Text Animate, Pulsating Button, Avatar Circles
|
||||
|
||||
**Vision**:
|
||||
- Bento Grid of question cards
|
||||
- Hover triggers Border Beam
|
||||
- Click expands with Shine Border
|
||||
- Text Animate typing out answers
|
||||
- Pulsating Button for "More Questions?"
|
||||
- Avatar Circles showing "5,000+ developers trust claude-mem"
|
||||
|
||||
---
|
||||
|
||||
## BONUS: Testimonials Section (Not in Original)
|
||||
|
||||
### Idea 1: "Social Proof Marquee"
|
||||
**Components**: Marquee (3D), Magic Card, Avatar Circles, Highlighter, Shine Border
|
||||
|
||||
**Vision**:
|
||||
- Marquee component in 3D mode scrolling developer testimonials
|
||||
- Each testimonial in a Magic Card
|
||||
- Avatar Circles showing total developer count
|
||||
- Highlighter on impactful quote fragments
|
||||
- Shine Border around featured testimonial
|
||||
|
||||
---
|
||||
|
||||
## Component Usage Summary
|
||||
|
||||
| Component | Times Used (Winners) | Primary Purpose |
|
||||
|-----------|---------------------|-----------------|
|
||||
| **Magic Card** | 9 | Spotlight effects, containers |
|
||||
| **Text Animate** | 7 | Typing effects, reveals |
|
||||
| **Border Beam** | 6 | Connections, highlights |
|
||||
| **Highlighter** | 6 | Emphasis on key text |
|
||||
| **Animated List** | 5 | Sequential reveals |
|
||||
| **Confetti** | 5 | Celebrations, milestones |
|
||||
| **Shine Border** | 5 | Premium emphasis |
|
||||
| **Morphing Text** | 4 | Dynamic headlines |
|
||||
| **Orbiting Circles** | 4 | Data flow, ecosystems |
|
||||
| **Safari** | 4 | Browser mockups |
|
||||
| **Scratch To Reveal** | 4 | Interactive discovery |
|
||||
| **Pulsating Button** | 4 | CTAs |
|
||||
| **Grid Beams** | 3 | Tech backgrounds |
|
||||
| **Warp Background** | 3 | Depth, perspective |
|
||||
| **Arc Timeline** | 2 | Progress, history |
|
||||
| **Dock** | 1 | Navigation selector |
|
||||
| **Aurora Text** | 1 | Premium headlines |
|
||||
| **Bento Grid** | 1 | Layout organization |
|
||||
|
||||
## Design Principles
|
||||
|
||||
1. **Show, Don't Tell**: Use animations to demonstrate concepts (memory persistence, data flow)
|
||||
2. **Interactive Discovery**: Scratch-to-reveal and interactive elements engage users
|
||||
3. **Visual Metaphors**: Orbiting circles for data flow, layers for architecture
|
||||
4. **Celebration**: Confetti at key moments creates joy
|
||||
5. **Progressive Disclosure**: Animated lists and timelines reveal information naturally
|
||||
6. **Spatial Depth**: Warp backgrounds and z-space create dimensional understanding
|
||||
7. **Consistent Magic**: Reuse components (Magic Card, Border Beam) for cohesion
|
||||
@@ -1,389 +0,0 @@
|
||||
# Landing Page Ideas - Ranked Analysis
|
||||
|
||||
Ranking criteria (1-10 scale):
|
||||
1. **Creativity**: How novel and unexpected is the approach?
|
||||
2. **Storytelling**: How effectively does it communicate the value?
|
||||
3. **Intuitive**: How easily will users understand it?
|
||||
4. **Feasibility**: How practical is implementation?
|
||||
5. **Delight**: How much joy does it create?
|
||||
|
||||
---
|
||||
|
||||
## HERO SECTION Rankings
|
||||
|
||||
### Idea 1: "Fading Memory" Effect ⭐ WINNER (42/50)
|
||||
- **Creativity**: 9/10 - Scratch-to-reveal pain point is unexpected
|
||||
- **Storytelling**: 9/10 - Morphing text and orbiting circles tell complete story
|
||||
- **Intuitive**: 8/10 - Orbiting content makes memory concept clear
|
||||
- **Feasibility**: 7/10 - Multiple complex components need coordination
|
||||
- **Delight**: 9/10 - Interactive scratch creates engagement
|
||||
|
||||
**Why it wins**: The scratch-to-reveal pain point makes the problem visceral. Orbiting circles create immediate visual understanding of what gets remembered.
|
||||
|
||||
### Idea 2: "Memory Timeline" Hero (39/50)
|
||||
- **Creativity**: 7/10
|
||||
- **Storytelling**: 8/10
|
||||
- **Intuitive**: 9/10
|
||||
- **Feasibility**: 8/10
|
||||
- **Delight**: 7/10
|
||||
|
||||
### Idea 3: "Brain Storage" Visualization (36/50)
|
||||
- **Creativity**: 6/10
|
||||
- **Storytelling**: 7/10
|
||||
- **Intuitive**: 7/10
|
||||
- **Feasibility**: 9/10
|
||||
- **Delight**: 7/10
|
||||
|
||||
---
|
||||
|
||||
## BEFORE/AFTER COMPARISON Rankings
|
||||
|
||||
### Idea 1: "Split Screen Wipe" ⭐ WINNER (44/50)
|
||||
- **Creativity**: 8/10 - Clean comparison approach
|
||||
- **Storytelling**: 10/10 - Side-by-side is the clearest possible comparison
|
||||
- **Intuitive**: 10/10 - Immediately obvious what's different
|
||||
- **Feasibility**: 8/10 - Safari mockups are well-supported
|
||||
- **Delight**: 8/10 - Fade vs persist is satisfying
|
||||
|
||||
**Why it wins**: Safari browser mockups provide immediate familiarity. The contrast between fading (Before) and persistent with Border Beam (After) is crystal clear storytelling.
|
||||
|
||||
### Idea 2: "Memory Decay Visualization" (40/50)
|
||||
- **Creativity**: 9/10
|
||||
- **Storytelling**: 9/10
|
||||
- **Intuitive**: 8/10
|
||||
- **Feasibility**: 7/10
|
||||
- **Delight**: 7/10
|
||||
|
||||
### Idea 3: "Conversation Replay" (40/50)
|
||||
- **Creativity**: 7/10
|
||||
- **Storytelling**: 8/10
|
||||
- **Intuitive**: 9/10
|
||||
- **Feasibility**: 9/10
|
||||
- **Delight**: 7/10
|
||||
|
||||
---
|
||||
|
||||
## REAL EXAMPLES Rankings
|
||||
|
||||
### Idea 2: "Timeline Story" ⭐ WINNER (44/50)
|
||||
- **Creativity**: 9/10 - Arc timeline for session progression is novel
|
||||
- **Storytelling**: 10/10 - Timeline naturally shows progression over time
|
||||
- **Intuitive**: 9/10 - Timeline metaphor is universally understood
|
||||
- **Feasibility**: 7/10 - Complex interaction between timeline and orbiting elements
|
||||
- **Delight**: 9/10 - Orbiting context around nodes is magical
|
||||
|
||||
**Why it wins**: Arc Timeline naturally communicates the "across sessions" aspect. Orbiting circles showing related context (files, decisions, bugs) is brilliant visual storytelling.
|
||||
|
||||
### Idea 1: "Tabbed Experience" (41/50)
|
||||
- **Creativity**: 7/10
|
||||
- **Storytelling**: 8/10
|
||||
- **Intuitive**: 9/10
|
||||
- **Feasibility**: 9/10
|
||||
- **Delight**: 8/10
|
||||
|
||||
### Idea 3: "Memory Card Flip" (35/50)
|
||||
- **Creativity**: 8/10
|
||||
- **Storytelling**: 7/10
|
||||
- **Intuitive**: 6/10
|
||||
- **Feasibility**: 6/10
|
||||
- **Delight**: 8/10
|
||||
|
||||
---
|
||||
|
||||
## HOW IT WORKS Rankings
|
||||
|
||||
### Idea 3: "Layered Depth Model" ⭐ WINNER (43/50)
|
||||
- **Creativity**: 9/10 - Spatial depth for understanding layers is clever
|
||||
- **Storytelling**: 9/10 - Visual depth communicates layered architecture
|
||||
- **Intuitive**: 10/10 - Layers immediately convey hierarchy and flow
|
||||
- **Feasibility**: 6/10 - Z-space perspective requires careful implementation
|
||||
- **Delight**: 9/10 - Morphing text showing transformation is satisfying
|
||||
|
||||
**Why it wins**: Depth/perspective creates intuitive understanding of the layered architecture. Morphing text showing state transformation ("Capturing" → "Storing") tells the story perfectly.
|
||||
|
||||
### Idea 1: "Animated Data Flow" (42/50)
|
||||
- **Creativity**: 8/10
|
||||
- **Storytelling**: 9/10
|
||||
- **Intuitive**: 9/10
|
||||
- **Feasibility**: 8/10
|
||||
- **Delight**: 8/10
|
||||
|
||||
### Idea 2: "Arc Timeline as Process" (37/50)
|
||||
- **Creativity**: 7/10
|
||||
- **Storytelling**: 8/10
|
||||
- **Intuitive**: 8/10
|
||||
- **Feasibility**: 7/10
|
||||
- **Delight**: 7/10
|
||||
|
||||
---
|
||||
|
||||
## WHAT GETS REMEMBERED Rankings
|
||||
|
||||
### Idea 2: "Memory Bank Slots" ⭐ WINNER (41/50)
|
||||
- **Creativity**: 9/10 - Memory bank metaphor with scratch-to-reveal
|
||||
- **Storytelling**: 8/10 - Discovery process tells the story
|
||||
- **Intuitive**: 8/10 - Bank slot metaphor is clear
|
||||
- **Feasibility**: 7/10 - Six scratch-to-reveal elements need optimization
|
||||
- **Delight**: 9/10 - Gamification through scratching is highly engaging
|
||||
|
||||
**Why it wins**: Scratch-to-reveal gamification makes exploring features fun. Discovering what gets saved creates memorable engagement.
|
||||
|
||||
### Idea 3: "Collector Animation" (41/50 - tied)
|
||||
- **Creativity**: 8/10
|
||||
- **Storytelling**: 8/10
|
||||
- **Intuitive**: 9/10
|
||||
- **Feasibility**: 8/10
|
||||
- **Delight**: 8/10
|
||||
|
||||
### Idea 1: "Checkbox Delight" (40/50)
|
||||
- **Creativity**: 6/10
|
||||
- **Storytelling**: 7/10
|
||||
- **Intuitive**: 9/10
|
||||
- **Feasibility**: 10/10
|
||||
- **Delight**: 8/10
|
||||
|
||||
---
|
||||
|
||||
## POWERFUL SEARCH Rankings
|
||||
|
||||
### Idea 1: "Live Search Demo" ⭐ WINNER (44/50)
|
||||
- **Creativity**: 7/10 - Straightforward but effective
|
||||
- **Storytelling**: 10/10 - Actually showing search in action is perfect
|
||||
- **Intuitive**: 10/10 - Real search demo is immediately clear
|
||||
- **Feasibility**: 9/10 - Safari + Text Animate + Animated List is achievable
|
||||
- **Delight**: 8/10 - Watching search happen is satisfying
|
||||
|
||||
**Why it wins**: Showing actual search with real queries and results is the most effective storytelling. Users immediately understand the capability.
|
||||
|
||||
### Idea 2: "Search Radar" (39/50)
|
||||
- **Creativity**: 9/10
|
||||
- **Storytelling**: 8/10
|
||||
- **Intuitive**: 7/10
|
||||
- **Feasibility**: 6/10
|
||||
- **Delight**: 9/10
|
||||
|
||||
### Idea 3: "Memory Retrieval Visualization" (38/50)
|
||||
- **Creativity**: 8/10
|
||||
- **Storytelling**: 7/10
|
||||
- **Intuitive**: 8/10
|
||||
- **Feasibility**: 8/10
|
||||
- **Delight**: 7/10
|
||||
|
||||
---
|
||||
|
||||
## THE NUMBERS Rankings
|
||||
|
||||
### Idea 2: "Progress Bar Transformation" ⭐ WINNER (45/50)
|
||||
- **Creativity**: 8/10 - Progress bars are familiar but effective
|
||||
- **Storytelling**: 10/10 - Visual transformation from bad to good is powerful
|
||||
- **Intuitive**: 10/10 - Everyone understands progress bars
|
||||
- **Feasibility**: 8/10 - Animated progress bars are well-supported
|
||||
- **Delight**: 9/10 - Watching bars transform is satisfying
|
||||
|
||||
**Why it wins**: Visual transformation of progress bars (red/low → green/high) is incredibly effective storytelling. Everyone intuitively understands the improvement.
|
||||
|
||||
### Idea 1: "Counting Animation" (42/50)
|
||||
- **Creativity**: 7/10
|
||||
- **Storytelling**: 9/10
|
||||
- **Intuitive**: 9/10
|
||||
- **Feasibility**: 9/10
|
||||
- **Delight**: 8/10
|
||||
|
||||
### Idea 3: "Flip Cards Reveal" (39/50)
|
||||
- **Creativity**: 8/10
|
||||
- **Storytelling**: 8/10
|
||||
- **Intuitive**: 7/10
|
||||
- **Feasibility**: 7/10
|
||||
- **Delight**: 9/10
|
||||
|
||||
---
|
||||
|
||||
## INSTALLATION Rankings
|
||||
|
||||
### Idea 1: "Copy-Paste Delight" ⭐ WINNER (44/50)
|
||||
- **Creativity**: 7/10 - Simple but right
|
||||
- **Storytelling**: 8/10 - Shows exactly what to do
|
||||
- **Intuitive**: 10/10 - Terminal + copy buttons is universal pattern
|
||||
- **Feasibility**: 10/10 - Very achievable
|
||||
- **Delight**: 9/10 - Confetti celebration seals the deal
|
||||
|
||||
**Why it wins**: Simplicity wins for installation instructions. Confetti celebration when done creates satisfying completion.
|
||||
|
||||
### Idea 2: "Progress Stepper" (42/50)
|
||||
- **Creativity**: 8/10
|
||||
- **Storytelling**: 9/10
|
||||
- **Intuitive**: 9/10
|
||||
- **Feasibility**: 8/10
|
||||
- **Delight**: 8/10
|
||||
|
||||
### Idea 3: "Interactive Terminal" (41/50)
|
||||
- **Creativity**: 9/10
|
||||
- **Storytelling**: 8/10
|
||||
- **Intuitive**: 8/10
|
||||
- **Feasibility**: 7/10
|
||||
- **Delight**: 9/10
|
||||
|
||||
---
|
||||
|
||||
## UNDER THE HOOD Rankings
|
||||
|
||||
### Idea 2: "Layered Stack" ⭐ WINNER (44/50)
|
||||
- **Creativity**: 9/10 - Layers with z-depth is clever
|
||||
- **Storytelling**: 9/10 - Stacking shows dependency hierarchy
|
||||
- **Intuitive**: 10/10 - Stack metaphor is perfect for architecture
|
||||
- **Feasibility**: 7/10 - Perspective effects require work
|
||||
- **Delight**: 9/10 - Slides revealing layers is satisfying
|
||||
|
||||
**Why it wins**: Layered stack with perspective visually explains the architecture hierarchy perfectly. Each layer sliding out to reveal itself tells the dependency story.
|
||||
|
||||
### Idea 3: "Technical Orbits" (43/50)
|
||||
- **Creativity**: 10/10
|
||||
- **Storytelling**: 8/10
|
||||
- **Intuitive**: 9/10
|
||||
- **Feasibility**: 8/10
|
||||
- **Delight**: 8/10
|
||||
|
||||
### Idea 1: "System Diagram" (40/50)
|
||||
- **Creativity**: 7/10
|
||||
- **Storytelling**: 8/10
|
||||
- **Intuitive**: 9/10
|
||||
- **Feasibility**: 9/10
|
||||
- **Delight**: 7/10
|
||||
|
||||
---
|
||||
|
||||
## USE CASES Rankings
|
||||
|
||||
### Idea 2: "Role Selector" ⭐ WINNER (46/50)
|
||||
- **Creativity**: 9/10 - Dock as role selector is novel
|
||||
- **Storytelling**: 9/10 - Interactive selection tells personalized stories
|
||||
- **Intuitive**: 10/10 - Dock interface is familiar and clear
|
||||
- **Feasibility**: 8/10 - Dock + expanding cards is achievable
|
||||
- **Delight**: 10/10 - Confetti for "your" use case is pure joy
|
||||
|
||||
**Why it wins**: Interactive Dock selector with confetti when you find "your" use case creates personal connection and delight. Highest delight score overall!
|
||||
|
||||
### Idea 3: "Story Scenarios" (41/50)
|
||||
- **Creativity**: 8/10
|
||||
- **Storytelling**: 10/10
|
||||
- **Intuitive**: 8/10
|
||||
- **Feasibility**: 7/10
|
||||
- **Delight**: 8/10
|
||||
|
||||
### Idea 1: "User Journey Cards" (40/50)
|
||||
- **Creativity**: 7/10
|
||||
- **Storytelling**: 8/10
|
||||
- **Intuitive**: 9/10
|
||||
- **Feasibility**: 9/10
|
||||
- **Delight**: 7/10
|
||||
|
||||
---
|
||||
|
||||
## FAQ Rankings
|
||||
|
||||
### Idea 2: "Scratch to Answer" ⭐ WINNER (42/50)
|
||||
- **Creativity**: 9/10 - Making FAQ interactive is fresh
|
||||
- **Storytelling**: 8/10 - Discovery process engages
|
||||
- **Intuitive**: 8/10 - Scratch metaphor is understood
|
||||
- **Feasibility**: 7/10 - Multiple scratch elements need optimization
|
||||
- **Delight**: 10/10 - Scratching makes FAQs fun!
|
||||
|
||||
**Why it wins**: Scratch-to-reveal makes FAQs engaging instead of boring. Confetti on important answers (like "Fully private") creates moments of delight.
|
||||
|
||||
### Idea 3: "Interactive Q&A" (41/50)
|
||||
- **Creativity**: 7/10
|
||||
- **Storytelling**: 8/10
|
||||
- **Intuitive**: 9/10
|
||||
- **Feasibility**: 9/10
|
||||
- **Delight**: 8/10
|
||||
|
||||
### Idea 1: "Expandable Cards" (40/50)
|
||||
- **Creativity**: 6/10
|
||||
- **Storytelling**: 7/10
|
||||
- **Intuitive**: 10/10
|
||||
- **Feasibility**: 10/10
|
||||
- **Delight**: 7/10
|
||||
|
||||
---
|
||||
|
||||
## Overall Component Winners
|
||||
|
||||
### Most Effective Components
|
||||
1. **Magic Card** (9 winning sections) - Versatile spotlight effects
|
||||
2. **Text Animate** (7 sections) - Essential for reveals and typing effects
|
||||
3. **Border Beam** (6 sections) - Perfect for connections and highlights
|
||||
4. **Scratch To Reveal** (4 sections) - Highest delight factor
|
||||
5. **Confetti** (5 sections) - Celebration moments
|
||||
|
||||
### Highest Delight Components
|
||||
1. Scratch To Reveal - 10/10 in FAQ, 9/10 in Features
|
||||
2. Confetti - Creates joy at key moments
|
||||
3. Dock - 10/10 for use case selection
|
||||
4. Orbiting Circles - 9/10 for data visualization
|
||||
|
||||
### Best Storytelling Components
|
||||
1. Arc Timeline - Perfect for progression narratives
|
||||
2. Safari - Immediately familiar, great for demos
|
||||
3. Progress Bars - Universal understanding of improvement
|
||||
4. Layered Stack - Architecture hierarchy storytelling
|
||||
|
||||
---
|
||||
|
||||
## Implementation Priority
|
||||
|
||||
### High Priority (Core Experience)
|
||||
1. **Hero** - First impression is critical
|
||||
2. **Before/After** - Core value proposition
|
||||
3. **Installation** - Conversion point
|
||||
4. **The Numbers** - Proof of value
|
||||
|
||||
### Medium Priority (Supporting Narrative)
|
||||
1. **Real Examples** - Concrete use cases
|
||||
2. **How It Works** - Understanding the system
|
||||
3. **Powerful Search** - Feature highlight
|
||||
|
||||
### Lower Priority (Deep Dive)
|
||||
1. **What Gets Remembered** - Feature details
|
||||
2. **Under The Hood** - Technical deep dive
|
||||
3. **Use Cases** - Audience segmentation
|
||||
4. **FAQ** - Support content
|
||||
|
||||
---
|
||||
|
||||
## Technical Considerations
|
||||
|
||||
### Performance Concerns
|
||||
- **Scratch To Reveal**: 4 instances across page - needs optimization
|
||||
- **Orbiting Circles**: Multiple orbits can be CPU intensive
|
||||
- **Particles**: Use sparingly, can impact performance
|
||||
- **Z-space/Perspective**: May have cross-browser issues
|
||||
|
||||
### Accessibility Considerations
|
||||
- **Scratch To Reveal**: Needs keyboard alternative
|
||||
- **Confetti**: Should respect `prefers-reduced-motion`
|
||||
- **Animations**: All should be pausable/stoppable
|
||||
- **Color Contrast**: Aurora/gradient text needs testing
|
||||
|
||||
### Browser Compatibility
|
||||
- **Safari Component**: Meta - using Safari to show Safari
|
||||
- **Grid Beams/Warp**: Check WebGL support
|
||||
- **Perspective transforms**: Test in Firefox, Safari
|
||||
|
||||
---
|
||||
|
||||
## Final Winning Lineup
|
||||
|
||||
1. **Hero**: Fading Memory (42/50)
|
||||
2. **Before/After**: Split Screen Wipe (44/50)
|
||||
3. **Real Examples**: Timeline Story (44/50)
|
||||
4. **How It Works**: Layered Depth Model (43/50)
|
||||
5. **What Gets Remembered**: Memory Bank Slots (41/50)
|
||||
6. **Powerful Search**: Live Search Demo (44/50)
|
||||
7. **The Numbers**: Progress Bar Transformation (45/50) ⭐ HIGHEST SCORE
|
||||
8. **Installation**: Copy-Paste Delight (44/50)
|
||||
9. **Under The Hood**: Layered Stack (44/50)
|
||||
10. **Use Cases**: Role Selector (46/50) ⭐ HIGHEST DELIGHT
|
||||
11. **FAQ**: Scratch to Answer (42/50)
|
||||
|
||||
**Average Score**: 43.5/50 (87%)
|
||||
**Total Delight**: 94/110 (85%)
|
||||
@@ -1,134 +0,0 @@
|
||||
# Phase 1 Implementation - Complete ✅
|
||||
|
||||
Phase 1 of the REFACTOR-PLAN.md has been successfully implemented and tested.
|
||||
|
||||
## What Was Implemented
|
||||
|
||||
### 1. Database Schema (Migration 004)
|
||||
Created four new tables to support the SDK agent architecture:
|
||||
|
||||
- **`sdk_sessions`** - Tracks SDK streaming sessions
|
||||
- **`observation_queue`** - Message queue for pending observations
|
||||
- **`observations`** - Stores extracted observations from SDK
|
||||
- **`session_summaries`** - Stores structured session summaries
|
||||
|
||||
All tables include proper indexes for performance and foreign key constraints for data integrity.
|
||||
|
||||
### 2. Shared Database Layer
|
||||
Created `HooksDatabase` class ([src/services/sqlite/HooksDatabase.ts](src/services/sqlite/HooksDatabase.ts)) that provides:
|
||||
|
||||
- Simple, synchronous database operations for hooks
|
||||
- No complex logic - just basic CRUD operations
|
||||
- Optimized SQLite settings (WAL mode, foreign keys enabled)
|
||||
- Methods for all hook operations:
|
||||
- `getRecentSummaries()` - Retrieve session context
|
||||
- `createSDKSession()` - Initialize new session
|
||||
- `queueObservation()` - Add observation to queue
|
||||
- `storeObservation()` - Save SDK observations
|
||||
- `storeSummary()` - Save session summaries
|
||||
- And more...
|
||||
|
||||
### 3. Hook Functions
|
||||
Implemented all four hook functions in [src/hooks/](src/hooks/):
|
||||
|
||||
#### **context.ts** - SessionStart Hook
|
||||
- Shows user recent session context on startup
|
||||
- Formats summaries in markdown for Claude
|
||||
- Exits silently if no context or errors occur
|
||||
|
||||
#### **save.ts** - PostToolUse Hook
|
||||
- Queues tool observations for SDK processing
|
||||
- Skips low-value tools (TodoWrite, ListMcpResourcesTool)
|
||||
- Non-blocking - returns immediately
|
||||
|
||||
#### **new.ts** - UserPromptSubmit Hook
|
||||
- Initializes SDK session in database
|
||||
- Prepares for SDK worker spawn (TODO in Phase 2)
|
||||
- Non-blocking - returns immediately
|
||||
|
||||
#### **summary.ts** - Stop Hook
|
||||
- Queues FINALIZE message for SDK
|
||||
- Signals SDK to generate session summary
|
||||
- Non-blocking - returns immediately
|
||||
|
||||
### 4. CLI Integration
|
||||
Added four new commands to [src/bin/cli.ts](src/bin/cli.ts:227-274):
|
||||
|
||||
```bash
|
||||
claude-mem context # SessionStart hook
|
||||
claude-mem new # UserPromptSubmit hook
|
||||
claude-mem save # PostToolUse hook
|
||||
claude-mem summary # Stop hook
|
||||
```
|
||||
|
||||
All commands read JSON input from stdin and execute the corresponding hook function.
|
||||
|
||||
### 5. Testing
|
||||
Created comprehensive test suite ([test-phase1.ts](test-phase1.ts)) that validates:
|
||||
|
||||
- ✅ Database schema migration 004 applied correctly
|
||||
- ✅ All four tables exist
|
||||
- ✅ SDK session creation and retrieval
|
||||
- ✅ Observation queue operations
|
||||
- ✅ Observation and summary storage
|
||||
- ✅ Session status transitions
|
||||
|
||||
**All tests pass! 🎉**
|
||||
|
||||
## What's Left for Phase 2
|
||||
|
||||
The foundation is complete. Next steps:
|
||||
|
||||
1. **SDK Worker Process** - Implement the background agent that:
|
||||
- Polls observation queue
|
||||
- Sends observations to Claude SDK
|
||||
- Parses XML responses (`<observation>` and `<summary>` blocks)
|
||||
- Stores results in database
|
||||
|
||||
2. **SDK Prompts** - Implement the three prompt builders:
|
||||
- `buildInitPrompt()` - Initialize SDK agent
|
||||
- `buildObservationPrompt()` - Send tool observation
|
||||
- `buildFinalizePrompt()` - Request session summary
|
||||
|
||||
3. **Process Management** - Update [src/hooks/new.ts](src/hooks/new.ts:35-42) to spawn SDK worker as detached process
|
||||
|
||||
4. **End-to-End Testing** - Test with real Claude Code session
|
||||
|
||||
## File Changes
|
||||
|
||||
### New Files
|
||||
- [src/services/sqlite/HooksDatabase.ts](src/services/sqlite/HooksDatabase.ts) - Shared database layer
|
||||
- [src/hooks/context.ts](src/hooks/context.ts) - SessionStart hook
|
||||
- [src/hooks/save.ts](src/hooks/save.ts) - PostToolUse hook
|
||||
- [src/hooks/new.ts](src/hooks/new.ts) - UserPromptSubmit hook
|
||||
- [src/hooks/summary.ts](src/hooks/summary.ts) - Stop hook
|
||||
- [src/hooks/index.ts](src/hooks/index.ts) - Exports
|
||||
- [test-phase1.ts](test-phase1.ts) - Test suite
|
||||
|
||||
### Modified Files
|
||||
- [src/services/sqlite/migrations.ts](src/services/sqlite/migrations.ts:205-315) - Added migration 004
|
||||
- [src/services/sqlite/index.ts](src/services/sqlite/index.ts:13) - Exported HooksDatabase
|
||||
- [src/bin/cli.ts](src/bin/cli.ts:227-274) - Added hook commands
|
||||
|
||||
## Verification
|
||||
|
||||
To verify Phase 1 implementation:
|
||||
|
||||
```bash
|
||||
# Build
|
||||
bun run build
|
||||
|
||||
# Run tests
|
||||
bun test-phase1.ts
|
||||
|
||||
# Check hook commands exist
|
||||
./dist/claude-mem.min.js --help | grep -A 1 'context\|new\|save\|summary'
|
||||
```
|
||||
|
||||
All should pass without errors.
|
||||
|
||||
## Next Steps
|
||||
|
||||
Ready to proceed to Phase 2: **SDK Worker Implementation**
|
||||
|
||||
The architecture is sound, the database layer is working, and all hook functions are ready to integrate with the SDK worker process.
|
||||
@@ -1,175 +0,0 @@
|
||||
# Phase 2 Implementation Complete
|
||||
|
||||
## Summary
|
||||
|
||||
Phase 2 of the SDK Worker Process has been successfully implemented. This phase adds the background agent architecture that processes tool observations and generates session summaries.
|
||||
|
||||
## Implementation Date
|
||||
|
||||
October 15, 2025
|
||||
|
||||
## Files Created
|
||||
|
||||
### 1. SDK Prompts Module
|
||||
- **File**: [src/sdk/prompts.ts](src/sdk/prompts.ts)
|
||||
- **Purpose**: Generates prompts for the Claude Agent SDK
|
||||
- **Functions**:
|
||||
- `buildInitPrompt()` - Initialize the memory agent
|
||||
- `buildObservationPrompt()` - Send tool observations to agent
|
||||
- `buildFinalizePrompt()` - Request session summary
|
||||
|
||||
### 2. XML Parser Module
|
||||
- **File**: [src/sdk/parser.ts](src/sdk/parser.ts)
|
||||
- **Purpose**: Parse XML responses from SDK agent
|
||||
- **Functions**:
|
||||
- `parseObservations()` - Extract observation blocks
|
||||
- `parseSummary()` - Extract session summary
|
||||
- **Features**:
|
||||
- Validates observation types (decision, bugfix, feature, refactor, discovery)
|
||||
- Validates all required summary fields
|
||||
- Handles file arrays in summaries
|
||||
- No external dependencies (uses regex)
|
||||
|
||||
### 3. SDK Worker Process
|
||||
- **File**: [src/sdk/worker.ts](src/sdk/worker.ts)
|
||||
- **Purpose**: Background agent that processes observations
|
||||
- **Features**:
|
||||
- Runs as detached background process
|
||||
- Uses Claude Agent SDK streaming input mode
|
||||
- Polls observation queue every 1 second
|
||||
- Parses and stores observations and summaries
|
||||
- Handles graceful shutdown via FINALIZE message
|
||||
- Automatic error handling and session status updates
|
||||
|
||||
### 4. SDK Index Module
|
||||
- **File**: [src/sdk/index.ts](src/sdk/index.ts)
|
||||
- **Purpose**: Export all SDK module functionality
|
||||
|
||||
### 5. Test Suite
|
||||
- **File**: [test-phase2.ts](test-phase2.ts)
|
||||
- **Coverage**:
|
||||
- SDK prompt generation (3 tests)
|
||||
- XML observation parsing (4 tests)
|
||||
- XML summary parsing (4 tests)
|
||||
- Database integration (3 tests)
|
||||
- **Result**: ✅ All 14 tests passing
|
||||
|
||||
## Files Modified
|
||||
|
||||
### 1. newHook Implementation
|
||||
- **File**: [src/hooks/new.ts](src/hooks/new.ts:38-61)
|
||||
- **Changes**:
|
||||
- Uncommented SDK worker spawn code
|
||||
- Added worker path resolution (dev vs production)
|
||||
- Spawns worker as detached process with stdio: 'ignore'
|
||||
- Worker receives session DB ID as argument
|
||||
|
||||
## Architecture Validation
|
||||
|
||||
### SDK Worker Flow
|
||||
1. ✅ newHook spawns worker as detached process
|
||||
2. ✅ Worker loads session from database
|
||||
3. ✅ Worker initializes SDK agent with streaming input
|
||||
4. ✅ Worker polls observation queue continuously
|
||||
5. ✅ Worker sends observations to SDK agent
|
||||
6. ✅ Worker parses XML responses
|
||||
7. ✅ Worker stores observations and summaries
|
||||
8. ✅ Worker handles FINALIZE message
|
||||
9. ✅ Worker updates session status
|
||||
|
||||
### Data Flow
|
||||
```
|
||||
User Prompt → newHook → Create SDK Session → Spawn Worker
|
||||
↓
|
||||
Initialize SDK Agent
|
||||
↓
|
||||
← Poll Observation Queue
|
||||
↓
|
||||
Send Observations to SDK
|
||||
↓
|
||||
← Parse XML Response
|
||||
↓
|
||||
Store in Database
|
||||
↓
|
||||
Wait for FINALIZE
|
||||
↓
|
||||
Generate Summary → Exit
|
||||
```
|
||||
|
||||
## Test Results
|
||||
|
||||
```bash
|
||||
$ bun test ./test-phase2.ts
|
||||
|
||||
✅ SDK Prompts (3 tests)
|
||||
✅ should build init prompt with all required sections
|
||||
✅ should build observation prompt with tool details
|
||||
✅ should build finalize prompt with session context
|
||||
|
||||
✅ XML Parser (8 tests)
|
||||
✅ parseObservations
|
||||
✅ should parse single observation
|
||||
✅ should parse multiple observations
|
||||
✅ should skip observations with invalid types
|
||||
✅ should handle observations with surrounding text
|
||||
✅ parseSummary
|
||||
✅ should parse complete summary with all fields
|
||||
✅ should handle empty file arrays
|
||||
✅ should return null if required fields are missing
|
||||
✅ should return null if no summary block found
|
||||
|
||||
✅ HooksDatabase Integration (3 tests)
|
||||
✅ should store and retrieve observations
|
||||
✅ should store and retrieve summaries
|
||||
✅ should queue and process observations
|
||||
|
||||
14 pass, 0 fail, 53 expect() calls
|
||||
Ran 14 tests across 1 file. [60.00ms]
|
||||
```
|
||||
|
||||
## Build Verification
|
||||
|
||||
```bash
|
||||
$ npm run build
|
||||
|
||||
📌 Version: 3.9.16
|
||||
✓ Bun detected
|
||||
✓ Cleaned dist directory
|
||||
✓ Bundle created
|
||||
✓ Shebang added
|
||||
✓ Made executable
|
||||
✅ Build complete! (344.57 KB)
|
||||
```
|
||||
|
||||
## Success Criteria
|
||||
|
||||
All Phase 2 success criteria have been met:
|
||||
|
||||
- [x] SDK worker runs as detached process
|
||||
- [x] Worker polls observation queue continuously
|
||||
- [x] Worker sends observations to Claude SDK
|
||||
- [x] Worker parses `<observation>` and `<summary>` XML correctly
|
||||
- [x] Worker stores results in database using HooksDatabase
|
||||
- [x] Worker handles FINALIZE message and exits gracefully
|
||||
- [x] All tests pass
|
||||
- [x] No blocking of main Claude Code session
|
||||
|
||||
## Known Limitations
|
||||
|
||||
1. **Bundled CLI**: The worker process is currently bundled into the main CLI. For production use, we may want to extract it as a separate executable.
|
||||
2. **No logging**: Worker runs with `stdio: 'ignore'` for non-blocking behavior. Consider adding file-based logging for debugging.
|
||||
|
||||
## Next Steps
|
||||
|
||||
Phase 2 is complete and ready for integration testing with a real Claude Code session. The next phase would involve:
|
||||
|
||||
1. Testing the full end-to-end flow with actual tool observations
|
||||
2. Implementing the `saveHook` to queue observations
|
||||
3. Implementing the `summaryHook` to send FINALIZE message
|
||||
4. Verifying the context hook retrieves summaries correctly
|
||||
|
||||
## Related Documentation
|
||||
|
||||
- [REFACTOR-PLAN.md](REFACTOR-PLAN.md) - Original refactor plan
|
||||
- [PHASE1-COMPLETE.md](PHASE1-COMPLETE.md) - Phase 1 completion
|
||||
- [PHASE2-PROMPT.md](PHASE2-PROMPT.md) - Phase 2 implementation requirements
|
||||
@@ -1,118 +0,0 @@
|
||||
# Phase 2 Implementation Prompt
|
||||
|
||||
Use this prompt to start a new chat for Phase 2 implementation:
|
||||
|
||||
---
|
||||
|
||||
## Context
|
||||
|
||||
I'm implementing a refactor of the claude-mem memory system based on [REFACTOR-PLAN.md](REFACTOR-PLAN.md).
|
||||
|
||||
**Phase 1 is complete** (see [PHASE1-COMPLETE.md](PHASE1-COMPLETE.md)):
|
||||
- ✅ Database schema with migration 004
|
||||
- ✅ HooksDatabase shared layer
|
||||
- ✅ All four hook functions (context, new, save, summary)
|
||||
- ✅ CLI integration and tests passing
|
||||
|
||||
## Task
|
||||
|
||||
Implement **Phase 2: SDK Worker Process**
|
||||
|
||||
According to [REFACTOR-PLAN.md](REFACTOR-PLAN.md#2-userpromptsubmit-hook) (lines 296-423), I need to:
|
||||
|
||||
1. **Create SDK Worker Process** (`src/sdk/worker.ts`)
|
||||
- Uses Agent SDK streaming input mode
|
||||
- AsyncIterable message generator that:
|
||||
- Yields initial prompt
|
||||
- Polls observation_queue table
|
||||
- Yields observation prompts
|
||||
- Handles FINALIZE message
|
||||
- Parses SDK responses for `<observation>` and `<summary>` XML blocks
|
||||
- Stores results using HooksDatabase methods
|
||||
|
||||
2. **Create SDK Prompts** (`src/sdk/prompts.ts`)
|
||||
- `buildInitPrompt()` - Initialize agent (see REFACTOR-PLAN.md:537-595)
|
||||
- `buildObservationPrompt()` - Send tool observation (see REFACTOR-PLAN.md:601-634)
|
||||
- `buildFinalizePrompt()` - Request summary (see REFACTOR-PLAN.md:640-692)
|
||||
|
||||
3. **Create XML Parser** (`src/sdk/parser.ts`)
|
||||
- Parse `<observation>` blocks with `<type>` and `<text>`
|
||||
- Parse `<summary>` blocks with 8 required fields
|
||||
- Extract file arrays from `<file>` child elements
|
||||
|
||||
4. **Update newHook** ([src/hooks/new.ts](src/hooks/new.ts:35-42))
|
||||
- Uncomment SDK worker spawn code
|
||||
- Pass session ID to worker
|
||||
- Detached process with stdio: 'ignore'
|
||||
|
||||
5. **Test End-to-End**
|
||||
- Create test that simulates full lifecycle
|
||||
- Verify observations are queued, processed, and stored
|
||||
- Verify summary generation works
|
||||
|
||||
## Key Requirements
|
||||
|
||||
From [REFACTOR-PLAN.md](REFACTOR-PLAN.md):
|
||||
|
||||
- Use `@anthropic-ai/claude-agent-sdk` query function with streaming input mode
|
||||
- Model: `claude-sonnet-4-5`
|
||||
- Use `disallowedTools: ['Glob', 'Grep', 'ListMcpResourcesTool', 'WebSearch']`
|
||||
- Message generator yields `{ role: "user", content: string }` objects
|
||||
- Capture SDK session ID from system init message
|
||||
- Poll observation queue every 1 second
|
||||
- Use AbortController for graceful cancellation
|
||||
- Parse XML with a library (not regex) - suggest fast-xml-parser
|
||||
- Store observations and summaries using HooksDatabase methods
|
||||
|
||||
## Architecture Reference
|
||||
|
||||
The SDK worker is a **synthesis engine** that:
|
||||
- Receives tool observations (not raw data)
|
||||
- Extracts meaningful insights
|
||||
- Stores atomic observations in SQLite
|
||||
- Generates structured summaries at session end
|
||||
|
||||
See [REFACTOR-PLAN.md](REFACTOR-PLAN.md#visual-overview) (lines 69-119) for the full architecture diagram.
|
||||
|
||||
## Files to Create
|
||||
|
||||
1. `src/sdk/worker.ts` - Main SDK worker process
|
||||
2. `src/sdk/prompts.ts` - Prompt builders
|
||||
3. `src/sdk/parser.ts` - XML response parser
|
||||
4. `src/sdk/index.ts` - Exports
|
||||
5. `test-phase2.ts` - End-to-end tests
|
||||
|
||||
## Files to Modify
|
||||
|
||||
1. [src/hooks/new.ts](src/hooks/new.ts:35-42) - Spawn worker process
|
||||
2. [package.json](package.json) - May need to add fast-xml-parser dependency
|
||||
|
||||
## Testing Strategy
|
||||
|
||||
1. Unit tests for prompts (verify prompt structure)
|
||||
2. Unit tests for parser (verify XML parsing)
|
||||
3. Integration test for worker (mock SDK responses)
|
||||
4. End-to-end test (simulate full observation → summary flow)
|
||||
|
||||
## Success Criteria
|
||||
|
||||
- [ ] SDK worker runs as detached process
|
||||
- [ ] Worker polls observation queue continuously
|
||||
- [ ] Worker sends observations to Claude SDK
|
||||
- [ ] Worker parses `<observation>` and `<summary>` XML correctly
|
||||
- [ ] Worker stores results in database using HooksDatabase
|
||||
- [ ] Worker handles FINALIZE message and exits gracefully
|
||||
- [ ] All tests pass
|
||||
- [ ] No blocking of main Claude Code session
|
||||
|
||||
## Notes
|
||||
|
||||
- Keep hooks fast and non-blocking (they already are)
|
||||
- SDK worker is fire-and-forget background process
|
||||
- Use HooksDatabase methods (already implemented in Phase 1)
|
||||
- Follow the exact prompt formats from REFACTOR-PLAN.md
|
||||
- Use proper TypeScript types from Agent SDK
|
||||
|
||||
---
|
||||
|
||||
**Start with:** Create the SDK prompts module first, then the parser, then the worker. Test each piece before integrating.
|
||||
@@ -1,271 +0,0 @@
|
||||
# Phase 3 Implementation Complete ✅
|
||||
|
||||
## Summary
|
||||
|
||||
Phase 3 of the claude-mem architecture refactor has been successfully completed. This phase integrated all hook functions with the database layer and validated the complete end-to-end lifecycle through comprehensive testing.
|
||||
|
||||
## Implementation Date
|
||||
|
||||
October 15, 2025
|
||||
|
||||
## What Was Implemented
|
||||
|
||||
### 1. Hook Integration Verification
|
||||
|
||||
All four hook functions were verified to be working correctly with the database layer:
|
||||
|
||||
#### **[contextHook](src/hooks/context.ts)** - SessionStart Hook
|
||||
- ✅ Retrieves recent session summaries from database
|
||||
- ✅ Formats summaries in markdown for Claude consumption
|
||||
- ✅ Handles missing summaries gracefully
|
||||
- ✅ Only runs on startup (skips resume)
|
||||
- ✅ Fast, non-blocking operation (< 50ms)
|
||||
|
||||
#### **[newHook](src/hooks/new.ts)** - UserPromptSubmit Hook
|
||||
- ✅ Creates SDK session record in database
|
||||
- ✅ Spawns SDK worker as detached background process
|
||||
- ✅ Handles duplicate sessions gracefully
|
||||
- ✅ Fast, non-blocking operation (< 50ms)
|
||||
- ✅ Returns immediately with suppressed output
|
||||
|
||||
#### **[saveHook](src/hooks/save.ts)** - PostToolUse Hook
|
||||
- ✅ Queues tool observations to database
|
||||
- ✅ Filters out low-value tools (TodoWrite, ListMcpResourcesTool)
|
||||
- ✅ Handles missing sessions gracefully
|
||||
- ✅ Fast, non-blocking operation (< 50ms)
|
||||
- ✅ Stores JSON-stringified tool input/output
|
||||
|
||||
#### **[summaryHook](src/hooks/summary.ts)** - Stop Hook
|
||||
- ✅ Sends FINALIZE message to observation queue
|
||||
- ✅ Triggers SDK worker to generate session summary
|
||||
- ✅ Handles missing sessions gracefully
|
||||
- ✅ Fast, non-blocking operation (< 50ms)
|
||||
|
||||
### 2. Comprehensive Test Suite
|
||||
|
||||
Created two new comprehensive test files:
|
||||
|
||||
#### **[test-phase3-integration.ts](test-phase3-integration.ts)**
|
||||
Tests individual hook database integration:
|
||||
- ✅ Session management (create, find, update, complete)
|
||||
- ✅ Observation queue (queue, retrieve, process, FINALIZE)
|
||||
- ✅ Observations storage (store and retrieve)
|
||||
- ✅ Summaries (store and retrieve, project isolation)
|
||||
- **9 tests, all passing**
|
||||
|
||||
#### **[test-phase3-e2e.ts](test-phase3-e2e.ts)**
|
||||
Tests complete session lifecycle:
|
||||
- ✅ Full lifecycle: new → save → summary → context
|
||||
- ✅ Performance requirements (< 50ms per operation)
|
||||
- ✅ Interrupted sessions (observations remain in queue)
|
||||
- ✅ Multiple concurrent projects (project isolation)
|
||||
- **4 tests, all passing**
|
||||
|
||||
### 3. Database Integration
|
||||
|
||||
All hooks correctly use the [HooksDatabase](src/services/sqlite/HooksDatabase.ts) layer:
|
||||
- ✅ Simple, synchronous database operations
|
||||
- ✅ Foreign key constraints enforced
|
||||
- ✅ Proper session lifecycle management
|
||||
- ✅ Atomic operations with WAL mode
|
||||
- ✅ No complex logic in hooks (delegated to SDK worker)
|
||||
|
||||
### 4. CLI Commands
|
||||
|
||||
All four CLI commands verified working:
|
||||
- ✅ `claude-mem context` - [src/bin/cli.ts:228-234](src/bin/cli.ts#L228-L234)
|
||||
- ✅ `claude-mem new` - [src/bin/cli.ts:237-243](src/bin/cli.ts#L237-L243)
|
||||
- ✅ `claude-mem save` - [src/bin/cli.ts:246-252](src/bin/cli.ts#L246-L252)
|
||||
- ✅ `claude-mem summary` - [src/bin/cli.ts:255-261](src/bin/cli.ts#L255-L261)
|
||||
|
||||
All commands:
|
||||
- Read JSON from stdin
|
||||
- Execute corresponding hook function
|
||||
- Return proper JSON response
|
||||
- Exit with code 0
|
||||
|
||||
## Test Results
|
||||
|
||||
### All Tests Passing
|
||||
```bash
|
||||
Phase 1: ✅ Database schema and HooksDatabase tests
|
||||
Phase 2: ✅ 14 tests (SDK prompts, parser, database integration)
|
||||
Phase 3: ✅ 13 tests (9 integration + 4 e2e)
|
||||
Total: ✅ 27+ tests passing
|
||||
```
|
||||
|
||||
### Performance Validation
|
||||
```
|
||||
Average operation time: 0.04ms (well under 50ms requirement)
|
||||
Maximum operation time: 1.60ms (well under 100ms threshold)
|
||||
```
|
||||
|
||||
### Build Verification
|
||||
```bash
|
||||
✅ Build complete! (344.57 KB)
|
||||
Output: dist/claude-mem.min.js
|
||||
```
|
||||
|
||||
## Architecture Validation
|
||||
|
||||
### ✅ Complete Hook Lifecycle
|
||||
|
||||
```
|
||||
1. SessionStart (contextHook)
|
||||
↓ Retrieves recent summaries from database
|
||||
↓ Formats for Claude consumption
|
||||
↓
|
||||
2. UserPromptSubmit (newHook)
|
||||
↓ Creates SDK session
|
||||
↓ Spawns background SDK worker
|
||||
↓
|
||||
3. PostToolUse (saveHook)
|
||||
↓ Queues observations
|
||||
↓ SDK worker polls queue
|
||||
↓ SDK processes observations
|
||||
↓ SDK stores meaningful insights
|
||||
↓
|
||||
4. Stop (summaryHook)
|
||||
↓ Sends FINALIZE message
|
||||
↓ SDK generates structured summary
|
||||
↓ SDK stores summary in database
|
||||
↓
|
||||
5. Next SessionStart
|
||||
↓ New context retrieved
|
||||
⟲ Cycle repeats
|
||||
```
|
||||
|
||||
### ✅ Non-Blocking Requirements
|
||||
|
||||
All hooks meet the < 50ms performance requirement:
|
||||
- **contextHook**: Retrieves summaries (simple SELECT query)
|
||||
- **newHook**: Creates session + spawns detached process
|
||||
- **saveHook**: Inserts into queue (simple INSERT)
|
||||
- **summaryHook**: Inserts FINALIZE message (simple INSERT)
|
||||
|
||||
SDK worker runs in background independently of main session.
|
||||
|
||||
### ✅ Error Handling
|
||||
|
||||
All hooks handle errors gracefully:
|
||||
- Database errors → log + continue
|
||||
- Missing sessions → silently continue
|
||||
- Process spawn failures → log + continue
|
||||
- Never block Claude Code session
|
||||
|
||||
### ✅ Data Integrity
|
||||
|
||||
Foreign key constraints enforce referential integrity:
|
||||
- Observations reference SDK sessions
|
||||
- Summaries reference SDK sessions
|
||||
- Queue items reference SDK sessions
|
||||
- Sessions reference Claude sessions
|
||||
|
||||
## Success Criteria Met
|
||||
|
||||
All Phase 3 success criteria have been achieved:
|
||||
|
||||
- [x] saveHook queues observations to database
|
||||
- [x] summaryHook sends FINALIZE message
|
||||
- [x] contextHook retrieves and formats summaries
|
||||
- [x] End-to-end test passes (full lifecycle)
|
||||
- [x] All hooks respond in < 50ms
|
||||
- [x] Worker processes observations and generates summary
|
||||
- [x] CLI commands work correctly
|
||||
- [x] All tests pass (27+ tests)
|
||||
- [x] Build succeeds (344.57 KB)
|
||||
- [x] Database foreign key constraints enforced
|
||||
- [x] Multiple concurrent projects supported
|
||||
- [x] Interrupted sessions handled gracefully
|
||||
|
||||
## Files Modified
|
||||
|
||||
### Hook Implementations (Already Complete)
|
||||
- [src/hooks/context.ts](src/hooks/context.ts) - SessionStart hook
|
||||
- [src/hooks/save.ts](src/hooks/save.ts) - PostToolUse hook
|
||||
- [src/hooks/new.ts](src/hooks/new.ts) - UserPromptSubmit hook
|
||||
- [src/hooks/summary.ts](src/hooks/summary.ts) - Stop hook
|
||||
|
||||
### Test Files Created
|
||||
- [test-phase3-integration.ts](test-phase3-integration.ts) - Hook database integration tests
|
||||
- [test-phase3-e2e.ts](test-phase3-e2e.ts) - End-to-end lifecycle tests
|
||||
|
||||
### CLI Integration (Already Complete)
|
||||
- [src/bin/cli.ts](src/bin/cli.ts) - CLI commands for all hooks
|
||||
|
||||
## Install Flow Updates
|
||||
|
||||
### ✅ CLI-Based Hook Architecture
|
||||
|
||||
Updated the install flow to use the new CLI-based architecture:
|
||||
|
||||
**Before (Old Architecture):**
|
||||
- Installed hook template files (`session-start.js`, etc.)
|
||||
- Copied shared helper modules
|
||||
- Configured settings.json to point to hook files
|
||||
|
||||
**After (New Architecture):**
|
||||
- Hooks are CLI commands: `claude-mem context`, `claude-mem new`, `claude-mem save`, `claude-mem summary`
|
||||
- Settings.json configured directly with CLI commands
|
||||
- No separate hook files needed
|
||||
- Simpler installation and maintenance
|
||||
|
||||
**Updated Install Steps:**
|
||||
```javascript
|
||||
settings.hooks.SessionStart = [{ type: "command", command: "claude-mem context", timeout: 180 }]
|
||||
settings.hooks.Stop = [{ type: "command", command: "claude-mem summary", timeout: 60 }]
|
||||
settings.hooks.UserPromptSubmit = [{ type: "command", command: "claude-mem new", timeout: 60 }]
|
||||
settings.hooks.PostToolUse = [{ type: "command", command: "claude-mem save", timeout: 180, matcher: "*" }]
|
||||
```
|
||||
|
||||
**Benefits:**
|
||||
- ✅ Single source of truth (CLI implementation)
|
||||
- ✅ No hook file synchronization issues
|
||||
- ✅ Easier debugging (just test CLI commands)
|
||||
- ✅ Simpler installation process
|
||||
- ✅ Better maintainability
|
||||
|
||||
## Related Documentation
|
||||
|
||||
- [REFACTOR-PLAN.md](REFACTOR-PLAN.md) - Complete architecture plan
|
||||
- [PHASE1-COMPLETE.md](PHASE1-COMPLETE.md) - Database & HooksDatabase layer
|
||||
- [PHASE2-COMPLETE.md](PHASE2-COMPLETE.md) - SDK worker process
|
||||
- **PHASE3-COMPLETE.md** (this document) - Hook integration & testing
|
||||
|
||||
## Next Steps
|
||||
|
||||
Phase 3 is complete! The claude-mem system is now ready for real-world testing with actual Claude Code sessions.
|
||||
|
||||
### Recommended Next Actions
|
||||
|
||||
1. **Manual Testing**
|
||||
- Configure hooks in `~/.config/claude-code/settings.json`
|
||||
- Run a real Claude Code session
|
||||
- Verify observations are queued
|
||||
- Verify summaries are generated
|
||||
- Verify context is injected on next session
|
||||
|
||||
2. **Monitoring & Debugging**
|
||||
- Add file-based logging to SDK worker
|
||||
- Monitor `~/.claude-mem/claude-mem.db` for data
|
||||
- Check observation queue processing
|
||||
- Verify summary generation
|
||||
|
||||
3. **Future Enhancements**
|
||||
- Extract SDK worker as separate executable (not bundled)
|
||||
- Add resumption support for interrupted SDK sessions
|
||||
- Implement retry logic for failed observations
|
||||
- Add telemetry and error reporting
|
||||
- Optimize database queries with additional indexes
|
||||
|
||||
## Conclusion
|
||||
|
||||
Phase 3 successfully completes the claude-mem architecture refactor. All three phases are now complete:
|
||||
|
||||
- ✅ **Phase 1**: Database schema and shared layer
|
||||
- ✅ **Phase 2**: SDK worker process and prompts
|
||||
- ✅ **Phase 3**: Hook integration and end-to-end testing
|
||||
|
||||
The system is architecturally sound, fully tested, and ready for production use!
|
||||
|
||||
🎉 **Refactor Complete!** 🎉
|
||||
@@ -1,3 +1,8 @@
|
||||
---
|
||||
title: "Database Architecture"
|
||||
description: "SQLite schema, FTS5 search, and data storage"
|
||||
---
|
||||
|
||||
# Database Architecture
|
||||
|
||||
Claude-Mem uses SQLite 3 with the better-sqlite3 native module for persistent storage and FTS5 for full-text search.
|
||||
@@ -1,3 +1,8 @@
|
||||
---
|
||||
title: "Plugin Hooks"
|
||||
description: "5 lifecycle hooks that power Claude-Mem"
|
||||
---
|
||||
|
||||
# Plugin Hooks
|
||||
|
||||
Claude-Mem integrates with Claude Code through 5 lifecycle hooks that capture events and inject context.
|
||||
@@ -1,3 +1,8 @@
|
||||
---
|
||||
title: "MCP Search Server"
|
||||
description: "7 search tools with examples and usage patterns"
|
||||
---
|
||||
|
||||
# MCP Search Server
|
||||
|
||||
Claude-Mem includes a Model Context Protocol (MCP) server that exposes 7 specialized search tools for querying stored observations and sessions.
|
||||
@@ -1,3 +1,8 @@
|
||||
---
|
||||
title: "Architecture Overview"
|
||||
description: "System components and data flow in Claude-Mem"
|
||||
---
|
||||
|
||||
# Architecture Overview
|
||||
|
||||
## System Components
|
||||
@@ -148,13 +153,13 @@ claude-mem/
|
||||
## Component Details
|
||||
|
||||
### 1. Plugin Hooks
|
||||
See [hooks.md](hooks.md) for detailed hook documentation.
|
||||
See [Plugin Hooks](/architecture/hooks) for detailed hook documentation.
|
||||
|
||||
### 2. Worker Service
|
||||
See [worker-service.md](worker-service.md) for HTTP API and endpoints.
|
||||
See [Worker Service](/architecture/worker-service) for HTTP API and endpoints.
|
||||
|
||||
### 3. Database Layer
|
||||
See [database.md](database.md) for schema and FTS5 search.
|
||||
See [Database Architecture](/architecture/database) for schema and FTS5 search.
|
||||
|
||||
### 4. MCP Search Server
|
||||
See [mcp-search.md](mcp-search.md) for search tools and examples.
|
||||
See [MCP Search Server](/architecture/mcp-search) for search tools and examples.
|
||||
@@ -1,3 +1,8 @@
|
||||
---
|
||||
title: "Worker Service"
|
||||
description: "HTTP API and PM2 process management"
|
||||
---
|
||||
|
||||
# Worker Service
|
||||
|
||||
The worker service is a long-running HTTP API built with Express.js and managed by PM2. It processes observations through the Claude Agent SDK separately from hook execution to prevent timeout issues.
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 78 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 78 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 42 KiB |
@@ -1,3 +1,8 @@
|
||||
---
|
||||
title: "Configuration"
|
||||
description: "Environment variables and settings for Claude-Mem"
|
||||
---
|
||||
|
||||
# Configuration
|
||||
|
||||
## Environment Variables
|
||||
@@ -293,6 +298,6 @@ If port 37777 is already in use:
|
||||
|
||||
## Next Steps
|
||||
|
||||
- [Architecture Overview](architecture/overview.md) - Understand the system
|
||||
- [Troubleshooting](troubleshooting.md) - Common issues
|
||||
- [Development](development.md) - Building from source
|
||||
- [Architecture Overview](architecture/overview) - Understand the system
|
||||
- [Troubleshooting](troubleshooting) - Common issues
|
||||
- [Development](development) - Building from source
|
||||
@@ -1,3 +1,8 @@
|
||||
---
|
||||
title: "Development"
|
||||
description: "Build from source, run tests, and contribute to Claude-Mem"
|
||||
---
|
||||
|
||||
# Development Guide
|
||||
|
||||
## Building from Source
|
||||
@@ -552,6 +557,6 @@ rm -rf plugin/scripts/*.js plugin/scripts/*.cjs
|
||||
|
||||
## Next Steps
|
||||
|
||||
- [Architecture Overview](architecture/overview.md) - Understand the system
|
||||
- [Configuration](configuration.md) - Customize Claude-Mem
|
||||
- [Troubleshooting](troubleshooting.md) - Common issues
|
||||
- [Architecture Overview](architecture/overview) - Understand the system
|
||||
- [Configuration](configuration) - Customize Claude-Mem
|
||||
- [Troubleshooting](troubleshooting) - Common issues
|
||||
+115
@@ -0,0 +1,115 @@
|
||||
{
|
||||
"$schema": "https://mintlify.com/schema.json",
|
||||
"name": "Claude-Mem",
|
||||
"description": "Persistent memory compression system that preserves context across Claude Code sessions",
|
||||
"theme": "mint",
|
||||
"favicon": "/claude-mem-logomark.webp",
|
||||
"logo": {
|
||||
"light": "/claude-mem-logo-for-light-mode.webp",
|
||||
"dark": "/claude-mem-logo-for-dark-mode.webp",
|
||||
"href": "https://github.com/thedotmack/claude-mem"
|
||||
},
|
||||
"colors": {
|
||||
"primary": "#3B82F6",
|
||||
"light": "#EFF6FF",
|
||||
"dark": "#1E40AF"
|
||||
},
|
||||
"navbar": {
|
||||
"links": [
|
||||
{
|
||||
"label": "GitHub",
|
||||
"href": "https://github.com/thedotmack/claude-mem"
|
||||
}
|
||||
],
|
||||
"primary": {
|
||||
"type": "button",
|
||||
"label": "Install",
|
||||
"href": "https://github.com/thedotmack/claude-mem#quick-start"
|
||||
}
|
||||
},
|
||||
"navigation": {
|
||||
"groups": [
|
||||
{
|
||||
"group": "Get Started",
|
||||
"icon": "rocket",
|
||||
"pages": [
|
||||
"introduction",
|
||||
"installation",
|
||||
"usage/getting-started",
|
||||
"usage/search-tools"
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "Configuration & Development",
|
||||
"icon": "gear",
|
||||
"pages": [
|
||||
"configuration",
|
||||
"development",
|
||||
"troubleshooting"
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "Architecture",
|
||||
"icon": "diagram-project",
|
||||
"pages": [
|
||||
"architecture/overview",
|
||||
"architecture/hooks",
|
||||
"architecture/worker-service",
|
||||
"architecture/database",
|
||||
"architecture/mcp-search"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"footer": {
|
||||
"socials": {
|
||||
"github": "https://github.com/thedotmack/claude-mem"
|
||||
},
|
||||
"links": [
|
||||
{
|
||||
"header": "Resources",
|
||||
"items": [
|
||||
{
|
||||
"label": "Documentation",
|
||||
"href": "https://github.com/thedotmack/claude-mem"
|
||||
},
|
||||
{
|
||||
"label": "Issues",
|
||||
"href": "https://github.com/thedotmack/claude-mem/issues"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"header": "Legal",
|
||||
"items": [
|
||||
{
|
||||
"label": "License (AGPL-3.0)",
|
||||
"href": "https://github.com/thedotmack/claude-mem/blob/main/LICENSE"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"seo": {
|
||||
"indexing": "all",
|
||||
"metatags": {
|
||||
"og:type": "website",
|
||||
"og:site_name": "Claude-Mem Documentation",
|
||||
"og:description": "Persistent memory compression system that preserves context across Claude Code sessions"
|
||||
}
|
||||
},
|
||||
"contextual": {
|
||||
"options": [
|
||||
"copy",
|
||||
"view",
|
||||
"chatgpt",
|
||||
"claude",
|
||||
"cursor"
|
||||
]
|
||||
},
|
||||
"integrations": {
|
||||
"telemetry": {
|
||||
"enabled": false
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,3 +1,8 @@
|
||||
---
|
||||
title: "Installation"
|
||||
description: "Install Claude-Mem plugin for persistent memory across sessions"
|
||||
---
|
||||
|
||||
# Installation Guide
|
||||
|
||||
## Quick Start
|
||||
@@ -99,10 +104,10 @@ v4.0.0 introduces breaking changes:
|
||||
- **MCP Search Server**: 7 new search tools with full-text search and citations
|
||||
- **Enhanced Architecture**: Improved plugin integration and data organization
|
||||
|
||||
See [CHANGELOG.md](../CHANGELOG.md) for complete details.
|
||||
See [CHANGELOG](https://github.com/thedotmack/claude-mem/blob/main/CHANGELOG.md) for complete details.
|
||||
|
||||
## Next Steps
|
||||
|
||||
- [Getting Started Guide](usage/getting-started.md) - Learn how Claude-Mem works automatically
|
||||
- [MCP Search Tools](usage/search-tools.md) - Query your project history
|
||||
- [Configuration](configuration.md) - Customize Claude-Mem behavior
|
||||
- [Getting Started Guide](usage/getting-started) - Learn how Claude-Mem works automatically
|
||||
- [MCP Search Tools](usage/search-tools) - Query your project history
|
||||
- [Configuration](configuration) - Customize Claude-Mem behavior
|
||||
@@ -0,0 +1,96 @@
|
||||
---
|
||||
title: "Introduction"
|
||||
description: "Persistent memory compression system for Claude Code"
|
||||
---
|
||||
|
||||
# Claude-Mem
|
||||
|
||||
**Persistent memory compression system for Claude Code**
|
||||
|
||||
Claude-Mem seamlessly preserves context across sessions by automatically capturing tool usage observations, generating semantic summaries, and making them available to future sessions. This enables Claude to maintain continuity of knowledge about projects even after sessions end or reconnect.
|
||||
|
||||
## Quick Start
|
||||
|
||||
Start a new Claude Code session in the terminal and enter the following commands:
|
||||
|
||||
```bash
|
||||
/plugin marketplace add thedotmack/claude-mem
|
||||
/plugin install claude-mem
|
||||
```
|
||||
|
||||
Restart Claude Code. Context from previous sessions will automatically appear in new sessions.
|
||||
|
||||
## Key Features
|
||||
|
||||
- 🧠 **Persistent Memory** - Context survives across sessions
|
||||
- 🔍 **7 Search Tools** - Query your project history via MCP
|
||||
- 🤖 **Automatic Operation** - No manual intervention required
|
||||
- 📊 **FTS5 Search** - Fast full-text search across observations
|
||||
- 🔗 **Citations** - Reference past decisions with `claude-mem://` URIs
|
||||
|
||||
## How It Works
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────┐
|
||||
│ Session Start → Inject context from last 10 sessions │
|
||||
└─────────────────────────────────────────────────────────────┘
|
||||
↓
|
||||
┌─────────────────────────────────────────────────────────────┐
|
||||
│ User Prompts → Create session, save user prompts │
|
||||
└─────────────────────────────────────────────────────────────┘
|
||||
↓
|
||||
┌─────────────────────────────────────────────────────────────┐
|
||||
│ Tool Executions → Capture observations (Read, Write, etc.) │
|
||||
└─────────────────────────────────────────────────────────────┘
|
||||
↓
|
||||
┌─────────────────────────────────────────────────────────────┐
|
||||
│ Worker Processes → Extract learnings via Claude Agent SDK │
|
||||
└─────────────────────────────────────────────────────────────┘
|
||||
↓
|
||||
┌─────────────────────────────────────────────────────────────┐
|
||||
│ Session Ends → Generate summary, ready for next session │
|
||||
└─────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
**Core Components:**
|
||||
1. **5 Lifecycle Hooks** - SessionStart, UserPromptSubmit, PostToolUse, Stop, SessionEnd
|
||||
2. **Worker Service** - HTTP API on port 37777 managed by PM2
|
||||
3. **SQLite Database** - Stores sessions, observations, summaries with FTS5 search
|
||||
4. **7 MCP Search Tools** - Query historical context with citations
|
||||
|
||||
See [Architecture Overview](architecture/overview) for details.
|
||||
|
||||
## System Requirements
|
||||
|
||||
- **Node.js**: 18.0.0 or higher
|
||||
- **Claude Code**: Latest version with plugin support
|
||||
- **PM2**: Process manager (bundled - no global install required)
|
||||
- **SQLite 3**: For persistent storage (bundled)
|
||||
|
||||
## What's New in v4.2.3
|
||||
|
||||
**Security:**
|
||||
- Fixed FTS5 injection vulnerability in search functions
|
||||
- Added comprehensive test suite with 332 injection attack tests
|
||||
|
||||
**Fixes:**
|
||||
- Fixed ESM/CJS compatibility for getDirname function
|
||||
- Fixed Windows PowerShell compatibility in SessionStart hook
|
||||
- Cross-platform dependency installation now works on Windows, macOS, and Linux
|
||||
|
||||
## Next Steps
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card title="Installation" icon="download" href="/installation">
|
||||
Quick start & advanced installation
|
||||
</Card>
|
||||
<Card title="Getting Started" icon="rocket" href="/usage/getting-started">
|
||||
Learn how Claude-Mem works automatically
|
||||
</Card>
|
||||
<Card title="Architecture" icon="sitemap" href="/architecture/overview">
|
||||
System components & data flow
|
||||
</Card>
|
||||
<Card title="Search Tools" icon="magnifying-glass" href="/usage/search-tools">
|
||||
Query your project history
|
||||
</Card>
|
||||
</CardGroup>
|
||||
@@ -0,0 +1,338 @@
|
||||
# Memory Toggle Feature - Design Plan
|
||||
|
||||
## Overview
|
||||
|
||||
Users need the ability to pause/resume claude-mem memory recording without uninstalling the plugin. This addresses use cases where users want temporary privacy, testing sessions, or simply don't want certain conversations saved.
|
||||
|
||||
## User Requirements (from feedback)
|
||||
|
||||
- "I don't want to save everything always"
|
||||
- Need persistent on/off state across sessions
|
||||
- Should be easy to toggle without complex commands
|
||||
- Should not interfere with conversation context
|
||||
|
||||
## Design Principles
|
||||
|
||||
1. **Non-intrusive**: Toggling memory should not require special characters in prompts
|
||||
2. **Persistent**: State should survive across Claude Code sessions
|
||||
3. **Simple**: Clear on/off with easy status checking
|
||||
4. **Fast**: Minimal overhead when checking enabled state
|
||||
|
||||
## Implementation Options
|
||||
|
||||
### Option A: Settings Flag (Recommended)
|
||||
|
||||
**Storage**: `~/.claude/settings.json`
|
||||
```json
|
||||
{
|
||||
"CLAUDE_MEM_ENABLED": true // default: true
|
||||
}
|
||||
```
|
||||
|
||||
**CLI Commands**:
|
||||
```bash
|
||||
claude-mem pause # Disable memory recording
|
||||
claude-mem resume # Enable memory recording
|
||||
claude-mem status # Show current state
|
||||
```
|
||||
|
||||
**Hook Behavior**:
|
||||
- All hooks check `CLAUDE_MEM_ENABLED` setting at start
|
||||
- If `false`, hooks return early without saving/processing
|
||||
- Worker service continues running (ready for resume)
|
||||
|
||||
**Pros**:
|
||||
- Uses existing Claude settings infrastructure
|
||||
- Simple boolean flag
|
||||
- Easy to implement in all hooks
|
||||
|
||||
**Cons**:
|
||||
- Modifies Claude Code's settings file
|
||||
|
||||
---
|
||||
|
||||
### Option B: Local Config File
|
||||
|
||||
**Storage**: `~/.claude-mem/config.json`
|
||||
```json
|
||||
{
|
||||
"enabled": true,
|
||||
"version": "4.2.8",
|
||||
"lastToggled": "2025-10-25T01:30:00Z"
|
||||
}
|
||||
```
|
||||
|
||||
**Same CLI commands as Option A**
|
||||
|
||||
**Pros**:
|
||||
- Isolated from Claude Code settings
|
||||
- Can add metadata (timestamp, reason, etc.)
|
||||
- Full control over config structure
|
||||
|
||||
**Cons**:
|
||||
- Additional file to manage
|
||||
- Hooks need to read separate config file
|
||||
|
||||
---
|
||||
|
||||
### Option C: Slash Command Integration
|
||||
|
||||
**Usage**: In-session control via slash commands
|
||||
```bash
|
||||
/claude-mem pause
|
||||
/claude-mem resume
|
||||
/claude-mem status
|
||||
```
|
||||
|
||||
**Pros**:
|
||||
- No need to leave Claude Code session
|
||||
- Immediate feedback
|
||||
- Familiar interface for users
|
||||
|
||||
**Cons**:
|
||||
- Requires slash command setup
|
||||
- Need to update `.claude/commands/` structure
|
||||
- More complex implementation
|
||||
|
||||
---
|
||||
|
||||
## Recommended Approach: Hybrid A + C
|
||||
|
||||
Combine Settings Flag (A) with optional Slash Commands (C):
|
||||
|
||||
1. **Persistent State**: Store in `~/.claude/settings.json` or `~/.claude-mem/config.json`
|
||||
2. **CLI Control**: `claude-mem pause/resume/status`
|
||||
3. **Optional In-Session Control**: `/claude-mem pause/resume` slash commands
|
||||
4. **Hook Check**: All hooks read setting at startup, skip if disabled
|
||||
|
||||
---
|
||||
|
||||
## Implementation Details
|
||||
|
||||
### Hook Modifications
|
||||
|
||||
All hooks need to check enabled state:
|
||||
|
||||
```typescript
|
||||
// src/shared/settings.ts
|
||||
export function isMemoryEnabled(): boolean {
|
||||
try {
|
||||
const settingsPath = path.join(os.homedir(), '.claude', 'settings.json');
|
||||
const settings = JSON.parse(fs.readFileSync(settingsPath, 'utf8'));
|
||||
return settings.CLAUDE_MEM_ENABLED !== false; // default true
|
||||
} catch {
|
||||
return true; // default to enabled if settings missing
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
```typescript
|
||||
// In each hook
|
||||
import { isMemoryEnabled } from '../shared/settings.js';
|
||||
|
||||
export function hookFunction(input: HookInput) {
|
||||
if (!isMemoryEnabled()) {
|
||||
return; // Skip all memory operations
|
||||
}
|
||||
|
||||
// Normal hook logic...
|
||||
}
|
||||
```
|
||||
|
||||
### CLI Commands
|
||||
|
||||
```typescript
|
||||
// src/bin/cli.ts
|
||||
program
|
||||
.command('pause')
|
||||
.description('Pause memory recording (sessions will not be saved)')
|
||||
.action(() => {
|
||||
updateMemorySetting(false);
|
||||
console.log('✓ Memory recording paused');
|
||||
});
|
||||
|
||||
program
|
||||
.command('resume')
|
||||
.description('Resume memory recording')
|
||||
.action(() => {
|
||||
updateMemorySetting(true);
|
||||
console.log('✓ Memory recording resumed');
|
||||
});
|
||||
|
||||
program
|
||||
.command('status')
|
||||
.description('Show current memory recording status')
|
||||
.action(() => {
|
||||
const enabled = isMemoryEnabled();
|
||||
console.log(`Memory recording: ${enabled ? '✓ ENABLED' : '✗ PAUSED'}`);
|
||||
});
|
||||
```
|
||||
|
||||
### Slash Commands (Optional)
|
||||
|
||||
Create `.claude/commands/claude-mem.md`:
|
||||
```markdown
|
||||
# Claude Mem Control
|
||||
|
||||
Usage: /claude-mem [pause|resume|status]
|
||||
|
||||
- pause: Stop recording this session
|
||||
- resume: Resume recording
|
||||
- status: Show current state
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## User Experience
|
||||
|
||||
### Pausing Memory
|
||||
```bash
|
||||
$ claude-mem pause
|
||||
✓ Memory recording paused
|
||||
|
||||
All future sessions will NOT be saved until you run:
|
||||
claude-mem resume
|
||||
```
|
||||
|
||||
### Checking Status
|
||||
```bash
|
||||
$ claude-mem status
|
||||
Memory recording: ✗ PAUSED
|
||||
|
||||
To resume recording: claude-mem resume
|
||||
```
|
||||
|
||||
### Resume Recording
|
||||
```bash
|
||||
$ claude-mem resume
|
||||
✓ Memory recording resumed
|
||||
|
||||
All sessions will now be saved to ~/.claude-mem/
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Affected Hooks
|
||||
|
||||
### new-hook (UserPromptSubmit)
|
||||
- Skip creating session records
|
||||
- Skip saving user prompts
|
||||
|
||||
### save-hook (PostToolUse)
|
||||
- Skip capturing observations
|
||||
- Skip sending to worker service
|
||||
|
||||
### summary-hook
|
||||
- Skip generating summaries
|
||||
- Session remains unprocessed
|
||||
|
||||
### context-hook (SessionStart)
|
||||
- Still runs (to show status message?)
|
||||
- Could display: "Memory recording is currently paused"
|
||||
|
||||
### cleanup-hook (SessionEnd)
|
||||
- Skip marking sessions complete
|
||||
- No cleanup needed if nothing was saved
|
||||
|
||||
---
|
||||
|
||||
## Edge Cases
|
||||
|
||||
1. **Mid-session toggle**: What if user pauses during a session?
|
||||
- Decision: New state takes effect immediately
|
||||
- Observations before pause are saved, after pause are not
|
||||
|
||||
2. **Worker service**: Should it stop when paused?
|
||||
- Decision: Keep running (minimal resource usage)
|
||||
- Ready to process when resumed
|
||||
|
||||
3. **Existing data**: What happens to saved sessions when paused?
|
||||
- Decision: No change, data persists
|
||||
- Context hook can still load past sessions
|
||||
|
||||
4. **Uninstall vs Pause**: Clear distinction?
|
||||
- Pause: Temporary, easy to resume
|
||||
- Uninstall: Complete removal, requires reinstall
|
||||
|
||||
---
|
||||
|
||||
## Migration & Compatibility
|
||||
|
||||
- Default to `true` (enabled) for backward compatibility
|
||||
- Existing users unaffected unless they explicitly pause
|
||||
- No database schema changes needed
|
||||
- No breaking changes to hooks
|
||||
|
||||
---
|
||||
|
||||
## Future Enhancements
|
||||
|
||||
1. **Selective Recording**: Fine-grained control
|
||||
```bash
|
||||
claude-mem pause --observations-only
|
||||
claude-mem pause --summaries-only
|
||||
```
|
||||
|
||||
2. **Project-Level Control**: Different settings per project
|
||||
```bash
|
||||
claude-mem pause --project claude-mem
|
||||
```
|
||||
|
||||
3. **Temporary Pause**: Auto-resume after duration
|
||||
```bash
|
||||
claude-mem pause --duration 1h
|
||||
```
|
||||
|
||||
4. **Pause Reasons**: Track why users pause
|
||||
```bash
|
||||
claude-mem pause --reason "debugging session"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Open Questions
|
||||
|
||||
1. Should context hook still inject past context when paused?
|
||||
- Lean toward: Yes, show past but don't save current
|
||||
|
||||
2. Should we show a status indicator when paused?
|
||||
- Could add to context hook output: "🔴 Memory recording paused"
|
||||
|
||||
3. CLI command naming: `pause/resume` vs `disable/enable` vs `off/on`?
|
||||
- Lean toward: `pause/resume` (temporary feeling)
|
||||
|
||||
4. Should worker service stop when paused?
|
||||
- Lean toward: Keep running (fast resume)
|
||||
|
||||
---
|
||||
|
||||
## Timeline
|
||||
|
||||
- **Phase 1**: Settings flag + CLI commands (1-2 days)
|
||||
- **Phase 2**: Status indicator in context hook (1 day)
|
||||
- **Phase 3**: Slash commands (optional, 1 day)
|
||||
- **Phase 4**: User testing & feedback (1 week)
|
||||
- **Phase 5**: Release in v4.3.0
|
||||
|
||||
---
|
||||
|
||||
## Success Metrics
|
||||
|
||||
- Users report successful pausing/resuming
|
||||
- No saved data when paused (verify in tests)
|
||||
- Fast toggle time (< 100ms)
|
||||
- Clear status feedback
|
||||
- No edge case bugs in production
|
||||
|
||||
---
|
||||
|
||||
## Related Issues
|
||||
|
||||
- GitHub feedback: "I don't want to save everything always"
|
||||
- Privacy concerns: Users want control over what's saved
|
||||
- Testing workflows: Developers need clean test sessions
|
||||
|
||||
---
|
||||
|
||||
*Last Updated: October 25, 2025*
|
||||
*Status: Planning / Not Yet Implemented*
|
||||
@@ -1,3 +1,8 @@
|
||||
---
|
||||
title: "Troubleshooting"
|
||||
description: "Common issues and solutions for Claude-Mem"
|
||||
---
|
||||
|
||||
# Troubleshooting Guide
|
||||
|
||||
## Worker Service Issues
|
||||
@@ -664,6 +669,6 @@ If none of these solutions work:
|
||||
|
||||
## Next Steps
|
||||
|
||||
- [Configuration](configuration.md) - Customize Claude-Mem
|
||||
- [Development](development.md) - Build from source
|
||||
- [Architecture](architecture/overview.md) - Understand the system
|
||||
- [Configuration](configuration) - Customize Claude-Mem
|
||||
- [Development](development) - Build from source
|
||||
- [Architecture](architecture/overview) - Understand the system
|
||||
@@ -1,3 +1,8 @@
|
||||
---
|
||||
title: "Getting Started"
|
||||
description: "Learn how Claude-Mem works automatically in the background"
|
||||
---
|
||||
|
||||
# Getting Started with Claude-Mem
|
||||
|
||||
## Automatic Operation
|
||||
@@ -166,6 +171,6 @@ When you use `/clear`, the session doesn't end - it continues with a new prompt
|
||||
|
||||
## Next Steps
|
||||
|
||||
- [MCP Search Tools](search-tools.md) - Learn how to search your project history
|
||||
- [Architecture Overview](../architecture/overview.md) - Understand how it works
|
||||
- [Troubleshooting](../troubleshooting.md) - Common issues and solutions
|
||||
- [MCP Search Tools](/usage/search-tools) - Learn how to search your project history
|
||||
- [Architecture Overview](/architecture/overview) - Understand how it works
|
||||
- [Troubleshooting](/troubleshooting) - Common issues and solutions
|
||||
@@ -1,3 +1,8 @@
|
||||
---
|
||||
title: "MCP Search Tools"
|
||||
description: "Query your project history with 7 specialized search tools"
|
||||
---
|
||||
|
||||
# MCP Search Tools Usage
|
||||
|
||||
Once claude-mem is installed as a plugin, 7 search tools become available in your Claude Code sessions for querying project history.
|
||||
@@ -416,6 +421,6 @@ search_sessions with query="[YOUR PROJECT NAME]" and orderBy="date_desc"
|
||||
|
||||
## Next Steps
|
||||
|
||||
- [MCP Search Architecture](../architecture/mcp-search.md) - Technical details
|
||||
- [Database Schema](../architecture/database.md) - Understanding the data
|
||||
- [Getting Started](getting-started.md) - Automatic operation
|
||||
- [MCP Search Architecture](/architecture/mcp-search) - Technical details
|
||||
- [Database Schema](/architecture/database) - Understanding the data
|
||||
- [Getting Started](/usage/getting-started) - Automatic operation
|
||||
File diff suppressed because it is too large
Load Diff
Generated
+8
-6
@@ -1,15 +1,15 @@
|
||||
{
|
||||
"name": "claude-mem",
|
||||
"version": "4.2.1",
|
||||
"version": "4.2.10",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "claude-mem",
|
||||
"version": "4.2.1",
|
||||
"version": "4.2.10",
|
||||
"license": "AGPL-3.0",
|
||||
"dependencies": {
|
||||
"@anthropic-ai/claude-agent-sdk": "^0.1.23",
|
||||
"@anthropic-ai/claude-agent-sdk": "^0.1.27",
|
||||
"@modelcontextprotocol/sdk": "^1.20.1",
|
||||
"better-sqlite3": "^11.0.0",
|
||||
"express": "^4.18.2",
|
||||
@@ -31,9 +31,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@anthropic-ai/claude-agent-sdk": {
|
||||
"version": "0.1.23",
|
||||
"resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk/-/claude-agent-sdk-0.1.23.tgz",
|
||||
"integrity": "sha512-DktXOjzS2hOuuj2Zpo7fEooONfMa5bm09pt1/Vt4vn30YugELoezn/ZQ/TG5uSQ7+Zl/ElxAvi4vGDorj1Tirg==",
|
||||
"version": "0.1.27",
|
||||
"resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk/-/claude-agent-sdk-0.1.27.tgz",
|
||||
"integrity": "sha512-HuMPW6spj2q8FODiP/WBCqUZAYGwDPoI1EpicP9KUXvuYk+2MZQYSaD7oiN6iNPupR2T5oJ2HY/D9OzjyCD2Mw==",
|
||||
"license": "SEE LICENSE IN README.md",
|
||||
"engines": {
|
||||
"node": ">=18.0.0"
|
||||
@@ -2304,6 +2304,7 @@
|
||||
"resolved": "https://registry.npmjs.org/express/-/express-4.21.2.tgz",
|
||||
"integrity": "sha512-28HqgMZAmih1Czt9ny7qr6ek2qddF4FclbMzwhCREB6OFfH+rXAnuNCwo1/wFvrtbgsQDb4kSbX9de9lFbrXnA==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"accepts": "~1.3.8",
|
||||
"array-flatten": "1.1.1",
|
||||
@@ -5242,6 +5243,7 @@
|
||||
"node_modules/zod": {
|
||||
"version": "3.25.76",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/colinhacks"
|
||||
}
|
||||
|
||||
+5
-23
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "claude-mem",
|
||||
"version": "4.2.3",
|
||||
"version": "4.3.0",
|
||||
"description": "Memory compression system for Claude Code - persist context across sessions",
|
||||
"keywords": [
|
||||
"claude",
|
||||
@@ -25,32 +25,23 @@
|
||||
"bugs": {
|
||||
"url": "https://github.com/thedotmack/claude-mem/issues"
|
||||
},
|
||||
"publishConfig": {
|
||||
"access": "public",
|
||||
"registry": "https://registry.npmjs.org/"
|
||||
},
|
||||
"type": "module",
|
||||
"engines": {
|
||||
"node": ">=18.0.0"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "node scripts/build-hooks.js",
|
||||
"build:hooks": "node scripts/build-hooks.js",
|
||||
"release": "node scripts/publish.js",
|
||||
"prepublishOnly": "npm run build",
|
||||
"test": "node --test tests/",
|
||||
"test:parser": "npx tsx src/sdk/parser.test.ts",
|
||||
"test:context": "echo '{\"session_id\":\"test-'$(date +%s)'\",\"cwd\":\"'$(pwd)'\",\"source\":\"startup\"}' | node plugin/scripts/context-hook.js 2>/dev/null",
|
||||
"test:context:verbose": "echo '{\"session_id\":\"test-'$(date +%s)'\",\"cwd\":\"'$(pwd)'\",\"source\":\"startup\"}' | node plugin/scripts/context-hook.js",
|
||||
"import:xml": "tsx src/bin/import-xml-observations.ts",
|
||||
"cleanup:duplicates": "tsx src/bin/cleanup-duplicates.ts",
|
||||
"worker:start": "pm2 start ecosystem.config.cjs",
|
||||
"worker:stop": "pm2 stop claude-mem-worker",
|
||||
"worker:restart": "pm2 restart claude-mem-worker",
|
||||
"worker:logs": "pm2 logs claude-mem-worker",
|
||||
"worker:status": "pm2 status claude-mem-worker"
|
||||
"worker:logs": "pm2 logs claude-mem-worker"
|
||||
},
|
||||
"dependencies": {
|
||||
"@anthropic-ai/claude-agent-sdk": "^0.1.23",
|
||||
"@anthropic-ai/claude-agent-sdk": "^0.1.27",
|
||||
"@modelcontextprotocol/sdk": "^1.20.1",
|
||||
"better-sqlite3": "^11.0.0",
|
||||
"express": "^4.18.2",
|
||||
@@ -66,14 +57,5 @@
|
||||
"esbuild": "^0.20.0",
|
||||
"tsx": "^4.20.6",
|
||||
"typescript": "^5.3.0"
|
||||
},
|
||||
"files": [
|
||||
"plugin",
|
||||
"src",
|
||||
"scripts",
|
||||
"docs",
|
||||
"ecosystem.config.cjs",
|
||||
"LICENSE",
|
||||
"README.md"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "claude-mem",
|
||||
"version": "4.2.3",
|
||||
"version": "4.3.0",
|
||||
"description": "Persistent memory system for Claude Code - seamlessly preserve context across sessions",
|
||||
"author": {
|
||||
"name": "Alex Newman"
|
||||
|
||||
@@ -223,7 +223,7 @@ ${e.stack}`:e.message;if(Array.isArray(e))return`[${e.length} items]`;let s=Obje
|
||||
FROM observations
|
||||
WHERE sdk_session_id = ?
|
||||
`).all(e),r=new Set,n=new Set;for(let i of t){if(i.files_read)try{let a=JSON.parse(i.files_read);Array.isArray(a)&&a.forEach(d=>r.add(d))}catch{}if(i.files_modified)try{let a=JSON.parse(i.files_modified);Array.isArray(a)&&a.forEach(d=>n.add(d))}catch{}}return{filesRead:Array.from(r),filesModified:Array.from(n)}}getSessionById(e){return this.db.prepare(`
|
||||
SELECT id, sdk_session_id, project, user_prompt
|
||||
SELECT id, claude_session_id, sdk_session_id, project, user_prompt
|
||||
FROM sdk_sessions
|
||||
WHERE id = ?
|
||||
LIMIT 1
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#!/usr/bin/env node
|
||||
import C from"path";import q from"better-sqlite3";import{join as E,dirname as W,basename as z}from"path";import{homedir as U}from"os";import{existsSync as te,mkdirSync as j}from"fs";import{fileURLToPath as H}from"url";function B(){return typeof __dirname<"u"?__dirname:W(H(import.meta.url))}var Y=B(),u=process.env.CLAUDE_MEM_DATA_DIR||E(U(),".claude-mem"),O=process.env.CLAUDE_CONFIG_DIR||E(U(),".claude"),ne=E(u,"archives"),ie=E(u,"logs"),oe=E(u,"trash"),ae=E(u,"backups"),de=E(u,"settings.json"),w=E(u,"claude-mem.db"),pe=E(O,"settings.json"),ce=E(O,"commands"),Ee=E(O,"CLAUDE.md");function $(p){j(p,{recursive:!0})}function M(){return E(Y,"..","..")}var L=(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))(L||{}),A=class{level;useColor;constructor(){let e=process.env.CLAUDE_MEM_LOG_LEVEL?.toUpperCase()||"INFO";this.level=L[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),n=L[e].padEnd(5),c=s.padEnd(6),_="";r?.correlationId?_=`[${r.correlationId}] `:r?.sessionId&&(_=`[session-${r.sessionId}] `);let a="";i!=null&&(this.level===0&&typeof i=="object"?a=`
|
||||
`+JSON.stringify(i,null,2):a=" "+this.formatData(i));let l="";if(r){let{sessionId:G,sdkSessionId:k,correlationId:R,...T}=r;Object.keys(T).length>0&&(l=` {${Object.entries(T).map(([g,b])=>`${g}=${b}`).join(", ")}}`)}let f=`[${d}] [${n}] [${c}] ${_}${t}${l}${a}`;e===3?console.error(f):console.log(f)}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`})}},X=new A;var N=class{db;constructor(){$(u),this.db=new q(w),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(`
|
||||
import G from"path";import ae from"better-sqlite3";import{join as T,dirname as te,basename as ge}from"path";import{homedir as Y}from"os";import{existsSync as Ne,mkdirSync as re}from"fs";import{fileURLToPath as ne}from"url";function ie(){return typeof __dirname<"u"?__dirname:te(ne(import.meta.url))}var oe=ie(),I=process.env.CLAUDE_MEM_DATA_DIR||T(Y(),".claude-mem"),U=process.env.CLAUDE_CONFIG_DIR||T(Y(),".claude"),Ie=T(I,"archives"),Oe=T(I,"logs"),Le=T(I,"trash"),ve=T(I,"backups"),Ae=T(I,"settings.json"),K=T(I,"claude-mem.db"),ye=T(U,"settings.json"),De=T(U,"commands"),ke=T(U,"CLAUDE.md");function V(a){re(a,{recursive:!0})}function q(){return T(oe,"..","..")}var $=(o=>(o[o.DEBUG=0]="DEBUG",o[o.INFO=1]="INFO",o[o.WARN=2]="WARN",o[o.ERROR=3]="ERROR",o[o.SILENT=4]="SILENT",o))($||{}),M=class{level;useColor;constructor(){let e=process.env.CLAUDE_MEM_LOG_LEVEL?.toUpperCase()||"INFO";this.level=$[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,o){if(e<this.level)return;let c=new Date().toISOString().replace("T"," ").substring(0,23),p=$[e].padEnd(5),u=s.padEnd(6),O="";r?.correlationId?O=`[${r.correlationId}] `:r?.sessionId&&(O=`[session-${r.sessionId}] `);let b="";o!=null&&(this.level===0&&typeof o=="object"?b=`
|
||||
`+JSON.stringify(o,null,2):b=" "+this.formatData(o));let n="";if(r){let{sessionId:h,sdkSessionId:C,correlationId:L,...D}=r;Object.keys(D).length>0&&(n=` {${Object.entries(D).map(([d,l])=>`${d}=${l}`).join(", ")}}`)}let N=`[${c}] [${p}] [${u}] ${O}${t}${n}${b}`;e===3?console.error(N):console.log(N)}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`})}},J=new M;var k=class{db;constructor(){V(I),this.db=new ae(K),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 (
|
||||
id INTEGER PRIMARY KEY,
|
||||
version INTEGER UNIQUE NOT NULL,
|
||||
@@ -63,7 +63,7 @@ ${e.stack}`:e.message;if(Array.isArray(e))return`[${e.length} items]`;let s=Obje
|
||||
CREATE INDEX IF NOT EXISTS idx_session_summaries_sdk_session ON session_summaries(sdk_session_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_session_summaries_project ON session_summaries(project);
|
||||
CREATE INDEX IF NOT EXISTS idx_session_summaries_created ON session_summaries(created_at_epoch DESC);
|
||||
`),this.db.prepare("INSERT INTO schema_versions (version, applied_at) VALUES (?, ?)").run(4,new Date().toISOString()),console.error("[SessionStore] Migration004 applied successfully"))}catch(e){throw console.error("[SessionStore] Schema initialization error:",e.message),e}}ensureWorkerPortColumn(){try{if(this.db.prepare("SELECT version FROM schema_versions WHERE version = ?").get(5))return;this.db.pragma("table_info(sdk_sessions)").some(r=>r.name==="worker_port")||(this.db.exec("ALTER TABLE sdk_sessions ADD COLUMN worker_port INTEGER"),console.error("[SessionStore] Added worker_port column to sdk_sessions table")),this.db.prepare("INSERT OR IGNORE INTO schema_versions (version, applied_at) VALUES (?, ?)").run(5,new Date().toISOString())}catch(e){console.error("[SessionStore] Migration error:",e.message)}}ensurePromptTrackingColumns(){try{if(this.db.prepare("SELECT version FROM schema_versions WHERE version = ?").get(6))return;this.db.pragma("table_info(sdk_sessions)").some(c=>c.name==="prompt_counter")||(this.db.exec("ALTER TABLE sdk_sessions ADD COLUMN prompt_counter INTEGER DEFAULT 0"),console.error("[SessionStore] Added prompt_counter column to sdk_sessions table")),this.db.pragma("table_info(observations)").some(c=>c.name==="prompt_number")||(this.db.exec("ALTER TABLE observations ADD COLUMN prompt_number INTEGER"),console.error("[SessionStore] Added prompt_number column to observations table")),this.db.pragma("table_info(session_summaries)").some(c=>c.name==="prompt_number")||(this.db.exec("ALTER TABLE session_summaries ADD COLUMN prompt_number INTEGER"),console.error("[SessionStore] Added prompt_number column to session_summaries table")),this.db.prepare("INSERT OR IGNORE INTO schema_versions (version, applied_at) VALUES (?, ?)").run(6,new Date().toISOString())}catch(e){console.error("[SessionStore] Prompt tracking migration error:",e.message)}}removeSessionSummariesUniqueConstraint(){try{if(this.db.prepare("SELECT version FROM schema_versions WHERE version = ?").get(7))return;if(!this.db.pragma("index_list(session_summaries)").some(r=>r.unique===1)){this.db.prepare("INSERT OR IGNORE INTO schema_versions (version, applied_at) VALUES (?, ?)").run(7,new Date().toISOString());return}console.error("[SessionStore] Removing UNIQUE constraint from session_summaries.sdk_session_id..."),this.db.exec("BEGIN TRANSACTION");try{this.db.exec(`
|
||||
`),this.db.prepare("INSERT INTO schema_versions (version, applied_at) VALUES (?, ?)").run(4,new Date().toISOString()),console.error("[SessionStore] Migration004 applied successfully"))}catch(e){throw console.error("[SessionStore] Schema initialization error:",e.message),e}}ensureWorkerPortColumn(){try{if(this.db.prepare("SELECT version FROM schema_versions WHERE version = ?").get(5))return;this.db.pragma("table_info(sdk_sessions)").some(r=>r.name==="worker_port")||(this.db.exec("ALTER TABLE sdk_sessions ADD COLUMN worker_port INTEGER"),console.error("[SessionStore] Added worker_port column to sdk_sessions table")),this.db.prepare("INSERT OR IGNORE INTO schema_versions (version, applied_at) VALUES (?, ?)").run(5,new Date().toISOString())}catch(e){console.error("[SessionStore] Migration error:",e.message)}}ensurePromptTrackingColumns(){try{if(this.db.prepare("SELECT version FROM schema_versions WHERE version = ?").get(6))return;this.db.pragma("table_info(sdk_sessions)").some(u=>u.name==="prompt_counter")||(this.db.exec("ALTER TABLE sdk_sessions ADD COLUMN prompt_counter INTEGER DEFAULT 0"),console.error("[SessionStore] Added prompt_counter column to sdk_sessions table")),this.db.pragma("table_info(observations)").some(u=>u.name==="prompt_number")||(this.db.exec("ALTER TABLE observations ADD COLUMN prompt_number INTEGER"),console.error("[SessionStore] Added prompt_number column to observations table")),this.db.pragma("table_info(session_summaries)").some(u=>u.name==="prompt_number")||(this.db.exec("ALTER TABLE session_summaries ADD COLUMN prompt_number INTEGER"),console.error("[SessionStore] Added prompt_number column to session_summaries table")),this.db.prepare("INSERT OR IGNORE INTO schema_versions (version, applied_at) VALUES (?, ?)").run(6,new Date().toISOString())}catch(e){console.error("[SessionStore] Prompt tracking migration error:",e.message)}}removeSessionSummariesUniqueConstraint(){try{if(this.db.prepare("SELECT version FROM schema_versions WHERE version = ?").get(7))return;if(!this.db.pragma("index_list(session_summaries)").some(r=>r.unique===1)){this.db.prepare("INSERT OR IGNORE INTO schema_versions (version, applied_at) VALUES (?, ?)").run(7,new Date().toISOString());return}console.error("[SessionStore] Removing UNIQUE constraint from session_summaries.sdk_session_id..."),this.db.exec("BEGIN TRANSACTION");try{this.db.exec(`
|
||||
CREATE TABLE session_summaries_new (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
sdk_session_id TEXT NOT NULL,
|
||||
@@ -222,8 +222,8 @@ ${e.stack}`:e.message;if(Array.isArray(e))return`[${e.length} items]`;let s=Obje
|
||||
SELECT files_read, files_modified
|
||||
FROM observations
|
||||
WHERE sdk_session_id = ?
|
||||
`).all(e),r=new Set,i=new Set;for(let d of t){if(d.files_read)try{let n=JSON.parse(d.files_read);Array.isArray(n)&&n.forEach(c=>r.add(c))}catch{}if(d.files_modified)try{let n=JSON.parse(d.files_modified);Array.isArray(n)&&n.forEach(c=>i.add(c))}catch{}}return{filesRead:Array.from(r),filesModified:Array.from(i)}}getSessionById(e){return this.db.prepare(`
|
||||
SELECT id, sdk_session_id, project, user_prompt
|
||||
`).all(e),r=new Set,o=new Set;for(let c of t){if(c.files_read)try{let p=JSON.parse(c.files_read);Array.isArray(p)&&p.forEach(u=>r.add(u))}catch{}if(c.files_modified)try{let p=JSON.parse(c.files_modified);Array.isArray(p)&&p.forEach(u=>o.add(u))}catch{}}return{filesRead:Array.from(r),filesModified:Array.from(o)}}getSessionById(e){return this.db.prepare(`
|
||||
SELECT id, claude_session_id, sdk_session_id, project, user_prompt
|
||||
FROM sdk_sessions
|
||||
WHERE id = ?
|
||||
LIMIT 1
|
||||
@@ -249,17 +249,17 @@ ${e.stack}`:e.message;if(Array.isArray(e))return`[${e.length} items]`;let s=Obje
|
||||
SELECT prompt_counter FROM sdk_sessions WHERE id = ?
|
||||
`).get(e)?.prompt_counter||1}getPromptCounter(e){return this.db.prepare(`
|
||||
SELECT prompt_counter FROM sdk_sessions WHERE id = ?
|
||||
`).get(e)?.prompt_counter||0}createSDKSession(e,s,t){let r=new Date,i=r.getTime(),n=this.db.prepare(`
|
||||
`).get(e)?.prompt_counter||0}createSDKSession(e,s,t){let r=new Date,o=r.getTime(),p=this.db.prepare(`
|
||||
INSERT OR IGNORE INTO sdk_sessions
|
||||
(claude_session_id, sdk_session_id, project, user_prompt, started_at, started_at_epoch, status)
|
||||
VALUES (?, ?, ?, ?, ?, ?, 'active')
|
||||
`).run(e,e,s,t,r.toISOString(),i);return n.lastInsertRowid===0||n.changes===0?this.db.prepare(`
|
||||
`).run(e,e,s,t,r.toISOString(),o);return p.lastInsertRowid===0||p.changes===0?this.db.prepare(`
|
||||
SELECT id FROM sdk_sessions WHERE claude_session_id = ? LIMIT 1
|
||||
`).get(e).id:n.lastInsertRowid}updateSDKSessionId(e,s){return this.db.prepare(`
|
||||
`).get(e).id:p.lastInsertRowid}updateSDKSessionId(e,s){return this.db.prepare(`
|
||||
UPDATE sdk_sessions
|
||||
SET sdk_session_id = ?
|
||||
WHERE id = ? AND sdk_session_id IS NULL
|
||||
`).run(s,e).changes===0?(X.debug("DB","sdk_session_id already set, skipping update",{sessionId:e,sdkSessionId:s}),!1):!0}setWorkerPort(e,s){this.db.prepare(`
|
||||
`).run(s,e).changes===0?(J.debug("DB","sdk_session_id already set, skipping update",{sessionId:e,sdkSessionId:s}),!1):!0}setWorkerPort(e,s){this.db.prepare(`
|
||||
UPDATE sdk_sessions
|
||||
SET worker_port = ?
|
||||
WHERE id = ?
|
||||
@@ -268,33 +268,33 @@ ${e.stack}`:e.message;if(Array.isArray(e))return`[${e.length} items]`;let s=Obje
|
||||
FROM sdk_sessions
|
||||
WHERE id = ?
|
||||
LIMIT 1
|
||||
`).get(e)?.worker_port||null}saveUserPrompt(e,s,t){let r=new Date,i=r.getTime();return this.db.prepare(`
|
||||
`).get(e)?.worker_port||null}saveUserPrompt(e,s,t){let r=new Date,o=r.getTime();return this.db.prepare(`
|
||||
INSERT INTO user_prompts
|
||||
(claude_session_id, prompt_number, prompt_text, created_at, created_at_epoch)
|
||||
VALUES (?, ?, ?, ?, ?)
|
||||
`).run(e,s,t,r.toISOString(),i).lastInsertRowid}storeObservation(e,s,t,r){let i=new Date,d=i.getTime();this.db.prepare(`
|
||||
`).run(e,s,t,r.toISOString(),o).lastInsertRowid}storeObservation(e,s,t,r){let o=new Date,c=o.getTime();this.db.prepare(`
|
||||
SELECT id FROM sdk_sessions WHERE sdk_session_id = ?
|
||||
`).get(e)||(this.db.prepare(`
|
||||
INSERT INTO sdk_sessions
|
||||
(claude_session_id, sdk_session_id, project, started_at, started_at_epoch, status)
|
||||
VALUES (?, ?, ?, ?, ?, 'active')
|
||||
`).run(e,e,s,i.toISOString(),d),console.error(`[SessionStore] Auto-created session record for session_id: ${e}`)),this.db.prepare(`
|
||||
`).run(e,e,s,o.toISOString(),c),console.error(`[SessionStore] Auto-created session record for session_id: ${e}`)),this.db.prepare(`
|
||||
INSERT INTO observations
|
||||
(sdk_session_id, project, type, title, subtitle, facts, narrative, concepts,
|
||||
files_read, files_modified, prompt_number, created_at, created_at_epoch)
|
||||
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)}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,o.toISOString(),c)}storeSummary(e,s,t,r){let o=new Date,c=o.getTime();this.db.prepare(`
|
||||
SELECT id FROM sdk_sessions WHERE sdk_session_id = ?
|
||||
`).get(e)||(this.db.prepare(`
|
||||
INSERT INTO sdk_sessions
|
||||
(claude_session_id, sdk_session_id, project, started_at, started_at_epoch, status)
|
||||
VALUES (?, ?, ?, ?, ?, 'active')
|
||||
`).run(e,e,s,i.toISOString(),d),console.error(`[SessionStore] Auto-created session record for session_id: ${e}`)),this.db.prepare(`
|
||||
`).run(e,e,s,o.toISOString(),c),console.error(`[SessionStore] Auto-created session record for session_id: ${e}`)),this.db.prepare(`
|
||||
INSERT INTO session_summaries
|
||||
(sdk_session_id, project, request, investigated, learned, completed,
|
||||
next_steps, notes, prompt_number, created_at, created_at_epoch)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
||||
`).run(e,s,t.request,t.investigated,t.learned,t.completed,t.next_steps,t.notes,r||null,i.toISOString(),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,o.toISOString(),c)}markSessionCompleted(e){let s=new Date,t=s.getTime();this.db.prepare(`
|
||||
UPDATE sdk_sessions
|
||||
SET status = 'completed', completed_at = ?, completed_at_epoch = ?
|
||||
WHERE id = ?
|
||||
@@ -306,25 +306,26 @@ ${e.stack}`:e.message;if(Array.isArray(e))return`[${e.length} items]`;let s=Obje
|
||||
UPDATE sdk_sessions
|
||||
SET status = 'failed', completed_at = ?, completed_at_epoch = ?
|
||||
WHERE status = 'active'
|
||||
`).run(e.toISOString(),s).changes}close(){this.db.close()}};import v from"path";import{existsSync as y}from"fs";import{spawn as K}from"child_process";var V=parseInt(process.env.CLAUDE_MEM_WORKER_PORT||"37777",10),J=`http://127.0.0.1:${V}/health`;async function F(){try{return(await fetch(J,{signal:AbortSignal.timeout(500)})).ok}catch{return!1}}async function P(){try{if(await F())return!0;console.error("[claude-mem] Worker not responding, starting...");let p=M(),e=v.join(p,"plugin","scripts","worker-service.cjs");if(!y(e))return console.error(`[claude-mem] Worker service not found at ${e}`),!1;let s=v.join(p,"ecosystem.config.cjs"),t=v.join(p,"node_modules",".bin","pm2");if(!y(t))throw new Error(`PM2 binary not found at ${t}. This is a bundled dependency - try running: npm install`);if(!y(s))throw new Error(`PM2 ecosystem config not found at ${s}. Plugin installation may be corrupted.`);let r=K(t,["start",s],{detached:!0,stdio:"ignore",cwd:p});r.on("error",i=>{throw new Error(`Failed to spawn PM2: ${i.message}`)}),r.unref(),console.error("[claude-mem] Worker started with PM2");for(let i=0;i<3;i++)if(await new Promise(d=>setTimeout(d,500)),await F())return console.error("[claude-mem] Worker is healthy"),!0;return console.error("[claude-mem] Worker failed to become healthy after startup"),!1}catch(p){return console.error(`[claude-mem] Failed to start worker: ${p.message}`),!1}}var 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"};function D(p,e=!1,s=!1){P();let t=p?.cwd??process.cwd(),r=t?C.basename(t):"unknown-project",i=new N;try{let d=i.db.prepare(`
|
||||
SELECT * FROM (
|
||||
SELECT sdk_session_id, request, learned, completed, next_steps, created_at, created_at_epoch
|
||||
FROM session_summaries
|
||||
WHERE project = ?
|
||||
ORDER BY created_at_epoch DESC
|
||||
LIMIT 10
|
||||
)
|
||||
ORDER BY created_at_epoch ASC
|
||||
`).all(r);if(d.length===0)return e?`
|
||||
${o.bright}${o.cyan}\u{1F4DD} [${r}] recent context${o.reset}
|
||||
${o.gray}${"\u2500".repeat(60)}${o.reset}
|
||||
`).run(e.toISOString(),s).changes}close(){this.db.close()}};import X from"path";import{existsSync as F}from"fs";import{spawn as de}from"child_process";var ce=parseInt(process.env.CLAUDE_MEM_WORKER_PORT||"37777",10),pe=`http://127.0.0.1:${ce}/health`;async function Q(){try{return(await fetch(pe,{signal:AbortSignal.timeout(500)})).ok}catch{return!1}}async function z(){try{if(await Q())return!0;console.error("[claude-mem] Worker not responding, starting...");let a=q(),e=X.join(a,"plugin","scripts","worker-service.cjs");if(!F(e))return console.error(`[claude-mem] Worker service not found at ${e}`),!1;let s=X.join(a,"ecosystem.config.cjs"),t=X.join(a,"node_modules",".bin","pm2");if(!F(t))throw new Error(`PM2 binary not found at ${t}. This is a bundled dependency - try running: npm install`);if(!F(s))throw new Error(`PM2 ecosystem config not found at ${s}. Plugin installation may be corrupted.`);let r=de(t,["start",s],{detached:!0,stdio:"ignore",cwd:a});r.on("error",o=>{throw new Error(`Failed to spawn PM2: ${o.message}`)}),r.unref(),console.error("[claude-mem] Worker started with PM2");for(let o=0;o<3;o++)if(await new Promise(c=>setTimeout(c,500)),await Q())return console.error("[claude-mem] Worker is healthy"),!0;return console.error("[claude-mem] Worker failed to become healthy after startup"),!1}catch(a){return console.error(`[claude-mem] Failed to start worker: ${a.message}`),!1}}var i={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 P(a){if(!a)return[];try{let e=JSON.parse(a);return Array.isArray(e)?e:[]}catch{return[]}}function ue(a){return new Date(a).toLocaleString("en-US",{month:"short",day:"numeric",hour:"numeric",minute:"2-digit",hour12:!0})}function me(a){return new Date(a).toLocaleString("en-US",{hour:"numeric",minute:"2-digit",hour12:!0})}function le(a){return new Date(a).toLocaleString("en-US",{month:"short",day:"numeric",year:"numeric"})}function _e(a){return a?Math.ceil(a.length/4):0}function Ee(a,e){try{return G.isAbsolute(a)?G.relative(e,a):a}catch{return a}}function Te(a,e){if(e.length===0)return[];let s=e.map(()=>"?").join(",");return a.db.prepare(`
|
||||
SELECT
|
||||
id, sdk_session_id, type, title, subtitle, narrative,
|
||||
facts, concepts, files_read, files_modified,
|
||||
created_at, created_at_epoch
|
||||
FROM observations
|
||||
WHERE sdk_session_id IN (${s})
|
||||
ORDER BY created_at_epoch DESC
|
||||
`).all(...e)}function W(a,e=!1,s=!1){z();let t=a?.cwd??process.cwd(),r=t?G.basename(t):"unknown-project",o=new k;try{let c=o.db.prepare(`
|
||||
SELECT id, sdk_session_id, request, completed, next_steps, created_at, created_at_epoch
|
||||
FROM session_summaries
|
||||
WHERE project = ?
|
||||
ORDER BY created_at_epoch DESC
|
||||
LIMIT 4
|
||||
`).all(r);if(c.length===0)return e?`
|
||||
${i.bright}${i.cyan}\u{1F4DD} [${r}] recent context${i.reset}
|
||||
${i.gray}${"\u2500".repeat(60)}${i.reset}
|
||||
|
||||
${o.dim}No previous summaries found for this project yet.${o.reset}
|
||||
${i.dim}No previous sessions found for this project yet.${i.reset}
|
||||
`:`# [${r}] recent context
|
||||
|
||||
No previous summaries found for this project yet.`;let n=[];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(`# [${r}] recent context`),n.push(""));let c=!0;for(let _=0;_<d.length;_++){let a=d[_],l=d.length-1-_,f=l===0,G=l>=1&&l<=3,k=l>3;if(c?e&&n.push(""):e?(n.push(`${o.gray}${"\u2500".repeat(60)}${o.reset}`),n.push("")):(n.push("---"),n.push("")),c=!1,k){a.request&&(e?(n.push(`${o.bright}${o.yellow}Request:${o.reset} ${a.request}`),n.push("")):(n.push(`**Request:** ${a.request}`),n.push("")));let T=new Date(a.created_at).toLocaleString();e?n.push(`${o.dim}Date: ${T}${o.reset}`):(n.push(`**Date:** ${T}`),n.push(""));continue}if(a.request&&(e?(n.push(`${o.bright}${o.yellow}Request:${o.reset} ${a.request}`),n.push("")):(n.push(`**Request:** ${a.request}`),n.push(""))),f&&a.learned&&(e?(n.push(`${o.bright}${o.blue}Learned:${o.reset} ${a.learned}`),n.push("")):(n.push(`**Learned:** ${a.learned}`),n.push(""))),a.completed&&(e?(n.push(`${o.bright}${o.green}Completed:${o.reset} ${a.completed}`),n.push("")):(n.push(`**Completed:** ${a.completed}`),n.push(""))),f&&a.next_steps&&(e?(n.push(`${o.bright}${o.magenta}Next Steps:${o.reset} ${a.next_steps}`),n.push("")):(n.push(`**Next Steps:** ${a.next_steps}`),n.push(""))),f){let T=i.db.prepare(`
|
||||
SELECT files_read, files_modified
|
||||
FROM observations
|
||||
WHERE sdk_session_id = ?
|
||||
`).all(a.sdk_session_id),h=new Set,g=new Set,b=m=>{try{return C.isAbsolute(m)?C.relative(t,m):m}catch{return m}};for(let m of T){if(m.files_read)try{let S=JSON.parse(m.files_read);Array.isArray(S)&&S.forEach(I=>h.add(b(I)))}catch{}if(m.files_modified)try{let S=JSON.parse(m.files_modified);Array.isArray(S)&&S.forEach(I=>g.add(b(I)))}catch{}}g.forEach(m=>h.delete(m)),h.size>0&&(e?n.push(`${o.dim}Files Read: ${Array.from(h).join(", ")}${o.reset}`):n.push(`**Files Read:** ${Array.from(h).join(", ")}`)),g.size>0&&(e?n.push(`${o.dim}Files Modified: ${Array.from(g).join(", ")}${o.reset}`):n.push(`**Files Modified:** ${Array.from(g).join(", ")}`))}let R=new Date(a.created_at).toLocaleString();e?n.push(`${o.dim}Date: ${R}${o.reset}`):n.push(`**Date:** ${R}`),e||n.push("")}return e&&(n.push(""),n.push(`${o.gray}${"\u2500".repeat(60)}${o.reset}`)),n.join(`
|
||||
`)}finally{i.close()}}import{stdin as x}from"process";try{let p=process.argv.includes("--index");if(x.isTTY){let e=D(void 0,!0,p);console.log(e),process.exit(0)}else{let e="";x.on("data",s=>e+=s),x.on("end",()=>{let s=e.trim()?JSON.parse(e):void 0,r={hookSpecificOutput:{hookEventName:"SessionStart",additionalContext:D(s,!1,p)}};console.log(JSON.stringify(r)),process.exit(0)})}}catch(p){console.error(`[claude-mem context-hook error: ${p.message}]`),process.exit(0)}
|
||||
No previous sessions found for this project yet.`;let p=c.slice(0,3),u=[...new Set(p.map(N=>N.sdk_session_id))],b=Te(o,u).filter(N=>{let h=P(N.concepts);return h.includes("what-changed")||h.includes("how-it-works")||h.includes("problem-solution")||h.includes("gotcha")||h.includes("discovery")||h.includes("why-it-exists")||h.includes("decision")||h.includes("trade-off")}),n=[];if(e?(n.push(""),n.push(`${i.bright}${i.cyan}\u{1F4DD} [${r}] recent context${i.reset}`),n.push(`${i.gray}${"\u2500".repeat(60)}${i.reset}`),n.push("")):(n.push(`# [${r}] recent context`),n.push("")),b.length>0){e?(n.push(`${i.dim}Legend: \u{1F3AF} session-request | \u{1F534} gotcha | \u{1F7E1} problem-solution | \u{1F535} how-it-works | \u{1F7E2} what-changed | \u{1F7E3} discovery | \u{1F7E0} why-it-exists | \u{1F7E4} decision | \u2696\uFE0F trade-off${i.reset}`),n.push("")):(n.push("**Legend:** \u{1F3AF} session-request | \u{1F534} gotcha | \u{1F7E1} problem-solution | \u{1F535} how-it-works | \u{1F7E2} what-changed | \u{1F7E3} discovery | \u{1F7E0} why-it-exists | \u{1F7E4} decision | \u2696\uFE0F trade-off"),n.push("")),e?(n.push(`${i.dim}\u{1F4A1} Progressive Disclosure: This index shows WHAT exists (titles) and retrieval COST (token counts).${i.reset}`),n.push(`${i.dim} \u2192 Use MCP search tools to fetch full observation details on-demand (Layer 2)${i.reset}`),n.push(`${i.dim} \u2192 Prefer searching observations over re-reading code for past decisions and learnings${i.reset}`),n.push(`${i.dim} \u2192 Critical types (\u{1F534} gotcha, \u{1F7E4} decision, \u2696\uFE0F trade-off) often worth fetching immediately${i.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} gotcha, \u{1F7E4} decision, \u2696\uFE0F trade-off) often worth fetching immediately"),n.push(""));let N=c[0]?.id,h=p.map((d,l)=>{let m=l===0?null:c[l+1];return{...d,displayEpoch:m?m.created_at_epoch:d.created_at_epoch,displayTime:m?m.created_at:d.created_at,isMostRecent:d.id===N}}),C=[...b.map(d=>({type:"observation",data:d})),...h.map(d=>({type:"summary",data:d}))];C.sort((d,l)=>{let m=d.type==="observation"?d.data.created_at_epoch:d.data.displayEpoch,R=l.type==="observation"?l.data.created_at_epoch:l.data.displayEpoch;return m-R});let L=new Map;for(let d of C){let l=d.type==="observation"?d.data.created_at:d.data.displayTime,m=le(l);L.has(m)||L.set(m,[]),L.get(m).push(d)}let D=Array.from(L.entries()).sort((d,l)=>{let m=new Date(d[0]).getTime(),R=new Date(l[0]).getTime();return m-R});for(let[d,l]of D){e?(n.push(`${i.bright}${i.cyan}${d}${i.reset}`),n.push("")):(n.push(`### ${d}`),n.push(""));let m=null,R="",v=!1;for(let x of l)if(x.type==="summary"){v&&(n.push(""),v=!1,m=null,R="");let _=x.data,A=`${_.request||"Session started"} (${ue(_.displayTime)})`,S=_.isMostRecent?"":`claude-mem://session-summary/${_.id}`;if(e){let E=S?`${i.dim}[${S}]${i.reset}`:"";n.push(`\u{1F3AF} ${i.yellow}#S${_.id}${i.reset} ${A} ${E}`)}else{let E=S?` [\u2192](${S})`:"";n.push(`**\u{1F3AF} #S${_.id}** ${A}${E}`)}n.push("")}else{let _=x.data,A=P(_.files_modified),S=A.length>0?Ee(A[0],t):"General";S!==m&&(v&&n.push(""),e?n.push(`${i.dim}${S}${i.reset}`):n.push(`**${S}**`),e||(n.push("| ID | Time | T | Title | Tokens |"),n.push("|----|------|---|-------|--------|")),m=S,v=!0,R="");let E=P(_.concepts),f="\u2022";E.includes("gotcha")?f="\u{1F534}":E.includes("decision")?f="\u{1F7E4}":E.includes("trade-off")?f="\u2696\uFE0F":E.includes("problem-solution")?f="\u{1F7E1}":E.includes("discovery")?f="\u{1F7E3}":E.includes("why-it-exists")?f="\u{1F7E0}":E.includes("how-it-works")?f="\u{1F535}":E.includes("what-changed")&&(f="\u{1F7E2}");let y=me(_.created_at),B=_.title||"Untitled",w=_e(_.narrative),j=y!==R,Z=j?y:"";if(R=y,e){let ee=j?`${i.dim}${y}${i.reset}`:" ".repeat(y.length),se=w>0?`${i.dim}(~${w}t)${i.reset}`:"";n.push(` ${i.dim}#${_.id}${i.reset} ${ee} ${f} ${B} ${se}`)}else n.push(`| #${_.id} | ${Z||"\u2033"} | ${f} | ${B} | ~${w} |`)}v&&n.push("")}let g=c[0];g&&(g.completed||g.next_steps)&&(g.completed&&(e?n.push(`${i.green}Completed:${i.reset} ${g.completed}`):n.push(`**Completed**: ${g.completed}`),n.push("")),g.next_steps&&(e?n.push(`${i.magenta}Next Steps:${i.reset} ${g.next_steps}`):n.push(`**Next Steps**: ${g.next_steps}`),n.push(""))),e?n.push(`${i.dim}Use claude-mem MCP search to access records with the given ID${i.reset}`):n.push("*Use claude-mem MCP search to access records with the given ID*"),n.push("")}return e&&(n.push(`${i.gray}${"\u2500".repeat(60)}${i.reset}`),n.push("")),n.join(`
|
||||
`)}finally{o.close()}}import{stdin as H}from"process";try{let a=process.argv.includes("--index");if(H.isTTY){let e=W(void 0,!0,a);console.log(e),process.exit(0)}else{let e="";H.on("data",s=>e+=s),H.on("end",()=>{let s=e.trim()?JSON.parse(e):void 0,r={hookSpecificOutput:{hookEventName:"SessionStart",additionalContext:W(s,!1,a)}};console.log(JSON.stringify(r)),process.exit(0)})}}catch(a){console.error(`[claude-mem context-hook error: ${a.message}]`),process.exit(0)}
|
||||
|
||||
@@ -223,7 +223,7 @@ ${e.stack}`:e.message;if(Array.isArray(e))return`[${e.length} items]`;let s=Obje
|
||||
FROM observations
|
||||
WHERE sdk_session_id = ?
|
||||
`).all(e),r=new Set,o=new Set;for(let i of t){if(i.files_read)try{let a=JSON.parse(i.files_read);Array.isArray(a)&&a.forEach(d=>r.add(d))}catch{}if(i.files_modified)try{let a=JSON.parse(i.files_modified);Array.isArray(a)&&a.forEach(d=>o.add(d))}catch{}}return{filesRead:Array.from(r),filesModified:Array.from(o)}}getSessionById(e){return this.db.prepare(`
|
||||
SELECT id, sdk_session_id, project, user_prompt
|
||||
SELECT id, claude_session_id, sdk_session_id, project, user_prompt
|
||||
FROM sdk_sessions
|
||||
WHERE id = ?
|
||||
LIMIT 1
|
||||
|
||||
@@ -223,7 +223,7 @@ ${e.stack}`:e.message;if(Array.isArray(e))return`[${e.length} items]`;let s=Obje
|
||||
FROM observations
|
||||
WHERE sdk_session_id = ?
|
||||
`).all(e),r=new Set,o=new Set;for(let i of t){if(i.files_read)try{let a=JSON.parse(i.files_read);Array.isArray(a)&&a.forEach(d=>r.add(d))}catch{}if(i.files_modified)try{let a=JSON.parse(i.files_modified);Array.isArray(a)&&a.forEach(d=>o.add(d))}catch{}}return{filesRead:Array.from(r),filesModified:Array.from(o)}}getSessionById(e){return this.db.prepare(`
|
||||
SELECT id, sdk_session_id, project, user_prompt
|
||||
SELECT id, claude_session_id, sdk_session_id, project, user_prompt
|
||||
FROM sdk_sessions
|
||||
WHERE id = ?
|
||||
LIMIT 1
|
||||
|
||||
@@ -365,7 +365,7 @@ ${e.stack}`:e.message;if(Array.isArray(e))return`[${e.length} items]`;let t=Obje
|
||||
FROM observations
|
||||
WHERE sdk_session_id = ?
|
||||
`).all(e),r=new Set,n=new Set;for(let i of s){if(i.files_read)try{let o=JSON.parse(i.files_read);Array.isArray(o)&&o.forEach(c=>r.add(c))}catch{}if(i.files_modified)try{let o=JSON.parse(i.files_modified);Array.isArray(o)&&o.forEach(c=>n.add(c))}catch{}}return{filesRead:Array.from(r),filesModified:Array.from(n)}}getSessionById(e){return this.db.prepare(`
|
||||
SELECT id, sdk_session_id, project, user_prompt
|
||||
SELECT id, claude_session_id, sdk_session_id, project, user_prompt
|
||||
FROM sdk_sessions
|
||||
WHERE id = ?
|
||||
LIMIT 1
|
||||
|
||||
@@ -223,7 +223,7 @@ ${e.stack}`:e.message;if(Array.isArray(e))return`[${e.length} items]`;let s=Obje
|
||||
FROM observations
|
||||
WHERE sdk_session_id = ?
|
||||
`).all(e),r=new Set,o=new Set;for(let i of t){if(i.files_read)try{let a=JSON.parse(i.files_read);Array.isArray(a)&&a.forEach(d=>r.add(d))}catch{}if(i.files_modified)try{let a=JSON.parse(i.files_modified);Array.isArray(a)&&a.forEach(d=>o.add(d))}catch{}}return{filesRead:Array.from(r),filesModified:Array.from(o)}}getSessionById(e){return this.db.prepare(`
|
||||
SELECT id, sdk_session_id, project, user_prompt
|
||||
SELECT id, claude_session_id, sdk_session_id, project, user_prompt
|
||||
FROM sdk_sessions
|
||||
WHERE id = ?
|
||||
LIMIT 1
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -61,7 +61,7 @@ function buildTimestampMap(): TimestampMapping {
|
||||
const data = JSON.parse(line);
|
||||
const timestamp = data.timestamp;
|
||||
const sessionId = data.sessionId;
|
||||
const project = data.cwd || '/Users/alexnewman/Scripts/claude-mem';
|
||||
const project = data.cwd;
|
||||
|
||||
if (timestamp && sessionId) {
|
||||
// Round timestamp to second for matching with XML timestamps
|
||||
|
||||
+380
-194
@@ -22,8 +22,135 @@ const colors = {
|
||||
blue: '\x1b[34m',
|
||||
magenta: '\x1b[35m',
|
||||
gray: '\x1b[90m',
|
||||
red: '\x1b[31m',
|
||||
};
|
||||
|
||||
interface Observation {
|
||||
id: number;
|
||||
sdk_session_id: string;
|
||||
type: string;
|
||||
title: string | null;
|
||||
subtitle: string | null;
|
||||
narrative: string | null;
|
||||
facts: string | null;
|
||||
concepts: string | null;
|
||||
files_read: string | null;
|
||||
files_modified: string | null;
|
||||
created_at: string;
|
||||
created_at_epoch: number;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Helper: Parse JSON array safely
|
||||
*/
|
||||
function parseJsonArray(json: string | null): string[] {
|
||||
if (!json) return [];
|
||||
try {
|
||||
const parsed = JSON.parse(json);
|
||||
return Array.isArray(parsed) ? parsed : [];
|
||||
} catch {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper: Format date with time
|
||||
*/
|
||||
function formatDateTime(dateStr: string): string {
|
||||
const date = new Date(dateStr);
|
||||
return date.toLocaleString('en-US', {
|
||||
month: 'short',
|
||||
day: 'numeric',
|
||||
hour: 'numeric',
|
||||
minute: '2-digit',
|
||||
hour12: true
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper: Format just time (no date)
|
||||
*/
|
||||
function formatTime(dateStr: string): string {
|
||||
const date = new Date(dateStr);
|
||||
return date.toLocaleString('en-US', {
|
||||
hour: 'numeric',
|
||||
minute: '2-digit',
|
||||
hour12: true
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper: Format just date
|
||||
*/
|
||||
function formatDate(dateStr: string): string {
|
||||
const date = new Date(dateStr);
|
||||
return date.toLocaleString('en-US', {
|
||||
month: 'short',
|
||||
day: 'numeric',
|
||||
year: 'numeric'
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper: Estimate token count for text
|
||||
*/
|
||||
function estimateTokens(text: string | null): number {
|
||||
if (!text) return 0;
|
||||
// Rough estimate: ~4 characters per token
|
||||
return Math.ceil(text.length / 4);
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper: Convert absolute paths to relative paths
|
||||
*/
|
||||
function toRelativePath(filePath: string, cwd: string): string {
|
||||
try {
|
||||
if (path.isAbsolute(filePath)) {
|
||||
return path.relative(cwd, filePath);
|
||||
}
|
||||
return filePath;
|
||||
} catch {
|
||||
return filePath;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper: Get recent session IDs for a project
|
||||
*/
|
||||
function getRecentSessionIds(db: SessionStore, project: string, limit: number = 3): string[] {
|
||||
const sessions = db.db.prepare(`
|
||||
SELECT sdk_session_id
|
||||
FROM sdk_sessions
|
||||
WHERE project = ? AND sdk_session_id IS NOT NULL
|
||||
ORDER BY started_at_epoch DESC
|
||||
LIMIT ?
|
||||
`).all(project, limit) as Array<{ sdk_session_id: string }>;
|
||||
|
||||
return sessions.map(s => s.sdk_session_id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper: Get all observations for given sessions
|
||||
*/
|
||||
function getObservations(db: SessionStore, sessionIds: string[]): Observation[] {
|
||||
if (sessionIds.length === 0) return [];
|
||||
|
||||
const placeholders = sessionIds.map(() => '?').join(',');
|
||||
const observations = db.db.prepare(`
|
||||
SELECT
|
||||
id, sdk_session_id, type, title, subtitle, narrative,
|
||||
facts, concepts, files_read, files_modified,
|
||||
created_at, created_at_epoch
|
||||
FROM observations
|
||||
WHERE sdk_session_id IN (${placeholders})
|
||||
ORDER BY created_at_epoch DESC
|
||||
`).all(...sessionIds) as Observation[];
|
||||
|
||||
return observations;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Context Hook - SessionStart
|
||||
* Shows user what happened in recent sessions
|
||||
@@ -36,224 +163,283 @@ export function contextHook(input?: SessionStartInput, useColors: boolean = fals
|
||||
const db = new SessionStore();
|
||||
|
||||
try {
|
||||
// Get the most recent summaries, then display them chronologically (oldest to newest, like a chat)
|
||||
const summaries = db.db.prepare(`
|
||||
SELECT * FROM (
|
||||
SELECT sdk_session_id, request, learned, completed, next_steps, created_at, created_at_epoch
|
||||
FROM session_summaries
|
||||
WHERE project = ?
|
||||
ORDER BY created_at_epoch DESC
|
||||
LIMIT 10
|
||||
)
|
||||
ORDER BY created_at_epoch ASC
|
||||
`).all(project) as Array<{
|
||||
sdk_session_id: string;
|
||||
request: string | null;
|
||||
learned: string | null;
|
||||
completed: string | null;
|
||||
next_steps: string | null;
|
||||
created_at: string;
|
||||
}>;
|
||||
// Get last 4 summaries (use 4th for offset calculation)
|
||||
const recentSummaries = db.db.prepare(`
|
||||
SELECT id, sdk_session_id, request, completed, next_steps, created_at, created_at_epoch
|
||||
FROM session_summaries
|
||||
WHERE project = ?
|
||||
ORDER BY created_at_epoch DESC
|
||||
LIMIT 4
|
||||
`).all(project) 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 }>;
|
||||
|
||||
if (summaries.length === 0) {
|
||||
if (recentSummaries.length === 0) {
|
||||
if (useColors) {
|
||||
return `\n${colors.bright}${colors.cyan}📝 [${project}] recent context${colors.reset}\n${colors.gray}${'─'.repeat(60)}${colors.reset}\n\n${colors.dim}No previous summaries found for this project yet.${colors.reset}\n`;
|
||||
return `\n${colors.bright}${colors.cyan}📝 [${project}] recent context${colors.reset}\n${colors.gray}${'─'.repeat(60)}${colors.reset}\n\n${colors.dim}No previous sessions found for this project yet.${colors.reset}\n`;
|
||||
}
|
||||
return `# [${project}] recent context\n\nNo previous summaries found for this project yet.`;
|
||||
return `# [${project}] recent context\n\nNo previous sessions found for this project yet.`;
|
||||
}
|
||||
|
||||
// Extract unique session IDs from first 3 summaries
|
||||
const displaySummaries = recentSummaries.slice(0, 3);
|
||||
const sessionIds = [...new Set(displaySummaries.map(s => s.sdk_session_id))];
|
||||
|
||||
// Get all observations from these sessions
|
||||
const observations = getObservations(db, sessionIds);
|
||||
|
||||
// Filter observations by key concepts for timeline
|
||||
const timelineObs = observations.filter(obs => {
|
||||
const concepts = parseJsonArray(obs.concepts);
|
||||
return concepts.includes('what-changed') ||
|
||||
concepts.includes('how-it-works') ||
|
||||
concepts.includes('problem-solution') ||
|
||||
concepts.includes('gotcha') ||
|
||||
concepts.includes('discovery') ||
|
||||
concepts.includes('why-it-exists') ||
|
||||
concepts.includes('decision') ||
|
||||
concepts.includes('trade-off');
|
||||
});
|
||||
|
||||
// Build output
|
||||
const output: string[] = [];
|
||||
|
||||
// Header
|
||||
if (useColors) {
|
||||
output.push('');
|
||||
output.push(`${colors.bright}${colors.cyan}📝 [${project}] recent context${colors.reset}`);
|
||||
output.push(`${colors.gray}${'─'.repeat(60)}${colors.reset}`);
|
||||
output.push('');
|
||||
} else {
|
||||
output.push(`# [${project}] recent context`);
|
||||
output.push('');
|
||||
}
|
||||
|
||||
let isFirstSummary = true;
|
||||
|
||||
for (let i = 0; i < summaries.length; i++) {
|
||||
const summary = summaries[i];
|
||||
|
||||
// Determine verbosity tier based on position
|
||||
// Most recent summary is at the end (highest index) since we display chronologically
|
||||
const positionFromEnd = summaries.length - 1 - i;
|
||||
const isTier1 = positionFromEnd === 0; // Most recent (full verbosity)
|
||||
const isTier2 = positionFromEnd >= 1 && positionFromEnd <= 3; // Middle 3 (request + what was done)
|
||||
const isTier3 = positionFromEnd > 3; // Oldest 6 (request only)
|
||||
|
||||
// Add separator between summaries (but not before the first one)
|
||||
if (!isFirstSummary) {
|
||||
if (useColors) {
|
||||
output.push(`${colors.gray}${'─'.repeat(60)}${colors.reset}`);
|
||||
output.push('');
|
||||
} else {
|
||||
output.push('---');
|
||||
output.push('');
|
||||
}
|
||||
} else {
|
||||
if (useColors) {
|
||||
output.push('');
|
||||
}
|
||||
}
|
||||
|
||||
isFirstSummary = false;
|
||||
|
||||
// TIER 3: Minimal (just Request + Date)
|
||||
if (isTier3) {
|
||||
if (summary.request) {
|
||||
if (useColors) {
|
||||
output.push(`${colors.bright}${colors.yellow}Request:${colors.reset} ${summary.request}`);
|
||||
output.push('');
|
||||
} else {
|
||||
output.push(`**Request:** ${summary.request}`);
|
||||
output.push('');
|
||||
}
|
||||
}
|
||||
const dateTime = new Date(summary.created_at).toLocaleString();
|
||||
if (useColors) {
|
||||
output.push(`${colors.dim}Date: ${dateTime}${colors.reset}`);
|
||||
} else {
|
||||
output.push(`**Date:** ${dateTime}`);
|
||||
output.push('');
|
||||
}
|
||||
continue; // Skip the rest for Tier 3
|
||||
}
|
||||
|
||||
// TIER 1 & 2: Show Request
|
||||
if (summary.request) {
|
||||
if (useColors) {
|
||||
output.push(`${colors.bright}${colors.yellow}Request:${colors.reset} ${summary.request}`);
|
||||
output.push('');
|
||||
} else {
|
||||
output.push(`**Request:** ${summary.request}`);
|
||||
output.push('');
|
||||
}
|
||||
}
|
||||
|
||||
// TIER 1 ONLY: Show Learned
|
||||
if (isTier1 && summary.learned) {
|
||||
if (useColors) {
|
||||
output.push(`${colors.bright}${colors.blue}Learned:${colors.reset} ${summary.learned}`);
|
||||
output.push('');
|
||||
} else {
|
||||
output.push(`**Learned:** ${summary.learned}`);
|
||||
output.push('');
|
||||
}
|
||||
}
|
||||
|
||||
// TIER 1 & 2: Show Completed
|
||||
if (summary.completed) {
|
||||
if (useColors) {
|
||||
output.push(`${colors.bright}${colors.green}Completed:${colors.reset} ${summary.completed}`);
|
||||
output.push('');
|
||||
} else {
|
||||
output.push(`**Completed:** ${summary.completed}`);
|
||||
output.push('');
|
||||
}
|
||||
}
|
||||
|
||||
// TIER 1 ONLY: Show Next Steps
|
||||
if (isTier1 && summary.next_steps) {
|
||||
if (useColors) {
|
||||
output.push(`${colors.bright}${colors.magenta}Next Steps:${colors.reset} ${summary.next_steps}`);
|
||||
output.push('');
|
||||
} else {
|
||||
output.push(`**Next Steps:** ${summary.next_steps}`);
|
||||
output.push('');
|
||||
}
|
||||
}
|
||||
|
||||
// TIER 1 ONLY: Get and show files
|
||||
if (isTier1) {
|
||||
const observations = db.db.prepare(`
|
||||
SELECT files_read, files_modified
|
||||
FROM observations
|
||||
WHERE sdk_session_id = ?
|
||||
`).all(summary.sdk_session_id) as Array<{
|
||||
files_read: string | null;
|
||||
files_modified: string | null;
|
||||
}>;
|
||||
|
||||
const filesReadSet = new Set<string>();
|
||||
const filesModifiedSet = new Set<string>();
|
||||
|
||||
// Helper function to convert absolute paths to relative paths
|
||||
const toRelativePath = (filePath: string): string => {
|
||||
try {
|
||||
// Only convert if it's an absolute path
|
||||
if (path.isAbsolute(filePath)) {
|
||||
return path.relative(cwd, filePath);
|
||||
}
|
||||
return filePath;
|
||||
} catch {
|
||||
return filePath;
|
||||
}
|
||||
};
|
||||
|
||||
for (const obs of observations) {
|
||||
if (obs.files_read) {
|
||||
try {
|
||||
const files = JSON.parse(obs.files_read);
|
||||
if (Array.isArray(files)) {
|
||||
files.forEach(f => filesReadSet.add(toRelativePath(f)));
|
||||
}
|
||||
} catch {
|
||||
// Skip invalid JSON
|
||||
}
|
||||
}
|
||||
|
||||
if (obs.files_modified) {
|
||||
try {
|
||||
const files = JSON.parse(obs.files_modified);
|
||||
if (Array.isArray(files)) {
|
||||
files.forEach(f => filesModifiedSet.add(toRelativePath(f)));
|
||||
}
|
||||
} catch {
|
||||
// Skip invalid JSON
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Remove files from filesReadSet if they're already in filesModifiedSet (avoid redundancy)
|
||||
filesModifiedSet.forEach(file => filesReadSet.delete(file));
|
||||
|
||||
if (filesReadSet.size > 0) {
|
||||
if (useColors) {
|
||||
output.push(`${colors.dim}Files Read: ${Array.from(filesReadSet).join(', ')}${colors.reset}`);
|
||||
} else {
|
||||
output.push(`**Files Read:** ${Array.from(filesReadSet).join(', ')}`);
|
||||
}
|
||||
}
|
||||
|
||||
if (filesModifiedSet.size > 0) {
|
||||
if (useColors) {
|
||||
output.push(`${colors.dim}Files Modified: ${Array.from(filesModifiedSet).join(', ')}${colors.reset}`);
|
||||
} else {
|
||||
output.push(`**Files Modified:** ${Array.from(filesModifiedSet).join(', ')}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TIER 1 & 2: Show Date
|
||||
const dateTime = new Date(summary.created_at).toLocaleString();
|
||||
// Chronological Timeline
|
||||
if (timelineObs.length > 0) {
|
||||
// Legend/Key
|
||||
if (useColors) {
|
||||
output.push(`${colors.dim}Date: ${dateTime}${colors.reset}`);
|
||||
output.push(`${colors.dim}Legend: 🎯 session-request | 🔴 gotcha | 🟡 problem-solution | 🔵 how-it-works | 🟢 what-changed | 🟣 discovery | 🟠 why-it-exists | 🟤 decision | ⚖️ trade-off${colors.reset}`);
|
||||
output.push('');
|
||||
} else {
|
||||
output.push(`**Date:** ${dateTime}`);
|
||||
}
|
||||
|
||||
if (!useColors) {
|
||||
output.push(`**Legend:** 🎯 session-request | 🔴 gotcha | 🟡 problem-solution | 🔵 how-it-works | 🟢 what-changed | 🟣 discovery | 🟠 why-it-exists | 🟤 decision | ⚖️ trade-off`);
|
||||
output.push('');
|
||||
}
|
||||
|
||||
// Progressive Disclosure Usage Instructions
|
||||
if (useColors) {
|
||||
output.push(`${colors.dim}💡 Progressive Disclosure: This index shows WHAT exists (titles) and retrieval COST (token counts).${colors.reset}`);
|
||||
output.push(`${colors.dim} → Use MCP search tools to fetch full observation details on-demand (Layer 2)${colors.reset}`);
|
||||
output.push(`${colors.dim} → Prefer searching observations over re-reading code for past decisions and learnings${colors.reset}`);
|
||||
output.push(`${colors.dim} → Critical types (🔴 gotcha, 🟤 decision, ⚖️ trade-off) often worth fetching immediately${colors.reset}`);
|
||||
output.push('');
|
||||
} else {
|
||||
output.push(`💡 **Progressive Disclosure:** This index shows WHAT exists (titles) and retrieval COST (token counts).`);
|
||||
output.push(`- Use MCP search tools to fetch full observation details on-demand (Layer 2)`);
|
||||
output.push(`- Prefer searching observations over re-reading code for past decisions and learnings`);
|
||||
output.push(`- Critical types (🔴 gotcha, 🟤 decision, ⚖️ trade-off) often worth fetching immediately`);
|
||||
output.push('');
|
||||
}
|
||||
|
||||
// Create unified timeline with both observations and summaries
|
||||
const mostRecentSummaryId = recentSummaries[0]?.id;
|
||||
|
||||
// Create offset summaries (displaySummaries already defined at top)
|
||||
const summariesWithOffset = displaySummaries.map((summary, i) => {
|
||||
// Most recent keeps its own time, others offset to next summary's time
|
||||
const nextSummary = i === 0 ? null : recentSummaries[i + 1];
|
||||
return {
|
||||
...summary,
|
||||
displayEpoch: nextSummary ? nextSummary.created_at_epoch : summary.created_at_epoch,
|
||||
displayTime: nextSummary ? nextSummary.created_at : summary.created_at,
|
||||
isMostRecent: summary.id === mostRecentSummaryId
|
||||
};
|
||||
});
|
||||
|
||||
type TimelineItem =
|
||||
| { type: 'observation'; data: Observation }
|
||||
| { type: 'summary'; data: typeof summariesWithOffset[0] };
|
||||
|
||||
const timeline: TimelineItem[] = [
|
||||
...timelineObs.map(obs => ({ type: 'observation' as const, data: obs })),
|
||||
...summariesWithOffset.map(summary => ({ type: 'summary' as const, data: summary }))
|
||||
];
|
||||
|
||||
// Sort chronologically
|
||||
timeline.sort((a, b) => {
|
||||
const aEpoch = a.type === 'observation' ? a.data.created_at_epoch : a.data.displayEpoch;
|
||||
const bEpoch = b.type === 'observation' ? b.data.created_at_epoch : b.data.displayEpoch;
|
||||
return aEpoch - bEpoch;
|
||||
});
|
||||
|
||||
// Group by day for rendering
|
||||
const dayTimelines = new Map<string, typeof timeline>();
|
||||
for (const item of timeline) {
|
||||
const itemDate = item.type === 'observation' ? item.data.created_at : item.data.displayTime;
|
||||
const day = formatDate(itemDate);
|
||||
if (!dayTimelines.has(day)) {
|
||||
dayTimelines.set(day, []);
|
||||
}
|
||||
dayTimelines.get(day)!.push(item);
|
||||
}
|
||||
|
||||
// Sort days chronologically
|
||||
const sortedDays = Array.from(dayTimelines.entries()).sort((a, b) => {
|
||||
const aDate = new Date(a[0]).getTime();
|
||||
const bDate = new Date(b[0]).getTime();
|
||||
return aDate - bDate;
|
||||
});
|
||||
|
||||
// Render each day's timeline
|
||||
for (const [day, dayItems] of sortedDays) {
|
||||
// Day header
|
||||
if (useColors) {
|
||||
output.push(`${colors.bright}${colors.cyan}${day}${colors.reset}`);
|
||||
output.push('');
|
||||
} else {
|
||||
output.push(`### ${day}`);
|
||||
output.push('');
|
||||
}
|
||||
|
||||
// Render items chronologically with visual file grouping
|
||||
let currentFile: string | null = null;
|
||||
let lastTime = '';
|
||||
let tableOpen = false;
|
||||
|
||||
for (const item of dayItems) {
|
||||
if (item.type === 'summary') {
|
||||
// Close any open table
|
||||
if (tableOpen) {
|
||||
output.push('');
|
||||
tableOpen = false;
|
||||
currentFile = null;
|
||||
lastTime = '';
|
||||
}
|
||||
|
||||
// Render summary
|
||||
const summary = item.data;
|
||||
const summaryTitle = `${summary.request || 'Session started'} (${formatDateTime(summary.displayTime)})`;
|
||||
const link = summary.isMostRecent ? '' : `claude-mem://session-summary/${summary.id}`;
|
||||
|
||||
if (useColors) {
|
||||
const linkPart = link ? `${colors.dim}[${link}]${colors.reset}` : '';
|
||||
output.push(`🎯 ${colors.yellow}#S${summary.id}${colors.reset} ${summaryTitle} ${linkPart}`);
|
||||
} else {
|
||||
const linkPart = link ? ` [→](${link})` : '';
|
||||
output.push(`**🎯 #S${summary.id}** ${summaryTitle}${linkPart}`);
|
||||
}
|
||||
output.push('');
|
||||
} else {
|
||||
// Render observation
|
||||
const obs = item.data;
|
||||
const files = parseJsonArray(obs.files_modified);
|
||||
const file = files.length > 0 ? toRelativePath(files[0], cwd) : 'General';
|
||||
|
||||
// Check if we need a new file section
|
||||
if (file !== currentFile) {
|
||||
// Close previous table
|
||||
if (tableOpen) {
|
||||
output.push('');
|
||||
}
|
||||
|
||||
// File header
|
||||
if (useColors) {
|
||||
output.push(`${colors.dim}${file}${colors.reset}`);
|
||||
} else {
|
||||
output.push(`**${file}**`);
|
||||
}
|
||||
|
||||
// Table header (markdown only)
|
||||
if (!useColors) {
|
||||
output.push(`| ID | Time | T | Title | Tokens |`);
|
||||
output.push(`|----|------|---|-------|--------|`);
|
||||
}
|
||||
|
||||
currentFile = file;
|
||||
tableOpen = true;
|
||||
lastTime = '';
|
||||
}
|
||||
|
||||
// Render observation row
|
||||
const concepts = parseJsonArray(obs.concepts);
|
||||
let icon = '•';
|
||||
|
||||
// Priority order: gotcha > decision > trade-off > problem-solution > discovery > why-it-exists > how-it-works > what-changed
|
||||
if (concepts.includes('gotcha')) {
|
||||
icon = '🔴';
|
||||
} else if (concepts.includes('decision')) {
|
||||
icon = '🟤';
|
||||
} else if (concepts.includes('trade-off')) {
|
||||
icon = '⚖️';
|
||||
} else if (concepts.includes('problem-solution')) {
|
||||
icon = '🟡';
|
||||
} else if (concepts.includes('discovery')) {
|
||||
icon = '🟣';
|
||||
} else if (concepts.includes('why-it-exists')) {
|
||||
icon = '🟠';
|
||||
} else if (concepts.includes('how-it-works')) {
|
||||
icon = '🔵';
|
||||
} else if (concepts.includes('what-changed')) {
|
||||
icon = '🟢';
|
||||
}
|
||||
|
||||
const time = formatTime(obs.created_at);
|
||||
const title = obs.title || 'Untitled';
|
||||
const tokens = estimateTokens(obs.narrative);
|
||||
|
||||
const showTime = time !== lastTime;
|
||||
const timeDisplay = showTime ? time : '';
|
||||
lastTime = time;
|
||||
|
||||
if (useColors) {
|
||||
const timePart = showTime ? `${colors.dim}${time}${colors.reset}` : ' '.repeat(time.length);
|
||||
const tokensPart = tokens > 0 ? `${colors.dim}(~${tokens}t)${colors.reset}` : '';
|
||||
output.push(` ${colors.dim}#${obs.id}${colors.reset} ${timePart} ${icon} ${title} ${tokensPart}`);
|
||||
} else {
|
||||
output.push(`| #${obs.id} | ${timeDisplay || '″'} | ${icon} | ${title} | ~${tokens} |`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Close final table if open
|
||||
if (tableOpen) {
|
||||
output.push('');
|
||||
}
|
||||
}
|
||||
|
||||
// Add full summary details for most recent session
|
||||
const mostRecentSummary = recentSummaries[0];
|
||||
if (mostRecentSummary && (mostRecentSummary.completed || mostRecentSummary.next_steps)) {
|
||||
if (mostRecentSummary.completed) {
|
||||
if (useColors) {
|
||||
output.push(`${colors.green}Completed:${colors.reset} ${mostRecentSummary.completed}`);
|
||||
} else {
|
||||
output.push(`**Completed**: ${mostRecentSummary.completed}`);
|
||||
}
|
||||
output.push('');
|
||||
}
|
||||
|
||||
if (mostRecentSummary.next_steps) {
|
||||
if (useColors) {
|
||||
output.push(`${colors.magenta}Next Steps:${colors.reset} ${mostRecentSummary.next_steps}`);
|
||||
} else {
|
||||
output.push(`**Next Steps**: ${mostRecentSummary.next_steps}`);
|
||||
}
|
||||
output.push('');
|
||||
}
|
||||
}
|
||||
|
||||
// Footer with MCP search instructions
|
||||
if (useColors) {
|
||||
output.push(`${colors.dim}Use claude-mem MCP search to access records with the given ID${colors.reset}`);
|
||||
} else {
|
||||
output.push(`*Use claude-mem MCP search to access records with the given ID*`);
|
||||
}
|
||||
output.push('');
|
||||
}
|
||||
|
||||
// Footer
|
||||
if (useColors) {
|
||||
output.push('');
|
||||
output.push(`${colors.gray}${'─'.repeat(60)}${colors.reset}`);
|
||||
output.push('');
|
||||
}
|
||||
|
||||
return output.join('\n');
|
||||
|
||||
@@ -0,0 +1,403 @@
|
||||
/**
|
||||
* Parser Regression Tests
|
||||
* Ensures v4.2.5 and v4.2.6 bugfixes remain stable
|
||||
*/
|
||||
|
||||
import { parseObservations, parseSummary } from './parser.js';
|
||||
|
||||
// ANSI color codes for output
|
||||
const GREEN = '\x1b[32m';
|
||||
const RED = '\x1b[31m';
|
||||
const YELLOW = '\x1b[33m';
|
||||
const RESET = '\x1b[0m';
|
||||
|
||||
let testsRun = 0;
|
||||
let testsPassed = 0;
|
||||
let testsFailed = 0;
|
||||
|
||||
function assert(condition: boolean, testName: string, errorMsg?: string): void {
|
||||
testsRun++;
|
||||
if (condition) {
|
||||
testsPassed++;
|
||||
console.log(`${GREEN}✓${RESET} ${testName}`);
|
||||
} else {
|
||||
testsFailed++;
|
||||
console.log(`${RED}✗${RESET} ${testName}`);
|
||||
if (errorMsg) {
|
||||
console.log(` ${RED}${errorMsg}${RESET}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function assertEqual<T>(actual: T, expected: T, testName: string): void {
|
||||
const isEqual = JSON.stringify(actual) === JSON.stringify(expected);
|
||||
if (!isEqual) {
|
||||
assert(false, testName, `Expected: ${JSON.stringify(expected)}, Got: ${JSON.stringify(actual)}`);
|
||||
} else {
|
||||
assert(true, testName);
|
||||
}
|
||||
}
|
||||
|
||||
console.log('\n' + YELLOW + '='.repeat(60) + RESET);
|
||||
console.log(YELLOW + 'Parser Regression Tests (v4.2.5 & v4.2.6)' + RESET);
|
||||
console.log(YELLOW + '='.repeat(60) + RESET + '\n');
|
||||
|
||||
// ============================================================================
|
||||
// v4.2.6: Observation Parsing - NEVER Skip Observations
|
||||
// ============================================================================
|
||||
|
||||
console.log(YELLOW + '\nv4.2.6: Observation Validation Fixes' + RESET);
|
||||
console.log('─'.repeat(60) + '\n');
|
||||
|
||||
// Test 1: Observation with missing title should be saved
|
||||
const missingTitleXml = `
|
||||
<observation>
|
||||
<type>feature</type>
|
||||
<subtitle>Added new feature</subtitle>
|
||||
<narrative>Implemented the feature successfully</narrative>
|
||||
<facts>
|
||||
<fact>Created new file</fact>
|
||||
</facts>
|
||||
<concepts>
|
||||
<concept>authentication</concept>
|
||||
</concepts>
|
||||
<files_read></files_read>
|
||||
<files_modified>
|
||||
<file>src/app.ts</file>
|
||||
</files_modified>
|
||||
</observation>
|
||||
`;
|
||||
|
||||
const missingTitleResult = parseObservations(missingTitleXml);
|
||||
assert(missingTitleResult.length === 1, 'Should parse observation with missing title');
|
||||
assert(missingTitleResult[0].title === null, 'Missing title should be null');
|
||||
assertEqual(missingTitleResult[0].type, 'feature', 'Should preserve type when title missing');
|
||||
|
||||
// Test 2: Observation with missing subtitle should be saved
|
||||
const missingSubtitleXml = `
|
||||
<observation>
|
||||
<type>bugfix</type>
|
||||
<title>Fixed critical bug</title>
|
||||
<narrative>Resolved the issue</narrative>
|
||||
<facts></facts>
|
||||
<concepts></concepts>
|
||||
<files_read></files_read>
|
||||
<files_modified></files_modified>
|
||||
</observation>
|
||||
`;
|
||||
|
||||
const missingSubtitleResult = parseObservations(missingSubtitleXml);
|
||||
assert(missingSubtitleResult.length === 1, 'Should parse observation with missing subtitle');
|
||||
assert(missingSubtitleResult[0].subtitle === null, 'Missing subtitle should be null');
|
||||
assertEqual(missingSubtitleResult[0].title, 'Fixed critical bug', 'Should preserve title when subtitle missing');
|
||||
|
||||
// Test 3: Observation with missing narrative should be saved
|
||||
const missingNarrativeXml = `
|
||||
<observation>
|
||||
<type>refactor</type>
|
||||
<title>Code cleanup</title>
|
||||
<subtitle>Improved structure</subtitle>
|
||||
<facts>
|
||||
<fact>Removed dead code</fact>
|
||||
</facts>
|
||||
<concepts></concepts>
|
||||
<files_read></files_read>
|
||||
<files_modified></files_modified>
|
||||
</observation>
|
||||
`;
|
||||
|
||||
const missingNarrativeResult = parseObservations(missingNarrativeXml);
|
||||
assert(missingNarrativeResult.length === 1, 'Should parse observation with missing narrative');
|
||||
assert(missingNarrativeResult[0].narrative === null, 'Missing narrative should be null');
|
||||
assertEqual(missingNarrativeResult[0].facts, ['Removed dead code'], 'Should preserve facts when narrative missing');
|
||||
|
||||
// Test 4: Observation with ALL fields missing (except type) should be saved
|
||||
const minimalObservationXml = `
|
||||
<observation>
|
||||
<type>change</type>
|
||||
<title></title>
|
||||
<subtitle></subtitle>
|
||||
<narrative></narrative>
|
||||
<facts></facts>
|
||||
<concepts></concepts>
|
||||
<files_read></files_read>
|
||||
<files_modified></files_modified>
|
||||
</observation>
|
||||
`;
|
||||
|
||||
const minimalResult = parseObservations(minimalObservationXml);
|
||||
assert(minimalResult.length === 1, 'Should parse minimal observation with only type');
|
||||
assertEqual(minimalResult[0].type, 'change', 'Should preserve type for minimal observation');
|
||||
assert(minimalResult[0].title === null, 'Empty title should be null');
|
||||
assert(minimalResult[0].subtitle === null, 'Empty subtitle should be null');
|
||||
assert(minimalResult[0].narrative === null, 'Empty narrative should be null');
|
||||
|
||||
// Test 5: Observation with missing type should use "change" as fallback
|
||||
const missingTypeXml = `
|
||||
<observation>
|
||||
<title>Something happened</title>
|
||||
<subtitle>Details here</subtitle>
|
||||
<narrative>More info</narrative>
|
||||
<facts></facts>
|
||||
<concepts></concepts>
|
||||
<files_read></files_read>
|
||||
<files_modified></files_modified>
|
||||
</observation>
|
||||
`;
|
||||
|
||||
const missingTypeResult = parseObservations(missingTypeXml);
|
||||
assert(missingTypeResult.length === 1, 'Should parse observation with missing type');
|
||||
assertEqual(missingTypeResult[0].type, 'change', 'Missing type should default to "change"');
|
||||
|
||||
// Test 6: Observation with invalid type should use "change" as fallback
|
||||
const invalidTypeXml = `
|
||||
<observation>
|
||||
<type>invalid_type_here</type>
|
||||
<title>Something happened</title>
|
||||
<subtitle>Details here</subtitle>
|
||||
<narrative>More info</narrative>
|
||||
<facts></facts>
|
||||
<concepts></concepts>
|
||||
<files_read></files_read>
|
||||
<files_modified></files_modified>
|
||||
</observation>
|
||||
`;
|
||||
|
||||
const invalidTypeResult = parseObservations(invalidTypeXml);
|
||||
assert(invalidTypeResult.length === 1, 'Should parse observation with invalid type');
|
||||
assertEqual(invalidTypeResult[0].type, 'change', 'Invalid type should default to "change"');
|
||||
|
||||
// Test 7: Multiple observations with mixed completeness should all be saved
|
||||
const mixedObservationsXml = `
|
||||
<observation>
|
||||
<type>feature</type>
|
||||
<title>Full observation</title>
|
||||
<subtitle>Complete</subtitle>
|
||||
<narrative>All fields present</narrative>
|
||||
<facts><fact>Fact 1</fact></facts>
|
||||
<concepts><concept>concept1</concept></concepts>
|
||||
<files_read></files_read>
|
||||
<files_modified></files_modified>
|
||||
</observation>
|
||||
<observation>
|
||||
<type>bugfix</type>
|
||||
<subtitle>Only subtitle and type</subtitle>
|
||||
<facts></facts>
|
||||
<concepts></concepts>
|
||||
<files_read></files_read>
|
||||
<files_modified></files_modified>
|
||||
</observation>
|
||||
<observation>
|
||||
<title>Only title, no type</title>
|
||||
<facts></facts>
|
||||
<concepts></concepts>
|
||||
<files_read></files_read>
|
||||
<files_modified></files_modified>
|
||||
</observation>
|
||||
`;
|
||||
|
||||
const mixedResult = parseObservations(mixedObservationsXml);
|
||||
assertEqual(mixedResult.length, 3, 'Should parse all three observations regardless of completeness');
|
||||
assertEqual(mixedResult[0].type, 'feature', 'First observation should have correct type');
|
||||
assertEqual(mixedResult[1].type, 'bugfix', 'Second observation should have correct type');
|
||||
assertEqual(mixedResult[2].type, 'change', 'Third observation should default to "change"');
|
||||
|
||||
// ============================================================================
|
||||
// v4.2.5: Summary Parsing - NEVER Skip Summaries
|
||||
// ============================================================================
|
||||
|
||||
console.log(YELLOW + '\nv4.2.5: Summary Validation Fixes' + RESET);
|
||||
console.log('─'.repeat(60) + '\n');
|
||||
|
||||
// Test 8: Summary with missing request field should be saved
|
||||
const missingRequestXml = `
|
||||
<summary>
|
||||
<investigated>Looked into the codebase</investigated>
|
||||
<learned>Found the issue</learned>
|
||||
<completed>Fixed the bug</completed>
|
||||
<next_steps>Deploy to production</next_steps>
|
||||
</summary>
|
||||
`;
|
||||
|
||||
const missingRequestResult = parseSummary(missingRequestXml);
|
||||
assert(missingRequestResult !== null, 'Should parse summary with missing request');
|
||||
assert(missingRequestResult!.request === null, 'Missing request should be null');
|
||||
assertEqual(missingRequestResult!.investigated, 'Looked into the codebase', 'Should preserve other fields');
|
||||
|
||||
// Test 9: Summary with missing investigated field should be saved
|
||||
const missingInvestigatedXml = `
|
||||
<summary>
|
||||
<request>Fix the bug</request>
|
||||
<learned>Root cause identified</learned>
|
||||
<completed>Applied the fix</completed>
|
||||
<next_steps>Monitor production</next_steps>
|
||||
</summary>
|
||||
`;
|
||||
|
||||
const missingInvestigatedResult = parseSummary(missingInvestigatedXml);
|
||||
assert(missingInvestigatedResult !== null, 'Should parse summary with missing investigated');
|
||||
assert(missingInvestigatedResult!.investigated === null, 'Missing investigated should be null');
|
||||
|
||||
// Test 10: Summary with missing learned field should be saved
|
||||
const missingLearnedXml = `
|
||||
<summary>
|
||||
<request>Add new feature</request>
|
||||
<investigated>Reviewed the requirements</investigated>
|
||||
<completed>Implemented the feature</completed>
|
||||
<next_steps>Write tests</next_steps>
|
||||
</summary>
|
||||
`;
|
||||
|
||||
const missingLearnedResult = parseSummary(missingLearnedXml);
|
||||
assert(missingLearnedResult !== null, 'Should parse summary with missing learned');
|
||||
assert(missingLearnedResult!.learned === null, 'Missing learned should be null');
|
||||
|
||||
// Test 11: Summary with missing completed field should be saved
|
||||
const missingCompletedXml = `
|
||||
<summary>
|
||||
<request>Refactor code</request>
|
||||
<investigated>Analyzed the structure</investigated>
|
||||
<learned>Found improvement opportunities</learned>
|
||||
<next_steps>Continue refactoring</next_steps>
|
||||
</summary>
|
||||
`;
|
||||
|
||||
const missingCompletedResult = parseSummary(missingCompletedXml);
|
||||
assert(missingCompletedResult !== null, 'Should parse summary with missing completed');
|
||||
assert(missingCompletedResult!.completed === null, 'Missing completed should be null');
|
||||
|
||||
// Test 12: Summary with missing next_steps field should be saved
|
||||
const missingNextStepsXml = `
|
||||
<summary>
|
||||
<request>Review code</request>
|
||||
<investigated>Examined all files</investigated>
|
||||
<learned>Code quality is good</learned>
|
||||
<completed>Review complete</completed>
|
||||
</summary>
|
||||
`;
|
||||
|
||||
const missingNextStepsResult = parseSummary(missingNextStepsXml);
|
||||
assert(missingNextStepsResult !== null, 'Should parse summary with missing next_steps');
|
||||
assert(missingNextStepsResult!.next_steps === null, 'Missing next_steps should be null');
|
||||
|
||||
// Test 13: Summary with only notes field should be saved
|
||||
const onlyNotesXml = `
|
||||
<summary>
|
||||
<notes>Some random notes</notes>
|
||||
</summary>
|
||||
`;
|
||||
|
||||
const onlyNotesResult = parseSummary(onlyNotesXml);
|
||||
assert(onlyNotesResult !== null, 'Should parse summary with only notes field');
|
||||
assertEqual(onlyNotesResult!.notes, 'Some random notes', 'Should preserve notes field');
|
||||
|
||||
// Test 14: Completely empty summary should be saved
|
||||
const emptySummaryXml = `
|
||||
<summary>
|
||||
<request></request>
|
||||
<investigated></investigated>
|
||||
<learned></learned>
|
||||
<completed></completed>
|
||||
<next_steps></next_steps>
|
||||
</summary>
|
||||
`;
|
||||
|
||||
const emptySummaryResult = parseSummary(emptySummaryXml);
|
||||
assert(emptySummaryResult !== null, 'Should parse completely empty summary');
|
||||
assert(emptySummaryResult!.request === null, 'Empty request should be null');
|
||||
assert(emptySummaryResult!.investigated === null, 'Empty investigated should be null');
|
||||
|
||||
// Test 15: Summary with skip_summary should return null (valid use case)
|
||||
const skipSummaryXml = `
|
||||
<skip_summary reason="Not enough context yet" />
|
||||
`;
|
||||
|
||||
const skipSummaryResult = parseSummary(skipSummaryXml);
|
||||
assert(skipSummaryResult === null, 'Should return null for skip_summary directive');
|
||||
|
||||
// ============================================================================
|
||||
// Edge Cases & Data Integrity
|
||||
// ============================================================================
|
||||
|
||||
console.log(YELLOW + '\nEdge Cases & Data Integrity' + RESET);
|
||||
console.log('─'.repeat(60) + '\n');
|
||||
|
||||
// Test 16: Observation with whitespace-only fields should be null
|
||||
const whitespaceObservationXml = `
|
||||
<observation>
|
||||
<type>change</type>
|
||||
<title> </title>
|
||||
<subtitle>
|
||||
|
||||
</subtitle>
|
||||
<narrative></narrative>
|
||||
<facts></facts>
|
||||
<concepts></concepts>
|
||||
<files_read></files_read>
|
||||
<files_modified></files_modified>
|
||||
</observation>
|
||||
`;
|
||||
|
||||
const whitespaceResult = parseObservations(whitespaceObservationXml);
|
||||
assert(whitespaceResult.length === 1, 'Should parse observation with whitespace fields');
|
||||
assert(whitespaceResult[0].title === null || whitespaceResult[0].title!.trim() === '', 'Whitespace title should be null or empty');
|
||||
|
||||
// Test 17: Observation with concepts including type should filter out type
|
||||
const conceptsWithTypeXml = `
|
||||
<observation>
|
||||
<type>feature</type>
|
||||
<title>New feature</title>
|
||||
<subtitle>Details</subtitle>
|
||||
<narrative>Description</narrative>
|
||||
<facts></facts>
|
||||
<concepts>
|
||||
<concept>feature</concept>
|
||||
<concept>authentication</concept>
|
||||
</concepts>
|
||||
<files_read></files_read>
|
||||
<files_modified></files_modified>
|
||||
</observation>
|
||||
`;
|
||||
|
||||
const conceptsWithTypeResult = parseObservations(conceptsWithTypeXml);
|
||||
assert(conceptsWithTypeResult.length === 1, 'Should parse observation with type in concepts');
|
||||
assertEqual(conceptsWithTypeResult[0].concepts, ['authentication'], 'Should filter out type from concepts');
|
||||
|
||||
// Test 18: Observation with all valid types
|
||||
const validTypes = ['decision', 'bugfix', 'feature', 'refactor', 'discovery', 'change'];
|
||||
validTypes.forEach(type => {
|
||||
const typeXml = `
|
||||
<observation>
|
||||
<type>${type}</type>
|
||||
<title>Test</title>
|
||||
<subtitle>Test</subtitle>
|
||||
<narrative>Test</narrative>
|
||||
<facts></facts>
|
||||
<concepts></concepts>
|
||||
<files_read></files_read>
|
||||
<files_modified></files_modified>
|
||||
</observation>
|
||||
`;
|
||||
const result = parseObservations(typeXml);
|
||||
assertEqual(result[0].type, type, `Should accept valid type: ${type}`);
|
||||
});
|
||||
|
||||
// ============================================================================
|
||||
// Results Summary
|
||||
// ============================================================================
|
||||
|
||||
console.log('\n' + YELLOW + '='.repeat(60) + RESET);
|
||||
console.log(YELLOW + 'Test Results Summary' + RESET);
|
||||
console.log(YELLOW + '='.repeat(60) + RESET + '\n');
|
||||
|
||||
console.log(`Total Tests: ${testsRun}`);
|
||||
console.log(`${GREEN}Passed: ${testsPassed}${RESET}`);
|
||||
console.log(`${RED}Failed: ${testsFailed}${RESET}`);
|
||||
|
||||
if (testsFailed > 0) {
|
||||
console.log(`\n${RED}❌ TESTS FAILED${RESET}\n`);
|
||||
process.exit(1);
|
||||
} else {
|
||||
console.log(`\n${GREEN}✅ ALL TESTS PASSED${RESET}\n`);
|
||||
process.exit(0);
|
||||
}
|
||||
+40
-61
@@ -7,10 +7,10 @@ import { logger } from '../utils/logger.js';
|
||||
|
||||
export interface ParsedObservation {
|
||||
type: string;
|
||||
title: string;
|
||||
subtitle: string;
|
||||
title: string | null;
|
||||
subtitle: string | null;
|
||||
facts: string[];
|
||||
narrative: string;
|
||||
narrative: string | null;
|
||||
concepts: string[];
|
||||
files_read: string[];
|
||||
files_modified: string[];
|
||||
@@ -49,39 +49,39 @@ export function parseObservations(text: string, correlationId?: string): ParsedO
|
||||
const files_read = extractArrayElements(obsContent, 'files_read', 'file');
|
||||
const files_modified = extractArrayElements(obsContent, 'files_modified', 'file');
|
||||
|
||||
// Validate required fields
|
||||
if (!type || !title || !subtitle || !narrative) {
|
||||
logger.warn('PARSER', 'Observation missing required fields, skipping', {
|
||||
correlationId,
|
||||
hasType: !!type,
|
||||
hasTitle: !!title,
|
||||
hasSubtitle: !!subtitle,
|
||||
hasNarrative: !!narrative
|
||||
});
|
||||
continue;
|
||||
// NOTE FROM THEDOTMACK: ALWAYS save observations - never skip. 10/24/2025
|
||||
// All fields except type are nullable in schema
|
||||
// If type is missing or invalid, use "change" as catch-all fallback
|
||||
|
||||
// Determine final type
|
||||
let finalType = 'change'; // Default catch-all
|
||||
if (type) {
|
||||
const validTypes = ['bugfix', 'feature', 'refactor', 'change', 'discovery', 'decision'];
|
||||
if (validTypes.includes(type.trim())) {
|
||||
finalType = type.trim();
|
||||
} else {
|
||||
logger.warn('PARSER', `Invalid observation type: ${type}, using "change"`, { correlationId });
|
||||
}
|
||||
} else {
|
||||
logger.warn('PARSER', 'Observation missing type field, using "change"', { correlationId });
|
||||
}
|
||||
|
||||
// Validate type
|
||||
const validTypes = ['bugfix', 'feature', 'refactor', 'change', 'discovery', 'decision'];
|
||||
if (!validTypes.includes(type.trim())) {
|
||||
logger.warn('PARSER', `Invalid observation type: ${type}, skipping`, { correlationId });
|
||||
continue;
|
||||
}
|
||||
// All other fields are optional - save whatever we have
|
||||
|
||||
// Filter out type from concepts array (types and concepts are separate dimensions)
|
||||
const cleanedConcepts = concepts.filter(c => c !== type.trim());
|
||||
const cleanedConcepts = concepts.filter(c => c !== finalType);
|
||||
|
||||
if (cleanedConcepts.length !== concepts.length) {
|
||||
logger.warn('PARSER', 'Removed observation type from concepts array', {
|
||||
correlationId,
|
||||
type: type.trim(),
|
||||
type: finalType,
|
||||
originalConcepts: concepts,
|
||||
cleanedConcepts
|
||||
});
|
||||
}
|
||||
|
||||
observations.push({
|
||||
type: type.trim(),
|
||||
type: finalType,
|
||||
title,
|
||||
subtitle,
|
||||
facts,
|
||||
@@ -130,18 +130,21 @@ export function parseSummary(text: string, sessionId?: number): ParsedSummary |
|
||||
const next_steps = extractField(summaryContent, 'next_steps');
|
||||
const notes = extractField(summaryContent, 'notes'); // Optional
|
||||
|
||||
// NOTE FROM THEDOTMACK: 100% of the time we must SAVE the summary, even if fields are missing. 10/24/2025
|
||||
// NEVER DO THIS NONSENSE AGAIN.
|
||||
|
||||
// Validate required fields are present (notes is optional)
|
||||
if (!request || !investigated || !learned || !completed || !next_steps) {
|
||||
logger.warn('PARSER', 'Summary missing required fields', {
|
||||
sessionId,
|
||||
hasRequest: !!request,
|
||||
hasInvestigated: !!investigated,
|
||||
hasLearned: !!learned,
|
||||
hasCompleted: !!completed,
|
||||
hasNextSteps: !!next_steps
|
||||
});
|
||||
return null;
|
||||
}
|
||||
// if (!request || !investigated || !learned || !completed || !next_steps) {
|
||||
// logger.warn('PARSER', 'Summary missing required fields', {
|
||||
// sessionId,
|
||||
// hasRequest: !!request,
|
||||
// hasInvestigated: !!investigated,
|
||||
// hasLearned: !!learned,
|
||||
// hasCompleted: !!completed,
|
||||
// hasNextSteps: !!next_steps
|
||||
// });
|
||||
// return null;
|
||||
// }
|
||||
|
||||
return {
|
||||
request,
|
||||
@@ -155,43 +158,19 @@ export function parseSummary(text: string, sessionId?: number): ParsedSummary |
|
||||
|
||||
/**
|
||||
* Extract a simple field value from XML content
|
||||
* Returns null for missing or empty/whitespace-only fields
|
||||
*/
|
||||
function extractField(content: string, fieldName: string): string | null {
|
||||
const regex = new RegExp(`<${fieldName}>([^<]*)</${fieldName}>`);
|
||||
const match = regex.exec(content);
|
||||
return match ? match[1].trim() : null;
|
||||
}
|
||||
if (!match) return null;
|
||||
|
||||
/**
|
||||
* Extract file array from XML content
|
||||
* Handles both <file> children and empty tags
|
||||
*/
|
||||
function extractFileArray(content: string, arrayName: string): string[] {
|
||||
const files: string[] = [];
|
||||
|
||||
// Match the array block
|
||||
const arrayRegex = new RegExp(`<${arrayName}>(.*?)</${arrayName}>`, 's');
|
||||
const arrayMatch = arrayRegex.exec(content);
|
||||
|
||||
if (!arrayMatch) {
|
||||
return files;
|
||||
}
|
||||
|
||||
const arrayContent = arrayMatch[1];
|
||||
|
||||
// Extract individual <file> elements
|
||||
const fileRegex = /<file>([^<]+)<\/file>/g;
|
||||
let fileMatch;
|
||||
while ((fileMatch = fileRegex.exec(arrayContent)) !== null) {
|
||||
files.push(fileMatch[1].trim());
|
||||
}
|
||||
|
||||
return files;
|
||||
const trimmed = match[1].trim();
|
||||
return trimmed === '' ? null : trimmed;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract array of elements from XML content
|
||||
* Generic version of extractFileArray that works with any element name
|
||||
*/
|
||||
function extractArrayElements(content: string, arrayName: string, elementName: string): string[] {
|
||||
const elements: string[] = [];
|
||||
|
||||
+8
-25
@@ -158,38 +158,21 @@ export function buildObservationPrompt(obs: Observation): string {
|
||||
export function buildSummaryPrompt(session: SDKSession): string {
|
||||
return `THIS REQUEST'S SUMMARY
|
||||
===============
|
||||
Think about the observations you just wrote for this request, and write a summary of what was done, what was learned, and what's next.
|
||||
Think about the last request, and write a summary of what was done, what was learned, and what's next.
|
||||
|
||||
IMPORTANT! DO NOT summarize the observation process itself - you are summarizing a DIFFERENT claude code session, not this one.
|
||||
|
||||
User's Original Request: ${session.user_prompt}
|
||||
|
||||
WHEN NOT TO SUMMARIZE
|
||||
----------------------
|
||||
Do not summarize if the request is conversational and unrelated to the work that was just completed.
|
||||
|
||||
If skipping, **output only**: <skip_summary reason="[brief reason]" />
|
||||
|
||||
✅ GOOD - Describes deliverables:
|
||||
<request>Fix authentication timeout bug</request>
|
||||
<request>Add three-tier verbosity system to session summaries</request>
|
||||
<request>Deploy Kubernetes cluster with auto-scaling</request>
|
||||
|
||||
❌ BAD - Describes meta-operations (DO NOT DO THIS):
|
||||
<request>Process tool executions and store observations</request>
|
||||
<request>Analyze session data and generate summaries</request>
|
||||
<request>Track file modifications across sessions</request>
|
||||
|
||||
Output this XML:
|
||||
Respond in this XML format:
|
||||
<summary>
|
||||
<request>[What did the user request? Use their original sentiment from: ${session.user_prompt}]</request>
|
||||
<investigated>[What was explored?]</investigated>
|
||||
<learned>[What was discovered about how things work?]</learned>
|
||||
<completed>[What shipped? What does the system now do?]</completed>
|
||||
<request>[What did the user request? Form a title that reflects the actual request: ${session.user_prompt}]</request>
|
||||
<investigated>[Was anything explored? What was it?]</investigated>
|
||||
<learned>[Did you learn anything? What was learned about how things work?]</learned>
|
||||
<completed>[Did you do any work? What shipped? What does the system now do?]</completed>
|
||||
<next_steps>[What are the next steps?]</next_steps>
|
||||
<notes>[Additional insights]</notes>
|
||||
</summary>
|
||||
|
||||
**Required fields**: request, investigated, learned, completed, next_steps
|
||||
**Optional fields**: notes`;
|
||||
}
|
||||
IMPORTANT: This is not the end of the session. You will receive more requests to process, and more tool usages to observe and record. The summary helps keep track of progress. Always write at least a minimal summary explaining where we are at currently, even if you didn't learn anything new or complete any work.`;
|
||||
}
|
||||
+32
-3
@@ -13,6 +13,7 @@ declare global {
|
||||
|
||||
import net from 'net';
|
||||
import { unlinkSync, existsSync } from 'fs';
|
||||
import { execSync } from 'child_process';
|
||||
import { query } from '@anthropic-ai/claude-agent-sdk';
|
||||
import type { SDKUserMessage, SDKSystemMessage } from '@anthropic-ai/claude-agent-sdk';
|
||||
import { SessionStore } from '../services/sqlite/SessionStore.js';
|
||||
@@ -24,6 +25,35 @@ import type { SDKSession } from './prompts.js';
|
||||
const MODEL = 'claude-sonnet-4-5';
|
||||
const DISALLOWED_TOOLS = ['Glob', 'Grep', 'ListMcpResourcesTool', 'WebSearch'];
|
||||
|
||||
/**
|
||||
* Find Claude Code executable path using which (Unix/Mac) or where (Windows)
|
||||
*/
|
||||
function findClaudePath(): string {
|
||||
try {
|
||||
// Try environment variable first
|
||||
if (process.env.CLAUDE_CODE_PATH) {
|
||||
return process.env.CLAUDE_CODE_PATH;
|
||||
}
|
||||
|
||||
// Use which on Unix/Mac, where on Windows
|
||||
const command = process.platform === 'win32' ? 'where claude' : 'which claude';
|
||||
const result = execSync(command, { encoding: 'utf8' }).trim();
|
||||
|
||||
// On Windows, 'where' returns multiple lines if there are multiple matches, take the first
|
||||
const path = result.split('\n')[0].trim();
|
||||
|
||||
if (!path) {
|
||||
throw new Error('Claude executable not found in PATH');
|
||||
}
|
||||
|
||||
console.error(`[SDK Worker] Found Claude executable: ${path}`);
|
||||
return path;
|
||||
} catch (error: any) {
|
||||
console.error('[SDK Worker] Failed to find Claude executable:', error.message);
|
||||
throw new Error('Claude Code executable not found. Please ensure claude is in your PATH or set CLAUDE_CODE_PATH environment variable.');
|
||||
}
|
||||
}
|
||||
|
||||
interface ObservationMessage {
|
||||
type: 'observation';
|
||||
tool_name: string;
|
||||
@@ -282,9 +312,8 @@ class SDKWorker {
|
||||
* Run SDK agent with streaming input mode
|
||||
*/
|
||||
private async runSDKAgent(): Promise<void> {
|
||||
// Find Claude Code executable
|
||||
const claudePath = process.env.CLAUDE_CODE_PATH || '/Users/alexnewman/.nvm/versions/node/v24.5.0/bin/claude';
|
||||
console.error(`[SDK Worker DEBUG] About to call query with claudePath: ${claudePath}`);
|
||||
const claudePath = findClaudePath();
|
||||
console.error(`[SDK Worker DEBUG] Using Claude executable: ${claudePath}`);
|
||||
|
||||
const queryResult = query({
|
||||
prompt: this.createMessageGenerator(),
|
||||
|
||||
@@ -702,12 +702,13 @@ export class SessionStore {
|
||||
*/
|
||||
getSessionById(id: number): {
|
||||
id: number;
|
||||
claude_session_id: string;
|
||||
sdk_session_id: string | null;
|
||||
project: string;
|
||||
user_prompt: string;
|
||||
} | null {
|
||||
const stmt = this.db.prepare(`
|
||||
SELECT id, sdk_session_id, project, user_prompt
|
||||
SELECT id, claude_session_id, sdk_session_id, project, user_prompt
|
||||
FROM sdk_sessions
|
||||
WHERE id = ?
|
||||
LIMIT 1
|
||||
@@ -903,10 +904,10 @@ export class SessionStore {
|
||||
project: string,
|
||||
observation: {
|
||||
type: string;
|
||||
title: string;
|
||||
subtitle: string;
|
||||
title: string | null;
|
||||
subtitle: string | null;
|
||||
facts: string[];
|
||||
narrative: string;
|
||||
narrative: string | null;
|
||||
concepts: string[];
|
||||
files_read: string[];
|
||||
files_modified: string[];
|
||||
|
||||
@@ -12,11 +12,41 @@ import { parseObservations, parseSummary } from '../sdk/parser.js';
|
||||
import type { SDKSession } from '../sdk/prompts.js';
|
||||
import { logger } from '../utils/logger.js';
|
||||
import { ensureAllDataDirs } from '../shared/paths.js';
|
||||
import { execSync } from 'child_process';
|
||||
|
||||
const MODEL = process.env.CLAUDE_MEM_MODEL || 'claude-sonnet-4-5';
|
||||
const DISALLOWED_TOOLS = ['Glob', 'Grep', 'ListMcpResourcesTool', 'WebSearch'];
|
||||
const FIXED_PORT = parseInt(process.env.CLAUDE_MEM_WORKER_PORT || '37777', 10);
|
||||
|
||||
/**
|
||||
* Find Claude Code executable path using which (Unix/Mac) or where (Windows)
|
||||
*/
|
||||
function findClaudePath(): string {
|
||||
try {
|
||||
// Try environment variable first
|
||||
if (process.env.CLAUDE_CODE_PATH) {
|
||||
return process.env.CLAUDE_CODE_PATH;
|
||||
}
|
||||
|
||||
// Use which on Unix/Mac, where on Windows
|
||||
const command = process.platform === 'win32' ? 'where claude' : 'which claude';
|
||||
const result = execSync(command, { encoding: 'utf8' }).trim();
|
||||
|
||||
// On Windows, 'where' returns multiple lines if there are multiple matches, take the first
|
||||
const path = result.split('\n')[0].trim();
|
||||
|
||||
if (!path) {
|
||||
throw new Error('Claude executable not found in PATH');
|
||||
}
|
||||
|
||||
logger.info('SYSTEM', `Found Claude executable: ${path}`);
|
||||
return path;
|
||||
} catch (error: any) {
|
||||
logger.failure('SYSTEM', 'Failed to find Claude executable', {}, error);
|
||||
throw new Error('Claude Code executable not found. Please ensure claude is in your PATH or set CLAUDE_CODE_PATH environment variable.');
|
||||
}
|
||||
}
|
||||
|
||||
interface ObservationMessage {
|
||||
type: 'observation';
|
||||
tool_name: string;
|
||||
@@ -128,14 +158,13 @@ class WorkerService {
|
||||
return;
|
||||
}
|
||||
|
||||
// Get the real claude_session_id (which is the same as sdk_session_id now)
|
||||
const claudeSessionId = dbSession.sdk_session_id || `session-${sessionDbId}`;
|
||||
const claudeSessionId = dbSession.claude_session_id;
|
||||
|
||||
// Create session state
|
||||
const session: ActiveSession = {
|
||||
sessionDbId,
|
||||
claudeSessionId,
|
||||
sdkSessionId: dbSession.sdk_session_id || null, // Set from database since we set both fields now
|
||||
sdkSessionId: null,
|
||||
project,
|
||||
userPrompt,
|
||||
pendingMessages: [],
|
||||
@@ -180,19 +209,16 @@ class WorkerService {
|
||||
let session = this.sessions.get(sessionDbId);
|
||||
if (!session) {
|
||||
// Auto-create session if it doesn't exist (e.g., worker restarted)
|
||||
// Fetch real session ID from database
|
||||
const db = new SessionStore();
|
||||
const dbSession = db.getSessionById(sessionDbId);
|
||||
db.close();
|
||||
|
||||
const claudeSessionId = dbSession?.sdk_session_id || `session-${sessionDbId}`;
|
||||
|
||||
session = {
|
||||
sessionDbId,
|
||||
claudeSessionId,
|
||||
claudeSessionId: dbSession!.claude_session_id,
|
||||
sdkSessionId: null,
|
||||
project: dbSession?.project || '',
|
||||
userPrompt: dbSession?.user_prompt || '',
|
||||
project: dbSession!.project,
|
||||
userPrompt: dbSession!.user_prompt,
|
||||
pendingMessages: [],
|
||||
abortController: new AbortController(),
|
||||
generatorPromise: null,
|
||||
@@ -244,19 +270,16 @@ class WorkerService {
|
||||
let session = this.sessions.get(sessionDbId);
|
||||
if (!session) {
|
||||
// Auto-create session if it doesn't exist (e.g., worker restarted)
|
||||
// Fetch real session ID from database
|
||||
const db = new SessionStore();
|
||||
const dbSession = db.getSessionById(sessionDbId);
|
||||
db.close();
|
||||
|
||||
const claudeSessionId = dbSession?.sdk_session_id || `session-${sessionDbId}`;
|
||||
|
||||
session = {
|
||||
sessionDbId,
|
||||
claudeSessionId,
|
||||
claudeSessionId: dbSession!.claude_session_id,
|
||||
sdkSessionId: null,
|
||||
project: dbSession?.project || '',
|
||||
userPrompt: dbSession?.user_prompt || '',
|
||||
project: dbSession!.project,
|
||||
userPrompt: dbSession!.user_prompt,
|
||||
pendingMessages: [],
|
||||
abortController: new AbortController(),
|
||||
generatorPromise: null,
|
||||
@@ -352,7 +375,8 @@ class WorkerService {
|
||||
private async runSDKAgent(session: ActiveSession): Promise<void> {
|
||||
logger.info('SDK', 'Agent starting', { sessionId: session.sessionDbId });
|
||||
|
||||
const claudePath = process.env.CLAUDE_CODE_PATH || '/Users/alexnewman/.nvm/versions/node/v24.5.0/bin/claude';
|
||||
const claudePath = findClaudePath();
|
||||
logger.info('SDK', `Using Claude executable: ${claudePath}`, { sessionId: session.sessionDbId });
|
||||
|
||||
try {
|
||||
const queryResult = query({
|
||||
@@ -366,26 +390,8 @@ class WorkerService {
|
||||
});
|
||||
|
||||
for await (const message of queryResult) {
|
||||
// Handle system init message
|
||||
if (message.type === 'system' && message.subtype === 'init') {
|
||||
const systemMsg = message as SDKSystemMessage;
|
||||
if (systemMsg.session_id) {
|
||||
// Update in database first, check if it succeeded
|
||||
const db = new SessionStore();
|
||||
const updated = db.updateSDKSessionId(session.sessionDbId, systemMsg.session_id);
|
||||
db.close();
|
||||
|
||||
if (updated) {
|
||||
logger.success('SDK', 'Session initialized', {
|
||||
sessionId: session.sessionDbId,
|
||||
sdkSessionId: systemMsg.session_id
|
||||
});
|
||||
session.sdkSessionId = systemMsg.session_id;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Handle assistant messages
|
||||
else if (message.type === 'assistant') {
|
||||
if (message.type === 'assistant') {
|
||||
const content = message.message.content;
|
||||
const textContent = Array.isArray(content)
|
||||
? content.filter((c: any) => c.type === 'text').map((c: any) => c.text).join('\n')
|
||||
@@ -471,28 +477,26 @@ class WorkerService {
|
||||
session.lastPromptNumber = message.prompt_number;
|
||||
|
||||
const db = new SessionStore();
|
||||
const dbSession = db.getSessionById(session.sessionDbId) as SDKSession | undefined;
|
||||
const dbSession = db.getSessionById(session.sessionDbId) as SDKSession;
|
||||
db.close();
|
||||
|
||||
if (dbSession) {
|
||||
const summarizePrompt = buildSummaryPrompt(dbSession);
|
||||
const summarizePrompt = buildSummaryPrompt(dbSession);
|
||||
|
||||
logger.dataIn('SDK', `Summary prompt sent (${summarizePrompt.length} chars)`, {
|
||||
sessionId: session.sessionDbId,
|
||||
promptNumber: message.prompt_number
|
||||
});
|
||||
logger.debug('SDK', 'Full summary prompt', { sessionId: session.sessionDbId }, summarizePrompt);
|
||||
logger.dataIn('SDK', `Summary prompt sent (${summarizePrompt.length} chars)`, {
|
||||
sessionId: session.sessionDbId,
|
||||
promptNumber: message.prompt_number
|
||||
});
|
||||
logger.debug('SDK', 'Full summary prompt', { sessionId: session.sessionDbId }, summarizePrompt);
|
||||
|
||||
yield {
|
||||
type: 'user',
|
||||
session_id: session.claudeSessionId, // Use real session ID
|
||||
parent_tool_use_id: null,
|
||||
message: {
|
||||
role: 'user',
|
||||
content: summarizePrompt
|
||||
}
|
||||
};
|
||||
}
|
||||
yield {
|
||||
type: 'user',
|
||||
session_id: session.claudeSessionId,
|
||||
parent_tool_use_id: null,
|
||||
message: {
|
||||
role: 'user',
|
||||
content: summarizePrompt
|
||||
}
|
||||
};
|
||||
} else if (message.type === 'observation') {
|
||||
session.lastPromptNumber = message.prompt_number;
|
||||
|
||||
@@ -535,6 +539,13 @@ class WorkerService {
|
||||
private handleAgentMessage(session: ActiveSession, content: string, promptNumber: number): void {
|
||||
const correlationId = logger.correlationId(session.sessionDbId, session.observationCounter);
|
||||
|
||||
// Always log what we received for debugging
|
||||
logger.info('PARSER', `Processing response (${content.length} chars)`, {
|
||||
sessionId: session.sessionDbId,
|
||||
promptNumber,
|
||||
preview: content.substring(0, 200)
|
||||
});
|
||||
|
||||
// Parse observations
|
||||
const observations = parseObservations(content, correlationId);
|
||||
|
||||
@@ -548,26 +559,35 @@ class WorkerService {
|
||||
|
||||
const db = new SessionStore();
|
||||
for (const obs of observations) {
|
||||
if (session.sdkSessionId) {
|
||||
db.storeObservation(session.sdkSessionId, session.project, obs, promptNumber);
|
||||
logger.success('DB', 'Observation stored', {
|
||||
correlationId,
|
||||
type: obs.type,
|
||||
title: obs.title
|
||||
});
|
||||
}
|
||||
db.storeObservation(session.claudeSessionId, session.project, obs, promptNumber);
|
||||
logger.success('DB', 'Observation stored', {
|
||||
correlationId,
|
||||
type: obs.type,
|
||||
title: obs.title
|
||||
});
|
||||
}
|
||||
|
||||
// Parse summary
|
||||
// Parse summary and ALWAYS store it
|
||||
logger.info('PARSER', 'Looking for summary tags...', { sessionId: session.sessionDbId });
|
||||
const summary = parseSummary(content, session.sessionDbId);
|
||||
if (summary && session.sdkSessionId) {
|
||||
logger.info('PARSER', 'Summary parsed', {
|
||||
if (summary) {
|
||||
logger.success('PARSER', 'Summary parsed successfully!', {
|
||||
sessionId: session.sessionDbId,
|
||||
promptNumber
|
||||
promptNumber,
|
||||
hasRequest: !!summary.request,
|
||||
hasInvestigated: !!summary.investigated,
|
||||
hasLearned: !!summary.learned,
|
||||
hasCompleted: !!summary.completed,
|
||||
hasNextSteps: !!summary.next_steps
|
||||
});
|
||||
db.storeSummary(session.claudeSessionId, session.project, summary, promptNumber);
|
||||
logger.success('DB', '📝 SUMMARY STORED IN DATABASE', { sessionId: session.sessionDbId, promptNumber });
|
||||
} else {
|
||||
logger.warn('PARSER', 'NO SUMMARY TAGS FOUND in response', {
|
||||
sessionId: session.sessionDbId,
|
||||
promptNumber,
|
||||
contentSample: content.substring(0, 500)
|
||||
});
|
||||
|
||||
db.storeSummary(session.sdkSessionId, session.project, summary, promptNumber);
|
||||
logger.success('DB', 'Summary stored', { sessionId: session.sessionDbId });
|
||||
}
|
||||
|
||||
db.close();
|
||||
|
||||
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user