@djolex999/vir-cli
v0.8.3
Published
An LLM Wiki for Claude Code, in your Obsidian vault.
Maintainers
Readme
The pattern
In April 2026, Andrej Karpathy described a pattern he calls the LLM Wiki — AI work that feeds back into itself through a persistent, curated, structured artifact, instead of resetting at the end of every session. He published the idea file at karpathy/llm-wiki.md and ended his post saying: "I think there is room here for an incredible new product instead of a hacky collection of scripts."
Several open source implementations of this pattern now exist (lucasastorian/llmwiki, Pratiyush/llm-wiki, nashsu/llm_wiki among them). Each takes a different shape.
Vir is the Obsidian-native one. It treats Obsidian as the primary frontend — not just a storage location — and integrates deeply (sidebar plugin coming, dataview-compatible frontmatter, canvas integration planned). It reads AI coding session transcripts retroactively, so months of existing history become a queryable knowledge base in one run.
Why this exists
Every Claude Code session produces patterns, gotchas, and architecture decisions.
Almost all of it ends up in ~/.claude/projects/**/*.jsonl — transcripts you
open once and never again. The knowledge is real; the storage is a graveyard.
Vir reads those transcripts, distills the durable knowledge into typed markdown
notes in your Obsidian vault, and feeds the best of it back into your CLAUDE.md
files — so every future session starts sharper than the last. It's a concrete
implementation of the pattern above.
What's coming
Vir is actively developed. In the next 30–60 days:
- Obsidian plugin v1 — sidebar, command palette, canvas integration, submitted to the community plugin marketplace
- Multi-agent support — Codex CLI, Cursor, Aider, Cline (one per release)
- PDF/paper ingestion — broaden beyond developer workflows
Track progress via GitHub issues or watch the repo for releases.
Quality controls
Auto-distilled notes can be wrong. The most common concern from early users: "if your distillations are wrong, Claude treats them as truth and you get worse results, not better." Fair. Vir addresses it in layers:
- Confidence scores on every note, written into the frontmatter
(
confidence: 0.xx). A cheap heuristic pre-filter drops low-signal sessions before any LLM call; classification then scores what survives, and anything at or below0.6is dropped before the more expensive distill step. Only high-confidence notes reach the vault. - Opt-in
CLAUDE.mdsync. Nothing vir generates touches your prompt context automatically.vir sync-claudeshows a diff and waits for your confirmation — you decide what reaches Claude. - Plain markdown output. Every note is a file in your Obsidian vault. Read it, edit it, delete it. Nothing is hidden in a compressed database you can't inspect.
- Lint and dedupe.
vir lintflags contradictions and stale notes;vir dedupemerges similar notes that have drifted apart. - Active learning via
vir review. Walk through new distillations and approve, edit, or reject each one. Verified notes get retrieval priority over unverified ones (invir queryand the MCP server). Rejected notes are moved to.rejected/— recoverable, not deleted. - MMR-diverse retrieval. Queries return notes covering different aspects of the topic, not 5 similar duplicates. The retrieval algorithm balances relevance against diversity automatically.
- Topic synthesis via
vir compose "<topic>". Embedding-searches the vault for related session notes and articles, then synthesizes them into a single topic page undertopics/, with each source wikilinked so it backlinks in Obsidian's graph.--dry-runpreviews the sources and cost for free. - Cost transparency.
vir run --dry-runestimates per-session cost before you spend a cent;vir costreports the actuals (total, median, p90, top sessions) from a local~/.vir/cost.log; and--force-model haiku|sonnetlets you calibrate quality against price. Pricing is provider-aware (Anthropic list rates + Kie's discount), so the numbers reflect your bill — not a blended guess. Kie rates are approximate; override them inconfig.pricingif a report looks off. - Reliable failures (v0.8.0). Every command now exits non-zero on failure —
no more silent successes hiding broken distills.
vir reconcile --dry-runreports any pre-0.8.0 sessions that landed with empty content (e.g. via the v0.7.1 Kie-200-with-body-error bug);vir reconcileretries them, bypassing the SHA-256 processed-cache for just those rows. Retries that fail again leave the row untouched so a second pass can catch them once the underlying cause is fixed. Kie 404s carrying anapi_errorbody envelope (transient service hiccups) are now retried alongside 429/5xx.
The bet: with these controls, signal-to-noise stays high enough that the vault
is a net positive. If your discipline is strong enough to maintain CLAUDE.md
and lessons.md by hand, you may not need this. If — like most of us — you let
those files drift after the first week, Vir catches what slips through.
How it works
Vir reads your transcripts from ~/.claude/projects/**/*.jsonl, runs each
session through a cheap heuristic filter, classifies the survivors with Haiku,
and distills durable knowledge with Sonnet. Before distillation it filters tool
calls — preserving intent (file paths, commands, search patterns, errors, short
results) while truncating large embedded content (file writes, long bash logs,
big grep dumps) to keep token cost bounded (tunable via filterToolCalls).
Results are written as typed notes — patterns, gotchas, decisions, tools —
cross-linked with wikilinks and indexed. State lives in local SQLite; content
hashes make reruns idempotent. Optional Ollama embeddings power semantic search,
and an MCP server exposes the whole vault to Claude Code mid-session.
Web articles saved to a raw/ directory (e.g. via Obsidian Web Clipper) flow
through a parallel pipeline with its own taxonomy — concept, technique,
reference, opinion — filed under articles/ in the same vault, embedded and
indexed alongside session notes, and queryable through the same MCP tools.
Articles always keep their source URL in frontmatter for backlinks; distillation
paraphrases and never reproduces more than a short quote.
Claude Code sessions
↓
vir
↓
Obsidian vault
↓
CLAUDE.md
↓
better sessions
↓
...How Vir compares
The LLM Wiki space has grown fast. Honest comparison:
vs other LLM Wiki implementations
| | Vir | lucasastorian/llmwiki | Pratiyush/llm-wiki | nashsu/llm_wiki |
| --------------------------------- | ---------------------------- | ----------------------------- | ---------------------------------------- | ------------------------- |
| Language | TypeScript / Node | Python | Python | Cross-platform desktop |
| Distribution | npm install -g | Local app + hosted SaaS | git clone + python | Desktop app installer |
| Obsidian integration | Native (plugin v1 in dev) | Markdown output | Outputs to vault | Own UI, no Obsidian |
| Input sources | Claude Code, Web Clipper (more agents coming) | PDFs, docs upload | Claude Code, Cursor, Cline, Codex, Gemini | Documents, mixed sources |
| Retroactive on existing sessions | ✓ | n/a | from install forward | n/a |
| MCP server | ✓ | ✓ | ✓ | ✓ |
| License | MIT | open source + hosted commercial | MIT | open source |
vs Claude Code memory tools
| | Vir | claude-mem | claude-memory | mem0 |
| ----------------------------------- | ---------------- | -------------------- | ------------------ | ----------------- |
| Reads existing Claude Code sessions | ✓ | from install forward | from install forward | n/a |
| Markdown output | ✓ | ChromaDB | LanceDB | various backends |
| Setup | npm install -g | Bun + uv + Python | pnpm + LM Studio | API/cloud setup |
| License | MIT | Apache 2.0 | MIT | open core + cloud |
Different tools for different needs:
- If you want a polished cross-platform desktop app for general document knowledge bases, use lucasastorian/llmwiki or nashsu/llm_wiki.
- If you want multi-agent support with rich entity/concept page taxonomy and don't care about Obsidian integration depth, use Pratiyush/llm-wiki.
- If you want a heavyweight Claude Code memory plugin with real-time capture and vector storage, use claude-mem.
- If you're building AI applications that need to remember users long-term, use mem0 (different layer entirely).
- If you want an Obsidian-native LLM Wiki that reads your existing Claude Code sessions and is on its way to supporting multiple AI coding agents, use Vir.
Real-world results
Real output from the author's first run across 226 Claude Code sessions.
| Metric | Value | | ------------------- | ------------------------------------------------- | | Sessions scanned | 226 | | Notes distilled | 126 | | Avg confidence | 0.91 | | High signal (≥0.8) | 121 of 126 | | Projects covered | 8 projects | | Knowledge breakdown | 54 patterns · 47 decisions · 23 gotchas · 2 tools |
These numbers show Vir works at scale, not that it's uniquely capable. Other LLM Wiki implementations would produce similar results with the same input. The distinguishing question for Vir is workflow fit — does the Obsidian-native + retroactive + multi-agent positioning match how you actually work?
Example query against the distilled vault:
$ vir query "what gotchas should I know about my auth implementation"Based on the notes, here are the key auth gotchas:
JWT dual-token setup needs silent refresh on mount — access tokens expire in 15 min. Without a mount-time refresh check, users hit 401s on first load after a break. Middleware runs before the session is hydrated — do not read session data in middleware to gate routes. Check the JWT directly from the cookie instead. Password reset tokens must be single-use and hashed at rest — storing raw tokens in the DB leaks them if the DB is compromised. Hash with bcrypt before storing, compare on redemption. OAuth callback URLs must be registered exactly — trailing slashes, http vs https, and localhost port mismatches all cause silent redirect failures with no useful error message. Logout must clear both the access token cookie and the refresh token — clearing only one leaves the session partially alive and causes confusing re-auth loops.
sources 4 · via embedding · searched 126
Prerequisites
- macOS or Linux (systemd or cron)
- Node.js 20+
- Claude Code (sessions at
~/.claude/projects/) - Obsidian vault
- Anthropic API key or Kie.ai API key (~72% cheaper, same models)
- Optional: Ollama +
nomic-embed-textfor semantic search
Install
npm install -g @djolex999/vir-cliQuick start
vir init # guided wizard: provider, models, vault, cadence,
# and an optional web-articles (raw/) folder
vir run # one pass over your sessions → notes in your vault
vir schedule install # register the daemon (runs every 3h by default)vir init asks whether you save web articles to a folder (e.g. Obsidian Web
Clipper). Point it at that raw/ directory and Vir distills those articles into
the same vault. Leave it blank to keep Vir session-only.
vir schedule install works on Linux too: systemd is preferred, with cron used
as a fallback when systemctl isn't available.
Cost
Vir runs two API calls per session: a Haiku classify (cheap) and a Sonnet distill (the main cost). Cost depends on session size and your provider.
Real cost shape (verified on 226 historical sessions via Kie)
| Metric | Sonnet (current default) | Haiku (opt-in via config) | |---|---|---| | Median session | $0.07 | $0.025 | | p90 session | $0.20 | $0.07 | | Long-tail outliers (5-hour epics) | $0.25-$0.30 | $0.08-$0.10 | | 226-session backfill | ~$21 | ~$7 |
Costs assume Kie.ai pricing (~28% of Anthropic direct). Multiply by ~3.5× for Anthropic direct rates.
What drives cost
Distill output dominates. A multi-hour Claude Code epic with hundreds of tool calls and architectural decisions distills to ~4500 output tokens at $4.27/M = $0.02 just for output, plus 25-30k input tokens at $0.85/M = $0.02. Skills, tool result payloads, and code blocks compound the input side. Vir v0.7.0 ships skill-stripping that drops average distill cost 60-70% versus pre-v0.7.0 builds, but multi-hour sessions remain the long tail.
Cost controls in v0.7.0
vir runshows a cost estimate before any API call when more than 20 new sessions are queued. Accept withy, decline withn, skip with--yes.vir cost --since 7daggregates real (not estimated) token usage from~/.vir/cost.log.vir cost --by-sessionsurfaces outliers for cost investigation.vir cost --top 5shows your most expensive sessions.vir run --dry-runpreviews per-session cost projections before the live run. Estimates are recalibrated from real v0.7.0 token data and run as a rough projection; actual cost varies with session content.
Hybrid routing
Haiku is ~3× cheaper than Sonnet and captures equal-or-more concrete detail on routine and tool-heavy sessions. Calibration data shows it only misses higher-order judgment/architectural lessons on decision-heavy and very large sessions. Hybrid routing exploits that: route routine sessions to Haiku, reserve Sonnet for where it matters.
When models.distillFast is set, each session is routed after classification:
category === "decision"→models.distill(Sonnet)inputTokens > models.distillThreshold(default100000) →models.distill- otherwise →
models.distillFast(Haiku)
New installs (vir init) enable hybrid by default — distillFast is set to the Haiku model. Existing installs are unaffected on upgrade: with distillFast unset, models.distill is used for every session exactly as before. Opt in by adding "distillFast": "claude-haiku-4-5" (Kie) or "claude-haiku-4-5-20251001" (Anthropic) to models in ~/.vir/config.json.
vir run --force-model haiku|sonnet bypasses hybrid routing entirely and forces every session to the named model for that run — useful for A/B comparison. Force-model wins over hybrid.
To go all-cheap instead, set models.distill itself to the Haiku model (quality degrades on decision-heavy and very large sessions).
Platform support
| Platform | Daemon | Notifications | Status | | --------------- | ------------------ | ------------- | ------------ | | macOS | launchd | osascript | Stable | | Linux (systemd) | systemd user timer | notify-send | Experimental | | Linux (cron) | crontab | notify-send | Experimental | | Windows | Not supported | — | Planned |
Linux support is experimental and untested — vir schedule install prefers
a systemd user timer and falls back to a crontab entry when systemd is absent.
Please report issues at
github.com/djolex999/vir/issues
with your distro, init system, and Node version.
Commands
| Command | Cost | Description |
| --------------------------- | ----- | ----------------------------------------- |
| vir init | free | Interactive setup |
| vir run | cheap | Process new sessions |
| vir run --full | $$ | Reprocess all sessions |
| vir run --rewrite-only | free | Reformat notes, no API calls |
| vir run --articles-only | cheap | Distill only web articles, skip sessions |
| vir run --yes | cheap | Skip cost confirmation |
| vir run --dry-run | free | Estimate per-session cost, exit before LLM |
| vir run --force-model <m> | cheap | Override distill model: haiku | sonnet |
| vir cost | free | API cost report (total/median/p90/top) |
| vir cost --since <dur> | free | Cost within a window, e.g. 7d 24h 2w |
| vir cost --by-session | free | Full per-session cost distribution |
| vir query "<question>" | cheap | Semantic search your vault |
| vir query … --json | cheap | Machine-readable results for tooling |
| vir compose "<topic>" | $$ | Synthesize a topic page from related notes |
| vir compose … --dry-run | free | Preview sources + cost, exit before LLM |
| vir summarize <project> | cheap | Cross-session project synthesis |
| vir summarize --all | $$ | Summarize all projects |
| vir lint | cheap | Find orphans, stale notes, contradictions |
| vir lint --orphans | free | Orphan check only |
| vir lint --stale | free | Staleness check only |
| vir lint --contradictions | cheap | Contradiction check (Haiku) |
| vir dedupe | cheap | Interactive duplicate detection |
| vir review | free | Walk new notes: approve/edit/reject |
| vir review --project <s> | free | Review one project's notes |
| vir review --all | free | Re-review, including verified notes |
| vir sync-claude | free | Inject top knowledge into CLAUDE.md |
| vir sync-claude --dry-run | free | Preview changes, no writes |
| vir sync-claude --force | free | Apply without confirmation |
| vir embed | free | Generate embeddings for semantic search |
| vir embed --force | free | Regenerate all embeddings |
| vir schedule install | free | Register the background daemon |
| vir schedule uninstall | free | Remove the background daemon |
| vir status | free | Knowledge heatmap + daemon status |
| vir doctor | cheap | Diagnose installation issues |
| vir doctor --json | cheap | Machine-readable install/health snapshot |
| vir reconcile --dry-run | free | Report sessions that silently failed pre-0.7.2 |
| vir reconcile | $$ | Retry those sessions — bypasses cache for them only |
Both vir query and vir doctor accept --json for programmatic consumers
(e.g. the vir-obsidian plugin). query --json
emits a JSON array of results to stdout ([] when none) and, on failure, a
single-line error object to stderr with empty stdout. doctor --json emits one
JSON object and always exits 0 (health lives in its daemon field).
MCP server (Claude Code integration)
Vir runs as an MCP server, letting Claude Code consult your vault mid-session instead of relying on static CLAUDE.md content.
Register Vir with Claude Code:
vir mcp installRestart Claude Code. The vault is now queryable mid-session via five tools:
vir_query, vir_status, vir_recent_notes, vir_recent_articles,
vir_project_summary. vir_query takes a type filter
(session | article | all) so Claude can scope a search to your dev
sessions or your saved articles. Human-verified notes (approved via
vir review) are ranked first; pass verified_only: true to vir_query or
vir_recent_notes to see only those.
To unregister:
vir mcp uninstallSemantic search (optional)
Vir uses TF-IDF by default. For semantic search via embeddings:
brew install ollama
ollama pull nomic-embed-text
ollama serveThen in a new terminal:
vir embed
vir query "how do I handle rate limiting in Next.js"Falls back to TF-IDF automatically if Ollama is not running.
Vir uses MMR (Maximum Marginal Relevance) reranking to balance relevance and
diversity in query results. Instead of returning 5 notes that all say similar
things, you get 5 notes covering different aspects of the topic. Tunable via
retrievalDiversity in config (default 0.3, range 0.0–1.0; higher = more
diverse).
Config reference
Located at ~/.vir/config.json.
| Field | Default | Description |
| ------------------- | --------------------------- | ---------------------------------------------------------- |
| vaultPath | — | Absolute path to Obsidian vault |
| outputDir | vir | Subdir inside vault |
| claudeProjectsDir | ~/.claude/projects | Claude Code sessions |
| cadenceHours | 3 | Daemon run frequency (hours) |
| provider | anthropic | anthropic or kie |
| anthropicApiKey | — | Required if provider=anthropic |
| kieApiKey | — | Required if provider=kie |
| filterThreshold | 0.4 | Heuristic pre-filter (0..1) |
| articlesDir | (unset) | raw/ dir for web articles. Unset → article ingestion off |
| distillArticles | true | Distill articles alongside sessions (needs articlesDir) |
| filterToolCalls | moderate | Tool-output filtering: aggressive | moderate | off |
| retrievalDiversity| 0.3 | MMR diversity (0..1): 0.0 = pure relevance, 1.0 = pure diversity |
| models.classify | claude-haiku-4-5-20251001 | Classify model |
| models.distill | claude-sonnet-4-6 | Distill model — the "smart" model for decision/large sessions |
| models.distillFast| (unset) | Cheap model for routine sessions. Set → hybrid routing on; unset → distill used for everything |
| models.distillThreshold| 100000 | Input-token ceiling above which a session is forced to distill |
| pricing | (built-in) | Optional per-provider $/1M overrides (inputPer1M/outputPer1M). Anthropic defaults track list rates; Kie defaults are approximate — verify on your Kie dashboard |
Vault structure
vault/vir/
index.md # full catalog of every note Vir has written
log.md # chronological append log of each run
patterns/ # reusable approaches worth repeating
gotchas/ # bugs, footguns, and edge cases
decisions/ # architecture decisions with their rationale
tools/ # per-tool knowledge and usage notes
articles/ # web articles distilled from your raw/ folder
projects/ # cross-session project summaries
archived/ # deduplicated notes (kept, never deleted)State & logs
~/.vir/config.json — configuration
~/.vir/vir.db — SQLite (hashes, embeddings, content)
~/.vir/daemon.log — daemon run logProject status
| | | | -------------- | ----------------------------------------- | | Tests | 147 passing | | Platforms | macOS (launchd), Linux (systemd/cron) | | Node | 20+ | | First-run cost | $1–5 (Kie.ai recommended for 72% savings) | | Ongoing cost | ~$0.05 per run |
Roadmap
- [x] Linux support (systemd timer + cron fallback) — experimental
- [x] Active learning —
vir reviewto approve, edit, or reject distillations, with verified notes prioritized in retrieval - [x] Web article ingestion — distill markdown clipped via Obsidian Web Clipper into the same vault (the LLM Wiki pivot)
- [ ] More input sources — PDFs, code repos, images (the full LLM Wiki pattern)
- [ ] Windows support
- [ ] GUI installer for non-developers
- [ ] Obsidian plugin for in-vault queries
- [ ] Export to anchor-plugin skill format
- [ ] Support for Cursor and other AI editors
Contributing
PRs welcome. Open an issue first for large changes. Built with TypeScript
strict — run npm run build to check before submitting. See
CONTRIBUTING.md for development setup and how to regenerate
the demo GIF.
git clone https://github.com/djolex999/vir
cd vir
npm install
npm run build
npm testLicense
MIT
Author & credits
Built by Djordje Marković / GrowthQ Lab DOO.
Vir (вир) is the Serbian word for whirlpool — the place where a river pulls everything in and concentrates it. Sessions flow in, Vir pulls out what matters, and deposits it somewhere permanent.
Inspired by Andrej Karpathy's LLM Wiki pattern and Uros Pesic's KB Brain concept.
GitHub · LinkedIn · npm · GrowthQ Lab
