Browse by type
Own the context. Rent the memory.
Your notes stay plain .md files you own — bring any AI model and swap it whenever you like.
A local-first markdown vault you can hand to an AI agent — and it gets better the more you use it. Browse it like a GitHub file tree, edit it like Notion, and plug it into Claude, Cursor, or any MCP host as 27 agent tools — semantic & hybrid search, RAG context, and cited answers. The vault self-improves: it learns your writing voice from your draft→final edits and self-tidies broken links, orphans, and duplicates — every change lands in a human-in-the-loop review queue where agents propose and you decide. Plain
.mdfiles, runs offline, no API key required.
If you’ve ever wanted your markdown content to be:
this project is for you.
Most note/documentation tools force a tradeoff:
This repo bridges both worlds:
etag / lastModified)CONTENT_ROOT (the "vault")[[wikilinks]], browse backlinks, and explore an
interactive knowledge graphThis isn't just a file browser — the vault is built to double as an AI agent's brain, with a human always in control. Everything below runs locally and offline by default (no API key needed), and anything an agent wants to change in your notes goes through a review queue you approve.
semantic search ranks by relevance and hybrid search fuses keyword +
semantic results. Great for "I know I wrote this somewhere…". Runs offline
on a built-in TF-IDF ranker by default; flip on real embeddings (below)
when you want stronger synonym/paraphrase matching.think). Ask a question and get a cited answer
assembled from your own notes — plus an honest list of gaps when the vault
can't fully answer, so you know what's missing.type: (person,
meeting, project, idea…) and the graph colours it by what it is, agents
learn the canonical types to author well-formed notes, and the maintenance scan
flags notes that break the vocabulary — all from plain frontmatter, no database.FSBRAIN_EMBEDDINGS=on with an API key and the same
search routes rank by dense vector similarity instead, so paraphrases and
synonyms that share no keywords still match. It's a drop-in swap: nothing
downstream (semantic/hybrid search, think, RAG context) changes, only the
scoring improves. Vectors are cached to .fsbrain/embeddings.json so a
restart re-embeds only changed notes, and the engine falls back to TF-IDF
on any provider outage — "off" is always a working search. See the
FSBRAIN_EMBEDDINGS env vars below.The throughline: agents propose, you decide. Risky or outward-facing changes are never applied automatically — they become reviewable proposals attributed to a named actor (e.g.
agent:maintenance,agent:feedback-loop), so you always see who suggested what.
The feedback loop in 30 seconds:
social/x/drafts/launch.md.social/x/old-posts/launch.md.type: feedback,
channel: x, draftPath, finalPath, and an optional reviewReason).run_feedback agent tool (or POST /api/feedback/scan). It compares
the two, distills the lesson into a channel playbook, and files it as a
proposal you approve in the Review tab.apps/web (React + Vite)
├─ File tree + editor / preview / graph / activity UI
└─ Calls the API over HTTP/JSON (live updates over SSE)
apps/api (Node HTTP server)
├─ Validates and resolves logical paths (sandboxed to CONTENT_ROOT)
├─ Markdown-focused file CRUD + optimistic concurrency
└─ Search (text/semantic/hybrid), backlinks, graph, think, audit, proposals
apps/mcp (MCP stdio server)
├─ Exposes the vault to AI agents as 27 tools
└─ Embeds the API in-process — one self-contained command for an MCP host
packages/shared
└─ Shared TypeScript contracts + pure helpers (markdown, search, graph, …)
Repository structure:
apps/
api/ # Backend HTTP server + filesystem storage (CONTENT_ROOT)
web/ # Frontend UI (React + Vite)
mcp/ # MCP stdio server — the vault as agent tools (embeds the API)
packages/
shared/ # Shared types/contracts + pure helpers
docs/
implementation.md # Source of truth for project state
CONNECT.md # Connect an MCP host (OpenClaw / Claude / Cursor)
integration-test-plan.md # Manual integration checks
AGENTS.md # Start here if you are an AI agent working in this repo
npm install
Terminal A:
npm run dev:api
Terminal B:
npm run dev:web
http://localhost:5173http://localhost:3001/healthSkip the web UI and hand the vault to an MCP-aware agent (OpenClaw, Claude Desktop, Claude Code, Cursor, …):
git clone https://github.com/andylow92/file-system-like-github.git
cd file-system-like-github
npm install
npm run build # produces apps/mcp/dist/server.js
npm run start:agent # launches the self-contained fsbrain-mcp on stdio
fsbrain-mcp embeds the storage API in-process and auto-creates the vault
at ~/.fsbrain/vault (override with CONTENT_ROOT=...). It exposes 27
vault tools (list_notes, read_note, create_note, patch_note,
semantic_search, hybrid_search, think, get_graph, propose_edit,
run_maintenance, list_skills, curate_skills, run_feedback, proposal_stats, …) and records every agent write to
<vault>/.fsbrain/audit.jsonl so you can always see what the agent did.
Copy-paste config snippets for OpenClaw / Claude Desktop / Claude Code
/ Cursor are in docs/CONNECT.md.
Heads-up if you have an older clone. The default
CONTENT_ROOTis now~/.fsbrain/vault(previously<cwd>/content). Existing./contentnotes aren't deleted, butnpm run dev:api/npm run dev:web/npm run start:agentwithoutCONTENT_ROOTset will now read the new path. SetCONTENT_ROOT=./content(inapps/api/.envor your shell) to keep the old location.
An opt-in integration that lets connected agents run structured, auditable prospect research. It is disabled by default — vaults that do no prospect research never see the tools or get asked to configure anything.
<vault>/.fsbrain/integrations.json (owner-only, never in
a note, never committed) and is never shown again once saved.npm run start:agent) and reconnect your
agent. The server decides which tools to register at startup, so the
rocketreach_* tools do not appear in an already-running session. Turning the
integration off needs no restart — calls fail closed immediately.rocketreach_get_account_status,
rocketreach_start_intake (standardized intake questions to ask before
spending credits), rocketreach_search_contacts (search-only — no paid
lookups), and rocketreach_lookup_contacts (paid enrichment, which requires
an explicit maxLookups cap and never exceeds it).prospects/… notes with full provenance (actor, timestamp,
normalized parameters, credit deltas).For apps/api:
CONTENT_ROOT~/.fsbrain/vault (auto-created on first run).CONTENT_ROOT=./content to keep an older clone's location.PORT3001).FSBRAIN_EMBEDDINGS1/true/on/yes turns it on); when off, semantic search uses the
fully-offline TF-IDF engine — no network, no key, exactly as before.EMBEDDINGS_API_KEY (falls back to
OPENROUTER_API_KEY). If the provider is unreachable or misconfigured,
retrieval automatically falls back to TF-IDF, so "off" is always a working
search — flip the flag off and restart to revert entirely.EMBEDDINGS_MODEL (default
openai/text-embedding-3-small), EMBEDDINGS_URL (any OpenAI-compatible
/v1/embeddings endpoint; default OpenRouter), EMBEDDINGS_BATCH_SIZE
(default 96).<CONTENT_ROOT>/.fsbrain/embeddings.json so a restart
re-embeds only changed notes. The file is safe to delete (it rebuilds on
demand) and is tagged with the model, so switching EMBEDDINGS_MODEL
invalidates it automatically.Example:
CONTENT_ROOT=/absolute/path/to/vault PORT=3001 npm run dev:api
# Opt into embedding-based semantic search:
FSBRAIN_EMBEDDINGS=on EMBEDDINGS_API_KEY=sk-... npm run dev:api
Files & tree
GET /healthGET /api/tree?path=...GET /api/file?path=... (or ?id=...)POST /api/file · PUT /api/file · PATCH /api/file (granular ops)POST /api/dirPATCH /api/path (move/rename) · DELETE /api/path?path=...&recursive=true|falseLinks, graph & blocks
GET /api/backlinks · GET /api/graphGET /api/block · GET /api/block-anchorsSearch & retrieval
GET /api/search · GET /api/semantic-search · GET /api/hybrid-searchGET /api/context (RAG bundle) · GET /api/think (cited answer kit)Provenance, review & maintenance
GET /api/auditGET /api/proposals · POST /api/proposals · POST /api/proposals/resolve (human-only)GET /api/proposals/stats (review-queue approval rates + threshold nudges)GET /api/maintenance · `POST /api/maintebrowse all types & interfaces →
$ claude mcp add file-system-like-github \
-- python -m otcore.mcp_server <graph>