vipune /ˈʋi.pu.ne/
███
░░░
█████ █████ ████ ████████ █████ ████ ████████ ██████
░░███ ░░███ ░░███ ░░███░░███░░███ ░███ ░░███░░███ ███░░███
░███ ░███ ░███ ░███ ░███ ░███ ░███ ░███ ░███ ░███████
░░███ ███ ░███ ░███ ░███ ░███ ░███ ░███ ░███ ░███░░░
░░█████ █████ ░███████ ░░████████ ████ █████░░██████
░░░░░ ░░░░░ ░███░░░ ░░░░░░░░ ░░░░ ░░░░░ ░░░░░░
░███
█████
░░░░░
A minimal memory layer for AI agents.
In Finnish mythology, Antero Vipunen is a giant who sleeps underground, holding all the world's knowledge and ancient songs. vipune is your agent's sleeping giant — a local knowledge store that remembers everything.
Store semantic memories, search by meaning, and detect conflicts. Single binary CLI. No API keys required.
Features
- Semantic search - Find memories by meaning, not keywords (ONNX embeddings, bge-small-en-v1.5)
- Conflict detection - Automatically warns when adding duplicate or similar memories
- Zero configuration - Works out of the box (auto-detected git projects, sensible defaults)
- Single binary - Just one CLI tool, no daemon, no database server
- No API keys - Everything runs locally, no network dependencies
- Project scoped - Memories isolated by git repository
Installation
Platform Support
Supported: macOS ARM64, Linux x86_64, Linux ARM64
Not supported: Windows (due to ONNX Runtime compilation complexity)
Quick install (macOS and Linux)
|
The installer detects your platform, downloads the correct binary, and adds it to
~/.cargo/bin/by default. If you don't use Rust, restart your shell or runexport PATH="$HOME/.cargo/bin:$PATH"to make vipune available. Use--prefix=/usr/localfor system-wide installs.
To verify the download before running, download the checksum file and check:
Pre-built binary
macOS Apple Silicon (arm64)
Download and extract:
&&
Linux x86_64
Download and extract:
&&
Linux ARM64
Download and extract:
&&
Build from source
Requires the Rust toolchain (1.85+). On Linux, you may also need libgomp1 and libc6.
Latest release (recommended)
Or clone and build manually
&&
The binary will be at ./target/release/vipune. Install it:
&&
Or add to PATH temporarily:
Install Claude/Pi skill (optional)
For agents with skill auto-discovery (Claude Code, Pi), install the vipune skill artifact to enable software-development-tuned memory patterns:
&& \
The skill becomes available at this URL once this change is merged to main.
This adds domain-specific patterns: issue/PR linkage, failed-approach tracking, pre-flight gotcha checks, dev-loop mapping, and ADR-lite decision capture. Use <project>/.claude/skills/ for project-scoped installation. See Agent Integration for details.
Note: Tier-2 (automated install via vipune skill install) and Tier-3 (cross-agent AGENTS.md snippets) are future follow-ups.
Uninstall
Or via cargo: cargo uninstall vipune
Optionally, clear all data:
Embedding Models
vipune embeds memories with a local ONNX model downloaded from HuggingFace Hub on first use, pinned to an exact revision for reproducibility. The model is chosen per database via the embedding_model setting (see Configuration) — set it before your first vipune add, because a database records which model it was created with.
Built-in profiles
| Model ID | Revision | Languages | Download size | Prefixes |
|---|---|---|---|---|
BAAI/bge-small-en-v1.5 (default) |
5c38ec7c405ec4b44b94cc5a9bb96e735b38267a |
English | ~66 MB | none |
intfloat/multilingual-e5-small |
614241f622f53c4eeff9890bdc4f31cfecc418b3 |
~100 languages (xlm-roberta) | ~470 MB | automatic query: / passage: |
Choosing a model: the default bge-small-en-v1.5 is fast and small but English-focused. If your memories are in other languages (e.g. Finnish, Swedish, German), select the multilingual profile instead — it embeds ~100 languages, at the cost of a much larger one-time download:
# Either set the environment variable
# ...or in ~/.config/vipune/config.toml
Automatic prefixes (e5 only)
The e5 model requires task prefixes to work well. vipune applies them automatically: query: is prepended to search queries and passage: to stored text at embedding time. Do not add these prefixes yourself — they are never written into stored content, search output, or the FTS index; they exist only at embed time.
The bge-small-en-v1.5 profile uses no prefixes.
Note: the prefix consumes tokens of the 512-token limit, so slightly longer texts that embed fine under bge may be rejected (exit code 3) under e5 — over-length input is always rejected, never truncated.
Switching models on an existing database
A database records the model id + revision it was created with. If the configured model no longer matches the recorded identity, add, update, and search refuse with an error naming both identities — the stored embeddings are in a different vector space and results would be meaningless.
To migrate an existing database to a different model:
This re-embeds every stored memory with the newly configured model (a plain vipune reindex would re-embed nothing, since existing vectors already look real). The migration is crash-safe and pre-checked:
- Pre-flight —
reindex --forcefirst token-counts every stored row with the target model's passage prefix. If any row would exceed the 512-token limit once prefixed, it refuses to start, writes nothing, and lists the offending memory ids. The migration marker is only ever written in a state the re-embed pass can complete. - Marker-first — it records a
migrating to <id>@<revision>marker before re-embedding. - Re-embed + record — it re-embeds every row, then in one transaction records the new identity and clears the marker.
If the run is interrupted, the marker stays and operations refuse until you re-run vipune reindex --force, which performs a full idempotent pass from the beginning.
Air-gapped / Offline Usage
vipune downloads the embedding model from HuggingFace Hub on first run. Each model is pinned to a specific revision to ensure reproducibility:
BAAI/bge-small-en-v1.5— revision5c38ec7c405ec4b44b94cc5a9bb96e735b38267aintfloat/multilingual-e5-small— revision614241f622f53c4eeff9890bdc4f31cfecc418b3
For air-gapped environments, pre-fetch the default model before going offline:
# Install huggingface-cli first if you don't have it
# Download the model revision to the cache directory
If you selected the multilingual profile instead, pre-fetch that one as well:
The model will be cached in the HF Hub cache layout at ~/.cache/huggingface/hub/ and vipune will use it without network access. You can verify the cache before going offline:
Note: When upgrading vipune, the pinned revision may change. Check the release notes and re-download the new revision if the SHA has changed.
Library consumers: The constants vipune::EMBED_MODEL_ID and vipune::EMBED_MODEL_REVISION are exported for tracking the pinned model version programmatically.
Quick Start
Add a memory:
Search by semantic meaning:
Add with metadata (optional):
CLI Commands
| Command | Description |
|---|---|
vipune add <text> |
Store a memory (with type, status, conflict detection) |
vipune search <query> |
Find memories by meaning (filters by type/status) |
vipune get <id> |
Retrieve a memory by ID |
vipune list |
List memories (default: active only) |
vipune delete <id> |
Delete a memory |
vipune update <id> [text] |
Update content, metadata, type, or status |
vipune validate <text> |
Check if text is within embedding token limits |
vipune version |
Show version |
Complete CLI reference • Quickstart guide • Search guide • Architecture
Library Usage
vipune can also be used as a Rust crate for programmatic integration:
# Cargo.toml
[]
= "0.5.0"
use ;
// Initialize memory store
let config = default;
let mut store = new.expect;
// Add a memory
let project_id = "my-project";
let result = store.add_with_conflict
.expect;
// Search memories
let results = store.search
.expect;
for memory in results
v0.4+ features: MemoryType, MemoryStatus, supersedes flag, and telemetry (retrieval_count) are available for type-aware memory management. See the CLI reference for details.
Direct embedding (EmbeddingEngine): callers that embed directly (bypassing MemoryStore) use the role-aware API — embed_passage for stored text and embed_query for search/classification text. The engine prepends the model profile's prefix (e5: query: / passage: ; bge: none) before tokenisation, and the 512-token limit is enforced on the prefixed text (over-length input is rejected, never truncated). The engine performs no database model-identity check; if you keep vector caches outside vipune, key them by vipune::current_identity's ModelIdentity (model id + revision) so a model switch invalidates them.
See the crate documentation at docs.rs for complete API reference.
Configuration
vipune works with zero configuration. All paths use the user's home directory:
Default paths:
- Database:
~/.vipune/memories.db - Model cache:
~/.cache/huggingface/hub/(standard HuggingFace Hub layout, created byhf-hubon first download) - Config file:
~/.config/vipune/config.toml
Environment variables (override defaults):
VIPUNE_DATABASE_PATH- SQLite database locationVIPUNE_EMBEDDING_MODEL- embedding model profile:BAAI/bge-small-en-v1.5(default) orintfloat/multilingual-e5-small— see Embedding ModelsHF_HOME- HuggingFace cache home directory (changes the model cache location to$HF_HOME/hub)VIPUNE_PROJECT- Project identifier (overrides auto-detection)VIPUNE_SIMILARITY_THRESHOLD- Conflict detection threshold, 0.0-1.0 (default:0.85)VIPUNE_RECENCY_WEIGHT- Recency bias in search results, 0.0-1.0 (default:0.3)VIPUNE_HYBRID- Enable hybrid search (semantic + BM25), true/false or 1/0
Config file (~/.config/vipune/config.toml):
= "~/.vipune/memories.db"
= "BAAI/bge-small-en-v1.5"
= 0.85
= 0.3
MCP Server
vipune can act as an MCP (Model Context Protocol) server, enabling AI agents like Claude Code and Cursor to use it as a native memory provider.
Note: MCP is enabled by default. Library users can use
default-features = falsefor sync-only builds.
Setup (Claude Code)
Add to your Claude Code configuration (~/.claude/settings.json or project .claude.json):
Setup (Cursor)
Add to your Cursor MCP configuration with the same JSON structure.
Available Tools
- store_memory: Store information for later recall
- search_memories: Find memories by meaning (supports
hybridparam for semantic + BM25) - list_memories: List recent memories
- supersede_memory: Replace an existing memory with new content
- get_memory: Retrieve a memory by ID
- delete_memory: Permanently delete a memory
- update_memory: Update a memory's content, metadata, type, or status
See the Claude Desktop on macOS guide for a complete Desktop setup with configuration examples and macOS-specific gotchas.
Agent Integration
vipune works with any agent that can run shell commands — no plugins, adapters, or API keys required. Configure your agent with a few lines of instructions, grant shell command permissions, and the agent can use vipune search and vipune add to maintain persistent memory across tasks.
→ See Agent Integration Guide for per-tool setup instructions (Claude Code, Cursor, Windsurf, Cline, Roo Code, GitHub Copilot, Goose, Aider, OpenCode, Zed, and more).
See also: Search Guide for agent-friendly query patterns.
Exit codes for agent workflows:
0- Success1- Error (missing file, invalid input, etc.)2- Conflicts detected (similar memories found)3- Content too long (exceeds embedding token limit)
Recency Scoring
Search results can be weighted by recency using the --recency flag or VIPUNE_RECENCY_WEIGHT config:
# Increase recency bias (recent memories rank higher)
# Pure semantic similarity (no recency bias)
The final score combines semantic similarity and recency time decay:
score = (1 - recency_weight) * similarity + recency_weight * time_score- Default balance: 70% semantic, 30% recency
Benchmarks
The repo ships a Criterion benchmark for the Database::search path. It benchmarks search over a synthetic corpus of 384-dim vectors at 1k and 10k rows, seeded only through the public API — no ONNX model and no network access required.
Run the benchmark locally:
A plain cargo bench compares the run against a stored main baseline when one exists (lenient mode: reports differences, never fails on them) and never overwrites the baseline.
Record or refresh the main baseline deliberately — this is the only run that writes to the baseline store:
After a performance-shaped change, force a strict comparison against it (fails only if the baseline is missing; a regression is reported in red text but does not fail the run):
Or a lenient comparison (reports the difference, no failure if the baseline is missing):
Criterion stores results and baselines under target/criterion/. No committed baseline is bundled: baselines are machine-specific, so each machine records its own main baseline and later runs diff against that stable reference numerically.
The bench reports timings only — it asserts no timing thresholds, so a slower result is a signal to look at, never a build failure.
License
Apache-2.0 © Janni Turunen