diff --git a/.claude-plugin/marketplace.json b/.claude-plugin/marketplace.json index 27648dcd2..82f1fb1ce 100644 --- a/.claude-plugin/marketplace.json +++ b/.claude-plugin/marketplace.json @@ -4,12 +4,12 @@ "name": "Alireza Rezvani", "url": "https://alirezarezvani.com" }, - "description": "234 production-ready skill packages for Claude AI across 9 domains: marketing (44), engineering (43+37), C-level advisory (34), regulatory/QMS (14), product (16), project management (9), business growth (5), and finance (4). Includes 306 Python tools, 427 reference documents, 25 agents, and 22 slash commands.", + "description": "235 production-ready skill packages for Claude AI across 9 domains: marketing (44), engineering (45+37), C-level advisory (34), regulatory/QMS (14), product (16), project management (9), business growth (5), and finance (4). Includes 314 Python tools, 435 reference documents, 28 agents, and 27 slash commands.", "homepage": "https://github.com/alirezarezvani/claude-skills", "repository": "https://github.com/alirezarezvani/claude-skills", "metadata": { - "description": "234 production-ready skill packages across 9 domains with 306 Python tools, 427 reference documents, 25 agents, and 22 slash commands. Compatible with Claude Code, Codex CLI, Gemini CLI, and OpenClaw.", - "version": "2.2.0" + "description": "235 production-ready skill packages across 9 domains with 314 Python tools, 435 reference documents, 28 agents, and 27 slash commands. Compatible with Claude Code, Codex CLI, Cursor, Antigravity, OpenCode, Gemini CLI, and OpenClaw.", + "version": "2.3.0" }, "plugins": [ { @@ -59,7 +59,7 @@ { "name": "engineering-advanced-skills", "source": "./engineering", - "description": "43 advanced engineering skills: agent designer, agent workflow designer, AgentHub, RAG architect, database designer, focused-fix, browser-automation, spec-driven-workflow, secrets-vault-manager, sql-database-assistant, migration architect, observability designer, dependency auditor, release manager, API reviewer, CI/CD pipeline builder, MCP server builder, skill security auditor, performance profiler, Helm chart builder, Terraform patterns, self-eval, llm-cost-optimizer, prompt-governance, behuman, code-tour, demo-video, data-quality-auditor, statistical-analyst, and more.", + "description": "44 advanced engineering skills: agent designer, agent workflow designer, AgentHub, RAG architect, database designer, focused-fix, browser-automation, spec-driven-workflow, secrets-vault-manager, sql-database-assistant, migration architect, observability designer, dependency auditor, release manager, API reviewer, CI/CD pipeline builder, MCP server builder, skill security auditor, performance profiler, Helm chart builder, Terraform patterns, self-eval, llm-cost-optimizer, prompt-governance, behuman, code-tour, demo-video, data-quality-auditor, statistical-analyst, llm-wiki (second brain for Obsidian + Claude Code, Karpathy pattern), and more.", "version": "2.2.0", "author": { "name": "Alireza Rezvani" @@ -638,6 +638,28 @@ "ui-design" ], "category": "design" + }, + { + "name": "llm-wiki", + "source": "./engineering/llm-wiki", + "description": "A second brain for Claude Code + Obsidian inspired by Karpathy's LLM Wiki gist. Turn any LLM CLI into a disciplined wiki maintainer: incrementally ingest sources into a persistent, interlinked markdown vault; update entity/concept/source pages; flag contradictions; maintain index and append-only log. Knowledge compounds instead of being re-derived by RAG on every query. Ships 3 sub-agents (wiki-ingestor, wiki-librarian, wiki-linter), 5 slash commands (/wiki-init, /wiki-ingest, /wiki-query, /wiki-lint, /wiki-log), 8 Python tools (stdlib only: init_vault, ingest_source, update_index, append_log, wiki_search BM25, lint_wiki, graph_analyzer, export_marp), 8 reference docs, full vault templates (CLAUDE.md, AGENTS.md, cursorrules, 5 page templates), and a worked example vault. Cross-tool compatible with Claude Code, Codex CLI, Cursor, Antigravity, OpenCode, and Gemini CLI.", + "version": "2.3.0", + "author": { + "name": "Alireza Rezvani" + }, + "keywords": [ + "knowledge-management", + "obsidian", + "second-brain", + "pkm", + "wiki", + "rag-alternative", + "karpathy", + "memex", + "incremental-knowledge", + "cross-tool" + ], + "category": "knowledge" } ] } diff --git a/.codex/skills-index.json b/.codex/skills-index.json index da502be9b..96251576e 100644 --- a/.codex/skills-index.json +++ b/.codex/skills-index.json @@ -3,7 +3,7 @@ "name": "claude-code-skills", "description": "Production-ready skill packages for AI agents - Marketing, Engineering, Product, C-Level, PM, and RA/QM", "repository": "https://github.com/alirezarezvani/claude-skills", - "total_skills": 196, + "total_skills": 197, "skills": [ { "name": "contract-and-proposal-writer", @@ -557,6 +557,12 @@ "category": "engineering-advanced", "description": "Use when you need to reduce LLM API spend, control token usage, route between models by cost/quality, implement prompt caching, or build cost observability for AI features. Triggers: 'my AI costs are too high', 'optimize token usage', 'which model should I use', 'LLM spend is out of control', 'implement prompt caching'. NOT for RAG pipeline design (use rag-architect). NOT for prompt writing quality (use senior-prompt-engineer)." }, + { + "name": "llm-wiki", + "source": "../../engineering/llm-wiki", + "category": "engineering-advanced", + "description": "Use when building or maintaining a persistent personal knowledge base (second brain) in Obsidian where an LLM incrementally ingests sources, updates entity/concept pages, maintains cross-references, and keeps a synthesis current. Triggers include \"second brain\", \"Obsidian wiki\", \"personal knowledge management\", \"ingest this paper/article/book\", \"build a research wiki\", \"compound knowledge\", \"Memex\", or whenever the user wants knowledge to accumulate across sessions instead of being re-derived by RAG on every query." + }, { "name": "mcp-server-builder", "source": "../../engineering/mcp-server-builder", @@ -1199,7 +1205,7 @@ "description": "Software engineering and technical skills" }, "engineering-advanced": { - "count": 44, + "count": 45, "source": "../../engineering", "description": "Advanced engineering skills - agents, RAG, MCP, CI/CD, databases, observability" }, diff --git a/.codex/skills/llm-wiki b/.codex/skills/llm-wiki new file mode 120000 index 000000000..368aeb709 --- /dev/null +++ b/.codex/skills/llm-wiki @@ -0,0 +1 @@ +../../engineering/llm-wiki \ No newline at end of file diff --git a/.gemini/skills-index.json b/.gemini/skills-index.json index bad46721d..4381122ae 100644 --- a/.gemini/skills-index.json +++ b/.gemini/skills-index.json @@ -1,7 +1,7 @@ { "version": "1.0.0", "name": "gemini-cli-skills", - "total_skills": 283, + "total_skills": 294, "skills": [ { "name": "README", @@ -93,6 +93,21 @@ "category": "agent", "description": "UX research agent for research planning, persona generation, journey mapping, and usability test analysis" }, + { + "name": "cs-wiki-ingestor", + "category": "agent", + "description": "Dispatched sub-agent that ingests a new source into an LLM Wiki vault. Reads the source, proposes TL;DR and key claims, identifies which entity/concept/synthesis pages will be touched, flags contradictions with existing pages, and \u2014 after user confirmation \u2014 writes the source summary, updates cross-references across 5-15 pages, regenerates the index, and appends a standardized log entry. Spawn when the user says \"ingest this\", \"add this paper/article/book to the wiki\", or drops a file into raw/." + }, + { + "name": "cs-wiki-librarian", + "category": "agent", + "description": "Dispatched sub-agent that answers queries against an LLM Wiki vault. Reads index.md first, drills into 3-10 relevant pages across categories, synthesizes an answer with inline [[wikilink]] citations, and offers to file the answer back into the wiki as a new comparison or synthesis page. Spawn when the user asks a substantive question the wiki might answer, says \"what does the wiki say about X\", \"compare A and B across my sources\", or wants to explore a topic." + }, + { + "name": "cs-wiki-linter", + "category": "agent", + "description": "Dispatched sub-agent that runs a periodic health check on an LLM Wiki vault. Runs mechanical checks via scripts (orphans, broken links, stale pages, missing frontmatter, duplicate titles, log gaps), does semantic checks (contradictions, stale claims, cross-reference gaps, concepts missing their own page), and produces a markdown report with suggested actions. Spawn weekly, after batch ingests, or when the user says \"check the wiki\" / \"lint my wiki\" / \"audit the vault\"." + }, { "name": "cs-workspace-admin", "category": "agent", @@ -418,6 +433,11 @@ "category": "command", "description": "Sprint planning shortcut. Usage: /sprint-plan [capacity]" }, + { + "name": "tc", + "category": "command", + "description": "Track technical changes with structured records, a state machine, and session handoff. Usage: /tc [args]" + }, { "name": "tdd", "category": "command", @@ -433,6 +453,31 @@ "category": "command", "description": "Generate user stories with acceptance criteria and sprint planning. Usage: /user-story [options]" }, + { + "name": "wiki-ingest", + "category": "command", + "description": "Ingest a source file from raw/ into the LLM Wiki \u2014 read, discuss, write summary page, update cross-references across 5-15 pages, regenerate index, append to log. Usage /wiki-ingest " + }, + { + "name": "wiki-init", + "category": "command", + "description": "Bootstrap a fresh LLM Wiki vault with the three-layer structure, schema files, and starter templates. Usage /wiki-init --topic \"\" [--tool all|claude-code|codex|cursor|antigravity]" + }, + { + "name": "wiki-lint", + "category": "command", + "description": "Run a health check on the LLM Wiki vault \u2014 mechanical checks (orphans, broken links, stale pages, missing frontmatter, log gap, duplicates) plus semantic checks (contradictions, cross-reference gaps, concepts missing their own page). Outputs a markdown report with suggested actions. Usage /wiki-lint [--stale-days N] [--log-gap-days N]" + }, + { + "name": "wiki-log", + "category": "command", + "description": "Show recent entries from the LLM Wiki log (wiki/log.md). Uses the standardized ## [YYYY-MM-DD] header format so grep + tail works. Usage /wiki-log [--last N] [--op ingest|query|lint|...]" + }, + { + "name": "wiki-query", + "category": "command", + "description": "Query the LLM Wiki \u2014 reads index.md first, drills into 3-10 relevant pages, synthesizes an answer with inline [[wikilink]] citations, and offers to file the answer back as a new comparison or synthesis page. Usage /wiki-query \"\"" + }, { "name": "a11y-audit", "category": "engineering", @@ -823,6 +868,11 @@ "category": "engineering-advanced", "description": "Use when you need to reduce LLM API spend, control token usage, route between models by cost/quality, implement prompt caching, or build cost observability for AI features. Triggers: 'my AI costs are too high', 'optimize token usage', 'which model should I use', 'LLM spend is out of control', 'implement prompt caching'. NOT for RAG pipeline design (use rag-architect). NOT for prompt writing quality (use senior-prompt-engineer)." }, + { + "name": "llm-wiki", + "category": "engineering-advanced", + "description": "Use when building or maintaining a persistent personal knowledge base (second brain) in Obsidian where an LLM incrementally ingests sources, updates entity/concept pages, maintains cross-references, and keeps a synthesis current. Triggers include \"second brain\", \"Obsidian wiki\", \"personal knowledge management\", \"ingest this paper/article/book\", \"build a research wiki\", \"compound knowledge\", \"Memex\", or whenever the user wants knowledge to accumulate across sessions instead of being re-derived by RAG on every query." + }, { "name": "loop", "category": "engineering-advanced", @@ -963,6 +1013,11 @@ "category": "engineering-advanced", "description": "Run hypothesis tests, analyze A/B experiment results, calculate sample sizes, and interpret statistical significance with effect sizes. Use when you need to validate whether observed differences are real, size an experiment correctly before launch, or interpret test results with confidence." }, + { + "name": "tc-tracker", + "category": "engineering-advanced", + "description": "Use when the user asks to track technical changes, create change records, manage TC lifecycles, or hand off work between AI sessions. Covers init/create/update/status/resume/close/export workflows for structured code change documentation." + }, { "name": "tech-debt-tracker", "category": "engineering-advanced", @@ -1223,6 +1278,11 @@ "category": "product", "description": "Agile product ownership for backlog management and sprint execution. Covers user story writing, acceptance criteria, sprint planning, and velocity tracking. Use for writing user stories, creating acceptance criteria, planning sprints, estimating story points, breaking down epics, or prioritizing backlog." }, + { + "name": "apple-hig-expert", + "category": "product", + "description": "Expert guidance on Apple Human Interface Guidelines (HIG). Covers iOS, macOS, and visionOS with 2026 Liquid Glass aesthetics and accessibility-first design." + }, { "name": "code-to-prd", "category": "product", @@ -1298,11 +1358,6 @@ "category": "product", "description": "UX research and design toolkit for Senior UX Designer/Researcher including data-driven persona generation, journey mapping, usability testing frameworks, and research synthesis. Use for user research, persona creation, journey mapping, and design validation." }, - { - "name": "apple-hig-expert", - "category": "product", - "description": "Expert guidance on Apple Human Interface Guidelines (HIG). Covers iOS, macOS, and visionOS with focus on 2026 Liquid Glass aesthetics and accessibility-first design." - }, { "name": "atlassian-admin", "category": "project-management", @@ -1421,7 +1476,7 @@ ], "categories": { "agent": { - "count": 25, + "count": 28, "description": "Agent resources" }, "business-growth": { @@ -1433,7 +1488,7 @@ "description": "C-level resources" }, "command": { - "count": 22, + "count": 28, "description": "Command resources" }, "engineering": { @@ -1441,7 +1496,7 @@ "description": "Engineering resources" }, "engineering-advanced": { - "count": 57, + "count": 59, "description": "Engineering-advanced resources" }, "finance": { @@ -1453,7 +1508,7 @@ "description": "Marketing resources" }, "product": { - "count": 16, + "count": 17, "description": "Product resources" }, "project-management": { diff --git a/.gemini/skills/apple-hig-expert/SKILL.md b/.gemini/skills/apple-hig-expert/SKILL.md new file mode 120000 index 000000000..5f2da183a --- /dev/null +++ b/.gemini/skills/apple-hig-expert/SKILL.md @@ -0,0 +1 @@ +../../../product-team/apple-hig-expert/SKILL.md \ No newline at end of file diff --git a/.gemini/skills/cs-wiki-ingestor/SKILL.md b/.gemini/skills/cs-wiki-ingestor/SKILL.md new file mode 120000 index 000000000..c4f828b5f --- /dev/null +++ b/.gemini/skills/cs-wiki-ingestor/SKILL.md @@ -0,0 +1 @@ +../../../agents/engineering/cs-wiki-ingestor.md \ No newline at end of file diff --git a/.gemini/skills/cs-wiki-librarian/SKILL.md b/.gemini/skills/cs-wiki-librarian/SKILL.md new file mode 120000 index 000000000..8a510161f --- /dev/null +++ b/.gemini/skills/cs-wiki-librarian/SKILL.md @@ -0,0 +1 @@ +../../../agents/engineering/cs-wiki-librarian.md \ No newline at end of file diff --git a/.gemini/skills/cs-wiki-linter/SKILL.md b/.gemini/skills/cs-wiki-linter/SKILL.md new file mode 120000 index 000000000..325d908b6 --- /dev/null +++ b/.gemini/skills/cs-wiki-linter/SKILL.md @@ -0,0 +1 @@ +../../../agents/engineering/cs-wiki-linter.md \ No newline at end of file diff --git a/.gemini/skills/llm-wiki/SKILL.md b/.gemini/skills/llm-wiki/SKILL.md new file mode 120000 index 000000000..f7581c7ef --- /dev/null +++ b/.gemini/skills/llm-wiki/SKILL.md @@ -0,0 +1 @@ +../../../engineering/llm-wiki/SKILL.md \ No newline at end of file diff --git a/.gemini/skills/tc-tracker/SKILL.md b/.gemini/skills/tc-tracker/SKILL.md new file mode 120000 index 000000000..23e8e65f4 --- /dev/null +++ b/.gemini/skills/tc-tracker/SKILL.md @@ -0,0 +1 @@ +../../../engineering/tc-tracker/SKILL.md \ No newline at end of file diff --git a/.gemini/skills/wiki-ingest/SKILL.md b/.gemini/skills/wiki-ingest/SKILL.md new file mode 120000 index 000000000..addbab0ba --- /dev/null +++ b/.gemini/skills/wiki-ingest/SKILL.md @@ -0,0 +1 @@ +../../../commands/wiki-ingest.md \ No newline at end of file diff --git a/.gemini/skills/wiki-init/SKILL.md b/.gemini/skills/wiki-init/SKILL.md new file mode 120000 index 000000000..1f88611dd --- /dev/null +++ b/.gemini/skills/wiki-init/SKILL.md @@ -0,0 +1 @@ +../../../commands/wiki-init.md \ No newline at end of file diff --git a/.gemini/skills/wiki-lint/SKILL.md b/.gemini/skills/wiki-lint/SKILL.md new file mode 120000 index 000000000..6a69eb18c --- /dev/null +++ b/.gemini/skills/wiki-lint/SKILL.md @@ -0,0 +1 @@ +../../../commands/wiki-lint.md \ No newline at end of file diff --git a/.gemini/skills/wiki-log/SKILL.md b/.gemini/skills/wiki-log/SKILL.md new file mode 120000 index 000000000..f71d4db26 --- /dev/null +++ b/.gemini/skills/wiki-log/SKILL.md @@ -0,0 +1 @@ +../../../commands/wiki-log.md \ No newline at end of file diff --git a/.gemini/skills/wiki-query/SKILL.md b/.gemini/skills/wiki-query/SKILL.md new file mode 120000 index 000000000..4133c1f94 --- /dev/null +++ b/.gemini/skills/wiki-query/SKILL.md @@ -0,0 +1 @@ +../../../commands/wiki-query.md \ No newline at end of file diff --git a/CLAUDE.md b/CLAUDE.md index 7b3292aea..8ca995c08 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -6,7 +6,7 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co This is a **comprehensive skills library** for Claude AI and Claude Code - reusable, production-ready skill packages that bundle domain expertise, best practices, analysis tools, and strategic frameworks. The repository provides modular skills that teams can download and use directly in their workflows. -**Current Scope:** 234 production-ready skills across 9 domains with 306 Python automation tools, 427 reference guides, 25 agents, and 22 slash commands. +**Current Scope:** 235 production-ready skills across 9 domains with 314 Python automation tools, 435 reference guides, 28 agents, and 27 slash commands. **Key Distinction**: This is NOT a traditional application. It's a library of skill packages meant to be extracted and deployed by users into their own Claude workflows. @@ -39,8 +39,8 @@ claude-code-skills/ ├── agents/ # 25 agents across all domains ├── commands/ # 22 slash commands (changelog, tdd, saas-health, prd, code-to-prd, plugin-audit, sprint-plan, etc.) ├── engineering-team/ # 37 core engineering skills + Playwright Pro + Self-Improving Agent + Security Suite -├── engineering/ # 43 POWERFUL-tier advanced skills (incl. AgentHub, self-eval) -├── product-team/ # 16 product skills + Python tools +├── engineering/ # 45 POWERFUL-tier advanced skills (incl. AgentHub, self-eval, llm-wiki, tc-tracker) +├── product-team/ # 16 product skills (incl. apple-hig-expert) + Python tools ├── marketing-skill/ # 44 marketing skills (7 pods) + Python tools ├── c-level-advisor/ # 34 C-level advisory skills (10 roles + orchestration) ├── project-management/ # 9 PM skills + Atlassian MCP @@ -124,7 +124,13 @@ See [standards/git/git-workflow-standards.md](standards/git/git-workflow-standar ## Current Version -**Version:** v2.2.0 (latest) +**Version:** v2.3.0 (latest) + +**v2.3.0 Highlights:** +- **llm-wiki plugin** — new POWERFUL-tier skill implementing Karpathy's LLM Wiki pattern. Second brain for Claude Code + Obsidian where the LLM incrementally ingests sources into a persistent, interlinked markdown vault. Ships SKILL.md (with `context: fork`), 3 sub-agents (wiki-ingestor, wiki-librarian, wiki-linter), 5 slash commands (/wiki-init, /wiki-ingest, /wiki-query, /wiki-lint, /wiki-log), 8 stdlib-only Python tools, 8 reference guides, full vault templates, and a worked example. Cross-tool compatible with Claude Code, Codex CLI, Cursor, Antigravity, OpenCode, Gemini CLI. +- 234 total skills, 313 Python tools, 432 references, 28 agents, 27 commands + +**Version:** v2.2.0 **v2.2.0 Highlights:** - **Security skills suite** — 6 new engineering-team skills: adversarial-reviewer, ai-security, cloud-security, incident-response, red-team, threat-detection (5 Python tools, 4 reference guides) @@ -206,6 +212,6 @@ This repository publishes skills to **ClawHub** (clawhub.com) as the distributio --- -**Last Updated:** March 31, 2026 -**Version:** v2.2.0 -**Status:** 234 skills deployed across 9 domains, 28 marketplace plugins, docs site live +**Last Updated:** April 11, 2026 +**Version:** v2.3.0 +**Status:** 235 skills deployed across 9 domains, 30 marketplace plugins, docs site live diff --git a/agents/engineering/cs-wiki-ingestor.md b/agents/engineering/cs-wiki-ingestor.md new file mode 100644 index 000000000..185f08ace --- /dev/null +++ b/agents/engineering/cs-wiki-ingestor.md @@ -0,0 +1,89 @@ +--- +name: cs-wiki-ingestor +description: Dispatched sub-agent that ingests a new source into an LLM Wiki vault. Reads the source, proposes TL;DR and key claims, identifies which entity/concept/synthesis pages will be touched, flags contradictions with existing pages, and — after user confirmation — writes the source summary, updates cross-references across 5-15 pages, regenerates the index, and appends a standardized log entry. Spawn when the user says "ingest this", "add this paper/article/book to the wiki", or drops a file into raw/. +skills: engineering/llm-wiki +domain: engineering +model: opus +tools: [Read, Write, Edit, Bash, Grep, Glob] +context: fork +--- + +# wiki-ingestor + +## Role + +You are a disciplined wiki maintainer. A user has dropped a new source into the `raw/` layer of an LLM Wiki vault and asked you to ingest it. Your job is to read it, discuss it with the user, and integrate it into the `wiki/` layer — touching every relevant entity, concept, and synthesis page, flagging contradictions, updating the index, and appending to the log. + +You are spawned **per-ingest**, not as a long-running agent. You do one source at a time. + +## Inputs + +- Path to a source file (must be inside the vault's `raw/` layer) +- The current state of `wiki/` (especially `index.md`) +- The vault's `CLAUDE.md` or `AGENTS.md` schema + +## Workflow + +Follow `references/ingest-workflow.md` in the llm-wiki skill. Summary: + +### 1. Prep +Run `python /scripts/ingest_source.py --vault . --source --json` to get the brief (title guess, word count, preview, suggested summary path, whether a summary already exists). + +### 2. Read +Use the Read tool on the source file directly. For PDFs, use Read's PDF support. For images, use vision. + +### 3. Discuss (user in the loop) +Before writing anything, report to the user: +- Title, authors, date +- 2-3 sentence TL;DR +- Key claims (3-7 bullets) +- **Which existing wiki pages you plan to touch** (bulleted wikilinks) +- **Any contradictions** with existing pages +- Whether this is a fresh ingest or a **merge** (summary page exists) + +**Wait for the user to confirm or redirect before writing.** + +### 4. Write the source summary +Create `wiki/sources/.md` using the source-summary template from the llm-wiki skill. Required frontmatter: `title`, `category: source`, `summary`, `source_path`, `ingested`, `updated`. + +If the page exists (merge mode), append a new `## Re-ingest ` section at the bottom. + +### 5. Update every relevant page +For each entity and concept mentioned in the source: +- **If the page exists:** update "Key claims", "Appears in" / "Used in", increment `sources:`, set `updated:` to today +- **If not:** create a stub page from the appropriate template with at least the minimum (title, summary, one key fact, link back to this source) + +A typical ingest touches **5-15 pages**. Don't skimp — the wiki's value comes from cross-references. + +### 6. Flag contradictions +If this source contradicts an existing page, add a `> ⚠️ Contradiction:` callout to **both** pages, linking the disagreeing sources. + +### 7. Update synthesis pages +If the source meaningfully shifts a `synthesis/` page's thesis, revise the "Thesis" paragraph and append a dated entry under "How this synthesis has changed". + +### 8. Regenerate the index +Run `python /scripts/update_index.py --vault .` OR edit `wiki/index.md` inline for small changes. + +### 9. Log the ingest +Run `python /scripts/append_log.py --vault . --op ingest --title "" --detail "<touched pages summary>"`. + +### 10. Report back +Give the user a bulleted list of every touched page as wikilinks, plus any contradictions flagged. + +## Rules + +- **`raw/` is immutable.** Never edit files there. Read only. +- **Every write goes to `wiki/`.** +- **Discuss before writing.** The user is in the loop. +- **Minimum 5 file touches per ingest.** (source summary + 2-4 cross-references + index + log) +- **Cite aggressively.** Every claim on an entity/concept page links to a source page. +- **Flag contradictions** on both sides. +- **Update `updated:` frontmatter** on every page you touch. + +## Red flags + +Stop and ask the user before proceeding if: +- The source is outside `raw/` +- The source appears to duplicate an existing source exactly +- Ingesting would require deleting existing wiki pages (only the user decides) +- You detect >5 contradictions in one ingest (likely a paradigm-shifting source — worth a conversation) diff --git a/agents/engineering/cs-wiki-librarian.md b/agents/engineering/cs-wiki-librarian.md new file mode 100644 index 000000000..7e2b7a2eb --- /dev/null +++ b/agents/engineering/cs-wiki-librarian.md @@ -0,0 +1,83 @@ +--- +name: cs-wiki-librarian +description: Dispatched sub-agent that answers queries against an LLM Wiki vault. Reads index.md first, drills into 3-10 relevant pages across categories, synthesizes an answer with inline [[wikilink]] citations, and offers to file the answer back into the wiki as a new comparison or synthesis page. Spawn when the user asks a substantive question the wiki might answer, says "what does the wiki say about X", "compare A and B across my sources", or wants to explore a topic. +skills: engineering/llm-wiki +domain: engineering +model: sonnet +tools: [Read, Write, Edit, Bash, Grep, Glob] +context: fork +--- + +# wiki-librarian + +## Role + +You answer questions against an LLM Wiki vault. You prioritize reading over re-deriving — the wiki already contains pre-synthesized knowledge with cross-references and citations. Your job is to find the right pages, read them, and compose an answer that cites them properly. You also **file good answers back** into the wiki so explorations compound. + +You are spawned **per-query**, not as a long-running agent. + +## Inputs + +- The user's question +- The current state of `wiki/` (especially `index.md`) + +## Workflow + +Follow `references/query-workflow.md`. Summary: + +### 1. Read `index.md` first +The index is the catalog. Scan it and pick the 3-10 pages most likely to contain the answer. Pick across categories: +- `synthesis/` for the big picture +- `concepts/` for definitions +- `sources/` for evidence +- `entities/` for context +- `comparisons/` for explicit contrasts + +### 2. Read the picked pages in full +They're short and curated. The wiki has done the hard work. + +### 3. Follow wikilinks opportunistically +If a read page points to another clearly relevant page, follow it. Stop when you have enough. + +### 4. Fall back to search if needed +If the index doesn't surface the right pages, run: +```bash +python <plugin>/scripts/wiki_search.py --vault . --query "<terms>" --limit 5 +``` + +Flag this to the user — stale index means lint time. + +### 5. Synthesize the answer +Format: +- **Direct answer** — 1-3 sentences +- **Supporting detail** — organized thematically +- **Inline citations** — `[[sources/xxx]]` wikilinks throughout; every claim links to its source +- **Related pages** — 3-5 wikilinks at the end + +### 6. Offer to file the answer back +This is the compounding move. At the end of the answer, ask: + +> _Should I file this as a new page in the wiki? Suggested location: +> `wiki/comparisons/<slug>.md` — or I can append it to an existing page._ + +If yes: +- Pick the right category (most often `comparisons/` or `synthesis/`) +- Use the appropriate template (see llm-wiki skill's `references/page-formats.md`) +- Add frontmatter with `category`, `summary`, `sources` (count), `updated` +- Update `wiki/index.md` (inline or via script) +- Append to `log.md`: `python <plugin>/scripts/append_log.py --vault . --op create --title "<question>" --detail "filed query response to <path>"` + +## Rules + +- **Read the index first.** Do not grep the entire wiki on every query. +- **Every claim cites a page.** No uncited assertions. +- **If the wiki doesn't know, say so.** Suggest a source to ingest instead of inventing content. +- **Offer to file back** every substantive answer — but don't file trivial one-off answers. +- **Output format follows the question.** Comparison questions get tables. Overview questions get markdown pages. Data questions get charts (save to `wiki/assets/charts/`). + +## Red flags + +- Answering without reading the index → go back +- Citing only one source for a multi-source question → broaden +- Inventing concepts not in the wiki → stop and suggest ingestion +- Creating a new page for a trivial question → don't pollute the wiki diff --git a/agents/engineering/cs-wiki-linter.md b/agents/engineering/cs-wiki-linter.md new file mode 100644 index 000000000..19c45a7f9 --- /dev/null +++ b/agents/engineering/cs-wiki-linter.md @@ -0,0 +1,104 @@ +--- +name: cs-wiki-linter +description: Dispatched sub-agent that runs a periodic health check on an LLM Wiki vault. Runs mechanical checks via scripts (orphans, broken links, stale pages, missing frontmatter, duplicate titles, log gaps), does semantic checks (contradictions, stale claims, cross-reference gaps, concepts missing their own page), and produces a markdown report with suggested actions. Spawn weekly, after batch ingests, or when the user says "check the wiki" / "lint my wiki" / "audit the vault". +skills: engineering/llm-wiki +domain: engineering +model: opus +tools: [Read, Write, Edit, Bash, Grep, Glob] +context: fork +--- + +# wiki-linter + +## Role + +You are the wiki's auditor. You run periodic health checks and surface problems for the user to fix — contradictions, orphans, stale pages, missing cross-references, concepts lacking their own page. You do NOT silently auto-fix structural issues; you report and suggest. The user decides what to fix. + +You are spawned **per-lint-pass**, not as a long-running agent. + +## Workflow + +Follow `references/lint-workflow.md`. Three passes. + +### Pass 1 — Mechanical (scripts) + +Run both: + +```bash +python <plugin>/scripts/lint_wiki.py --vault . --json > /tmp/lint.json +python <plugin>/scripts/graph_analyzer.py --vault . --json > /tmp/graph.json +``` + +Parse the JSON. Capture: +- Orphans (zero inbound links) +- Broken links (wikilinks pointing to non-existent pages) +- Stale pages (`updated:` older than 90 days) +- Missing frontmatter (pages without title/category/summary) +- Duplicate titles +- Log gap (no entries in 14+ days) +- Connected components (more than 1 = disconnected islands) +- Hubs (high-fan-out or high-fan-in pages) +- Sinks (no outbound links) + +### Pass 2 — Semantic (you read and think) + +The scripts can't catch these. You must read. + +**A. Contradictions.** Scan pages whose `updated:` is recent. For each, check whether it contradicts any related page. If so, add a `> ⚠️ Contradiction:` callout to both. + +**B. Stale claims.** For each flagged stale page, ask: has a newer source invalidated a claim? Suggest re-ingest or a new source hunt. + +**C. Concepts mentioned without their own page.** Grep for concept-shaped nouns that appear across 3+ pages as plain text (not wikilinks). Suggest new concept pages. + +**D. Cross-reference gaps.** For each recently-touched page, check if every entity/concept mentioned is a wikilink. Promote plain-text mentions to wikilinks where appropriate. + +**E. Index drift.** Compare `index.md` against actual wiki contents. If out of sync, suggest regeneration. + +### Pass 3 — Report + +Produce a markdown report: + +```markdown +# Wiki lint — <date> + +**Total pages:** N **Components:** N **Last log:** <date> + +## Found +- ⚠️ <N> contradictions (list with wikilinks) +- <N> orphan pages +- <N> broken links +- <N> stale pages +- <N> concepts mentioned across 3+ pages without their own page +- <N> pages with missing frontmatter +- <other findings> + +## Suggested actions +1. Investigate contradiction between [[sources/a]] and [[sources/b]] +2. Create concept page for "<name>" (mentioned in N sources) +3. Re-ingest [[sources/c]] — stale + contradicted by newer sources +4. Fix broken link in [[concepts/x]] +5. Cross-reference the N orphans (most belong under [[synthesis/overview]]) + +Want me to run these in order, or pick specific ones? +``` + +Then append a log entry: + +```bash +python <plugin>/scripts/append_log.py --vault . --op lint --title "<date> health check" --detail "<findings summary>" +``` + +## Rules + +- **Report, don't silently fix.** The user decides what to change. +- **Prioritize by impact.** Contradictions > broken links > orphans > stale > style issues. +- **Use both scripts.** Mechanical + graph both reveal different problems. +- **Suggest actions** — never just dump findings without recommendations. +- **Always log the pass.** The log tracks wiki health over time. + +## Red flags + +- Auto-fixing structural issues without asking → stop +- Skipping semantic pass because "the scripts look clean" → do the read-and-think pass anyway +- Reporting without suggestions → add suggestions +- Not updating `log.md` → always log diff --git a/commands/wiki-ingest.md b/commands/wiki-ingest.md new file mode 100644 index 000000000..e640ca2f3 --- /dev/null +++ b/commands/wiki-ingest.md @@ -0,0 +1,52 @@ +--- +name: wiki-ingest +description: Ingest a source file from raw/ into the LLM Wiki — read, discuss, write summary page, update cross-references across 5-15 pages, regenerate index, append to log. Usage /wiki-ingest <path-to-source> +--- + +# /wiki-ingest + +Ingest a new source into the LLM Wiki. This is the most-used command. + +The flow: read the source → discuss TL;DR and key claims with you → write a source summary page → update every relevant entity and concept page → flag contradictions → update `index.md` → append to `log.md`. + +A typical ingest touches **5-15 wiki pages**. You (the user) are in the loop: the ingestor proposes changes and waits for your confirmation before writing. + +## Usage + +``` +/wiki-ingest <path> +/wiki-ingest raw/papers/monosemanticity.pdf +/wiki-ingest raw/articles/2026-04-01-interpretability-post.md +``` + +## What happens + +1. **Prep** — runs `scripts/ingest_source.py` to get title, preview, and suggested summary path +2. **Read** — reads the source directly +3. **Discuss** — reports TL;DR, key claims, which pages will be touched, any contradictions +4. **Confirm** — waits for your go-ahead (or redirects) +5. **Write** — creates the source summary, updates 5-15 pages, flags contradictions +6. **Index** — runs `scripts/update_index.py` or edits `wiki/index.md` inline +7. **Log** — runs `scripts/append_log.py --op ingest --title "<title>"` +8. **Report** — bulleted wikilinks to every touched page + +## Sub-agent + +This command dispatches the `wiki-ingestor` sub-agent for the heavy lifting. See `agents/wiki-ingestor.md`. + +## Scripts + +- `engineering/llm-wiki/scripts/ingest_source.py` — source prep (metadata + preview) +- `engineering/llm-wiki/scripts/update_index.py` — regenerate index +- `engineering/llm-wiki/scripts/append_log.py` — log the ingest + +## Rules + +- The source must be inside the vault's `raw/` layer. If it isn't, the command will ask you to move it first. +- `raw/` is immutable — the ingestor reads only. +- If a summary page already exists, the ingestor enters **merge mode** and appends a re-ingest section. + +## Skill Reference + +→ `engineering/llm-wiki/SKILL.md` +→ `engineering/llm-wiki/references/ingest-workflow.md` diff --git a/commands/wiki-init.md b/commands/wiki-init.md new file mode 100644 index 000000000..e62f95754 --- /dev/null +++ b/commands/wiki-init.md @@ -0,0 +1,60 @@ +--- +name: wiki-init +description: Bootstrap a fresh LLM Wiki vault with the three-layer structure, schema files, and starter templates. Usage /wiki-init <path> --topic "<topic>" [--tool all|claude-code|codex|cursor|antigravity] +--- + +# /wiki-init + +Bootstrap a new LLM Wiki vault. Creates `raw/`, `wiki/{entities,concepts,sources,comparisons,synthesis}`, the index and log, and installs the schema file(s) for your LLM CLI of choice. + +## Usage + +``` +/wiki-init <path> --topic "<one-line topic>" +/wiki-init <path> --topic "<topic>" --tool <claude-code|codex|cursor|antigravity|opencode|gemini-cli|all> +/wiki-init <path> --topic "<topic>" --force # overwrite non-empty dir +``` + +## Examples + +``` +/wiki-init ~/vaults/research --topic "LLM interpretability" +/wiki-init ./book-wiki --topic "The Power Broker — Robert Caro" --tool all +/wiki-init ~/vaults/founders --topic "SaaS founder playbook" --tool codex +``` + +## What it creates + +``` +<path>/ +├── raw/ +│ └── assets/ +├── wiki/ +│ ├── index.md # from template +│ ├── log.md # from template +│ ├── entities/ +│ ├── concepts/ +│ ├── sources/ +│ ├── comparisons/ +│ ├── synthesis/ +│ └── .templates/ # page templates for reference +├── CLAUDE.md # if --tool claude-code or all +├── AGENTS.md # if --tool codex|cursor|antigravity|opencode|gemini-cli|all +├── .cursorrules # if --tool cursor or all +└── .gitignore +``` + +## Next steps + +After init: +1. Open the vault in Obsidian +2. Drop a source into `raw/` +3. Run `/wiki-ingest raw/<your-file>` + +## Script + +- `engineering/llm-wiki/scripts/init_vault.py` + +## Skill Reference + +→ `engineering/llm-wiki/SKILL.md` diff --git a/commands/wiki-lint.md b/commands/wiki-lint.md new file mode 100644 index 000000000..1822a2f85 --- /dev/null +++ b/commands/wiki-lint.md @@ -0,0 +1,83 @@ +--- +name: wiki-lint +description: Run a health check on the LLM Wiki vault — mechanical checks (orphans, broken links, stale pages, missing frontmatter, log gap, duplicates) plus semantic checks (contradictions, cross-reference gaps, concepts missing their own page). Outputs a markdown report with suggested actions. Usage /wiki-lint [--stale-days N] [--log-gap-days N] +--- + +# /wiki-lint + +Health-check the wiki. Surfaces orphan pages, broken wikilinks, stale claims, missing frontmatter, contradictions, and structural drift. **Reports, doesn't silently fix** — you decide what to change. + +Run this weekly, after batch ingests, and always before sharing the wiki. + +## Usage + +``` +/wiki-lint +/wiki-lint --stale-days 60 +/wiki-lint --log-gap-days 7 +``` + +## What happens + +### Pass 1 — Mechanical (scripts) + +- `scripts/lint_wiki.py` — orphans, broken links, stale pages, missing frontmatter, duplicate titles, log gap +- `scripts/graph_analyzer.py` — hubs, sinks, connected components, graph stats + +### Pass 2 — Semantic (LLM reads and thinks) + +- Contradictions between recently-updated pages +- Stale claims superseded by newer sources +- Concepts mentioned in plain text across 3+ pages without their own page +- Cross-reference gaps (entities mentioned but not wikilinked) +- Index drift (index.md out of sync with wiki/) + +### Pass 3 — Report + +A markdown report grouped by severity: + +```markdown +# Wiki lint — <date> + +**Total pages:** N **Components:** N **Last log:** <date> + +## Found +- ⚠️ <N> contradictions (list) +- <N> orphans +- <N> broken links +- <N> stale pages +- ... + +## Suggested actions +1. Investigate contradiction between [[sources/a]] and [[sources/b]] +2. Create concept page for "<name>" +3. Fix broken link in [[concepts/x]] +4. Re-ingest [[sources/c]] — stale + contradicted +5. ... +``` + +Then appends a `lint` entry to `log.md`. + +## Sub-agent + +Dispatches the `wiki-linter` sub-agent. See `agents/wiki-linter.md`. + +## Scripts + +- `engineering/llm-wiki/scripts/lint_wiki.py` +- `engineering/llm-wiki/scripts/graph_analyzer.py` +- `engineering/llm-wiki/scripts/append_log.py` + +## Frequency + +| Trigger | Pass | +|---|---| +| Weekly | Mechanical only — fast | +| After batch ingest | Full (mechanical + semantic) | +| Monthly | Full + structural review | +| Before sharing | Full + extra review | + +## Skill Reference + +→ `engineering/llm-wiki/SKILL.md` +→ `engineering/llm-wiki/references/lint-workflow.md` diff --git a/commands/wiki-log.md b/commands/wiki-log.md new file mode 100644 index 000000000..404c38c4b --- /dev/null +++ b/commands/wiki-log.md @@ -0,0 +1,64 @@ +--- +name: wiki-log +description: Show recent entries from the LLM Wiki log (wiki/log.md). Uses the standardized ## [YYYY-MM-DD] header format so grep + tail works. Usage /wiki-log [--last N] [--op ingest|query|lint|...] +--- + +# /wiki-log + +Show recent entries from `wiki/log.md`. Every LLM operation on the wiki leaves a standardized entry: + +``` +## [YYYY-MM-DD] <op> | <title> +<optional detail> +``` + +## Usage + +``` +/wiki-log # last 10 entries +/wiki-log --last 20 +/wiki-log --op ingest --last 10 # only ingest entries +/wiki-log --op lint # recent lint passes +/wiki-log --since 2026-04-01 +``` + +## What it does + +Parses `wiki/log.md` and prints matching entries. No LLM involvement needed — this is essentially: + +```bash +grep "^## \[" wiki/log.md | tail -N +``` + +…plus optional filters for op type and date range. + +## Valid ops + +- `ingest` — a source was read and integrated +- `query` — a question was answered (when filed back) +- `lint` — a health check ran +- `create` — a new page was created outside an ingest +- `update` — a page was updated outside an ingest +- `delete` — a page was removed +- `note` — freeform note (contradictions flagged, thesis revisions, etc.) + +## Example output + +``` +## [2026-04-11] lint | weekly health check +3 contradictions, 12 orphans, 2 broken links. Fixed broken links; left contradictions for next session. + +## [2026-04-10] ingest | Anthropic Monosemanticity +Added sources/monosemanticity.md. Updated concepts/sparse-autoencoder, concepts/polysemanticity, entities/anthropic. + +## [2026-04-09] query | SAE vs probing +Filed back to comparisons/sae-vs-probing.md. +``` + +## Scripts + +- Uses `grep` + `tail` directly on `wiki/log.md`. No dedicated script needed; that's the point of the standardized header format. + +## Skill Reference + +→ `engineering/llm-wiki/SKILL.md` diff --git a/commands/wiki-query.md b/commands/wiki-query.md new file mode 100644 index 000000000..d209dc52e --- /dev/null +++ b/commands/wiki-query.md @@ -0,0 +1,58 @@ +--- +name: wiki-query +description: Query the LLM Wiki — reads index.md first, drills into 3-10 relevant pages, synthesizes an answer with inline [[wikilink]] citations, and offers to file the answer back as a new comparison or synthesis page. Usage /wiki-query "<question>" +--- + +# /wiki-query + +Ask the wiki a question. The librarian reads `index.md` first, picks relevant pages across categories, synthesizes an answer with citations, and offers to file the answer back into the wiki so your explorations compound. + +## Usage + +``` +/wiki-query "<your question>" +/wiki-query "what does the wiki say about sparse autoencoders?" +/wiki-query "compare monosemanticity and polysemanticity across my sources" +/wiki-query "which sources disagree on scaling laws?" +/wiki-query "give me a comparison table of SAE vs linear probing" +``` + +## What happens + +1. **Index-first read** — reads `wiki/index.md` to find relevant pages +2. **Drill-in** — reads 3-10 pages in full (synthesis + concepts + sources + entities) +3. **Follow links** — opportunistically follows wikilinks between pages +4. **Fallback search** — if the index isn't enough, runs `scripts/wiki_search.py` (BM25) +5. **Synthesize** — composes a direct answer + supporting detail + inline `[[sources/xxx]]` citations + "Related pages" section +6. **Offer to file back** — asks whether to save this as a new wiki page (usually in `comparisons/` or `synthesis/`) + +## Output formats + +The answer's format follows the question: + +| Question shape | Output | +|---|---| +| "What is X?" | Markdown explanation with citations | +| "A vs B" | Comparison table | +| "Give me a slide deck on X" | Markdown synthesis → `/wiki-marp` to render | +| "Chart the trend in X" | Python script + saved chart in `wiki/assets/charts/` | + +## Sub-agent + +This command dispatches the `wiki-librarian` sub-agent. See `agents/wiki-librarian.md`. + +## Scripts + +- `engineering/llm-wiki/scripts/wiki_search.py` — BM25 fallback search +- `engineering/llm-wiki/scripts/append_log.py` — log filed answers + +## Rules + +- **Read the index first.** No grep-everything. +- **Every claim cites a page** with a `[[wikilink]]`. +- **Offer to file the answer back** — but only for substantive answers worth keeping. + +## Skill Reference + +→ `engineering/llm-wiki/SKILL.md` +→ `engineering/llm-wiki/references/query-workflow.md` diff --git a/documentation/implementation/craighewitt-mattpocock-reimplementation-plan.md b/documentation/implementation/craighewitt-mattpocock-reimplementation-plan.md new file mode 100644 index 000000000..12a5de826 --- /dev/null +++ b/documentation/implementation/craighewitt-mattpocock-reimplementation-plan.md @@ -0,0 +1,144 @@ +# CraigHewitt + MattPocock Skills — Reimplementation Plan + +**Status:** Deferred (captured 2026-04-10, not yet executed) +**Source:** User-installed skill collections at `~/.claude/skills-sources/thecraighewitt-skills/` and `~/.claude/skills-sources/mattpocock-skills/` +**Goal:** Build better, more practical versions of selected skills from both collections inside claude-code-skills, reflecting real user workflows. + +--- + +## Source collections surveyed + +### TheCraigHewitt Skills (49 skills) +- **Layout:** `sales/` (21), `ceo/` (10), `youtube/` (15), `coding/` (2), `general/` (1) +- **Depth:** Avg ~420 lines per skill. Deep playbooks, opinionated frameworks (SPIN, MEDDIC, Gap Selling, retention curves). +- **Strengths:** Battle-tested methodologies from a working SaaS founder + YouTube creator. Context-aware via persistent `BUSINESS_CONTEXT.md`. +- **Gaps:** Mostly prompt-only. No scripts, no templates, no reference docs, no measurement, no skill chaining beyond naming. + +### MattPocock Skills (19 skills) +- **Layout:** Flat directory. Themes: Planning & Design, Development, Tooling & Setup, Writing & Knowledge. +- **Depth:** Avg ~90 lines per skill (TDD has 6 supporting reference files). +- **Strengths:** Composable engineering pipeline (`grill-me` → `write-a-prd` → `prd-to-issues`). Some real shell scripts (`git-guardrails-claude-code`, `setup-pre-commit`). Tracer-bullet TDD methodology. +- **Gaps:** Light on examples, assumes prior knowledge ("deep modules", "tracer bullets"), narrow to software engineering. + +--- + +## Overlap analysis vs existing claude-code-skills repo + +| Domain | Existing skill | New material overlaps? | Net-new | +|---|---|---|---| +| Sales | `marketing-skill/cold-email`, `marketing-skill/email-sequence`, `business-growth/sales-engineer`, `business-growth/revenue-operations` | Partial | discovery-call, demo-script, objection-handling, negotiation, pipeline-review, forecast, win-loss-analysis, proposal-pricing, outbound-sequence, cold-call, lead-research, prospect-research, direct-mail, referral-intro, event-networking | +| CEO | `c-level-advisor/ceo-advisor`, `c-level-advisor/founder-coach`, `c-level-advisor/decision-logger` | Partial | weekly-review, quarterly-review, financial-review, one-on-ones, hiring, delegation, strategic-sparring, meeting-prep, content-repurpose | +| YouTube/Creator | ❌ none | ✅ entirely net-new | All 15 skills (idea-generation, hook-writing, title-craft, thumbnail-design, script-structure, retention-editing, description-seo, end-screen-cta, channel-strategy, channel-audit, content-calendar, video-analysis, audience-research, collab-outreach) | +| Engineering process | `engineering-team/senior-qa`, `engineering-team/code-reviewer` | ❌ no TDD/PRD/grill equivalents | write-a-prd, grill-me, tdd-vertical-slice, design-an-interface, improve-codebase-architecture, triage-issue, git-guardrails, setup-pre-commit, ubiquitous-language, edit-article, qa | + +--- + +## Proposed build — 4 pods, ~30 skills + +Each pod targets a high-value area and is built to claude-code-skills standards: `SKILL.md` + `scripts/` (Python CLI tools) + `references/` (knowledge bases) + `assets/` (templates + example galleries). All new skills include YAML frontmatter, measurable outputs, and integration hooks with existing skills. + +### Pod 1 — `creator-team/` (NEW DOMAIN, ~10 skills) — highest priority +**Why:** Entirely net-new territory. No YouTube/creator skills in our repo today. Obvious gap for solo creators, YouTubers, content businesses. + +**Skills to build:** +1. `creator-context` — persistent context file (channel, audience, niche, tone, goals) that other creator skills read +2. `idea-generation` — content ideation w/ scoring matrix, competitive gap analysis, keyword tool integration +3. `hook-writing` — first-30-seconds hook generator with retention curve benchmarks +4. `title-craft` — SEO + browse-optimized title generator with A/B scoring tool +5. `thumbnail-design` — composition rules, psychology brief, Canva/Figma brief generator +6. `script-structure` — long-form script skeleton with retention beat markers +7. `retention-editing` — edit decision list (EDL) generator, pacing analyzer +8. `channel-audit` — YouTube API analytics pull + diagnosis +9. `content-calendar` — batch planning + pillar allocation +10. `audience-research` — survey builder, comment-mining tool, persona synthesizer + +**Python tools to add:** `youtube_analytics_pull.py`, `retention_analyzer.py`, `title_scorer.py`, `thumbnail_brief.py`, `idea_scorer.py`, `hook_rater.py` +**References:** retention curve theory, thumbnail psychology, YouTube algorithm primer, brand voice guide +**Templates:** channel brief, video brief, script outline, thumbnail brief, calendar spreadsheet + +### Pod 2 — `business-growth/sales-motion/` (EXTEND, ~10 skills) +**Why:** We have strategic `sales-engineer` but no tactical motion playbooks. Tactical motion skills are what sales reps actually run daily. + +**Skills to build:** +1. `sales-motion-context` — persistent ICP, stage definitions, methodology choice (MEDDIC/SPIN) +2. `discovery-call` — call framework + scorecard +3. `demo-script` — scoped demo generator with tailored value mapping +4. `objection-handling` — objection library + response bank +5. `negotiation` — concession matrix, BATNA scaffolding +6. `forecast` — weighted pipeline math, commit/upside/best-case categorizer +7. `pipeline-review` — stage hygiene checker, stalled deal detector +8. `win-loss-analysis` — pattern extractor across closed deals +9. `proposal-pricing` — pricing tiers, ROI builder +10. `outbound-sequence` — multi-channel cadence designer with personalization tokens + +**Python tools:** `forecast_calculator.py`, `pipeline_hygiene_checker.py`, `deal_score.py`, `sequence_builder.py`, `win_loss_miner.py` +**References:** MEDDIC, SPIN, Gap Selling, Sandler frameworks; deal stage rubrics; objection library +**Templates:** call scorecard, discovery framework, demo script shell, proposal template, forecast spreadsheet, sequence JSON + +### Pod 3 — `c-level-advisor/operating-rhythm/` (EXTEND, ~6 skills) +**Why:** We have strategic advisors but thin operating rhythm coverage. These are the recurring rituals that keep a company running. + +**Skills to build:** +1. `weekly-review` — 15-min scorecard + blocker surfacing +2. `quarterly-review` — 90-min reset + OKR refresh +3. `financial-review` — P&L walkthrough + variance analysis +4. `one-on-ones` — 1:1 framework, check-in cadence +5. `hiring` — problem-based role spec, candidate rubric, comp positioning +6. `delegation` — delegation matrix, stretch assignment framework +7. `strategic-sparring` — pressure-test decisions, 30/90/180 day consequence mapping (integrates with existing `decision-logger`) + +**Python tools:** `scorecard_builder.py`, `okr_tracker.py`, `delegation_matrix.py`, `hiring_rubric_scorer.py` +**References:** hiring frameworks, 1:1 playbooks, OKR design, operating cadence patterns +**Templates:** weekly scorecard, QBR deck outline, 1:1 agenda, hiring scorecard, delegation matrix + +### Pod 4 — `engineering/dev-process/` (NEW POWERFUL POD, ~6 skills) +**Why:** MattPocock's methodology (deep modules, tracer bullets, vertical slices, HITL vs AFK) isn't represented in our engineering pods. These are "how you think about building" skills, not role-based. + +**Skills to build:** +1. `grill-me` — relentless interview to resolve decision tree before any code +2. `write-a-prd` — interactive PRD builder → GitHub issue +3. `prd-to-issues` — break PRD into independently-grabbable GitHub issues +4. `tdd-vertical-slice` — tracer-bullet TDD (not horizontal slices) +5. `design-an-interface` — generate 3+ parallel API designs, compare on depth/simplicity +6. `triage-issue` — bug investigation → fix plan → GitHub issue +7. `git-guardrails` — PreToolUse hook installer blocking dangerous git commands + +**Python tools:** `prd_to_issues.py`, `issue_filer.py`, `git_guardrails_installer.py`, `module_depth_analyzer.py` +**References:** Ousterhout deep modules primer, tracer bullet pattern, vertical slice TDD, ports & adapters +**Templates:** PRD template, GitHub issue template, refactor plan template, pre-commit hook config + +--- + +## What makes this "better" than the originals + +1. **Python automation** — every skill has at least one executable tool, not just prompts +2. **Real templates** — ready-to-fill artifacts, not "here's the structure" +3. **Measurement baked in** — benchmarks and success metrics per skill (e.g., cold-email reply rate targets, retention curve thresholds) +4. **Example galleries** — 3-5 anonymized real outputs per skill +5. **Shared context models** — per-pod context files (creator-context, sales-motion-context) that multiple skills read, so the user doesn't re-answer diagnostic questions each session +6. **Skill chaining** — explicit orchestration (e.g., `creator-workflow` meta-skill chains ideation → script → hook → thumbnail) +7. **Integration with existing skills** — `pipeline-review` reads from `revenue-operations`; `strategic-sparring` writes to `decision-logger` +8. **Cross-tool portability** — plugin.json, marketplace entry, symlink-friendly for Codex/Cursor/OpenClaw + +## What NOT to port + +- `coding/ralph` — autonomous PRD impl loop. Complex, Docker-sandboxed, overlaps with our `self-improving-agent`. Skip or reference. +- Duplicates where we already have strong coverage (`cold-email` — our existing version is deeper) +- Anything too opinionated to generalize (e.g., specific SaaS founder methodology that doesn't fit all audiences) + +## Execution plan + +- **Phase A — Pod 1 (creator-team)** as proof of concept. Highest-value, entirely net-new, measurable success. +- **Phase B — Pod 2 (sales-motion)** once Phase A is validated. +- **Phase C — Pod 3 (operating-rhythm)** extends existing c-level-advisor. +- **Phase D — Pod 4 (dev-process)** extends engineering/. + +**Estimated effort:** 2-3 focused sessions per pod (~10 sessions total). Can be run as separate PRs off `dev`. + +**Branch strategy:** `feature/creator-team-pod1` → PR to `dev` → repeat per pod. + +**Version target:** v2.3.0 when Pod 1 ships; v2.4.0 when all 4 pods complete. + +--- + +**Next action when resumed:** Confirm scope with user (all 4 pods vs start with Pod 1), then create `feature/creator-team-pod1` branch and begin building `creator-context` + first 3 skills. diff --git a/engineering/.claude-plugin/plugin.json b/engineering/.claude-plugin/plugin.json index ae4cbb7b7..a38f302b9 100644 --- a/engineering/.claude-plugin/plugin.json +++ b/engineering/.claude-plugin/plugin.json @@ -1,7 +1,7 @@ { "name": "engineering-advanced-skills", - "description": "38 advanced engineering skills: agent designer, agent workflow designer, AgentHub, RAG architect, database designer, migration architect, observability designer, dependency auditor, release manager, API reviewer, CI/CD pipeline builder, MCP server builder, skill security auditor, performance profiler, Helm chart builder, Terraform patterns, focused-fix, browser-automation, spec-driven-workflow, secrets-vault-manager, sql-database-assistant, self-eval, llm-cost-optimizer, prompt-governance, and more. Agent skill and plugin for Claude Code, Codex, Gemini CLI, Cursor, OpenClaw.", - "version": "2.2.0", + "description": "44 advanced engineering skills: agent designer, agent workflow designer, AgentHub, RAG architect, database designer, migration architect, observability designer, dependency auditor, release manager, API reviewer, CI/CD pipeline builder, MCP server builder, skill security auditor, performance profiler, Helm chart builder, Terraform patterns, focused-fix, browser-automation, spec-driven-workflow, secrets-vault-manager, sql-database-assistant, self-eval, llm-cost-optimizer, prompt-governance, llm-wiki (second brain for Obsidian + Claude Code, Karpathy pattern), and more. Agent skill and plugin for Claude Code, Codex, Gemini CLI, Cursor, OpenClaw.", + "version": "2.3.0", "author": { "name": "Alireza Rezvani", "url": "https://alirezarezvani.com" diff --git a/engineering/llm-wiki/.claude-plugin/plugin.json b/engineering/llm-wiki/.claude-plugin/plugin.json new file mode 100644 index 000000000..22c0a291b --- /dev/null +++ b/engineering/llm-wiki/.claude-plugin/plugin.json @@ -0,0 +1,13 @@ +{ + "name": "llm-wiki", + "description": "Turn Claude Code + Obsidian into a second brain. The LLM incrementally ingests sources into a persistent, interlinked markdown wiki — building entity/concept/source pages, flagging contradictions, maintaining an index and log. Knowledge compounds instead of being re-derived by RAG on every query. Inspired by Karpathy's LLM Wiki gist. Ships SKILL, 3 sub-agents, 5 slash commands, 8 Python tools (stdlib only), full vault templates, and cross-tool compatibility (Claude Code, Codex CLI, Cursor, Antigravity, OpenCode, Gemini CLI).", + "version": "2.3.0", + "author": { + "name": "Alireza Rezvani", + "url": "https://alirezarezvani.com" + }, + "homepage": "https://github.com/alirezarezvani/claude-skills/tree/main/engineering/llm-wiki", + "repository": "https://github.com/alirezarezvani/claude-skills", + "license": "MIT", + "skills": "./" +} diff --git a/engineering/llm-wiki/README.md b/engineering/llm-wiki/README.md new file mode 100644 index 000000000..38dfb9f2c --- /dev/null +++ b/engineering/llm-wiki/README.md @@ -0,0 +1,114 @@ +# llm-wiki + +> **A second brain for Claude Code + Obsidian.** +> Inspired by [Andrej Karpathy's LLM Wiki gist](https://gist.github.com/karpathy/442a6bf555914893e9891c11519de94f). + +Turn any LLM CLI into a disciplined wiki maintainer. You curate sources and ask questions. The LLM reads, files, cross-references, flags contradictions, and keeps a living synthesis current. Knowledge **compounds** instead of being re-derived by RAG on every query. + +## The idea in one paragraph + +Most LLM+docs workflows are RAG: retrieve fragments at query time, synthesize from scratch, forget. The wiki is **compounding**. The LLM reads each source once and integrates it into a persistent, interlinked Obsidian vault — updating entity pages, revising concept pages, flagging contradictions, and strengthening the synthesis. The wiki is the compiled artifact; RAG is the just-in-time retrieval. This plugin gives the LLM the discipline (SKILL.md), the delegation (sub-agents), the triggers (slash commands), and the bookkeeping (Python tools) to do the job. + +## What's in the box + +| Piece | What it does | +|---|---| +| **SKILL.md** | Master skill doc — architecture, workflows, iron rules, cross-tool compat. Has `context: fork` so other skills can chain into it. | +| **3 sub-agents** | `wiki-ingestor`, `wiki-librarian`, `wiki-linter` | +| **5 slash commands** | `/wiki-init`, `/wiki-ingest`, `/wiki-query`, `/wiki-lint`, `/wiki-log` | +| **8 Python tools** | Standard library only: `init_vault`, `ingest_source`, `update_index`, `append_log`, `wiki_search` (BM25), `lint_wiki`, `graph_analyzer`, `export_marp` | +| **8 reference docs** | Schema, page formats, ingest/query/lint workflows, Obsidian setup, cross-tool setup, Memex principles | +| **Vault templates** | `CLAUDE.md`, `AGENTS.md`, `.cursorrules`, `index.md`, `log.md`, plus 5 page templates (entity, concept, source, comparison, synthesis) | +| **Example vault** | A small worked example on "LLM interpretability" | + +## Quick start + +```bash +# 1. Initialize a vault +python scripts/init_vault.py --path ~/vaults/research --topic "LLM interpretability" --tool all + +# 2. Open in Obsidian +open -a Obsidian ~/vaults/research + +# 3. Drop a source into raw/ and ingest +cp ~/Downloads/paper.pdf ~/vaults/research/raw/papers/ +cd ~/vaults/research +# in Claude Code: +> /wiki-ingest raw/papers/paper.pdf + +# 4. Ask questions +> /wiki-query "what does the paper say about sparse features?" + +# 5. Health check +> /wiki-lint +``` + +## Cross-tool compatibility + +The scripts are pure Python stdlib — they run anywhere. Only the **schema loader** changes per tool: + +| Tool | Loader file | +|---|---| +| Claude Code | `CLAUDE.md` | +| Codex CLI (OpenAI) | `AGENTS.md` | +| Cursor (modern) | `AGENTS.md` | +| Cursor (legacy) | `.cursorrules` | +| Antigravity (Google) | `AGENTS.md` | +| OpenCode / Pi | `AGENTS.md` | +| Gemini CLI | `AGENTS.md` | + +`init_vault.py --tool all` installs all three. You can run multiple CLIs against the same vault. + +See `references/cross-tool-setup.md` for per-tool instructions. + +## Architecture + +``` +<vault>/ +├── raw/ # IMMUTABLE sources (you own) +├── wiki/ # LLM-owned knowledge base +│ ├── index.md # content catalog +│ ├── log.md # append-only timeline +│ ├── entities/ # people, orgs, places, products +│ ├── concepts/ # ideas, theories, frameworks +│ ├── sources/ # one summary per ingested source +│ ├── comparisons/ # cross-source analyses +│ └── synthesis/ # high-level overviews and theses +├── CLAUDE.md # schema for Claude Code +├── AGENTS.md # schema for Codex/Cursor/Antigravity +└── .cursorrules # (optional) legacy Cursor +``` + +**Iron rule:** The LLM never edits `raw/`. All writes go to `wiki/`. + +## Three operations + +- **Ingest** — Read a source, discuss with user, write summary page, update 5-15 cross-referenced pages, update index, log it +- **Query** — Read index first, drill into 3-10 pages, synthesize answer with inline citations, offer to file back as a new page +- **Lint** — Mechanical + semantic health check; surface contradictions, orphans, stale claims, cross-reference gaps + +## Why not just RAG? + +| Plain RAG | LLM Wiki | +|---|---| +| Rediscover knowledge each query | Knowledge accumulates | +| Cross-references re-computed every time | Cross-references pre-written and maintained | +| Contradictions surface only if you ask | Contradictions flagged during ingest | +| Exploration disappears into chat history | Good answers re-filed as new pages | +| Scales by embeddings infrastructure | Scales by markdown + `index.md` + optional local search | + +The wiki and RAG aren't opposites — RAG can sit on top of the wiki once you outgrow index-first search. + +## Status + +**v1.0.0** — initial release. SKILL + 3 agents + 5 commands + 8 scripts + 8 references + full vault templates + example vault. + +## License + +MIT. + +## Related + +- [Karpathy's original gist](https://gist.github.com/karpathy/442a6bf555914893e9891c11519de94f) — the pattern this plugin implements +- Vannevar Bush, "As We May Think" (1945) — the Memex +- [qmd](https://github.com/tobi/qmd) — local hybrid search over markdown (pair with this when the wiki outgrows `index.md`) diff --git a/engineering/llm-wiki/SKILL.md b/engineering/llm-wiki/SKILL.md new file mode 100644 index 000000000..b1080ef2c --- /dev/null +++ b/engineering/llm-wiki/SKILL.md @@ -0,0 +1,178 @@ +--- +name: llm-wiki +description: Use when building or maintaining a persistent personal knowledge base (second brain) in Obsidian where an LLM incrementally ingests sources, updates entity/concept pages, maintains cross-references, and keeps a synthesis current. Triggers include "second brain", "Obsidian wiki", "personal knowledge management", "ingest this paper/article/book", "build a research wiki", "compound knowledge", "Memex", or whenever the user wants knowledge to accumulate across sessions instead of being re-derived by RAG on every query. +context: fork +version: 1.0.0 +author: claude-code-skills +license: MIT +tags: [knowledge-management, obsidian, second-brain, pkm, rag-alternative, wiki, karpathy, memex] +compatible_tools: [claude-code, codex-cli, cursor, antigravity, opencode, gemini-cli] +--- + +# LLM Wiki — Second Brain for Claude Code + Obsidian + +Inspired by Andrej Karpathy's LLM Wiki pattern ([gist](https://gist.github.com/karpathy/442a6bf555914893e9891c11519de94f)). This skill turns Claude Code (or any agent CLI) into a disciplined wiki maintainer that **incrementally builds and maintains** a persistent, interlinked Obsidian vault as you feed it sources. The knowledge compounds — cross-references, contradictions, and synthesis are already there when you query. + +## Core principle + +Most LLM+docs workflows are **RAG**: retrieve fragments at query time, synthesize from scratch, forget. The wiki is **compounding**: sources are read once, integrated into a persistent markdown knowledge base, and kept current. You curate and ask; the LLM reads, files, cross-references, and maintains. + +> Obsidian is the IDE. The LLM is the programmer. The wiki is the codebase. + +## When to use + +- **Personal**: track goals, health, psychology, journaling, self-improvement +- **Research**: deep dives over weeks on a topic — papers, articles, reports, evolving thesis +- **Book companion**: file chapters as you read; build a fan-wiki-style companion for characters, themes, plot threads +- **Business/team**: internal wiki fed by Slack, meeting notes, calls — LLM does maintenance nobody else wants to do +- **Competitive analysis, due diligence, trip planning, course notes, hobby deep-dives** + +**Do NOT use when:** you need one-shot Q&A over a fixed document (use RAG), you don't plan to add sources over time, or you don't want Obsidian in the loop. + +## Architecture (three layers) + +``` +vault/ +├── raw/ # Layer 1 — IMMUTABLE source of truth +│ ├── <source files> # Articles, papers, PDFs, images, data +│ └── assets/ # Downloaded images from clipped articles +├── wiki/ # Layer 2 — LLM-owned knowledge base +│ ├── index.md # Content catalog (LLM updates every ingest) +│ ├── log.md # Append-only timeline (## [YYYY-MM-DD] <op> | <title>) +│ ├── entities/ # Person/Org/Place pages +│ ├── concepts/ # Ideas, theories, frameworks +│ ├── sources/ # One summary page per ingested source +│ ├── comparisons/ # Cross-source analysis pages +│ └── synthesis/ # High-level syntheses, theses, overviews +├── CLAUDE.md # Schema + conventions (Claude Code) +└── AGENTS.md # Same content, for Codex/Cursor/Antigravity +``` + +- **Layer 1 (raw/)** — you own. LLM only reads; never writes. +- **Layer 2 (wiki/)** — LLM owns. It creates, updates, and cross-references pages. You read it. +- **Layer 3 (CLAUDE.md / AGENTS.md)** — the *schema*. Conventions, workflows, frontmatter rules. Co-evolved by you and the LLM. + +## Three core operations + +1. **Ingest** — LLM reads a source, discusses takeaways with you, writes a source summary, updates 10-15 relevant pages, updates index, appends to log. See `references/ingest-workflow.md`. +2. **Query** — LLM reads `index.md` first, drills into relevant pages, synthesizes with citations. Good answers get **filed back into the wiki** so explorations compound. See `references/query-workflow.md`. +3. **Lint** — Health check: contradictions, stale claims, orphan pages, missing cross-refs, concepts mentioned but lacking their own page, data gaps to fill with web search. See `references/lint-workflow.md`. + +## Quick start + +```bash +# 1. Initialize a vault (in Obsidian's vault directory) +python scripts/init_vault.py --path ~/vaults/research --topic "LLM interpretability" + +# 2. Drop a source into raw/, then ingest +/wiki-ingest ~/vaults/research/raw/anthropic-monosemanticity.pdf + +# 3. Ask questions (answers can be re-filed into the wiki) +/wiki-query "how does monosemanticity compare to mechanistic interpretability?" + +# 4. Periodic health check +/wiki-lint + +# 5. See the timeline +/wiki-log --last 10 +``` + +## Slash commands (this plugin ships) + +| Command | Purpose | +|---|---| +| `/wiki-init` | Bootstrap a fresh vault with schema files + starter structure | +| `/wiki-ingest <path>` | Read a source, discuss, update wiki, log it | +| `/wiki-query <question>` | Search wiki, synthesize answer, offer to file back | +| `/wiki-lint` | Run health check — contradictions, orphans, stale claims, gaps | +| `/wiki-log` | Show recent log entries (uses unix tools on `log.md`) | + +## Sub-agents (this plugin ships) + +| Agent | When dispatched | +|---|---| +| `wiki-ingestor` | Delegated ingest flow — reads source, proposes updates, applies after your approval | +| `wiki-linter` | Runs the health-check workflow independently, reports findings | +| `wiki-librarian` | Answers queries using index-first search, synthesizes with citations | + +## Python tools (`scripts/`) + +All tools are **standard library only** (no pip installs). Run with `python scripts/<tool>.py --help`. + +| Script | Purpose | +|---|---| +| `init_vault.py` | Create folder structure + seed CLAUDE.md, AGENTS.md, index.md, log.md | +| `ingest_source.py` | Helper: extract text/frontmatter from a source file, ready for LLM review | +| `update_index.py` | Regenerate `index.md` from wiki page frontmatter (category, date, source count) | +| `append_log.py` | Append a standardized log entry `## [YYYY-MM-DD] <op> \| <title>` | +| `wiki_search.py` | BM25 search over wiki pages (standalone fallback when index.md isn't enough) | +| `lint_wiki.py` | Find orphans (no inbound links), stale pages, missing cross-refs, broken links | +| `graph_analyzer.py` | Compute link graph stats — hubs, orphans, clusters, disconnected components | +| `export_marp.py` | Render a wiki page (or subtree) to a Marp slide deck | + +## Cross-tool compatibility + +The vault's **schema** lives in CLAUDE.md (Claude Code) or AGENTS.md (Codex/Cursor/Antigravity/OpenCode). The same content works in both. This plugin ships both templates. For per-tool setup instructions see `references/cross-tool-setup.md`. + +``` +CLAUDE.md → Claude Code +AGENTS.md → Codex CLI, Cursor, Antigravity, OpenCode, Gemini CLI +.cursorrules → legacy Cursor (pre-AGENTS.md) +``` + +The scripts are pure Python stdlib → run identically everywhere. Only the loader file changes per tool. + +## Obsidian setup (recommended) + +- **Obsidian Web Clipper** — browser extension; converts web articles to markdown and drops them in `raw/` +- **Download images locally** — Settings → Files and links → Attachment folder path = `raw/assets/`. Settings → Hotkeys → bind "Download attachments for current file" to `Ctrl+Shift+D` +- **Graph view** — see hubs/orphans; essential for spotting structural problems +- **Marp plugin** — Markdown-based slide decks directly from wiki pages +- **Dataview plugin** — dynamic tables/lists over page frontmatter (tags, dates, source counts) +- **Git** — the vault is a plain markdown repo; version it + +Full setup walkthrough: `references/obsidian-setup.md` + +## Why this works (vs plain RAG) + +| Plain RAG | LLM Wiki | +|---|---| +| Rediscover knowledge each query | Knowledge accumulates | +| Cross-references re-computed every time | Cross-references pre-written and maintained | +| Contradictions surface only if you ask | Contradictions flagged during ingest | +| Exploration disappears into chat history | Good answers re-filed as new pages | +| Scales by embeddings infrastructure | Scales by markdown + `index.md` + optional local search | + +At ~100 sources / hundreds of pages, `index.md` + filesystem search is enough. Past that, layer in a local search tool like [qmd](https://github.com/tobi/qmd) or use `scripts/wiki_search.py`. + +## Related skills (chains via `context: fork`) + +This skill is marked `context: fork` so other skills can chain into it: + +- **`para-memory-files`** — PARA-method memory; complementary as long-term personal memory that feeds sources into the wiki +- **`obsidian-vault`** (mattpocock) — lightweight Obsidian note helper; this skill is the maintained-wiki layer on top +- **`rag-design`** — when wiki outgrows ~500 pages, use rag-design to bolt on a retrieval layer +- **`mcp-design`** — expose the wiki as an MCP tool +- **`agent-communication`** — for multi-agent wiki maintenance (ingestor + linter + librarian) + +## Reference docs + +- `references/wiki-schema.md` — full vault layout, page frontmatter, naming conventions +- `references/page-formats.md` — entity, concept, source, comparison, synthesis templates +- `references/ingest-workflow.md` — the detailed ingest flow the wiki-ingestor agent follows +- `references/query-workflow.md` — query patterns, citation format, re-filing answers +- `references/lint-workflow.md` — health-check heuristics +- `references/obsidian-setup.md` — Obsidian plugins, hotkeys, vault config +- `references/cross-tool-setup.md` — per-tool setup (Codex, Cursor, Antigravity, etc.) +- `references/memex-principles.md` — Bush's Memex, why the LLM changes the maintenance math + +## Templates (`assets/`) + +- `CLAUDE.md.template`, `AGENTS.md.template`, `.cursorrules.template` — schema loaders per tool +- `index.md.template`, `log.md.template` — starter index and log +- `page-templates/` — entity, concept, source-summary, comparison, synthesis +- `example-vault/` — small worked example you can study or copy + +## Iron rule + +**The LLM never edits files in `raw/`.** Ever. Sources are immutable. All LLM writes go to `wiki/`. If you need to correct a source, do it in `raw/` yourself — then re-ingest. diff --git a/engineering/llm-wiki/agents/wiki-ingestor.md b/engineering/llm-wiki/agents/wiki-ingestor.md new file mode 100644 index 000000000..0df48677c --- /dev/null +++ b/engineering/llm-wiki/agents/wiki-ingestor.md @@ -0,0 +1,89 @@ +--- +name: wiki-ingestor +description: Dispatched sub-agent that ingests a new source into an LLM Wiki vault. Reads the source, proposes TL;DR and key claims, identifies which entity/concept/synthesis pages will be touched, flags contradictions with existing pages, and — after user confirmation — writes the source summary, updates cross-references across 5-15 pages, regenerates the index, and appends a standardized log entry. Spawn when the user says "ingest this", "add this paper/article/book to the wiki", or drops a file into raw/. +skills: engineering/llm-wiki +domain: engineering +model: opus +tools: [Read, Write, Edit, Bash, Grep, Glob] +context: fork +--- + +# wiki-ingestor + +## Role + +You are a disciplined wiki maintainer. A user has dropped a new source into the `raw/` layer of an LLM Wiki vault and asked you to ingest it. Your job is to read it, discuss it with the user, and integrate it into the `wiki/` layer — touching every relevant entity, concept, and synthesis page, flagging contradictions, updating the index, and appending to the log. + +You are spawned **per-ingest**, not as a long-running agent. You do one source at a time. + +## Inputs + +- Path to a source file (must be inside the vault's `raw/` layer) +- The current state of `wiki/` (especially `index.md`) +- The vault's `CLAUDE.md` or `AGENTS.md` schema + +## Workflow + +Follow `references/ingest-workflow.md` in the llm-wiki skill. Summary: + +### 1. Prep +Run `python <plugin>/scripts/ingest_source.py --vault . --source <path> --json` to get the brief (title guess, word count, preview, suggested summary path, whether a summary already exists). + +### 2. Read +Use the Read tool on the source file directly. For PDFs, use Read's PDF support. For images, use vision. + +### 3. Discuss (user in the loop) +Before writing anything, report to the user: +- Title, authors, date +- 2-3 sentence TL;DR +- Key claims (3-7 bullets) +- **Which existing wiki pages you plan to touch** (bulleted wikilinks) +- **Any contradictions** with existing pages +- Whether this is a fresh ingest or a **merge** (summary page exists) + +**Wait for the user to confirm or redirect before writing.** + +### 4. Write the source summary +Create `wiki/sources/<slug>.md` using the source-summary template from the llm-wiki skill. Required frontmatter: `title`, `category: source`, `summary`, `source_path`, `ingested`, `updated`. + +If the page exists (merge mode), append a new `## Re-ingest <date>` section at the bottom. + +### 5. Update every relevant page +For each entity and concept mentioned in the source: +- **If the page exists:** update "Key claims", "Appears in" / "Used in", increment `sources:`, set `updated:` to today +- **If not:** create a stub page from the appropriate template with at least the minimum (title, summary, one key fact, link back to this source) + +A typical ingest touches **5-15 pages**. Don't skimp — the wiki's value comes from cross-references. + +### 6. Flag contradictions +If this source contradicts an existing page, add a `> ⚠️ Contradiction:` callout to **both** pages, linking the disagreeing sources. + +### 7. Update synthesis pages +If the source meaningfully shifts a `synthesis/` page's thesis, revise the "Thesis" paragraph and append a dated entry under "How this synthesis has changed". + +### 8. Regenerate the index +Run `python <plugin>/scripts/update_index.py --vault .` OR edit `wiki/index.md` inline for small changes. + +### 9. Log the ingest +Run `python <plugin>/scripts/append_log.py --vault . --op ingest --title "<title>" --detail "<touched pages summary>"`. + +### 10. Report back +Give the user a bulleted list of every touched page as wikilinks, plus any contradictions flagged. + +## Rules + +- **`raw/` is immutable.** Never edit files there. Read only. +- **Every write goes to `wiki/`.** +- **Discuss before writing.** The user is in the loop. +- **Minimum 5 file touches per ingest.** (source summary + 2-4 cross-references + index + log) +- **Cite aggressively.** Every claim on an entity/concept page links to a source page. +- **Flag contradictions** on both sides. +- **Update `updated:` frontmatter** on every page you touch. + +## Red flags + +Stop and ask the user before proceeding if: +- The source is outside `raw/` +- The source appears to duplicate an existing source exactly +- Ingesting would require deleting existing wiki pages (only the user decides) +- You detect >5 contradictions in one ingest (likely a paradigm-shifting source — worth a conversation) diff --git a/engineering/llm-wiki/agents/wiki-librarian.md b/engineering/llm-wiki/agents/wiki-librarian.md new file mode 100644 index 000000000..1b300cd37 --- /dev/null +++ b/engineering/llm-wiki/agents/wiki-librarian.md @@ -0,0 +1,83 @@ +--- +name: wiki-librarian +description: Dispatched sub-agent that answers queries against an LLM Wiki vault. Reads index.md first, drills into 3-10 relevant pages across categories, synthesizes an answer with inline [[wikilink]] citations, and offers to file the answer back into the wiki as a new comparison or synthesis page. Spawn when the user asks a substantive question the wiki might answer, says "what does the wiki say about X", "compare A and B across my sources", or wants to explore a topic. +skills: engineering/llm-wiki +domain: engineering +model: sonnet +tools: [Read, Write, Edit, Bash, Grep, Glob] +context: fork +--- + +# wiki-librarian + +## Role + +You answer questions against an LLM Wiki vault. You prioritize reading over re-deriving — the wiki already contains pre-synthesized knowledge with cross-references and citations. Your job is to find the right pages, read them, and compose an answer that cites them properly. You also **file good answers back** into the wiki so explorations compound. + +You are spawned **per-query**, not as a long-running agent. + +## Inputs + +- The user's question +- The current state of `wiki/` (especially `index.md`) + +## Workflow + +Follow `references/query-workflow.md`. Summary: + +### 1. Read `index.md` first +The index is the catalog. Scan it and pick the 3-10 pages most likely to contain the answer. Pick across categories: +- `synthesis/` for the big picture +- `concepts/` for definitions +- `sources/` for evidence +- `entities/` for context +- `comparisons/` for explicit contrasts + +### 2. Read the picked pages in full +They're short and curated. The wiki has done the hard work. + +### 3. Follow wikilinks opportunistically +If a read page points to another clearly relevant page, follow it. Stop when you have enough. + +### 4. Fall back to search if needed +If the index doesn't surface the right pages, run: +```bash +python <plugin>/scripts/wiki_search.py --vault . --query "<terms>" --limit 5 +``` + +Flag this to the user — stale index means lint time. + +### 5. Synthesize the answer +Format: +- **Direct answer** — 1-3 sentences +- **Supporting detail** — organized thematically +- **Inline citations** — `[[sources/xxx]]` wikilinks throughout; every claim links to its source +- **Related pages** — 3-5 wikilinks at the end + +### 6. Offer to file the answer back +This is the compounding move. At the end of the answer, ask: + +> _Should I file this as a new page in the wiki? Suggested location: +> `wiki/comparisons/<slug>.md` — or I can append it to an existing page._ + +If yes: +- Pick the right category (most often `comparisons/` or `synthesis/`) +- Use the appropriate template (see llm-wiki skill's `references/page-formats.md`) +- Add frontmatter with `category`, `summary`, `sources` (count), `updated` +- Update `wiki/index.md` (inline or via script) +- Append to `log.md`: `python <plugin>/scripts/append_log.py --vault . --op create --title "<question>" --detail "filed query response to <path>"` + +## Rules + +- **Read the index first.** Do not grep the entire wiki on every query. +- **Every claim cites a page.** No uncited assertions. +- **If the wiki doesn't know, say so.** Suggest a source to ingest instead of inventing content. +- **Offer to file back** every substantive answer — but don't file trivial one-off answers. +- **Output format follows the question.** Comparison questions get tables. Overview questions get markdown pages. Data questions get charts (save to `wiki/assets/charts/`). + +## Red flags + +- Answering without reading the index → go back +- Citing only one source for a multi-source question → broaden +- Inventing concepts not in the wiki → stop and suggest ingestion +- Creating a new page for a trivial question → don't pollute the wiki diff --git a/engineering/llm-wiki/agents/wiki-linter.md b/engineering/llm-wiki/agents/wiki-linter.md new file mode 100644 index 000000000..938afa6a0 --- /dev/null +++ b/engineering/llm-wiki/agents/wiki-linter.md @@ -0,0 +1,104 @@ +--- +name: wiki-linter +description: Dispatched sub-agent that runs a periodic health check on an LLM Wiki vault. Runs mechanical checks via scripts (orphans, broken links, stale pages, missing frontmatter, duplicate titles, log gaps), does semantic checks (contradictions, stale claims, cross-reference gaps, concepts missing their own page), and produces a markdown report with suggested actions. Spawn weekly, after batch ingests, or when the user says "check the wiki" / "lint my wiki" / "audit the vault". +skills: engineering/llm-wiki +domain: engineering +model: opus +tools: [Read, Write, Edit, Bash, Grep, Glob] +context: fork +--- + +# wiki-linter + +## Role + +You are the wiki's auditor. You run periodic health checks and surface problems for the user to fix — contradictions, orphans, stale pages, missing cross-references, concepts lacking their own page. You do NOT silently auto-fix structural issues; you report and suggest. The user decides what to fix. + +You are spawned **per-lint-pass**, not as a long-running agent. + +## Workflow + +Follow `references/lint-workflow.md`. Three passes. + +### Pass 1 — Mechanical (scripts) + +Run both: + +```bash +python <plugin>/scripts/lint_wiki.py --vault . --json > /tmp/lint.json +python <plugin>/scripts/graph_analyzer.py --vault . --json > /tmp/graph.json +``` + +Parse the JSON. Capture: +- Orphans (zero inbound links) +- Broken links (wikilinks pointing to non-existent pages) +- Stale pages (`updated:` older than 90 days) +- Missing frontmatter (pages without title/category/summary) +- Duplicate titles +- Log gap (no entries in 14+ days) +- Connected components (more than 1 = disconnected islands) +- Hubs (high-fan-out or high-fan-in pages) +- Sinks (no outbound links) + +### Pass 2 — Semantic (you read and think) + +The scripts can't catch these. You must read. + +**A. Contradictions.** Scan pages whose `updated:` is recent. For each, check whether it contradicts any related page. If so, add a `> ⚠️ Contradiction:` callout to both. + +**B. Stale claims.** For each flagged stale page, ask: has a newer source invalidated a claim? Suggest re-ingest or a new source hunt. + +**C. Concepts mentioned without their own page.** Grep for concept-shaped nouns that appear across 3+ pages as plain text (not wikilinks). Suggest new concept pages. + +**D. Cross-reference gaps.** For each recently-touched page, check if every entity/concept mentioned is a wikilink. Promote plain-text mentions to wikilinks where appropriate. + +**E. Index drift.** Compare `index.md` against actual wiki contents. If out of sync, suggest regeneration. + +### Pass 3 — Report + +Produce a markdown report: + +```markdown +# Wiki lint — <date> + +**Total pages:** N **Components:** N **Last log:** <date> + +## Found +- ⚠️ <N> contradictions (list with wikilinks) +- <N> orphan pages +- <N> broken links +- <N> stale pages +- <N> concepts mentioned across 3+ pages without their own page +- <N> pages with missing frontmatter +- <other findings> + +## Suggested actions +1. Investigate contradiction between [[sources/a]] and [[sources/b]] +2. Create concept page for "<name>" (mentioned in N sources) +3. Re-ingest [[sources/c]] — stale + contradicted by newer sources +4. Fix broken link in [[concepts/x]] +5. Cross-reference the N orphans (most belong under [[synthesis/overview]]) + +Want me to run these in order, or pick specific ones? +``` + +Then append a log entry: + +```bash +python <plugin>/scripts/append_log.py --vault . --op lint --title "<date> health check" --detail "<findings summary>" +``` + +## Rules + +- **Report, don't silently fix.** The user decides what to change. +- **Prioritize by impact.** Contradictions > broken links > orphans > stale > style issues. +- **Use both scripts.** Mechanical + graph both reveal different problems. +- **Suggest actions** — never just dump findings without recommendations. +- **Always log the pass.** The log tracks wiki health over time. + +## Red flags + +- Auto-fixing structural issues without asking → stop +- Skipping semantic pass because "the scripts look clean" → do the read-and-think pass anyway +- Reporting without suggestions → add suggestions +- Not updating `log.md` → always log diff --git a/engineering/llm-wiki/assets/AGENTS.md.template b/engineering/llm-wiki/assets/AGENTS.md.template new file mode 100644 index 000000000..e1858914e --- /dev/null +++ b/engineering/llm-wiki/assets/AGENTS.md.template @@ -0,0 +1,140 @@ +# {{VAULT_NAME}} — LLM Wiki + +> **Topic:** {{TOPIC}} +> **Initialized:** {{DATE}} +> **Tool:** Any AGENTS.md-aware CLI (Codex, Cursor, Antigravity, OpenCode, Gemini CLI, etc.). Claude Code uses `CLAUDE.md`, which is identical. + +You are the maintainer of this wiki. You read from `raw/`, you write to `wiki/`. You never edit `raw/`. + +## The three layers + +``` +raw/ → sources (articles, papers, notes). IMMUTABLE. You only read. +wiki/ → the knowledge base. You own this. Create, update, cross-reference. +AGENTS.md → schema (this file). Co-evolved with the user. +``` + +## Vault structure + +``` +raw/ +├── <sources> # articles, papers, notes — IMMUTABLE +└── assets/ # downloaded images from clipped articles + +wiki/ +├── index.md # content catalog — update every ingest +├── log.md # append-only timeline +├── entities/ # people, orgs, places, products +├── concepts/ # ideas, theories, frameworks +├── sources/ # one summary page per ingested source +├── comparisons/ # cross-source analysis +├── synthesis/ # high-level overviews and theses +└── .templates/ # page templates (reference only) +``` + +## Page frontmatter (required on every wiki page) + +```yaml +--- +title: <Title> +category: entity | concept | source | comparison | synthesis +summary: <one-line summary> +tags: [tag1, tag2] +sources: <count of sources referencing this page> +updated: YYYY-MM-DD +--- +``` + +For `source` pages, also include: +```yaml +source_path: raw/<path> +source_date: YYYY-MM +authors: [author1, author2] +ingested: YYYY-MM-DD +``` + +## The three operations + +### Ingest + +When the user says "ingest this source" or points you at a file in `raw/`: + +1. Read the source directly with your file-reading tool +2. **Discuss with the user first** — TL;DR, key claims, pages you'll touch, contradictions +3. Wait for confirmation +4. Create/merge the summary page at `wiki/sources/<slug>.md` +5. Update every relevant entity and concept page (typically 5-15 pages) +6. Flag contradictions with `> ⚠️ Contradiction:` callouts on both sides +7. Update `wiki/index.md` +8. Append a log entry: `## [YYYY-MM-DD] ingest | <title>` with touched pages in the body +9. Report back with a bulleted list of touched pages + +If Python is available, use the helpers: +```bash +python <plugin-path>/scripts/ingest_source.py --vault . --source <path> --json +python <plugin-path>/scripts/append_log.py --vault . --op ingest --title "<title>" +python <plugin-path>/scripts/update_index.py --vault . +``` + +### Query + +When the user asks a question: + +1. Read `wiki/index.md` first +2. Pick 3-10 relevant pages across categories +3. Read them in full +4. Follow wikilinks opportunistically +5. Fall back to `wiki_search.py --query <terms>` if needed +6. Synthesize: direct answer → supporting detail → inline `[[sources/xxx]]` citations → "Related pages" +7. **Offer to file the answer back** as a new page + +### Lint + +When the user says "check the wiki" or periodically: + +1. Run `lint_wiki.py` + `graph_analyzer.py` +2. Do semantic checks (contradictions, stale claims, concept gaps) +3. Present a report with suggested actions +4. Append a `lint` entry to `log.md` + +## Iron rules + +1. **`raw/` is immutable.** You read from it; you never write to it. +2. **All writes go to `wiki/`.** No exceptions. +3. **Every wiki page has YAML frontmatter** with `title`, `category`, `summary`, `updated`. +4. **Every ingest touches ≥5 files.** +5. **Every claim has a citation.** +6. **Contradictions get flagged inline.** Both pages. +7. **Good answers get filed back.** Explorations compound. + +## Log format + +``` +## [YYYY-MM-DD] <op> | <title> +<optional detail> +``` + +Ops: `ingest`, `query`, `lint`, `create`, `update`, `delete`, `note`. + +## Tools + +Python scripts live wherever you installed the plugin. Standard library only. + +- `init_vault.py` +- `ingest_source.py` +- `update_index.py` +- `append_log.py` +- `wiki_search.py` +- `lint_wiki.py` +- `graph_analyzer.py` +- `export_marp.py` + +Run any of them with `--help`. + +## Style + +- Concise. Wiki pages are read, not generated. +- Short paragraphs. Bulleted lists where appropriate. +- Cite aggressively with `[[wikilinks]]`. +- Say "I don't know" when you don't. Don't invent content. +- Update `updated:` whenever you touch a page. diff --git a/engineering/llm-wiki/assets/CLAUDE.md.template b/engineering/llm-wiki/assets/CLAUDE.md.template new file mode 100644 index 000000000..aff2ee898 --- /dev/null +++ b/engineering/llm-wiki/assets/CLAUDE.md.template @@ -0,0 +1,133 @@ +# {{VAULT_NAME}} — LLM Wiki + +> **Topic:** {{TOPIC}} +> **Initialized:** {{DATE}} +> **Tool:** Claude Code (this file). A parallel `AGENTS.md` exists for Codex/Cursor/Antigravity. + +You are the maintainer of this wiki. You read from `raw/`, you write to `wiki/`. You never edit `raw/`. + +## The three layers + +``` +raw/ → sources (articles, papers, notes). IMMUTABLE. You only read. +wiki/ → the knowledge base. You own this. Create, update, cross-reference. +CLAUDE.md / AGENTS.md → schema (this file). Co-evolved with the user. +``` + +## Vault structure + +``` +raw/ +├── <sources> # articles, papers, notes — IMMUTABLE +└── assets/ # downloaded images from clipped articles + +wiki/ +├── index.md # content catalog — update every ingest +├── log.md # append-only timeline +├── entities/ # people, orgs, places, products +├── concepts/ # ideas, theories, frameworks +├── sources/ # one summary page per ingested source +├── comparisons/ # cross-source analysis +├── synthesis/ # high-level overviews and theses +└── .templates/ # page templates (reference only) +``` + +## Page frontmatter (required on every wiki page) + +```yaml +--- +title: <Title> +category: entity | concept | source | comparison | synthesis +summary: <one-line summary> +tags: [tag1, tag2] +sources: <count of sources referencing this page> +updated: YYYY-MM-DD +--- +``` + +For `source` pages, also include: +```yaml +source_path: raw/<path> +source_date: YYYY-MM (original publication) +authors: [author1, author2] +ingested: YYYY-MM-DD +``` + +## The three operations + +### Ingest (`/wiki-ingest <path>`) + +1. Run `python scripts/ingest_source.py --vault . --source <path> --json` to get the brief +2. Read the source directly +3. **Discuss with the user first** — TL;DR, key claims, which pages will be touched, contradictions +4. Wait for confirmation +5. Create or merge the summary page at `wiki/sources/<slug>.md` +6. Update every relevant entity and concept page (typically 5-15 pages) +7. Flag contradictions with `> ⚠️ Contradiction:` callouts on both sides +8. Update `wiki/index.md` (run `update_index.py` or edit inline) +9. Run `append_log.py --op ingest --title "<title>" --detail "<touched pages>"` +10. Report back with a bulleted list of touched pages + +### Query (`/wiki-query <question>`) + +1. Read `wiki/index.md` first +2. Pick 3-10 relevant pages across categories (synthesis + concepts + sources + entities) +3. Read them in full +4. Follow wikilinks opportunistically +5. Fall back to `wiki_search.py --query <terms>` if the index doesn't surface the answer +6. Synthesize: direct answer (1-3 sentences) → supporting detail → inline `[[sources/xxx]]` citations → "Related pages" section +7. **Offer to file the answer back** as a new page in `comparisons/` or `synthesis/` + +### Lint (`/wiki-lint`) + +1. Run `python scripts/lint_wiki.py --vault .` for mechanical checks +2. Run `python scripts/graph_analyzer.py --vault .` for structural stats +3. Semantic checks: look for contradictions, stale claims, concepts mentioned without their own page, cross-reference gaps +4. Present findings as a markdown report with suggested actions +5. Append a `lint` entry to `log.md` + +## Iron rules + +1. **`raw/` is immutable.** You read from it; you never write to it. +2. **All writes go to `wiki/`.** No exceptions. +3. **Every wiki page has YAML frontmatter** with `title`, `category`, `summary`, `updated`. +4. **Every ingest touches ≥5 files.** The source summary, 2-4 entity/concept pages, `index.md`, `log.md`. +5. **Every claim has a citation.** Link back to the `sources/<slug>` page. +6. **Contradictions get flagged inline.** Both pages get the callout. +7. **Good answers get filed back.** Explorations compound. + +## Log format + +``` +## [YYYY-MM-DD] <op> | <title> +<optional detail — which pages touched, what changed> +``` + +Valid ops: `ingest`, `query`, `lint`, `create`, `update`, `delete`, `note`. + +Grep the log: `grep "^## \[" wiki/log.md | tail -10` + +## Tools + +All scripts live at `~/.claude/skills/llm-wiki/scripts/` (or wherever you installed the plugin). Standard library only. + +- `init_vault.py` — bootstrap a vault +- `ingest_source.py` — prep a source for ingest (metadata + preview) +- `update_index.py` — regenerate `wiki/index.md` from page frontmatter +- `append_log.py` — append a standardized log entry +- `wiki_search.py` — BM25 search fallback +- `lint_wiki.py` — mechanical health check +- `graph_analyzer.py` — link graph stats +- `export_marp.py` — render a page as a Marp slide deck + +## Obsidian + +The user opens this vault in Obsidian. They watch the graph view while you edit. Useful plugins: Graph view, Backlinks, Dataview, Marp, Templates, Git. + +## Style + +- Be concise. Wiki pages are read, not generated. +- Prefer short paragraphs. Bulleted lists where appropriate. +- Cite aggressively with `[[wikilinks]]`. +- When you're not sure, say so in the page. Don't invent content. +- Update `updated:` frontmatter whenever you touch a page. diff --git a/engineering/llm-wiki/assets/cursorrules.template b/engineering/llm-wiki/assets/cursorrules.template new file mode 100644 index 000000000..887231472 --- /dev/null +++ b/engineering/llm-wiki/assets/cursorrules.template @@ -0,0 +1,30 @@ +# Cursor rules for {{VAULT_NAME}} LLM Wiki + +You are the maintainer of this wiki. You read from `raw/` and write only to `wiki/`. +You never edit files in `raw/`. Full schema is in `AGENTS.md` — read it first. + +Topic: {{TOPIC}} +Initialized: {{DATE}} + +Core rules: +1. `raw/` is immutable. Read only. +2. All writes go to `wiki/`. +3. Every wiki page has YAML frontmatter with: title, category, summary, updated. +4. Every ingest touches at least 5 files: source summary + 2-4 entity/concept pages + index.md + log.md. +5. Every claim has a wikilink citation to its source page. +6. Contradictions get `> ⚠️ Contradiction:` callouts on both sides. +7. Good query answers get filed back into the wiki as new pages. + +Operations: ingest / query / lint. Full workflows in AGENTS.md. + +Log format: `## [YYYY-MM-DD] <op> | <title>` +Valid ops: ingest, query, lint, create, update, delete, note. + +Scripts (Python stdlib) in `<plugin-path>/scripts/`: +- ingest_source.py, update_index.py, append_log.py +- lint_wiki.py, graph_analyzer.py, wiki_search.py, export_marp.py + +Run any with `--help`. Use them when helpful — they're fast and deterministic. + +Style: concise. Cite with `[[wikilinks]]`. Say "I don't know" when you don't. +Update `updated:` frontmatter on every touch. diff --git a/engineering/llm-wiki/assets/example-vault/README.md b/engineering/llm-wiki/assets/example-vault/README.md new file mode 100644 index 000000000..bd831ec6b --- /dev/null +++ b/engineering/llm-wiki/assets/example-vault/README.md @@ -0,0 +1,35 @@ +# Example Vault — "LLM Interpretability" + +A minimal worked example to study before initializing your own. + +**Not** a runnable vault — it's missing most files. The goal is to show what a healthy small vault looks like after ingesting 2-3 sources on one topic. + +## Layout + +``` +example-vault/ +├── raw/ +│ └── assets/ +├── wiki/ +│ ├── index.md +│ ├── log.md +│ ├── entities/ +│ │ └── anthropic.md +│ ├── concepts/ +│ │ └── sparse-autoencoder.md +│ ├── sources/ +│ │ └── monosemanticity.md +│ └── synthesis/ +│ └── interpretability-overview.md +├── CLAUDE.md +└── AGENTS.md +``` + +## What to notice + +1. **Every page has frontmatter.** This is what makes the index + lint scripts work. +2. **The source page is the single source of truth** for claims from that paper. Other pages cite it rather than duplicating content. +3. **`index.md` is organized by category**, not chronologically. +4. **`log.md` uses the standardized header format** `## [YYYY-MM-DD] <op> | <title>`. +5. **Cross-references are wikilinks**, not prose references. `[[sources/monosemanticity]]`, not "see the Monosemanticity paper". +6. **The synthesis page has a `How this synthesis has changed` section.** Append-only history so you can see the thesis evolve. diff --git a/engineering/llm-wiki/assets/example-vault/wiki/concepts/sparse-autoencoder.md b/engineering/llm-wiki/assets/example-vault/wiki/concepts/sparse-autoencoder.md new file mode 100644 index 000000000..d8d924ec2 --- /dev/null +++ b/engineering/llm-wiki/assets/example-vault/wiki/concepts/sparse-autoencoder.md @@ -0,0 +1,33 @@ +--- +title: Sparse Autoencoder +category: concept +summary: Dictionary-learning method for decomposing polysemantic neurons into monosemantic features +tags: [interpretability, sparse-autoencoders, dictionary-learning] +sources: 1 +updated: 2026-04-10 +--- + +# Sparse Autoencoder + +## Definition +A neural-network-based dictionary-learning method that decomposes the activations of a target model's layer into a larger set of sparsely-active features, each of which is hoped to be monosemantic (interpretable as a single concept). + +## Origin +Introduced to LLM interpretability by [[entities/anthropic]] in the Transformer Circuits thread. The specific form used in [[sources/monosemanticity]] is a wide, sparse autoencoder trained on the residual stream activations of a one-layer transformer. + +## Key claims +- SAEs extract features that are *more* monosemantic than raw neurons — cited from [[sources/monosemanticity]] +- The resulting feature dictionary is larger than the original layer width (overcomplete) +- Features come in interpretable families (specific tokens, contexts, circuits) + +## Contrasts with +- **Linear probing** — supervised; requires you to know what feature to look for +- **Direct neuron inspection** — limited by polysemanticity + +## Open questions +- Does it scale beyond one-layer models? +- Are the features truly monosemantic or just more monosemantic? + +## Used in +- [[synthesis/interpretability-overview]] +- [[sources/monosemanticity]] diff --git a/engineering/llm-wiki/assets/example-vault/wiki/entities/anthropic.md b/engineering/llm-wiki/assets/example-vault/wiki/entities/anthropic.md new file mode 100644 index 000000000..4e9c4dd4e --- /dev/null +++ b/engineering/llm-wiki/assets/example-vault/wiki/entities/anthropic.md @@ -0,0 +1,31 @@ +--- +title: Anthropic +category: entity +summary: AI safety company, developer of Claude; major contributor to interpretability research +tags: [company, ai-safety, interpretability] +sources: 1 +updated: 2026-04-10 +--- + +# Anthropic + +## What it is +AI safety company founded in 2021 by former OpenAI researchers. Builds the Claude family of large language models and publishes research on AI safety, alignment, and interpretability. + +## Why it matters +Primary source of modern mechanistic interpretability work, including the sparse-autoencoder line of research that this wiki is tracking. + +## Key facts +- Founded 2021 — cited from [[sources/monosemanticity]] +- Publishes the Transformer Circuits thread (interpretability research) +- Runs the [[concepts/sparse-autoencoder]] line of work + +## Related +- [[concepts/sparse-autoencoder]] +- [[synthesis/interpretability-overview]] + +## Appears in +- [[sources/monosemanticity]] — primary SAE paper + +## Open questions +- Has the SAE approach scaled beyond one-layer models as of late 2024? diff --git a/engineering/llm-wiki/assets/example-vault/wiki/index.md b/engineering/llm-wiki/assets/example-vault/wiki/index.md new file mode 100644 index 000000000..51bfd1577 --- /dev/null +++ b/engineering/llm-wiki/assets/example-vault/wiki/index.md @@ -0,0 +1,18 @@ +# Index — example-vault + +_Updated 2026-04-10 • 4 pages_ + +> Content catalog. Read this first when answering queries. +> Topic: **LLM Interpretability** + +## Synthesis (1) +- [[synthesis/interpretability-overview|Interpretability Overview]] — current thesis on mechanistic interpretability in LLMs _(1 source · upd 2026-04-10)_ + +## Concept (1) +- [[concepts/sparse-autoencoder|Sparse Autoencoder]] — dictionary-learning method for decomposing polysemantic neurons into monosemantic features _(1 source · upd 2026-04-10)_ + +## Entity (1) +- [[entities/anthropic|Anthropic]] — AI safety company, developer of Claude; major contributor to interpretability research _(1 source · upd 2026-04-10)_ + +## Source (1) +- [[sources/monosemanticity|Towards Monosemanticity]] — Anthropic 2024 paper using sparse autoencoders to extract interpretable features from a one-layer transformer _(upd 2026-04-10)_ diff --git a/engineering/llm-wiki/assets/example-vault/wiki/log.md b/engineering/llm-wiki/assets/example-vault/wiki/log.md new file mode 100644 index 000000000..4dabbd403 --- /dev/null +++ b/engineering/llm-wiki/assets/example-vault/wiki/log.md @@ -0,0 +1,11 @@ +# Log — example-vault + +> Append-only timeline. Grep recent: `grep "^## \[" log.md | tail -10` + +## [2026-04-10] note | Vault initialized +Topic: **LLM Interpretability**. Layers created. + +## [2026-04-10] ingest | Towards Monosemanticity +Added sources/monosemanticity.md. Created entities/anthropic.md, +concepts/sparse-autoencoder.md. Started synthesis/interpretability-overview.md. +No contradictions (first source). diff --git a/engineering/llm-wiki/assets/example-vault/wiki/sources/monosemanticity.md b/engineering/llm-wiki/assets/example-vault/wiki/sources/monosemanticity.md new file mode 100644 index 000000000..69f929821 --- /dev/null +++ b/engineering/llm-wiki/assets/example-vault/wiki/sources/monosemanticity.md @@ -0,0 +1,41 @@ +--- +title: "Towards Monosemanticity: Decomposing Language Models With Dictionary Learning" +category: source +summary: Anthropic 2023 paper using sparse autoencoders to extract interpretable features from a one-layer transformer +tags: [interpretability, sparse-autoencoders, anthropic] +source_path: raw/papers/monosemanticity.pdf +source_date: 2023-10 +authors: [Bricken et al.] +ingested: 2026-04-10 +updated: 2026-04-10 +--- + +# Towards Monosemanticity + +## TL;DR +Trains a wide sparse autoencoder on the residual stream of a one-layer transformer and finds that the resulting features are substantially more interpretable than the model's native neurons. + +## Key claims +1. SAE features are more monosemantic than neurons +2. Features come in interpretable families (tokens, contexts, syntactic roles) +3. The approach is complementary to, not a replacement for, mechanistic circuits work + +## Methods +- One-layer transformer target +- Wide sparse autoencoder on residual stream +- L1 sparsity regularization +- Feature dictionary size >> model width + +## Evidence cited +- Qualitative inspection of top activating examples per feature +- Comparison with probing +- Feature family analysis + +## Connections +- Extends [[concepts/sparse-autoencoder]] +- Builds on [[entities/anthropic]]'s prior Transformer Circuits work + +## Where it's cited +- [[concepts/sparse-autoencoder]] +- [[entities/anthropic]] +- [[synthesis/interpretability-overview]] diff --git a/engineering/llm-wiki/assets/example-vault/wiki/synthesis/interpretability-overview.md b/engineering/llm-wiki/assets/example-vault/wiki/synthesis/interpretability-overview.md new file mode 100644 index 000000000..7221022e2 --- /dev/null +++ b/engineering/llm-wiki/assets/example-vault/wiki/synthesis/interpretability-overview.md @@ -0,0 +1,32 @@ +--- +title: Interpretability Overview +category: synthesis +summary: Current synthesis on mechanistic interpretability in large language models +tags: [interpretability, overview] +sources: 1 +updated: 2026-04-10 +--- + +# Interpretability Overview + +## Thesis +_(early — only one source ingested)_ Mechanistic interpretability is shifting from direct neuron inspection (limited by polysemanticity) toward sparse-autoencoder-based feature decomposition. The empirical bet is that overcomplete sparse dictionaries recover the "true" feature basis of trained models. + +## The landscape +- **Sparse-autoencoder line** — pursued by [[entities/anthropic]]; see [[sources/monosemanticity]] +- **Circuits work** — (no sources ingested yet) +- **Probing** — (no sources ingested yet) + +## Key concepts +- [[concepts/sparse-autoencoder]] — primary method under investigation + +## Key sources +- [[sources/monosemanticity]] — foundational SAE paper + +## Current open problems +- Scaling SAEs beyond toy models +- Whether features are truly monosemantic vs merely "more" monosemantic +- How SAE features relate to circuits-based analysis + +## How this synthesis has changed +- **2026-04-10** — initial synthesis after ingesting [[sources/monosemanticity]]. Only one data point; thesis is intentionally tentative. diff --git a/engineering/llm-wiki/assets/index.md.template b/engineering/llm-wiki/assets/index.md.template new file mode 100644 index 000000000..4cbad75cf --- /dev/null +++ b/engineering/llm-wiki/assets/index.md.template @@ -0,0 +1,32 @@ +# Index — {{VAULT_NAME}} + +_Initialized {{DATE}} • 0 pages_ + +> Content-oriented catalog of every page in `wiki/`. Updated by +> `scripts/update_index.py` or during `/wiki-ingest`. Answer queries +> by reading this file first, then drilling into relevant pages. +> +> Topic: **{{TOPIC}}** + +## Synthesis (0) +_No pages yet. Will appear here as you ingest sources and build high-level theses._ + +## Concept (0) +_No pages yet. Will populate with ideas, theories, methods as sources are ingested._ + +## Entity (0) +_No pages yet. People, organizations, places, products mentioned in your sources will show up here._ + +## Source (0) +_No pages yet. Each ingested source gets one summary page here._ + +## Comparison (0) +_No pages yet. Cross-source or cross-concept analyses will live here._ + +--- + +### First steps + +1. Drop a source into `raw/` +2. Run `/wiki-ingest raw/<your-file>` in your LLM CLI +3. Watch this index populate diff --git a/engineering/llm-wiki/assets/log.md.template b/engineering/llm-wiki/assets/log.md.template new file mode 100644 index 000000000..7d6a1d2c1 --- /dev/null +++ b/engineering/llm-wiki/assets/log.md.template @@ -0,0 +1,12 @@ +# Log — {{VAULT_NAME}} + +> Append-only timeline. Every LLM operation leaves an entry here. +> +> Format: `## [YYYY-MM-DD] <op> | <title>` followed by an optional detail line. +> Valid ops: `ingest`, `query`, `lint`, `create`, `update`, `delete`, `note`. +> +> Grep the last 10 entries: `grep "^## \[" log.md | tail -10` + +## [{{DATE}}] note | Vault initialized +Topic: **{{TOPIC}}**. Layers created: `raw/`, `wiki/{entities,concepts,sources,comparisons,synthesis}`. +Schema loader: `CLAUDE.md` + `AGENTS.md` + `.cursorrules`. diff --git a/engineering/llm-wiki/assets/page-templates/comparison.md b/engineering/llm-wiki/assets/page-templates/comparison.md new file mode 100644 index 000000000..345136fc6 --- /dev/null +++ b/engineering/llm-wiki/assets/page-templates/comparison.md @@ -0,0 +1,39 @@ +--- +title: "<A> vs <B>" +category: comparison +summary: <one-line summary — what this comparison is about> +tags: [comparison] +sources: 0 +updated: <YYYY-MM-DD> +--- + +# <A> vs <B> + +## What they share +Common ground. Same problem space? Same goals? + +## Where they diverge +| Dimension | <A> | <B> | +|---|---|---| +| Dimension 1 | ... | ... | +| Dimension 2 | ... | ... | +| Dimension 3 | ... | ... | + +## Which sources take which side +- [[sources/xxx]] — pro-<A> +- [[sources/yyy]] — pro-<B> +- [[sources/zzz]] — neutral / both + +## When to prefer <A> +- Context where A wins + +## When to prefer <B> +- Context where B wins + +## Open questions +- Unresolved disagreements +- Data that would settle the question + +## Related +- [[concepts/a]] · [[concepts/b]] +- [[synthesis/xxx]] diff --git a/engineering/llm-wiki/assets/page-templates/concept.md b/engineering/llm-wiki/assets/page-templates/concept.md new file mode 100644 index 000000000..2423ec917 --- /dev/null +++ b/engineering/llm-wiki/assets/page-templates/concept.md @@ -0,0 +1,31 @@ +--- +title: <Concept Name> +category: concept +summary: <one-line definition> +tags: [] +sources: 0 +updated: <YYYY-MM-DD> +--- + +# <Concept Name> + +## Definition +Precise, one-paragraph definition. The canonical form used across your sources. + +## Origin +Who proposed it, when, in what work. Link [[entities]] and [[sources]]. + +## Key claims +- Claim 1 — cited from [[sources/xxx]] +- Claim 2 — cited from [[sources/yyy]] + +## Contrasts with +- [[concepts/other]] — see [[comparisons/xxx-vs-other]] + +## Open questions / disagreements +- Unresolved questions across sources +- ⚠️ Contradiction: [[sources/a]] claims X but [[sources/b]] claims ~X + +## Used in +- [[synthesis/xxx]] +- [[sources/yyy]] diff --git a/engineering/llm-wiki/assets/page-templates/entity.md b/engineering/llm-wiki/assets/page-templates/entity.md new file mode 100644 index 000000000..702f8c8cb --- /dev/null +++ b/engineering/llm-wiki/assets/page-templates/entity.md @@ -0,0 +1,31 @@ +--- +title: <Entity Name> +category: entity +summary: <one-line summary — what this entity is and why it matters> +tags: [] +sources: 0 +updated: <YYYY-MM-DD> +--- + +# <Entity Name> + +## What it is +One-paragraph definition. What kind of entity (person, org, place, product), founded/born when, by whom, active in what. + +## Why it matters +Why this entity shows up across sources. What role does it play in the narrative of this wiki? + +## Key facts +- Fact 1 — cited from [[sources/xxx]] +- Fact 2 — cited from [[sources/yyy]] + +## Related +- Related [[entities/other]] +- Related [[concepts/xxx]] + +## Appears in +- [[sources/xxx]] — short note on the connection +- [[sources/yyy]] — short note + +## Open questions +- Things the sources don't answer. Good prompts for new source hunts. diff --git a/engineering/llm-wiki/assets/page-templates/source-summary.md b/engineering/llm-wiki/assets/page-templates/source-summary.md new file mode 100644 index 000000000..81e92e79b --- /dev/null +++ b/engineering/llm-wiki/assets/page-templates/source-summary.md @@ -0,0 +1,41 @@ +--- +title: "<Source Title>" +category: source +summary: <one-line summary> +tags: [] +source_path: raw/<path-to-source> +source_date: <YYYY-MM> +authors: [<author1>, <author2>] +ingested: <YYYY-MM-DD> +updated: <YYYY-MM-DD> +--- + +# <Source Title> + +## TL;DR +Two sentences max. What did they do, what did they find / argue. + +## Key claims +1. Claim with page/section pointer if applicable +2. ... + +## Methods (if applicable) +How the work was done. Data, model, training, evaluation. For non-research sources, describe the approach/argument structure. + +## Evidence cited +- Figure X shows ... +- Table Y ... +- Quote: "..." (p. NN) + +## Surprises / contradictions +- Where this source conflicts with [[sources/other]] or [[concepts/xxx]] + +## Connections +- Extends [[concepts/xxx]] +- Builds on [[entities/yyy]]'s prior work +- Related: [[sources/zzz]] + +## Where it's cited in this wiki +- [[concepts/xxx]] +- [[entities/yyy]] +- [[synthesis/zzz]] diff --git a/engineering/llm-wiki/assets/page-templates/synthesis.md b/engineering/llm-wiki/assets/page-templates/synthesis.md new file mode 100644 index 000000000..a6516946c --- /dev/null +++ b/engineering/llm-wiki/assets/page-templates/synthesis.md @@ -0,0 +1,38 @@ +--- +title: <Topic> Overview +category: synthesis +summary: <current thesis in one line> +tags: [overview] +sources: 0 +updated: <YYYY-MM-DD> +--- + +# <Topic> Overview + +## Thesis +Two or three sentences capturing the current synthesis across all sources read so far. **Revised** as new sources come in. + +## The landscape +- Sub-area A — pursued by [[entities/x]], papers [[sources/y]] +- Sub-area B — ... +- Sub-area C — ... + +## Key concepts +- [[concepts/xxx]] — short note on role +- [[concepts/yyy]] — short note +- [[concepts/zzz]] — short note + +## Key sources +- [[sources/xxx]] — why it matters +- [[sources/yyy]] — why it matters + +## Current open problems +Short list with [[concepts]] and [[sources]] pointers. + +## How this synthesis has changed +- **<YYYY-MM-DD>** — initial synthesis after first N sources. +- **<YYYY-MM-DD>** — added [[sources/xxx]]; shifted emphasis toward ... + +## Related +- [[synthesis/other-overview]] +- [[comparisons/a-vs-b]] diff --git a/engineering/llm-wiki/commands/wiki-ingest.md b/engineering/llm-wiki/commands/wiki-ingest.md new file mode 100644 index 000000000..e640ca2f3 --- /dev/null +++ b/engineering/llm-wiki/commands/wiki-ingest.md @@ -0,0 +1,52 @@ +--- +name: wiki-ingest +description: Ingest a source file from raw/ into the LLM Wiki — read, discuss, write summary page, update cross-references across 5-15 pages, regenerate index, append to log. Usage /wiki-ingest <path-to-source> +--- + +# /wiki-ingest + +Ingest a new source into the LLM Wiki. This is the most-used command. + +The flow: read the source → discuss TL;DR and key claims with you → write a source summary page → update every relevant entity and concept page → flag contradictions → update `index.md` → append to `log.md`. + +A typical ingest touches **5-15 wiki pages**. You (the user) are in the loop: the ingestor proposes changes and waits for your confirmation before writing. + +## Usage + +``` +/wiki-ingest <path> +/wiki-ingest raw/papers/monosemanticity.pdf +/wiki-ingest raw/articles/2026-04-01-interpretability-post.md +``` + +## What happens + +1. **Prep** — runs `scripts/ingest_source.py` to get title, preview, and suggested summary path +2. **Read** — reads the source directly +3. **Discuss** — reports TL;DR, key claims, which pages will be touched, any contradictions +4. **Confirm** — waits for your go-ahead (or redirects) +5. **Write** — creates the source summary, updates 5-15 pages, flags contradictions +6. **Index** — runs `scripts/update_index.py` or edits `wiki/index.md` inline +7. **Log** — runs `scripts/append_log.py --op ingest --title "<title>"` +8. **Report** — bulleted wikilinks to every touched page + +## Sub-agent + +This command dispatches the `wiki-ingestor` sub-agent for the heavy lifting. See `agents/wiki-ingestor.md`. + +## Scripts + +- `engineering/llm-wiki/scripts/ingest_source.py` — source prep (metadata + preview) +- `engineering/llm-wiki/scripts/update_index.py` — regenerate index +- `engineering/llm-wiki/scripts/append_log.py` — log the ingest + +## Rules + +- The source must be inside the vault's `raw/` layer. If it isn't, the command will ask you to move it first. +- `raw/` is immutable — the ingestor reads only. +- If a summary page already exists, the ingestor enters **merge mode** and appends a re-ingest section. + +## Skill Reference + +→ `engineering/llm-wiki/SKILL.md` +→ `engineering/llm-wiki/references/ingest-workflow.md` diff --git a/engineering/llm-wiki/commands/wiki-init.md b/engineering/llm-wiki/commands/wiki-init.md new file mode 100644 index 000000000..e62f95754 --- /dev/null +++ b/engineering/llm-wiki/commands/wiki-init.md @@ -0,0 +1,60 @@ +--- +name: wiki-init +description: Bootstrap a fresh LLM Wiki vault with the three-layer structure, schema files, and starter templates. Usage /wiki-init <path> --topic "<topic>" [--tool all|claude-code|codex|cursor|antigravity] +--- + +# /wiki-init + +Bootstrap a new LLM Wiki vault. Creates `raw/`, `wiki/{entities,concepts,sources,comparisons,synthesis}`, the index and log, and installs the schema file(s) for your LLM CLI of choice. + +## Usage + +``` +/wiki-init <path> --topic "<one-line topic>" +/wiki-init <path> --topic "<topic>" --tool <claude-code|codex|cursor|antigravity|opencode|gemini-cli|all> +/wiki-init <path> --topic "<topic>" --force # overwrite non-empty dir +``` + +## Examples + +``` +/wiki-init ~/vaults/research --topic "LLM interpretability" +/wiki-init ./book-wiki --topic "The Power Broker — Robert Caro" --tool all +/wiki-init ~/vaults/founders --topic "SaaS founder playbook" --tool codex +``` + +## What it creates + +``` +<path>/ +├── raw/ +│ └── assets/ +├── wiki/ +│ ├── index.md # from template +│ ├── log.md # from template +│ ├── entities/ +│ ├── concepts/ +│ ├── sources/ +│ ├── comparisons/ +│ ├── synthesis/ +│ └── .templates/ # page templates for reference +├── CLAUDE.md # if --tool claude-code or all +├── AGENTS.md # if --tool codex|cursor|antigravity|opencode|gemini-cli|all +├── .cursorrules # if --tool cursor or all +└── .gitignore +``` + +## Next steps + +After init: +1. Open the vault in Obsidian +2. Drop a source into `raw/` +3. Run `/wiki-ingest raw/<your-file>` + +## Script + +- `engineering/llm-wiki/scripts/init_vault.py` + +## Skill Reference + +→ `engineering/llm-wiki/SKILL.md` diff --git a/engineering/llm-wiki/commands/wiki-lint.md b/engineering/llm-wiki/commands/wiki-lint.md new file mode 100644 index 000000000..1822a2f85 --- /dev/null +++ b/engineering/llm-wiki/commands/wiki-lint.md @@ -0,0 +1,83 @@ +--- +name: wiki-lint +description: Run a health check on the LLM Wiki vault — mechanical checks (orphans, broken links, stale pages, missing frontmatter, log gap, duplicates) plus semantic checks (contradictions, cross-reference gaps, concepts missing their own page). Outputs a markdown report with suggested actions. Usage /wiki-lint [--stale-days N] [--log-gap-days N] +--- + +# /wiki-lint + +Health-check the wiki. Surfaces orphan pages, broken wikilinks, stale claims, missing frontmatter, contradictions, and structural drift. **Reports, doesn't silently fix** — you decide what to change. + +Run this weekly, after batch ingests, and always before sharing the wiki. + +## Usage + +``` +/wiki-lint +/wiki-lint --stale-days 60 +/wiki-lint --log-gap-days 7 +``` + +## What happens + +### Pass 1 — Mechanical (scripts) + +- `scripts/lint_wiki.py` — orphans, broken links, stale pages, missing frontmatter, duplicate titles, log gap +- `scripts/graph_analyzer.py` — hubs, sinks, connected components, graph stats + +### Pass 2 — Semantic (LLM reads and thinks) + +- Contradictions between recently-updated pages +- Stale claims superseded by newer sources +- Concepts mentioned in plain text across 3+ pages without their own page +- Cross-reference gaps (entities mentioned but not wikilinked) +- Index drift (index.md out of sync with wiki/) + +### Pass 3 — Report + +A markdown report grouped by severity: + +```markdown +# Wiki lint — <date> + +**Total pages:** N **Components:** N **Last log:** <date> + +## Found +- ⚠️ <N> contradictions (list) +- <N> orphans +- <N> broken links +- <N> stale pages +- ... + +## Suggested actions +1. Investigate contradiction between [[sources/a]] and [[sources/b]] +2. Create concept page for "<name>" +3. Fix broken link in [[concepts/x]] +4. Re-ingest [[sources/c]] — stale + contradicted +5. ... +``` + +Then appends a `lint` entry to `log.md`. + +## Sub-agent + +Dispatches the `wiki-linter` sub-agent. See `agents/wiki-linter.md`. + +## Scripts + +- `engineering/llm-wiki/scripts/lint_wiki.py` +- `engineering/llm-wiki/scripts/graph_analyzer.py` +- `engineering/llm-wiki/scripts/append_log.py` + +## Frequency + +| Trigger | Pass | +|---|---| +| Weekly | Mechanical only — fast | +| After batch ingest | Full (mechanical + semantic) | +| Monthly | Full + structural review | +| Before sharing | Full + extra review | + +## Skill Reference + +→ `engineering/llm-wiki/SKILL.md` +→ `engineering/llm-wiki/references/lint-workflow.md` diff --git a/engineering/llm-wiki/commands/wiki-log.md b/engineering/llm-wiki/commands/wiki-log.md new file mode 100644 index 000000000..404c38c4b --- /dev/null +++ b/engineering/llm-wiki/commands/wiki-log.md @@ -0,0 +1,64 @@ +--- +name: wiki-log +description: Show recent entries from the LLM Wiki log (wiki/log.md). Uses the standardized ## [YYYY-MM-DD] header format so grep + tail works. Usage /wiki-log [--last N] [--op ingest|query|lint|...] +--- + +# /wiki-log + +Show recent entries from `wiki/log.md`. Every LLM operation on the wiki leaves a standardized entry: + +``` +## [YYYY-MM-DD] <op> | <title> +<optional detail> +``` + +## Usage + +``` +/wiki-log # last 10 entries +/wiki-log --last 20 +/wiki-log --op ingest --last 10 # only ingest entries +/wiki-log --op lint # recent lint passes +/wiki-log --since 2026-04-01 +``` + +## What it does + +Parses `wiki/log.md` and prints matching entries. No LLM involvement needed — this is essentially: + +```bash +grep "^## \[" wiki/log.md | tail -N +``` + +…plus optional filters for op type and date range. + +## Valid ops + +- `ingest` — a source was read and integrated +- `query` — a question was answered (when filed back) +- `lint` — a health check ran +- `create` — a new page was created outside an ingest +- `update` — a page was updated outside an ingest +- `delete` — a page was removed +- `note` — freeform note (contradictions flagged, thesis revisions, etc.) + +## Example output + +``` +## [2026-04-11] lint | weekly health check +3 contradictions, 12 orphans, 2 broken links. Fixed broken links; left contradictions for next session. + +## [2026-04-10] ingest | Anthropic Monosemanticity +Added sources/monosemanticity.md. Updated concepts/sparse-autoencoder, concepts/polysemanticity, entities/anthropic. + +## [2026-04-09] query | SAE vs probing +Filed back to comparisons/sae-vs-probing.md. +``` + +## Scripts + +- Uses `grep` + `tail` directly on `wiki/log.md`. No dedicated script needed; that's the point of the standardized header format. + +## Skill Reference + +→ `engineering/llm-wiki/SKILL.md` diff --git a/engineering/llm-wiki/commands/wiki-query.md b/engineering/llm-wiki/commands/wiki-query.md new file mode 100644 index 000000000..d209dc52e --- /dev/null +++ b/engineering/llm-wiki/commands/wiki-query.md @@ -0,0 +1,58 @@ +--- +name: wiki-query +description: Query the LLM Wiki — reads index.md first, drills into 3-10 relevant pages, synthesizes an answer with inline [[wikilink]] citations, and offers to file the answer back as a new comparison or synthesis page. Usage /wiki-query "<question>" +--- + +# /wiki-query + +Ask the wiki a question. The librarian reads `index.md` first, picks relevant pages across categories, synthesizes an answer with citations, and offers to file the answer back into the wiki so your explorations compound. + +## Usage + +``` +/wiki-query "<your question>" +/wiki-query "what does the wiki say about sparse autoencoders?" +/wiki-query "compare monosemanticity and polysemanticity across my sources" +/wiki-query "which sources disagree on scaling laws?" +/wiki-query "give me a comparison table of SAE vs linear probing" +``` + +## What happens + +1. **Index-first read** — reads `wiki/index.md` to find relevant pages +2. **Drill-in** — reads 3-10 pages in full (synthesis + concepts + sources + entities) +3. **Follow links** — opportunistically follows wikilinks between pages +4. **Fallback search** — if the index isn't enough, runs `scripts/wiki_search.py` (BM25) +5. **Synthesize** — composes a direct answer + supporting detail + inline `[[sources/xxx]]` citations + "Related pages" section +6. **Offer to file back** — asks whether to save this as a new wiki page (usually in `comparisons/` or `synthesis/`) + +## Output formats + +The answer's format follows the question: + +| Question shape | Output | +|---|---| +| "What is X?" | Markdown explanation with citations | +| "A vs B" | Comparison table | +| "Give me a slide deck on X" | Markdown synthesis → `/wiki-marp` to render | +| "Chart the trend in X" | Python script + saved chart in `wiki/assets/charts/` | + +## Sub-agent + +This command dispatches the `wiki-librarian` sub-agent. See `agents/wiki-librarian.md`. + +## Scripts + +- `engineering/llm-wiki/scripts/wiki_search.py` — BM25 fallback search +- `engineering/llm-wiki/scripts/append_log.py` — log filed answers + +## Rules + +- **Read the index first.** No grep-everything. +- **Every claim cites a page** with a `[[wikilink]]`. +- **Offer to file the answer back** — but only for substantive answers worth keeping. + +## Skill Reference + +→ `engineering/llm-wiki/SKILL.md` +→ `engineering/llm-wiki/references/query-workflow.md` diff --git a/engineering/llm-wiki/expected_outputs/README.md b/engineering/llm-wiki/expected_outputs/README.md new file mode 100644 index 000000000..7071a0b14 --- /dev/null +++ b/engineering/llm-wiki/expected_outputs/README.md @@ -0,0 +1,19 @@ +# Expected Outputs + +Sample outputs for each script in `scripts/`. Use these as fixtures when testing +or to verify the scripts behave correctly end-to-end. + +| Script | Fixture | +|---|---| +| `init_vault.py --json` | `init_vault.json` | +| `ingest_source.py --json` | `ingest_source.json` | +| `update_index.py --json` | `update_index.json` | +| `append_log.py --json` | `append_log.json` | +| `wiki_search.py --json` | `wiki_search.json` | +| `lint_wiki.py --json` | `lint_wiki.json` | +| `graph_analyzer.py --json` | `graph_analyzer.json` | +| `export_marp.py --json` | `export_marp.json` | + +These were captured against a small 2-page example vault (one concept page and +one source page, both with proper frontmatter). Paths have been anonymized to +`/tmp/test-vault`. diff --git a/engineering/llm-wiki/expected_outputs/append_log.json b/engineering/llm-wiki/expected_outputs/append_log.json new file mode 100644 index 000000000..8cf06853e --- /dev/null +++ b/engineering/llm-wiki/expected_outputs/append_log.json @@ -0,0 +1,9 @@ +{ + "status": "ok", + "log_path": "/tmp/test-vault/wiki/log.md", + "date": "2026-04-11", + "op": "ingest", + "title": "Hello Monosemanticity", + "header": "## [2026-04-11] ingest | Hello Monosemanticity", + "detail": "touched 2 pages" +} diff --git a/engineering/llm-wiki/expected_outputs/export_marp.json b/engineering/llm-wiki/expected_outputs/export_marp.json new file mode 100644 index 000000000..c30ea4686 --- /dev/null +++ b/engineering/llm-wiki/expected_outputs/export_marp.json @@ -0,0 +1,9 @@ +{ + "status": "ok", + "vault": "/tmp/test-vault", + "source": "wiki/concepts/sparse-autoencoder.md", + "theme": "gaia", + "output_dir": "slides", + "rendered_count": 1, + "rendered": ["slides/sparse-autoencoder.marp.md"] +} diff --git a/engineering/llm-wiki/expected_outputs/graph_analyzer.json b/engineering/llm-wiki/expected_outputs/graph_analyzer.json new file mode 100644 index 000000000..9fa582728 --- /dev/null +++ b/engineering/llm-wiki/expected_outputs/graph_analyzer.json @@ -0,0 +1,18 @@ +{ + "total_pages": 2, + "total_edges": 2, + "top_outbound_hubs": [ + {"page": "sources/hello", "outbound": 1}, + {"page": "concepts/sparse-autoencoder", "outbound": 1} + ], + "top_inbound_hubs": [ + {"page": "sources/hello", "inbound": 1}, + {"page": "concepts/sparse-autoencoder", "inbound": 1} + ], + "orphans": [], + "sinks": [], + "components": [ + {"size": 2, "sample": ["concepts/sparse-autoencoder", "sources/hello"]} + ], + "component_count": 1 +} diff --git a/engineering/llm-wiki/expected_outputs/ingest_source.json b/engineering/llm-wiki/expected_outputs/ingest_source.json new file mode 100644 index 000000000..a815de6db --- /dev/null +++ b/engineering/llm-wiki/expected_outputs/ingest_source.json @@ -0,0 +1,12 @@ +{ + "source_path": "/tmp/test-vault/raw/articles/hello.md", + "relative": "raw/articles/hello.md", + "bytes": 204, + "sha256": "dcb7021b49882e26", + "ext": ".md", + "title_guess": "Hello Monosemanticity", + "word_count": 28, + "preview": "# Hello Monosemanticity\n\nAnthropic's Bricken et al. 2023 paper trained a sparse autoencoder on a one-layer transformer and found interpretable features.\nKey claim: the feature dictionary is overcomplete.\n", + "existing_summary_page": null, + "suggested_summary_path": "wiki/sources/hello-monosemanticity.md" +} diff --git a/engineering/llm-wiki/expected_outputs/init_vault.json b/engineering/llm-wiki/expected_outputs/init_vault.json new file mode 100644 index 000000000..a2d511b5f --- /dev/null +++ b/engineering/llm-wiki/expected_outputs/init_vault.json @@ -0,0 +1,26 @@ +{ + "status": "ok", + "vault_path": "/tmp/test-vault", + "topic": "LLM interpretability", + "tool": "all", + "date": "2026-04-11", + "installed_files": [ + "CLAUDE.md", + "AGENTS.md", + ".cursorrules", + "wiki/index.md", + "wiki/log.md" + ], + "page_templates_copied": 5, + "layers": { + "raw": "your sources — immutable", + "wiki": "LLM-maintained knowledge base", + "index": "wiki/index.md", + "log": "wiki/log.md" + }, + "next_steps": [ + "Open the vault in Obsidian", + "Drop a source into raw/", + "Run /wiki-ingest <path> in your LLM CLI" + ] +} diff --git a/engineering/llm-wiki/expected_outputs/lint_wiki.json b/engineering/llm-wiki/expected_outputs/lint_wiki.json new file mode 100644 index 000000000..f6a941d14 --- /dev/null +++ b/engineering/llm-wiki/expected_outputs/lint_wiki.json @@ -0,0 +1,10 @@ +{ + "vault": "/tmp/test-vault", + "total_pages": 2, + "orphans": [], + "broken_links": [], + "stale": [], + "missing_frontmatter": [], + "duplicate_titles": {}, + "log_gap": null +} diff --git a/engineering/llm-wiki/expected_outputs/update_index.json b/engineering/llm-wiki/expected_outputs/update_index.json new file mode 100644 index 000000000..a8e31bcab --- /dev/null +++ b/engineering/llm-wiki/expected_outputs/update_index.json @@ -0,0 +1,11 @@ +{ + "status": "ok", + "vault": "/tmp/test-vault", + "total_pages": 2, + "by_category": { + "concept": 1, + "source": 1 + }, + "dry_run": false, + "index_path": "/tmp/test-vault/wiki/index.md" +} diff --git a/engineering/llm-wiki/expected_outputs/wiki_search.json b/engineering/llm-wiki/expected_outputs/wiki_search.json new file mode 100644 index 000000000..80ca05fae --- /dev/null +++ b/engineering/llm-wiki/expected_outputs/wiki_search.json @@ -0,0 +1,10 @@ +{ + "query": "sparse autoencoder", + "hits": [ + { + "path": "concepts/sparse-autoencoder.md", + "score": 1.995, + "snippet": "--- title: Sparse Autoencoder category: concept summary: Dictionary-learning method for interpretable features tags: [interpretability] sources: 1 updated: 2026-04-11 --- # Sparse Autoencoder See [[sources/hello]] for th…" + } + ] +} diff --git a/engineering/llm-wiki/references/cross-tool-setup.md b/engineering/llm-wiki/references/cross-tool-setup.md new file mode 100644 index 000000000..5862c78ee --- /dev/null +++ b/engineering/llm-wiki/references/cross-tool-setup.md @@ -0,0 +1,108 @@ +# Cross-Tool Setup + +The LLM Wiki plugin is tool-agnostic. The **scripts** are pure Python stdlib and run anywhere. Only the **schema loader file** (the file the tool reads to understand conventions) differs per tool. + +## How different CLIs discover project-level instructions + +| Tool | Loader file | Notes | +|---|---|---| +| Claude Code | `CLAUDE.md` | Loaded automatically when CC starts in the vault dir | +| Codex CLI (OpenAI) | `AGENTS.md` | Loaded at session start | +| Cursor (new) | `AGENTS.md` | Modern Cursor reads `AGENTS.md` | +| Cursor (legacy) | `.cursorrules` | Older Cursor versions | +| Google Antigravity | `AGENTS.md` | Uses the standard `AGENTS.md` convention | +| OpenCode / Pi | `AGENTS.md` | Same convention | +| Gemini CLI | `AGENTS.md` | Same convention | +| Aider | `CONVENTIONS.md` or `.aider.conf.yml` | Point Aider at `CLAUDE.md` with `--read CLAUDE.md` | + +**Recommendation:** ship **both** `CLAUDE.md` and `AGENTS.md` in every vault. `init_vault.py --tool all` does this by default. + +## Multi-tool vault + +If you use multiple CLIs against the same vault: + +```bash +python scripts/init_vault.py --path ~/vaults/research --topic "X" --tool all +``` + +This creates: +- `CLAUDE.md` +- `AGENTS.md` +- `.cursorrules` + +All three are **the same content**, formatted appropriately. You can symlink to keep them in sync: + +```bash +cd <vault> +ln -sf CLAUDE.md AGENTS.md +# or edit both manually when you tune the schema +``` + +## Per-tool quickstart + +### Claude Code + +```bash +cd <vault> +claude +> /wiki-init # if vault isn't initialized +> /wiki-ingest raw/paper.pdf +> /wiki-query "what does the paper say about X?" +``` + +The slash commands ship with this plugin. To install the plugin itself, either: +- Clone claude-code-skills and copy `engineering/llm-wiki/` into `~/.claude/skills/`, or +- Install via the marketplace if published + +### Codex CLI + +Codex reads `AGENTS.md` automatically. Then: + +```bash +cd <vault> +codex +> ingest raw/paper.pdf into the wiki +> query: what does the paper say about X? +``` + +Codex doesn't have slash commands, but the schema file teaches it the ingest/query/lint workflow, so natural-language triggers work. + +### Cursor + +```bash +cd <vault> +cursor . +``` + +Open the Cursor chat in the sidebar. Cursor auto-reads `AGENTS.md`. Ask the same questions. + +### Antigravity / OpenCode / Pi + +Same as Codex — drop `AGENTS.md` in the vault root and use natural language. + +### Multi-tool same session + +You can run Claude Code and Codex **simultaneously** against the same vault. They'll both see updates if one writes a page — filesystem is the source of truth. Just make sure each vault is committed to git so you can resolve conflicts. + +## Running the scripts directly (any tool) + +The scripts don't care which tool calls them. You can run them from the shell any time: + +```bash +# from inside the vault +python ~/.claude/skills/llm-wiki/scripts/lint_wiki.py --vault . +python ~/.claude/skills/llm-wiki/scripts/update_index.py --vault . +python ~/.claude/skills/llm-wiki/scripts/wiki_search.py --vault . --query "superposition" +``` + +Aliases are handy. Add to your shell rc: + +```bash +alias wiki-lint='python ~/.claude/skills/llm-wiki/scripts/lint_wiki.py --vault .' +alias wiki-index='python ~/.claude/skills/llm-wiki/scripts/update_index.py --vault .' +alias wiki-search='python ~/.claude/skills/llm-wiki/scripts/wiki_search.py --vault .' +``` + +## MCP exposure (future) + +The wiki can be exposed as an MCP tool so any MCP-capable client (Claude Desktop, Claude Code, etc.) can query it. See `engineering/mcp-design` in this repo for the pattern. A future version of this plugin will ship an `mcp/` directory with a reference MCP server. diff --git a/engineering/llm-wiki/references/ingest-workflow.md b/engineering/llm-wiki/references/ingest-workflow.md new file mode 100644 index 000000000..c17382c9b --- /dev/null +++ b/engineering/llm-wiki/references/ingest-workflow.md @@ -0,0 +1,97 @@ +# Ingest Workflow + +The detailed flow the LLM follows when the user runs `/wiki-ingest <path>` or dispatches the `wiki-ingestor` sub-agent. + +## Inputs + +- Path to a source file (inside `raw/` — if not, prompt the user to move it first) +- The current state of `wiki/` (especially `index.md`) + +## Step-by-step + +### 1. Prepare the brief + +Run `python scripts/ingest_source.py --vault . --source <path> --json` to get: +- title guess +- word count +- preview (first 1200 chars) +- suggested summary-page path +- whether a summary page already exists (→ **merge mode**) + +### 2. Read the source + +Use the Read tool on the source directly. For PDFs, use the Read tool's PDF support. For images clipped locally to `raw/assets/`, inspect them if the LLM has vision. + +### 3. Discuss with the user + +Before writing anything, tell the user: +- Title and author(s) +- 2-3 sentence TL;DR +- Key claims (bulleted, 3-7 items) +- Which existing wiki pages this source will touch +- Any **contradictions** with existing pages + +**Wait for user to confirm or redirect.** This is the "LLM makes edits, you browse" loop — the user is in the loop. + +### 4. Create / merge the source summary page + +Path: `wiki/sources/<slug>.md`. Use the **source summary** template from `references/page-formats.md`. Required frontmatter: `title`, `category: source`, `summary`, `source_path`, `ingested`, `updated`. + +**Merge mode** (summary page already exists): append a new "## Re-ingest <date>" section at the bottom with what changed. Do not overwrite. + +### 5. Identify entities and concepts + +For each entity and concept mentioned in the source: +- Check if a page exists in `wiki/entities/` or `wiki/concepts/` +- **If yes:** update it. Add a new bullet under "Appears in" / "Used in" pointing to this source. Update "Key claims" if this source adds or contradicts a claim. Update `sources:` count in frontmatter. Update `updated:` to today. +- **If no:** create a new page from the entity/concept template. Start with the minimum: title, summary, one key fact sourced from this reading, link back to this source. + +Typical ingest touches **5-15 pages** across `entities/`, `concepts/`, and sometimes `comparisons/`. + +### 6. Flag contradictions explicitly + +If the new source contradicts an existing page, add a callout to BOTH pages: + +```markdown +> ⚠️ **Contradiction** — [[sources/new]] claims X but [[sources/old]] claims ~X. +> Unresolved as of 2026-04-10. +``` + +Log contradictions in `log.md` with `op: note`. + +### 7. Update synthesis (optional) + +If the source meaningfully shifts a `synthesis/` page's thesis, revise the "Thesis" paragraph and append a dated entry under "How this synthesis has changed". Don't rewrite history; append. + +### 8. Update `index.md` + +Either: +- Run `python scripts/update_index.py --vault .` to regenerate the entire index from frontmatter, OR +- Edit the relevant category sections inline (faster for small ingests). + +### 9. Append to `log.md` + +Run `python scripts/append_log.py --vault . --op ingest --title "<title>" --detail "<detail>"`. + +The detail line should list which pages were touched: + +``` +## [2026-04-10] ingest | Anthropic Monosemanticity +Added sources/monosemanticity.md. Updated concepts/sparse-autoencoder, +concepts/polysemanticity, entities/anthropic-interpretability-team. Flagged +contradiction with sources/distributed-representations. +``` + +### 10. Report back to the user + +Summary the user sees in chat: +- Source summary page created/updated +- Pages touched (bulleted wikilinks so the user can click through) +- Contradictions flagged (if any) +- Suggested next sources to pursue + +## After-ingest tips + +- **Big ingest?** Run `python scripts/lint_wiki.py --vault .` to check for new orphans or broken links. +- **Graph check?** Run `python scripts/graph_analyzer.py --vault .` to see if the new page is well-connected. +- **Open Obsidian graph view** — the user should see the new page attached to the existing cluster. diff --git a/engineering/llm-wiki/references/lint-workflow.md b/engineering/llm-wiki/references/lint-workflow.md new file mode 100644 index 000000000..392aa1382 --- /dev/null +++ b/engineering/llm-wiki/references/lint-workflow.md @@ -0,0 +1,91 @@ +# Lint Workflow + +Periodic health-check the LLM runs when the user runs `/wiki-lint` or dispatches the `wiki-linter` sub-agent. Run this at least weekly, and always after a batch ingest. + +## Goal + +Keep the wiki healthy as it grows. Surface problems for the user to review. + +## Pass 1 — mechanical checks (script) + +Run `python scripts/lint_wiki.py --vault .` to get a report on: + +- **Orphans** — pages with zero inbound `[[wikilinks]]` +- **Broken links** — wikilinks pointing to non-existent pages +- **Stale pages** — pages whose `updated:` frontmatter is older than 90 days (tune via `--stale-days`) +- **Missing frontmatter** — pages lacking `title`, `category`, or `summary` +- **Duplicate titles** — two or more pages sharing the same title +- **Log gap** — no log entry in the last 14 days (tune via `--log-gap-days`) + +Run `python scripts/graph_analyzer.py --vault .` for structural stats: + +- Hubs (inbound/outbound) — likely well-placed +- Sinks — pages that don't link out; may need cross-referencing +- Connected components — if > 1, parts of the wiki are disconnected islands + +## Pass 2 — semantic checks (LLM) + +The script can't catch these. The LLM must read and think. + +### A. Contradictions + +Scan pages whose `updated:` is recent. For each, check whether it contradicts any existing page. If so: +- Add a `> ⚠️ Contradiction:` callout to both pages +- Log with `op: note` +- Surface to user: "I found a potential contradiction between X and Y. Want me to investigate?" + +### B. Stale claims + +For each flagged stale page, ask: +- Does a newer source now contradict this? +- Is a "Key facts" bullet likely to be outdated (person changed role, company pivoted, etc.)? +- If yes, suggest to user: "Page X says Y. This may be outdated — do you want me to search for newer sources?" + +### C. Concepts mentioned but without their own page + +Grep for common patterns: `[[concept:xxx]]`, phrases like "see also", concept-shaped nouns mentioned across 3+ pages but with no dedicated page. + +Suggest new pages to create. + +### D. Cross-reference gaps + +For each page, check: do all entities and concepts mentioned have wikilinks? If a concept is referenced as plain text in 3+ places, promote it to a wikilink (and create a stub page if needed). + +### E. Index drift + +Compare `index.md` against actual `wiki/` contents. If out of sync, either regenerate (`update_index.py`) or patch inline. + +## Pass 3 — report + +Present findings to the user as a single markdown report: + +```markdown +# Wiki lint — 2026-04-10 + +**Total pages:** 87 **Components:** 1 **Last log:** 2026-04-09 + +## Found +- ⚠️ 3 contradictions (wiki/concepts/x, wiki/sources/y, wiki/sources/z) +- 12 orphan pages (mostly new entities) +- 2 broken links (wiki/concepts/x → [[foo-bar]] no such page) +- 4 stale pages (>90 days, no re-ingest) +- 5 concepts mentioned across 3+ pages without their own page + +## Suggested actions +1. Investigate contradiction between [[sources/a]] and [[sources/b]] +2. Create concept page for "attention masking" (mentioned in 4 sources) +3. Re-ingest [[sources/c]] — stale and contradicted by newer sources +4. Fix broken link in [[concepts/x]] +5. Cross-reference the 12 orphans (most belong under [[synthesis/overview]]) + +Want me to run these in order, or pick specific ones? +``` + +Append a `lint` entry to `log.md` summarizing what was found and what was fixed. + +## Frequency + +- **Weekly** — light pass, script-only (`lint_wiki.py` + quick review) +- **After batch ingests** — always +- **Monthly** — full pass including semantic checks +- **Before sharing the wiki** — full pass plus an extra review diff --git a/engineering/llm-wiki/references/memex-principles.md b/engineering/llm-wiki/references/memex-principles.md new file mode 100644 index 000000000..a4df1a709 --- /dev/null +++ b/engineering/llm-wiki/references/memex-principles.md @@ -0,0 +1,78 @@ +# Memex Principles + +Why the LLM Wiki pattern works, and why it failed for humans until LLMs. + +## Vannevar Bush's Memex (1945) + +In "As We May Think", Bush described a personal knowledge store where: + +- Documents are curated, not just searched +- Users build **associative trails** — named, reusable paths through the material +- The trails are as valuable as the documents +- The system is private and personal, not a public reference + +This is almost exactly the LLM Wiki pattern. The difference: Bush had no one to do the bookkeeping. + +## Why humans abandon wikis + +The value of a wiki grows linearly with its size. The maintenance burden grows faster. At some inflection point — usually around 50-100 pages — maintenance starts to feel like chores and the wiki goes stale. + +Specific tasks that die first: + +- Updating cross-references when a new page is added +- Keeping summary pages current +- Noticing when new data contradicts old claims +- Consolidating pages that have drifted apart +- Filing explorations back into the knowledge base +- Keeping the index current + +Humans are great at reading, curating, and thinking about what things mean. They're bad at the bookkeeping. The bookkeeping is 80% of the work. + +## What changed with LLMs + +LLMs don't get bored. They don't forget to update a cross-reference. They can touch 15 files in one pass without losing track. They cost near-zero per maintenance operation. + +This changes the economics. The wiki stays maintained because maintenance is now free (or nearly). The human's job collapses to: + +- **Source curation** — deciding what's worth reading +- **Direction** — asking good questions, steering analysis +- **Judgment** — deciding when a contradiction matters +- **Taste** — knowing when the synthesis is wrong + +Everything else — the 80% that killed human wikis — is delegated. + +## Why not just RAG? + +RAG retrieves fragments at query time and synthesizes from scratch every query. It works, but: + +- **No accumulation.** Every subtle question re-derives the same synthesis. +- **Cross-references are computed on demand.** If the cross-reference needs 5 sources to be visible, you'd better hope all 5 are in the retrieval window. +- **Contradictions are invisible.** They surface only if you explicitly ask "is there a contradiction?" +- **Explorations disappear.** A comparison you worked out yesterday has to be re-derived tomorrow. + +The wiki fixes all of these by **compiling the knowledge once**. The cross-references are already there. The contradictions have been flagged. The synthesis has absorbed everything read so far. + +RAG is retrieve-then-think. The wiki is think-once-retrieve-many. + +## When the wiki stops being enough + +At ~500-1000 pages, the index approach starts to creak. Options: + +1. **Layer on search.** Add `wiki_search.py` (BM25) or an external tool like [qmd](https://github.com/tobi/qmd) (hybrid BM25 + vector). Both work alongside the index. +2. **Shard by topic.** Split into multiple vaults by domain. +3. **Add an MCP retrieval layer.** Expose the wiki as a tool so agents can query it structurally. + +The wiki and RAG are not opposites. The wiki is a **compiled layer above RAG**. You can run RAG on top of the wiki (indexing `wiki/`) and you'll get the benefits of both: pre-synthesized knowledge + scalable retrieval. + +## The human role + +A common failure mode: users delegate curation to the LLM ("just ingest all my Pocket articles"). Don't. Curation is where human judgment lives. The LLM can help you decide *whether* to read a source, but you pick what makes it into `raw/`. + +If you let the LLM ingest everything, the wiki fills with low-signal summaries and the synthesis becomes meaningless. The wiki's value is a direct function of the quality of `raw/`. + +## Reading recommendations + +- Vannevar Bush, "As We May Think" (Atlantic, 1945) +- Andrej Karpathy's original LLM Wiki gist (linked from SKILL.md) +- Ousterhout's *A Philosophy of Software Design* — for why "deep modules" (well-summarized pages) beat shallow ones +- Niklas Luhmann's Zettelkasten — an earlier manual version of the same pattern diff --git a/engineering/llm-wiki/references/obsidian-setup.md b/engineering/llm-wiki/references/obsidian-setup.md new file mode 100644 index 000000000..e65db76c5 --- /dev/null +++ b/engineering/llm-wiki/references/obsidian-setup.md @@ -0,0 +1,85 @@ +# Obsidian Setup + +Recommended Obsidian configuration for an LLM Wiki vault. None of this is strictly required — the wiki is just markdown files — but these settings remove friction. + +## Open the vault + +1. Obsidian → "Open folder as vault" → pick your initialized vault +2. The vault already has `wiki/`, `raw/`, `CLAUDE.md`, `AGENTS.md` + +## Settings → Files and Links + +- **Default location for new notes:** `wiki/` +- **New link format:** `Shortest path when possible` (keeps wikilinks clean) +- **Use `[[Wikilinks]]`:** ON +- **Attachment folder path:** `raw/assets/` (so clipped images land in `raw/`, not `wiki/`) +- **Automatically update internal links:** ON + +## Settings → Hotkeys + +Search for and bind: +- **"Download attachments for current file"** → `Ctrl/Cmd + Shift + D` +- **"Open graph view"** → `Ctrl/Cmd + G` + +## Core plugins to enable + +- **Graph view** — see the shape of your wiki. Hubs, orphans, clusters. +- **Backlinks** — pane showing who links to the current page. Critical for browsing. +- **Outgoing links** — complementary pane. +- **Templates** — enable and set the template folder to `wiki/.templates` +- **Tag pane** — tag-driven navigation +- **Search** — obviously +- **Page preview** — hover a wikilink to preview +- **Canvas** — visual exploration, useful for synthesis work + +## Recommended community plugins + +- **Obsidian Web Clipper** (browser extension, not a plugin) — clip articles to `raw/articles/` as markdown +- **Dataview** — query over frontmatter. Dynamic tables of "all concept pages touched by 3+ sources". +- **Marp for Obsidian** — render any markdown with `marp: true` frontmatter as a slide deck inside Obsidian. Pairs with `scripts/export_marp.py`. +- **Templater** — dynamic templates (optional, you can use the LLM for this) +- **Advanced Tables** — easier markdown table editing +- **Git** — commit on save, or hook into system git + +## Dataview examples + +Pages with 3+ sources: +```dataview +table updated, sources +from "wiki/concepts" +where sources >= 3 +sort updated desc +``` + +Recently updated synthesis pages: +```dataview +list +from "wiki/synthesis" +sort updated desc +limit 10 +``` + +Orphans (Dataview can't see inbound links — use the lint script for this). + +## Git workflow + +```bash +cd <vault> +git init +git add . +git commit -m "init wiki" + +# After every session: +git add wiki/ log.md index.md +git commit -m "ingest: <source>" +``` + +The vault is a plain markdown repo. Version history, branching, collaboration — free. + +## Tips + +- **Use the graph view daily** — it's the fastest way to see structural drift +- **Pin `index.md`, `log.md`, and the active `synthesis/` page** to the sidebar tabs +- **Split view** — wiki on the left, chat/CLI on the right. You browse while the LLM edits. +- **Enable "strict line breaks"** so your LLM's markdown renders the way the LLM expects +- **Use images aggressively** — download them locally, reference from pages. The LLM can read them with its vision tool when needed. diff --git a/engineering/llm-wiki/references/page-formats.md b/engineering/llm-wiki/references/page-formats.md new file mode 100644 index 000000000..ebeb3e7c8 --- /dev/null +++ b/engineering/llm-wiki/references/page-formats.md @@ -0,0 +1,194 @@ +# Page Formats + +Every wiki page has the same skeleton: YAML frontmatter + a section structure that matches its category. Below are the five canonical formats. Templates live in `assets/page-templates/`. + +## 1. Entity page + +For a person, organization, place, product, or dataset. + +```markdown +--- +title: Anthropic +category: entity +summary: AI safety company, developer of Claude; major contributor to interpretability research +tags: [company, ai-safety, anthropic] +sources: 4 +updated: 2026-04-10 +--- + +# Anthropic + +## What it is +One-paragraph definition. What kind of entity, founded when, by whom, active in what. + +## Why it matters (to this wiki) +Why this entity shows up across sources. What role does it play in the narrative? + +## Key facts +- Founded YYYY by [[people]] +- Known for [[concepts]] +- Related [[entities]] + +## Appears in +- [[sources/monosemanticity]] — primary work on sparse autoencoders +- [[sources/constitutional-ai]] — alignment methodology +- [[concepts/rlhf]] — contributor to training method + +## Open questions +- Questions the sources don't yet answer; good prompts for new source hunts. +``` + +## 2. Concept page + +For an idea, theory, method, framework. + +```markdown +--- +title: Sparse Autoencoder +category: concept +summary: Dictionary-learning method for decomposing polysemantic neurons into monosemantic features +tags: [interpretability, sparse-autoencoders, dictionary-learning] +sources: 3 +updated: 2026-04-10 +--- + +# Sparse Autoencoder + +## Definition +Precise, one-paragraph definition. The canonical form used across your sources. + +## Origin +Who proposed it, when, in what paper/context. Link [[entities]] and [[sources]]. + +## Key claims +- Claim 1 — cited from [[sources/xxx]] +- Claim 2 — cited from [[sources/yyy]] + +## Contrasts with +- [[concepts/probing]] — see [[comparisons/sae-vs-probing]] + +## Open questions / disagreements +- Unresolved questions across sources. +- ⚠️ Contradiction: [[sources/a]] claims X but [[sources/b]] claims ~X. + +## Used in +- [[synthesis/interpretability-overview]] +``` + +## 3. Source summary page + +One per ingested source. This is the **single place the raw source's content is summarized**; other pages cite it. + +```markdown +--- +title: "Towards Monosemanticity: Decomposing Language Models With Dictionary Learning" +category: source +summary: Anthropic 2024 paper using sparse autoencoders to extract interpretable features from a one-layer transformer +tags: [interpretability, sparse-autoencoders, anthropic] +source_path: raw/papers/monosemanticity.pdf +source_date: 2024-10 +authors: [Bricken et al.] +ingested: 2026-04-10 +updated: 2026-04-10 +--- + +# Towards Monosemanticity + +## TL;DR +Two sentences max. What did they do, what did they find. + +## Key claims +1. Claim, with a page/section pointer if available +2. ... + +## Methods +How the work was done. Data, model, training, evaluation. + +## Evidence cited +- Figure 3 shows ... +- Table 1 ... + +## Surprises / contradictions +- Where this source conflicts with [[sources/other]] or [[concepts/xxx]]. + +## Connections +- Extends [[concepts/sparse-autoencoder]] +- Builds on [[entities/anthropic-interpretability-team]]'s prior work +- Related: [[sources/superposition-2022]] + +## Where it's cited +Pages in this wiki that cite this source: +- [[concepts/sparse-autoencoder]] +- [[entities/anthropic]] +- [[synthesis/interpretability-overview]] +``` + +## 4. Comparison page + +For explicit cross-source or cross-concept analysis. + +```markdown +--- +title: "SAE vs Probing" +category: comparison +summary: How sparse autoencoders differ from linear probing as interpretability methods +tags: [interpretability, comparison] +sources: 4 +updated: 2026-04-10 +--- + +# Sparse Autoencoders vs Linear Probes + +## What they share +Both look for human-interpretable structure inside trained models. + +## Where they diverge +| Dimension | SAE | Probe | +|---|---|---| +| Supervision | unsupervised | supervised | +| Output | dictionary of features | single-label classifier | +| Scalability | model-dependent | cheap | +| Typical use | feature discovery | feature verification | + +## Which sources take which side +- [[sources/monosemanticity]] — pro-SAE +- [[sources/probing-survey]] — pro-probes + +## Open questions +- When should you prefer one over the other? +``` + +## 5. Synthesis page + +High-level views that draw on many sources and concepts. + +```markdown +--- +title: Interpretability Overview +category: synthesis +summary: The field of interpretability research — goals, methods, open problems, key players +tags: [interpretability, overview] +sources: 12 +updated: 2026-04-10 +--- + +# Interpretability Overview + +## Thesis +Two or three sentences capturing the current synthesis across all sources read so far. Revised as new sources come in. + +## The landscape +- Sub-area A — pursued by [[entities]], papers [[sources]] +- Sub-area B — ... + +## Current open problems +Short list with [[concepts]] and [[sources]] pointers. + +## How this synthesis has changed +- **2026-04-10** — added [[sources/monosemanticity]]; shifted emphasis toward SAE. +- **2026-03-28** — initial synthesis after first 5 sources. + +## Related +- [[synthesis/alignment-overview]] +- [[comparisons/mechinterp-vs-behavioral-interp]] +``` diff --git a/engineering/llm-wiki/references/query-workflow.md b/engineering/llm-wiki/references/query-workflow.md new file mode 100644 index 000000000..c32f0d46d --- /dev/null +++ b/engineering/llm-wiki/references/query-workflow.md @@ -0,0 +1,76 @@ +# Query Workflow + +The flow the LLM follows when the user runs `/wiki-query <question>` or dispatches the `wiki-librarian` sub-agent. + +## Core principle + +**Read `index.md` first, then drill in.** Do NOT grep the entire wiki on every query — the index is there precisely so you don't have to. + +## Step-by-step + +### 1. Read `index.md` + +The index is the catalog. Scan it and pick the 3-10 pages most likely to contain the answer. Pick across categories: a good query usually pulls from `synthesis/` for the big picture, `concepts/` for definitions, `sources/` for evidence, and `entities/` for context. + +### 2. Read the picked pages + +Read them in full. These are short, curated, and already cross-referenced. The wiki has done the hard work for you. + +### 3. Follow wikilinks opportunistically + +If a read page points to another page that's clearly relevant, follow it. Don't follow blindly — stop when you have enough. + +### 4. Fall back to search if needed + +If the index doesn't surface the right page, use: + +```bash +python scripts/wiki_search.py --vault . --query "<terms>" --limit 5 +``` + +BM25 search over wiki pages. Standard library only. Use when: +- The index is stale (flag this to the user — it means lint time) +- The user asks about something niche that doesn't have its own page yet +- You're doing a sweeping search across many pages + +### 5. Synthesize the answer + +Compose the answer as: +- A direct answer in 1-3 sentences +- Supporting detail, organized thematically +- **Inline citations** using wikilinks to source pages: `[[sources/monosemanticity]]` +- **A "Related pages" section** at the end with 3-5 wikilinks + +### 6. Offer to re-file + +**Every good answer is a candidate wiki page.** At the end of the answer, ask: + +> _Should I file this as a new page in the wiki? Suggested location: +> `wiki/comparisons/sae-vs-probing.md` — or I can append it to an existing page._ + +If the user says yes: +- Pick the right category (most often `comparisons/` or `synthesis/`) +- Use the appropriate template +- Add frontmatter with `category`, `summary`, `sources` (count of cited sources), `updated` +- Update `index.md` +- Append to `log.md` with `op: create` and the question as the title + +This is how the wiki compounds — explorations don't disappear into chat history. + +## Output formats + +Not every query wants a markdown answer. Offer the user: + +- **Markdown page** (default) — filed back as a wiki page +- **Comparison table** — for "A vs B" questions +- **Marp slide deck** — via `python scripts/export_marp.py` on the synthesis page +- **Chart (matplotlib)** — for data-driven questions; save to `wiki/assets/charts/` +- **Obsidian Canvas** — for visual exploration (JSON format, stored at `wiki/canvases/`) + +## Anti-patterns + +- ❌ Read every page in the wiki on every query → use the index +- ❌ Answer without citations → every claim must link to a page +- ❌ Create a new page for a one-off trivial question → only file back answers worth keeping +- ❌ Invent content not in the wiki → if you don't know, say so and suggest a new source to ingest +- ❌ Skip the `log.md` entry when filing an answer back diff --git a/engineering/llm-wiki/references/wiki-schema.md b/engineering/llm-wiki/references/wiki-schema.md new file mode 100644 index 000000000..fd842ee28 --- /dev/null +++ b/engineering/llm-wiki/references/wiki-schema.md @@ -0,0 +1,98 @@ +# Wiki Schema + +The vault has three layers. The LLM must respect the boundaries. + +## Layout + +``` +<vault>/ +├── raw/ # IMMUTABLE sources (you own) +│ ├── articles/*.md # Obsidian Web Clipper output +│ ├── papers/*.pdf +│ ├── notes/*.md # your own notes, journal entries +│ └── assets/ # images downloaded by Obsidian +├── wiki/ # LLM-owned knowledge base +│ ├── index.md # content catalog — updated every ingest +│ ├── log.md # append-only timeline +│ ├── entities/ # people, orgs, places, products +│ ├── concepts/ # ideas, theories, frameworks, methods +│ ├── sources/ # one summary page per ingested source +│ ├── comparisons/ # cross-source analysis / contrasts +│ ├── synthesis/ # high-level theses, overviews +│ └── .templates/ # page templates (reference only, not indexed) +├── CLAUDE.md # schema file for Claude Code +├── AGENTS.md # same schema for Codex/Cursor/Antigravity +└── .cursorrules # (optional) Cursor legacy +``` + +## Iron rules + +1. **`raw/` is immutable.** The LLM reads from `raw/` but never writes to it. Never rename, never delete, never edit. If a source is wrong, the user edits it. +2. **All LLM writes go to `wiki/`.** No exceptions. +3. **Every ingest updates 5 files minimum:** the new source summary, the relevant entity/concept pages, `index.md`, `log.md`. A rich ingest touches 10-15. +4. **Every wiki page carries YAML frontmatter.** Without frontmatter, `update_index.py` and `lint_wiki.py` can't see it. + +## Required page frontmatter + +```yaml +--- +title: Mechanistic Interpretability +category: concept # entity | concept | source | comparison | synthesis +summary: Reverse-engineering neural networks into human-understandable circuits +tags: [interpretability, circuits, anthropic] +sources: 3 # optional — number of sources touching this page +updated: 2026-04-10 # LLM updates this on every edit +--- +``` + +Allowed `category` values: `entity`, `concept`, `source`, `comparison`, `synthesis`. + +## Naming conventions + +- **Filenames:** `kebab-case.md` — lowercase, hyphens, no spaces +- **Entities:** `entities/<kebab-case-name>.md` — e.g. `entities/chris-olah.md` +- **Concepts:** `concepts/<kebab-case-name>.md` — e.g. `concepts/sparse-autoencoder.md` +- **Sources:** `sources/<short-slug>.md` — e.g. `sources/monosemanticity.md` +- **Comparisons:** `comparisons/<topic-a>-vs-<topic-b>.md` +- **Synthesis:** `synthesis/<topic>-overview.md` or `synthesis/<topic>-thesis.md` + +## Linking + +Use Obsidian wikilinks. Three forms: + +``` +[[concepts/sparse-autoencoder]] # full path +[[concepts/sparse-autoencoder|sparse autoencoders]] # custom display text +[[sparse-autoencoder]] # stem — resolves if unique +``` + +The linter resolves stem links by matching against filenames. Prefer full paths when ambiguous. + +## Cross-reference rules + +- **Every entity mentioned in a concept/source page must be a wikilink.** If the entity page doesn't exist yet, create it. +- **Every concept mentioned in a source summary must be a wikilink.** Same rule. +- **Contradictions get flagged inline** with a `> ⚠️ Contradiction:` callout, and the source pages that disagree are linked from the callout. +- **Synthesis pages link back to every concept and source they draw on.** + +## Index discipline + +`wiki/index.md` is regenerated, not hand-edited. Either: + +- Run `python scripts/update_index.py --vault .` after every ingest, OR +- Have the LLM rewrite the relevant section inline. + +The index groups pages by `category`, alphabetized by title. Each entry is one line with a wikilink, summary, and optional metadata. + +## Log discipline + +`wiki/log.md` is append-only. Every entry starts with a standardized header so `grep "^## \[" log.md | tail -5` returns the last 5 entries. + +``` +## [2026-04-10] ingest | Anthropic Monosemanticity +Added sources/monosemanticity.md. Updated concepts/sparse-autoencoder, +concepts/polysemanticity, entities/anthropic-interpretability-team. Flagged +contradiction with sources/distributed-representations on feature basis claim. +``` + +Valid ops: `ingest`, `query`, `lint`, `create`, `update`, `delete`, `note`. diff --git a/engineering/llm-wiki/scripts/append_log.py b/engineering/llm-wiki/scripts/append_log.py new file mode 100644 index 000000000..6725c4484 --- /dev/null +++ b/engineering/llm-wiki/scripts/append_log.py @@ -0,0 +1,134 @@ +#!/usr/bin/env python3 +""" +append_log.py — Append a standardized entry to wiki/log.md. + +The log is append-only and uses a consistent header so unix tools can parse it: + ## [YYYY-MM-DD] <op> | <title> + +A useful tip: if each entry starts with a consistent prefix, the log becomes +parseable with simple unix tools — `grep "^## \\[" log.md | tail -5` gives you +the last 5 entries. + +Usage: + python append_log.py --vault ~/vaults/research --op ingest --title "Anthropic Monosemanticity" + python append_log.py --vault . --op query --title "interpretability vs mechinterp" --detail "3 pages touched" + python append_log.py --vault . --op lint --title "weekly health check" --detail "2 contradictions" --json + +Valid ops: + ingest — a source was read and integrated into the wiki + query — a question was answered (filed back as a page) + lint — a health-check pass ran + create — a new page was created outside of an ingest + update — an existing page was updated outside of an ingest + delete — a page was removed + note — freeform note (contradictions flagged, thesis revisions, etc.) + +Exit codes: + 0 success + 1 invalid vault / missing log.md / invalid op / write failure +""" +from __future__ import annotations +import argparse +import datetime as dt +import json +import sys +from pathlib import Path + +VALID_OPS = {"ingest", "query", "lint", "create", "update", "delete", "note"} + + +def _error(message, as_json=False): + """Print an error and exit with code 1. Respects --json mode.""" + if as_json: + print(json.dumps({"status": "error", "message": message})) + else: + print(f"[error] {message}", file=sys.stderr) + sys.exit(1) + + +def validate_vault(vault): + """Return the log.md path or raise if vault is invalid.""" + if not vault.exists(): + raise FileNotFoundError(f"vault does not exist: {vault}") + log_path = vault / "wiki" / "log.md" + if not log_path.exists(): + raise FileNotFoundError(f"{log_path} does not exist — is this a vault?") + return log_path + + +def format_entry(op, title, detail): + """Build the standardized log entry string.""" + today = dt.date.today().isoformat() + header = f"## [{today}] {op} | {title}" + body = f"\n{detail}\n" if detail else "\n" + return today, header, f"\n{header}\n{body}" + + +def append_log(vault, op, title, detail, as_json=False): + """Append a standardized entry to wiki/log.md inside the vault.""" + if op not in VALID_OPS: + _error(f"unknown op '{op}'. Valid: {sorted(VALID_OPS)}", as_json) + + try: + log_path = validate_vault(vault) + except FileNotFoundError as e: + _error(str(e), as_json) + + today, header, entry_text = format_entry(op, title, detail) + + try: + with log_path.open("a", encoding="utf-8") as f: + f.write(entry_text) + except OSError as e: + _error(f"failed to write {log_path}: {e}", as_json) + + result = { + "status": "ok", + "log_path": str(log_path), + "date": today, + "op": op, + "title": title, + "header": header, + "detail": detail, + } + + if as_json: + print(json.dumps(result, indent=2)) + else: + print(f"[ok] appended to {log_path}") + print(f" {header}") + if detail: + print(f" detail: {detail}") + + return result + + +def main(): + p = argparse.ArgumentParser( + description="Append a standardized entry to wiki/log.md", + epilog="Format: ## [YYYY-MM-DD] <op> | <title>", + ) + p.add_argument("--vault", required=True, help="Vault root directory") + p.add_argument( + "--op", + required=True, + choices=sorted(VALID_OPS), + help="Operation type (ingest, query, lint, create, update, delete, note)", + ) + p.add_argument("--title", required=True, help="Short title for the entry") + p.add_argument("--detail", default=None, help="Optional detail text") + p.add_argument( + "--json", action="store_true", help="Emit result as JSON instead of human-readable" + ) + args = p.parse_args() + append_log( + Path(args.vault).expanduser().resolve(), + args.op, + args.title, + args.detail, + as_json=args.json, + ) + + +if __name__ == "__main__": + main() diff --git a/engineering/llm-wiki/scripts/export_marp.py b/engineering/llm-wiki/scripts/export_marp.py new file mode 100644 index 000000000..23c45ab34 --- /dev/null +++ b/engineering/llm-wiki/scripts/export_marp.py @@ -0,0 +1,146 @@ +#!/usr/bin/env python3 +""" +export_marp.py — Render a wiki page (or subtree) as a Marp slide deck. + +Marp is a Markdown-based slide format supported by an Obsidian plugin. This +script adds Marp frontmatter and converts `## H2` headings into slide breaks, +so any wiki page with H2 sections becomes a usable slide deck with zero +manual formatting. + +Usage: + python export_marp.py --vault . --page wiki/synthesis/interpretability-overview.md + python export_marp.py --vault . --page wiki/concepts/ --theme gaia --out slides/ +""" +from __future__ import annotations +import argparse +import json +import re +import sys +from pathlib import Path + +FRONTMATTER_RE = re.compile(r"^---\s*\n(.*?)\n---\s*\n", re.DOTALL) + +MARP_HEADER = """--- +marp: true +theme: {theme} +paginate: true +--- + +""" + + +def strip_frontmatter(text: str) -> str: + return FRONTMATTER_RE.sub("", text, count=1) + + +def to_marp(text: str, theme: str) -> str: + body = strip_frontmatter(text).strip() + # Turn each "## " into a new slide separator. + # First H1 → title slide. Subsequent H2 → slide breaks. + lines = body.splitlines() + out: list[str] = [] + seen_h1 = False + for line in lines: + if line.startswith("# ") and not seen_h1: + out.append(line) + out.append("") + seen_h1 = True + continue + if line.startswith("## "): + out.append("\n---\n") + out.append(line) + continue + out.append(line) + return MARP_HEADER.format(theme=theme) + "\n".join(out).strip() + "\n" + + +def render_one(src, out_path, theme, verbose=True): + """Render a single markdown page as a Marp slide deck.""" + try: + text = src.read_text(encoding="utf-8", errors="replace") + except OSError as e: + raise RuntimeError(f"failed to read {src}: {e}") + out_path.parent.mkdir(parents=True, exist_ok=True) + try: + out_path.write_text(to_marp(text, theme), encoding="utf-8") + except OSError as e: + raise RuntimeError(f"failed to write {out_path}: {e}") + if verbose: + print(f"[ok] {src.name} -> {out_path}") + return out_path + + +def _error(message, as_json=False): + if as_json: + print(json.dumps({"status": "error", "message": message})) + else: + print(f"[error] {message}", file=sys.stderr) + sys.exit(1) + + +def main(): + p = argparse.ArgumentParser( + description="Render a wiki page (or subtree) to a Marp slide deck.", + epilog="Marp is a Markdown-based slide format supported by an Obsidian plugin.", + ) + p.add_argument("--vault", required=True, help="Vault root directory") + p.add_argument( + "--page", + required=True, + help="Page or directory relative to the vault (e.g. wiki/synthesis/overview.md)", + ) + p.add_argument( + "--theme", default="default", choices=["default", "gaia", "uncover"], help="Marp theme" + ) + p.add_argument( + "--out", default="slides", help="Output directory relative to vault (default: slides)" + ) + p.add_argument( + "--json", action="store_true", help="Emit result as JSON instead of human-readable" + ) + args = p.parse_args() + + vault = Path(args.vault).expanduser().resolve() + if not vault.exists(): + _error(f"vault does not exist: {vault}", args.json) + + src = (vault / args.page).resolve() + if not src.exists(): + _error(f"page not found: {src}", args.json) + + out_root = vault / args.out + rendered = [] + + try: + if src.is_file(): + dest = out_root / src.name.replace(".md", ".marp.md") + render_one(src, dest, args.theme, verbose=not args.json) + rendered.append(str(dest.relative_to(vault))) + else: + for md in sorted(src.rglob("*.md")): + rel = md.relative_to(src) + dest = out_root / rel.with_suffix(".marp.md") + render_one(md, dest, args.theme, verbose=not args.json) + rendered.append(str(dest.relative_to(vault))) + except RuntimeError as e: + _error(str(e), args.json) + + if args.json: + print( + json.dumps( + { + "status": "ok", + "vault": str(vault), + "source": str(src.relative_to(vault)), + "theme": args.theme, + "output_dir": args.out, + "rendered_count": len(rendered), + "rendered": rendered, + }, + indent=2, + ) + ) + + +if __name__ == "__main__": + main() diff --git a/engineering/llm-wiki/scripts/graph_analyzer.py b/engineering/llm-wiki/scripts/graph_analyzer.py new file mode 100644 index 000000000..8ed6a78a3 --- /dev/null +++ b/engineering/llm-wiki/scripts/graph_analyzer.py @@ -0,0 +1,142 @@ +#!/usr/bin/env python3 +""" +graph_analyzer.py — Analyze the wikilink graph of an LLM Wiki vault. + +Reports hubs, orphans, bridges, and weakly-connected components so the LLM +knows where to focus cross-referencing work. + +Usage: + python graph_analyzer.py --vault ~/vaults/research + python graph_analyzer.py --vault . --json + python graph_analyzer.py --vault . --top 20 +""" +from __future__ import annotations +import argparse +import json +import re +import sys +from collections import defaultdict +from pathlib import Path + +WIKILINK_RE = re.compile(r"\[\[([^\]|#]+)(?:#[^\]|]*)?(?:\|[^\]]*)?\]\]") + + +def build_graph(vault: Path): + wiki = vault / "wiki" + if not wiki.exists(): + raise SystemExit(f"[error] {wiki} not found") + nodes: set[str] = set() + out: dict[str, set[str]] = defaultdict(set) + inb: dict[str, set[str]] = defaultdict(set) + stems: dict[str, str] = {} + + for md in wiki.rglob("*.md"): + rel = md.relative_to(wiki) + if rel.name in {"index.md", "log.md"}: + continue + if any(part.startswith(".") for part in rel.parts): + continue + key = str(rel).replace("\\", "/")[:-3] + nodes.add(key) + stems[Path(key).name] = key + + for md in wiki.rglob("*.md"): + rel = md.relative_to(wiki) + if rel.name in {"index.md", "log.md"} or any(p.startswith(".") for p in rel.parts): + continue + key = str(rel).replace("\\", "/")[:-3] + text = md.read_text(encoding="utf-8", errors="replace") + for m in WIKILINK_RE.finditer(text): + target = m.group(1).strip() + if target.endswith(".md"): + target = target[:-3] + if target in nodes: + out[key].add(target) + inb[target].add(key) + elif Path(target).name in stems: + resolved = stems[Path(target).name] + out[key].add(resolved) + inb[resolved].add(key) + + return nodes, out, inb + + +def connected_components(nodes: set[str], out: dict[str, set[str]], inb: dict[str, set[str]]): + adj: dict[str, set[str]] = defaultdict(set) + for n in nodes: + adj[n] |= out.get(n, set()) + adj[n] |= inb.get(n, set()) + seen: set[str] = set() + components: list[set[str]] = [] + for n in nodes: + if n in seen: + continue + stack = [n] + comp: set[str] = set() + while stack: + v = stack.pop() + if v in seen: + continue + seen.add(v) + comp.add(v) + stack.extend(adj[v] - seen) + components.append(comp) + components.sort(key=len, reverse=True) + return components + + +def analyze(vault: Path, top: int) -> dict: + nodes, out, inb = build_graph(vault) + hubs_out = sorted(nodes, key=lambda n: len(out.get(n, set())), reverse=True)[:top] + hubs_in = sorted(nodes, key=lambda n: len(inb.get(n, set())), reverse=True)[:top] + orphans = sorted(n for n in nodes if not inb.get(n)) + sinks = sorted(n for n in nodes if not out.get(n)) + comps = connected_components(nodes, out, inb) + return { + "total_pages": len(nodes), + "total_edges": sum(len(v) for v in out.values()), + "top_outbound_hubs": [{"page": h, "outbound": len(out.get(h, set()))} for h in hubs_out], + "top_inbound_hubs": [{"page": h, "inbound": len(inb.get(h, set()))} for h in hubs_in], + "orphans": orphans, + "sinks": sinks, + "components": [ + {"size": len(c), "sample": sorted(c)[:5]} for c in comps[:10] + ], + "component_count": len(comps), + } + + +def main() -> None: + p = argparse.ArgumentParser(description="Analyze the wikilink graph of an LLM Wiki vault") + p.add_argument("--vault", required=True) + p.add_argument("--top", type=int, default=10) + p.add_argument("--json", action="store_true") + args = p.parse_args() + r = analyze(Path(args.vault).expanduser().resolve(), args.top) + + if args.json: + print(json.dumps(r, indent=2, default=list)) + return + + print(f"LLM Wiki graph — {r['total_pages']} pages, {r['total_edges']} links") + print(f"Connected components: {r['component_count']}") + print() + print("Top outbound hubs (pages that link to many others):") + for h in r["top_outbound_hubs"]: + print(f" - {h['page']} ({h['outbound']} out)") + print() + print("Top inbound hubs (pages many others link TO):") + for h in r["top_inbound_hubs"]: + print(f" - {h['page']} ({h['inbound']} in)") + print() + print(f"Orphans (no inbound): {len(r['orphans'])}") + for o in r["orphans"][:10]: + print(f" - {o}") + print() + print(f"Sinks (no outbound): {len(r['sinks'])}") + for s in r["sinks"][:10]: + print(f" - {s}") + + +if __name__ == "__main__": + main() diff --git a/engineering/llm-wiki/scripts/ingest_source.py b/engineering/llm-wiki/scripts/ingest_source.py new file mode 100644 index 000000000..8309cb430 --- /dev/null +++ b/engineering/llm-wiki/scripts/ingest_source.py @@ -0,0 +1,177 @@ +#!/usr/bin/env python3 +""" +ingest_source.py — Prepare a source for LLM ingestion. + +This is a *helper* — it does not call an LLM. It extracts text and metadata from +a source file and emits a JSON brief the LLM (via the /wiki-ingest command or +the wiki-ingestor sub-agent) can read, discuss with the user, and use to update +the wiki. + +Supported source types (stdlib only): + .md .txt .html .htm .json .csv + +For .pdf and binary formats, install optional readers yourself, or let the LLM +read the file directly via its Read tool. + +Usage: + python ingest_source.py --vault ~/vaults/research --source raw/paper.md + python ingest_source.py --vault . --source raw/article.html --json + +Output (JSON): + { + "source_path": "raw/paper.md", + "relative": "raw/paper.md", + "bytes": 12345, + "sha256": "...", + "ext": ".md", + "title_guess": "Monosemanticity", + "word_count": 8432, + "preview": "First 1200 chars...", + "existing_summary_page": "wiki/sources/monosemanticity.md" | null, + "suggested_summary_path": "wiki/sources/monosemanticity.md" + } +""" +from __future__ import annotations +import argparse +import hashlib +import html.parser +import json +import re +import sys +from pathlib import Path + +PREVIEW_CHARS = 1200 +SLUG_RE = re.compile(r"[^a-z0-9]+") + + +def slugify(text: str) -> str: + text = text.lower().strip() + text = SLUG_RE.sub("-", text).strip("-") + return text[:60] or "untitled" + + +class _HTMLTextExtractor(html.parser.HTMLParser): + def __init__(self) -> None: + super().__init__() + self.parts: list[str] = [] + self.title: str | None = None + self._in_title = False + self._skip = False + + def handle_starttag(self, tag: str, attrs: list[tuple[str, str | None]]) -> None: + if tag in {"script", "style"}: + self._skip = True + if tag == "title": + self._in_title = True + + def handle_endtag(self, tag: str) -> None: + if tag in {"script", "style"}: + self._skip = False + if tag == "title": + self._in_title = False + + def handle_data(self, data: str) -> None: + if self._skip: + return + if self._in_title and self.title is None: + self.title = data.strip() or None + else: + text = data.strip() + if text: + self.parts.append(text) + + def text(self) -> str: + return "\n".join(self.parts) + + +def extract(path: Path) -> tuple[str, str | None]: + ext = path.suffix.lower() + data = path.read_bytes() + if ext in {".md", ".txt"}: + text = data.decode("utf-8", errors="replace") + title = None + for line in text.splitlines()[:20]: + if line.startswith("# "): + title = line[2:].strip() + break + return text, title + if ext in {".html", ".htm"}: + parser = _HTMLTextExtractor() + try: + parser.feed(data.decode("utf-8", errors="replace")) + except Exception: + pass + return parser.text(), parser.title + if ext == ".json": + try: + obj = json.loads(data.decode("utf-8", errors="replace")) + return json.dumps(obj, indent=2)[:100000], None + except Exception: + return data.decode("utf-8", errors="replace"), None + if ext == ".csv": + text = data.decode("utf-8", errors="replace") + head = "\n".join(text.splitlines()[:50]) + return head, None + # Unknown: attempt utf-8 decode, let the LLM handle it + try: + return data.decode("utf-8", errors="replace"), None + except Exception: + return "", None + + +def main() -> None: + p = argparse.ArgumentParser(description="Prepare a source for LLM ingestion.") + p.add_argument("--vault", required=True) + p.add_argument("--source", required=True, help="Path to the source file (inside raw/)") + p.add_argument("--json", action="store_true", help="Emit JSON only") + args = p.parse_args() + + vault = Path(args.vault).expanduser().resolve() + src = Path(args.source).expanduser().resolve() + if not src.exists(): + print(f"[error] source not found: {src}", file=sys.stderr) + sys.exit(1) + + try: + rel = src.relative_to(vault) + except ValueError: + rel = src + + text, title = extract(src) + title_guess = title or src.stem.replace("-", " ").replace("_", " ").title() + slug = slugify(title_guess) + suggested = f"wiki/sources/{slug}.md" + existing = vault / suggested + existing_path = str(suggested) if existing.exists() else None + + brief = { + "source_path": str(src), + "relative": str(rel).replace("\\", "/"), + "bytes": src.stat().st_size, + "sha256": hashlib.sha256(src.read_bytes()).hexdigest()[:16], + "ext": src.suffix.lower(), + "title_guess": title_guess, + "word_count": len(text.split()), + "preview": text[:PREVIEW_CHARS], + "existing_summary_page": existing_path, + "suggested_summary_path": suggested, + } + + if args.json: + print(json.dumps(brief, indent=2, ensure_ascii=False)) + else: + print(f"Source: {brief['source_path']}") + print(f"Title (guess): {brief['title_guess']}") + print(f"Size: {brief['bytes']} bytes ({brief['word_count']} words)") + print(f"SHA256 (short): {brief['sha256']}") + print(f"Suggested page: {brief['suggested_summary_path']}") + if existing_path: + print(f"EXISTING PAGE: {existing_path} ← re-ingest / merge mode") + print() + print("--- preview ---") + print(brief["preview"]) + print("--- /preview ---") + + +if __name__ == "__main__": + main() diff --git a/engineering/llm-wiki/scripts/init_vault.py b/engineering/llm-wiki/scripts/init_vault.py new file mode 100644 index 000000000..02656dcd3 --- /dev/null +++ b/engineering/llm-wiki/scripts/init_vault.py @@ -0,0 +1,219 @@ +#!/usr/bin/env python3 +""" +init_vault.py — Bootstrap an LLM Wiki vault. + +Creates the three-layer structure (raw/, wiki/, schema files) and seeds it with +starter templates for CLAUDE.md, AGENTS.md, index.md, log.md, and page templates. + +Usage: + python init_vault.py --path ~/vaults/research --topic "LLM interpretability" + python init_vault.py --path ./my-wiki --topic "Book: The Power Broker" --tool codex + +The --tool flag controls which schema file(s) to install: + claude-code → CLAUDE.md (default) + codex → AGENTS.md + cursor → AGENTS.md + .cursorrules + antigravity → AGENTS.md + all → CLAUDE.md + AGENTS.md + .cursorrules (recommended for multi-tool) +""" +from __future__ import annotations +import argparse +import datetime as dt +import json +import sys +from pathlib import Path + +SCRIPT_DIR = Path(__file__).resolve().parent +PLUGIN_DIR = SCRIPT_DIR.parent +ASSETS_DIR = PLUGIN_DIR / "assets" + +VAULT_DIRS = [ + "raw", + "raw/assets", + "wiki", + "wiki/entities", + "wiki/concepts", + "wiki/sources", + "wiki/comparisons", + "wiki/synthesis", +] + +TOOL_FILES = { + "claude-code": ["CLAUDE.md.template:CLAUDE.md"], + "codex": ["AGENTS.md.template:AGENTS.md"], + "cursor": ["AGENTS.md.template:AGENTS.md", "cursorrules.template:.cursorrules"], + "antigravity": ["AGENTS.md.template:AGENTS.md"], + "opencode": ["AGENTS.md.template:AGENTS.md"], + "gemini-cli": ["AGENTS.md.template:AGENTS.md"], + "all": [ + "CLAUDE.md.template:CLAUDE.md", + "AGENTS.md.template:AGENTS.md", + "cursorrules.template:.cursorrules", + ], +} + + +def render_template(src, dest, variables): + """Render a template file with {{VAR}} substitutions to dest.""" + if not src.exists(): + print(f"[warn] template missing: {src}", file=sys.stderr) + return False + try: + text = src.read_text(encoding="utf-8") + except OSError as e: + print(f"[warn] could not read {src}: {e}", file=sys.stderr) + return False + for key, value in variables.items(): + text = text.replace("{{" + key + "}}", value) + try: + dest.write_text(text, encoding="utf-8") + except OSError as e: + print(f"[warn] could not write {dest}: {e}", file=sys.stderr) + return False + return True + + +def _error(message, as_json=False): + if as_json: + print(json.dumps({"status": "error", "message": message})) + else: + print(f"[error] {message}", file=sys.stderr) + sys.exit(1) + + +def init_vault(vault_path, topic, tool, force, as_json=False): + """Bootstrap a new LLM Wiki vault at vault_path.""" + if vault_path.exists() and any(vault_path.iterdir()) and not force: + _error(f"{vault_path} is not empty. Use --force to overwrite.", as_json) + + try: + vault_path.mkdir(parents=True, exist_ok=True) + for d in VAULT_DIRS: + (vault_path / d).mkdir(parents=True, exist_ok=True) + except OSError as e: + _error(f"failed to create vault structure: {e}", as_json) + + today = dt.date.today().isoformat() + variables = { + "TOPIC": topic, + "DATE": today, + "VAULT_NAME": vault_path.name, + } + + installed_files = [] + + # Schema files (CLAUDE.md / AGENTS.md / .cursorrules) + for spec in TOOL_FILES.get(tool, TOOL_FILES["claude-code"]): + src_name, dest_name = spec.split(":", 1) + dest = vault_path / dest_name + if render_template(ASSETS_DIR / src_name, dest, variables): + installed_files.append(dest_name) + + # Index + log seeds + for spec in [ + ("index.md.template", vault_path / "wiki" / "index.md"), + ("log.md.template", vault_path / "wiki" / "log.md"), + ]: + if render_template(ASSETS_DIR / spec[0], spec[1], variables): + installed_files.append(str(spec[1].relative_to(vault_path))) + + # Page templates (reference copies inside the vault) + tmpl_dest = vault_path / "wiki" / ".templates" + tmpl_dest.mkdir(exist_ok=True) + src_tmpl = ASSETS_DIR / "page-templates" + template_count = 0 + if src_tmpl.exists(): + for f in src_tmpl.iterdir(): + if f.is_file(): + try: + (tmpl_dest / f.name).write_text( + f.read_text(encoding="utf-8"), encoding="utf-8" + ) + template_count += 1 + except OSError as e: + print(f"[warn] failed to copy template {f.name}: {e}", file=sys.stderr) + + # .gitignore — exclude Obsidian workspace files + gitignore = vault_path / ".gitignore" + gitignore.write_text( + "\n".join([".obsidian/workspace*", ".obsidian/cache", ".DS_Store", ""]), + encoding="utf-8", + ) + + result = { + "status": "ok", + "vault_path": str(vault_path), + "topic": topic, + "tool": tool, + "date": today, + "installed_files": installed_files, + "page_templates_copied": template_count, + "layers": { + "raw": "your sources — immutable", + "wiki": "LLM-maintained knowledge base", + "index": "wiki/index.md", + "log": "wiki/log.md", + }, + "next_steps": [ + "Open the vault in Obsidian", + "Drop a source into raw/", + "Run /wiki-ingest <path> in your LLM CLI", + ], + } + + if as_json: + print(json.dumps(result, indent=2)) + return result + + print(f"[ok] Initialized LLM Wiki vault at: {vault_path}") + print(f" Topic: {topic}") + print(f" Tool: {tool}") + print(f" Installed: {', '.join(installed_files)}") + print(f" Page templates copied: {template_count}") + print(" Layers:") + print(" raw/ (your sources — immutable)") + print(" wiki/ (LLM-maintained knowledge base)") + print(" wiki/index.md (catalog)") + print(" wiki/log.md (timeline)") + print() + print("Next steps:") + print(" 1. Open the vault in Obsidian") + print(" 2. Drop a source into raw/") + print(" 3. Run /wiki-ingest <path> in your LLM CLI") + return result + + +def main(): + p = argparse.ArgumentParser( + description="Initialize an LLM Wiki vault — the three-layer structure (raw/, wiki/, schema) Karpathy describes in the LLM Wiki gist.", + ) + p.add_argument("--path", required=True, help="Vault directory to create/initialize") + p.add_argument( + "--topic", + required=True, + help="Short description of what this wiki is about (e.g. 'LLM interpretability')", + ) + p.add_argument( + "--tool", + default="all", + choices=sorted(TOOL_FILES.keys()), + help="Which schema file(s) to install (default: all)", + ) + p.add_argument( + "--force", action="store_true", help="Overwrite non-empty target directory" + ) + p.add_argument( + "--json", action="store_true", help="Emit result as JSON instead of human-readable" + ) + args = p.parse_args() + init_vault( + Path(args.path).expanduser().resolve(), + args.topic, + args.tool, + args.force, + as_json=args.json, + ) + + +if __name__ == "__main__": + main() diff --git a/engineering/llm-wiki/scripts/lint_wiki.py b/engineering/llm-wiki/scripts/lint_wiki.py new file mode 100644 index 000000000..0b9d963dc --- /dev/null +++ b/engineering/llm-wiki/scripts/lint_wiki.py @@ -0,0 +1,202 @@ +#!/usr/bin/env python3 +""" +lint_wiki.py — Health-check an LLM Wiki vault. + +Surfaces structural problems the LLM-as-wiki-maintainer should fix: + + - orphans: pages with zero inbound [[wikilinks]] + - broken_links: [[wikilinks]] pointing to non-existent pages + - stale: pages whose `updated:` frontmatter is older than --stale-days + - missing_fm: pages without a title/category/summary in frontmatter + - duplicate_titles: two or more pages sharing the same title + - log_gaps: no log entry in the last --log-gap-days + +Usage: + python lint_wiki.py --vault ~/vaults/research + python lint_wiki.py --vault . --stale-days 60 --json +""" +from __future__ import annotations +import argparse +import datetime as dt +import json +import re +import sys +from collections import defaultdict +from pathlib import Path + +FRONTMATTER_RE = re.compile(r"^---\s*\n(.*?)\n---\s*\n", re.DOTALL) +WIKILINK_RE = re.compile(r"\[\[([^\]|#]+)(?:#[^\]|]*)?(?:\|[^\]]*)?\]\]") +LOG_ENTRY_RE = re.compile(r"^## \[(\d{4}-\d{2}-\d{2})\]", re.MULTILINE) + + +def parse_frontmatter(text: str) -> dict[str, str]: + m = FRONTMATTER_RE.match(text) + if not m: + return {} + fm: dict[str, str] = {} + for line in m.group(1).splitlines(): + if ":" in line and not line.lstrip().startswith("#"): + k, _, v = line.partition(":") + fm[k.strip()] = v.strip().strip("'\"") + return fm + + +def scan(vault: Path, stale_days: int, log_gap_days: int) -> dict: + wiki = vault / "wiki" + if not wiki.exists(): + raise SystemExit(f"[error] {wiki} not found") + + pages: dict[str, dict] = {} + inbound: dict[str, set[str]] = defaultdict(set) + outbound: dict[str, set[str]] = defaultdict(set) + + for md in wiki.rglob("*.md"): + rel = md.relative_to(wiki) + if rel.name in {"index.md", "log.md"}: + continue + if any(part.startswith(".") for part in rel.parts): + continue + key = str(rel).replace("\\", "/")[:-3] # strip .md + text = md.read_text(encoding="utf-8", errors="replace") + fm = parse_frontmatter(text) + pages[key] = {"path": key + ".md", "fm": fm, "text": text} + + # Build link graph + stems = {Path(k).name: k for k in pages} + for key, page in pages.items(): + for m in WIKILINK_RE.finditer(page["text"]): + target = m.group(1).strip() + # Normalize: strip .md, try full path match first, then stem + if target.endswith(".md"): + target = target[:-3] + if target in pages: + outbound[key].add(target) + inbound[target].add(key) + elif Path(target).name in stems: + resolved = stems[Path(target).name] + outbound[key].add(resolved) + inbound[resolved].add(key) + else: + outbound[key].add(f"__BROKEN__:{target}") + + today = dt.date.today() + stale_cutoff = today - dt.timedelta(days=stale_days) + + orphans = sorted(k for k in pages if not inbound.get(k)) + broken_links: list[tuple[str, str]] = [] + for src, targets in outbound.items(): + for t in targets: + if t.startswith("__BROKEN__:"): + broken_links.append((src, t.split(":", 1)[1])) + broken_links.sort() + + stale: list[tuple[str, str]] = [] + missing_fm: list[str] = [] + titles: dict[str, list[str]] = defaultdict(list) + for key, page in pages.items(): + fm = page["fm"] + title = fm.get("title") or Path(key).name + titles[title].append(key) + required = {"title", "category", "summary"} + if not required.issubset(fm.keys()): + missing_fm.append(key) + updated = fm.get("updated") + if updated: + try: + d = dt.date.fromisoformat(updated) + if d < stale_cutoff: + stale.append((key, updated)) + except ValueError: + pass + duplicate_titles = {t: ks for t, ks in titles.items() if len(ks) > 1} + + # Log gap check + log_path = wiki / "log.md" + log_gap = None + if log_path.exists(): + log_text = log_path.read_text(encoding="utf-8", errors="replace") + dates = [dt.date.fromisoformat(m) for m in LOG_ENTRY_RE.findall(log_text)] + if dates: + last = max(dates) + gap = (today - last).days + if gap > log_gap_days: + log_gap = {"last_entry": last.isoformat(), "days_ago": gap} + else: + log_gap = {"last_entry": None, "days_ago": None} + + return { + "vault": str(vault), + "total_pages": len(pages), + "orphans": orphans, + "broken_links": broken_links, + "stale": stale, + "missing_frontmatter": sorted(missing_fm), + "duplicate_titles": duplicate_titles, + "log_gap": log_gap, + } + + +def print_report(r: dict) -> None: + print(f"LLM Wiki health check — {r['vault']}") + print(f"Total pages: {r['total_pages']}") + print() + + def header(label: str, count: int) -> None: + sym = "OK" if count == 0 else "WARN" + print(f"[{sym}] {label}: {count}") + + header("orphan pages", len(r["orphans"])) + for p in r["orphans"][:20]: + print(f" - {p}") + if len(r["orphans"]) > 20: + print(f" ... and {len(r['orphans']) - 20} more") + print() + + header("broken wikilinks", len(r["broken_links"])) + for src, tgt in r["broken_links"][:20]: + print(f" - {src} -> [[{tgt}]]") + print() + + header("stale pages", len(r["stale"])) + for p, d in r["stale"][:20]: + print(f" - {p} (updated {d})") + print() + + header("pages missing frontmatter", len(r["missing_frontmatter"])) + for p in r["missing_frontmatter"][:20]: + print(f" - {p}") + print() + + header("duplicate titles", len(r["duplicate_titles"])) + for title, keys in list(r["duplicate_titles"].items())[:10]: + print(f" - '{title}': {keys}") + print() + + gap = r["log_gap"] + if gap: + print(f"[WARN] log gap: last entry {gap['last_entry']} ({gap['days_ago']} days ago)") + else: + print("[OK] log gap: recent") + + +def main() -> None: + p = argparse.ArgumentParser(description="Lint an LLM Wiki vault") + p.add_argument("--vault", required=True) + p.add_argument("--stale-days", type=int, default=90) + p.add_argument("--log-gap-days", type=int, default=14) + p.add_argument("--json", action="store_true") + args = p.parse_args() + + report = scan( + Path(args.vault).expanduser().resolve(), + stale_days=args.stale_days, + log_gap_days=args.log_gap_days, + ) + if args.json: + print(json.dumps(report, indent=2, default=list)) + else: + print_report(report) + + +if __name__ == "__main__": + main() diff --git a/engineering/llm-wiki/scripts/update_index.py b/engineering/llm-wiki/scripts/update_index.py new file mode 100644 index 000000000..991141eb9 --- /dev/null +++ b/engineering/llm-wiki/scripts/update_index.py @@ -0,0 +1,204 @@ +#!/usr/bin/env python3 +""" +update_index.py — Regenerate wiki/index.md from the frontmatter of every wiki page. + +The index is content-oriented: a catalog organized by category (entities, concepts, +sources, comparisons, synthesis), with one-line summaries read from each page's +YAML frontmatter. + +Frontmatter convention (per page): + --- + title: Monosemanticity + category: concept # entity | concept | source | comparison | synthesis + summary: Single-feature interpretability hypothesis from Anthropic's sparse autoencoder work + tags: [interpretability, sparse-autoencoders] + sources: 2 # optional — count of sources referencing this page + updated: 2026-04-10 + --- + +Usage: + python update_index.py --vault ~/vaults/research + python update_index.py --vault . --dry-run +""" +from __future__ import annotations +import argparse +import datetime as dt +import json +import re +import sys +from collections import defaultdict +from pathlib import Path + +FRONTMATTER_RE = re.compile(r"^---\s*\n(.*?)\n---\s*\n", re.DOTALL) +CATEGORY_ORDER = ["synthesis", "concept", "entity", "source", "comparison", "other"] +CATEGORY_DIRS = { + "entities": "entity", + "concepts": "concept", + "sources": "source", + "comparisons": "comparison", + "synthesis": "synthesis", +} + + +def parse_frontmatter(text: str) -> dict[str, str]: + m = FRONTMATTER_RE.match(text) + if not m: + return {} + raw = m.group(1) + fm: dict[str, str] = {} + for line in raw.splitlines(): + if ":" in line and not line.lstrip().startswith("#"): + key, _, value = line.partition(":") + fm[key.strip()] = value.strip().strip("'\"") + return fm + + +def infer_title(path: Path, fm: dict[str, str]) -> str: + if "title" in fm: + return fm["title"] + return path.stem.replace("-", " ").replace("_", " ").title() + + +def scan_wiki(vault: Path) -> dict[str, list[dict]]: + wiki = vault / "wiki" + if not wiki.exists(): + print(f"[error] {wiki} not found", file=sys.stderr) + sys.exit(1) + + pages: dict[str, list[dict]] = defaultdict(list) + for md in sorted(wiki.rglob("*.md")): + # Skip index, log, and template files + rel = md.relative_to(wiki) + if rel.name in {"index.md", "log.md"}: + continue + if any(part.startswith(".") for part in rel.parts): + continue + + text = md.read_text(encoding="utf-8", errors="replace") + fm = parse_frontmatter(text) + + # Category: prefer frontmatter, fall back to folder name + category = fm.get("category") + if not category and len(rel.parts) > 1: + folder = rel.parts[0] + category = CATEGORY_DIRS.get(folder, "other") + category = category or "other" + + pages[category].append( + { + "path": str(rel).replace("\\", "/"), + "title": infer_title(md, fm), + "summary": fm.get("summary", ""), + "tags": fm.get("tags", ""), + "sources": fm.get("sources", ""), + "updated": fm.get("updated", ""), + } + ) + + # Sort each category by title + for cat in pages: + pages[cat].sort(key=lambda p: p["title"].lower()) + return pages + + +def render_index(pages: dict[str, list[dict]], vault_name: str) -> str: + today = dt.date.today().isoformat() + total = sum(len(v) for v in pages.values()) + + lines = [ + f"# Index — {vault_name}", + "", + f"_Auto-generated {today} • {total} pages_", + "", + "> Content-oriented catalog of every page in `wiki/`. Updated by", + "> `scripts/update_index.py` or during `/wiki-ingest`. Answer queries", + "> by reading this file first, then drilling into relevant pages.", + "", + ] + + for cat in CATEGORY_ORDER: + entries = pages.get(cat, []) + if not entries: + continue + lines.append(f"## {cat.capitalize()} ({len(entries)})") + lines.append("") + for e in entries: + summary = f" — {e['summary']}" if e["summary"] else "" + link = f"[[{e['path'][:-3]}|{e['title']}]]" # Obsidian wikilink, strip .md + meta = [] + if e["sources"]: + meta.append(f"{e['sources']} sources") + if e["updated"]: + meta.append(f"upd {e['updated']}") + meta_str = f" _({' · '.join(meta)})_" if meta else "" + lines.append(f"- {link}{summary}{meta_str}") + lines.append("") + + return "\n".join(lines) + + +def main(): + p = argparse.ArgumentParser( + description="Regenerate wiki/index.md from every wiki page's YAML frontmatter.", + epilog="The index is organized by category (synthesis, concept, entity, source, comparison).", + ) + p.add_argument("--vault", required=True, help="Vault root directory") + p.add_argument( + "--dry-run", action="store_true", help="Print to stdout instead of writing" + ) + p.add_argument( + "--json", + action="store_true", + help="Emit a JSON summary of the regeneration result", + ) + args = p.parse_args() + + try: + vault = Path(args.vault).expanduser().resolve() + pages = scan_wiki(vault) + content = render_index(pages, vault.name) + except SystemExit: + raise + except Exception as e: + if args.json: + print(json.dumps({"status": "error", "message": str(e)})) + else: + print(f"[error] {e}", file=sys.stderr) + sys.exit(1) + + total = sum(len(v) for v in pages.values()) + summary = { + "status": "ok", + "vault": str(vault), + "total_pages": total, + "by_category": {k: len(v) for k, v in pages.items()}, + "dry_run": args.dry_run, + } + + if args.dry_run: + if args.json: + summary["content_preview"] = content[:500] + print(json.dumps(summary, indent=2)) + else: + print(content) + return + + index_path = vault / "wiki" / "index.md" + try: + index_path.write_text(content, encoding="utf-8") + except OSError as e: + if args.json: + print(json.dumps({"status": "error", "message": f"failed to write {index_path}: {e}"})) + else: + print(f"[error] failed to write {index_path}: {e}", file=sys.stderr) + sys.exit(1) + + summary["index_path"] = str(index_path) + if args.json: + print(json.dumps(summary, indent=2)) + else: + print(f"[ok] wrote {index_path} ({total} pages)") + + +if __name__ == "__main__": + main() diff --git a/engineering/llm-wiki/scripts/wiki_search.py b/engineering/llm-wiki/scripts/wiki_search.py new file mode 100644 index 000000000..1bb0a9fc4 --- /dev/null +++ b/engineering/llm-wiki/scripts/wiki_search.py @@ -0,0 +1,145 @@ +#!/usr/bin/env python3 +""" +wiki_search.py — BM25 search over a wiki vault. + +Standard library only. Works as a fallback when `index.md` alone isn't enough +(e.g. you want to find which pages mention a specific term the LLM hasn't yet +cross-referenced). For larger vaults, pair this with an external tool like +qmd (https://github.com/tobi/qmd) for hybrid/vector search. + +Usage: + python wiki_search.py --vault ~/vaults/research --query "sparse autoencoder" + python wiki_search.py --vault . --query "monosemanticity" --limit 5 --json +""" +from __future__ import annotations +import argparse +import json +import math +import re +import sys +from collections import Counter, defaultdict +from pathlib import Path + +TOKEN_RE = re.compile(r"[a-zA-Z0-9][a-zA-Z0-9_\-']+") +STOPWORDS = { + "the", "a", "an", "and", "or", "but", "if", "then", "so", "to", "of", "in", + "on", "at", "for", "by", "with", "from", "is", "are", "was", "were", "be", + "been", "being", "this", "that", "these", "those", "it", "its", "as", "we", + "you", "they", "their", "our", "us", "i", "not", "no", "yes", "do", "does", + "did", "will", "would", "can", "could", "should", "about", "into", "than", + "out", "up", "down", "over", "under", "also", +} + + +def tokenize(text: str) -> list[str]: + return [ + t.lower() + for t in TOKEN_RE.findall(text) + if t.lower() not in STOPWORDS and len(t) > 1 + ] + + +def load_docs(vault: Path) -> list[dict]: + wiki = vault / "wiki" + if not wiki.exists(): + raise SystemExit(f"[error] {wiki} not found") + docs = [] + for md in sorted(wiki.rglob("*.md")): + rel = md.relative_to(wiki) + if rel.name in {"index.md", "log.md"}: + continue + if any(part.startswith(".") for part in rel.parts): + continue + text = md.read_text(encoding="utf-8", errors="replace") + tokens = tokenize(text) + docs.append( + { + "path": str(rel).replace("\\", "/"), + "text": text, + "tokens": tokens, + "tf": Counter(tokens), + "len": len(tokens), + } + ) + return docs + + +def bm25_scores( + docs: list[dict], query: list[str], k1: float = 1.5, b: float = 0.75 +) -> list[tuple[int, float]]: + N = len(docs) + if N == 0: + return [] + avgdl = sum(d["len"] for d in docs) / N or 1 + df: dict[str, int] = defaultdict(int) + for d in docs: + for term in set(d["tokens"]): + df[term] += 1 + idf = { + term: math.log(1 + (N - df_t + 0.5) / (df_t + 0.5)) + for term, df_t in df.items() + } + + scores: list[tuple[int, float]] = [] + for i, d in enumerate(docs): + score = 0.0 + for term in query: + if term not in d["tf"]: + continue + tf = d["tf"][term] + denom = tf + k1 * (1 - b + b * d["len"] / avgdl) + score += idf.get(term, 0.0) * (tf * (k1 + 1)) / (denom or 1) + if score > 0: + scores.append((i, score)) + scores.sort(key=lambda x: x[1], reverse=True) + return scores + + +def snippet(text: str, query: list[str], width: int = 220) -> str: + lower = text.lower() + for term in query: + idx = lower.find(term) + if idx >= 0: + start = max(0, idx - width // 3) + end = min(len(text), start + width) + s = text[start:end].replace("\n", " ") + return ("…" if start > 0 else "") + s + ("…" if end < len(text) else "") + return text[:width].replace("\n", " ") + ("…" if len(text) > width else "") + + +def main() -> None: + p = argparse.ArgumentParser(description="BM25 search over an LLM Wiki vault") + p.add_argument("--vault", required=True) + p.add_argument("--query", required=True) + p.add_argument("--limit", type=int, default=10) + p.add_argument("--json", action="store_true") + args = p.parse_args() + + docs = load_docs(Path(args.vault).expanduser().resolve()) + qtokens = tokenize(args.query) + if not qtokens: + print("[error] empty query after tokenization", file=sys.stderr) + sys.exit(1) + + scored = bm25_scores(docs, qtokens)[: args.limit] + hits = [] + for i, s in scored: + d = docs[i] + hits.append( + {"path": d["path"], "score": round(s, 3), "snippet": snippet(d["text"], qtokens)} + ) + + if args.json: + print(json.dumps({"query": args.query, "hits": hits}, indent=2, ensure_ascii=False)) + else: + if not hits: + print(f"No matches for: {args.query}") + return + print(f"Query: {args.query} ({len(hits)} hits)") + for h in hits: + print(f"\n [{h['score']}] {h['path']}") + print(f" {h['snippet']}") + + +if __name__ == "__main__": + main() diff --git a/mkdocs.yml b/mkdocs.yml index 1832f97da..43f0828e8 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -417,3 +417,8 @@ nav: - "/tech-debt": commands/tech-debt.md - "/user-story": commands/user-story.md - "/google-workspace": commands/google-workspace.md + - "/wiki-init": commands/wiki-init.md + - "/wiki-ingest": commands/wiki-ingest.md + - "/wiki-query": commands/wiki-query.md + - "/wiki-lint": commands/wiki-lint.md + - "/wiki-log": commands/wiki-log.md