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 <noreply@anthropic.com>
This commit is contained in:
Cal Corum 2026-02-16 10:30:40 -06:00
parent 1e9b52186b
commit 8e3cdf830f

View File

@ -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}"