- TypeScript 91.5%
- JavaScript 8.5%
| Filename | Latest commit message | Latest commit date |
|---|---|---|
| bin | ||
| src | ||
| .gitignore | ||
| .npmrc | ||
| .prettierignore | ||
| .prettierrc | ||
| CLAUDE.md | ||
| eslint.config.mjs | ||
| mise.toml | ||
| package.json | ||
| pnpm-lock.yaml | ||
| README.md | ||
| Taskfile.yml | ||
| tsconfig.json | ||
librarian
A personal markdown wiki, maintained for you by a coding agent.
You drop in sources, ask questions, and the wiki grows itself — pages get written, indexes get updated, contradictions get flagged. The notes live on your disk as plain markdown; you can read them in any editor, version them in git, or grep through them. There's nothing to learn beyond "here are three operations."
~/.local/share/librarian/wiki/
├── AGENTS.md ← your customization (topics, voice, conventions)
├── index.md ← root catalog — kept up to date by the librarian
├── log.md ← append-only history of every operation
├── raw/ ← immutable source material (your originals)
└── <topic>/ ← topic directories the librarian organises into
├── index.md ← per-topic catalog
└── *.md ← concept pages, with YAML frontmatter
The problem
Personal notes apps die because maintenance scales worse than value. The longer you use them, the more tangled they get; eventually you stop trusting them, and they become a graveyard.
LLMs don't get bored. They will read every page, follow every link, update every catalog, and write a log entry — every time you add something. The maintenance cost goes to zero.
librarian is the smallest thing that makes that workflow real, on disk, in plain markdown you own.
It's heavily inspired by Andrej Karpathy's "LLM Wiki" gist (April 2026) and shape-compatible with Google's Open Knowledge Format.
How it works
Three operations. That's the whole conceptual interface.
| Operation | What it does |
|---|---|
| ingest | Hand it one or more sources — local files, URLs, or both, with optional context. It reads, summarizes into topic pages, revises affected concept pages, updates catalogs, appends one log entry per batch. |
| ask | Hand it a question. It reads the wiki, answers with citations, files the answer back as a new page if it's worth keeping (so the next ask on the same topic hits an existing page instead of re-discovering). |
| lint | Hand it nothing — or a focus area. It scans for contradictions, stale claims, orphan pages, missing cross-references; reports findings; auto-fixes the trivial ones; drops into an interactive Q&A so you can adjudicate the rest. |
Under the hood: the CLI you install (librarian) doesn't do the wiki work itself. It delegates each operation to a real coding agent — by default Claude Code via @agentclientprotocol/claude-agent-acp — bound to your wiki directory. The agent reads and writes the markdown files on your behalf. You can swap in any ACP-speaking agent (Codex, Gemini CLI, etc.) via the config.
Quick start
Requires Node 22+ and a way to authenticate with Claude (either an ANTHROPIC_API_KEY env var or an active Claude Code login).
# clone, install, build
git clone https://example.com/librarian.git
cd librarian
pnpm install
pnpm build # → dist/
# run via pnpm dev (tsx, no rebuild needed) or directly from dist/
pnpm dev ingest ~/Downloads/some-article.md
pnpm dev ask "what was the main point of that article?"
pnpm dev lint
The wiki is auto-created on first use at $XDG_DATA_HOME/librarian/wiki/ (i.e. ~/.local/share/librarian/wiki/ on most systems). No init step, no boilerplate.
To put librarian on your $PATH for daily use, either install globally (npm i -g . from the cloned repo) or symlink ./dist/bin/librarian.js into a directory already on your path. The rest of this README uses bare librarian; substitute pnpm dev if you prefer running from source.
Daily use
# Drop one or more sources — anything readable (markdown, text, PDFs the librarian can read)
librarian ingest ./paper.md
librarian ingest ./paper.md ./appendix.md ./author-bio.md # batch related files together
# Ingest a URL — fetched, reader-mode-extracted, saved as markdown into raw/
librarian ingest https://blog.example.com/some-article
# Pass context alongside any ingest — helpful when the file alone is opaque (CSVs, data dumps, etc.)
librarian ingest ./q3-numbers.csv --context "Q3 2026 EU sales — each row is one region"
# Ask a question — the librarian uses the wiki and may file the answer
librarian ask "what does Karpathy mean by 'LLMs don't get bored'?"
# Periodic health-check — interactive: type follow-ups to act on findings
librarian lint
> use source-b for the ACP origin claim
lint drops into an interactive prompt after streaming its findings so you can adjudicate "needs-your-call" items directly. Exit with \q, quit, or Ctrl-D.
Git, by default
Your wiki is automatically a git repository. The first time librarian touches the directory, it runs git init, drops a tiny .gitignore, and makes an initial commit. After every ask, ingest, or lint, if anything changed, it commits with a mechanical message:
ingest: paper.md
ingest: 3 sources (acp.md, mcp.md, comparison.md)
ask: what was the main point of that article?
lint
This means full undo, full blame, and the option to share or back up the wiki anywhere git goes — without you ever running a git command.
Syncing with a remote
If you want the wiki on multiple machines (laptop + desktop), point it at a git remote:
cd ~/.local/share/librarian/wiki
git remote add origin git@github.com:you/wiki.git
git push -u origin main
Then, instead of running git by hand:
librarian sync
sync pulls from the remote, asks the librarian to resolve any merge conflicts (it reads the conflicted files, picks the right content, drops the markers — using your AGENTS.md conventions for voice and structure), then pushes. If the librarian can't fully resolve (residual <<<<<<< markers), sync aborts before push and tells you which files still need a manual look.
The wiki on disk
It's just markdown. You can edit any file by hand and the librarian will pick up your changes on the next operation. The only conventions:
- Plain markdown + YAML frontmatter — every concept page has at least
type:(e.g.concept,entity,source,note). raw/is immutable — your originals live there, untouched. The librarian only writes elsewhere.- Collision handling: if you ingest two files with the same name,
librariancompares the bytes. If they're identical, the second ingest is a no-op (idempotent). If they differ, the second is saved as<name>-1.md(then-2.md, etc.), so nothing ever gets overwritten and both sources stay queryable.
- Collision handling: if you ingest two files with the same name,
index.mdper directory — one-line summaries of the entries beneath it, kept current.log.mdat the root — append-only, format## [YYYY-MM-DD] verb | summary.- Plain markdown links for cross-references, not wikilinks — works in any viewer.
If you outgrow librarian or just want to read your notes in something else, point Obsidian (or VS Code, or anything) at the directory. Nothing is locked in.
Customizing it
AGENTS.md at the root of your wiki is your customization layer. The librarian has its own built-in instructions (the three operations, the on-disk shape, a default voice); AGENTS.md is where you tell it about your preferences:
## Topics
- `ml/` — papers, models, training notes
- `infra/` — system design, ops, incidents
- `people/` — short profiles worth remembering
## Voice and style
- British English
- Never use emojis
## Frontmatter and tags
- All pages must have a `tags:` field
- Tag from: `#ml`, `#infra`, `#notes`, `#people`
The starter file scaffolded on first run has section headers ready for you to fill in. If you leave it as-is or delete it entirely, the librarian just uses its defaults.
Using it from your coding agent (MCP)
librarian mcp exposes two tools (ask and ingest) over Model Context Protocol, so Claude Code, Cursor, etc. can call into your wiki mid-task.
Once installed globally (npm i -g librarian), add to your MCP config:
{
"mcpServers": {
"librarian": {
"command": "librarian",
"args": ["mcp"]
}
}
}
Or, running from source (after pnpm build):
{
"mcpServers": {
"librarian": {
"command": "node",
"args": ["/absolute/path/to/librarian/dist/bin/librarian.js", "mcp"]
}
}
}
After restarting your editor, your agent gets:
ask({question})— search your notes for relevant contextingest({paths, context?})— fold one or more new sources into your wiki; batch related files together; passcontextwhen the file alone is opaque (CSVs, data dumps, anything that needs framing)
URLs are deliberately rejected by the MCP ingest tool — your coding agent has its own WebFetch and can decide what's worth handing over as a file. URL ingestion is supported on the CLI, where you've made the decision yourself.
lint is also deliberately not exposed over MCP — it's a maintenance operation the user invokes explicitly, not something an agent should trigger speculatively. (You can still ask your agent to run librarian lint as a shell command.)
Configuration
Everything works out of the box. If you want to override defaults, drop a TOML file at $XDG_CONFIG_HOME/librarian/config.toml (usually ~/.config/librarian/config.toml):
# Where the wiki lives
wikiPath = "/Users/alice/Documents/wiki"
# Which agent runs as the librarian
[librarian]
command = ["npx", "-y", "@agentclientprotocol/claude-agent-acp"]
librarian config prints the resolved config (defaults merged with your overrides) — useful for debugging.
Any partially-set fields fall back to defaults — if you only set wikiPath, the librarian command stays default.
Command reference
| Command | Purpose |
|---|---|
librarian ingest <path-or-url>... [-c "..."] |
Fold sources into the wiki. Mix files and URLs. -c / --context adds caller-supplied framing. |
librarian ask "<question>" |
Answer a question using the wiki, citing sources; file as a new page if worth keeping. |
librarian lint [focus] |
Scan for inconsistencies; auto-fix trivial; interactive Q&A on a TTY. focus narrows the scan. |
librarian sync |
git pull → librarian resolves any conflicts using your AGENTS.md conventions → git push. |
librarian mcp |
Start the MCP server on stdio. Exposes ask and ingest. Run from your editor's MCP config. |
librarian config |
Print the resolved configuration (defaults merged with ~/.config/librarian/config.toml). |
Every operation that touches files is automatically committed to git with a mechanical message — you never run git add or git commit manually unless you want to.
Architecture, for the curious
your coding agent (Claude Code, Cursor, ...)
↓ MCP over stdio
librarian mcp ← what we ship
↓ runLibrarian
composeSessionPrompt
+ ACP client (spawns the librarian agent)
↓ ACP over stdio
librarian agent (Claude Code / Codex / Gemini)
↓ fs/read_text_file, fs/write_text_file
your wiki directory
Two protocols, both stdio-based, both JSON-RPC:
- MCP is how your coding agent talks to us.
- ACP is how we talk to the librarian agent. Agent-agnostic: swap which agent acts as librarian via config.
The librarian's instructions are split into two layers:
- Default contract — role, three operations, on-disk shape, default voice. Lives in the binary (
src/librarian/librarian.prompt.ts), versioned with releases. - User customization — your
AGENTS.mdat the wiki root. The librarian reads it on every session as overrides.
Each operation (CLI or MCP) spawns a fresh ACP session bound to your wiki directory. The agent's FS access is client-mediated and sandboxed to that directory — the librarian can't reach outside. lint is the one exception: when stdin is a TTY, it keeps the session alive across multiple turns so you can adjudicate findings conversationally.
What's intentionally not here
- No embeddings, no vector DB, no RAG. Karpathy's claim is that index-file navigation works up to "hundreds of pages" — match. When/if you outgrow it, that's the time to layer something on top, not now.
- No wikilinks. Plain markdown links, so any viewer (GitHub, Obsidian, plain
cat) renders them. - No web fetching in MCP. Coding agents have their own WebFetch and can judge whether content is worth ingesting before handing it to us. (CLI
librarian ingest <url>is supported.) - No proprietary format. If you don't like
librarian, your notes are markdown files you already own.
Credits
- Andrej Karpathy's "LLM Wiki" gist — the design we're implementing.
- Google Open Knowledge Format — shape-compatible.
- Agent Client Protocol (Zed Industries + JetBrains) — how we talk to any code agent.
- Model Context Protocol (Anthropic) — how coding agents talk to us.