# supamem

> Qdrant-backed dual-memory tooling for AI coding agents (Claude Code, Cursor, OpenCode).
> Provides a CLI to bootstrap, index, run an MCP server, install per-client hooks, and run
> retrieval evals — all backed by a locked tuned-hybrid (BM25 + MiniLM) Qdrant pipeline.
> Extracted from the SoftChat (https://app.softchat.ru) production memory stack so any
> team can run on the same battle-tested foundation.

`supamem` packages a hybrid sparse+dense semantic memory layer (Qdrant), a Model Context
Protocol server, and per-client session/edit hooks as a single Python distribution. Once
installed, AI coding assistants gain persistent semantic memory across projects.

## Core docs

- [README](README.md): Hero, quickstart, prerequisites, install matrix, CLI reference, client wiring
- [MIGRATION](MIGRATION.md): Migrating from an in-tree `dev_memory` setup to supamem
- [LICENSE](LICENSE): MIT

## Distribution

- [PyPI](https://pypi.org/project/supamem/): Released via Trusted Publisher OIDC; `pip install supamem` or `uv tool install supamem`. Current version: v0.3.0a1 (alpha pre-release; v0.2.0 / v0.2.1 already shipped).
- [CHANGELOG](CHANGELOG.md): Per-version release notes (v0.1.0 initial, v0.1.1 update-check + AGENTS.md, v0.1.2 project-tunable regress baselines, v0.1.3 dual_memory_write + qdrant aliases, v0.1.4 SessionStart banner + supamem live dashboard, v0.1.5 SessionStart wired by installer, v0.2.0 mcp.caps + multi-project install + agent-discipline hooks, v0.2.1 user-visible banner + drift signal, v0.2.2a1 transcript chunker plugin, v0.2.3a1 coding-path classifier + where filter, v0.2.4a1 code-aware reranker + eager ML fetch + doctor-driven repair self-heal, v0.2.5a1 subagent reachability auto-patcher + unpatch-agents + doctor reachability panel, v0.3.0a1 per-source temporal validity + transcript-only recency decay + temporal doctor panel)

## Translations

- [README (English, canonical)](README.md): The one PyPI renders
- [README zh-CN](README.zh-CN.md): Simplified Chinese
- [README es](README.es.md): Spanish
- [README ja](README.ja.md): Japanese
- [README ru](README.ru.md): Russian (project author native)

## CLI commands

- [supamem init](README.md#cli-surface): Greenfield bootstrap — probes Qdrant, creates collection, writes `.supamem/config.toml`
- [supamem install](README.md#wiring-into-your-client): Patch a client config (claude-code, cursor, opencode) — atomic with backup. `--scope project` (default, per-workspace `.mcp.json` / `.cursor/mcp.json`) or `--scope user` (legacy global). `--enforce-search` (claude-code only) registers the opt-in PreToolUse edit-gate. v0.2.4a1+ proactively downloads all ML prerequisites (MiniLM ~90 MB, BM25 ~10 MB, mxbai-rerank-base-v2 ~1 GB) with `rich.progress`. Pass `--skip-models` / `--no-skip-models` to skip eager ML model download (air-gapped first-run; backfill via `supamem repair`). v0.2.5a1+ also auto-patches `~/.claude/agents/` and `<project>/.claude/agents/` to append `mcp__supamem__*` to restrictive `tools:` whitelists; opt out with `--skip-patch-agents`.
- [supamem repair](README.md#cli-surface): doctor-driven self-heal — re-fetches missing/partial reranker model, re-syncs `share/`, repairs managed CLAUDE.md/AGENTS.md blocks, restores client config, re-applies subagent reachability patches (idempotent). Composes with `supamem doctor` (diagnose) as the canonical UX entry points. v0.2.4a1+ extends the v0.2.0 migrate-from-global-install path; original behavior preserved. Supports `--skip-patch-agents` (v0.2.5a1+).
- [supamem index](README.md#cli-surface): Embed dev memories into Qdrant using the locked tuned-hybrid pipeline (D-25). v0.2.2a1+ adds `--transcripts` (bare → uses `[supamem.transcript] default_root`, defaults to `~/.claude/projects/`) and `--transcripts <path>` (explicit) to ingest Claude Code session JSONL as Q+A drawer chunks; `--transcripts-only` skips the default project corpus; `--since Nd|Nh|0` filters transcripts by mtime (default 180d, `0` disables)
- [supamem mcp-server](README.md#cli-surface): Run the MCP server over stdio (default) or HTTP
- [supamem hook](README.md#cli-surface): Per-client session/edit hooks called by the client itself
- [supamem doctor](README.md#cli-surface): Probe Qdrant, resolve config chain, report version drift, surface update-check status
- [supamem stats](README.md#cli-surface): Welford schema-v2 usage counters
- [supamem live](README.md#-see-it-work--supamem-live): Real-time terminal dashboard tailing the audit JSONL — visibility into PreToolUse hook injections (v0.1.4+)
- [supamem migrate](README.md#cli-surface): Brownfield migration from an existing `dev_memory` collection
- [supamem eval](README.md#cli-surface): Run the regression harness; project-tunable baselines via `[supamem.eval]` config (v0.1.2+)
- [supamem uninstall](README.md#cli-surface): Reverse `supamem install` cleanly
- [supamem unpatch-agents](README.md#cli-surface): Reverse subagent reachability patches recorded in `agent_patches.json`. Skips files the user has edited since (frontmatter SHA-256 match) with a per-file warning. Run BEFORE `pip uninstall supamem` — there is no portable pip/uv/pipx uninstall hook (v0.2.5a1+).

## MCP tools (v0.1.3)

- `dual_memory_search(query, top_k=5, where=None)`: Hybrid (BM25+dense, RRF) retrieval over the project's Qdrant collection. Top-k, latency, summary. Response shape (v0.2.0+): each `Chunk` carries `text` (full intact payload) and `preview` (display-only excerpt capped at `mcp.caps.max_preview_chars`); top-level `SearchResult.clamped_to` is set when the server clamped requested `top_k`. v0.2.3a1+ accepts `where: dict[str, str | list[str]] | None` — optional Qdrant payload filter; AND across keys, OR within list values (`MatchAny`); single string → exact `MatchValue`. v1 documents `room` as the only key (one of: `backend`, `frontend`, `tests`, `docs`, `scripts`, `config`, `migrations`, `types`, or `null`). Examples: `where={"room": "backend"}` or `where={"room": ["backend", "tests"]}`. Unknown keys are passed through to Qdrant (forward-compat for Phase 9/11)
- `dual_memory_write`: Persist agent-authored memory — writes Markdown to `<project>/.claude/insights/_agent/<slug>.md` with YAML frontmatter, immediately upserts into Qdrant (wait=True), idempotent on topic via UUIDv5
- `qdrant_find(query, top_k=5, where=None)` (alias of dual_memory_search): Backward-compat for users coming from upstream `mcp-server-qdrant`. Inherits the same caps, response shape, AND the v0.2.3a1+ `where` payload filter byte-identically (D-17 alias parity)
- `qdrant_store` (alias of dual_memory_write): Same compat shim. Disable both aliases with `SUPAMEM_QDRANT_ALIASES=0`

## MCP response caps (v0.2.0+)

Server-side hard caps on every retrieval response. Configured under the `[supamem.mcp.caps]` TOML table; surfaced in `supamem doctor` with config-source provenance.

- `mcp.caps.max_top_k` (default: 25) — server silently clamps requested `top_k` to this value; `SearchResult.clamped_to` is populated when clamping fires so callers can detect it
- `mcp.caps.max_query_chars` (default: 250) — Pydantic `Field(max_length=...)` baked into the tool schema at registration time; queries longer than the cap are rejected with a structured MCP validation error (no silent truncation, no stdout pollution)
- `mcp.caps.max_preview_chars` (default: 200) — display preview cap on each `Chunk.preview`; the full canonical payload in `Chunk.text` is never truncated

## Visibility surfaces (v0.1.4+)

- `supamem live` CLI: Rich-Live terminal dashboard, real-time tail of the audit JSONL with rotation/resize/Ctrl-C handling and pipe-safe plain-JSONL fallback when stdout isn't a TTY
- `supamem hook session-start`: cross-client SessionStart banner injected via `additionalContext` (Claude Code) + `additional_context` (Cursor/OpenCode forks). Auto-detects calling client from `CLAUDECODE`/`OPENCODE`/`CURSOR_AGENT` env vars. Format: `🧠 supamem v<x.y.z> · <collection> · <N> chunks · audit <path>`. Fail-soft per hook discipline — never blocks session start

## MCP project-root resolution (v0.2.0+)

stdio MCP servers are often launched by hosts (Cursor, IDE wrappers) from a cwd that is NOT the workspace, which silently drops supamem to the default collection (`dev_memory_tuned_hybrid`) and produces Qdrant 404s when callers query the project's actual collection.

- `SUPAMEM_PROJECT_ROOT` (env var) — preferred, explicit. Auto-injected by `supamem install --scope project` into `<repo>/.mcp.json` (Claude Code) and `<repo>/.cursor/mcp.json` (Cursor) so the subprocess locates `.supamem/config.toml` regardless of cwd
- Parent-walk fallback — when the env var is unset, supamem walks parents from `Path.cwd()` looking for `.supamem/config.toml` or `pyproject.toml [tool.supamem]`. Stops at filesystem root or `$HOME` to avoid scanning above the user's home
- Stderr fallthrough warning — when neither the env var nor the parent-walk locate a project marker AND the resolved collection is still the shipped default, `supamem mcp-server --transport stdio` emits a one-line stderr warning (cwd inspected, env var presence — never values, fix command). Stdout stays JSON-RPC clean
- Verify with `supamem doctor` from the repo root: the resolved collection must match what the MCP client returns from `dual_memory_search`

## Multi-project install + agent-discipline hooks (v0.2.0+)

- **Per-workspace install is the default** as of v0.2.0. `supamem install --client claude-code` writes to `<repo>/.mcp.json` (Anthropic project-scope MCP file, takes precedence over user-scope per docs). `supamem install --client cursor` writes to `<repo>/.cursor/mcp.json` (Cursor per-workspace, project-level wins on conflict). Use `--scope user` to keep legacy global writes (last install wins on multi-project machines).
- **`supamem repair`** is the migration verb for users on legacy global installs. Strips supamem from BOTH project AND user scopes (defensive uninstall) then re-installs at project scope from current cwd. Idempotent. Auto-detects clients when `--client` omitted. Forwards `--enforce-search`.
- **Claude Code edit-gate hook** (`supamem hook claude-code-gate`, opt-in via `supamem install --enforce-search`). Registers a PreToolUse `Edit|Write|MultiEdit` matcher that DENIES the tool call when no `mcp__supamem__dual_memory_search` (or `qdrant_find` alias) is logged in the session transcript since the last user turn (strategy A — strict per-turn). Reverse-scans the transcript JSONL with a 256 KB byte cap; emits Anthropic's `permissionDecision: deny` JSON contract on stdout. Override per-session with `SUPAMEM_GATE_DISABLE=1`.
- **Cursor `beforeSubmitPrompt` advisory hook** (`supamem hook cursor-advisory`). Cursor 1.7's hooks API has no fail-closed pre-edit event, so this is advisory-only: when the user's prompt looks edit-bound (regex over `fix|refactor|rename|implement|add|...`), emit `{"continue": true, "permission": "allow", "agentMessage": "..."}` reminding the agent to call `dual_memory_search` first. Override with `SUPAMEM_ADVISORY_DISABLE=1`. Auto-installed by Cursor installer alongside the existing sessionStart snapshot.

## SessionStart banner (v0.2.0 enriched)

Format: `🧠 supamem ✓ v0.2.0 · <collection> · <N> chunks · audit <path>` (additional `· update v0.X.Y available` segment when `update_check` cache reports a newer release).

- Health flag — single character right after `supamem`: `✓` healthy / `⚠` qdrant unreachable OR resolved collection is still the shipped default (legacy global-install / wrong-cwd failure mode)
- Update hint — cache-only read of `update_check.json`; never blocks session-open on network. Healing is NEVER automatic — the banner only signals; run `supamem repair` to act
- Suppress entirely with `SUPAMEM_BANNER_DISABLE=1`
- Suppress ONLY the user-visible terminal line (keep injecting context for the model) with `SUPAMEM_BANNER_QUIET=1`. v0.2.1+ emits `systemMessage` (Claude Code) and `user_message` (Cursor forward-compat) alongside `additionalContext` — Claude Code renders `systemMessage` as the `SessionStart:startup says: <line>` row in the terminal. Health flag `⚠` now also fires on per-client install drift detected by `supamem doctor` (managed-block version != running CLI version).

## Update-check (v0.1.1+)

- Daemon-thread GitHub Releases probe; 24h TTL cache at `platformdirs.user_cache_dir("supamem")/update_check.json`
- Stderr footer on next invocation when newer release available; never blocks
- Suppress with `SUPAMEM_NO_UPDATE_CHECK=1`, `NO_UPDATE_NOTIFIER=1`, or `CI=1`
- Visible in `supamem doctor` (current vs cached-latest, last-check timestamp, suppression env)

## Transcript ingestion (v0.2.2a1+)

- `supamem index --transcripts` (bare flag) ingests Claude Code session JSONL from `~/.claude/projects/` (or `[supamem.transcript] default_root`) as Q+A drawer chunks via the new `transcript` chunker entry-point. Pass an explicit path with `supamem index --transcripts /path/to/sessions/`. Mixed corpora dispatch per-suffix: `*.md` → `markdown_header`, `*.jsonl` → `transcript`.
- `--transcripts-only` skips the default project corpus and indexes only transcripts in the same run.
- `--since 30d` / `--since 12h` / `--since 0` — mtime filter on transcript JSONL; `0` disables. Defaults to `[supamem.transcript] since_days = 180`.
- New `[supamem.transcript]` config table (six keys; surfaced by `supamem doctor` with `[source: ...]` provenance):
  - `default_root` (str, default `~/.claude/projects/`)
  - `since_days` (int, default `180`)
  - `tool_payload_max_chars` (int, default `2000`) — tool-use payloads above this size are elided to a synthesis stub
  - `chunk_soft_max_tokens` (int, default `600`)
  - `include_paths_glob` (list[str], default `[]`)
  - `exclude_paths_glob` (list[str], default `[]`) — hand-exclude sensitive sessions before indexing
- New plugin entry-point: `transcript = "supamem.indexer.transcript.chunker:chunk_transcript"` under the `supamem.chunker` group. Signature: `chunk_transcript(text: str, *, source_path: Path, **kwargs) -> list[ChunkRecord]`.
- Per-message-uuid dedupe in the manifest (`__transcripts__` key) — re-running on an unchanged corpus reports `0 new, 0 changed`. Editing one message purges-then-reinserts only that chunk.
- ⚠ Transcripts may contain secrets — review your `~/.cache/supamem` Qdrant collection before sharing (no v1 redaction; see CHANGELOG security note for v0.2.2a1).

## Coding-path classifier (v0.2.3a1+)

Every indexed chunk gains a `payload.room` facet (string or `null`) via **exact path-component equality** — `set(Path(file_path).parts) ∩ set(keywords)` — never substring matching. A file at `data/chest_xray/img.png` is NEVER classified as `tests`. The `where` retrieval filter on `dual_memory_search` and `qdrant_find` (see MCP tools section) consumes this facet.

- `payload.room` — single string or JSON `null`. ALWAYS present on every point (uniform schema, D-06). v1 values: `backend`, `frontend`, `tests`, `docs`, `scripts`, `config`, `migrations`, `types`, or `null`.
- Priority is encoded by config order (first-match-wins, D-01a). Putting `tests` before `backend` makes `tests/backend/api_test.py` classify as `tests`.
- Hash-drift sweep — `manifest.classifier_hash = sha256(json.dumps(rooms, sort_keys=False))` captures both content AND priority order. On every `supamem index` run, if the stored hash differs from the current config hash, supamem scrolls the collection in batches and `client.set_payload({"room": new_room}, points=[ids], wait=True)` per-room — pure metadata update, **zero re-embedding cost** (D-08, D-09).
- Pre-v0.2.3 collections auto-migrate on first post-upgrade index invocation (missing `__classifier_hash__` → drift from null → one-time sweep).
- `supamem doctor` surfaces the active rooms map with `[source: ...]` provenance, the stored `classifier_hash`, and a per-room histogram (including a `null` bucket for unmatched chunks) — D-07, D-16.

`[supamem.classifier.rooms]` config table (default keyword map, priority order locked):

```toml
[supamem.classifier.rooms]
tests      = ["tests", "test", "__tests__", "spec", "specs"]
types      = ["types", "@types", "typings"]
migrations = ["migrations", "alembic", "schema"]
config     = ["config", "configs", ".github", "ci"]
scripts    = ["scripts", "bin", "tools"]
docs       = ["docs", "documentation"]
frontend   = ["frontend", "web", "client", "ui", "components", "pages"]
backend    = ["src", "backend", "api", "server", "lib"]
```

User TOML at `[supamem.classifier.rooms]` REPLACES the defaults dict (leaf-replace, not merge — matches the `transcript_*` precedent). Reordering rooms in the user config trips the sweep gate because `sort_keys=False` is intentional. Transcript chunks (chunker == "transcript") classify to `room = null` by construction — non-coding paths; filter them via the existing `payload.chunker` key instead.

## Code-aware reranker (v0.2.4a1+)

Cross-encoder `mixedbread-ai/mxbai-rerank-base-v2` (Apache-2.0, ~1 GB) plugged into `tuned_hybrid` retrieval as the **new default** (`retrieval.reranker = "mxbai_v2"`). Setting `retrieval.reranker = "off"` restores pre-Phase-8 byte-identical behavior. When reranker is on: PREFETCH_LIMIT widens to 50 per arm; T-4 recency multiplier is skipped; T-5 dedup + T-8 token budget run AFTER rerank.

- New `supamem.reranker` plugin entry-point group — third parties register custom rerankers without forking. Registered: `mxbai_v2 = supamem.rerankers.mxbai_v2:MxbaiV2Reranker`. Plugin signature: `rerank(query: str, candidates: list[RetrievedChunk]) -> list[RetrievedChunk]`. Lazy model load on first `rerank()` call mirrors `embedders/_ensure()`.
- `RetrievedChunk` gains optional `rerank_score: float | None` field for telemetry; primary `score` carries the rerank score when reranker is on.
- `supamem doctor` `Reranker` panel: name, model_id, cache path (`platformdirs.user_cache_dir("supamem")/models/<model_id>/`), on-disk size + partial-download detection, last-load latency, last-100-query rerank p50/p95, detected device (cuda/mps/cpu).
- `[retrieval.reranker]` config table:
  - `name = "mxbai_v2" | "off"` (default `"mxbai_v2"`)
  - `top_n = 50` — rerank pool size; clamps to fused-candidate count
  - `prefetch_per_arm = 50` — widened from default 20 when reranker on
  - `batch_size = 16`
  - `model_id = "mixedbread-ai/mxbai-rerank-base-v2"`
- New env vars: `SUPAMEM_CACHE_DIR` (override the platformdirs cache root for tests/CI); `HF_HUB_OFFLINE=1` + `TRANSFORMERS_OFFLINE=1` (respected by `prepare()` — refuse network probes); `SUPAMEM_INTEGRATION_RERANKER=1` (opt-in integration test gate).
- New deps: `mxbai-rerank>=0.1.6,<0.2`, `huggingface_hub>=0.24`, `filelock>=3.13` (pulls `transformers>=4.49`, `torch>=2.0`, `accelerate>=1.5` transitively).

## Subagent reachability (v0.2.5a1+)

Subagents (Claude Code agent definitions under `~/.claude/agents/*.md` and `<project>/.claude/agents/*.md`) inherit ONLY the tools listed in their frontmatter `tools:` whitelist. Plugins like GSD, superpowers, and hookify ship agents with restrictive whitelists (e.g. `Read, Edit, mcp__context7__*`) that exclude `mcp__supamem__*`, so the parent session having the supamem MCP server connected is irrelevant — code-touching subagents silently fail dual-memory lookups.

- **Auto-patcher** runs on `supamem install` and `supamem repair`. Idempotently appends `mcp__supamem__*` to any restrictive `tools:` whitelist that doesn't already cover supamem (broad `mcp__*`, `mcp__supamem__*`, or any specific `mcp__supamem__<tool>` literal counts as covered). Files with a missing or empty `tools:` line have full-inheritance per Claude Code semantics and are left untouched. Symlinked files are skipped with a warning to avoid polluting upstream repos. Style preserved (CSV vs YAML list) via `ruamel.yaml` round-trip.
- **`--skip-patch-agents`** opt-out flag on `install` / `init` / `repair`.
- **`supamem unpatch-agents`** reverses every recorded patch. Skips files the user has edited since the patch (frontmatter SHA-256 newline-normalized match) and emits a per-file warning naming them. Exit 0 even when nothing to restore.
- **Backup manifest** at `platformdirs.user_cache_dir("supamem") / agent_patches.json` (override the cache root with `SUPAMEM_CACHE_DIR`). Single rolling JSON, schema_version=1, FileLock-protected concurrent writes, atomic temp-and-rename. Per-entry: relative path, original frontmatter SHA, patched frontmatter SHA, original `tools:` value verbatim, timestamp, supamem version.
- **`supamem doctor` Subagent reachability panel** (between Reranker and Installed clients) lists per-agent status grouped by `[global]` and `[project]` scope: `patched (added mcp__supamem__*)`, `OK (already covered)`, `OK (full inheritance)`, `skipped: <reason>`, or `needs patching (run supamem repair)`. Renders the manifest path + an `unpatch-agents` reminder when a manifest exists. Read-only by construction; never flips the doctor exit code.
- **Uninstall contract** is a documented two-step (no portable pip/uv/pipx uninstall hook in 2026):

  ```bash
  supamem unpatch-agents      # restore agent whitelists first
  pip uninstall supamem       # then remove the package
  ```

  `supamem doctor` displays the manifest path and reminder so this flow is discoverable without docs.

## Per-source temporal validity (v0.3.0a1+)

Every indexed chunk carries a binary `valid_to` field: `null` ⇒ live; `≤ now()` ⇒ superseded (filtered out of every retrieval). Re-indexing a CHANGED file atomically scrolls existing chunks for that path, `set_payload(valid_to=now())`s them, then upserts new content-hash-keyed chunks with `valid_to=null`. Old and new chunks coexist in Qdrant; auto-GC at end of `supamem index` deletes superseded chunks past `retention_days`. The retrieval-time filter is constructed at a single site (`retrieval/filters.py:build_qdrant_filter`) and inherited by every backend — `tuned_hybrid` (both Prefetch arms), `dense`, `bm25`, `qdrant_find`, `dual_memory_search`. Filter uses `IsEmptyCondition` on `valid_to` (NOT `IsNullCondition` — Qdrant#5342: `IsNull` does not match missing fields).

### Config keys

`[retrieval.temporal]` — universal binary `valid_to` invalidation:

- `retention_days` (int, default `90`) — auto-GC sweeps superseded chunks older than this at the end of `supamem index`. Set to `0` to disable auto-GC entirely (kept-forever escape hatch for compliance / audit collections).

`[retrieval.recency.per_source.transcript]` — transcript-only opt-in decay (does NOT affect code / ADR / doc / null-room rankings):

- `enabled` (bool, default `false`) — gates the post-rerank decay multiplier.
- `half_life_days` (float, default `14.0`) — half-life of the multiplier; e.g. age=14d ⇒ multiplier=0.85 with α=0.7.
- `alpha` (float, default `0.7`) — floor of the multiplier; range `[0.0, 1.0]`. Validators reject out-of-range at boot.

Worked example with locked defaults: age 0d → 1.000; 7d → 0.924; 14d → 0.850; 28d → 0.775; ∞ → 0.700 (floor).

### Doctor panel

`supamem doctor` adds a "Temporal validity" panel between Reranker and Subagent reachability:

- live / superseded / awaiting_gc / future_dated counts
- per-source breakdown (markdown_header / transcript / null)
- oldest + newest `valid_from` across the collection
- `retention_days` provenance line
- `validity_migration` provenance line (when manifest gate has tripped)

Read-only by construction; never flips the doctor exit code.

### Migration

First post-upgrade `supamem index` back-fills `valid_to=null` on legacy points (gated by manifest `__validity_migration__` reserved key, idempotent on subsequent runs). Defense-in-depth alongside the `IsEmpty` runtime filter.

## Architecture

- [How it works](README.md#how-it-works): MCP server topology, hybrid retrieval, hook flow
- [Hybrid retrieval](README.md#features): Tuned BM25 + MiniLM fusion, locked schema D-25
- [Markdown chunker](README.md#features): Header-aware T-1 chunker, 200-token target / 250 soft max
- [Transcript chunker](README.md#transcript-ingestion-v022a1): Q+A drawer chunks from Claude Code session JSONL (v0.2.2a1+)

## Prerequisites

- [Python 3.12+](README.md#prerequisites): macOS / Linux / Windows install commands
- [Qdrant 1.10+](README.md#prerequisites): Docker, docker compose, or Qdrant Cloud
- [MCP-compatible client](README.md#prerequisites): Claude Code, Cursor, or OpenCode

## Optional

- [Contributing](README.md#contributing): Local dev setup with uv + pytest + ruff
- [SoftChat](https://app.softchat.ru): Russian-language AI chat platform — origin project
- [SoftSkillz](https://softskillz.ai): AI-first product engineering team
- [Qdrant docs](https://qdrant.tech/documentation/): Vector database upstream
- [Model Context Protocol](https://modelcontextprotocol.io/): MCP spec
- [uv](https://docs.astral.sh/uv/): Recommended Python package manager
