feat: Provider-Agnostic Architecture - Memory Plugin ohne externe Abhängigkeiten #112

Closed
opened 2026-01-23 12:02:34 +00:00 by jack · 2 comments
Owner

Zusammenfassung

Refactoring von claude-mem zu einem provider-agnostischen Memory-Plugin, das ohne externe Services lauffähig ist (nur SQLite), flexible Provider-Wahl für AI, Embeddings und Vector-DB ermöglicht, und bestehende Patterns (Agent-Registry) konsequent anwendet.


Aktuelle Probleme

Harte Abhängigkeiten

Komponente Status Problem
LLM/AI Provider Gut Agent-Interface mit Registry vorhanden
Embedding Provider Hardcoded Xenova direkt in QdrantService
Vector Database Hardcoded Qdrant direkt referenziert
Database ⚠️ Teilweise Interface existiert, nur SQLite impl.

Installations-Hürden

Problem Auswirkung
Qdrant erforderlich Docker oder externer Service nötig
Xenova hardcoded Keine Alternative für Embeddings
Komplexe Ersteinrichtung Hohe Einstiegshürde für neue Nutzer

Ziel-Architektur

┌─────────────────────────────────────────────────────────────┐
│                    claude-mem / memory-plugin               │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  ┌─────────────┐  ┌─────────────┐  ┌─────────────┐         │
│  │ AI Provider │  │  Embedding  │  │  Vector DB  │         │
│  │  Registry   │  │   Registry  │  │   Registry  │         │
│  └──────┬──────┘  └──────┬──────┘  └──────┬──────┘         │
│         │                │                │                 │
│    ┌────┴────┐     ┌────┴────┐     ┌────┴────┐             │
│    │Anthropic│     │  Local  │     │  None   │ ← Default   │
│    │ Mistral │     │ Mistral │     │ Qdrant  │             │
│    │OpenRouter│    │ OpenAI  │     │SQLiteVec│             │
│    │ Gemini  │     │ Voyage  │     │Pinecone │             │
│    │ OpenAI  │     │ Cohere  │     └─────────┘             │
│    └─────────┘     └─────────┘                              │
│                                                             │
├─────────────────────────────────────────────────────────────┤
│                   SQLite (immer vorhanden)                  │
│              FTS5 Keyword-Search als Fallback               │
└─────────────────────────────────────────────────────────────┘

Implementierungsplan

Phase 1: Vector-DB Optional machen Quick Win

Ziel: claude-mem funktioniert ohne Qdrant

// settings.ts
VECTOR_DB: 'none' | 'qdrant' | 'sqlite-vec'  // Default: 'none'

Änderungen:

  • qdrant-sync Task nur erstellen wenn VECTOR_DB !== 'none'
  • Search-Route fällt zurück auf SQLite FTS5 (bereits vorhanden)
  • Worker-Capabilities dynamisch basierend auf Config

Phase 2: Embedding Provider Abstraktion

Neues Interface:

// packages/worker/src/embeddings/types.ts
export interface EmbeddingProvider {
  readonly name: string;
  readonly dimension: number;
  
  initialize(): Promise<void>;
  embed(texts: string[]): Promise<number[][]>;
  embedSingle(text: string): Promise<number[]>;
}

// packages/worker/src/embeddings/index.ts
const embeddingRegistry = new Map<string, EmbeddingProvider>();

export function getEmbeddingProvider(name?: string): EmbeddingProvider { ... }
export function registerEmbeddingProvider(name: string, provider: EmbeddingProvider): void { ... }

Implementierungen:

Provider Dimension API-Key nötig Notizen
local (Xenova) 384 Default, läuft lokal
mistral 1024 Text + Code Embeddings, gleicher API-Key wie AI
openai 1536/3072 text-embedding-3-small/large
voyage 1024 voyage-3
cohere 1024 embed-v3

💡 Mistral-Vorteil: Wer bereits AI_PROVIDER: 'mistral' nutzt, kann denselben API-Key für Embeddings verwenden - keine zusätzliche Konfiguration nötig!

Mistral Embedding-Modelle:

Modell Verwendung Besonderheit
mistral-embed Allgemeine Texte RAG, Clustering, Klassifikation
codestral-embed Code Code-Suche, Duplikat-Erkennung

Settings für Mistral:

{
  "EMBEDDING_PROVIDER": "mistral",
  "MISTRAL_EMBEDDING_MODEL": "mistral-embed",  // oder "codestral-embed"
  "MISTRAL_API_KEY": "..."  // Falls nicht bereits für AI gesetzt
}

Phase 3: Vector-DB Abstraktion

Neues Interface:

// packages/worker/src/vector-db/types.ts
export interface VectorDatabase {
  readonly name: string;
  
  initialize(): Promise<void>;
  upsert(documents: VectorDocument[]): Promise<void>;
  search(vector: number[], options: SearchOptions): Promise<SearchResult[]>;
  delete(ids: string[]): Promise<void>;
  deleteByFilter(filter: Record<string, unknown>): Promise<number>;
}

Implementierungen:

Provider Selbst-hosted Cloud Notizen
none - - Kein Vector-Search, nur FTS5
qdrant Aktuell implementiert
sqlite-vec - Embedded, keine externen Services
pinecone - Populärer Cloud-Service

Phase 4: AI Provider Erweiterung

Fehlende Implementierungen für das bestehende Agent-Interface:

  • openai-agent.ts - OpenAI GPT-4o
  • gemini-agent.ts - Google Gemini
  • openrouter-agent.ts - OpenRouter Proxy mit Multi-Model Fallback

Konfigurations-Profile

Minimal (Zero-Config)

{
  "AI_PROVIDER": "anthropic",
  "EMBEDDING_PROVIDER": "none",
  "VECTOR_DB": "none"
}

Local (Selbst-gehostet)

{
  "AI_PROVIDER": "anthropic",
  "EMBEDDING_PROVIDER": "local",
  "VECTOR_DB": "sqlite-vec"
}

Mistral All-in-One

{
  "AI_PROVIDER": "mistral",
  "EMBEDDING_PROVIDER": "mistral",
  "MISTRAL_EMBEDDING_MODEL": "mistral-embed",
  "VECTOR_DB": "qdrant",
  "MISTRAL_API_KEY": "..."
}

Ein API-Key für AI + Embeddings!

Cloud (Vollständig)

{
  "AI_PROVIDER": "openrouter",
  "EMBEDDING_PROVIDER": "voyage",
  "VECTOR_DB": "qdrant",
  "QDRANT_URL": "https://xxx.qdrant.cloud"
}

Betroffene Dateien

Neue Dateien

  • packages/worker/src/embeddings/types.ts
  • packages/worker/src/embeddings/index.ts
  • packages/worker/src/embeddings/local-provider.ts
  • packages/worker/src/embeddings/mistral-provider.ts
  • packages/worker/src/embeddings/openai-provider.ts
  • packages/worker/src/vector-db/types.ts
  • packages/worker/src/vector-db/index.ts
  • packages/worker/src/vector-db/none-provider.ts
  • packages/worker/src/vector-db/sqlite-vec-provider.ts
  • packages/worker/src/agents/openai-agent.ts
  • packages/worker/src/agents/openrouter-agent.ts

Zu refactoren

  • packages/worker/src/services/qdrant-service.tsvector-db/qdrant-provider.ts
  • packages/worker/src/handlers/qdrant-sync.ts - Nutzt VectorDatabase Interface
  • packages/shared/src/settings.ts - Neue Settings
  • packages/types/src/capabilities.ts - Neue Capabilities

Akzeptanzkriterien

  • claude-mem funktioniert ohne Qdrant (VECTOR_DB: 'none')
  • Embedding-Provider sind austauschbar via Registry
  • Vector-DB-Provider sind austauschbar via Registry
  • FTS5 Fallback funktioniert bei VECTOR_DB: 'none'
  • Mindestens 3 Embedding-Provider implementiert (local, mistral, openai)
  • Mindestens 2 Vector-DB-Provider implementiert (none, qdrant)
  • Mistral nutzt automatisch vorhandenen API-Key wenn AI_PROVIDER: 'mistral'
  • Alle AI-Provider (anthropic, mistral, openai, openrouter) verfügbar
  • Zero-Config Installation funktioniert
  • Dokumentation für Provider-Konfiguration

Risiken

Risiko Wahrscheinlichkeit Auswirkung Mitigation
Breaking Changes Mittel Hoch Careful Migration, alte Config-Keys weiterhin unterstützen
Performance-Unterschiede Mittel Mittel Benchmarks pro Provider, Dokumentation
API-Key-Management Niedrig Mittel Klare Settings-Dokumentation
sqlite-vec Kompatibilität Niedrig Mittel Fallback auf FTS5
Provider-Ausfall Niedrig Niedrig Graceful Degradation zu FTS5
Dimensions-Mismatch Mittel Hoch Provider-Wechsel erfordert Re-Embedding aller Dokumente

Geschätzter Aufwand

Phase Aufwand Priorität
Phase 1: Vector-DB optional 8-12h Hoch
Phase 2: Embedding Abstraktion 12-16h Mittel
Phase 3: Vector-DB Abstraktion 16-20h Mittel
Phase 4: AI Provider 18-24h Niedrig
Gesamt 54-72h

Migration bestehender Installationen

Szenario Aktion
Bestehende Qdrant-Nutzer Keine Änderung nötig, VECTOR_DB: 'qdrant' wird Default für existierende Configs
Neue Installationen VECTOR_DB: 'none' als Default
Upgrade auf sqlite-vec Dokumentierter Migrationspfad
Provider-Wechsel Re-Embedding aller Dokumente erforderlich (verschiedene Dimensionen)

Zusammengefasste Issues

Dieses Issue fasst zusammen und ersetzt:

  • #103 - Option to disable Chroma / SQLite-only backend mode
  • #32 - OpenRouter multi-model fallback configuration
  • #13 - Multi-model OpenRouter with automatic fallback

Referenzen

## Zusammenfassung Refactoring von claude-mem zu einem **provider-agnostischen Memory-Plugin**, das ohne externe Services lauffähig ist (nur SQLite), flexible Provider-Wahl für AI, Embeddings und Vector-DB ermöglicht, und bestehende Patterns (Agent-Registry) konsequent anwendet. --- ## Aktuelle Probleme ### Harte Abhängigkeiten | Komponente | Status | Problem | |------------|--------|---------| | **LLM/AI Provider** | ✅ Gut | Agent-Interface mit Registry vorhanden | | **Embedding Provider** | ❌ Hardcoded | Xenova direkt in QdrantService | | **Vector Database** | ❌ Hardcoded | Qdrant direkt referenziert | | **Database** | ⚠️ Teilweise | Interface existiert, nur SQLite impl. | ### Installations-Hürden | Problem | Auswirkung | |---------|------------| | Qdrant erforderlich | Docker oder externer Service nötig | | Xenova hardcoded | Keine Alternative für Embeddings | | Komplexe Ersteinrichtung | Hohe Einstiegshürde für neue Nutzer | --- ## Ziel-Architektur ``` ┌─────────────────────────────────────────────────────────────┐ │ claude-mem / memory-plugin │ ├─────────────────────────────────────────────────────────────┤ │ │ │ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │ │ │ AI Provider │ │ Embedding │ │ Vector DB │ │ │ │ Registry │ │ Registry │ │ Registry │ │ │ └──────┬──────┘ └──────┬──────┘ └──────┬──────┘ │ │ │ │ │ │ │ ┌────┴────┐ ┌────┴────┐ ┌────┴────┐ │ │ │Anthropic│ │ Local │ │ None │ ← Default │ │ │ Mistral │ │ Mistral │ │ Qdrant │ │ │ │OpenRouter│ │ OpenAI │ │SQLiteVec│ │ │ │ Gemini │ │ Voyage │ │Pinecone │ │ │ │ OpenAI │ │ Cohere │ └─────────┘ │ │ └─────────┘ └─────────┘ │ │ │ ├─────────────────────────────────────────────────────────────┤ │ SQLite (immer vorhanden) │ │ FTS5 Keyword-Search als Fallback │ └─────────────────────────────────────────────────────────────┘ ``` --- ## Implementierungsplan ### Phase 1: Vector-DB Optional machen ⭐ Quick Win **Ziel:** claude-mem funktioniert ohne Qdrant ```typescript // settings.ts VECTOR_DB: 'none' | 'qdrant' | 'sqlite-vec' // Default: 'none' ``` **Änderungen:** - [ ] `qdrant-sync` Task nur erstellen wenn `VECTOR_DB !== 'none'` - [ ] Search-Route fällt zurück auf SQLite FTS5 (bereits vorhanden) - [ ] Worker-Capabilities dynamisch basierend auf Config --- ### Phase 2: Embedding Provider Abstraktion **Neues Interface:** ```typescript // packages/worker/src/embeddings/types.ts export interface EmbeddingProvider { readonly name: string; readonly dimension: number; initialize(): Promise<void>; embed(texts: string[]): Promise<number[][]>; embedSingle(text: string): Promise<number[]>; } // packages/worker/src/embeddings/index.ts const embeddingRegistry = new Map<string, EmbeddingProvider>(); export function getEmbeddingProvider(name?: string): EmbeddingProvider { ... } export function registerEmbeddingProvider(name: string, provider: EmbeddingProvider): void { ... } ``` **Implementierungen:** | Provider | Dimension | API-Key nötig | Notizen | |----------|-----------|---------------|---------| | `local` (Xenova) | 384 | ❌ | Default, läuft lokal | | `mistral` | 1024 | ✅ | Text + Code Embeddings, **gleicher API-Key wie AI** | | `openai` | 1536/3072 | ✅ | text-embedding-3-small/large | | `voyage` | 1024 | ✅ | voyage-3 | | `cohere` | 1024 | ✅ | embed-v3 | > **💡 Mistral-Vorteil:** Wer bereits `AI_PROVIDER: 'mistral'` nutzt, kann denselben API-Key für Embeddings verwenden - keine zusätzliche Konfiguration nötig! **Mistral Embedding-Modelle:** | Modell | Verwendung | Besonderheit | |--------|------------|--------------| | `mistral-embed` | Allgemeine Texte | RAG, Clustering, Klassifikation | | `codestral-embed` | Code | Code-Suche, Duplikat-Erkennung | **Settings für Mistral:** ```json { "EMBEDDING_PROVIDER": "mistral", "MISTRAL_EMBEDDING_MODEL": "mistral-embed", // oder "codestral-embed" "MISTRAL_API_KEY": "..." // Falls nicht bereits für AI gesetzt } ``` --- ### Phase 3: Vector-DB Abstraktion **Neues Interface:** ```typescript // packages/worker/src/vector-db/types.ts export interface VectorDatabase { readonly name: string; initialize(): Promise<void>; upsert(documents: VectorDocument[]): Promise<void>; search(vector: number[], options: SearchOptions): Promise<SearchResult[]>; delete(ids: string[]): Promise<void>; deleteByFilter(filter: Record<string, unknown>): Promise<number>; } ``` **Implementierungen:** | Provider | Selbst-hosted | Cloud | Notizen | |----------|---------------|-------|---------| | `none` | - | - | Kein Vector-Search, nur FTS5 | | `qdrant` | ✅ | ✅ | Aktuell implementiert | | `sqlite-vec` | ✅ | - | Embedded, keine externen Services | | `pinecone` | - | ✅ | Populärer Cloud-Service | --- ### Phase 4: AI Provider Erweiterung Fehlende Implementierungen für das bestehende Agent-Interface: - [ ] `openai-agent.ts` - OpenAI GPT-4o - [ ] `gemini-agent.ts` - Google Gemini - [ ] `openrouter-agent.ts` - OpenRouter Proxy mit Multi-Model Fallback --- ## Konfigurations-Profile ### Minimal (Zero-Config) ```json { "AI_PROVIDER": "anthropic", "EMBEDDING_PROVIDER": "none", "VECTOR_DB": "none" } ``` ### Local (Selbst-gehostet) ```json { "AI_PROVIDER": "anthropic", "EMBEDDING_PROVIDER": "local", "VECTOR_DB": "sqlite-vec" } ``` ### Mistral All-in-One ⭐ ```json { "AI_PROVIDER": "mistral", "EMBEDDING_PROVIDER": "mistral", "MISTRAL_EMBEDDING_MODEL": "mistral-embed", "VECTOR_DB": "qdrant", "MISTRAL_API_KEY": "..." } ``` > Ein API-Key für AI + Embeddings! ### Cloud (Vollständig) ```json { "AI_PROVIDER": "openrouter", "EMBEDDING_PROVIDER": "voyage", "VECTOR_DB": "qdrant", "QDRANT_URL": "https://xxx.qdrant.cloud" } ``` --- ## Betroffene Dateien ### Neue Dateien - `packages/worker/src/embeddings/types.ts` - `packages/worker/src/embeddings/index.ts` - `packages/worker/src/embeddings/local-provider.ts` - `packages/worker/src/embeddings/mistral-provider.ts` - `packages/worker/src/embeddings/openai-provider.ts` - `packages/worker/src/vector-db/types.ts` - `packages/worker/src/vector-db/index.ts` - `packages/worker/src/vector-db/none-provider.ts` - `packages/worker/src/vector-db/sqlite-vec-provider.ts` - `packages/worker/src/agents/openai-agent.ts` - `packages/worker/src/agents/openrouter-agent.ts` ### Zu refactoren - `packages/worker/src/services/qdrant-service.ts` → `vector-db/qdrant-provider.ts` - `packages/worker/src/handlers/qdrant-sync.ts` - Nutzt VectorDatabase Interface - `packages/shared/src/settings.ts` - Neue Settings - `packages/types/src/capabilities.ts` - Neue Capabilities --- ## Akzeptanzkriterien - [ ] claude-mem funktioniert ohne Qdrant (`VECTOR_DB: 'none'`) - [ ] Embedding-Provider sind austauschbar via Registry - [ ] Vector-DB-Provider sind austauschbar via Registry - [ ] FTS5 Fallback funktioniert bei `VECTOR_DB: 'none'` - [ ] Mindestens 3 Embedding-Provider implementiert (local, mistral, openai) - [ ] Mindestens 2 Vector-DB-Provider implementiert (none, qdrant) - [ ] Mistral nutzt automatisch vorhandenen API-Key wenn `AI_PROVIDER: 'mistral'` - [ ] Alle AI-Provider (anthropic, mistral, openai, openrouter) verfügbar - [ ] Zero-Config Installation funktioniert - [ ] Dokumentation für Provider-Konfiguration --- ## Risiken | Risiko | Wahrscheinlichkeit | Auswirkung | Mitigation | |--------|-------------------|------------|------------| | **Breaking Changes** | Mittel | Hoch | Careful Migration, alte Config-Keys weiterhin unterstützen | | **Performance-Unterschiede** | Mittel | Mittel | Benchmarks pro Provider, Dokumentation | | **API-Key-Management** | Niedrig | Mittel | Klare Settings-Dokumentation | | **sqlite-vec Kompatibilität** | Niedrig | Mittel | Fallback auf FTS5 | | **Provider-Ausfall** | Niedrig | Niedrig | Graceful Degradation zu FTS5 | | **Dimensions-Mismatch** | Mittel | Hoch | Provider-Wechsel erfordert Re-Embedding aller Dokumente | --- ## Geschätzter Aufwand | Phase | Aufwand | Priorität | |-------|---------|-----------| | Phase 1: Vector-DB optional | 8-12h | ⭐ Hoch | | Phase 2: Embedding Abstraktion | 12-16h | Mittel | | Phase 3: Vector-DB Abstraktion | 16-20h | Mittel | | Phase 4: AI Provider | 18-24h | Niedrig | | **Gesamt** | **54-72h** | | --- ## Migration bestehender Installationen | Szenario | Aktion | |----------|--------| | Bestehende Qdrant-Nutzer | Keine Änderung nötig, `VECTOR_DB: 'qdrant'` wird Default für existierende Configs | | Neue Installationen | `VECTOR_DB: 'none'` als Default | | Upgrade auf sqlite-vec | Dokumentierter Migrationspfad | | Provider-Wechsel | Re-Embedding aller Dokumente erforderlich (verschiedene Dimensionen) | --- ## Zusammengefasste Issues Dieses Issue fasst zusammen und ersetzt: - #103 - Option to disable Chroma / SQLite-only backend mode - #32 - OpenRouter multi-model fallback configuration - #13 - Multi-model OpenRouter with automatic fallback --- ## Referenzen - Mistral Embeddings: https://docs.mistral.ai/capabilities/embeddings - sqlite-vec: https://github.com/asg017/sqlite-vec - Voyage AI: https://docs.voyageai.com/ - OpenRouter: https://openrouter.ai/docs
Author
Owner

Phase 1 implementiert: Vector-DB optional

Commit: c0f9694

Änderungen

Workers registrieren jetzt qdrant:sync und semantic:search Capabilities nur wenn VECTOR_DB === 'qdrant':

  • packages/worker/src/worker-service.ts: Conditional capability registration
  • packages/worker/src/in-process-worker.ts: Same change for in-process workers

Bereits vorhanden

Das VECTOR_DB: 'none' | 'qdrant' Setting existierte bereits mit Default 'none'. Die Search-Route fällt bereits auf FTS5 zurück wenn kein Qdrant aktiv ist.

Nächste Schritte (noch offen)

  • Phase 2: Embedding Provider Abstraktion
  • Phase 3: Vector-DB Provider Abstraktion
  • Phase 4: AI Provider Erweiterung
## Phase 1 implementiert: Vector-DB optional **Commit:** `c0f9694` ### Änderungen Workers registrieren jetzt `qdrant:sync` und `semantic:search` Capabilities nur wenn `VECTOR_DB === 'qdrant'`: - `packages/worker/src/worker-service.ts`: Conditional capability registration - `packages/worker/src/in-process-worker.ts`: Same change for in-process workers ### Bereits vorhanden Das `VECTOR_DB: 'none' | 'qdrant'` Setting existierte bereits mit Default `'none'`. Die Search-Route fällt bereits auf FTS5 zurück wenn kein Qdrant aktiv ist. ### Nächste Schritte (noch offen) - Phase 2: Embedding Provider Abstraktion - Phase 3: Vector-DB Provider Abstraktion - Phase 4: AI Provider Erweiterung
Author
Owner

Phase 2 Complete: Embedding Provider Abstraction

Implemented pluggable embedding provider system with registry pattern (commit 6052dea):

New Files

  • packages/worker/src/embeddings/types.ts - EmbeddingProvider interface
  • packages/worker/src/embeddings/local-provider.ts - Xenova/transformers.js provider
  • packages/worker/src/embeddings/mistral-provider.ts - Mistral AI API provider
  • packages/worker/src/embeddings/index.ts - Provider registry with factory pattern

Settings Added

  • EMBEDDING_PROVIDER: 'local' | 'mistral' (default: 'local')
  • MISTRAL_EMBEDDING_MODEL: string (default: 'mistral-embed')

Integration

  • Updated QdrantService to use embedding provider registry
  • Updated embedding-handler.ts to use provider from registry
  • Providers are singleton instances, initialized on first use

Provider Dimensions

Provider Model Dimension
local Xenova/all-MiniLM-L6-v2 384
mistral mistral-embed 1024
mistral codestral-embed 1024

Next Steps (Phase 3)

  • Add OpenAI embedding provider
  • Add tests for embedding providers
  • Consider dimension compatibility checks when switching providers
## Phase 2 Complete: Embedding Provider Abstraction Implemented pluggable embedding provider system with registry pattern (commit `6052dea`): ### New Files - `packages/worker/src/embeddings/types.ts` - EmbeddingProvider interface - `packages/worker/src/embeddings/local-provider.ts` - Xenova/transformers.js provider - `packages/worker/src/embeddings/mistral-provider.ts` - Mistral AI API provider - `packages/worker/src/embeddings/index.ts` - Provider registry with factory pattern ### Settings Added - `EMBEDDING_PROVIDER`: 'local' | 'mistral' (default: 'local') - `MISTRAL_EMBEDDING_MODEL`: string (default: 'mistral-embed') ### Integration - Updated `QdrantService` to use embedding provider registry - Updated `embedding-handler.ts` to use provider from registry - Providers are singleton instances, initialized on first use ### Provider Dimensions | Provider | Model | Dimension | |----------|-------|-----------| | local | Xenova/all-MiniLM-L6-v2 | 384 | | mistral | mistral-embed | 1024 | | mistral | codestral-embed | 1024 | ### Next Steps (Phase 3) - Add OpenAI embedding provider - Add tests for embedding providers - Consider dimension compatibility checks when switching providers
jack closed this issue 2026-01-25 13:23:30 +00:00
jack closed this issue 2026-01-25 16:58:28 +00:00
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Reference
customable/claude-mem#112
No description provided.