Set up /wrapup for Claude Code - pre-exit checklist, auto-rename, capture learnings
Copy everything in the box below and paste it into Claude Code.
Help me set up a /wrapup command for Claude Code session management.
## What this does
When I run "/wrapup" before ending a session, Claude:
1. Checks if I should actually exit (or just pause)
2. Summarizes what got done
3. Lists any open items so nothing falls through cracks
4. Shows files modified this session
5. **Auto-renames the session** using the marker approach (works even with multiple concurrent sessions!)
6. Offers to capture any learnings from the session
It's a pre-flight checklist before closing a conversation.
## The rename script
Create `~/.claude/scripts/rename_session.py`:
```python
#!/usr/bin/env python3
"""
Rename current Claude Code session using the marker approach.
The marker approach reliably identifies THIS session even with concurrent sessions:
1. Generate unique marker → echo it → marker is written to session file
2. Search all session files for marker → found file = THIS session
3. Append custom-title entry → session is renamed
Usage (two-step from Claude - MUST be two separate bash calls):
# Step 1: Generate and echo marker
MARKER=$(python3 ~/.claude/scripts/rename_session.py marker) && echo "$MARKER"
# Step 2: Find session and rename (use marker from step 1)
python3 ~/.claude/scripts/rename_session.py rename "Session Name" "$MARKER"
Why two steps? The marker gets written to the session file when Step 1 completes.
Step 2 then searches for it. A single command can't work because the search
would run before the marker is written.
"""
import sys
import os
import json
import uuid
import glob
from pathlib import Path
def get_sessions_dir() -> Path:
"""Get the Claude sessions directory for the current project."""
cwd = os.getcwd()
project_key = cwd.replace('/', '-')
sessions_dir = Path.home() / '.claude' / 'projects' / project_key
if not sessions_dir.exists():
# Try to find a matching project dir
projects_dir = Path.home() / '.claude' / 'projects'
for d in projects_dir.iterdir():
if d.is_dir() and project_key in str(d):
return d
raise RuntimeError(f"No project dir found for {cwd}")
return sessions_dir
def generate_marker() -> str:
"""Generate a unique marker. Must be echoed to be captured in session file."""
marker = f"__RENAME_MARKER_{uuid.uuid4().hex}__"
print(marker)
return marker
def find_session(marker: str) -> str | None:
"""Find the session file containing the marker."""
sessions_dir = get_sessions_dir()
for filepath in glob.glob(str(sessions_dir / '*.jsonl')):
try:
with open(filepath, 'r', encoding='utf-8', errors='ignore') as f:
if marker in f.read():
return filepath
except Exception:
continue
return None
def rename_session(filepath: str, name: str) -> bool:
"""Append custom-title entry to session file."""
session_id = os.path.basename(filepath).replace('.jsonl', '')
entry = {
"type": "custom-title",
"customTitle": name,
"sessionId": session_id
}
try:
with open(filepath, 'a', encoding='utf-8') as f:
# CRITICAL: Write with trailing newline to prevent concatenation
# with Claude's next write
f.write(json.dumps(entry) + '\n')
f.flush()
os.fsync(f.fileno())
return True
except Exception as e:
print(f"Error: {e}", file=sys.stderr)
return False
def main():
if len(sys.argv) < 2:
print("""
Usage (two separate bash calls required):
# Step 1: Generate marker (echo the output!)
MARKER=$(python3 rename_session.py marker) && echo "$MARKER"
# Step 2: Rename (after marker has been echoed)
python3 rename_session.py rename "Session Name" "<marker>"
""", file=sys.stderr)
sys.exit(1)
cmd = sys.argv[1]
if cmd == "marker":
generate_marker()
elif cmd == "rename":
if len(sys.argv) < 4:
print("Usage: rename_session.py rename <name> <marker>", file=sys.stderr)
sys.exit(1)
name = sys.argv[2]
marker = sys.argv[3]
filepath = find_session(marker)
if not filepath:
print(f"ERROR: Could not find session for marker: {marker}", file=sys.stderr)
print("Make sure the marker was echoed in a previous command!", file=sys.stderr)
sys.exit(1)
session_id = os.path.basename(filepath).replace('.jsonl', '')
print(f"Found session: {session_id[:8]}...")
if rename_session(filepath, name):
print(f"✓ Session renamed to: {name}")
else:
sys.exit(1)
else:
print(f"Unknown command: {cmd}", file=sys.stderr)
sys.exit(1)
if __name__ == "__main__":
main()
Make it executable: chmod +x ~/.claude/scripts/rename_session.py
Create ~/.claude/commands/wrapup.md:
# Session wrap-up
Run before ending a session to make sure nothing falls through the cracks.
## Steps
1. **Should this chat stay open?**
- Is there active work that will continue shortly?
- Are we waiting on something that'll come back soon?
- If yes → suggest pausing instead of exiting
- If no → proceed with wrapup
2. **Summarize the session** (2-4 bullets max):
- What was the main goal?
- What got done?
- What didn't get done?
3. **Check for open items:**
- Incomplete tasks mentioned but not finished
- Questions raised but not answered
- Promises made ("I'll do X later")
- Things marked TODO or FIXME this session
4. **Files modified this session** - quick list
5. **Auto-rename the session** (TWO-STEP PROCESS - CRITICAL):
- Generate a 2-4 word name for the session
- Step 1: Generate marker (must complete before step 2):
```bash
MARKER=$(python3 ~/.claude/scripts/rename_session.py marker) && echo "$MARKER"
```
- Step 2: Use marker to find and rename (separate bash call):
```bash
python3 ~/.claude/scripts/rename_session.py rename "[SESSION_NAME]" "[MARKER_FROM_STEP_1]"
```
- The marker approach reliably identifies THIS session even with concurrent sessions
6. **Quick learning capture** (use AskUserQuestion with multi-select):
- Analyze the session for potential learnings/patterns worth remembering
- Offer 2-4 specific suggestions based on what happened
- Include options: "Close the session" and "Keep session open"
- For selected items: output them clearly for user to capture
## Output format
Keep it tight:
\`\`\`
## Should you exit?
[Yes, clean break / No, keep open because X]
## Session summary
- [bullet 1]
- [bullet 2]
## Open items
- [ ] [item] — [status/next step]
(or "None - clean exit")
## Files touched
- `path/file` — [what changed]
## Before you go
- [x] Session renamed
- [ ] [any other action needed]
\`\`\`
Then use AskUserQuestion for the learning capture prompt.
If everything is clean and no learnings worth noting, just say "Clean exit - nothing pending."Claude Code stores sessions as JSONL files at ~/.claude/projects/[project-key]/[uuid].jsonl.
Each line is a JSON object representing a message, tool call, or metadata. To rename a session, you append a special entry:
{"type": "custom-title", "customTitle": "My Session Name", "sessionId": "uuid-here"}The gotchas we discovered:
- Field is
customTitle(nottitle) - this took some reverse-engineering - Must include
sessionIdfield - Entry MUST have a trailing newline - without it, Claude's next write concatenates directly after your entry, breaking the JSONL format and making Claude unable to parse the custom-title
- With concurrent sessions, "most recently modified file" heuristics fail - you might rename the wrong session
The marker approach solves concurrent sessions:
- Generate a unique marker string (UUID-based)
- Echo it - this output gets written to the current session's JSONL file as part of the tool result
- Search all session files for that marker - it only exists in THIS session's file
- Now you know exactly which file to modify
This is why two bash calls are required: the marker must be written to the file (when Step 1 completes) before Step 2 can search for it.
Test it by running /wrapup at the end of your next session. It should:
- Analyze your conversation
- Give you a clean summary
- Auto-rename the session (check with
/resumeto verify) - Ask about learnings to capture
The goal is zero things falling through the cracks when you close a chat.