118 lines
3.2 KiB
Python
118 lines
3.2 KiB
Python
#!/usr/bin/env python3
|
|
"""
|
|
PRISM Context Memory: File Change Capture Hook (Obsidian)
|
|
|
|
Automatically captures context when files are edited or created.
|
|
Invoked by PostToolUse:Edit and PostToolUse:Write hooks.
|
|
Uses Obsidian markdown storage.
|
|
"""
|
|
|
|
import sys
|
|
import io
|
|
import os
|
|
import json
|
|
from pathlib import Path
|
|
|
|
# Fix Windows console encoding for emoji support
|
|
if sys.stdout.encoding != 'utf-8':
|
|
sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8', errors='replace')
|
|
sys.stderr = io.TextIOWrapper(sys.stderr.buffer, encoding='utf-8', errors='replace')
|
|
|
|
# Add utils to path
|
|
PRISM_ROOT = Path(__file__).parent.parent
|
|
sys.path.insert(0, str(PRISM_ROOT / "skills" / "context-memory" / "utils"))
|
|
|
|
try:
|
|
from storage_obsidian import remember_file, get_vault_path
|
|
except ImportError:
|
|
# Memory system not initialized, skip silently
|
|
sys.exit(0)
|
|
|
|
|
|
def should_capture_file(file_path: str) -> bool:
|
|
"""Check if file should be captured in memory."""
|
|
|
|
# Skip if memory system not enabled
|
|
if os.environ.get("PRISM_MEMORY_AUTO_CAPTURE", "true").lower() != "true":
|
|
return False
|
|
|
|
# Skip certain file types
|
|
skip_extensions = [
|
|
'.md', '.txt', '.log', '.json', '.yaml', '.yml',
|
|
'.svg', '.png', '.jpg', '.jpeg', '.gif',
|
|
'.lock', '.sum', '.mod'
|
|
]
|
|
|
|
ext = os.path.splitext(file_path)[1].lower()
|
|
if ext in skip_extensions:
|
|
return False
|
|
|
|
# Skip certain directories
|
|
skip_dirs = [
|
|
'node_modules', '.git', 'dist', 'build', '__pycache__',
|
|
'.prism', 'vendor', 'target', 'PRISM-Memory'
|
|
]
|
|
|
|
path_parts = Path(file_path).parts
|
|
if any(skip_dir in path_parts for skip_dir in skip_dirs):
|
|
return False
|
|
|
|
# Only capture source code files
|
|
code_extensions = [
|
|
'.py', '.js', '.ts', '.jsx', '.tsx', '.rb', '.go',
|
|
'.rs', '.java', '.cs', '.cpp', '.c', '.h', '.hpp',
|
|
'.php', '.swift', '.kt'
|
|
]
|
|
|
|
return ext in code_extensions
|
|
|
|
|
|
def main():
|
|
"""
|
|
Capture file context from hook invocation.
|
|
|
|
Expected environment:
|
|
- TOOL_NAME: 'Edit' or 'Write'
|
|
- TOOL_PARAMS_file_path: Path to the file
|
|
"""
|
|
|
|
tool_name = os.environ.get("TOOL_NAME", "")
|
|
file_path = os.environ.get("TOOL_PARAMS_file_path", "")
|
|
|
|
if not file_path:
|
|
# Try alternative param names
|
|
file_path = os.environ.get("TOOL_PARAMS_path", "")
|
|
|
|
if not file_path:
|
|
sys.exit(0)
|
|
|
|
# Check if we should capture this file
|
|
if not should_capture_file(file_path):
|
|
sys.exit(0)
|
|
|
|
# Check if vault exists
|
|
try:
|
|
vault = get_vault_path()
|
|
if not vault.exists():
|
|
# Vault not initialized, skip
|
|
sys.exit(0)
|
|
except Exception:
|
|
sys.exit(0)
|
|
|
|
# Capture file context
|
|
try:
|
|
# Add note about how file was changed
|
|
note = f"Modified via {tool_name}" if tool_name else None
|
|
remember_file(file_path, note=note)
|
|
except Exception as e:
|
|
# Log error but don't block the workflow
|
|
error_log = PRISM_ROOT / ".prism-memory-log.txt"
|
|
with open(error_log, 'a') as f:
|
|
f.write(f"[{tool_name}] Error capturing {file_path}: {e}\n")
|
|
|
|
sys.exit(0)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|