From 8e3cdf830ffb5c7eca9c3e75569a20ba6ee8bf9e Mon Sep 17 00:00:00 2001 From: Cal Corum Date: Mon, 16 Feb 2026 10:30:40 -0600 Subject: [PATCH] Fix heredoc commit message extraction in session memory hook The regex used mismatched quote types, causing $(cat << to leak into memory titles. Now tries heredoc format first with proper EOF terminator matching, falls back to same-type quoted strings. Co-Authored-By: Claude Opus 4.6 --- scripts/session-memory/session_memory.py | 43 +++++++++++++++++------- 1 file changed, 30 insertions(+), 13 deletions(-) diff --git a/scripts/session-memory/session_memory.py b/scripts/session-memory/session_memory.py index 8bc85b5..70ff36d 100755 --- a/scripts/session-memory/session_memory.py +++ b/scripts/session-memory/session_memory.py @@ -380,12 +380,9 @@ def build_memory_content(summary: dict) -> str: if summary["commits"]: parts.append(f"Commits made: {len(summary['commits'])}") for c in summary["commits"][:3]: - # Extract commit message - match = re.search(r'-m\s+["\'](.+?)["\']', c) - if not match: - match = re.search(r"<<'?EOF'?\n(.+?)(?:\n|EOF)", c, re.DOTALL) - if match: - parts.append(f" - {match.group(1)[:200]}") + msg = extract_commit_message(c) + if msg: + parts.append(f" - {msg}") if summary["files_edited"]: parts.append(f"Files edited ({len(summary['files_edited'])}):") @@ -426,18 +423,38 @@ def determine_memory_type(summary: dict) -> str: return "general" +def extract_commit_message(commit_cmd: str) -> str | None: + """Extract the commit message from a git commit command string. + + Handles both simple quoted (-m "msg") and heredoc (-m "$(cat <<'EOF'...EOF)") + formats. Tries heredoc first since that's the standard Claude Code format. + """ + # Try heredoc format first (standard Claude Code format) + match = re.search(r"<<'?EOF'?\n(.+?)(?:\nEOF|\n\s*EOF)", commit_cmd, re.DOTALL) + if match: + # Get first non-empty line as the message + for line in match.group(1).strip().split("\n"): + line = line.strip() + if line and not line.startswith("Co-Authored-By:"): + return line[:200] + + # Fall back to simple quoted message (matching same quote type) + match = re.search(r'-m\s+"([^"]+)"', commit_cmd) + if not match: + match = re.search(r"-m\s+'([^']+)'", commit_cmd) + if match: + return match.group(1).split("\n")[0][:200] + + return None + + def build_title(summary: dict) -> str: """Generate a descriptive title for the memory.""" project = summary["project"] work = ", ".join(sorted(summary["work_types"])) if summary["commits"]: - # Try to use first commit message as title basis - first_commit = summary["commits"][0] - match = re.search(r'-m\s+["\'](.+?)["\']', first_commit) - if not match: - match = re.search(r"<<'?EOF'?\n(.+?)(?:\n|EOF)", first_commit, re.DOTALL) - if match: - msg = match.group(1).split("\n")[0][:80] + msg = extract_commit_message(summary["commits"][0]) + if msg: return f"[{project}] {msg}" return f"[{project}] Session: {work}"