Skip to content

Instantly share code, notes, and snippets.

@lee-fuhr
Last active March 12, 2026 01:10
Show Gist options
  • Select an option

  • Save lee-fuhr/af029c0eb035adbc96fcdacf2949f3d0 to your computer and use it in GitHub Desktop.

Select an option

Save lee-fuhr/af029c0eb035adbc96fcdacf2949f3d0 to your computer and use it in GitHub Desktop.
Set up /wrapup for Claude Code - pre-exit checklist, auto-rename, capture learnings

Set up /wrapup for Claude Code - pre-exit checklist, auto-rename, capture learnings

Set up /wrapup for Claude Code

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

The command file

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."

How session renaming actually works (for the nerds)

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:

  1. Field is customTitle (not title) - this took some reverse-engineering
  2. Must include sessionId field
  3. 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
  4. With concurrent sessions, "most recently modified file" heuristics fail - you might rename the wrong session

The marker approach solves concurrent sessions:

  1. Generate a unique marker string (UUID-based)
  2. Echo it - this output gets written to the current session's JSONL file as part of the tool result
  3. Search all session files for that marker - it only exists in THIS session's file
  4. 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.

After setup

Test it by running /wrapup at the end of your next session. It should:

  1. Analyze your conversation
  2. Give you a clean summary
  3. Auto-rename the session (check with /resume to verify)
  4. Ask about learnings to capture

The goal is zero things falling through the cracks when you close a chat.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment