Back to catalogue
NotificationSessionEnd
End-of-session audit log
Every session leaves an audit trail
At session end, writes an audit line (timestamp, end reason, directory) to a local log file for DevSecOps traceability.
What does the End-of-session audit log hook do?
End-of-session audit log is a Claude Code SessionEnd hook. It fires automatically at that lifecycle event — outside the model, so it can't be skipped or forgotten. Every session leaves an audit trail.
As a SessionEnd hook it runs after the action, reacting to what just happened rather than blocking it. Because it is a deterministic Node.js script, it executes on every matching event without relying on the model to remember — the guarantee that makes agentic workflows safe to automate.
Use cases
- Traceability
- DevSecOps audit
- Usage reporting
Tags
#notification#audit#logging#devsecops
settings.json fragment
{
"hooks": {
"SessionEnd": [
{
"hooks": [
{
"command": "node $CLAUDE_PROJECT_DIR/.claude/hooks/audit-log.mjs",
"type": "command"
}
]
}
]
}
}Script · .claude/hooks/audit-log.mjs
#!/usr/bin/env node
// @hookstack session-end-audit-log
// Enregistre un résumé de session dans ~/.claude/audit-log.jsonl (SessionEnd)
import { appendFileSync, mkdirSync, readFileSync } from "node:fs";
import { homedir } from "node:os";
import { join } from "node:path";
import { fileURLToPath } from "node:url";
export function run(
input,
{
append = appendFileSync,
mkdir = mkdirSync,
home = homedir(),
projectDir = process.env.CLAUDE_PROJECT_DIR,
now = () => new Date().toISOString(),
} = {},
) {
const logDir = join(home, ".claude");
mkdir(logDir, { recursive: true });
const entry = {
timestamp: now(),
project: projectDir?.split("/").pop() ?? "unknown",
session_id: input.session_id ?? null,
total_cost_usd: input.total_cost_usd ?? null,
num_turns: input.num_turns ?? null,
};
append(join(logDir, "audit-log.jsonl"), `${JSON.stringify(entry)}\n`);
return entry;
}
/* v8 ignore next 4 */
if (process.argv[1] === fileURLToPath(import.meta.url)) {
const input = JSON.parse(readFileSync(0, "utf8"));
run(input);
}