Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
150df22
docs: add multi-domain memories specification (SPEC-2025-12-25-001)
zircote Dec 26, 2025
2ae98bd
docs: add LLM subconsciousness specification (Issue #11)
zircote Dec 26, 2025
a0458f2
docs: initialize implementation tracking for multi-domain memories
zircote Dec 26, 2025
c9896fa
feat(config): add Domain enum and user memory path functions
zircote Dec 26, 2025
71bf5ad
feat(models): add domain field to Memory dataclass (Task 1.2)
zircote Dec 26, 2025
35ce5ad
feat(index): add schema migration v3 and domain support (Tasks 1.3, 1.4)
zircote Dec 26, 2025
2b191ac
feat(storage): implement Phase 2 storage layer for multi-domain (Task…
zircote Dec 26, 2025
08d2300
docs: update progress tracking for Phase 2 completion
zircote Dec 26, 2025
873b759
fix: apply code review remediation for security and performance
zircote Dec 26, 2025
04df87e
fix: resolve remaining lint warnings in test files
zircote Dec 26, 2025
b66259f
feat(capture): integrate secrets filtering in capture pipeline
zircote Dec 26, 2025
6b961b4
feat: Observability instrumentation and distributed tracing (#24)
zircote Dec 26, 2025
71b858b
fix: resolve SQLite connection leaks and domain parameter issue
zircote Dec 26, 2025
fc5394e
refactor: add warning logging for batch note fetch fallback
zircote Dec 26, 2025
a75633f
docs: add comprehensive environment variables reference
zircote Dec 26, 2025
b9194c4
feat(hooks): add security scanning and fix hook paths
zircote Dec 26, 2025
6019ea4
docs: add CHANGELOG entry for v0.12.0
zircote Dec 26, 2025
efcf493
docs: add deep-clean code review artifacts
zircote Dec 26, 2025
456c093
feat(hooks): add proactive memory search guidance to session templates
zircote Dec 26, 2025
6425c28
fix(plugin): correct marketplace.json for local testing
zircote Dec 26, 2025
9a29049
chore(release): bump version 0.12.0 → 1.0.0
zircote Dec 26, 2025
ebe8242
chore: add PROGRESS.md for implementation tracking
zircote Dec 26, 2025
f6063be
feat(subconsciousness): add LLM-powered memory management layer
zircote Dec 26, 2025
581177d
ci: add version branch triggers to workflow
zircote Dec 26, 2025
9d74f44
fix: address code review feedback
zircote Dec 26, 2025
7f34c9b
chore(spec): close out SPEC-2025-12-25-001 (LLM Subconsciousness)
zircote Dec 26, 2025
d836af1
fix: address code review remediation findings (97 total)
zircote Dec 26, 2025
6f6d23f
test: add tests for code review remediation
zircote Dec 26, 2025
6bc8084
docs: update code review findings and remediation tracking
zircote Dec 26, 2025
7b10ba6
chore: update plugin and config files
zircote Dec 26, 2025
cf8714b
fix: resolve OTEL collector duplicate label conflict
zircote Dec 26, 2025
67a8ab6
feat(observability): add OTLP log export to Loki
zircote Dec 26, 2025
b3d9306
feat: add Grafana dashboards for traces and logs
zircote Dec 26, 2025
7a0ee7f
docs: update observability documentation and tooling
zircote Dec 26, 2025
446189b
feat(observability): add real-time metrics flushing to OTLP
zircote Dec 26, 2025
cf64ea9
fix: use sync_notes_with_remote in stop hook for multi-worktree safety
zircote Dec 26, 2025
19c1f3f
docs: update spec status to completed with PR link
zircote Dec 26, 2025
b03a2a9
fix: remove PLUGIN_ROOT path resolution from all commands
zircote Dec 26, 2025
35f54de
docs: add spec for PLUGIN_ROOT path resolution fix
zircote Dec 26, 2025
ec35840
fix: restore --directory flag for uv run commands
zircote Dec 26, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 5 additions & 12 deletions .claude-plugin/marketplace.json
Original file line number Diff line number Diff line change
@@ -1,25 +1,18 @@
{
"name": "git-notes-memory",
"description": "Plugin marketplace for git-notes-memory - Git-native, semantically-searchable memory storage for Claude Code",
"version": "1.0.0",
"owner": {
"name": "zircote",
"email": "zircote@gmail.com"
},
"metadata": {
"description": "Local development marketplace for git-notes-memory plugin"
},
"plugins": [
{
"name": "memory-capture",
"description": "Git-backed memory system for Claude Code. Captures decisions, learnings, and context as git notes with semantic search and automatic recall.",
"version": "0.12.0",
"author": {
"name": "Robert Allen",
"email": "zircote@gmail.com"
},
"repository": "https://github.com/zircote/git-notes-memory-manager",
"license": "MIT",
"keywords": ["memory", "git-notes", "semantic-search", "context", "recall"],
"source": "./",
"homepage": "https://github.com/zircote/git-notes-memory-manager#readme"
"version": "1.0.0",
"source": "./../"
}
]
}
6 changes: 2 additions & 4 deletions .claude-plugin/plugin.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "memory-capture",
"version": "0.12.0",
"version": "1.0.0",
"description": "Git-backed memory system for Claude Code. Captures decisions, learnings, and context as git notes with semantic search and automatic recall.",
"author": {
"name": "Robert Allen",
Expand All @@ -15,7 +15,5 @@
"semantic-search",
"context",
"recall"
],
"commands": "./commands/",
"skills": "./skills/"
]
}
27 changes: 19 additions & 8 deletions .claude/hooks.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,44 +3,55 @@
"hooks": [
{
"name": "format-on-edit",
"description": "Run ruff format on edited Python files",
"description": "Auto-format Python files with ruff (matches CI: ruff format)",
"event": "PostToolUse",
"matcher": "Write|Edit|str_replace_editor",
"type": "command",
"command": "cd /Users/AllenR1_1/Projects/zircote/git-notes-memory-manager && uv run ruff format $CLAUDE_FILE_PATHS 2>/dev/null || true",
"command": "cd \"$CLAUDE_PROJECT_DIR\" && uv run ruff format $CLAUDE_FILE_PATHS 2>/dev/null || true",
"conditions": {
"fileExtensions": [".py", ".pyi"]
}
},
{
"name": "lint-check-on-edit",
"description": "Check for lint errors after Python edits",
"description": "Check lint errors with ruff (matches CI: ruff check src/ tests/)",
"event": "PostToolUse",
"matcher": "Write|Edit|str_replace_editor",
"type": "command",
"command": "cd /Users/AllenR1_1/Projects/zircote/git-notes-memory-manager && uv run ruff check $CLAUDE_FILE_PATHS --output-format=concise 2>/dev/null | head -20 || true",
"command": "cd \"$CLAUDE_PROJECT_DIR\" && uv run ruff check $CLAUDE_FILE_PATHS --output-format=concise 2>/dev/null | head -20 || true",
"conditions": {
"fileExtensions": [".py", ".pyi"]
}
},
{
"name": "typecheck-on-edit",
"description": "Run mypy on edited files to catch type errors",
"description": "Type check with mypy strict mode (matches CI: mypy src/)",
"event": "PostToolUse",
"matcher": "Write|Edit|str_replace_editor",
"type": "command",
"command": "cd /Users/AllenR1_1/Projects/zircote/git-notes-memory-manager && uv run mypy $CLAUDE_FILE_PATHS 2>&1 | grep -E 'error:' | head -10 || true",
"command": "cd \"$CLAUDE_PROJECT_DIR\" && uv run mypy $CLAUDE_FILE_PATHS 2>&1 | grep -E 'error:' | head -10 || true",
"conditions": {
"fileExtensions": [".py", ".pyi"]
}
},
{
"name": "security-scan-on-edit",
"description": "Security scan with bandit (matches CI: bandit -r src/ -ll)",
"event": "PostToolUse",
"matcher": "Write|Edit|str_replace_editor",
"type": "command",
"command": "cd \"$CLAUDE_PROJECT_DIR\" && uv run bandit -ll $CLAUDE_FILE_PATHS 2>/dev/null | grep -E '^>>' | head -10 || true",
"conditions": {
"fileExtensions": [".py"]
}
},
{
"name": "pre-commit-quality-gate",
"description": "Full quality check before git commits",
"description": "Full CI quality check before git commits (format, lint, typecheck, security, tests)",
"event": "PreToolUse",
"matcher": "Bash",
"type": "command",
"command": "if echo \"$CLAUDE_TOOL_INPUT\" | grep -qE 'git commit'; then cd /Users/AllenR1_1/Projects/zircote/git-notes-memory-manager && make quality; fi",
"command": "if echo \"$CLAUDE_TOOL_INPUT\" | grep -qE 'git commit'; then cd \"$CLAUDE_PROJECT_DIR\" && make quality; fi",
"blocking": true
}
]
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@ name: CI

on:
push:
branches: [main, develop]
branches: [main, develop, 'v*']
pull_request:
branches: [main, develop]
branches: [main, develop, 'v*']

concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
Expand Down
23 changes: 22 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,26 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Added
- Add GitHub release creation to Makefile release workflow

## [0.12.0] - 2025-12-26

### Added
- Observability instrumentation: metrics collection, distributed tracing, structured logging (closes #10)
- CLI commands: `/memory:health`, `/memory:metrics`, `/memory:traces` for observability
- Secrets filtering and sensitive data protection subsystem (closes #12)
- Custom PII detection: SSN, credit cards (with Luhn validation), phone numbers
- Four filtering strategies: REDACT, MASK, BLOCK, WARN
- SOC2/GDPR compliant audit logging with rotation
- CLI commands: `/memory:scan-secrets`, `/memory:secrets-allowlist`, `/memory:test-secret`, `/memory:audit-log`

### Fixed
- Fix SQLite connection leak on index initialization failure (CRIT-001)
- Fix SQLite connection cleanup in session start hook (MED-001)
- Add warning logging for batch note fetch fallback (LOW-010)

### Changed
- Add composite index for status+timestamp queries (LOW-004)
- Add logging import to git_ops module

## [0.11.0] - 2025-12-25

### Added
Expand Down Expand Up @@ -157,7 +177,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Fixed
- Initial stable release with core memory capture functionality

[Unreleased]: https://github.com/zircote/git-notes-memory/compare/v0.11.0...HEAD
[Unreleased]: https://github.com/zircote/git-notes-memory/compare/v0.12.0...HEAD
[0.12.0]: https://github.com/zircote/git-notes-memory/compare/v0.11.0...v0.12.0
[0.11.0]: https://github.com/zircote/git-notes-memory/compare/v0.10.0...v0.11.0
[0.10.0]: https://github.com/zircote/git-notes-memory/compare/v0.9.1...v0.10.0
[0.9.1]: https://github.com/zircote/git-notes-memory/compare/v0.9.0...v0.9.1
Expand Down
108 changes: 94 additions & 14 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,25 @@ Recall:
→ Memory objects with distance scores
```

### Multi-Domain Memory Storage

Memories are organized into two domains:

| Domain | Scope | Storage Location | Use Case |
|--------|-------|------------------|----------|
| **PROJECT** | Repo-scoped | `refs/notes/mem/{namespace}` in project repo | Project-specific decisions, progress, learnings |
| **USER** | Global, cross-project | `~/.local/share/memory-plugin/user-memories/` (bare repo) | Patterns, preferences, cross-project learnings |

**Domain Selection:**
- Default captures go to PROJECT domain (repo-scoped)
- Use `[global]` or `[user]` inline markers for USER domain
- Use domain prefix in blocks: `global:decision`, `user:learned`
- Commands support `--domain=all|user|project` filter

**Memory ID Format:**
- Project: `{namespace}:{commit_sha[:7]}:{index}` (e.g., `decisions:abc1234:0`)
- User: `user:{namespace}:{commit_sha[:7]}:{index}` (e.g., `user:learnings:def5678:0`)

### Git Notes Storage

Memories are stored under `refs/notes/mem/{namespace}` where namespace is one of:
Expand All @@ -90,6 +109,7 @@ timestamp: 2024-01-15T10:30:00Z
summary: Use PostgreSQL for persistence
spec: my-project
tags: [database, architecture]
domain: project # or "user" for global memories
---
## Context
...
Expand Down Expand Up @@ -157,10 +177,11 @@ Content → PIIDetector → DetectSecretsAdapter → Deduplicate → AllowlistCh
### Models

All models are immutable (`@dataclass(frozen=True)`):
- `Memory` - Core entity with id format `{namespace}:{commit_sha}:{index}`
- `MemoryResult` - Memory + distance score from vector search
- `Memory` - Core entity with id format `{namespace}:{commit_sha}:{index}` or `user:{namespace}:{commit_sha}:{index}`
- `MemoryResult` - Memory + distance score from vector search + domain
- `CaptureResult` - Operation result with success/warning status
- `HydrationLevel` - SUMMARY → FULL → FILES progressive loading
- `Domain` - Enum: `PROJECT` (repo-scoped) or `USER` (global, cross-project)

### Claude Code Plugin Integration

Expand Down Expand Up @@ -208,15 +229,18 @@ def capture_service(tmp_path, monkeypatch):
|----------|-------------|---------|
| `HOOK_ENABLED` | Master switch for all hooks | `true` |
| `HOOK_SESSION_START_ENABLED` | Enable SessionStart context injection | `true` |
| `HOOK_SESSION_START_FETCH_REMOTE` | Fetch notes from remote on session start | `false` |
| `HOOK_SESSION_START_FETCH_REMOTE` | Fetch project notes from remote on session start | `false` |
| `HOOK_SESSION_START_FETCH_USER_REMOTE` | Fetch user memories from remote on session start | `false` |
| `HOOK_USER_PROMPT_ENABLED` | Enable capture marker detection | `false` |
| `HOOK_POST_TOOL_USE_ENABLED` | Enable file-contextual memory injection | `true` |
| `HOOK_PRE_COMPACT_ENABLED` | Enable auto-capture before compaction | `true` |
| `HOOK_STOP_ENABLED` | Enable Stop hook processing | `true` |
| `HOOK_STOP_PUSH_REMOTE` | Push notes to remote on session stop | `false` |
| `HOOK_STOP_PUSH_REMOTE` | Push project notes to remote on session stop | `false` |
| `HOOK_STOP_PUSH_USER_REMOTE` | Push user memories to remote on session stop | `false` |
| `HOOK_DEBUG` | Enable debug logging to stderr | `false` |
| `HOOK_SESSION_START_INCLUDE_GUIDANCE` | Include response guidance templates | `true` |
| `HOOK_SESSION_START_GUIDANCE_DETAIL` | Guidance level: minimal/standard/detailed | `standard` |
| `USER_MEMORIES_REMOTE` | Remote URL for user memories sync | (none) |

### Secrets Filtering Configuration

Expand All @@ -229,34 +253,79 @@ def capture_service(tmp_path, monkeypatch):
| `SECRETS_FILTER_AUDIT_ENABLED` | Enable audit logging | `true` |
| `SECRETS_FILTER_AUDIT_DIR` | Audit log directory | `~/.local/share/memory-plugin/audit/` |

### Observability Configuration (OTLP)

| Variable | Description | Default |
|----------|-------------|---------|
| `MEMORY_PLUGIN_OTLP_ENDPOINT` | OTLP HTTP endpoint (e.g., `http://localhost:4318`) | (none) |
| `MEMORY_PLUGIN_OTLP_ALLOW_INTERNAL` | Allow internal/localhost endpoints (SEC-H-001 SSRF override) | `false` |

To enable observability with the local Docker stack:

```bash
# Add to ~/.bashrc or ~/.zshrc for persistence
export MEMORY_PLUGIN_OTLP_ENDPOINT=http://localhost:4318
export MEMORY_PLUGIN_OTLP_ALLOW_INTERNAL=true
```

**Note**: The `ALLOW_INTERNAL` flag is required because the OTLP exporter has SSRF protection (SEC-H-001) that blocks localhost/private IPs by default. This is a security feature for production environments.

Start the observability stack with:
```bash
cd docker && docker compose up -d
```

Access dashboards:
- **Grafana**: http://localhost:3000 (admin/admin) - Memory Operations and Hook Performance dashboards
- **Prometheus**: http://localhost:9090 - Direct metrics queries
- **Tempo traces**: Access via Grafana → Explore → Tempo datasource (no direct web UI)

### Remote Sync (Team Collaboration)

For team environments where multiple developers share memories:

```bash
# Enable automatic sync with remote (opt-in)
export HOOK_SESSION_START_FETCH_REMOTE=true # Fetch from remote on session start
export HOOK_STOP_PUSH_REMOTE=true # Push to remote on session stop
# Project memories - sync with origin repository (opt-in)
export HOOK_SESSION_START_FETCH_REMOTE=true # Fetch project memories on session start
export HOOK_STOP_PUSH_REMOTE=true # Push project memories on session stop
```

With these enabled, memories are automatically synchronized with the origin repository:
With these enabled, project memories are automatically synchronized with the origin repository:
- **Session start**: Fetches and merges remote notes using `cat_sort_uniq` strategy
- **Session stop**: Pushes local notes to remote

Manual sync is always available via `/memory:sync --remote`.

### User Memory Remote Sync

For syncing global (user-level) memories across machines:

```bash
# Configure remote for user memories
export USER_MEMORIES_REMOTE=git@github.com:username/my-memories.git

# Enable automatic sync (opt-in)
export HOOK_SESSION_START_FETCH_USER_REMOTE=true # Fetch user memories on session start
export HOOK_STOP_PUSH_USER_REMOTE=true # Push user memories on session stop
```

User memories are stored in a bare git repo at `~/.local/share/memory-plugin/user-memories/` and can be synced to a personal remote repository for cross-machine access.

## Code Intelligence (LSP)

LSP hooks are configured in `.claude/hooks.json` for immediate feedback on Python edits.

### Installed Hooks

| Hook | Trigger | Action |
|------|---------|--------|
| `format-on-edit` | PostToolUse (Write/Edit) | Runs `ruff format` on changed files |
| `lint-check-on-edit` | PostToolUse (Write/Edit) | Runs `ruff check` on changed files |
| `typecheck-on-edit` | PostToolUse (Write/Edit) | Runs `mypy` on changed files |
| `pre-commit-quality-gate` | PreToolUse (git commit) | Runs full `make quality` before commit |
These hooks mirror the CI workflow (`.github/workflows/ci.yml`) to catch issues before push:

| Hook | Trigger | CI Equivalent | Action |
|------|---------|---------------|--------|
| `format-on-edit` | PostToolUse (Write/Edit) | `ruff format` | Auto-formats Python files |
| `lint-check-on-edit` | PostToolUse (Write/Edit) | `ruff check` | Reports lint violations |
| `typecheck-on-edit` | PostToolUse (Write/Edit) | `mypy src/` | Type checks with strict mode |
| `security-scan-on-edit` | PostToolUse (Write/Edit) | `bandit -r src/ -ll` | Scans for security issues |
| `pre-commit-quality-gate` | PreToolUse (git commit) | Full CI | Runs `make quality` (blocking) |

### Navigation & Understanding

Expand Down Expand Up @@ -286,6 +355,17 @@ LSP hooks are configured in `.claude/hooks.json` for immediate feedback on Pytho

## Completed Spec Projects

- `docs/spec/completed/2025-12-25-llm-subconsciousness/` - LLM-Powered Subconsciousness for Intelligent Memory Management
- Completed: 2025-12-26
- Outcome: success
- GitHub Issue: [#11](https://github.com/zircote/git-notes-memory/issues/11)
- GitHub PR: [#26](https://github.com/zircote/git-notes-memory/pull/26) (open, ready for merge)
- Features: Provider-agnostic LLM client (Anthropic/OpenAI/Ollama), implicit memory capture with confidence scoring, approval queue, hook integration
- Deliverables: Phases 1-2 completed (30/85 tasks), 134 tests with 87%+ coverage, 13 ADRs, security fix (command injection)
- Scope: LLM Foundation + Implicit Capture delivered; Phases 3-6 deferred (Semantic Linking, Memory Decay, Consolidation, Proactive Surfacing)
- Effort: ~14 hours (planned: ~80-100 hours, -86% under budget)
- Key docs: REQUIREMENTS.md, ARCHITECTURE.md, IMPLEMENTATION_PLAN.md, DECISIONS.md, RETROSPECTIVE.md, PROGRESS.md

- `docs/spec/completed/2025-12-25-observability-instrumentation/` - Observability Instrumentation
- Completed: 2025-12-26
- Outcome: success
Expand Down
7 changes: 2 additions & 5 deletions commands/audit-log.md
Original file line number Diff line number Diff line change
Expand Up @@ -92,14 +92,13 @@ Parse the following flags:
**Query with filters**:

```bash
PLUGIN_ROOT="${CLAUDE_PLUGIN_ROOT:-$(ls -d ~/.claude/plugins/cache/git-notes-memory/memory-capture/*/ 2>/dev/null | head -1)}"
SINCE="${SINCE:-}" # e.g., "24h"
NAMESPACE="${NAMESPACE:-}"
EVENT_TYPE="${EVENT_TYPE:-}"
JSON_OUTPUT="${JSON_OUTPUT:-false}"
LIMIT="${LIMIT:-50}"

uv run --directory "$PLUGIN_ROOT" python3 -c "
uv run --directory "${CLAUDE_PLUGIN_ROOT}" python3 -c "
import json
import sys
from datetime import UTC, datetime, timedelta
Expand Down Expand Up @@ -205,9 +204,7 @@ else:
**Show overall statistics**:

```bash
PLUGIN_ROOT="${CLAUDE_PLUGIN_ROOT:-$(ls -d ~/.claude/plugins/cache/git-notes-memory/memory-capture/*/ 2>/dev/null | head -1)}"

uv run --directory "$PLUGIN_ROOT" python3 -c "
uv run --directory "${CLAUDE_PLUGIN_ROOT}" python3 -c "
from git_notes_memory.security import get_audit_logger

logger = get_audit_logger()
Expand Down
5 changes: 2 additions & 3 deletions commands/capture.md
Original file line number Diff line number Diff line change
Expand Up @@ -87,8 +87,7 @@ If `$ARGUMENTS` is empty or very short (< 10 characters):
Use Bash to invoke the Python library:

```bash
PLUGIN_ROOT="${CLAUDE_PLUGIN_ROOT:-$(ls -d ~/.claude/plugins/cache/git-notes-memory/memory-capture/*/ 2>/dev/null | head -1)}"
uv run --directory "$PLUGIN_ROOT" python3 -c "
uv run --directory "${CLAUDE_PLUGIN_ROOT}" python3 -c "
from git_notes_memory import get_capture_service

capture = get_capture_service()
Expand Down Expand Up @@ -160,7 +159,7 @@ For structured captures, the library also provides:

If the capture fails:
1. Check if we're in a git repository: `git rev-parse --git-dir`
2. Check if the library is installed: `uv run python3 -c "import git_notes_memory"`
2. Check if the library is installed: `uv run --directory "${CLAUDE_PLUGIN_ROOT}" python3 -c "import git_notes_memory"`
3. Show helpful error message with recovery action

</error_handling>
Expand Down
Loading
Loading