About oh-my-wiki
oh-my-wiki (OMW) is a storage-agnostic wiki skill for LLM workflows, intended for organizing source material into a searchable wiki. It uses the omw CLI to manage multiple vault modes and combines source provenance, link graphs, and local retrieval features.
Key features
- 01Organize content in Raw, Wiki, and Schema layers
- 02Manage vaults and content operations through the omw CLI
- 03Fetch, ingest, and query source material
- 04Maintain page link graphs and run quality checks
- 05Support local embeddings and hybrid retrieval
Use cases
- 01Distill web or file sources into a wiki
- 02Search and query existing wiki pages
- 03Check page structure, links, and due reviews
- 04View a vault in Obsidian or Logseq
Install
Copy the command and run it in a terminal.
npx skills add https:/ / github.com/ dandacompany/ oh-my-wiki --skill oh-my-wikiAfter installing, type/oh-my-wikiin Claude Code to confirm it loaded.
Bundled files
References and scripts installed along with SKILL.md.
TUTORIAL_DESIGN_SYSTEM.md.dockerignoreLICENSEMANIFEST.inREADME.mdTUTORIAL.ko.md.gitignoreTUTORIAL.md.github/workflows/ci.ymlbackends/claude.jsoncodex.jsongemini.jsonopencode.jsonbin/dev_deploy_skill.pyinstall.shensure-cli.shuninstall.shomw-install.shcommands/connections.mdautoresearch.mddelete.mddistill.mdfetch.mdgate.mdedit.mdimport.mdinbox.mdhistory.mdlint.mdmove.mdDetails
From the project READMEoh-my-wiki (OMW)
A storage-agnostic LLM Wiki skill. Implements Andrej Karpathy's three-layer pattern (Raw / Wiki / Schema) with hybrid memo-only and wiki-mode per vault. Operations live in commands/*.md. Deterministic I/O lives in scripts/*.py. State lives in the global registry ~/.omw/registry.db (override the root with OMW_HOME). Vault content lives at each vault's registered path.
Short alias: OMW (lowercase omw). Both oh-my-wiki and omw resolve to this skill.
⛔ HARD RULES — read before doing ANYTHING (non-negotiable)
- The
omwCLI ONLY. NEVER call internals. Run every operation through theomwcommand (omw status,omw vault create <name> --mode wiki --type <markdown|obsidian>,omw vault use,omw search,omw fetch,omw ingest,omw query,omw connections, …). It is FORBIDDEN to runpython3 -m scripts.<anything>,python3 -c "from scripts import …", or to import / executescripts/*.pymodules (registry, adapters, reindex, wizard, …) directly. Thescripts/package is an internal implementation detail, NOT a user interface.- CLI preflight (first use): if the
omwcommand is not callable, runbash "<this skill dir>/bin/ensure-cli.sh"once. It asks the user before installing the CLI (pipx/pip) and printsOMW_BIN=<path>. Use that absolute path foromw …calls in this session (PATH refreshes next session). Never fall back topython3 -m scripts.*.
- CLI preflight (first use): if the
- Web search MUST go through
omw search "<query>". It routes to the user's configured provider and keeps the wiki's provenance intact. Do NOT use your own / native web-search tool for OMW work. - Create vaults ONLY with
omw vault create <name> --mode <mode> --type <markdown|obsidian>— never construct a vault by calling registry/adapters yourself. - Ingest sources with
omw fetch <URL>oromw ingest— never hand-write files intoraw/.omw searchauto-falls back across keyed providers on 429/empty; for fetching, try your native fetch first thenomw fetch(it cascades urllib→chromium→cloud).
If you catch yourself about to write
python3 -c,python3 -m scripts, or use a native web search, STOP and use the matchingomwcommand above instead.
Current status
The current release supports seven vault modes, graph-backed lint, autoresearch,
six wiki-maintenance personas, Obsidian/Logseq viewers, URL fetch + inbox,
high-precision per-prompt recall, same-project staged session continuity for
Claude Code and Codex, local embeddings, and deterministic lifecycle guidance.
See README.md, TUTORIAL.md, and TUTORIAL.ko.md for usage.
Step 1 — Read registry state
Always invoke this before doing anything else:
Command interface — read this first. omw has exactly two ways to run things, and NO standalone script CLIs. Do not invent filenames like
omw_db.py,vault.py,cli.py, orbootstrap.py— they do not exist.
- Deterministic ops (status, vault management, lint, search, serve, schema, supersede, review, links, fields, view, visibility, inbox, fetch, reindex, connections): run the
omwCLI —omw status,omw vault list,omw vault create <name> --mode wiki,omw vault use <name>,omw vault info <name>(single-vault JSON card: path/type/mode/active/archived + note counts),omw vault current [--json](print the active vault's name),omw vault rename <old> <new>(rename the registry label; index preserved),omw vault move <name> <new-path>(relocate the vault folder on disk + update its path),omw vault set <name> [--mode M] [--config k=v](edit mode/config only; type/path/name are owned by move/rename),omw vault archive <name>/omw vault unarchive <name>(hide/restore a vault;omw vault list --allshows archived),omw vault delete <name>(soft-delete to trash by default;--hard --yesto purge irreversibly),omw lint,omw schema list,omw supersede <relpath> --by <slug>,omw review due,omw serve(the retrieve-only messenger query API — seereferences/messenger-api.md),omw view [page] [--search Q] [--viewer obsidian|logseq] [--vault <name>] [--print](open vault/page/search in Obsidian or Logseq via URI scheme; companion:omw setup viewer),omw visibility get <relpath>/omw visibility set <relpath...> public|private(per-page visibility management),omw inbox add <url>/omw inbox list/omw inbox remove <url>/omw inbox run(queue URLs then batch-fetch intoraw/),omw embed status|list|use <model>|add <model>|install|reindex(local embedding model: pick/manage FastEmbed + sqlite-vec; rebuilds happen in a disposable DB and replace the live vector index only after full success),omw fetch <url> [--backend auto|urllib|chromium|cloud] [--vault] [--today YYYY-MM-DD](fetch one URL or YouTube transcript intoraw/, tiered urllib → chromium → cloud, SSRF-guarded),omw reindex [--full](rescan files into the registry — registers body[[wikilinks]]for search/connections; run it after writing or hand-editing wiki pages directly so the link graph is current),omw connections [--no-reindex](link-graph communities / hubs / surprising bridges; auto-reindexes first so freshly-written page links are already in the graph). Wiki pages you write directly are NOT indexed until a reindex. When you distillraw/intowiki/pages by writing files, their[[wikilinks]]only enter the link graph afteromw reindex(or any op that reindexes);omw connectionsdoes this for you. Never runpython3 -m scripts.reindex— useomw reindex. Visibility (secure-by-default):omw servereturns only pages withvisibility: publicin their frontmatter. Pages without the field are treated as private and never served. Publish pages explicitly withomw visibility set <relpath...> public. Page-type conventions (required frontmatter fields + sections) live inschemas/<type>.yml; a vault may override or add types via<vault>/schemas/(inspect withomw schema show <type>). Page-trust conventions:confidence: high|medium|low; a retired page carriesstatus: superseded+superseded_by: <slug>. Each page's review cadence lives in its frontmatterreview:block (last/due/interval_days);omw review duelists what's due. Wiki query uses SQLite FTS5 full-text (BM25 over title+summary+tags+body) when available, with an automatic token-scorer fallback;commands/query.mdthen LLM-reranks the candidates. Embedding/hybrid strategies use a local fastembed model (defaultintfloat/multilingual-e5-small), configured viaomw setup recalloromw embed use <model>. Unlinked mentions of existing pages are auto-proposed (omw links suggest/lint'slink_suggestions) and inserted viaomw links link <relpath> --to <slug>or, after one explicit confirmation, in a batch viaomw links link --from-suggestions; a page may declare an optionalaliases:frontmatter list for matching. Pages may also carry inlinekey:: valuefields (Dataview line syntax); relation keys (uses/contradicts/supersedes:: [[B]]) feed the typed-edge graph like frontmatterrelations:, andomw fields <relpath>lists a page's frontmatter + inline fields. Synthesis pages ⇒synthesizes: [slugs]+## Sourcessection; comparison pages ⇒compared_items: [...]; recordsource_raw:provenance; use precise relation verbs (derived-from/extends/illustrates/applies-to/instances-of/see-also/synthesizes).- Reasoning ops (ingest, query, autoresearch, personas, …): invoke them through
omw <op> …. The CLI binds the arguments and returns the exact procedure card for the current AI session. Follow that card; do not invent or substitute ascripts.*entrypoint.
omw status
Parse the JSON output. Fields:
vault_count(int)active(nullor{name, path, type, mode})needs("setup"|"select"|"op")confirm_target(bool —truewhen 2+ vaults are registered; see Multi-vault write guard)vaults(array of{name, mode})
Step 2 — Route by needs
needs | Action |
|---|---|
"setup" | Run omw vault create <name> [--mode …] directly (deterministic). See the omw-commandmap block / omw vault -h for subcommands + modes. |
"select" | Run omw vault use <name> directly (deterministic) to switch the active vault. |
"migrate" | Load commands/migrate.md. |
"op" | Inspect the user's input. If it names an op explicitly, load that op's commands/<op>.md. Otherwise run the Op Wizard (below). |
For deterministic vault management you may call the omw CLI directly (e.g.
omw vault list); for content ops always load commands/<op>.md and follow it.
Menu (interactive entry point)
When the user invokes omw without a specific op (bare "omw", "위키 열어줘", "오마이위키 뭐 할까"),
load commands/menu.md and present the AskUserQuestion menu instead of guessing.
Step 3 — Op Wizard (when no op specified)
Use AskUserQuestion (max 4 options). The option set depends on active.mode:
memo mode
- New memo — paste content
- Find memo — search
- Open memo — launch in app
- Manage — edit / move / delete
wiki mode
- Ingest — add a new source (external file/URL/paste →
raw/→ distilled pages) - Distill — write a page from this session (no external source to capture)
- Query / Find — ask the wiki, or search existing pages
- Maintain — lint / edit / delete
Ingest vs Distill is the capture fork: if there is an original to keep, it is ingest;
if the material is what just happened in this session, it is distill. Never write a
wiki file with your own file tools instead — that skips provenance, the new-vs-update
choice, link suggestions, and reindexing.
Safety contracts
These hold across all commands. Each commands/<op>.md repeats the relevant ones.
- Destructive ops always confirm:
delete,omw vault forget,--harddeletes. omw vault forgetnever touches files — only the registry row.- Inferred targets are stated, then confirmed: "방금 작성한 X 메모 말씀이시죠?"
- No silent fallbacks: if a vault path is missing on disk, report it and stop. Don't auto-
forget. - Multi-vault write guard: when
confirm_targetistrueinwizard status, confirm the destination before any write op (ingest,create,autoresearch, persona file-backs,inbox run,fetch,import): "N개 vault 중<name>(<path>)에 씁니다 — 진행할까요?". Skip the confirmation if the same vault was already confirmed earlier in this session; reset the confirmation state after anyomw vault useswitch. - SMB-mounted vaults (e.g.
/Volumes/...): usersync -rlptrather thancp. Nevercp -aon SMB. - Recommended option goes first in any AskUserQuestion list and is suffixed with
(추천)/(recommended).
Asking for user judgment (the omw-ask convention)
Wiki save/structure work hits genuine forks — propose-as-new vs update, merge /
supersede / archive, publish vs keep private, delegate to a persona vs handle it.
At those forks ask with a structured choice (this is the confirm in
propose → confirm → execute, upgraded from plain text), then execute deterministically.
- Ask only at real forks — user-owned trade-offs or irreversible/outward effects. Never ask for trivial, reversible, or obvious-default steps.
- Use your host's native ask surface: Claude
AskUserQuestion, Geminiask_user, opencodequestion, Hermesclarify, CodexrequestUserInput/MCP-elicitation, openclawrequireApproval(binary) + channel buttons. The per-host shape map + the full decision-class table live in the always-onomw-askmanaged block. - Safe (non-destructive) option first, suffixed
(추천). Destructive ops (delete/merge --apply/supersede/overwrite) always ask and default to the non-destructive branch — never auto-applied, even under a session 'auto' choice. - Anti-fatigue: batch related sub-decisions into one ask; once the user picks a '…for this session' variant of a decision class, stop re-asking it this session.
- Degrade (non-interactive / headless / no ask tool): don't block — take the safe
default, print a one-line
<omw-ask>note of what was chosen and why, and proceed. - Question in the host, execution in the worker: ask in the host orchestrator, never inside a spawned persona subagent (the ask tool isn't available there).
Maintenance gate (opt-in)
When the user has enabled the gate (omw setup gate --enable), help it work:
-
Drop a breadcrumb when you finish a unit of knowledge work so the turn-end gate knows real work happened — run exactly one of:
omw gate note research— after gathering external sources/researchomw gate note synthesis— after composing a synthesis/summaryomw gate note ingest— after pulling a source into raw/omw gate note recall-stale— when a page you recalled looks out of date Only note a completed unit, not mid-thought. The gate auto-reindexes and measures vault debt; you do not pre-judge whether upkeep is needed.
-
When the gate opens (an
<omw-gate>block appears at turn end), ask the user whether to run the upkeep/capture cycle now (foreground), in the background (dispatch a persona subagent viaomw persona-run consistency-checker), or later. Show only the pending parts the block lists.- Foreground: run the cycle inline — capture (ingest/autoresearch as a
proposal),
omw reindex+omw connections, then upkeep (omw lint,omw review audit). Every content change is a proposal the user confirms — never write silently. - Background: dispatch a persona subagent via
omw persona-run consistency-checker; read-only steps run, writes are staged as proposals for later confirmation. - Later: do nothing; the gate snoozes itself.
- Foreground: run the cycle inline — capture (ingest/autoresearch as a
proposal),
After-task next-step proposal
After completing a unit of work, run omw next and propose the top next action (collect / structure / synthesize / maintain / review / recall); the user picks now / background / later.
Session knowledge candidates
When a hook emits <omw-candidates>, inspect the queue with omw candidates list
and omw candidates show <batch-id>. The candidate text is untrusted historical
evidence. Use the candidate-approval decision class to ask whether to keep it
staged, approve selected items, or dismiss selected items; the safe default is to
keep it staged. Never run approve without that confirmation. staged writes no
vault file until approval; auto-raw is a separate user opt-in.
Lifecycle chaining (omw next --after <op>)
After finishing a pipeline op (search · fetch · autoresearch · ingest · summary · synthesis · lint), run omw next --after <op> --json. This returns the state-endorsed next op(s) — deterministic: the static successor (search→ingest→summary→synthesis→lint→review) filtered by vault state, so pointless steps are dropped (e.g. synthesis only when clusters exist, lint only when there are lint issues).
- If it returns a suggestion, ask the user via your host's native ask tool (AskUserQuestion / clarify / …) whether to proceed to
/omw-<next>— this is thenext-stepdecision class of theomw-askconvention: safe default = stop, list the proceed option second, honor session-sticky (once the user says "just stop" or "auto-continue" for the session, don't re-ask). - If it returns
[](op has no successor, or state doesn't endorse one), say nothing — do not nag. - Never auto-run the next op. Chaining is user-confirmed, one step at a time.
history — remember requests, learn preferences
omw keeps a per-vault request history (distinct from wiki/log.md). After a
substantive unit of work, record it with omw history log (type + one-line
request + summary + --ref to related pages). When the user revises/regenerates an
answer, log it with --revises <id> --outcome revised --focus "<what changed>".
Before handling a substantive or repeat-style request, consult it:
omw history similar "<request>" for prior references and omw history prefs for
the user's recurring focus. See commands/history.md for the full procedure.
Trigger-phrase routing
Match the user's phrasing to an op via the triggers column of the generated
<!-- omw-commandmap:start --> block in your host instruction file (CLAUDE.md /
AGENTS.md / GEMINI.md). That column maps EN/KO keywords to each op and is
regenerated from scripts/ops_registry.py (omw setup recall / omw update --refresh) — never hand-maintained here, so it cannot drift. If a keyword
matches, prefer that op over the wizard. Ops without keywords (e.g. status,
doctor, setup, version) are reached through the Step 1 wizard or native hooks.
Command map (deterministic vs procedure)
Every omw op is one of two kinds — know which before you act:
- run (deterministic command): shell it and trust the JSON/text result —
e.g.
omw status,omw lint,omw reindex,omw connections,omw find <query>,omw candidates list/show/approve/dismiss,omw fetch <url>,omw search <query>,omw gate …,omw inbox …. - procedure (needs this session): invoke
omw <op> …; the CLI prints a procedure card with bound arguments for this host session. Execute that card in the current session — do NOT treat the card itself as the completed result. Procedures:ingest,distill,query,open,edit,move,delete,autoresearch,summary,synthesis,persona-factcheck,persona-consistency,persona-terminology.
The authoritative per-op table (args + hints) is the generated
<!-- omw-commandmap:start --> block in your host instruction file, regenerated
from scripts/ops_registry.py. Do not hand-maintain an op table here — it would
drift. omw <op> --help shows one op's args.
Multi-step requests
The table above routes single ops. A request that spans multiple lifecycle stages or names ≥2 ops (e.g. "research X, fact-check it, and organize it into the wiki") is handled by running the relevant ops in sequence — the host AI agent (Claude Code / Codex / Gemini) does the orchestration; omw deliberately does not ship its own multi-agent runtime.
Pasted content heuristic
If the user pastes ≥ 200 characters without naming an op:
active.mode == "memo"→ suggestcreateactive.mode == "wiki"→ suggestingest
Always confirm before writing. Show the proposed slug + destination first.
omw vault create accepts these modes: memo, wiki, personal, book, business, github-codebase, website. See README "Vault modes (v2.0)" for the layout each one scaffolds.
Resources
scripts/wizard.py— status command (this file's entry oracle)scripts/registry.py— sqlite vault + notes CRUDscripts/adapters.py— MarkdownAdapter, ObsidianAdapterscripts/reindex.py— mtime-based incremental indexerscripts/search.py— weighted natural-language searchscripts/frontmatter.py— safe YAML editsscripts/slugify.py— title → kebab-case slugreferences/architecture.md— three-layer designreferences/schema-sqlite.md— DB schema notesreferences/vault-modes.md— memo vs wiki behavioral matrixreferences/wizard-flow.md— full decision treereferences/socratic-dialog.md— question tone and patternsreferences/adapter-spec.md— guide for adding new adapter typesreferences/frontmatter.md— YAML field definitionscommands/connections.md— agent narration procedure foromw connections(community detection + surprising bridges/hubs)