| name | vipune |
| description | Mandates the use of the vipune memory CLI as the primary persistent memory store for software development agents. Search vipune at the start of any task to discover prior decisions, conventions, pitfalls, and validated patterns. Save new findings as typed memories—code conventions, architectural decisions, failed approaches, quality-gate gotchas, and ADR-lite rationale—at task boundaries. Covers issue/PR linkage, experiment tracking, pre-flight gotcha checks, dev-loop phase mapping, lightweight ADR capture, the 5-type memory taxonomy (fact/preference/procedure/guard/observation), active/candidate split, mandatory freshness verification, contradiction handling via --supersedes, and the hard prohibition on storing secrets. |
vipune memory skill for software development
This skill makes vipune — a CLI semantic memory store with project scoping, hybrid search, and typed memories — the authoritative persistent memory layer for software development agents working in codebases.
Codebases evolve, patterns accumulate, quality-gate gotchas emerge, and the agent learns which approaches fail and why. None of this should be re-learned session by session. vipune is the durable layer that prevents repeating mistakes, re-paving ground already walked, and losing the rationale behind decisions.
The mandate: search vipune at the start of every task, save vipune memories at task boundaries. This is not optional. It is the protocol.
Why vipune over file-based notes
- Semantic search finds memories by meaning, not exact keywords
- Project scoping is automatic via git remote — no cross-contamination between codebases
- Typed memories route by purpose (fact / preference / procedure / guard / observation)
- Conflict detection at 0.85 similarity flags duplicate memories before write
- Supersede atomically replaces outdated memories — no orphaned contradictions
- Status field (active/candidate) gives a long-term/short-term split
- Recency-weighted search gives implicit decay without losing data
- Metadata support for issue/PR linkage and experiment tracking
- Single binary, local SQLite — no API calls, no external service, no network dep
The protocol — two bookends and a watchpoint
1. Session open / new task — SEARCH FIRST
Before reading code, before running anything, before forming a plan:
vipune search "<topic the user mentioned>" --hybrid --recency 0.3 --limit 5
Read the returned memories. They tell you:
- The project's conventions (what tools, what style, what to NOT do)
- Past user corrections (don't repeat past mistakes)
- Validated workflows (recipes that already work)
- Known pitfalls and dead-ends
- Architecture decisions and their rationale
- Issue/PR context from previous work
- In-progress context that crossed a session boundary
For software development tasks, run 3–4 targeted searches:
- Domain/gotcha search:
vipune search "<domain> gotcha" --memory-type guard — what traps exist?
- Task-specific search:
vipune search "<task topic>" --hybrid — what do we know?
- Decision rationale search:
vipune search "<approach> why" --memory-type procedure — WHY was this chosen?
- Issue/PR recall (if issue number known): search gets IDs, then fetch details:
vipune get <id> --json | jq '.metadata' — what was decided?
Run searches across 2–3 angles before starting non-trivial work. Single-term queries score highest (0.81+); compound [domain] [component] [action] queries also work well. See "Search recipes" below.
2. Mid-task — SEARCH ON SURPRISE
If the codebase contradicts a recalled memory, or you encounter unexpected behavior, search again:
vipune search "<symptom> <component>" --hybrid --limit 5
The memory may explain the surprise (known gotcha), or you need to update/supersede a stale memory.
3. Task close / session end — SAVE WHAT'S WORTH KEEPING
At the natural end of a unit of work, distill the findings into typed memories:
vipune add "Build runs via 'cargo test --release' not 'cargo test' (release tests reveal UB)" \
--memory-type procedure
vipune add "Operator wants error messages to reference actual model_id from DB, not hardcoded MODEL_ID const" \
--memory-type guard
vipune add "Clippy::too_many_arguments triggers for wrapper functions, disable locally for thin wrappers" \
--memory-type guard
vipune add "Chose bge-small-en-v1.5 over bge-base-en-v1.5: 2.5x faster inference with <3% accuracy loss for code queries" \
--memory-type procedure
vipune add "tried async sqlite (rusqlite with tokio) — failed because model loading is CPU-bound, not I/O-bound" \
--memory-type observation --metadata '{"experiment":"async-db","failed":true}'
vipune add "CLI --local-dir flag uses HF Hub symlink layout, not direct model path" \
--memory-type fact --metadata '{"issue":135}'
Never save mid-debug. Only after a conclusion is reached. Mid-debug hypotheses are exactly the kind of false-positive context that becomes "memory rot."
Memory types — when to use which
vipune ships with 5 types. Each routes by purpose:
| Type | What it captures | Examples |
|---|
fact (default) | Objective truths about the project, codebase, or environment | "Project uses ONNX Runtime, not PyTorch" / "Embedding model pinned to bge-small-en-v1.5 revision main" / "Database path: ~/.vipune/memories.db" |
preference | How the user wants things done | "User prefers one-command test runs: cargo test over pytest-style suites" / "Always confirm before cargo clippy --fix" |
procedure | Validated step-by-step recipes AND decision rationale | "Quality gate: cargo fmt --check && cargo clippy -- -D warnings && cargo test" / "Chose zero-copy embeddings: avoids 40% memory overhead" |
guard | Things the agent MUST NOT do | "Never commit with --no-verify" / "Never store secrets in vipune" / "Never use MODEL_ID const in error messages; use actual model_id from DB" |
observation | Notable but not yet load-bearing context, including failed experiments | "Embedding inference ~20ms/cpu for bge-small on M1" / "tried async sqlite — failed because model loading is CPU-bound" / "cargo test release mode triggers UB in float parsing" |
Default to fact only when nothing more specific fits. Be aggressive about typing — typed memories filter better, decay better, and survive consolidation passes.
Failed approaches and experiments: Store as observation with appropriate metadata (e.g., {"experiment":"async-db","failed":true} or {"issue":147,"attempt":"refactor"}). This distinguishes "we tried X and it failed" from "never do X" — the former is provisional learning, the latter is a hard constraint.
Active vs candidate — the long-term / short-term mechanism
Vipune has no built-in TTL. Use --status instead:
| Status | Behavior | Use for |
|---|
active (default) | Surfaces in default searches | Validated, durable knowledge |
candidate | Hidden unless --include-candidates is passed | Provisional observations, hypotheses, things that may not outlast this session |
vipune add "cargo build --release takes ~30s for incremental changes" \
--memory-type observation --status candidate --metadata '{"timing": "release-build"}'
vipune update <id> --status active
Promote candidates only after the fact survives a second confirmation. Otherwise, let them sit — they're inert until explicitly queried. This is the short-term tier.
Issue/PR linkage — connecting memories to work
Use the --metadata flag to link memories to GitHub issues or PRs:
vipune add "Error messages must reference actual model_id from DB, not MODEL_ID const" \
--memory-type guard --metadata '{"issue":135}'
vipune add "tried parsing HF Hub cache directly -- failed because symlink layout differs from --local-dir layout" \
--memory-type observation --metadata '{"issue":138,"experiment":"hf-cache-parsing","failed":true}'
vipune add "Supersede requires similarity check before accepting replacement" \
--memory-type procedure --metadata '{"pr":84}'
Recall by issue/PR context:
Note: Search results include ID and content, not metadata. To get issue/PR-linked memories:
vipune search "issue 135" --json | jq -r '.results[].id' | xargs -I {} vipune get {} --json | jq 'select((.metadata | fromjson? // {} | .issue) == 135)'
vipune search "failed" --memory-type observation
This pattern creates traceability: future agents can see exactly what was decided and why, linked to the original issue context.
Freshness verification — REQUIRED before acting
Every memory is a snapshot of when it was written. Code moves, files rename, decisions reverse. Before acting on a retrieved memory, verify it against current state. This is the single biggest gap in most agentic memory implementations.
| Memory claims | Verification step |
|---|
A file exists at path/src/foo.rs | ls path/src/foo.rs |
| A function/symbol exists | grep -r "function_name" . or rg "function_name" |
| A CLI flag is supported | Check --help or the actual command surface |
| A version is pinned at X | Read the lockfile / image tag / Cargo.toml |
| A user preference is current | If context suggests it may have changed, ask |
| An issue/PR still exists | Search the GitHub repo or check git log |
If verification fails: update or delete the stale memory immediately. Don't just ignore it — the next session will hit the same trap.
vipune add "New corrected statement" --supersedes <old-id> --memory-type fact --metadata '{"supersedes_issue":135}'
Contradiction handling — supersede, don't duplicate
When you save a memory similar to an existing one (≥ 0.85 similarity), vipune flags a conflict. Three responses:
- The new is correct, the old is wrong →
vipune add "..." --supersedes <old-id> (atomic replacement)
- Both are true but address different angles →
vipune add "..." --force (intentional coexistence — rare)
- The old is fine, no new memory needed → don't add
Default to option 1 if the facts contradict. Never let two contradictory memories coexist — that's the leading cause of "memory rot" where agents confidently cite stale info while also citing the correction.
Pre-flight gotcha check — before quality gates
Before running tests, linting, or other quality gates, search for known gotchas:
vipune search "<domain> gotcha" --memory-type guard
vipune search "clippy gotcha" --memory-type guard
vipune search "test gotcha" --memory-type guard
vipune search "embedding gotcha" --memory-type guard
If relevant gotchas surface, address them before running quality gates. This pattern prevents wasted CI runs and repeated failures from known issues.
Dev-loop phase → action mapping
Map vipune actions to the software development loop:
| Phase | vipune search | vipune save |
|---|
| Read issue | Search for prior context: vipune search "issue <number>", vipune search "<task domain>" --hybrid | Store initial understanding if novel: vipune add "Issue scope: ..." --memory-type fact --metadata '{"issue":N}' |
| Plan | Search for patterns: vipune search "<approach> pattern", vipune search "<domain> gotcha" --memory-type guard | Store chosen approach with rationale: vipune add "Choosing X over Y because: ..." --memory-type procedure --metadata '{"issue":N}' |
| Implement | Search mid-stream on surprises: vipune search "<symptom> <component>" --hybrid | Store discovered patterns mid-task as candidate: vipune add "..." --memory-type observation --status candidate |
| Test | Pre-flight gotcha check: vipune search "<domain> gotcha" --memory-type guard | Store test/lint findings: vipune add "Test revealed X" --memory-type guard |
| Commit | Search for commit conventions: vipune search "commit message" --memory-type preference | Store completion + link to PR: vipune add "Completed issue N: ..." --memory-type fact --metadata '{"issue":N,"pr":M}' |
Lightweight ADR — capturing decision rationale
Store WHY an approach was chosen or rejected, not just WHAT. This creates an ADR-lite layer that survives rotation:
vipune add "Chose bge-small-en-v1.5 over bge-base-en-v1.5: 2.5x faster inference with <3% accuracy loss for code queries; tested on 500 sample queries" \
--memory-type procedure --metadata '{"adr":"embed-model-choice"}'
vipune add "Rejected async sqlite (rusqlite with tokio): model loading is CPU-bound, not I/O-bound; async adds complexity without benefit; verified via profiler" \
--memory-type observation --metadata '{"adr":"rejected-async-db"}'
vipune add "Using 0.85 as conflict threshold: balances precision/recall for 500+ memories; 0.90 too strict, 0.80 creates noise" \
--memory-type fact --metadata '{"adr":"conflict-threshold"}'
Future agents searching for "<approach> why" or "rejected will surface the rationale, preventing circular re-decisions.
Search recipes — what actually works
From vipune's own query-guide scoring data:
| Pattern | Example | Top score |
|---|
| Single technical term | vipune search "Caddyfile" | 0.81 |
[Domain] [Component] | vipune search "embed model" | 0.80 |
[Feature] [Implementation] | vipune search "hybrid search" | 0.79 |
[Component] [Action] [Concept] | vipune search "conflict detection threshold" | 0.78–0.81 |
[Problem] [Technical Context] | vipune search "clippy too_many_arguments" | 0.80 |
[Approach] [Rationale] | vipune search "why bge-small" | 0.76–0.80 |
| Natural-language question | vipune search "how do we detect conflicts" | ~0.70 (still useful) |
Hybrid mode helps short / proper-noun queries:
vipune search "MCP" --hybrid
vipune search "ConfigClass" --hybrid
Recency-weighted for "what changed recently":
vipune search "deployment" --recency 0.7 --limit 10
Type-filtered for narrow recall:
vipune search "do not" --memory-type guard
vipune search "test" --memory-type guard
vipune search "chose" --memory-type procedure
vipune search "procedure" --memory-type procedure
For issue/PR context, search by content and inspect metadata per-result:
vipune search "memory" --json | jq -r '.results[] | "\(.id): \(.content)"'
Score thresholds:
- 0.80+ — Use the result directly (verify freshness first)
- 0.70–0.79 — Cross-check top 2–3
- 0.60–0.69 — Refine the query
- < 0.60 — Probably nothing relevant; don't act on it
What to ALWAYS save vs NEVER save
ALWAYS (long-term, active):
- Project conventions (build tool, package manager, lint config, CI pipeline)
- Quality-gate gotchas (test quirks, linting exceptions, known failures)
- User preferences and corrections (style, format, tone, depth)
- Architectural decisions and the reason behind them (ADR-lite)
- Validated workflows (the exact commands that work)
- Known pitfalls + their workarounds
- Non-obvious constraints (compliance, performance, security)
- Issue/PR decisions and their rationale
- Failed approaches / experiments (with metadata)
NEVER (defeats the purpose, causes bloat or actual harm):
- Secrets of any kind — API tokens, passwords, SSH private keys, .env contents. Vipune stores in plaintext SQLite. This is a hard line. If you discover a secret in memory,
vipune delete immediately and rotate the credential.
- Raw conversation turns
- One-off file paths likely to move
- Intermediate debugging hypotheses
- Ephemeral stack traces
- Anything reconstructible cheaply from
git log or current code state
CONDITIONAL (use --status candidate, promote only if it survives):
- Tentative explanations for observed behavior
- Performance numbers (they drift)
- "I think this is how X works" claims
- Process state mid-task
Periodic consolidation — the reflection pass
Every ~10–15 sessions on a project, run:
vipune list --limit 50 --json | jq .
Look for:
- Duplicates (≥ 2 memories saying nearly the same thing) → supersede the older ones
- Stale facts (still
active but contradicted by current code) → delete or supersede
- Drift (multiple
observation-type memories on the same theme) → consolidate into one fact or procedure, then delete the originals
- Candidates that have survived 5+ sessions → promote to
active
- Candidates untouched for months → delete
- Orphaned issue references → verify issues still open/relevant, update or delete
This is the reflection pattern from the agentic memory literature (Park et al. 2023, A-MEM 2025, MemoryBank). It prevents the bloat-driven degradation that hits unbounded memory stores around 50+ sessions.
Failure modes to avoid
Real failure patterns documented in agentic memory systems — don't repeat them:
- Stale-fact citation — Quoting a memory that no longer matches reality. Mitigation: freshness verification before acting (above).
- Contradiction accumulation — Two memories that disagree, both surfacing in search. Mitigation:
--supersedes, never --force unless they truly address different angles.
- Context bloat — So many memories surface that the context window saturates and reasoning quality drops. Mitigation: aggressive pruning, candidate status, tight
--limit.
- Ephemeral leakage — Persisting raw conversation-context as memory. Mitigation: only save distilled conclusions, never raw turns.
- Premature commit — Saving a hypothesis mid-debug. Mitigation: save only at task close, after the conclusion holds.
- Secret leakage — A single token or password committed to memory is a breach. Mitigation: the explicit NEVER list above + immediate delete + rotate if found.
- Issue-link rot — Metadata referencing resolved/closed issues without context. Mitigation: periodic consolidation checks for orphaned references.
Quick reference
vipune search "<technical term or compound>" --hybrid --recency 0.3 --limit 5
vipune search "<domain> gotcha" --memory-type guard
vipune search "issue 135" --json | jq -r '.results[].id' | xargs -I {} vipune get {} --json | jq -r '"\(.id): \(.metadata // "(no metadata)")"'
vipune add "<distilled fact/preference/procedure/guard/observation>" \
--memory-type <type> [--status candidate] [--supersedes <old-id>] [--metadata '{"issue":N}']
vipune list --limit 50
vipune update <id> --text "<new content>"
vipune add "<new content>" --supersedes <old-id> --memory-type <type>
vipune delete <id>
vipune validate "<text>"
vipune list --json | jq .
vipune search "<q>" --json | jq '.results[]'
Project scoping
vipune auto-detects the project from the git remote. Override only when intentionally cross-cutting:
vipune search "<q>" -p some-other-project
Default behavior is correct ~99% of the time — don't override unless you know why.
When to NOT use vipune
This skill is opinionated, but there are legitimate non-vipune paths:
- One-shot session info that won't outlast the conversation → keep in context, don't persist
- Code-level documentation that belongs in comments / docstrings / READMEs → put it there, not in vipune
- User-cross-project preferences that belong in user-wide memory (e.g., the file-based
MEMORY.md system in ~/.claude/projects/.../memory/) → those go there; vipune is project-scoped
- Anything sensitive → no memory tool; use a real secrets manager (1Password, op, vault)
vipune is for project-scoped, persistent, semantic-recall-worthy knowledge. Use it for that. Don't use it as a notes file.
Pre-flight checklist (start of any software development task)
Closing checklist (end of any software development task)