Skip to content

Instantly share code, notes, and snippets.

@jimmyislive
Created August 16, 2026 06:47
Show Gist options
  • Select an option

  • Save jimmyislive/883238a86c4f8ffe5603c7e3e8d1e903 to your computer and use it in GitHub Desktop.

Select an option

Save jimmyislive/883238a86c4f8ffe5603c7e3e8d1e903 to your computer and use it in GitHub Desktop.
Obsidian session logger
#!/usr/bin/env python3
"""
Claude Code -> Obsidian session logger.
Called by Claude Code hooks (see settings.json snippet). Reads hook JSON
from stdin, writes one markdown note per event into a per-session folder
in your Obsidian vault, and chains them together with wikilinks.
Usage (set by the hook command):
python3 log_event.py prompt # UserPromptSubmit
python3 log_event.py tool_call # PreToolUse
python3 log_event.py result # PostToolUse
"""
import sys, json, os, re, datetime, pathlib
# --- CONFIGURE THIS ---
VAULT = pathlib.Path(os.path.expanduser("~/ObsidianVaults/ClaudeSessions"))
# -----------------------
def slugify(s, maxlen=40):
s = re.sub(r"[^a-zA-Z0-9._-]+", "-", s).strip("-")
return s[:maxlen] or "note"
def load_state(session_dir):
p = session_dir / ".state.json"
if p.exists():
try:
return json.loads(p.read_text())
except json.JSONDecodeError:
pass
return {"seq": 0, "last_note": None, "pending": {}}
def save_state(session_dir, state):
(session_dir / ".state.json").write_text(json.dumps(state))
def append_index(session_dir, note_name, kind):
idx = session_dir / "_session-index.md"
if not idx.exists():
idx.write_text("---\ntype: session-index\ntags: [session-index]\n---\n\n# Session log\n\n")
with idx.open("a") as f:
f.write(f"- [[{note_name}]] — {kind}\n")
def write_note(session_dir, note_name, frontmatter, body):
fm = "\n".join(f"{k}: {v}" for k, v in frontmatter.items())
(session_dir / f"{note_name}.md").write_text(f"---\n{fm}\n---\n\n{body}\n")
def main():
if len(sys.argv) < 2:
sys.exit(0) # never block the session over a bad invocation
kind = sys.argv[1]
try:
data = json.load(sys.stdin)
except json.JSONDecodeError:
sys.exit(0)
session_id = data.get("session_id", "unknown")
date = datetime.date.today().isoformat()
session_dir = VAULT / f"{date}-{session_id[:8]}"
session_dir.mkdir(parents=True, exist_ok=True)
state = load_state(session_dir)
state["seq"] += 1
seq = state["seq"]
ts = datetime.datetime.now().isoformat(timespec="seconds")
if kind == "prompt":
prompt = data.get("prompt", "")
note_name = f"{seq:03d}-prompt"
links = "\n\n## Links\n"
if state["last_note"]:
links += f"- Previous: [[{state['last_note']}]]\n"
write_note(
session_dir, note_name,
{"type": "prompt", "seq": seq, "session": session_id, "timestamp": ts, "tags": "[prompt]"},
prompt + links,
)
append_index(session_dir, note_name, "prompt")
state["last_note"] = note_name
elif kind == "tool_call":
tool_name = data.get("tool_name", "unknown_tool")
tool_input = data.get("tool_input", {})
tool_use_id = data.get("tool_use_id", "")
note_name = f"{seq:03d}-call-{slugify(tool_name)}"
body = f"**Tool:** `{tool_name}`\n\n```json\n{json.dumps(tool_input, indent=2)}\n```"
links = "\n\n## Links\n"
if state["last_note"]:
links += f"- Triggered by: [[{state['last_note']}]]\n"
write_note(
session_dir, note_name,
{"type": "tool_call", "seq": seq, "session": session_id, "timestamp": ts,
"tool": tool_name, "tags": "[tool_call]"},
body + links,
)
append_index(session_dir, note_name, f"tool_call: {tool_name}")
state["last_note"] = note_name
if tool_use_id:
state["pending"][tool_use_id] = note_name
elif kind == "result":
tool_name = data.get("tool_name", "unknown_tool")
tool_use_id = data.get("tool_use_id", "")
resp = data.get("tool_response", "")
resp_str = resp if isinstance(resp, str) else json.dumps(resp, indent=2)
if len(resp_str) > 4000:
resp_str = resp_str[:4000] + "\n... [truncated]"
note_name = f"{seq:03d}-result-{slugify(tool_name)}"
body = f"**Tool:** `{tool_name}`\n\n```\n{resp_str}\n```"
links = "\n\n## Links\n"
call_note = state["pending"].pop(tool_use_id, None)
if call_note:
links += f"- Produced by: [[{call_note}]]\n"
elif state["last_note"]:
links += f"- Previous: [[{state['last_note']}]]\n"
write_note(
session_dir, note_name,
{"type": "result", "seq": seq, "session": session_id, "timestamp": ts,
"tool": tool_name, "tags": "[result]"},
body + links,
)
append_index(session_dir, note_name, f"result: {tool_name}")
state["last_note"] = note_name
save_state(session_dir, state)
sys.exit(0)
if __name__ == "__main__":
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment