AGENTS.md - Personal Knowledge Base Schema
April 6, 2026 · View on GitHub
Adapted from Andrej Karpathy's LLM Knowledge Base architecture. Instead of ingesting external articles, this system compiles knowledge from your own AI conversations.
The Compiler Analogy
daily/ = source code (your conversations - the raw material)
LLM = compiler (extracts and organizes knowledge)
knowledge/ = executable (structured, queryable knowledge base)
lint = test suite (health checks for consistency)
queries = runtime (using the knowledge)
You don't manually organize your knowledge. You have conversations, and the LLM handles the synthesis, cross-referencing, and maintenance.
Architecture
Layer 1: daily/ - Conversation Logs (Immutable Source)
Daily logs capture what happened in your AI coding sessions. These are the "raw sources" - append-only, never edited after the fact.
daily/
├── 2026-04-01.md
├── 2026-04-02.md
├── ...
Each file follows this format:
# Daily Log: YYYY-MM-DD
## Sessions
### Session (HH:MM) - Brief Title
**Context:** What the user was working on.
**Key Exchanges:**
- User asked about X, assistant explained Y
- Decided to use Z approach because...
- Discovered that W doesn't work when...
**Decisions Made:**
- Chose library X over Y because...
- Architecture: went with pattern Z
**Lessons Learned:**
- Always do X before Y to avoid...
- The gotcha with Z is that...
**Action Items:**
- [ ] Follow up on X
- [ ] Refactor Y when time permits
Layer 2: knowledge/ - Compiled Knowledge (LLM-Owned)
The LLM owns this directory entirely. Humans read it but rarely edit it directly.
knowledge/
├── index.md # Master catalog - every article with one-line summary
├── log.md # Append-only chronological build log
├── concepts/ # Atomic knowledge articles
├── connections/ # Cross-cutting insights linking 2+ concepts
└── qa/ # Filed query answers (compounding knowledge)
Layer 3: This File (AGENTS.md)
The schema that tells the LLM how to compile and maintain the knowledge base. This is the "compiler specification."
Structural Files
knowledge/index.md - Master Catalog
A table listing every knowledge article. This is the primary retrieval mechanism - the LLM reads this FIRST when answering any query, then selects relevant articles to read in full.
Format:
# Knowledge Base Index
| Article | Summary | Compiled From | Updated |
|---------|---------|---------------|---------|
| [[concepts/supabase-auth]] | Row-level security patterns and JWT gotchas | daily/2026-04-02.md | 2026-04-02 |
| [[connections/auth-and-webhooks]] | Token verification patterns shared across Supabase auth and Stripe webhooks | daily/2026-04-02.md, daily/2026-04-04.md | 2026-04-04 |
knowledge/log.md - Build Log
Append-only chronological record of every compile, query, and lint operation.
Format:
# Build Log
## [2026-04-01T14:30:00] compile | Daily Log 2026-04-01
- Source: daily/2026-04-01.md
- Articles created: [[concepts/nextjs-project-structure]], [[concepts/tailwind-setup]]
- Articles updated: (none)
## [2026-04-02T09:00:00] query | "How do I handle auth redirects?"
- Consulted: [[concepts/supabase-auth]], [[concepts/nextjs-middleware]]
- Filed to: [[qa/auth-redirect-handling]]
Article Formats
Concept Articles (knowledge/concepts/)
One article per atomic piece of knowledge. These are facts, patterns, decisions, preferences, and lessons extracted from your conversations.
---
title: "Concept Name"
aliases: [alternate-name, abbreviation]
tags: [domain, topic]
sources:
- "daily/2026-04-01.md"
- "daily/2026-04-03.md"
created: 2026-04-01
updated: 2026-04-03
---
# Concept Name
[2-4 sentence core explanation]
## Key Points
- [Bullet points, each self-contained]
## Details
[Deeper explanation, encyclopedia-style paragraphs]
## Related Concepts
- [[concepts/related-concept]] - How it connects
## Sources
- [[daily/2026-04-01.md]] - Initial discovery during project setup
- [[daily/2026-04-03.md]] - Updated after debugging session
Connection Articles (knowledge/connections/)
Cross-cutting synthesis linking 2+ concepts. Created when a conversation reveals a non-obvious relationship.
---
title: "Connection: X and Y"
connects:
- "concepts/concept-x"
- "concepts/concept-y"
sources:
- "daily/2026-04-04.md"
created: 2026-04-04
updated: 2026-04-04
---
# Connection: X and Y
## The Connection
[What links these concepts]
## Key Insight
[The non-obvious relationship discovered]
## Evidence
[Specific examples from conversations]
## Related Concepts
- [[concepts/concept-x]]
- [[concepts/concept-y]]
Q&A Articles (knowledge/qa/)
Filed answers from queries. Every complex question answered by the system can be permanently stored, making future queries smarter.
---
title: "Q: Original Question"
question: "The exact question asked"
consulted:
- "concepts/article-1"
- "concepts/article-2"
filed: 2026-04-05
---
# Q: Original Question
## Answer
[The synthesized answer with [[wikilinks]] to sources]
## Sources Consulted
- [[concepts/article-1]] - Relevant because...
- [[concepts/article-2]] - Provided context on...
## Follow-Up Questions
- What about edge case X?
- How does this change if Y?
Core Operations
1. Compile (daily/ -> knowledge/)
When processing a daily log:
- Read the daily log file
- Read
knowledge/index.mdto understand current knowledge state - Read existing articles that may need updating
- For each piece of knowledge found in the log:
- If an existing concept article covers this topic: UPDATE it with new information, add the daily log as a source
- If it's a new topic: CREATE a new
concepts/article
- If the log reveals a non-obvious connection between 2+ existing concepts: CREATE a
connections/article - UPDATE
knowledge/index.mdwith new/modified entries - APPEND to
knowledge/log.md
Important guidelines:
- A single daily log may touch 3-10 knowledge articles
- Prefer updating existing articles over creating near-duplicates
- Use Obsidian-style
[[wikilinks]]with full relative paths from knowledge/ - Write in encyclopedia style - factual, concise, self-contained
- Every article must have YAML frontmatter
- Every article must link back to its source daily logs
2. Query (Ask the Knowledge Base)
- Read
knowledge/index.md(the master catalog) - Based on the question, identify 3-10 relevant articles from the index
- Read those articles in full
- Synthesize an answer with
[[wikilink]]citations - If
--file-backis specified: create aknowledge/qa/article and update index.md and log.md
Why this works without RAG: At personal knowledge base scale (50-500 articles), the LLM reading a structured index outperforms cosine similarity. The LLM understands what the question is really asking and selects pages accordingly. Embeddings find similar words; the LLM finds relevant concepts.
3. Lint (Health Checks)
Seven checks, run periodically:
- Broken links -
[[wikilinks]]pointing to non-existent articles - Orphan pages - Articles with zero inbound links from other articles
- Orphan sources - Daily logs that haven't been compiled yet
- Stale articles - Source daily log changed since article was last compiled
- Contradictions - Conflicting claims across articles (requires LLM judgment)
- Missing backlinks - A links to B but B doesn't link back to A
- Sparse articles - Below 200 words, likely incomplete
Output: a markdown report with severity levels (error, warning, suggestion).
Conventions
- Wikilinks: Use Obsidian-style
[[path/to/article]]without.mdextension - Writing style: Encyclopedia-style, factual, third-person where appropriate
- Dates: ISO 8601 (YYYY-MM-DD for dates, full ISO for timestamps in log.md)
- File naming: lowercase, hyphens for spaces (e.g.,
supabase-row-level-security.md) - Frontmatter: Every article must have YAML frontmatter with at minimum: title, sources, created, updated
- Sources: Always link back to the daily log(s) that contributed to an article
Full Project Structure
llm-personal-kb/
|-- .claude/
| |-- settings.json # Hook configuration (auto-activates in Claude Code)
|-- .gitignore # Excludes runtime state, temp files, caches
|-- AGENTS.md # This file - schema + full technical reference
|-- README.md # Concise overview + quick start
|-- pyproject.toml # Dependencies (at root so hooks can find it)
|-- daily/ # "Source code" - conversation logs (immutable)
|-- knowledge/ # "Executable" - compiled knowledge (LLM-owned)
| |-- index.md # Master catalog - THE retrieval mechanism
| |-- log.md # Append-only build log
| |-- concepts/ # Atomic knowledge articles
| |-- connections/ # Cross-cutting insights linking 2+ concepts
| |-- qa/ # Filed query answers (compounding knowledge)
|-- scripts/ # CLI tools
| |-- compile.py # Compile daily logs -> knowledge articles
| |-- query.py # Ask questions (index-guided, no RAG)
| |-- lint.py # 7 health checks
| |-- flush.py # Extract memories from conversations (background)
| |-- config.py # Path constants
| |-- utils.py # Shared helpers
|-- hooks/ # Claude Code hooks
| |-- session-start.py # Injects knowledge into every session
| |-- session-end.py # Extracts conversation -> daily log
| |-- pre-compact.py # Safety net: captures context before compaction
|-- reports/ # Lint reports (gitignored)
Hook System (Automatic Capture)
Hooks are configured in .claude/settings.json and fire automatically when you use Claude Code in this project.
.claude/settings.json Format
{
"hooks": {
"SessionStart": [{ "matcher": "", "hooks": [{ "type": "command", "command": "uv run python hooks/session-start.py", "timeout": 15 }] }],
"PreCompact": [{ "matcher": "", "hooks": [{ "type": "command", "command": "uv run python hooks/pre-compact.py", "timeout": 10 }] }],
"SessionEnd": [{ "matcher": "", "hooks": [{ "type": "command", "command": "uv run python hooks/session-end.py", "timeout": 10 }] }]
}
}
Commands use simple relative paths from the project root. Empty matcher catches all events.
Hook Details
session-start.py (SessionStart)
- Pure local I/O, no API calls, runs in under 1 second
- Reads
knowledge/index.mdand the most recent daily log - Outputs JSON to stdout:
{"hookSpecificOutput": {"hookEventName": "SessionStart", "additionalContext": "..."}} - Claude sees the knowledge base index at the start of every session
- Max context: 20,000 characters
session-end.py (SessionEnd)
- Reads hook input from stdin (JSON with
session_id,transcript_path,cwd) - Copies the raw JSONL transcript to a temp file (no parsing in the hook - keeps it fast)
- Spawns
flush.pyas a fully detached background process - Recursion guard: exits immediately if
CLAUDE_INVOKED_BYenv var is set
pre-compact.py (PreCompact)
- Same architecture as session-end.py
- Fires before Claude Code auto-compacts the context window
- Guards against empty
transcript_path(known Claude Code bug #13668) - Critical for long sessions: captures context before summarization discards it
Why both PreCompact and SessionEnd? Long-running sessions may trigger multiple auto-compactions before you close the session. Without PreCompact, intermediate context is lost to summarization before SessionEnd ever fires.
Background Flush Process (flush.py)
Spawned by both hooks as a fully detached background process:
- Windows:
CREATE_NEW_PROCESS_GROUP | DETACHED_PROCESSflags - Mac/Linux:
start_new_session=True
This ensures flush.py survives after Claude Code's hook process exits.
What flush.py does:
- Sets
CLAUDE_INVOKED_BY=memory_flushenv var (prevents recursive hook firing) - Reads the pre-extracted conversation context from the temp
.mdfile - Skips if context is empty or if same session was flushed within 60 seconds (deduplication)
- Calls Claude Agent SDK (
query()withallowed_tools=[],max_turns=2) - Claude decides what's worth saving - returns structured bullet points or
FLUSH_OK - Appends result to
daily/YYYY-MM-DD.md - Cleans up temp context file
- End-of-day auto-compilation: If it's past 6 PM local time (
COMPILE_AFTER_HOUR = 18) and today's daily log has changed since its last compilation (hash comparison againststate.json), spawnscompile.pyas another detached background process. This means compilation happens automatically once a day without needing a cron job or manual trigger.
JSONL Transcript Format
Claude Code stores conversations as .jsonl files. Messages are nested under a message key:
entry = json.loads(line)
msg = entry.get("message", {})
role = msg.get("role", "") # "user" or "assistant"
content = msg.get("content", "") # string or list of content blocks
Content can be a string or a list of blocks ({"type": "text", "text": "..."} dicts).
Script Details
compile.py - The Compiler
Uses the Claude Agent SDK's async streaming query():
async for message in query(
prompt=compile_prompt,
options=ClaudeAgentOptions(
cwd=str(ROOT_DIR),
system_prompt={"type": "preset", "preset": "claude_code"},
allowed_tools=["Read", "Write", "Edit", "Glob", "Grep"],
permission_mode="acceptEdits",
max_turns=30,
),
):
- Builds a prompt with: AGENTS.md schema, current index, all existing articles, and the daily log
- Claude reads the daily log, decides what concepts to extract, and writes files directly
permission_mode="acceptEdits"auto-approves all file operations- Incremental: tracks SHA-256 hashes of daily logs in
state.json, skips unchanged files - Cost: ~$0.45-0.65 per daily log (increases as KB grows)
CLI:
uv run python scripts/compile.py # compile new/changed only
uv run python scripts/compile.py --all # force recompile everything
uv run python scripts/compile.py --file daily/2026-04-01.md
uv run python scripts/compile.py --dry-run
query.py - Index-Guided Retrieval
Loads the entire knowledge base into context (index + all articles). No RAG.
At personal KB scale (50-500 articles), the LLM reading a structured index outperforms vector similarity. The LLM understands what you're really asking; cosine similarity just finds similar words.
CLI:
uv run python scripts/query.py "What auth patterns do I use?"
uv run python scripts/query.py "What's my error handling strategy?" --file-back
With --file-back, creates a Q&A article in knowledge/qa/ and updates the index and log. This is the compounding loop - every question makes the KB smarter.
lint.py - Health Checks
Seven checks:
| Check | Type | Catches |
|---|---|---|
| Broken links | Structural | [[wikilinks]] to non-existent articles |
| Orphan pages | Structural | Articles with zero inbound links |
| Orphan sources | Structural | Daily logs not yet compiled |
| Stale articles | Structural | Source logs changed since compilation |
| Missing backlinks | Structural | A links to B but B doesn't link back |
| Sparse articles | Structural | Under 200 words |
| Contradictions | LLM | Conflicting claims across articles |
CLI:
uv run python scripts/lint.py # all checks
uv run python scripts/lint.py --structural-only # skip LLM check (free)
Reports saved to reports/lint-YYYY-MM-DD.md.
State Tracking
scripts/state.json tracks:
ingested- map of daily log filenames to SHA-256 hashes, compilation timestamps, and costsquery_count- total queries runlast_lint- timestamp of most recent linttotal_cost- cumulative API cost
scripts/last-flush.json tracks flush deduplication (session_id + timestamp).
Both are gitignored and regenerated automatically.
Dependencies
pyproject.toml (at project root):
claude-agent-sdk>=0.1.29- Claude Agent SDK for LLM calls with tool usepython-dotenv>=1.0.0- Environment variable managementtzdata>=2024.1- Timezone data- Python 3.12+, managed by uv
No API key needed - uses Claude Code's built-in credentials at ~/.claude/.credentials.json.
Costs
| Operation | Cost |
|---|---|
| Compile one daily log | $0.45-0.65 |
| Query (no file-back) | ~$0.15-0.25 |
| Query (with file-back) | ~$0.25-0.40 |
| Full lint (with contradictions) | ~$0.15-0.25 |
| Structural lint only | $0.00 |
| Memory flush (per session) | ~$0.02-0.05 |
Customization
Additional Article Types
Add directories like people/, projects/, tools/ to knowledge/. Define the article format in this file (AGENTS.md) and update utils.py's list_wiki_articles() to include them.
Obsidian Integration
The knowledge base is pure markdown with [[wikilinks]] - works natively in Obsidian. Point a vault at knowledge/ for graph view, backlinks, and search.
Scaling Beyond Index-Guided Retrieval
At ~2,000+ articles / ~2M+ tokens, the index becomes too large for the context window. At that point, add hybrid RAG (keyword + semantic search) as a retrieval layer before the LLM. See Karpathy's recommendation of qmd by Tobi Lutke for search at scale.