A portable, agent-operated knowledge base: plain Markdown in a folder that a human, an LLM, and the wiki CLI all read the same way. One store holds your notes, documents, datasets, and backlog, and an agent can run the whole thing while you keep every file.
Karpathy's LLM-wiki note named the idea, a knowledge base an agent could actually live in. The idea resonated but a full version never shipped. This is the rest of the stack that makes it actionable.
- Markdown (the format). Plain files in a folder, with a light convention (a
typein frontmatter, links between entries, two reserved filenames) compatible with the Open Knowledge Format. Complete and navigable on its own. wiki(the tool). A neutral, deterministic engine that indexes the folder and answers structured queries, reports the link graph, and safely refactors. Built for agents to call, runnable by anyone.- Skills. The operating manual an agent follows to drive the tool, where workflow opinions live, and yours to customize.
The foundation is just your Markdown; agentic-wiki provides the top two layers, the tool and the skills.
Separation principle: the format stands alone, the tool is a neutral engine over it, and the skill is where opinion lives. Never let one layer do another's job.
Markdown in git is the only store that is human-readable, LLM-native, greppable, diffable, versioned, portable, self-hostable, and standard, all at once. Your IDE, Obsidian, the CLI, and any LLM open the very same files, and nothing is locked inside a proprietary app.
A plain folder, though, is dumb: grep cannot tell you what links to a page, which notes have no home, or move a file and fix the links behind it. wiki CLI adds exactly that structure, so you get the freedom of plain text and the power of a database at once.
Three orthogonal axes classify every entry. Keeping them separate is what keeps the system friction-free.
| Axis | Question it answers | Cardinality | Home |
|---|---|---|---|
| Folder path | Where does this one file live? | exactly one | directory tree |
type |
What kind of entry is it? | exactly one | frontmatter |
| Tags | What other dimensions does it touch? | many | frontmatter |
Domain rule: if you'd ever want a thing to appear in two places at once, it is a tag, not a folder.
- Prefer domain over type for folders. A trip plan (concept), its flights (dataset), and the trip itself (event) all sit together in
personal/travel/, sliced bytypeon demand, rather than scattered acrossconcepts/ events/ datasets/. This is a lean, not a law (atasks/folder that groups actions by actionability is a reasonable workflow choice). - A folder is a single, stable home. The path is the entry's identity, so moving a file rewrites the links to it. Keep the top levels stable;
wiki moverewrites links safely when you do move things. - Tags carry everything cross-cutting:
database,2026,needs-review,feature.
Most of these are judgment calls (whether something is a folder or a tag, which domain it belongs to) that no tool can make; you and the skill define them. wiki check verifies only the mechanical rules: every file carries a known type, links resolve, folders stay shallow.
A knowledge base is a bundle: a directory marked by a wiki.toml file, with the markdown content sitting directly inside it. It works like a git repo, the marker plus your files at the root, not a wiki/ subfolder wrapping the content. Root-absolute links resolve from the bundle root, so /finance/income.md is the file finance/income.md beside wiki.toml.
my-kb/ # the bundle root == the content root
├── wiki.toml # marker + config (tracked)
├── .wiki/ # query cache/index (gitignored, rebuilt on demand)
├── .gitignore
├── index.md (home page, links into each domain)
├── finance/
│ ├── index.md
│ ├── budget.md (type: note)
│ └── expenses.md (type: dataset)
├── personal/travel/
│ ├── index.md
│ ├── flights.md (type: dataset)
│ └── copenhagen-2026.md (type: event)
├── tech/infra/
│ ├── index.md
│ ├── reverse-proxy.md (type: concept)
│ └── hetzner.md (type: tool)
├── sources/
│ └── okf-spec.md (type: source)
└── tasks/
├── index.md (the board)
└── renew-passport.md (type: task)
wiki.toml(tracked) marks the bundle root and declares config. The CLI walks up from the working directory to find it, the same way git finds.git. It is deliberately tiny:The optionalspec = "0.1" types = ["note", "concept", "entity", "event", "tool", "project", "task", "dataset", "source", "draft"] skip = ["AGENTS.md", "CLAUDE.md", "WORKFLOW.md"] # optional: paths that are not content entries
skiplist names paths (relative to the bundle root) that are not content: agent-operating files likeAGENTS.md/CLAUDE.md/WORKFLOW.md, aREADME, aLICENSE. Askippath inside the bundle is left out of the index (see Content); askippath resolving outside it acknowledges an out-of-bundle reference (see Links)..wiki/is a disposable per-bundle cache, the same idea as.git: local, never committed, rebuildable.- Content is every
.mdfile under the bundle root. The scanner indexes only markdown, skips hidden directories, and skips any path listed inwiki.toml'sskip, so the cache (.wiki/), viewer artifacts (.obsidian/), and declared non-entries (AGENTS.md, aREADME) never become entries — absent fromlist/search/the graph and raising nocheckissue, though a link to a skipped file still resolves on disk and so is not broken. Hand someone the bundle and they hold a clean, self-contained knowledge base; Obsidian opens it as a vault (configure markdown links and absolute-path-in-vault).
Every markdown file is an entry with YAML frontmatter. The only required field is type, on every file.
---
type: concept
title: Docker Networking
tags: [infra, docker, networking]
timestamp: 2026-06-24
---
A two-to-three sentence summary.
## Details
…
## See also
- [Container orchestration](/tech/infra/container-orchestration.md)Standard optional fields (OKF): title, description, tags, timestamp (ISO 8601, validated when present). This profile adds resource: a pointer to the specific live thing a page represents (a server console, a dataset's table), distinct from a source's url, which is provenance.
Declared in wiki.toml and extensible: there are no reserved types. index.md/log.md are reserved filenames with no frontmatter (see Reserved files).
| type | For | Notable fields |
|---|---|---|
note |
a processed thought or observation | status |
concept |
an idea, technology, methodology | |
entity |
a person, org, product, place, named thing | |
event |
something that happened at a point in time | date |
tool |
software, hardware, utility | |
project |
something being built or planned | status, priority, effort |
task |
an action item | status, priority, effort, due |
dataset |
time-series / tabular source data | |
source |
an external reference (provenance) | url, source_type |
draft |
a transitional entry, not yet classified |
The list is a starting vocabulary, not a mandate: extend or replace it in wiki.toml. draft is the inbox-capture convention (see the skills).
Standard markdown links can be root-absolute from the bundle root, [Income](/finance/income.md) (canonical, and preferred for readability and stability), or relative, [a](./other.md) (resolved against the file's directory). No wikilinks.
If you use Obsidian, set it to write standard markdown links: Settings → Files and links → turn off Use [[Wikilinks]], set New link format to Absolute path in vault, turn on Automatically update internal links. Otherwise it emits [[wikilinks]], which neither the format nor wiki recognizes, so those links silently vanish from the graph.
wiki tidy --links rewrites relative links to root-absolute. A broken link (target not in the bundle) is not malformed, it may be not-yet-written knowledge, so consumers tolerate it; wiki check and wiki unresolved surface them as a courtesy. A link that resolves outside the bundle (climbing above the root, e.g. ../PRD.md from a nested bundle) is neither an edge nor broken; wiki check notes it as an out-of-bundle advisory, and listing that path in wiki.toml's skip silences it. Such a path is never resolved on disk (the containment guard that also blocks path traversal).
resource: is a frontmatter pointer, never a body link, and is not treated as an internal link. Entry filenames are slugs (lowercase, hyphenated, no spaces) so links stay simple bare paths; wiki check warns on a space in a path. (wiki does resolve <…>-wrapped links, markdown's way to allow a space, but wiki check still flags the spaced filename; wiki tidy --slug renames it to a slug, and naming files as slugs from the start avoids it.)
They are not concept documents: no frontmatter, exempt from the type requirement, and skipped by wiki orphans (they are entry points or side-narratives).
index.md: progressive-disclosure navigation for its folder (a list of links). Agents and humans read indexes top-down and follow links rather than grepping blindly.log.md(optional): an append-only chronological changelog or narrative (what happened in this area over time), with ISOYYYY-MM-DDdate headings. This is not data. Create it only when a domain benefits from an audit trail; it is not mandatory.wiki checkandwiki orphansskip it.
Three different things, often confused:
-
Dataset (
type: dataset): tabular or time-series source data (flights, expenses, activity). One dataset per file equals one table (the header row names the fields); keep small data inline as a markdown table (wiki tableextracts it as csv/json), and pointresource:at an external CSV/sqlite for large data. Multiple datasets are multiple files, so extraction is never ambiguous. -
log.md: a changelog or narrative, not data (see Reserved files). -
Tasks: an inline
- [ ]checkbox is the default, and most tasks stay inline. Promote one to an entry withtype: taskwhen it carries context an inline line cannot capture (a multi-step plan, a blocked orduestatus, links to other entries); not every entry needs subtasks. The work-kind (feature, bug, debt, chore) is a tag. The active list is a board: anindex.md(or atodo.md) of checkboxes that link to the detailed entries.Sync invariant: when a task entry carries a
statusfrontmatter field, the board's checkbox for that entry is checked exactly whenstatusisdone. Update both in the same change so the board never goes stale.wiki taskssurfaces open checkboxes;wiki list --type tasklists all task entries regardless of checkbox state.Pruning: the board is a short-lived working surface; task entries are working records. When work ships, its lasting value is captured in
log.md(a dated summary) or promoted to a permanent KB entry. The task file itself can then be deleted: it served its purpose. Your workflow may decide to use only checkboxes, only entries, or both; the tool is not prescriptive.
Prefer a shallow domain → subdomain shape: 2-3 levels recommended, no hard cap (wiki check warns past three). The exact buckets are yours to choose. Coarse scoping is the folder's job; cross-cutting retrieval is the job of tags and links.
Folder names and entry filenames use slugs: lowercase, hyphenated words, no spaces, no underscores. wiki check warns on non-slug names; wiki tidy --slug rewrites them.
Why a tool at all: grep cannot compute a link graph (orphans, backlinks, unresolved) or answer structured frontmatter queries (by type/tag/field), and an agent would burn a lot of tokens reading content every time, of which only a fraction might be relevant. wiki is the deterministic engine in between: every query, move, and check is exact and repeatable, so an agent runs your base with the reliability of a database, not the guesswork of a prompt.
A single static Go binary, zero runtime dependencies, native on macOS, Linux, and Windows. It indexes a bundle and answers structured queries (list/search with --type/--tag/--prefix), introspects the vocabulary (tags, properties, property), reports the link graph (links, backlinks, unresolved, orphans), lists tasks, canonicalizes links and filenames (tidy), and safely refactors (move rewrites every link to a relocated entry). Output as text, JSON, CSV, or TSV.
Exit codes: 0 on success: enumeration and diagnostic commands (list, tasks, orphans, unresolved, …). 1: search with no match (like grep), table with no such table, and check with conformance errors (like a linter). 2 on actual errors (missing bundle, bad arguments, unreadable file).
wiki check is an opt-in lint (broken links, missing or unknown types, depth, slug, okf_version drift, timestamp format), not an OKF gate: a bundle that does not pass check can still be valid OKF. wiki init scaffolds a conformant bundle from an embedded starter, so the tool is its own template generator and there is no template-versus-spec drift.
The operating manuals an agent follows to drive wiki, defined once and surfaced per runtime, living in the skills repo. They encode the mental framework (the judgment the tool cannot make) and the data lifecycle. The format stays neutral; all workflow opinion lives here. Two ship today:
- agentic-wiki: operating a knowledge base. A set of independent operations the agent can enter at any point — capture a rough thought as a
type: draftininbox/; refine it by reading it back and sharpening; promote it (realtype,wiki moveinto its domain, link it in); retrieve (search, list, follow links); groom (check,tidy, cross-link, groom orphans). A session might do just one of these, or chain several. Already fully formed? Skip the inbox and create directly. - agentic-backlog: operating a task backlog that is itself a wiki bundle, with the entry
statusand its board checkbox kept in sync. Bring your own UI or agent on top.
The inbox can be a live queue of type: draft entries, so wiki list --type draft is the to-refine list; an item leaves the queue the moment it is promoted. A binary in flight (a PDF) can sit in a gitignored inbox/resources/, pointed at by the draft's resource:.
The conventions are yours. Beyond "every file has a type," the format fixes almost nothing; how you work the bundle is a skill-layer choice you customize. The inbox above is one example: have one or don't. Same for boards, naming, and ingestion flow.
In scope: the markdown format, the wiki CLI, the agent skills, a minimal embedded scaffold.
Out of scope:
- Binary document store. The framework's job ends when knowledge is extracted into the wiki; where an original binary lives afterward (an external store, or deleted) is not its concern. The optional
resource:field only points at it. - Sync. Git handles it.
- Encryption. Staying git-based means git-crypt can drop in per folder. Defined in your agent skills.
- History search. Git is the time machine (
git log -S,git log --follow,git blame); the tool indexes current state only.
Three single-purpose repos, each with its own release cadence:
agentic-wiki/wiki: the CLI tool; embeds the starter sowiki initneeds no network.agentic-wiki/template: the reference bundlewiki initemits.agentic-wiki/skills: the operating manuals, installed into an agent's skill directory, evolving independently of the tool.
The CLI is built, tested, and dogfooded. This repo dogfoods the format itself: its roadmap lives under tasks/, an agentic-wiki bundle operated by wiki (wiki tasks, wiki list --type task, wiki check).
Builds on Andrej Karpathy's LLM-wiki idea and Google's Open Knowledge Format.