feat: Claude Code hooks for automatic memory integration#124
feat: Claude Code hooks for automatic memory integration#124russellbrenner wants to merge 57 commits intoCaviraOSS:mainfrom
Conversation
- Fix openmemory-js Dockerfile: replace Bun with npm (no bun.lockb exists) - Add NODE_OPTIONS for increased heap size during TypeScript compilation - Restore dashboard app from git history (removed in Beta 1.3.0) - Update docker-compose.yml to re-enable dashboard service Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add SessionStart hook to query project context at session start - Add Stop hook to store session summaries - Both hooks fail silently if OpenMemory/Docker unavailable - Include CLAUDE.md.example with comprehensive MCP tool documentation - Add import-claude-mem.js for migrating claude-mem databases Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Use jq to build JSON output instead of bash substring expression that fails on some bash versions. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
This PR adds Claude Code integration hooks and a comprehensive dashboard for OpenMemory, enabling automatic memory capture during development sessions and providing visual analytics for memory management.
Changes:
- Added session start/stop hooks for automatic memory integration with Claude Code
- Created a Next.js dashboard with memory analytics, chat interface, and system monitoring
- Provided migration tooling for users coming from claude-mem
- Switched Docker build from Bun to npm with increased Node.js heap size
Reviewed changes
Copilot reviewed 33 out of 36 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| scripts/import-claude-mem.js | Migration script to import memories from claude-mem SQLite database |
| packages/openmemory-js/Dockerfile | Changed build system from Bun to npm with heap size optimization |
| hooks/openmemory-session-stop.sh | Bash script to capture session summaries at Claude Code session end |
| hooks/openmemory-session-start.sh | Bash script to inject relevant context at Claude Code session start |
| hooks/README.md | Installation and configuration guide for Claude Code hooks |
| hooks/CLAUDE.md.example | Template documentation for Claude to use OpenMemory MCP tools |
| docker-compose.yml | Removed obsolete version specification |
| dashboard/* | Complete Next.js dashboard implementation with memory visualization and chat interface |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
- Add NODE_OPTIONS=--max-old-space-size=128 to Dockerfile production stage - Document memory tuning in README for users needing larger heaps - Default 128MB provides ~4x headroom over minimal Node.js heap Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
The previous 128MB limit caused 96% memory pressure during normal operations. Increased to 512MB to provide headroom for vector operations and embedding storage. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
|
Hi Russell, thank you for your contribution! This helps us a lot, but I don't understand why there is a neccesity for frontend files here |
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
|
@copilot open a new pull request to apply changes based on the comments in this thread |
Fork cleanup: remove hardcoded CDN hostname from next.config.ts (now env-based via NEXT_PUBLIC_CDN_HOSTNAME), add comprehensive security scanning workflow with CodeQL, Gitleaks, and Trivy. Also includes Dockerfile improvements and opm.js executable bit fix. Co-Authored-By: Claude <noreply@anthropic.com> AI-Generated: true
Co-authored-by: russellbrenner <5236354+russellbrenner@users.noreply.github.com>
Merge all changes from upstream repository (CaviraOSS/OpenMemory) commit 30daf78: - Fix unterminated f-strings in google_slides.py (lines 77, 91) - Enhanced MCP integration with temporal fact support (contextual/factual/unified queries) - Add temporal graph user_id scoping for multi-tenant support - Improve Postgres and Valkey vector store implementations - Update LangChain integration with better async/await handling - Bump versions: openmemory-py 1.3.2, openmemory-js 1.3.3 - Database improvements including better query handling - Enhanced documentation in docstrings Files updated: 20 (10 Python, 10 TypeScript/JavaScript) Total changes: +404 -168 lines Co-authored-by: russellbrenner <5236354+russellbrenner@users.noreply.github.com>
Fix incorrect parameter names when calling query_facts_at_time: - Change 'obj' to 'subject_object' to match function signature - Change 'at_time' to 'at' to match function signature This fixes a bug introduced in the upstream code. Co-authored-by: russellbrenner <5236354+russellbrenner@users.noreply.github.com>
Fix CI: Add missing pytest dev dependencies to Python SDK
Merge upstream changes from CaviraOSS/OpenMemory (30daf78)
…ent plan - Fixed authentication bypass vulnerability with clear warnings - Fixed error message information leakage in all routes - Added GitHub webhook signature verification - Updated SECURITY.md with new security features - Added GITHUB_WEBHOOK_SECRET to .env.example - Created comprehensive IMPROVEMENT_PLAN.md with all findings Co-authored-by: russellbrenner <5236354+russellbrenner@users.noreply.github.com>
- Import crypto at module level for best practices - Require raw body for signature verification (no fallbacks) - Add explicit error handling if raw body unavailable - Improve variable naming for clarity (d -> rawBody) - Ensure byte-for-byte signature accuracy Co-authored-by: russellbrenner <5236354+russellbrenner@users.noreply.github.com>
- Created SECURITY_FIXES_SUMMARY.md with detailed findings - Documented all vulnerabilities and fixes - Added production deployment checklist - Confirmed 0 CodeQL alerts - TypeScript compilation successful Co-authored-by: russellbrenner <5236354+russellbrenner@users.noreply.github.com>
Co-authored-by: russellbrenner <5236354+russellbrenner@users.noreply.github.com>
Co-authored-by: russellbrenner <5236354+russellbrenner@users.noreply.github.com>
Co-authored-by: russellbrenner <5236354+russellbrenner@users.noreply.github.com>
…plan Rewrite IMPROVEMENT_PLAN.md into a repo-grounded implementation roadmap with parallel execution lanes
Co-authored-by: russellbrenner <5236354+russellbrenner@users.noreply.github.com>
…omments Co-authored-by: russellbrenner <5236354+russellbrenner@users.noreply.github.com>
Co-authored-by: russellbrenner <5236354+russellbrenner@users.noreply.github.com>
Co-authored-by: russellbrenner <5236354+russellbrenner@users.noreply.github.com>
Co-authored-by: russellbrenner <5236354+russellbrenner@users.noreply.github.com>
Co-authored-by: russellbrenner <5236354+russellbrenner@users.noreply.github.com>
…vement Execute next improvement-plan phase with minimal Phase 2 document metadata quick wins
A1.1: Fix noisy auth warnings - Auth disabled warnings now log once at startup, not per-request - Added auth_warning_logged flag to prevent log spam B2.1: Remove duplicate index creation - Removed duplicate openmemory_stats_type_idx in Postgres init - Removed duplicate idx_edges_validity in SQLite init Also updated IMPROVEMENT_PLAN.md with comprehensive Phase 0/1 review findings including code quality assessment and remediation workstream. Co-Authored-By: Claude <noreply@anthropic.com> AI-Generated: true
Implements structured logging and metrics for background tasks (decay, prune, reflect, user_summary). Operators can now monitor task health via /dashboard/tasks endpoint showing run counts, success/failure rates, durations, and recent errors. Co-Authored-By: Claude <noreply@anthropic.com> AI-Generated: true
Implements append-only audit logging for memory mutations. Tracks create, update, delete, reinforce, and ingest actions with actor info, timestamps, changes, and metadata. Query via /audit/logs, /audit/stats, and /audit/resource/:type/:id endpoints. Co-Authored-By: Claude <noreply@anthropic.com> AI-Generated: true
Implements version history tracking for memories. Features: - Auto-save version before content updates - Line-based diff computation with change classification - Version history, retrieval, comparison, and restore endpoints - /memory/:id/versions, /version, /diff/*, /restore/:version APIs Co-Authored-By: Claude <noreply@anthropic.com> AI-Generated: true
nate-mcneil
left a comment
There was a problem hiding this comment.
This PR is titled as Claude Code hooks but includes 96 changed files across the entire codebase -- hooks, dashboard, security hardening, auth middleware, config, CI workflows, and more. This should be broken into separate PRs. Reviewing the hooks and migration script (the stated scope) plus flagging issues in the rest.
Major concerns:
-
PR scope: 17k+ lines across 96 files is unreviewable as a single unit. The hooks, security hardening, dashboard changes, and auth middleware are all independent concerns.
-
Stop hook shell injection: In
openmemory-session-stop.shline 45,$project_nameis interpolated directly into the JSONtagsarray without escaping. A directory namedfoo","injectedwould break the JSON or inject arbitrary tags. Use jq to build the entire payload. -
Stop hook transcript parsing is fragile: Line 29-30 pipes
tail -50of the transcript throughjqwithselect(.type == "assistant"), but the transcript file is not necessarily newline-delimited JSON. If the format is a JSON array or changes between Claude versions, this silently produces nothing or garbage. -
Migration script has no idempotency:
import-claude-mem.jshas no deduplication. Running it twice imports everything twice. Should check for existing memories with matchingoriginal_idin metadata before inserting. -
Migration script has no rate limiting: It fires sequential fetch calls as fast as possible for potentially thousands of records. Should add a small delay or batch size to avoid overwhelming the server.
-
Auth bypass on user_id checks: In
memory.ts, the user_id authorization checks (lines 153, 226, 260) only enforce ownership if the caller provides a user_id. If omitted, the check is skipped entirely -- any unauthenticated caller can read/modify/delete any memory by simply not sending user_id. This is not new code but worth flagging since the PR adds auth hardening elsewhere.
| \"content\": $escaped_summary, | ||
| \"user_id\": \"claude-session\", | ||
| \"tags\": [\"session\", \"project:$project_name\"] | ||
| }" > /dev/null 2>&1 || true # Ignore errors |
There was a problem hiding this comment.
$project_name is interpolated raw into the JSON tags array. A directory with quotes or special characters in the name will break the JSON or inject arbitrary tags. Build the entire curl payload with jq instead of string interpolation.
| # Extract recent assistant messages from transcript for summary | ||
| if [ -f "$transcript_path" ]; then | ||
| # Get last few assistant messages to summarise session | ||
| recent_content=$(tail -50 "$transcript_path" 2>/dev/null | \ |
There was a problem hiding this comment.
This assumes the transcript is newline-delimited JSON objects. If the transcript format is a JSON array or changes between Claude Code versions, this silently produces no summary. Worth documenting the expected format or adding a fallback.
| } | ||
|
|
||
| try { | ||
| const response = await fetch(`${OPENMEMORY_URL}/memory/add`, { |
There was a problem hiding this comment.
No deduplication -- running this script twice imports everything twice. Consider checking for existing memories with matching metadata.original_id before inserting, or at minimum document that it's not idempotent.
| response=$(curl -s --connect-timeout 2 --max-time "$TIMEOUT_SECS" \ | ||
| -X POST "$OPENMEMORY_URL/memory/query" \ | ||
| -H "Content-Type: application/json" \ | ||
| -d "{\"query\": \"project $project_name\", \"k\": 5}" 2>/dev/null) |
There was a problem hiding this comment.
No API key / auth header is sent with the curl requests. If the user has OM_API_KEY configured (which this same PR encourages), both hooks will get 401s and silently fail. Should pass OM_API_KEY via x-api-key header when the env var is set.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 91 out of 96 changed files in this pull request and generated 14 comments.
Files not reviewed (1)
- packages/openmemory-js/package-lock.json: Language not supported
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| } { | ||
| const context = QueryAnalyzer.analyze(query) | ||
|
|
||
| let strategy: 'broad' | 'focused' | 'deep' | 'temporal' = 'focused' |
There was a problem hiding this comment.
The initial value of strategy is unused, since it is always overwritten.
| const context = QueryAnalyzer.analyze(query) | ||
|
|
||
| let strategy: 'broad' | 'focused' | 'deep' | 'temporal' = 'focused' | ||
| let recommendedCount = 10 |
There was a problem hiding this comment.
The initial value of recommendedCount is unused, since it is always overwritten.
| const [messages, setMessages] = useState<ChatMessage[]>([]) | ||
| const [input, setInput] = useState("") | ||
| const [busy, setBusy] = useState(false) | ||
| const [connecting, setConnecting] = useState(false) |
There was a problem hiding this comment.
Unused variable setConnecting.
| const [connecting, setConnecting] = useState(false) | |
| const [connecting] = useState(false) |
| const [riskmems, setriskmems] = useState<memory[]>([]) | ||
| const [loading, setloading] = useState(true) | ||
| const [error, seterror] = useState<string | null>(null) | ||
| const [dashstats, setdashstats] = useState<any>(null) |
There was a problem hiding this comment.
Unused variable dashstats.
| const [dashstats, setdashstats] = useState<any>(null) |
| const [qpsData, setQpsData] = useState<any[]>([]) | ||
| const [healthMetrics, setHealthMetrics] = useState<any>({}) | ||
| const [logs, setLogs] = useState<any[]>([]) | ||
| const [topUsers, setTopUsers] = useState<any[]>([]) |
There was a problem hiding this comment.
Unused variable topUsers.
| const [topUsers, setTopUsers] = useState<any[]>([]) |
|
|
||
| if (contextSegments.length > 0) { | ||
| for (const seg of contextSegments) { | ||
| const sources = seg.sources.map(id => `[${id.slice(0, 8)}]`).join(' ') |
There was a problem hiding this comment.
Unused variable sources.
| const idealPercentage = 1 / sectors.length | ||
|
|
||
| let balance = 0 | ||
| for (const [sector, count] of sectors) { |
There was a problem hiding this comment.
Unused variable sector.
| const connectivity = node ? node.linkedMemories.length / Math.max(1, allMemories.length) : 0 | ||
|
|
||
| const tokens = TextProcessor.tokenize(memory.content) | ||
| const allTokens = new Set(allMemories.flatMap(m => TextProcessor.tokenize(m.content))) |
There was a problem hiding this comment.
Unused variable allTokens.
| // Set up environment before importing audit module | ||
| process.env.OM_EMBEDDINGS = "synthetic"; | ||
|
|
||
| import { run_async, all_async, get_async } from "../src/core/db"; |
There was a problem hiding this comment.
Unused imports all_async, get_async.
| // Set up environment before importing | ||
| process.env.OM_EMBEDDINGS = "synthetic"; | ||
|
|
||
| import { run_async, all_async, q } from "../src/core/db"; |
There was a problem hiding this comment.
Unused imports all_async, q.
Word-level diff with automatic categorisation of changes into financial, date, party, legal_term, and general categories. Severity calculation based on change type and volume. Generates HTML redline markup with data attributes. Co-Authored-By: Claude <noreply@anthropic.com> AI-Generated: true
Extracts citations from text using pattern matching for legal (case law, legislation), academic (author-date, footnotes), and URL references. Stores citations with edges to source memories for reverse-lookup. Co-Authored-By: Claude <noreply@anthropic.com> AI-Generated: true
Schema-driven extraction for document types: agreement, contract, invoice, legal_filing, correspondence. Validates with zod, supports merge with existing metadata. Extracts parties, dates, amounts, jurisdiction, etc. Co-Authored-By: Claude <noreply@anthropic.com> AI-Generated: true
Segments documents into clauses with type detection (definition, obligation, confidentiality, termination, etc.). Stores clause embeddings for similarity search across documents. Detects various numbering formats. Co-Authored-By: Claude <noreply@anthropic.com> AI-Generated: true
CRUD for document templates with typed variables (string, number, date,
boolean, select, list). Auto-extracts variables from {{var:type}} syntax.
Instantiation validates and substitutes variables, creating memory entries.
Co-Authored-By: Claude <noreply@anthropic.com>
AI-Generated: true
C2: Add scripts/check-sector-parity.ts to verify TS/Python config sync D7: Implement compliance rules engine with 7 rule types: - required_clause: Ensure specific clauses are present - prohibited_term: Flag forbidden terms with context - required_field: Validate metadata field presence - pattern_match: Custom regex matching (must/must-not) - field_format: Validate field format with regex - word_count: Enforce min/max word limits - date_range: Validate dates within bounds Includes rule sets for grouping rules, full CRUD API, and audit logging. Co-Authored-By: Claude <noreply@anthropic.com> AI-Generated: true
Created project-level CLAUDE.md for future Claude Code sessions with build commands, architecture overview, and sector/scoring documentation. Added comprehensive 48-hour code review identifying 2 critical bugs and 5 improvement opportunities across new document intelligence features. Co-Authored-By: Claude <noreply@anthropic.com> AI-Generated: true
Critical fixes: - Fix operator precedence bug in structured_extraction.ts:269 - Fix prohibited_term to report all matches in compliance.ts High priority: - Batch embedding calls in clause_similarity store_clauses() - Add ReDoS protection for user-provided regex patterns Medium priority: - Standardise UUID generation on rid() across codebase - Add version pruning to prevent unbounded growth Co-Authored-By: Claude <noreply@anthropic.com> AI-Generated: true
Replace hardcoded 'memories' table references with the dynamic memories_table variable to support PostgreSQL deployments where the table is named "public"."openmemory_memories". Fixed files: - src/ops/dynamics.ts: 3 queries for salience retrieval and decay - src/memory/decay.ts: 1 SELECT, 2 UPDATE queries for summary updates - src/server/routes/system.ts: 1 SELECT for sector statistics Discovered during integration testing against the test k3s deployment. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> AI-Generated: true
- audit.ts: Extend resource_type to include compliance_rule, rule_set, template - clause_similarity.ts: Fix VectorStore method names (storeVector, searchSimilar) - structured_extraction.ts: Fix TypeScript doc_type spread conflict - embed.ts: Add embed_advanced() batch function for clause embeddings - audit routes: Accept new resource types in validation - templates.ts: Fix add_memory signature to match add_hsg_memory Co-Authored-By: Claude <noreply@anthropic.com> AI-Generated: true
Add comprehensive analysis of Redis/Valkey caching layer requirements. Conclusion: not needed at current scale, but architecture is ready. Also add gitignore pattern for auto-generated claude-mem context files in subdirectories while preserving root CLAUDE.md. Co-Authored-By: Claude <noreply@anthropic.com> AI-Generated: true
Summary
Adds Claude Code integration via hooks and a migration script for users coming from claude-mem.
Both hooks fail silently with 3-second timeouts when OpenMemory isn't running.
Disclosure
This PR was authored with assistance from Claude (Anthropic). The hooks and documentation were developed iteratively based on actual usage. Happy to adjust anything to fit the project's direction.
Files
Test plan
🤖 Generated with Claude Code