SuperAPI AI Directory

llm-wiki

Agents & models

View repository

Updated 10-16 06:44

About this skill

LLM Wiki (Claude Code)

A persistent, interlinked markdown knowledge base at ~/wiki/llm-wiki/. Based on Karpathy's LLM Wiki pattern.

Unlike RAG (which rediscovers knowledge per query), the wiki compiles knowledge once and keeps it current. Cross-references exist. Contradictions are flagged. Synthesis reflects everything ingested.

Boundary: Wiki vs Claude Code Memory

Store Path Contains
Wiki (this skill) ~/wiki/llm-wiki/ Research knowledge — papers, concepts, entities, comparisons
Memory (Claude Code default) ~/.claude/projects/-Users-charlesqin/memory/ User preferences, feedback, project context

Never mix them. If a note describes what the user is learning, it goes in the wiki. If it describes how the user works, it goes in memory.

Orient Before Acting (every session)

Before any ingest / query / lint, read these three in order:

  1. Read ~/wiki/llm-wiki/SCHEMA.md — domain, conventions, tag taxonomy
  2. Read ~/wiki/llm-wiki/index.md — what pages exist
  3. List ~/wiki/llm-wiki/log/ via Glob(pattern: "log/*.md") and Read the last 2–3 days — recent activity

Skipping orientation causes duplicate pages, missed cross-references, tag sprawl, and repeated work.

For queries on large wikis (100+ pages), also run a Grep for the topic before creating anything new.

Structure

Multi-wiki container: ~/wiki/ is a container that may hold multiple sub-wikis (e.g. ~/wiki/llm-wiki/, future ~/wiki/<name>-wiki/). This skill operates on the llm-wiki sub-wiki by default. A single Obsidian vault at the container root (~/wiki/.obsidian/) covers all sub-wikis, so cross-wiki [[wikilinks]] still work.

When adding a new sub-wiki: mkdir ~/wiki/<name>-wiki, scaffold the same tree below, and write a domain-specific SCHEMA.md. Update ~/wiki/README.md to list it.

~/wiki/                      ← Container (Obsidian vault root)
├── .obsidian/               ← Shared vault config (covers all sub-wikis)
├── README.md                ← Container-level sub-wiki index
└── llm-wiki/                ← This skill operates here by default
    ├── SCHEMA.md            # Domain, conventions, tag taxonomy (read-first)
    ├── index.md             # Sectioned content catalog
    ├── log/                 # Per-day action log (YYYYMMDD.md)
    ├── raw/                 # Immutable sources — NEVER modify
    │   ├── articles/
    │   ├── papers/
    │   ├── transcripts/
    │   ├── assets/
    │   └── refs/            # Pointer files for large binaries kept outside raw/
    ├── entities/            # People, orgs, products, models
    ├── concepts/            # Topics, techniques, methods
    ├── comparisons/         # Side-by-side analyses
    ├── queries/             # Filed query answers worth keeping
    └── _archive/            # Superseded content (kept, de-indexed)

Raw sources in raw/ are immutable. Corrections go in wiki pages, never in raw files.

Log convention: one markdown file per day at log/YYYYMMDD.md. H1 is the ISO date (# 2026-04-23), each entry is ## [HH:MM] <op> | <subject> with a short bullet body. Ops: ingest, query, lint, compile, create, update, merge, archive, migrate. Grep across history:

grep -rh "^## \[" ~/wiki/llm-wiki/log/ | tail -20       # recent activity
grep -rh "^## \[.*\] lint"  ~/wiki/llm-wiki/log/        # all lint runs
grep -rl "dpo"              ~/wiki/llm-wiki/log/        # days that touched dpo

Page Sizing & Divide-and-Conquer

Target page length: 400–1200 words. One page = one scannable unit.

When a topic grows past ~1200 words, split into a subfolder:

concepts/<topic>/
├── index.md          # Overview + list of sub-pages with one-line summaries
├── <aspect-1>.md
├── <aspect-2>.md
└── <aspect-3>.md

In the wiki root index.md, show the hierarchy via indented bullets:

- [[concepts/<topic>/index|<topic>]] — one-line summary
    - [[concepts/<topic>/<aspect-1>]] — ...
    - [[concepts/<topic>/<aspect-2>]] — ...

One fat file covering seven aspects is unreadable, unlinkable, and produces noisy diffs. Seven focused files + an index page give navigation, selective reading, clean backlinks, and small audit targets.

When to split: during a compile pass (see Core Operations), not mid-ingest — splitting while ingesting fragments attention.

Large Binaries: raw/refs/ Pointer Files

Small text sources (md, txt, PDFs under ~10 MB, small images) → copy into raw/<subfolder>/.

Large binaries (videos, model weights, datasets, PDFs >10 MB) → do not copy into the wiki. Create a pointer at raw/refs/<slug>.md:

---
kind: ref
external_path: /Volumes/external/models/llama-3-70b/
size: ~140 GB
---

Llama 3 70B weights, downloaded 2026-01-15 from HuggingFace. Used by [[lora]] experiments.

Wiki pages cite [[raw/refs/<slug>]] just like any other source. The wiki directory stays git-friendly and rsync-able without dragging binaries around.

Diagrams & Math

  • Any flow, sequence, hierarchy, or state diagram → mermaid. Never ASCII art — it rots fast and is unsearchable.

    ```mermaid
    flowchart LR
        A[raw/paper.md] --> B[summary]
        B --> C[concept page]
        C --> D[index.md]
    ```
    
  • Any formula → KaTeX. Inline $f(x) = \sum_i w_i x_i$ or block:

    $$
    L_{DPO} = -\mathbb{E}[\log \sigma(\beta \log \frac{\pi(y_w|x)}{\pi_{ref}(y_w|x)} - \beta \log \frac{\pi(y_l|x)}{\pi_{ref}(y_l|x)})]
    $$
    

Both render in Obsidian (default settings) and in most Markdown viewers. ASCII diagrams and formulas are refactor-hostile, unsearchable, and don't survive copy-paste cleanly.

Core Operations

1. Ingest — add a source to the wiki

Step 1 — Capture raw source

Source Tool Destination
URL (article) WebFetch ~/wiki/llm-wiki/raw/articles/<slug>.md
PDF / arxiv WebFetch ~/wiki/llm-wiki/raw/papers/<slug>.md
Pasted text Write appropriate raw/ subdir

Name files descriptively: raw/papers/lora-hu-2021.md, raw/articles/karpathy-llm-wiki-2026.md.

Step 2 — Discuss takeaways with the user (skip in automated/cron contexts).

Step 3 — Check what already exists

  • Read ~/wiki/llm-wiki/index.md
  • Grep for entities/concepts across the wiki:
    Grep(pattern: "LoRA", path: "/Users/charlesqin/wiki/llm-wiki", glob: "*.md", output_mode: "files_with_matches")
    
  • This is the difference between a growing wiki and a pile of duplicates.

Step 4 — Write or update pages per SCHEMA.md thresholds

  • Create a page when an entity/concept appears in 2+ sources OR is central to one source
  • Update an existing page when the source adds or refines information — bump updated date
  • Don't create pages for passing mentions, minor details, or things outside the domain
  • Every new/updated page: ≥2 outbound [[wikilinks]] to other pages
  • Every tag must be in SCHEMA.md's taxonomy — add new tags there first, then use them
  • Contradictions: don't silently overwrite. Note both positions with dates + sources, set contradictions: [page-name] in frontmatter, flag for user

Required frontmatter on every page:

---
title: Page Title
created: YYYY-MM-DD
updated: YYYY-MM-DD
type: entity | concept | comparison | query | summary
tags: [from SCHEMA taxonomy]
sources: [raw/papers/source-name.md]
---

Step 5 — Update navigation

  • Add new pages to index.md under the correct section, alphabetically within section
  • Update "Total pages" count and "Last updated" in index header
  • Append to log/<today-YYYYMMDD>.md (create the file with # YYYY-MM-DD H1 if today's doesn't exist yet):
    ## [HH:MM] ingest | <Source Title>
    - Raw: raw/papers/<slug>.md
    - Created: entities/X.md, concepts/Y.md
    - Updated: concepts/Z.md (added section on ...)
    

Step 6 — Report every file created/updated back to the user.

A single source commonly touches 5–15 wiki pages. That's the compounding effect.

2. Query — answer a question from the wiki

  1. Read ~/wiki/llm-wiki/index.md
  2. For wikis with 100+ pages, also Grep across ~/wiki/llm-wiki/**/*.md for key terms
  3. Read the relevant pages
  4. Synthesize. Cite the sources: "Based on [[lora]] and [[peft]]…"
  5. File the answer back if it's a substantial synthesis / comparison / deep dive:
    • Create a page in queries/ or comparisons/ with required frontmatter
    • Add to index.md
    • Don't file trivial lookups — only answers that would be painful to re-derive
  6. Append to log/<today-YYYYMMDD>.md:
    ## [HH:MM] query | <question>
    - Filed to: queries/<slug>.md  (or "not filed — trivial")
    

3. Lint — audit wiki health

Run a Python script via Bash for programmatic checks. Inline template:

python3 <<'EOF'
import os, re, yaml
from pathlib import Path
from collections import defaultdict

WIKI = Path.home() / "wiki" / "llm-wiki"
DIRS = ["entities", "concepts", "comparisons", "queries"]

# Collect all wiki pages — rglob picks up split-page subfolders (concepts/<topic>/<aspect>.md).
# Pages are keyed by relative path for uniqueness; a stem-to-paths multimap supports flat [[wikilinks]].
pages = {}                          # rel-path str -> Path
stem_to_paths = defaultdict(list)   # stem -> [rel-path str]
for d in DIRS:
    for p in (WIKI / d).rglob("*.md"):
        rel = str(p.relative_to(WIKI))
        pages[rel] = p
        stem_to_paths[p.stem].append(rel)

inbound = defaultdict(set)
issues = {"orphans": [], "broken_links": [], "missing_frontmatter": [], "unknown_tags": [], "missing_sources": []}

# Load taxonomy from SCHEMA.md — scoped to the "## Tag Taxonomy" section so non-tag bullets don't leak in
schema = (WIKI / "SCHEMA.md").read_text()
taxonomy = set()
in_section = False
for line in schema.splitlines():
    stripped = line.strip()
    if stripped.startswith("## Tag Taxonomy"):
        in_section = True
        continue
    if in_section and line.startswith("## "):
        in_section = False
    if in_section and stripped.startswith("- "):
        for t in stripped.lstrip("- ").split(","):
            t = t.strip()
            if t:
                taxonomy.add(t)

URL_RE = re.compile(r"^https?://", re.I)

def resolve_wikilink(target):
    """Resolve [[target]] to a canonical rel-path. Returns rel-path | 'AMBIGUOUS:<paths>' | None."""
    # Path-qualified form (contains slash): match exact rel-path
    for cand in (target, target + ".md"):
        if cand in pages:
            return cand
    # Bare-stem form: match against stem_to_paths
    stem = target.split("/")[-1]
    matches = stem_to_paths.get(stem, [])
    if len(matches) == 1:
        return matches[0]
    if len(matches) > 1:
        return "AMBIGUOUS:" + "|".join(matches)
    return None

def raw_target_exists(target):
    """Check that a raw/ wikilink or raw/ source path resolves to a file on disk."""
    candidates = [WIKI / target, WIKI / (target + ".md")]
    return any(p.exists() for p in candidates)

for rel, path in pages.items():
    text = path.read_text()
    fm_match = re.match(r"^---\n(.*?)\n---", text, re.S)
    if not fm_match:
        issues["missing_frontmatter"].append(rel)
        continue
    try:
        fm = yaml.safe_load(fm_match.group(1))
    except Exception:
        issues["missing_frontmatter"].append(rel)
        continue
    if not isinstance(fm, dict):
        issues["missing_frontmatter"].append(f"{rel} (frontmatter is not a mapping)")
        continue
    # Required frontmatter — `sources` is required; pages with no raw source still set `sources: []`
    required = {"title", "created", "updated", "type", "tags", "sources"}
    missing = required - set(fm)
    if missing:
        issues["missing_frontmatter"].append(f"{rel} (missing: {missing})")
    # Tags against scoped taxonomy
    for tag in fm.get("tags") or []:
        if tag not in taxonomy:
            issues["unknown_tags"].append(f"{rel}: {tag}")
    # `sources: []` is the backfill backlog signal; URL entries are valid (allowed for query pages)
    src = fm.get("sources")
    if "sources" in fm and not src:
        issues["missing_sources"].append(rel)
    # raw/ entries inside `sources:` must exist on disk
    if isinstance(src, list):
        for s in src:
            if isinstance(s, str) and s.startswith("raw/") and not raw_target_exists(s):
                issues["broken_links"].append(f"{rel} (sources): missing raw target {s}")
    # Wikilinks
    for link in re.findall(r"\[\[([^\]]+)\]\]", text):
        target = link.split("|")[0].split("#")[0].strip()
        if target.startswith("raw/"):
            if not raw_target_exists(target):
                issues["broken_links"].append(f"{rel} -> [[{target}]] (raw target not on disk)")
            continue
        resolved = resolve_wikilink(target)
        if resolved is None:
            issues["broken_links"].append(f"{rel} -> [[{target}]]")
        elif resolved.startswith("AMBIGUOUS:"):
            paths = resolved[len("AMBIGUOUS:"):]
            issues["broken_links"].append(f"{rel} -> [[{target}]] (ambiguous, matches: {paths})")
        elif resolved != rel:
            inbound[resolved].add(rel)

# Orphans: zero inbound links
for rel in pages:
    if not inbound[rel]:
        issues["orphans"].append(rel)

# Index completeness — page is indexed if its stem OR rel-path appears in index.md
index_text = (WIKI / "index.md").read_text()
for rel, path in pages.items():
    if path.stem not in index_text and rel not in index_text:
        issues.setdefault("not_indexed", []).append(rel)

# Report
print(f"Pages: {len(pages)} | Taxonomy: {len(taxonomy)} tags")
total = sum(len(v) for v in issues.values())
print(f"Total issues: {total}")
for k, v in issues.items():
    print(f"\n## {k} ({len(v)})")
    for item in v[:20]:
        print(f"  - {item}")
    if len(v) > 20:
        print(f"  ... and {len(v)-20} more")
EOF

Other checks (add or run separately):

  • Stale pages: updated date >90 days ago
  • Page size: >1200 words → candidate for splitting via compile (see Page Sizing & Divide-and-Conquer)
  • Log rotation: not needed — daily log/YYYYMMDD.md files stay naturally small and git-diff-friendly

Report findings grouped by severity: broken_links > missing_frontmatter > orphans > unknown_tags > missing_sources > stale. The missing_sources count must agree with the backfill backlog in sources.md; if they diverge, one of them is stale. Append to log/<today-YYYYMMDD>.md:

## [HH:MM] lint | N issues found
- broken_links: X, orphans: Y, unknown_tags: Z, missing_sources: W

4. Compile — restructure existing wiki content

Periodic structural maintenance: split oversized pages, merge near-duplicates, rebuild index.md. lint finds problems; compile fixes structural ones.

When to run

  • After a batch ingest (5+ sources in a row)
  • When an existing page has outgrown ~1200 words
  • When index.md drifts from filesystem reality
  • When the user says "clean up the wiki" / "restructure" / "refactor the wiki"

Steps

  1. Orient: Read ~/wiki/llm-wiki/SCHEMA.md, ~/wiki/llm-wiki/index.md, and every file in the target subtree.
  2. For each page over ~1200 words: plan a split into concepts/<topic>/index.md + <aspect>.md per the divide-and-conquer pattern. Confirm the plan with the user before writing — splits are structural and expensive to reverse.
  3. For each pair of near-duplicate pages: propose a merge. Confirm, then rewrite as one.
  4. Rewrite index.md so every page appears exactly once under the right section, with hierarchy via indented bullets for split topics.
  5. Re-run lint as the final check — broken wikilinks are common after moves.
  6. Log to log/<today-YYYYMMDD>.md:
## [HH:MM] compile | <what changed>
- Split: concepts/lora.md (1800 words) → concepts/lora/{index,rank-r-choice,merging,limitations}.md
- Merged: concepts/dpo.md + concepts/direct-preference.md → concepts/dpo.md
- Rebuilt index.md (29 → 32 pages)

Compile is destructive — it moves files and rewrites pages. Always orient first, always confirm before applying, always re-lint after.

Tool Mapping Reference

Claude Code tools used by this skill:

Operation Tool Notes
Read a wiki page or SCHEMA Read Use offset/limit for long files
Search wiki content Grep path: "/Users/charlesqin/wiki/llm-wiki", glob: "*.md"
List all pages Glob pattern: "**/*.md"
Fetch URL/PDF WebFetch Saves markdown from the web
Write new page Write Only for new files
Update existing page Edit Always bump updated date
Run lint script Bash Python script above

Pitfalls

  • Never modify raw/ — sources are immutable, corrections live in wiki pages
  • Always orient first — SCHEMA + index + recent log, every new session. Skipping causes duplicates.
  • Always update index.md and today's log/YYYYMMDD.md — these are the navigational backbone; skipping makes the wiki decay
  • Don't create pages for passing mentions — follow SCHEMA thresholds (2+ sources OR central to one)
  • Every page needs ≥2 outbound [[wikilinks]] — isolated pages are invisible
  • Tags only from SCHEMA taxonomy — freeform tags decay into noise; add new tags there first
  • Handle contradictions explicitly — note both, mark in frontmatter, flag for user
  • Ask before mass updates — if an ingest would touch 10+ existing pages, confirm scope first
  • Keep pages scannable — target 400–1200 words per page; split oversized via compile into concepts/<topic>/ subfolders
  • Don't cross-contaminate with Claude Code memory — research goes in wiki, user preferences in memory

Obsidian

The Obsidian vault is ~/wiki/ (the container), not any individual sub-wiki. .obsidian/ lives at the container root so all sub-wikis share one graph view and one link namespace. Open ~/wiki/ in Obsidian for:

  • [[wikilinks]] as clickable links
  • Graph View for the knowledge network
  • Dataview queries like TABLE tags FROM "concepts" WHERE contains(tags, "lora")

Set Obsidian's attachment folder to raw/assets/ so ![[image.png]] resolves.