Skip to content

Instantly share code, notes, and snippets.

@lee-fuhr
Last active January 13, 2026 21:01
Show Gist options
  • Select an option

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

Select an option

Save lee-fuhr/d3dfa6c0f088bd27b6f9ced923f47489 to your computer and use it in GitHub Desktop.
Set up /pause for Claude Code - auto-rename sessions, create terminal deeplinks, set task manager reminders

Set up /pause for Claude Code - auto-rename sessions, create terminal deeplinks, set task manager reminders

Set up /pause for Claude Code

Copy everything in the box below and paste it into Claude Code.


Help me set up a /pause command for Claude Code session management.

## What this does

When I run "/pause for 3 days", it:
1. **Auto-renames the current session** using the marker approach (works even with multiple concurrent sessions!)
2. Creates a one-click terminal deeplink to resume
3. Creates a reminder in my task manager with the link + context

Later: reminder pops up → I click the link → terminal opens → runs `claude --resume "Session Name"` → I'm back exactly where I left off.

## Walk me through setup

Ask me two questions:

**Question 1: Which terminal do you use?**
- Warp
- iTerm2
- Terminal.app
- Hyper/Alacritty/Kitty
- Other

**Question 2: Which task manager do you use?**
- Todoist
- Things 3
- Apple Reminders
- Notion
- Linear
- None / I'll just copy the link manually

Then create the script and command file based on my answers.

## Implementation details you'll need

### Core session detection - THE MARKER APPROACH (use this exactly)

The marker approach reliably identifies THIS session even with concurrent sessions:

1. Generate unique marker → echo it → marker is written to session JSONL file
2. Search all session files for that marker → found file = THIS session
3. Append custom-title entry → session is renamed

```python
import json, os, re, sys, uuid, glob
from pathlib import Path


def get_sessions_dir():
    """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():
        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"__PAUSE_MARKER_{uuid.uuid4().hex}__"
    print(marker)
    return marker


def find_session_by_marker(marker: str):
    """Find the session file containing the marker. Most reliable method."""
    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 Path(filepath).stem, Path(filepath)
        except Exception:
            continue

    return None


def rename_session(session_id: str, jsonl_path: Path, name: str):
    """Rename by appending custom-title entry to the jsonl file."""
    entry = {
        "type": "custom-title",
        "customTitle": name,
        "sessionId": session_id
    }
    with open(jsonl_path, 'a') as f:
        # CRITICAL: Write with trailing newline to prevent concatenation
        # with Claude's next write (this breaks parsing otherwise!)
        f.write(json.dumps(entry) + '\n')
        f.flush()
        os.fsync(f.fileno())
    print(f"✓ Session renamed: {name}")

CRITICAL: Two-step process required from Claude

The script must support a marker command for generating markers:

def main():
    if len(sys.argv) < 2:
        print("Usage: pause.py marker | pause.py 'Name' 'due' 'context' 'marker'")
        sys.exit(1)

    # Step 1: Generate marker (Claude calls this first, echoes output)
    if sys.argv[1] == "marker":
        generate_marker()
        return

    # Step 2: Pause the session (Claude calls this with the marker)
    name = sys.argv[1]
    due = sys.argv[2] if len(sys.argv) > 2 else "tomorrow"
    context = sys.argv[3] if len(sys.argv) > 3 else "Continue where we left off"
    marker = sys.argv[4] if len(sys.argv) > 4 else None

    if marker:
        result = find_session_by_marker(marker)
        if result:
            session_id, jsonl_path = result
        else:
            print("⚠️ Marker not found, using fallback")
            session_id, jsonl_path = get_current_session_fallback()
    else:
        print("⚠️ No marker - less reliable with concurrent sessions")
        session_id, jsonl_path = get_current_session_fallback()

    # ... rest of implementation (rename, create deeplink, create task)

Terminal-specific implementations

Warp:

def create_warp_config(name: str, cwd: str) -> str:
    safe_name = re.sub(r'[^a-zA-Z0-9_-]', '-', name.lower())
    safe_name = re.sub(r'-+', '-', safe_name).strip('-')

    config_dir = Path.home() / '.warp' / 'launch_configurations'
    config_dir.mkdir(parents=True, exist_ok=True)
    config_path = config_dir / f"parked-{safe_name}.yaml"

    # CRITICAL: name field MUST match filename or deeplink silently fails
    config = f'''name: "parked-{safe_name}"
windows:
  - tabs:
      - title: "{name}"
        layout:
          cwd: "{cwd}"
          commands:
            - exec: claude --resume "{name}"
'''
    config_path.write_text(config)
    return f"warp://launch/parked-{safe_name}"

iTerm2:

def create_iterm_command(name: str, cwd: str) -> str:
    script = f'''osascript -e 'tell app "iTerm2" to create window with default profile command "cd {cwd} && claude --resume \\"{name}\\""' '''
    return script

Terminal.app:

def create_terminal_command(name: str, cwd: str) -> str:
    script = f'''osascript -e 'tell app "Terminal" to do script "cd {cwd} && claude --resume \\"{name}\\""' '''
    return script

Other terminals: Just output claude --resume "{name}" and tell them to run it manually.

Task manager implementations

Todoist:

import requests

def create_todoist_task(name, due, context, deeplink, session_id):
    TOKEN = os.environ.get("TODOIST_API_TOKEN")  # or hardcode it

    description = f"""## Resume
{deeplink}

## Manual
claude --resume "{name}"

## Context
{context}

## Session ID (fallback)
{session_id}
"""

    requests.post(
        "https://api.todoist.com/rest/v2/tasks",
        headers={"Authorization": f"Bearer {TOKEN}"},
        json={
            "content": f"Resume: {name}",
            "description": description,
            "due_string": due,
            "priority": 2
        }
    )

Token from: Todoist Settings → Integrations → Developer

Things 3:

import subprocess, urllib.parse

def create_things_task(name, due, context, deeplink, session_id):
    notes = f"{deeplink}\n\n{context}\n\nSession: {session_id}"
    url = f"things:///add?title={urllib.parse.quote(f'Resume: {name}')}&notes={urllib.parse.quote(notes)}&when={due}"
    subprocess.run(["open", url])

Apple Reminders / Notion / Linear: See original implementations - they work the same, just pass the deeplink and context.

None: Just print the deeplink and manual command.

The command file

Create ~/.claude/commands/pause.md:

# /pause - Pause this conversation for later

When user runs `/pause [timeframe]`:

1. **Generate session name** (3-6 words, captures what we worked on)

2. **Gather context:**
   - What was accomplished this session
   - What's blocked or waiting on something
   - What to do first when resuming
   - Key files created or modified

3. **Run the script (TWO-STEP PROCESS - CRITICAL):**

   Step 1: Generate marker (must complete before step 2)
   ```bash
   MARKER=$(~/.claude/scripts/pause.py marker) && echo "$MARKER"

Step 2: Pause the session using the marker (separate bash call)

~/.claude/scripts/pause.py "[NAME]" "[TIMEFRAME]" "[CONTEXT]" "$MARKER"
  1. Confirm: ⏸️ Paused: [NAME] ✓ Session renamed (via marker) ✓ Terminal config created ✓ Reminder set for [date]

## How session renaming actually works (for the nerds)

Claude Code stores sessions as JSONL files at `~/.claude/projects/[project-key]/[uuid].jsonl`.

To rename a session, you append this entry:
```json
{"type": "custom-title", "customTitle": "My Session Name", "sessionId": "uuid-here"}

The gotchas we discovered:

  1. Field is customTitle (not title) - took reverse-engineering to figure out
  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
  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 THIS 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 /pause for a day in a session. Should:

  1. Rename the session (verify with /resume - you should see your custom name)
  2. Create terminal config (check ~/.warp/launch_configurations/ if using Warp)
  3. Create task in your task manager

Then test the deeplink actually works by clicking it.

Troubleshooting

  • Warp deeplink just brings Warp to front: The name: field in the YAML doesn't match the filename. Must be exact match without .yaml extension.
  • "No project dir found": Run from same directory where you started Claude.
  • Session not renamed correctly: Make sure you're using the two-step marker approach. Without the marker, the script has to guess which session is yours (unreliable with concurrent sessions).
  • Entry not appearing in /resume: Check that your script writes with a trailing newline, not a leading one.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment