v4.0.0
The kit becomes vkm-kit: one plug-and-play efficiency suite for Claude Code —
persistent vault memory + token-saver + local usage doctor + spec-builder. The
repo is now Vahlame/create-vkm-kit; the installer package is
@vkmikc/create-vkm-kit (bins create-vkm-kit and vkm), and the old
@vkmikc/create-obsidian-memory name lives on as a forwarding shim — see the
migration notes in ADR-0041/0050. Machine identifiers your installs depend on
are FROZEN: the MCP server key obsidian-memory-hybrid, the obsidian-memoryd
daemon, and BASIC_MEMORY_HOME. Managed blocks and hook files written by 3.x
are recognized and migrated in place on the next install (dual-read markers,
vkm-kit:start/end sentinels, VKM_VAULT env accepted alongside the legacy
names).
Changed
- Rename (ADR-0041):
packages/create-obsidian-memory→
packages/create-vkm-kit; ownership markervkm-kit(legacy
create-obsidian-memorystill recognized); managed-block sentinels
<!-- vkm-kit:start/end -->with in-place migration of legacy blocks
(test-pinned);VKM_VAULTaccepted beforeBASIC_MEMORY_HOME/
OBSIDIAN_MEMORY_VAULT; release workflow publishes the renamed installer
plus the shim.
Security
- RAG-passthrough MCP tools no longer accept a caller-supplied
vaultpath.
vault_hybrid_search,vault_fts_search,vault_fts_index,vault_complete,
vault_relations,vault_observations,vault_kg_suggest,
memory_extract_candidates,vault_audit, andvault_memory_reportexposed
an optionalvaultparameter with no validation against the configured
default — a prompt-injected note could coerce an agent into pointing one of
these tools at an arbitrary directory on disk, indexing and searching its
.mdfiles (a cross-directory read oracle) and creating a
.obsidian-memory-rag/sidecar there. Removed the parameter from all 10
tool schemas, matching the posture the filesystem tools (vault_read_file,
vault_write_file,vault_edit_file,vault_list_directory) already had.
See ADR-0040.
Added
-
vkm-specpackage (ADR-0046/0047/0048) — the spec-builder pipeline: a one-line
idea + vault context (oneassembleContextcall) compiles into an editable
<orchestration_package>XML prompt, optionally enriched by a LOCAL Ollama
draft (phi4-mini:3.8b-q4_K_M,/api/chatwithformat=<JSON schema>
constrained decoding, fetch-only client, typed failures) — then reviewed by
the human (GUI on127.0.0.1:4923with an SSE draft stream and a visible
ollama/fallback source badge, orvkm-specCLI with$EDITORreview) and
pasted into Claude Code / Claude web / any AI. The deterministic fallback is
structural:buildSpecalways compiles a working prompt and the LLM draft
only overwrites fields on success — pinned by a degradation-gate test (Ollama
down → SSEerrorframe still carries a working XML). The installer can
auto-provision Ollama + the model under explicit--fullor--ollama
(winget silent on Windows, ~2.3GB pull, best-effort — nevercurl|sh, never
a surprise download on a bare install;--no-ollamaopts out). -
vkm skills + subagent template (ADR-0049) —
/vkm-discipline(execution
contract: same functionality and quality in fewer readable lines — density,
never reduced scope; vault-first context via oneassemble_contextcall;
terse output; nothing is "done" without executed evidence) and/vkm-spec
(turn a one-line idea into a precise, context-grounded spec in-session — no
local LLM in the synchronous path) installed into~/.claude/skills/, plus
thevkm-implementersubagent template in~/.claude/agents/. All
hash-tracked (--skills/--agentstoggles; user-modified files survive
uninstall), with a lint gate keeping every skill description ≤300 chars. The
managed rules block gains one "Executable discipline (vkm)" bullet (ES/EN,
within existing budgets; AGENTS.md + install docs re-synced — drift gates
green). -
assemble_contextMCP tool (ADR-0045) — one call returns a budgeted context
bundle for a task (typed[decision]s from the project note, non-decision
observations + relevant cross-note passages,#stackfacts, and a raw-note
fallback excerpt when a project has no typed decisions yet), replacing the
3-6 discrete search/read round-trips an agent would otherwise chain. Engine
relocated from the prompt-compiler'scontext-search.mjsinto
obsidian-memory-mcp/src/context-assemble.mjs(paralleljson-hybrid-search --graph+ twojson-observationspasses over the same Python bridge).
Honors the security posture of every RAG tool: no wire-levelvaultparam
(env-resolved only, pinned by test), untrusted-data_trustenvelope with
injection flagging, and abudget_charscap (default 6000) that trims
passages-first so decisions survive longest. Measured and CI-locked by the
newbench-assemblegate (--assert-savings 0.60 --assert-answered 0.90):
on the labelled fixture, median 68% fewer wire tokens than the naive
multi-call pattern at 100% completeness (10,867 vs 30,816 tokens aggregate;
cross-cutting queries save an honest 45%, project tasks 68% — and the naive
arm pays no discovery cost, so these are floors). -
vkm-doctorpackage + local telemetry (ADR-0044) — a zero-dependency local
OTLP/HTTP JSON sink (vkm-otel-sink, 127.0.0.1:4319 → NDJSON rollups in
~/.vkm/telemetry/, 90-day prune, lockfile singleton, tolerant parser that
archives unknown metric shapes instead of dropping them) plus avkm-doctor
CLI that reports token usage per day/model/type, the cache-hit ratio, cost,
and a "broken cache" diagnosis (high input volume with near-zero cacheRead).
The installer wires Claude Code's OTEL export to the sink (managedenv
block;--no-telemetryopts out) and aSessionStarthook spawns the sink
when it isn't running — no OS service. Local only: nothing leaves the
machine. Transcript scanning exists only as a clearly-labeled
transcript-estimatefallback (the JSONL format is officially unstable). -
Token-saver module (ADR-0043) in
create-obsidian-memory— on by default for
Claude Code installs (--no-token-saver/--no-terse-styleopt out;
VKM_TOKEN_SAVER=0is a runtime kill switch). Three pieces, all reconciled
symmetrically and fully reversed by--uninstall: (1) aPostToolUsehook
(matcherBash) that compacts noisy shell output before it enters context —
ANSI stripped,\rprogress repaints reduced to their final frame, runs of
identical lines collapsed, >200-line logs windowed to head+tail — while a
hard, test-gated guarantee keeps every error/warn/fail/exit-code line and the
final lines verbatim (≥30% reduction on the noisy CI fixture, zero
diagnostic loss); (2) aPostToolUsehook (matchermcp__.*) that
re-serializes pretty-printed JSON tool results into compact form
(whitespace-only — parsed data, including the_trustenvelope, is
byte-identical afterJSON.parse); (3)permissions.denyread rules for
token-hungry artifacts (node_modules/**,**/dist/**, lockfiles) plus the
vkm-terseoutput style (keep-coding-instructions: true), installed
hash-tracked and activated via theoutputStylesetting. -
Shared settings-write infrastructure (
settings-io.mjs,settings-writers.mjs,
asset-install.mjs) increate-obsidian-memory— Phase 0 groundwork for the
vkm-kit efficiency-suite train. The safe~/.claude/settings.jsonidiom
(read→BOM-strip→parse with invalid-JSON backup, pure managed-hook merge/remove
deduped by filename stem, JSON sanity parse, restricted backups, atomic
tmp+rename writes, ownership-marker checks) is extracted out of
claude-native-memory.mjsintosettings-io.mjs— behavior unchanged, existing
suite as the refactor guard — and joined by new pure writers for the sections
upcoming modules manage (env,permissions.deny/allow,outputStyle;
removal only reverses values still provably ours) plus a managed-asset
installer that tracks installed template files by SHA-256 in a
~/.claude/vkm-kit.assets.jsonsidecar so uninstall deletes only unmodified
files. -
Cross-process lock for the Go daemon's git-sync. Two
obsidian-memoryd
instances (or a daemon plus a manualsync once/git operation) pointed at
the same vault could previously raceadd/commit/pull/pushagainst
the same working tree. See ADR-0040. -
create-obsidian-memory --uninstall. Fully reverses this kit's
Claude Code integration (hook entries,autoMemoryEnabledoverride, and the
hook script files it owns) without touching a user's own unrelated hooks or
same-named files. Turning an enforcement flag off on a re-run
(--no-effort-gate,--no-memory-enforcement,--no-native-memory-override)
now also removes the corresponding previously-installed hook instead of
leaving it active. See ADR-0040.
Removed
obsidian-prompt-compilerpackage (ADR-0046) — absorbed intovkm-spec.
Its pure modules (compile-xml,prompt-defaults,project-resolve,
clipboard,review) moved verbatim; its retrieval was already relocated to
obsidian-memory-mcp/src/context-assemble.mjs(ADR-0045). The GUI moved off
port 4317 (OTLP collision) to 4923.
Fixed
- Go daemon
doctorcould report healthy while actually blind. A failed
git add/commit/pull(not justpush) now feeds the same health state
doctoralarms on; a repeatedrebase --abortnow feeds the alarm too
(previously only displayed, never alarmed); a silently-failing filesystem
watcher startup (fsnotify.NewWatcher) now sets an immediate alarm instead
of leaving the daemon looking idle-but-fine with a heartbeat that never
starts; a sync aborted by intentional shutdown (Ctrl-C / service stop) no
longer counts toward the "vault not syncing" alarm;doctor's unpushed-
commit git call now has a timeout like every other git call in the file,
instead of being able to hang forever. - Go daemon push retries now re-pull first. A push rejected because the
remote advanced was retried identically on every attempt with no chance of
succeeding; retries now re-pull/rebase before retrying the push. doctor's exit code / output ordering under the shipped-H windowsgui
build. Root-caused to PowerShell not waiting for a GUI-subsystem child
process unless its output is redirected (not fixable from inside the
process) — documented the correct invocation pattern
(docs/en/troubleshooting.md,docs/es/troubleshooting.md,
docs/observability.md) and added a defensive writer flush beforedoctor
returns.obsidian-memoryd inspect --last -5no longer panics on a negative
line count.vault_completeandmemory_extract_candidatesresults now carry the
same untrusted-data envelope as every other RAG tool — both previously
returned vault-derived content (autocomplete matches, memory-dedup
snippets) with no_trust/injection-flagging, unlikevault_hybrid_search
and friends.- No subprocess timeout on the MCP↔Python RAG bridge. A hung/misbehaving
Python backend could previously block an MCP tool call (and leak the
process) indefinitely; configurable viaOBSIDIAN_MEMORY_RAG_TIMEOUT_MS
(default 120s). - Claude Code hook commands no longer corrupt on vault paths containing a
quote or a trailing backslash. Switched from an interpolated shell string
to Claude Code's exec form (command+argsarray), which eliminates the
escaping bug class entirely rather than patching it. - Hook transcript scans are now incremental.
guard-effort-gateand
stop-vault-close-reminderused to re-read and re-parse the entire session
transcript on every gated event (measured ~75ms on a 25MB transcript,
compounding across a long session); they now cache the last-scanned offset
and only read the new suffix, falling back to a full rescan on any doubt. - Backup files (
.bak.*ofsettings.json/mcp.json) are now actually
permission-restricted on Windows. The existingchmod 0600protection was
unconditionally skipped onwin32— the platform this repo is chiefly used
on — despite the code's own stated intent of protecting files that may
contain tokens. Now applies an equivalenticaclsACL restriction. vault_complete's autocomplete no longer surfaces tags found inside
fenced code blocks. A note documenting#tagsyntax inside a code fence
polluted the autocomplete trie with the example text as if it were a real
tag.- Usage-boost retrieval scoring no longer lets stale-but-in-window usage
permanently outrank a new note. Theusagelever (ADR-0038) counted a use
fully anywhere inside its 90-day window and not at all outside it; it now
weights each use by recency-decay before scoring, closer to the lever's
original "recent activity should matter more" intent. - A vector index built with one embedder no longer silently grows a second,
redundant index under another. Every RAG CLI call that didn't pass an
explicit--embedderresolved one independent of whatever embedder had
actually built the existing on-disk index; it now reuses the on-disk
identity when the caller passed no explicit override. memory-report --duplicates/memory-reflectno longer report "zero
near-duplicates" indistinguishably from "duplicate detection was skipped."
Above the 1500-note pair-comparison cap, the scan now signals that it was
skipped instead of returning a bare empty result.
See ADR-0040 for the design-level decisions in this pass (cross-process lock,
MCP wire-schema lockdown, hook exec-form, installer uninstall symmetry,
incremental transcript cache, Windows ACL backup protection, usage-decay
scoring, embedder-identity reuse).