How I Run One Vault Across Neovim, Obsidian, and Claude Code

My note system is a folder of markdown files in a git repo. That is the whole thing. Four tools take turns working on that folder, and none of them owns it.

Neovim is my primary workspace on the laptop. I use the vault there to track current work, take notes, and keep context for projects I have worked on and patterns I have established. It is a context machine: when I need to recover a decision or understand an old project, the notes are in a searchable folder I control.

Obsidian is another client for the same files. I use it occasionally to read a note, check a link, or make a quick capture, including from my phone, but it is not the center of the workflow. Claude Code is the maintainer: it fills in my daily note, answers questions against the vault, and suggests tags and links I would otherwise postpone. Git keeps the laptop and optional mobile copy in sync.

The reason this holds together is the constraint, not the tools. Everything is plain markdown in one repository. No proprietary sync, no database, no tool-specific file format. If every app in this post disappeared tomorrow, I would still have a directory of readable text files with full history.

The relevant setup is below, including the config that matters.

The four jobs

Tool Job
Obsidian Browse and occasionally capture notes
Neovim Write, track current work, and run the vault as a project
Claude Code Maintain, retrieve, suggest structure
Git Move bytes between devices, keep history

Worth being clear about the split: Neovim is the primary desktop workspace. Obsidian is useful when I want a visual file tree, backlinks, or a quick check-in, but the vault does not depend on the Obsidian desktop app. Because it is plain markdown in a folder, you can use whichever client fits the device.

That is my preference, not a requirement of the setup. Run the Obsidian desktop client if you want the graph view and backlinks pane. Open the folder in VS Code, Zed, or whatever IDE you already use. Nothing else in this post changes, which is the point of keeping the storage format boring.

One structural detail worth getting right before anything else: my git repo wraps the vault, it does not equal the vault. The repo root holds tooling config and scratch files. The Obsidian vault is a folder inside it:

~/vaults/            <- git repo root
└── work/            <- Obsidian vault root (this is what you open in Obsidian)
    ├── .obsidian/
    ├── 00-Inbox/
    └── ...

That one level of nesting means I can keep repo-level junk out of the vault's file tree and out of Obsidian's search index.

The folder tree: PARA with two extra buckets

PARA gives you four buckets: Projects, Areas, Resources, Archive. I use those four plus numeric prefixes, plus two categories PARA has no name for.

work/
├── 00-Inbox/        capture zone, gets processed
├── 10-Projects/     active work with an end state
├── 20-Areas/        ongoing responsibilities, no end state
├── 30-Resources/    reference library
├── 40-Archive/      cold storage
├── 50-Training/     deliberate practice: rubric, curriculum, progress log
├── 99-Knowledge/    subjects where I am the SME
├── daily/           chronological journal
└── templates/       note scaffolds

Four decisions in that tree do real work.

The prefixes count by ten. 00, 10, 20, 30, 40 sort correctly in every file browser, in ls, and in a fuzzy finder. The gaps are insert space. When I needed a training bucket, it became 50-Training without renumbering anything. 99 is the reserved last slot, so 99-Knowledge always sorts to the bottom no matter what I add later.

Two buckets are not PARA categories. 99-Knowledge holds subjects where I am the expert, which is different from Resources. Resources is material I collected. Knowledge is material I wrote because I understand the thing. 50-Training is not a note category at all, it is a small application: a rubric, a curriculum, a progress log, and graded assessments. Both of these would have been forced awkwardly into Resources under strict PARA, and both are clearer as their own top-level bucket.

daily/ and templates/ have no prefix. That is deliberate. The unnumbered folders are infrastructure, not content. When I see a bare folder name in that tree I know it is plumbing.

Every bucket has a README.md that states its charter. This convention keeps the folder understandable over time. 00-Inbox/README.md opens with "This is the capture zone for quick notes, ideas, tasks, and information that needs to be processed later." 99-Knowledge/README.md scopes itself to "subjects and topics where I have developed expertise or serve as a Subject Matter Expert." Six months later, when I cannot remember whether something belongs in Resources or Knowledge, the answer is written down. It also means an AI agent reading the folder gets the rules for free, without me maintaining a separate instruction file.

When one domain outgrows a single folder, I nest a second PARA inside it rather than polluting the top level:

work/vendor-kb/
├── 00-Index/        map of content, the entry point
├── 01-Projects/
├── 02-Areas/
├── 03-Resources/
└── 99-Meta/         how this knowledge base is built and refreshed

Note the different numbering, single digits instead of tens, which signals at a glance that you are one level down. The 00-Index map of content is the piece canonical PARA lacks entirely: a hand-maintained entry note that links out to everything else in the sub-vault. For a domain with fifty notes, that index is what makes the folder usable instead of a pile.

Obsidian configuration

The setting that matters most for a multi-device setup is committing .obsidian/ to git. That directory holds your plugin list, plugin settings, hotkeys, and appearance. Commit it and your setup travels with the notes, so a fresh machine or a phone gets the same vault behavior after one clone. The exceptions go in .gitignore:

# Obsidian per-machine UI state: pane layout, not content
**/.obsidian/workspace.json

# Plugin caches that regenerate locally and churn hundreds of files
.smart-env/

workspace.json records which panes you had open, which differs per device and conflicts constantly. Exclude it and keep everything else.

One setting worth turning on regardless of which client you use: alwaysUpdateLinks. Moving a note between folders is the core maintenance action in a PARA vault, and it breaks every wikilink pointing at that note unless something rewrites them. Obsidian does this when you move a file inside the app. Since I move files from Neovim instead, that job falls to my own plugin's refactor commands, covered below. Either way, do not reorganize a vault with plain mv and hope.

If you do run a client, the two plugins I would install first on any vault are Templater for note scaffolds and Tag Wrangler for renaming a tag everywhere at once.

The Neovim layer

Writing happens here. Two plugins, one of which I wrote.

Install obsidian.nvim, the actively maintained community fork. The original epwalsh/obsidian.nvim is the one most tutorials point at and it has not seen meaningful maintenance in a while.

return {
  "obsidian-nvim/obsidian.nvim",
  version = "*",
  lazy = true,
  ft = "markdown",
  dependencies = {
    "nvim-lua/plenary.nvim",
    "nvim-telescope/telescope.nvim",
    "nvim-treesitter/nvim-treesitter",
  },
  opts = {
    workspaces = {
      { name = "work", path = "~/vaults/work" },
    },
    ui = {
      enable = false,
    },
    daily_notes = {
      folder = "daily",
      template = "~/vaults/work/templates/daily.md",
    },
    templates = {
      folder = "templates",
      date_format = "%Y-%m-%d",
    },
    open_app_foreground = false,
    follow_url_func = function(url)
      vim.fn.jobstart({ "open", url })
    end,
  },
}

Three settings matter here.

workspaces points at the vault root, the inner folder, not the repo root. Get this wrong and daily notes land in the wrong place.

daily_notes.folder plus template creates the daily note from a template. :Obsidian today creates daily/2026-08-31.md with my template already in it.

ui.enable = false is not me turning off features. It hands in-buffer rendering to render-markdown.nvim, which does concealment, headings, and code blocks better. Running both fights over the same buffer decorations, so pick one.

The commands live under a <leader>o prefix. A trimmed version of what I map:

local map = function(lhs, cmd) vim.keymap.set("n", lhs, cmd, { silent = true }) end

map("<leader>otd", ":Obsidian today<CR>")
map("<leader>oy",  ":Obsidian yesterday<CR>")
map("<leader>on",  ":Obsidian new<CR>")
map("<leader>oq",  ":Obsidian quick_switch<CR>")
map("<leader>os",  ":Obsidian search<CR>")
map("<leader>ob",  ":Obsidian backlinks<CR>")
map("<leader>otc", ":Obsidian toggle_checkbox<CR>")

-- link works on a visual selection, so it gets a visual-mode map
vim.keymap.set("v", "<leader>ol", ":Obsidian link<CR>", { silent = true })

If you are coming from the original plugin, the fork moved from :ObsidianToday to :Obsidian today subcommands. Set legacy_commands = false in your opts to turn the old names off once you have migrated.

The last piece is that the vault is a git repo, so <leader>gg opens lazygit rooted at the vault while I am editing a note. Staging a note and writing a commit message never means leaving the editor.

The Claude Code layer

This is the maintenance work Claude Code handles for me. It can inspect the vault, fill daily notes within a narrow boundary, and suggest structure without taking over the whole folder.

Claude Code does three distinct jobs here, and the distinction matters because they need different permissions.

It writes into my daily note, and only my daily note. I have a skill that gathers context from git, GitHub, and my ticket tracker, then populates the note's goals and standup sections. The important part of that skill is not the gathering, it is the boundary:

**Write:**
- Only to today's daily note: `~/vaults/work/daily/YYYY-MM-DD.md`
- No writes outside daily notes directory

An agent with write access to a whole vault is a bad idea. An agent with write access to exactly one file per day is a useful teammate. Scoping the write is what makes me comfortable running it unattended every morning. If you want the longer argument for writing this kind of thing down, I covered it in AGENTS.md best practices.

It answers questions against the vault. A retrieval skill searches notes, summarizes what it found, and returns the source note paths so I can verify. Search alone gives me a list of files; this gives me an answer with the relevant notes attached. When a teammate asks "what did we decide about X," I can return the answer with the source notes attached.

It acts as the inference backend for a Neovim plugin. I wrote a companion plugin, obsidian-tools.nvim, that handles vault-wide analysis: orphan notes, broken links, tag suggestions, promoting a note out of the inbox into the right PARA bucket. Rather than wiring up an API client, it shells out to Claude Code:

require("obsidian-tools").setup({
  vaults = { work = vim.fn.expand("~/vaults/work") },
  inbox_dir = "00-Inbox",

  para = {
    buckets = {
      project  = "10-Projects",
      area     = "20-Areas",
      resource = "30-Resources",
      archive  = "40-Archive",
    },
  },

  ai = {
    enabled = true,
    provider = "claude-code",
    model = "sonnet",
    max_context_notes = 30,
    timeout_ms = 60000,
  },
})

Two details make this work. The para.buckets table means the plugin knows my folder scheme as configuration, so :VaultPromote on an inbox note can offer the right four destinations instead of a generic file picker. And max_context_notes = 30 is a real ceiling, not decoration. Vault-wide AI operations will happily read four hundred notes and produce a worse answer than thirty relevant ones.

The commands I use most from it: :VaultSuggestTags and :VaultSuggestLinks on a note I just wrote, and :VaultOrphanRescue when the orphan count creeps up. Tagging and linking are easy to postpone, so these commands keep them from accumulating.

The other half of the plugin is the refactor side, which is what replaces the Obsidian client's link handling for me. :VaultMove and :VaultRename move or rename a note and rewrite every wikilink pointing at it. :VaultRetag does the same for a tag across the vault. All three go through an undo log:

refactor = {
  undo_history = 20,
},

:VaultUndo reverses the last refactor. A bulk rename that touches forty notes is the kind of operation you want to be able to take back in one keystroke, and git alone is a clumsy undo when the change is not committed yet.

Optional mobile access with GitSync

I use my phone for occasional quick checks and captures, so I keep an optional mobile copy of the vault. It is not part of my main workflow. This setup uses GitSync rather than Obsidian Sync or Dropbox.

GitSync is an open source mobile Git client for Android and iOS. It syncs a local directory with a remote repository.

Setup, once:

  1. In GitSync, authenticate to GitHub. Either an SSH key generated in the app or a personal access token.
  2. Clone your vault repo to a directory on the device. I use Documents/vaults.
  3. Open Obsidian, choose "Open folder as vault," and pick the vault folder inside that clone. For my tree that is Documents/vaults/work, not the repo root.
  4. Back in GitSync, point the sync directory at the clone and set an interval. Mine runs on a schedule plus on app open and app close.

The important distinction is the folder boundary. GitSync watches the repo, while Obsidian opens the vault folder inside it. Point Obsidian at the repo root and its wrapper files will appear in the vault and its search index.

If you use the mobile copy, sync before switching devices. GitSync's sync-on-close covers the phone side. On the laptop, I commit when I stop working, and that is one of the check-off items in my daily template. Otherwise, it is easy to edit the same note on both devices and resolve a conflict by hand. Markdown makes that conflict readable, but avoiding it is easier than fixing it.

The loop

Put together, a day looks like this.

Morning, <leader>otd in Neovim creates today's note from the template, or I ask Claude Code to prep it with goals, carried-forward tasks, and a standup draft. Anything that arrives during the day gets captured to 00-Inbox/, no thinking about where it belongs. Real writing happens in whichever bucket owns it: a project note in 10-Projects/, a technical explainer in 99-Knowledge/.

Once a note in the inbox is clearly about something, :VaultPromote moves it into a PARA bucket and rewrites the links that pointed at its old path. End of day, Claude Code fills in the EOD sections of the daily note from what happened in git and my tickets, so I do not have to fill them in manually.

Monthly, current daily notes get swept into daily/2026/2026-08/ so the folder stays browsable. Yearly, finished projects move to 40-Archive/.

Start smaller than this

This tree took a couple of years to settle. Do not build it in an afternoon.

The smallest version that gets the core workflow: one git repo with a vault folder inside it, five folders (00-Inbox, 10-Projects, 20-Areas, 30-Resources, 40-Archive), a README.md in each stating what belongs there, and one daily template. Add mobile sync later if you need it. The other pieces in this post can attach without a migration.

Add the AI layer last, and scope it narrowly when you do. The mistake is pointing an agent at a whole vault and asking it to organize your thinking. What works is one file it may write, one question it answers well, and a folder scheme it knows about as configuration.

about raymond ware

About Me

Hi, I am Full Stack Developer. I am passionate about JavaScript, and find myself working on a lot of React based projects.