CLAUDE.md · diff

git:20260310.6e74979 to git:20260311.e617ba8

1 added, 182 removed. Audit A to B.

- # CLAUDE.md — Agent context for mnemos
-
- ## What is this repo?
-
- mnemos is cloud-persistent memory for AI agents. Plugins connect to mnemo-server (Go) for multi-agent, tenant-isolated memory with hybrid vector + keyword search.
-
- Three components:
- - `server/` — Go REST API (chi router, TiDB/MySQL, optional embedding)
- - `openclaw-plugin/` — Agent plugin for OpenClaw (server backend)
- - `claude-plugin/` — Claude Code plugin (bash hooks + skills)
-
- ## Commands
-
- ```bash
- # Build server
- cd server && go build ./cmd/mnemo-server
-
- # Run server (requires MNEMO_DSN)
- cd server && MNEMO_DSN="user:pass@tcp(host:4000)/mnemos?parseTime=true" go run ./cmd/mnemo-server
-
- # Vet / lint
- cd server && go vet ./...
-
- # Run all checks
- make build && make vet
- ```
-
- ## Project layout
-
- ```
- server/cmd/mnemo-server/main.go — Entry point, DI wiring, graceful shutdown
- server/internal/config/ — Env var config loading (DB + embedding)
- server/internal/domain/ — Core types (Memory with Metadata/Embedding/Score), errors
- server/internal/embed/ — Embedding provider (OpenAI-compatible HTTP client)
- server/internal/handler/ — HTTP handlers + chi router setup + JSON helpers
- server/internal/middleware/ — Tenant resolution (tenantID → DB) + rate limiter
- server/internal/repository/ — Repository interfaces + TiDB SQL (vector + keyword search)
- server/internal/service/ — Business logic: upsert, LWW, hybrid search, embedding on write
- server/schema.sql — Database DDL (control plane + tenant data plane)
-
- openclaw-plugin/index.ts — Tool registration via MemoryBackend interface
- openclaw-plugin/backend.ts — MemoryBackend interface (store/search/get/update/remove)
- openclaw-plugin/server-backend.ts — Server mode: fetch → mnemo API
- openclaw-plugin/hooks.ts — Lifecycle hooks (auto-recall, auto-capture, compact/reset)
- openclaw-plugin/types.ts — Shared TypeScript types
-
- claude-plugin/hooks/common.sh — Server helpers (REST API calls)
- claude-plugin/hooks/session-start.sh — Load recent memories → additionalContext
- claude-plugin/hooks/stop.sh — Save last response as memory
- claude-plugin/hooks/user-prompt-submit.sh — System hint about available memory
- claude-plugin/skills/memory-recall/ — On-demand search skill
- claude-plugin/skills/memory-store/ — On-demand save skill
- ```
-
- ## Code style
-
- - Go: standard `gofmt`, no ORM, raw `database/sql` with parameterized queries
- - TypeScript: ESM modules, interface-based backend abstraction
- - Bash hooks: `set -euo pipefail`, Python for JSON parsing (avoid shell injection)
- - Layers: handler → service → repository (interfaces). Domain types imported by all layers.
- - Errors: sentinel errors in `domain/errors.go`, mapped to HTTP status codes in `handler/handler.go`
- - No globals. Manual DI in `main.go`. All constructors take interfaces.
-
- ## Key design decisions
-
- - **Server-only architecture**: All plugins connect to mnemo-server via REST API. No direct DB access from plugins.
- - **Plugin over skill**: Memory uses `kind: "memory"` plugin (automatic) not skill (agent-dependent)
- - **Hooks over MCP tools**: Claude Code memory is via lifecycle hooks (guaranteed) not tools (optional)
- - **Hybrid search**: Vector + keyword with graceful degradation. No embedder → keyword only.
- - **Embedder nullable**: `embed.New()` returns nil when unconfigured. All code accepts nil embedder.
- - **encoding_format: "float"**: Always set when calling embedding API (Ollama defaults to base64)
- - **VEC_COSINE_DISTANCE**: Must appear identically in SELECT and ORDER BY for TiDB VECTOR INDEX
- - **embedding IS NOT NULL**: Mandatory in vector search WHERE clause
- - **3x fetch limit**: Both vector and keyword search fetch limit×3, merge after
- - **Score**: `1 - distance` for vector results, `0.5` for keyword-only
- - Upsert uses `INSERT ... ON DUPLICATE KEY UPDATE` (atomic, no race conditions)
- - Version increment is atomic in SQL: `SET version = version + 1`
- - Tags stored as JSON column, filtered with `JSON_CONTAINS`; empty tags stored as `[]` (not NULL)
- - **No auth required**: Tenant ID in URL path is the only identification. No Bearer tokens, no API keys.
-
- ## Database schema
-
- `server/schema.sql` contains DDL for two separate planes:
-
- | Plane | Database | Tables |
- |-------|----------|--------|
- | **Control plane** | `MNEMO_DSN` (shared) | `tenants`, `upload_tasks` |
- | **Tenant data plane** | Per-tenant DB provisioned by TiDB Zero | `memories` |
-
- The `memories` table is **never created in the control plane DB**. It is created in each tenant's own dedicated database when a tenant is provisioned (automatically via TiDB Zero, or manually).
-
- When deploying a new server environment, only run the control-plane tables against `MNEMO_DSN`:
-
- ```sql
- -- Run against MNEMO_DSN only
- CREATE TABLE IF NOT EXISTS tenants (...);
- CREATE TABLE IF NOT EXISTS upload_tasks (...);
- -- DO NOT run memories table here
- ```
-
- The `memories` table DDL in `schema.sql` is reference only — used when manually setting up a tenant DB. For auto-embedding environments, use the generated column variant (see comments in `schema.sql`).
-
- ## Tenant provisioning
-
- Provision a new tenant via the unauthenticated bootstrap endpoint:
-
- ```bash
- curl -s -X POST http://<server>/v1alpha1/mem9s | jq .
- # → { "id": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx", "claim_url": "..." }
- ```
-
- Save the returned `id` — this is the tenant ID used in all subsequent API calls.
-
- All memory operations use tenant-scoped routes:
- ```
- POST /v1alpha1/mem9s/{tenantID}/memories — Create
- GET /v1alpha1/mem9s/{tenantID}/memories — Search/list
- GET /v1alpha1/mem9s/{tenantID}/memories/{id} — Get by ID
- PUT /v1alpha1/mem9s/{tenantID}/memories/{id} — Update
- DELETE /v1alpha1/mem9s/{tenantID}/memories/{id} — Delete
- ```
-
- Agent identity is passed via the `X-Mnemo-Agent-Id` header.
-
- ## Metrics
-
- Prometheus metrics are exposed at `GET /metrics` (no auth).
-
- | Metric | Type | Labels | Description |
- |--------|------|--------|-------------|
- | `mnemo_http_requests_total` | Counter | `method`, `route`, `status` | Total HTTP requests |
- | `mnemo_http_request_duration_seconds` | Histogram | `method`, `route` | HTTP request latency |
-
- `route` uses the chi route pattern (e.g. `/v1alpha1/mem9s/{tenantID}/memories`) — not the raw URL — to avoid high cardinality from tenant IDs.
-
- Source: `server/internal/metrics/metrics.go`
-
- ## Installing the Claude Code plugin
-
- If a user asks to install or set up the claude-plugin, read `claude-plugin/README.md` for the full guide.
-
- **Marketplace install (recommended):**
-
- 1. In Claude Code: `/plugin marketplace add qiffang/mnemos`
- 2. Then: `/plugin install mnemo-memory@mnemos`
- 3. Accept the hook permissions when prompted
- 4. Add server credentials to `~/.claude/settings.json` under `env`:
- - Set `MNEMO_API_URL` and `MNEMO_TENANT_ID`
- 5. Restart Claude Code
-
- **Manual install (alternative):**
-
- 1. `chmod +x claude-plugin/hooks/*.sh`
- 2. Copy skills: `cp -r claude-plugin/skills/memory-{recall,store} ~/.claude/skills/`
- 3. Add `env` + `hooks` entries to `~/.claude/settings.json` (see `claude-plugin/README.md` for full config)
-
- ## Installing the OpenCode plugin
-
- If a user asks to install mnemo for OpenCode, read `opencode-plugin/README.md` for the full guide.
-
- **npm plugin (recommended):**
-
- 1. Add to `opencode.json`: `{"plugin": ["mnemo-opencode"]}`
- 2. Set env vars: `MNEMO_API_URL` and `MNEMO_TENANT_ID`
- 3. Restart OpenCode — plugin auto-installs from npm and logs `[mnemo] Server mode...`
-
- **From source (alternative):**
-
- 1. `cd opencode-plugin && npm install`
- 2. Add to `opencode.json`: `{"plugins": {"mnemo": {"path": "/absolute/path/to/mnemos/opencode-plugin"}}}`
- 3. Set env vars (same as above)
-
- ## Installing the OpenClaw plugin
-
- If a user asks to install mnemo for OpenClaw, read `openclaw-plugin/README.md` for the full guide. Quick summary:
-
- 1. `cd openclaw-plugin && npm install`
- 2. Add to `openclaw.json`:
- - Set `plugins.slots.memory` to `"mnemo"`
- - Add `plugins.entries.mnemo` with `enabled: true` and config
- - Set `apiUrl` and `tenantID` in config
- 3. Plugin is `kind: "memory"` — OpenClaw framework manages the lifecycle automatically
+ @AGENTS.md