Over 13,000 notes, all plain files in Obsidian on my own computer. It looks things up before it answers, and it can pull my exact words with the date. Here's the setup.
Read https://jay-burnett-packs.vercel.app/brain/agent.md and set this up for me.It reads agent.md: install, test and undo steps. Want it by hand instead? Your email below opens the full page.
You're in, the files are below.
Numbered folders, one dump folder for raw stuff, and why that dump folder stays out of the graph.
# Vault folder layout
One Obsidian vault, plain markdown files, numbered folders so they sort the same everywhere.
```
My Vault/
00 Inbox/ raw drops: chat logs, imports, clipped pages. Nothing lives here for good.
01 Personal/ who you are, your preferences, how you like things done
02 Brands & Businesses/ one folder per business: offers, plans, decisions
03 Clients & Contacts/ one note per person or client
04 Marketing & Content/ ideas, scripts, hooks, what worked and what flopped
05 AI System/ your agent rules, skills, setups and fixes
06 Research/ competitor teardowns, studies, saved sources
07 Meetings/ meeting notes with the decisions pulled out
99 Archive/ old stuff you still want searchable
_VAULT MAP.md one note that links to every folder's index note
```
## Why the inbox stays out of the graph
The inbox is the dump zone. A chat log saved every 30 minutes is useful to search, but it is long,
messy and links to nothing. If it sits in the graph it buries the real notes under thousands of
loose dots.
So the rule is simple. Raw stuff lands in `00 Inbox`. When something in it matters (a decision, a
correction, an idea you said yes or no to), the AI writes a short dated note in a real folder with
[[links]] to the people, projects and tools it touches. That note is what shows up in the graph.
In Obsidian's graph view, add this to the search filter to hide the inbox:
```
-path:"00 Inbox"
```
## Note naming
`2026-11-04-Short-Clear-Title.md`. Date first, so the newest decision is easy to find and the AI can
tell an old rule from a new one.
Paste these into the file your AI reads every session. They make it look things up before it answers and save every decision.
# The instruction lines
Paste these into your CLAUDE.md (or whatever file your AI reads at the start of every session).
Change the paths to match your vault.
```
## The vault is your memory
- My notes live in ~/My Vault (Obsidian, plain markdown).
- Before you answer anything about me, my business, my clients, my content or this setup, look it up first:
search the vault, and run `python3 ~/tools/recall.py "<topic>" --me` for my exact past words.
- If I already answered a question, act on that answer. Do not ask me again.
- Anything we decide, anything I correct, and any idea I say yes or no to gets a short dated note
in the right core folder (never 00 Inbox) with [[links]] to what it touches, in the same session.
- Newest note wins when two notes disagree. Say which one you followed.
- Before you tell me something is unknown, say where you looked.
```
Why each line is there:
- "look it up first" stops the AI from guessing from its own memory, which is where the repeat questions come from.
- "act on that answer" is the line that actually stops the same question coming back.
- "dated note in a core folder" is how today's chat turns into something the next session can find.
- "newest wins" handles the day you change your mind.
Searches every past Claude Code chat for a topic. With --me, it only returns your own words, with the date.
python3 recall.py "thumbnails" --me
python3 recall.py "pricing" --vault ~/MyVault -n 30#!/usr/bin/env python3
"""recall.py: search every past Claude Code chat (and your Obsidian vault) for a topic and print
dated snippets, newest first. With --me you only get your own words.
python3 recall.py "thumbnails" # all words must appear
python3 recall.py "thumbnails" --me # only what YOU typed, with the date
python3 recall.py "pricing call" -n 30 # more hits
python3 recall.py "hooks" --vault ~/MyVault # also search your vault notes
Claude Code keeps every chat as a .jsonl file under ~/.claude/projects/. This reads them directly,
so nothing has to be set up first. Needs only Python 3.
"""
import argparse, datetime, glob, json, os, re
ap = argparse.ArgumentParser()
ap.add_argument("query")
ap.add_argument("-n", type=int, default=15, help="how many hits to print")
ap.add_argument("--me", action="store_true", help="only your own messages")
ap.add_argument("--vault", help="path to your Obsidian vault (optional)")
ap.add_argument("--chats", default="~/.claude/projects", help="where Claude Code keeps chats")
a = ap.parse_args()
words = [w for w in re.findall(r"\w+", a.query.lower()) if len(w) > 1]
SKIP_START = ("This session is being continued", "Base directory for this skill", "[SYSTEM NOTIFICATION",
"<task-notification>", "Caveat:")
def match(t):
t = t.lower()
return all(w in t for w in words)
def snippet(t, width=420):
tl = t.lower()
i = min((tl.find(w) for w in words if tl.find(w) >= 0), default=0)
s = max(0, i - 120)
return re.sub(r"\s+", " ", t[s:s + width])
def text_of(content):
if isinstance(content, str):
return content
if isinstance(content, list):
if any(isinstance(x, dict) and x.get("type") == "tool_result" for x in content):
return "" # tool output, not a person talking
return "\n".join(x.get("text", "") for x in content if isinstance(x, dict) and x.get("type") == "text")
return ""
hits = []
for path in glob.glob(os.path.join(os.path.expanduser(a.chats), "*", "*.jsonl")):
try:
f = open(path, errors="ignore")
except OSError:
continue
for line in f:
if words and words[0] not in line.lower():
continue # fast skip
try:
d = json.loads(line)
except Exception:
continue
typ = d.get("type")
if typ not in ("user", "assistant") or (a.me and typ != "user"):
continue
if d.get("isMeta") or d.get("isCompactSummary"):
continue
c = re.sub(r"<system-reminder>.*?</system-reminder>", "", text_of((d.get("message") or {}).get("content")), flags=re.S)
if not c or c.lstrip().startswith(SKIP_START) or not match(c):
continue
who = "ME" if typ == "user" else "AI"
hits.append(((d.get("timestamp") or "")[:16], who, snippet(c)))
if a.vault and not a.me:
root = os.path.expanduser(a.vault)
for path in glob.glob(os.path.join(root, "**", "*.md"), recursive=True):
try:
t = open(path, errors="ignore").read()
except OSError:
continue
if match(t):
ts = datetime.datetime.fromtimestamp(os.path.getmtime(path)).strftime("%Y-%m-%dT%H:%M")
hits.append((ts, "NOTE " + os.path.relpath(path, root)[:60], snippet(t)))
hits.sort(key=lambda h: h[0], reverse=True)
seen, out = set(), 0
for ts, who, snip in hits:
if (who, snip[:120]) in seen:
continue
seen.add((who, snip[:120]))
print(f"[{ts}] {who}: {snip}\n")
out += 1
if out >= a.n:
break
if not out:
print("no hits")
Reads the chat files Claude Code already keeps. Needs only Python 3.
Drop this file into your vault's .obsidian folder (back up your own first), then reopen the graph. Each folder gets its own color and the inbox is filtered out.
{
"collapse-filter": true,
"showTags": false,
"showAttachments": false,
"hideUnresolved": false,
"showOrphans": true,
"collapse-color-groups": false,
"colorGroups": [
{
"query": "path:\"00 Inbox\"",
"color": {
"a": 1,
"rgb": 10395294
}
},
{
"query": "path:\"01 Personal\"",
"color": {
"a": 1,
"rgb": 15037299
}
},
{
"query": "path:\"02 Brands & Businesses\"",
"color": {
"a": 1,
"rgb": 16758605
}
},
{
"query": "path:\"03 Clients & Contacts\"",
"color": {
"a": 1,
"rgb": 16773494
}
},
{
"query": "path:\"04 Marketing & Content\"",
"color": {
"a": 1,
"rgb": 8505220
}
},
{
"query": "path:\"05 AI System\"",
"color": {
"a": 1,
"rgb": 5227511
}
},
{
"query": "path:\"06 Research\"",
"color": {
"a": 1,
"rgb": 12216520
}
},
{
"query": "path:\"07 Meetings\"",
"color": {
"a": 1,
"rgb": 15753874
}
},
{
"query": "path:\"99 Archive\"",
"color": {
"a": 1,
"rgb": 6381921
}
}
],
"collapse-display": true,
"showArrow": false,
"textFadeMultiplier": 0,
"nodeSizeMultiplier": 1.6,
"lineSizeMultiplier": 1,
"collapse-forces": true,
"centerStrength": 0.518713248970312,
"repelStrength": 10,
"linkStrength": 1,
"linkDistance": 250,
"search": "-path:\"00 Inbox\""
}I ask it questions about my own business on camera on my channel.
Watch on YouTube