Skip to content

Add session-logger hook for durable session notes#14

Open
karanb192 wants to merge 4 commits intomainfrom
feat/session-logger
Open

Add session-logger hook for durable session notes#14
karanb192 wants to merge 4 commits intomainfrom
feat/session-logger

Conversation

@karanb192
Copy link
Copy Markdown
Owner

@karanb192 karanb192 commented Apr 18, 2026

Summary

Adds a new hook — session-logger — that writes a human-readable markdown log of every Claude Code session: timestamps, cwd, git repo/branch, files touched, and bash commands run.

The repo's own CONTRIBUTING.md and README have had session-summary on the "Ideas for new hooks" list since day one. This ships it, scoped more broadly as a full session logger (covers start, during, and end of session).

Why

Claude Code sessions are ephemeral. You finish a session, switch repos, and two days later can't remember which session touched which file or ran which command. This hook gives you a durable, greppable (and Obsidian-friendly) record.

What it does

  • SessionStart → creates a new note at <log-dir>/YYYY-MM-DD_HHmm_<sid-short>.md with YAML frontmatter (session_id, cwd, git_repo, git_branch, started, ended) and two pre-seeded sections: Files Touched and Commands Run.
  • PostToolUse (Edit | Write | Read | Bash) → appends an entry to the right section.
  • SessionEnd → fills in ended: in frontmatter and appends a final git status.

Design choices

  • Register against SessionEnd, not Stop. Stop fires at the end of every Claude turn (many times per session); SessionEnd fires once when the session actually ends. The handler is also idempotent — if ended: is already filled, it no-ops, so accidentally registering against Stop is harmless.
  • Abrupt terminal close is safe. PostToolUse writes synchronously during the session, so if the terminal is killed before SessionEnd fires, all activity up to the last tool call is preserved — the note just doesn't get a final ended: timestamp.
  • Single script, three registrations — one file to install, dispatches on hook_event_name.
  • Filename format YYYY-MM-DD_HHmm_<sid-short>.md — chronologically sortable in any file explorer, greppable by session id suffix.
  • Session resume handling — on duplicate SessionStart with the same id, appends a Resumed at marker rather than overwriting.
  • Bash safety — commands are truncated to the first line (200 chars) to avoid logging multi-line command bodies that may contain secrets.
  • Configurable path — top-of-file SESSION_LOG_DIR constant (matches the existing SAFETY_LEVEL pattern), overridable via CC_SESSION_LOG_DIR env var. Point it at an Obsidian vault or iCloud folder for cross-device sync.
  • Graceful errors — malformed JSON, missing files, non-git paths all fall through to {} output.

Changes

  • hook-scripts/session/session-logger.js — new hook (~195 LOC).
  • hook-scripts/tests/session/session-logger.test.js20 new tests, all passing (includes an explicit regression test that Stop events do not finalize the note, and an idempotency test for duplicate SessionEnd).
  • README.md — added the new "Session" section to the hooks table, removed session-summary from the Ideas list (it's now shipped), bumped test badge 262 → 282.

Test plan

  • node --test hook-scripts/tests/session/session-logger.test.js — 20/20 pass.
  • npm test — 280/282 pass (the 2 failures are pre-existing notify-permission SLACK_WEBHOOK env issues on main, unrelated to this PR — verified by stashing untracked changes and re-running).
  • Manual smoke test in a live Claude Code session — recommended before merge.

karanb192 added 4 commits April 18, 2026 10:51
Single script that handles SessionStart, PostToolUse, and Stop events
to write a human-readable markdown log of every Claude Code session:
timestamps, cwd, git repo/branch, files touched, and bash commands run.

- One install, three registrations (SessionStart + PostToolUse + Stop).
- Filename: YYYY-MM-DD_HHmm_<sid-short>.md — sortable + greppable.
- Session resume: appends a "Resumed at" marker instead of overwriting.
- Bash commands truncated to the first line (200 chars) to avoid logging
  multi-line command bodies that may contain secrets.
- Configurable log directory via top-of-file SESSION_LOG_DIR constant
  (matches existing SAFETY_LEVEL pattern) or CC_SESSION_LOG_DIR env var.
  Point it at an Obsidian vault or iCloud folder for cross-device sync.

Tests: 18 new, all passing (unit + stdin/stdout integration).
Stop fires at the end of every Claude turn (many times per session), which
would write duplicate "Session End" sections and repeatedly overwrite the
ended: timestamp. SessionEnd fires once when the session actually ends.

- Rename handleStop → handleSessionEnd.
- Drop Stop from the event dispatch (add an explanatory comment).
- Idempotent finalize: skip if the "ended:" frontmatter line is already
  filled, so accidentally registering against Stop is harmless.
- Update README + install snippet to use SessionEnd.
- Tests: add idempotency test and a regression test confirming Stop events
  do not finalize the note. Bump test badge to 282.
Update the install-example comment in session-logger.js to register
PostToolUse with "async": true. PostToolUse fires after every tool call,
so making it non-blocking keeps the hot path off Claude's critical path.

SessionStart stays sync (note must exist before PostToolUse tries to
append); SessionEnd stays sync (finalization must complete before
session termination).

Ref: https://code.claude.com/docs/en/hooks — async/asyncRewake fields.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant