Vault Change Feed
approvedby kains
Records vault changes as a machine-readable feed with per-reader cursors, so AI assistants can incrementally catch up on what you changed. - This plugin has not been manually reviewed by Obsidian staff.
Vault Change Feed
Give your AI agents a changelog of your vault. Every edit you make is recorded as a machine-readable event feed with per-reader cursors — so any AI can catch up on what changed since its last visit, instead of blindly rescanning thousands of notes.
中文文档 · Obsidian Community Listing
Support / 支持
If this plugin saves you time, you can buy me a coffee — it keeps the development going. 如果这个插件帮你省了时间,可以请我喝杯咖啡 ☕
- International:

- 中国大陆: 爱发电(微信 / 支付宝直达 · WeChat / Alipay)
Features
- Live change tracking — create / modify / delete / rename events with line-level diff stats (
+added / −removed) - Offline backfill — startup reconciliation catches edits made while Obsidian was closed (phone, iCloud sync, CLI tools); content-hash rename detection included
- Incremental AI reads — each reader (AI agent) keeps its own cursor and pulls only what's new; per-file merge on read collapses edit bursts into one cumulative line
- Self-describing to agents — a reading-protocol block is auto-installed into
AGENTS.md/CLAUDE.md/GEMINI.mdon first run, so coding agents discover the feed with zero setup - Rotation + stale signal — the log is capped (90 days / 50k entries by default); readers are told explicitly when they must do a full rescan
- Fully local — no network, no telemetry, works on desktop and mobile
The problem
Your AI assistant has no idea what you edited between sessions. Scanning the whole vault every time is expensive; not scanning means it works from stale knowledge. This plugin continuously records which file changed, how, and by how much — the AI pulls that incrementally, on demand.
Installation
Community market (recommended): Settings → Community plugins → Browse → search Vault Change Feed → Install → Enable. The AI protocol block installs itself on first run.
BRAT: add kains2866/vault-change-feed as a beta plugin.
Manual: copy main.js and manifest.json from the latest release into <vault>/.obsidian/plugins/vault-change-feed/, then enable the plugin.
How it works
- Live: listens to Obsidian's create / modify / delete / rename events and computes line-level diff stats
- On startup: reconciles against the last baseline snapshot to backfill changes made while Obsidian was closed. A delete+create pair with identical content hash is reported as a rename (binary files re-downloaded by iCloud get a fresh mtime and may degrade to delete+create)
- All data stays local, under
<configDir>/plugins/vault-change-feed/:changelog.jsonl— the event stream, one JSON event per linecursors.json— per-reader read cursorsbaseline.gz— content baseline snapshot (for diffs and reconciliation)
Note: the plugin has built-in standby protection — concurrent instances coordinate through a heartbeat writer lock (writer.lock); only the lock holder records, other instances stand by (read-only API still works) and automatically take over once the lock goes stale (90 s). Still, prefer running it in only one Obsidian instance per vault at a time.
Event format
{"seq": 1284, "ts": 1785000000000, "op": "modify", "path": "ML/overfitting.md", "stat": {"added": 12, "removed": 3}, "source": "live"}
op:create/modify/delete/rename(carriesoldPath) /resync(baseline rebuilt — full rescan advised)stat:{added, removed}line counts;nullmeans "changed, magnitude unknown — open the file" (binaries, oversized files)source:live/reconcile(startup backfill) /system
Letting AI agents discover the feed
AI agents don't know the feed exists out of the box. On first enable, the plugin automatically installs a reading-protocol block (wrapped in <!-- vault-change-feed:start/end --> markers) into the vault-root AGENTS.md, CLAUDE.md, and GEMINI.md — the conventional discovery points for coding agents (AGENTS.md is the cross-tool standard; CLAUDE.md for Claude Code; GEMINI.md for Gemini CLI, which doesn't read AGENTS.md by default). Zero clicks needed.
- Opt-out: disable
Auto-install AI protocol on first runto fall back to a one-time notice; theInstall AI protocol for agents/Remove AI protocol from agent filescommands remain available - Idempotent: re-runs update only the marked block; your own content outside the markers is preserved verbatim
- Refreshes with the plugin: after updates, installed blocks are refreshed automatically — but auto-sync only touches files that already have a block, it never creates new ones (can be disabled)
- Clean removal: the remove command strips the block from all three files, deleting a file only if nothing else remains
- Per-file toggles: Sync AGENTS.md / Sync CLAUDE.md / Sync GEMINI.md
- AIs without filesystem access (plain web chats) use the
Copy unread changes for AIcommand instead
Optional: SessionStart hook (enforced, not "please read")
The protocol block relies on the AI choosing to read it. extras/vault-feed-hook.mjs goes further: hooked into an agent's SessionStart event, it injects merged unread changes into the context automatically and advances the cursor. The script walks up from the session cwd to find a tracked vault, and stays completely silent outside a vault or when nothing is unread.
Kimi Code (~/.kimi-code/config.toml):
[[hooks]]
event = "SessionStart"
command = "node /path/to/extras/vault-feed-hook.mjs --reader=kimi-code --format=kimi"
timeout = 10
Claude Code (hooks.SessionStart in ~/.claude/settings.json):
{ "type": "command", "command": "node /path/to/extras/vault-feed-hook.mjs --reader=claude-code --format=claude", "timeout": 10 }
--reader is the agent's stable cursor name. Use an absolute node path.
Protocol for AI agents (this README is the interface doc)
This vault is tracked by the vault-change-feed Obsidian plugin. Before editing notes, catch up on what the user changed since your last visit:
- Read
.obsidian/plugins/vault-change-feed/cursors.jsonand find your reader name (use your agent id, e.g."kimi-cli"; absent means cursor0). - Read
.obsidian/plugins/vault-change-feed/changelog.jsonl(one JSON event per line) and take events withseqgreater than your cursor.- If your cursor is
> 0and the smallestseqin the file is greater thancursor + 1, the log was rotated and you missed events — stop and do a full vault rescan instead. - If you see an event with
op: "resync", the plugin rebuilt its baseline — a full rescan is advised.
- If your cursor is
- Event shape:
{"seq", "ts", "op": "create"|"modify"|"delete"|"rename"|"resync", "path", "oldPath"?, "stat": {"added", "removed"} | null, "source"}.stat: nullmeans "changed, magnitude unknown — open the file if you care". - After processing, write the largest
seqyou saw back tocursors.jsonunder your reader name. Write atomically: writecursors.json.tmp, then rename it tocursors.json.
Inside Obsidian, other plugins/scripts can use the JS API instead of files:
const api = app.plugins.plugins['vault-change-feed'].api;
const { events, stale, latestSeq } = await api.getChanges('my-plugin');
// ...handle events...
await api.markRead('my-plugin', latestSeq);
The JS API's getChanges merges unread events per file by default (api.getChanges(name, { merge: false }) returns the raw stream; groups that can't be merged losslessly are passed through, see below). External agents reading changelog.jsonl directly see the raw event stream and may implement the same merging:
- Sort by
seqfirst (the log may be out of order), then group bypath(resyncis never merged); merged events take the group's maxseq/tsand the last event'ssource - Created and deleted within the window → group dropped; last event is
deleteand the group contains a rename →deleteon the first rename'soldPath(nooldPathfield, stat from the delete itself); last event isdelete→delete(stat from the last delete) - Deletes and renames interleaved beyond the case above → not merged, group emitted as-is (any merge would lose some path's fate)
- Deleted then re-created →
modify(statnull); starts with create →create; contains a rename →rename(keeps the first rename'soldPath); otherwise →modify - For the last three,
statis the per-line sum if all entries are non-null, elsenull; output sorted by merged seq
Commands
Copy unread changes for AI— copies a compact summary of unread changes (readermanual) to the clipboard, ready to paste into any AI chatInstall AI protocol for agents/Remove AI protocol from agent files— manage the discovery blocks inAGENTS.md/CLAUDE.md/GEMINI.md
Settings
| Setting | Default | Description |
|---|---|---|
| Tracked text extensions | md, markdown, txt, canvas, json, csv | These extensions get diff stats |
| Exclude globs | empty | Extra exclusion rules; the vault config folder is always excluded |
| Large file threshold | 1024 KB | Larger files get stat: null |
| Baseline content budget | 102400 KB (100 MB) | Text kept in memory for diffing; beyond the budget, files store hash only (stat falls back to null) |
| Retention days / max entries | 90 / 50000 | Log rotation, whichever limit hits first |
| Baseline flush interval | 300 s | Baseline persistence period |
| Auto-install AI protocol on first run | on | Install the protocol block on first enable |
| Sync AGENTS.md / CLAUDE.md / GEMINI.md | on | Per-file install targets |
| Auto-sync protocol block | on | Refresh installed blocks after plugin updates |
Privacy
Fully local: no network calls, no uploads, no data collection. Everything lives in your own vault.
Development
npm install
npm run build # typecheck + bundle main.js
npm test # vitest
Works on desktop and mobile (all file operations go through the Obsidian vault API).
License
MIT © tiyukains
For plugin developers
Search results and similarity scores are powered by semantic analysis of your plugin's README. If your plugin isn't appearing for searches you'd expect, try updating your README to clearly describe your plugin's purpose, features, and use cases.