Back to catalogue
DocumentationStop
Session summary generation
An automatic changelog of what the agent shipped
At session end, captures the diff of modified files and appends it to a timestamped change log, creating an automatic changelog of what the agent produced.
What does the Session summary generation hook do?
Session summary generation is a Claude Code Stop hook. It fires automatically at that lifecycle event — outside the model, so it can't be skipped or forgotten. An automatic changelog of what the agent shipped.
As a Stop 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
- Automatic changelog
- Change traceability
- Post-hoc review
Tags
#documentation#changelog#git#summary
settings.json fragment
{
"hooks": {
"Stop": [
{
"hooks": [
{
"command": "node $CLAUDE_PROJECT_DIR/.claude/hooks/session-changelog.mjs",
"type": "command"
}
]
}
]
}
}Script · .claude/hooks/session-changelog.mjs
#!/usr/bin/env node
// @hookstack stop-generate-changelog
// Génère une entrée de changelog depuis le diff git de la session (Stop)
import { execSync } from "node:child_process";
import { appendFileSync, existsSync } from "node:fs";
import { join } from "node:path";
import { fileURLToPath } from "node:url";
export function run({
exec,
append = appendFileSync,
exists = existsSync,
projectDir = process.env.CLAUDE_PROJECT_DIR ?? process.cwd(),
now = () => new Date().toISOString(),
} = {}) {
const doExec =
exec ??
((cmd) => {
try {
return execSync(cmd, {
encoding: "utf8",
timeout: 10_000,
cwd: projectDir,
}).trim();
} catch {
return "";
}
});
const branch = doExec("git branch --show-current");
const diff = doExec(
"git diff --stat HEAD~1 HEAD 2>/dev/null || git diff --stat HEAD",
);
const commits = doExec('git log -5 --pretty="- %s (%h)"');
if (!diff && !commits) return null;
const date = now().split("T")[0];
const entry = [
`\n## ${date} — Session sur \`${branch || "main"}\``,
"",
commits ? `### Commits\n${commits}` : "",
diff ? `### Fichiers modifiés\n\`\`\`\n${diff}\n\`\`\`` : "",
]
.filter(Boolean)
.join("\n");
const changelogPath = join(projectDir, "CHANGELOG.md");
if (!exists(changelogPath)) {
return {
written: false,
message: "[session-changelog] CHANGELOG.md absent — entrée ignorée.\n",
};
}
append(changelogPath, `${entry}\n`);
return {
written: true,
entry,
message: "[session-changelog] ✓ Entrée ajoutée dans CHANGELOG.md\n",
};
}
/* v8 ignore next 4 */
if (process.argv[1] === fileURLToPath(import.meta.url)) {
const result = run();
if (result?.message) process.stderr.write(result.message);
}
Learn more
Related hooks
- Save compaction summary to logKeep a readable trail of every compaction
- Docs consistency reminderNo more READMEs telling two different stories
- Force implementation doc at stopNo code change ships without a doc/implementation/ trace
- OKF bundle staleness reminder on session startNever let your project knowledge base go stale and lie to you