# AGENTS.md - Personal Knowledge Base Schema > Adapted from [Andrej Karpathy's LLM Knowledge Base](https://gist.github.com/karpathy/442a6bf555914893e9891c11519de94f) 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: ```markdown # 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: ```markdown # 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: ```markdown # 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. ```markdown --- 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. ```markdown --- 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. ```markdown --- 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: 1. Read the daily log file 2. Read `knowledge/index.md` to understand current knowledge state 3. Read existing articles that may need updating 4. 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 5. If the log reveals a non-obvious connection between 2+ existing concepts: CREATE a `connections/` article 6. UPDATE `knowledge/index.md` with new/modified entries 7. 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) 1. Read `knowledge/index.md` (the master catalog) 2. Based on the question, identify 3-10 relevant articles from the index 3. Read those articles in full 4. Synthesize an answer with `[[wikilink]]` citations 5. If `--file-back` is specified: create a `knowledge/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: 1. **Broken links** - `[[wikilinks]]` pointing to non-existent articles 2. **Orphan pages** - Articles with zero inbound links from other articles 3. **Orphan sources** - Daily logs that haven't been compiled yet 4. **Stale articles** - Source daily log changed since article was last compiled 5. **Contradictions** - Conflicting claims across articles (requires LLM judgment) 6. **Missing backlinks** - A links to B but B doesn't link back to A 7. **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 `.md` extension - **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 ```json { "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.md` and 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.py` as a fully detached background process - Recursion guard: exits immediately if `CLAUDE_INVOKED_BY` env 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_PROCESS` flags - **Mac/Linux:** `start_new_session=True` This ensures flush.py survives after Claude Code's hook process exits. **What flush.py does:** 1. Sets `CLAUDE_INVOKED_BY=memory_flush` env var (prevents recursive hook firing) 2. Reads the pre-extracted conversation context from the temp `.md` file 3. Skips if context is empty or if same session was flushed within 60 seconds (deduplication) 4. Calls Claude Agent SDK (`query()` with `allowed_tools=[]`, `max_turns=2`) 5. Claude decides what's worth saving - returns structured bullet points or `FLUSH_OK` 6. Appends result to `daily/YYYY-MM-DD.md` 7. Cleans up temp context file 8. **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 against `state.json`), spawns `compile.py` as 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: ```python 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()`: ```python 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:** ```bash 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:** ```bash 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:** ```bash 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 costs - `query_count` - total queries run - `last_lint` - timestamp of most recent lint - `total_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 use - `python-dotenv>=1.0.0` - Environment variable management - `tzdata>=2024.1` - Timezone data - Python 3.12+, managed by [uv](https://docs.astral.sh/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.