` | cheap | Override distill model: `haiku` \| `sonnet` |
| `vir projects` | free | Per-project triage: counts + pending cost |
| `vir projects include ` | free | Track a project |
| `vir projects exclude
` | free | Stop tracking (existing notes untouched) |
| `vir cost` | free | API cost report (total/median/p90/top) |
| `vir query ""` | cheap | Semantic search your vault |
| `vir queries` | free | Retrieval report: method split, degraded rate, dead-weight notes |
| `vir compose ""` | $$ | Synthesize a topic page from related notes |
| `vir connect --dry-run` | free | Find lessons you keep re-learning; show candidates + cost |
| `vir connect` | $ | Propose those lessons as cited rules (≤10 LLM calls) |
| `vir summarize ` | cheap | Cross-session project synthesis |
| `vir summarize --week` | cheap | Period summary of the week's notes |
| `vir lint` | cheap | Find orphans, stale notes, contradictions |
| `vir dedupe` | cheap | Interactive duplicate detection + merge |
| `vir review` | free | Walk new notes: approve/edit/reject |
| `vir review --restore ` | free | Put one rejected note back |
| `vir audit` | cheap | Model judges each note; verdicts feed `vir review --audited` |
| `vir audit --dry-run` | free | Notes, batches and est. cost, no model call |
| `vir review --audited` | free | Walk notes vir audit flagged, worst first |
| `vir review --insights` | free | Accept, edit or reject rules proposed by `vir connect` |
| `vir audit --apply-rejects` | free | Move fresh reject verdicts to `.rejected/` (reversible) |
| `vir lint --strays` | free | Note files with no live DB row (retitle debris) |
| `vir lint --strays --fix` | free | Move retitle debris to `archived/` (only when a live copy exists) |
| `vir lint --legacy-related` | free | Pre-0.12.0 notes whose stored Related holds content a rewrite drops |
| `vir lint --legacy-related --fix` | free | Move that content into `## Details`, re-render the notes |
| `vir prune` | free | Dry run: agent-derived notes to demote |
| `vir prune --apply` | free | Demote them to `.rejected/` (never deletes) |
| `vir prune --restore` | free | Put every pruned note back, exactly |
| `vir sync-claude` | free | Inject top knowledge into CLAUDE.md (and existing AGENTS.md; `--no-agents`, `--agents-only`; `--backfill-lessons` is a cheap paid step for older gotchas) |
| `vir embed` | free | Generate embeddings for semantic search |
| `vir embed --setup` | free | Install the local embedding provider (no Ollama needed) |
| `vir schedule install` | free | Register the background daemon |
| `vir status` | free | Knowledge base breakdown + daemon status |
| `vir doctor` | cheap | 15 install/config checks |
| `vir notifications` | free | Allow vir's macOS notifications + send a test |
| `vir reconcile` | $$ | Retry sessions that failed, cache-bypassed |
| `vir mcp install` | free | Register the MCP server with Claude Code |
Most commands take `--dry-run`, `--yes`, or `--json` where they make sense;
run `vir --help` for the full flag list. `vir query --json` and
`vir doctor --json` are the machine contracts the
[vir-obsidian](https://github.com/djolex999/vir-obsidian) plugin consumes.
## Use vir from any agent (skill)
vir ships an [Agent Skills](https://agentskills.io) skill that teaches Claude
Code, Codex, Cursor and other skill-aware agents to check your notes before
they work: search with `vir query --json` (free, no LLM call), cite the notes
they use, and verify them against the current code.
```bash
npx skills add djolex999/vir
```
The skill is read-only. It never writes your notes, never runs paid commands
like `vir run` unless you ask, and if vir isn't installed it tells you how to
set it up instead of installing it. It prefers the MCP server when that's
registered, and falls back to reading the notes folder directly. Source:
[`skills/vir/SKILL.md`](skills/vir/SKILL.md).
## MCP server (Claude Code and Codex)
Vir runs as an MCP server, letting your coding agent consult your vault
mid-session instead of relying on static CLAUDE.md / AGENTS.md content.
```bash
vir mcp install # Claude Code: registers via `claude mcp add`
vir mcp install --target codex # Codex: prints the config to add
```
For Codex, vir never edits `~/.codex/config.toml` itself; it prints the block
(or run `codex mcp add vir -- vir mcp`):
```toml
[mcp_servers.vir]
command = "vir"
args = ["mcp"]
```
Restart the agent. The vault is now queryable mid-session via six tools:
`vir_query`, `vir_status`, `vir_recent_notes`, `vir_recent_articles`,
`vir_project_summary`, `vir_compose`. `vir_query` takes a `type` filter
(`session` | `article` | `topic` | `pdf` | `all`). Human-verified notes
(approved via `vir review`) get a +0.2 score boost; pass `verified_only: true`
to see only those. The server never writes the vault. `vir_query` is the one tool
that spends tokens: it synthesizes an answer with your provider, and
`synthesize: false` returns just the matching notes.
To unregister: `vir mcp uninstall`.
## Semantic search (optional)
Vir works out of the box with keyword search (TF-IDF). No embedding setup is
required, ever. For semantic search, pick one of two providers; vir detects
whichever is present:
**One command, no Ollama:**
```bash
vir embed --setup # installs fastembed + bge-small-en-v1.5 into ~/.vir
# (~233 MB + ~128 MB model; states cost, asks first)
```
**Or Ollama** (768d, slightly larger model):
```bash
brew install ollama
ollama pull nomic-embed-text
ollama serve
```
Then:
```bash
vir embed
vir query "how do I handle rate limiting in Next.js"
```
Every stored vector records the model that produced it. Vectors from
different models are never compared; if you switch providers,
`vir embed --force` re-embeds the index after telling you what it costs.
Falls back to keyword search automatically when no provider is available,
and says so: `via tfidf (no provider)`. Results rank by pure relevance by
default; `retrievalDiversity` (0..1) turns on MMR reranking, which trades
relevance for diversity. A verified note gets a small boost only when it
already ranks near the top, so it wins near-ties without displacing more
relevant notes.
## Config reference
Located at `~/.vir/config.json`.
| Field | Default | Description |
| ------------------- | --------------------------- | ---------------------------------------------------------- |
| `vaultPath` | (required) | Absolute path to your notes folder (Obsidian vault optional) |
| `outputDir` | `vir` | Subdir inside vault |
| `claudeProjectsDir` | `~/.claude/projects` | Claude Code sessions |
| `cadenceHours` | `3` | Daemon run frequency (hours) |
| `provider` | `anthropic` | `anthropic` \| `claude-cli` (subscription, keyless) \| `kie` |
| `anthropicApiKey` | (unset) | Required if `provider=anthropic` |
| `kieApiKey` | (unset) | Required if `provider=kie` |
| `filterThreshold` | `0.4` | Heuristic pre-filter (0..1) |
| `articlesDir` | (unset) | Folder of clipped articles. Unset → article ingestion off |
| `pdfsDir` | (unset) | Folder of PDFs. Unset → PDF ingestion off |
| `workflowTranscripts` | `exclude` | Workflow/sidechain transcripts: `exclude` \| `include` |
| `agentTranscripts` | `exclude` | Headless SDK agent transcripts: `exclude` \| `include` |
| `projects` | (unset) | Per-project `include`/`exclude` map; absent = undecided |
| `filterToolCalls` | `moderate` | Tool-output filtering: `aggressive` \| `moderate` \| `off` |
| `retrievalDiversity`| `0` | MMR diversity (0..1); 0 = pure relevance |
| `logQueries` | `true` | Log retrievals to `~/.vir/queries.jsonl`; `false` = off |
| `embeddingProvider` | (unset) | `ollama` \| `local` \| `none`; unset = auto-detect |
| `models.classify` | `claude-haiku-5-5` | Classify model (Kie keeps Haiku 4.5) |
| `models.distill` | `claude-sonnet-5` | Distill model for decision-heavy and large sessions |
| `models.distillFast`| (unset) | Cheap model for routine sessions; set → hybrid routing on |
| `models.distillThreshold` | `100000` | Input-token ceiling above which `distill` is forced |
| `pricing` | (built-in) | Optional per-provider `$/1M` overrides |
## 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 clips folder
pdfs/ # distilled PDFs and papers
topics/ # synthesized topic pages (vir compose)
projects/ # cross-session project summaries
summaries/ # weekly/monthly period summaries (derived, never indexed)
archived/ # deduplicated notes (kept, never deleted)
```
## State & logs
```
~/.vir/config.json # configuration
~/.vir/vir.db # SQLite (hashes, embeddings, content)
~/.vir/cost.log # per-call cost records (JSONL)
~/.vir/daemon.log # daemon run log
~/.vir/queries.jsonl # retrieval log (JSONL, rotates at 5 MB, one .1 kept)
```
### Query logging
Every `vir query` and MCP `vir_query` retrieval appends one record to
`~/.vir/queries.jsonl`: the query text, which notes surfaced at what rank and
score, the search method (embedding vs TF-IDF fallback), and latency. It never
records the synthesized answer.
This log stays on your machine — it is **never transmitted anywhere**. It
exists so `vir queries` can tell you which notes actually earn their place in
retrieval (and which never surface), and it is the baseline for future ranking
improvements. Delete it any time; disable it with `"logQueries": false` in
`~/.vir/config.json`.
## Project status
| | |
| -------------- | ----------------------------------------- |
| Version | 0.24.2 |
| Tests | 1073 passing |
| Platforms | macOS (launchd), Linux (systemd/cron) |
| Node | 20+ |
| First-run cost | $1 to $5 (Kie.ai optional, ~72% cheaper) |
| Ongoing cost | ~$0.05 per run |
## Roadmap
Shipped:
- [x] Linux support (systemd timer + cron fallback), experimental
- [x] Active learning: `vir review`, verified notes boosted in retrieval
- [x] Web article ingestion (a clipped-markdown folder → same vault)
- [x] PDF and paper ingestion
- [x] Obsidian plugin: [vir-obsidian](https://github.com/djolex999/vir-obsidian), sidebar queries against the vault
- [x] Topic synthesis: `vir compose` builds topic pages from related notes
- [x] Transcript filtering: workflow, sidechain, and SDK-agent transcripts detected and skipped by default
- [x] Project triage: `vir projects`, per-project include/exclude with pending-cost estimates
- [x] Duplicate detection and merge: `vir dedupe`
- [x] Obsidian optional: any folder of markdown works (0.24.3)
- [x] Any agent can read your notes: `npx skills add djolex999/vir` (0.25.0)
- [x] Recurring lessons become reviewed rules: `vir connect` (0.25.0)
- [x] Codex sessions, and a keyless `codex-cli` provider (0.26.0)
Not built:
- [ ] Windows support
- [ ] GUI installer for non-developers
- [ ] More input sources: code repos, images
- [ ] Export to anchor-plugin skill format
- [ ] Cursor sessions (Codex shipped in 0.26.0)
- [ ] Connect pass v2: flag decisions that contradict each other or went stale
## Contributing
PRs welcome. Open an issue first for large changes. Built with TypeScript
strict; run `npm run build` and `npm test` before submitting. See
[CONTRIBUTING.md](CONTRIBUTING.md) for development setup.
```bash
git clone https://github.com/djolex999/vir
cd vir
npm install
npm run build
npm test
```
## License
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.
If vir saves you time, [a coffee on Ko-fi](https://ko-fi.com/djmarkovic) keeps it
maintained and in step with Claude Code and Codex releases.
[GitHub](https://github.com/djolex999) ·
[LinkedIn](https://www.linkedin.com/in/djmarkovic/) ·
[npm](https://www.npmjs.com/~djolex999) ·
[GrowthQ Lab](https://growthqlab.com)