@djolex999/vir-cli 0.3.0 → 0.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/package.json +7 -1
  2. package/AGENTS.md +0 -201
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@djolex999/vir-cli",
3
- "version": "0.3.0",
3
+ "version": "0.3.1",
4
4
  "description": "Distills Claude Code sessions into a compounding knowledge vault",
5
5
  "author": "Djordje Marković <djordje@growthq.rs>",
6
6
  "license": "MIT",
@@ -24,6 +24,12 @@
24
24
  "bin": {
25
25
  "vir": "dist/cli.js"
26
26
  },
27
+ "files": [
28
+ "dist",
29
+ "assets",
30
+ "README.md",
31
+ "LICENSE"
32
+ ],
27
33
  "scripts": {
28
34
  "build": "tsc",
29
35
  "dev": "tsc --watch",
package/AGENTS.md DELETED
@@ -1,201 +0,0 @@
1
- # vir
2
-
3
- Local macOS daemon that distills Codex session transcripts into an
4
- Obsidian vault.
5
-
6
- Published to npm as `@djolex999/vir-cli` (scoped). Install:
7
- `npm install -g @djolex999/vir-cli`. Repo: https://github.com/djolex999/vir. Reads `~/.Codex/projects/**/*.jsonl`, filters by heuristic,
8
- classifies with Haiku, extracts durable knowledge with Sonnet, writes typed
9
- notes to the vault, maintains an index, and syncs back into AGENTS.md files.
10
-
11
- ## Stack
12
-
13
- - Node.js ≥ 20, TypeScript strict (`noImplicitAny`, `noUncheckedIndexedAccess`)
14
- - `commander` for CLI, `chalk` for output, `zod` for config validation
15
- - `better-sqlite3` for state (synchronous — no async complexity)
16
- - `@anthropic-ai/sdk` for the Anthropic path; native `fetch` for the Kie path
17
- - macOS `launchd` for the daemon, `osascript` for notifications
18
- - `vitest` for unit tests (`npm test` → `vitest run`)
19
-
20
- ## Structure
21
-
22
- ```
23
- src/
24
- cli.ts # commander entry — every subcommand wired here
25
- config.ts # ~/.vir/config.json schema + helpers
26
- pipeline/
27
- run.ts # orchestrator (scan → filter → scrub → distill → write)
28
- scanner.ts # walk ~/.Codex/projects, SHA-256 each .jsonl
29
- parser.ts # extract assistant/user text, tool calls, files touched
30
- filter.ts # heuristic scorer (length, tools, files, signal words)
31
- scrubber.ts # strip API keys, bearer tokens, absolute paths, emails
32
- distiller.ts # callLLM helper, Haiku classify + Sonnet extract,
33
- # withRateLimitRetry, buildAnthropicClient,
34
- # normalizeModelName
35
- writer.ts # frontmatter + body, wikilink injection, index/log
36
- summarizer.ts # per-project knowledge summaries
37
- types.ts # ParsedSession, Classification, DistilledNote, Category
38
- search/
39
- retriever.ts # unified async search: embeddings → TF-IDF fallback
40
- embedder.ts # Ollama integration (nomic-embed-text), cosine sim
41
- synthesizer.ts # Codex synthesis for `vir query`
42
- lint/
43
- linter.ts # orphans, staleness, contradictions
44
- dedupe/
45
- detector.ts # candidate pairs + LLM judgment
46
- merger.ts # archive / keep / Sonnet-merge
47
- Codex/
48
- updater.ts # VIR:START / VIR:END block management
49
- state/
50
- db.ts # better-sqlite3, idempotent migrations
51
- daemon/
52
- launchd.ts # plist render + load/unload via spawnSync('launchctl')
53
- mcp/
54
- server.ts # @modelcontextprotocol/sdk stdio server — vir_query,
55
- # vir_status, vir_recent_notes, vir_project_summary;
56
- # thin facade over search+synthesize+summarizer, opens
57
- # StateDb read-only, logs to stderr only
58
- install.ts # register/unregister with Codex via
59
- # spawnSync('Codex', ['mcp', 'add'|'remove'|'list', …])
60
- ui/
61
- display.ts # the ONE place console.log lives — palette, glyphs,
62
- # header(), divider(), box(), spinner(), summary(),
63
- # wrap(), sourceRow(), categoryRow()
64
- ```
65
-
66
- ## Key conventions
67
-
68
- - **Path expansion.** All paths from config (`vaultPath`, `claudeProjectsDir`)
69
- must run through `expandHome()` (or `os.homedir()` directly) before use.
70
- Never assume `~/` works in `fs.*`.
71
- - **Per-session try/catch.** The daemon must never die on a single bad
72
- transcript. `pipeline/run.ts` wraps every session iteration and records the
73
- error in the DB so it doesn't keep retrying.
74
- - **Provider routing.**
75
- - `provider: 'anthropic'` → `@anthropic-ai/sdk` (`buildAnthropicClient`).
76
- - `provider: 'kie'` → native `fetch` to `https://api.kie.ai/Codex/v1/messages`
77
- with `Authorization: Bearer <kieApiKey>`. The SDK is **not** used for Kie
78
- even with a `baseURL` override — its `x-api-key` header conflicts.
79
- - Both flow through `callLLM(config, client, opts)`.
80
- - On the Kie path the Anthropic SDK is **never instantiated** —
81
- `maybeAnthropicClient()` returns `null` for `provider: 'kie'`, and
82
- `callLLM` accepts `Anthropic | null` (guards before the Anthropic branch).
83
- All 6 LLM callers use `maybeAnthropicClient`, not `buildAnthropicClient`.
84
- - **Model names.** `normalizeModelName(model, provider)` collapses any model
85
- that *starts with* a Kie canonical id (`Codex-haiku-4-5`,
86
- `Codex-sonnet-4-6`) back to the bare id. Fallback strips trailing
87
- `-YYYYMMDD`. Anthropic path passes through untouched.
88
- - **Retries.** Every LLM call is wrapped in `withRateLimitRetry()` — three
89
- attempts with `60s / 120s / 240s` backoff. The `isRetryable()` predicate
90
- retries `429` on both providers; on the Kie path (`HttpError`) it also
91
- retries transient `5xx` (`500/502/503/504`). The Anthropic SDK path stays
92
- `429`-only — the SDK retries `5xx` itself, so we don't double-retry.
93
- `isRetryable` is exported and covered by `distiller.test.ts`. Sessions are
94
- processed sequentially with a `2s` delay after each successful distill.
95
- - **State is the source of truth.** `~/.vir/vir.db` records every session by
96
- path with its SHA-256 hash. Reruns are idempotent; a session is only
97
- re-distilled if its file content changes. Migrations are additive only
98
- (`PRAGMA table_info(sessions)` + `ALTER TABLE ADD COLUMN`).
99
- - **VIR:START / VIR:END markers are sacred.** `sync-Codex` only mutates bytes
100
- between those markers. When a AGENTS.md has no block, the new one is
101
- appended; the rest of the file is preserved verbatim.
102
- - **No comments explaining obvious things.** Comment WHY a non-obvious
103
- invariant exists — never WHAT the code does.
104
- - **All user-facing output goes through `ui/display.ts`.** Other modules
105
- must not call `console.log` directly. The pipeline's `fileLog()` writes
106
- to `~/.vir/daemon.log` in plain text regardless of UI mode; the
107
- display module renders only when `!opts.quiet`.
108
- - **Ollama is best-effort.** `writer.maybeEmbed()` and the search path
109
- both probe via `isOllamaAvailableCached()` and silently fall back
110
- (TF-IDF for query; no-op for writer). An embedding failure must never
111
- fail a write.
112
- - **Cost prompts respect intent.** `--yes`, `--daemon`, and
113
- `--rewrite-only` all skip the > 20 new-session confirmation. The
114
- daemon path *never* prompts (it has no tty).
115
- - **`vir init` is an @inquirer/* wizard.** Arrow-key `select` for
116
- provider and models, `input` with `validate` for keys/numbers,
117
- `confirm` for the "create missing path?" flow. Don't fall back to
118
- raw readline here — the rest of the file already imports it for
119
- dedupe/sync-Codex/cost prompts, but the init UX is the wizard.
120
- - **`vir run` prints a preflight line.** `N files found · M cached · K
121
- new` shows after the scan in dim text, and the same triple goes to
122
- the daemon log. Diagnoses "fresh DB looks stale" misconfigurations
123
- in one line.
124
- - **MCP server is a read-only facade.** `vir mcp run` (and the bare `vir mcp`
125
- alias) start an `@modelcontextprotocol/sdk` stdio server that reuses
126
- `search()`, `synthesize()`, `summarizeProject()`, and
127
- `db.listDistilled()`/`getStats()` — no new pipeline logic. It opens
128
- `StateDb` with `{ readonly: true }` (skips the WAL pragma + migrations) and
129
- must never mutate state. stdout is the JSON-RPC channel, so **all logs go to
130
- stderr** via `process.stderr.write`. `SearchHit` carries no category/project,
131
- so `vir_query` recovers them by parsing each note's frontmatter.
132
- - **MCP registration shells out to `Codex`.** `vir mcp install/uninstall/
133
- status` call `Codex mcp add|remove|list` via `spawnSync` arg-arrays (never
134
- shell strings). A missing `Codex` binary (spawnSync `error.code === 'ENOENT'`)
135
- must yield an actionable message, never a crash — `vir mcp status` shows
136
- "Codex CLI not detected". The registration runs `vir mcp`, which is why the
137
- bare alias must keep launching the server.
138
- - **`vir --version` reads `package.json` at runtime.** `program.version()` loads
139
- it from one dir up from `dist/cli.js` (rootDir is `./src`, so it can't be
140
- imported) — never hardcode the version string or it drifts on every bump.
141
- - **Tests run on Vitest.** `npm test` (`vitest run`). Test files are
142
- `*.test.ts` colocated with source and listed in `tsconfig.json` `exclude`,
143
- so `tsc`/`npm run build` never emit them to `dist/` (and `src/` is
144
- npmignored, so they don't ship). Vitest discovers them via its own glob.
145
- Import the unit-under-test with a `.js` extension (`./distiller.js`) per
146
- NodeNext — Vitest resolves it to the `.ts`. Start with pure functions.
147
- - **`sync-Codex` resolves project paths flexibly.** `projectClaudePath(slug)`
148
- checks `~/projects/<slug>`, `~/projects/<slug>-*` (glob), `~/code/<slug>`,
149
- and `~/dev/<slug>` (first existing wins; canonical fallback). The resolved
150
- path flows into `PlanItem.target` and shows as the dry-run plan heading.
151
-
152
- ## Commands
153
-
154
- ```
155
- vir init # interactive setup
156
- vir run # one pass (used by daemon)
157
- vir run --full # ignore state cache, re-process everything
158
- vir run --rewrite-only # re-render notes from stored content
159
- # (no scan, no LLM, free)
160
- vir run --yes # skip the > 20 sessions cost prompt
161
- vir schedule install # write + load ~/Library/LaunchAgents plist
162
- vir schedule uninstall # unload + remove plist
163
- vir status # daemon state + knowledge base breakdown
164
- vir query "<question>" # embeddings (Ollama) → TF-IDF fallback
165
- # → Codex synthesis
166
- vir embed # generate Ollama embeddings for notes
167
- vir embed --force # regenerate all embeddings
168
- vir summarize <project> # generate per-project summary
169
- vir summarize --all # all projects with notes
170
- vir lint # orphans + stale + contradictions
171
- vir lint --orphans # orphans only (free)
172
- vir lint --stale # staleness only (free)
173
- vir lint --contradictions # contradictions only (Haiku tokens)
174
- vir dedupe # interactive duplicate review + merge
175
- vir sync-Codex # diff then confirm AGENTS.md updates
176
- vir sync-Codex --dry-run # diff only, never write
177
- vir sync-Codex --force # apply without confirmation
178
- vir sync-Codex <project> # specific project only
179
- vir sync-Codex --global # only ~/.Codex/AGENTS.md
180
- vir mcp # run MCP server over stdio (alias for run)
181
- vir mcp run # run MCP server over stdio
182
- vir mcp install # register with Codex (Codex mcp add)
183
- vir mcp install --scope project # register at project scope (default: user)
184
- vir mcp uninstall # unregister (Codex mcp remove vir)
185
- vir mcp status # check registration (Codex mcp list)
186
- ```
187
-
188
- ## File locations
189
-
190
- - Config: `~/.vir/config.json`
191
- - State: `~/.vir/vir.db` (better-sqlite3 with WAL). A one-shot rename in
192
- `StateDb`'s constructor moves the pre-rename `~/.vir/state.db` over to
193
- `~/.vir/vir.db` if it's still around — preserves cache, hides the
194
- history of the doc/code mismatch.
195
- - Daemon log: `~/.vir/daemon.log`
196
- - launchd plist: `~/Library/LaunchAgents/lab.growthq.vir.plist`
197
- - MCP registration: managed by Codex (`Codex mcp add/remove`), not by
198
- vir — user scope writes to `~/.Codex.json`, project scope to `.mcp.json`
199
- - Vault notes: `<vaultPath>/<outputDir>/{patterns,gotchas,decisions,tools,projects,archived}/`
200
- - Vault index: `<vaultPath>/<outputDir>/index.md`
201
- - Vault run log: `<vaultPath>/<outputDir>/log.md`