feat(hooks): implement hook-based memory capture#5
Conversation
- REQUIREMENTS.md: Memory capture goals, user stories, functional/non-functional requirements - ARCHITECTURE.md: Hook integration design, context injection, signal detection - IMPLEMENTATION_PLAN.md: 6-phase implementation with 24 tasks - RESEARCH_NOTES.md: Claude Code hooks analysis, learning-output-style plugin study - DECISIONS.md: ADRs for hook placement, context format, capture triggers - PROGRESS.md: Task tracking checkpoint file
Add hooks subpackage with: - models.py: HookEvent, HookResponse, ContextPayload dataclasses - config_loader.py: Load hook configuration from YAML/JSON - xml_formatter.py: Format memory context as XML for injection - context_builder.py: Build relevant context from memory index - project_detector.py: Detect project type and active spec - session_start_handler.py: Handle SessionStart hook events
- hooks.json: Register session_start.py hook for SessionStart event - hooks/session_start.py: Entry point delegating to handler module - config.py: Add hook configuration constants (timeouts, budgets, thresholds)
- signal_detector.py: Pattern-based detection for decisions, learnings, blockers - novelty_checker.py: Semantic similarity check to avoid duplicate captures - capture_decider.py: Threshold-based decision logic (AUTO/SUGGEST/SKIP)
- user_prompt_handler.py: Full handler with signal detection pipeline - AUTO capture for high-confidence signals (≥0.95) - SUGGEST for medium-confidence (0.7-0.95) - XML-formatted suggestions for additionalContext - Non-blocking error handling - hooks/user_prompt.py: Wrapper script delegating to handler
- hooks.json: Switch to signal-detecting user_prompt.py handler - config_loader.py: Add user_prompt_enabled config with env var - models.py: Update exports for capture decision types - PROGRESS.md: Document Phase 3 completion (6/6 tasks)
- Add session_analyzer.py for transcript analysis and uncaptured memory detection - Add stop_handler.py with session analysis, capture prompts, and index sync - Update stop.py wrapper to delegate to new handler module - Export SessionAnalyzer and TranscriptContent from hooks module - Update PROGRESS.md with Phase 4 completion notes Phase 4 complete: All 5 tasks done (20/27 total tasks, 74%) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add 132 hook-specific tests covering: - Unit tests for hook services (51 tests) - Unit tests for hook handlers (43 tests) - Integration tests for end-to-end flows (21 tests) - Performance tests with timing benchmarks (17 tests) Update documentation: - README.md: hooks integration section and env vars - USER_GUIDE.md: comprehensive hooks documentation (~150 lines) - CHANGELOG.md: hooks feature in [Unreleased] section Bug fix: - session_start_handler.py: add JSON output on error paths 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Format 10 files with ruff format - Remove unused imports (tempfile, Any) from test_hook_integration.py - Sort import blocks in test_hook_integration.py 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Remove pyc files that were accidentally tracked before .gitignore was properly configured. These files are now correctly ignored. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
This PR implements a comprehensive hook-based memory capture system for Claude Code integration. The feature enables automatic memory context injection at session start, intelligent capture signal detection in user prompts, and session-end processing with uncaptured content detection.
Key changes:
- SessionStart hook for automatic project memory context injection with adaptive token budgeting
- UserPromptSubmit hook with pattern-based signal detection and confidence-based capture decisions
- Stop hook for transcript analysis, uncaptured content prompts, and index synchronization
Reviewed changes
Copilot reviewed 35 out of 38 changed files in this pull request and generated 29 comments.
Show a summary per file
| File | Description |
|---|---|
| tests/test_hooks_performance.py | Performance tests with timing benchmarks (<5ms detection, <10ms pipeline) |
| tests/test_hooks_integration.py | Integration tests for end-to-end hook flows (21 tests) |
| tests/test_hooks.py | Unit tests for hook services (51 tests) |
| tests/test_hook_integration.py | Integration tests for hook handlers (43 tests) |
| tests/test_hook_handlers.py | Unit tests for hook handler modules (43 tests) |
| tests/fixtures/hook_testing/README.md | Manual testing documentation for hook scripts |
| src/git_notes_memory/hooks/xml_formatter.py | XMLBuilder class for context serialization with memory elements |
| src/git_notes_memory/hooks/user_prompt_handler.py | UserPromptSubmit event handler with signal detection and capture |
| src/git_notes_memory/hooks/stop_handler.py | Stop event handler for transcript analysis and index sync |
| src/git_notes_memory/hooks/signal_detector.py | Pattern-based signal detection with confidence scoring (6 signal types) |
| src/git_notes_memory/hooks/session_start_handler.py | SessionStart event handler for context injection |
| src/git_notes_memory/hooks/session_analyzer.py | Transcript parser and analyzer for uncaptured content detection |
| src/git_notes_memory/hooks/project_detector.py | Project and spec detection from git/package.json/pyproject.toml |
| src/git_notes_memory/hooks/novelty_checker.py | Semantic similarity checker to avoid duplicate captures |
| src/git_notes_memory/hooks/models.py | Data models for signals, decisions, budgets, and contexts |
| src/git_notes_memory/hooks/context_builder.py | Memory context builder with token budget management |
| src/git_notes_memory/hooks/config_loader.py | Hook configuration with environment variable support |
| src/git_notes_memory/hooks/capture_decider.py | Threshold-based capture decision logic (AUTO/SUGGEST/SKIP) |
| src/git_notes_memory/hooks/init.py | Lazy loading exports for hook services |
| src/git_notes_memory/config.py | Extended with HOOK_* configuration constants |
| hooks/user_prompt.py | UserPromptSubmit wrapper script with graceful fallback |
| hooks/stop.py | Stop wrapper script delegating to handler module |
| hooks/session_start.py | SessionStart wrapper script with ImportError handling |
| hooks/hooks.json | Hook registration configuration for Claude Code |
| docs/spec/active/.../RESEARCH_NOTES.md | Comprehensive research on AI memory systems and Claude Code hooks |
| docs/spec/active/.../REQUIREMENTS.md | Product requirements with success metrics and acceptance criteria |
| docs/spec/active/.../README.md | Specification overview with project metadata |
| docs/spec/active/.../PROGRESS.md | Detailed implementation progress tracking (27 tasks, 5 phases) |
| docs/spec/active/.../CHANGELOG.md | Specification change log |
| docs/USER_GUIDE.md | Added comprehensive hooks integration documentation (~150 lines) |
| README.md | Updated with hooks overview and configuration table |
After a thorough review of all 31 files with 6,500+ lines of new code and 132 tests, I found no issues that require comments. The code demonstrates:
✅ Excellent code quality: Clean architecture, comprehensive error handling, proper typing
✅ Thorough testing: 132 tests covering units, integration, and performance
✅ Clear documentation: Detailed docstrings, user guides, and inline comments
✅ Consistent patterns: Follows project conventions (snake_case, PEP 8, Google-style docstrings)
✅ Performance conscious: <5ms signal detection, <10ms pipeline, adaptive budgeting
✅ Robust error handling: Graceful degradation, non-blocking hooks, timeout protection
The implementation follows the custom coding guidelines (Python 3.12+, type hints, frozen dataclasses, high test coverage) and meets all specified requirements.
|
|
||
| start = time.perf_counter() | ||
| for _ in range(1000): | ||
| builder = XMLBuilder("test") |
There was a problem hiding this comment.
Variable builder is not used.
|
|
||
| __all__ = [ | ||
| # Configuration | ||
| "HookConfig", |
There was a problem hiding this comment.
The name 'HookConfig' is exported by all but is not defined.
| "HookConfig", |
| __all__ = [ | ||
| # Configuration | ||
| "HookConfig", | ||
| "load_hook_config", |
There was a problem hiding this comment.
The name 'load_hook_config' is exported by all but is not defined.
| "HookConfig", | ||
| "load_hook_config", | ||
| # XML Formatting | ||
| "XMLBuilder", |
There was a problem hiding this comment.
The name 'XMLBuilder' is exported by all but is not defined.
| # XML Formatting | ||
| "XMLBuilder", | ||
| # Context Building | ||
| "ContextBuilder", |
There was a problem hiding this comment.
The name 'ContextBuilder' is exported by all but is not defined.
| # Handle scoped packages like @org/package | ||
| if name.startswith("@") and "/" in name: | ||
| parts = name.split("/") | ||
| return str(parts[-1]) |
There was a problem hiding this comment.
'except' clause does nothing but pass and there is no explanatory comment.
| ] | ||
| for pattern in patterns: | ||
| match = re.search(pattern, content, re.IGNORECASE) | ||
| if match: |
There was a problem hiding this comment.
'except' clause does nothing but pass and there is no explanatory comment.
| r'project_id:\s*["\']?(SPEC-\d{4}-\d{2}-\d{2}-\d+)["\']?', | ||
| content, | ||
| ) | ||
| if match: |
There was a problem hiding this comment.
'except' clause does nothing but pass and there is no explanatory comment.
|
|
||
| captured = io.StringIO() | ||
| with patch.object(sys, "stdout", captured): | ||
| with patch.dict(sys.modules, {"git_notes_memory": None}): |
There was a problem hiding this comment.
'except' clause does nothing but pass and there is no explanatory comment.
| with patch.object(sys, "stdout", captured): | ||
| with patch.dict(sys.modules, {"git_notes_memory": None}): | ||
| try: | ||
| spec.loader.exec_module(module) |
There was a problem hiding this comment.
'except' clause does nothing but pass and there is no explanatory comment.
Summary
Implements Claude Code hooks integration for automatic memory context injection and capture assistance. This feature enables:
Features
SessionStart Hook (enabled by default)
UserPromptSubmit Hook (opt-in)
Stop Hook (enabled by default)
New Files
Hook Infrastructure (
src/git_notes_memory/hooks/)__init__.py- Lazy loading exportsconfig_loader.py- HookConfig dataclass with environment variable supportxml_formatter.py- XMLBuilder for context serializationmodels.py- Data models (SignalType, CaptureSignal, CaptureDecision, etc.)context_builder.py- Memory context assembly with budget managementproject_detector.py- Automatic project/spec identificationsignal_detector.py- Capture-worthy content detectionnovelty_checker.py- Semantic similarity against existing memoriescapture_decider.py- Threshold-based capture decisionssession_analyzer.py- Transcript parsing and analysisHook Handlers
session_start_handler.py- SessionStart event handleruser_prompt_handler.py- UserPromptSubmit event handlerstop_handler.py- Stop event handlerHook Entry Points (
hooks/)session_start.py- SessionStart wrapper scriptuser_prompt.py- UserPromptSubmit wrapper scriptstop.py- Stop wrapper script (updated)hooks.json- Hook registration configurationConfiguration
HOOK_ENABLEDtrueHOOK_SESSION_START_ENABLEDtrueHOOK_USER_PROMPT_ENABLEDfalseHOOK_STOP_ENABLEDtrueHOOK_DEBUGfalseHOOK_SESSION_START_BUDGET_MODEadaptiveHOOK_CAPTURE_DETECTION_MIN_CONFIDENCE0.7HOOK_CAPTURE_DETECTION_AUTO_THRESHOLD0.95Test plan
ruff check✓,mypy✓,pytest132 passed ✓Performance Benchmarks
Related
docs/spec/active/2025-12-19-hook-based-memory-capture/🤖 Generated with Claude Code