resume

A command that retrieves coding-session handoff summaries and the latest code from Git so work can continue on another device.

In plain words
What is it for?
It pulls the repository, shows handoffs from other devices, keeps the latest handoff for the current device, and commits that cleanup. Another device must have saved a handoff first.
Why use it?
It brings back the previous session's context and files without requiring the developer to reconstruct what happened.

Command

Part of the convosync plugin — 3 commands shipped together

Install

Getting it into your agent

One page per mod, every tool's command on it. A separate URL per tool would split the same page into five that compete with each other.

agentmods
npx agentmods add commands/convocli/convosync-plugin/resume
Clone the repo
git clone --depth 1 https://github.com/convocli/convosync-plugin

Or install convosync, the plugin that ships this one along with the rest of its 3 commands.

Per session 0 Only the description is in the session, so the agent can decide to use it. The body loads when it is invoked.
When invoked 3,113 The whole file, excluding the scripts and references it only reads on demand.
Security scan A 1 finding. Scan, not verified.
Origin original No closer match found in the catalogue.
Token cost

What it costs to keep this loaded

Counted locally with the o200k_base tokenizer, which is exact for GPT models; Claude uses its own tokenizer and its counts differ. Treat this as one consistent yardstick across the catalogue rather than a bill. Prices are per million input tokens.

ModelPer sessionOnce invoked
Fable 5 $0.00000 $0.03113
Opus 5 $0.00000 $0.01556
Sonnet 5 $0.00000 $0.00623
Haiku 4.5 $0.00000 $0.00311

Measured 2d ago against content hash c115681389ff, method: parsed. Prices are Anthropic first-party input rates as of 2026-08-30, from the pricing page.

Security

Grade A, and why

resume scanned grade A with 1 finding against 26 rules in 11 categories — prompt injection, anti-refusal, data exfiltration, privilege escalation, supply chain, agent snooping, system-prompt leakage, SSRF and excessive agency — measured 2d ago.

A static scan of the body, not an audit. Every finding is printed with the line that produced it so you can judge whether it matters here. A mod is markdown that instructs an agent; that is exactly why what it instructs is worth reading.

Runs shell commandslowCapability

Expected in a hook, worth knowing in a rule or an instructions file.

subprocess.run(["git", "pull"], check=True, capture_output=True)
commands/resume.md · 407 lines

How it starts

The opening of the file, as written. The whole thing — 407 lines — stays where its author put it; the contents beside it link to each section on GitHub.

Resume ConvoSync Session

Resume work on another device by viewing session handoffs from other devices.

What this does:

  1. Pulls latest code and handoff file from git
  2. Parses handoffs from all devices
  3. Cleans up old handoffs from THIS device (keeps latest only)
  4. Displays handoffs from OTHER devices in current conversation
  5. Commits cleaned handoff file back to git

Prerequisites:

  • Another device must have run /convosync:generate-handoff and /convosync:save
  • Git repository must be configured

Usage:

/convosync:resume

Execute the resume process:

python3 << 'RESUME_SCRIPT'
import subprocess
import sys
from pathlib import Path
from datetime import datetime, timezone
import re

print("🔄 ConvoSync: Resuming session...")
print()

# ============================================================================
# 1. Git pull
# ============================================================================

print("→ Pulling latest code and handoffs...")
try:
    subprocess.run(["git", "pull"], check=True, capture_output=True)
    commit_short = subprocess.run(['git', 'rev-parse', '--short', 'HEAD'], capture_output=True, text=True).stdout.strip()
    print(f"✓ Code pulled: {commit_short}")
except subprocess.CalledProcessError as e:
    print(f"⚠️  Git pull failed: {e.stderr}")
    print("  Continuing with local version...")

print()

# ============================================================================
# 2. Get device ID
# ============================================================================

def get_or_create_device_id():
    """Get unique device identifier, create if doesn't exist."""
    device_file = Path('.convosync/device-id')

    if device_file.exists():
        return device_file.read_text().strip()

    # Generate device ID from hostname
    try:
        hostname = subprocess.run(['hostname'], capture_output=True, text=True).stdout.strip()
        device_id = re.sub(r'[^a-zA-Z0-9-]', '-', hostname)
    except:
        device_id = 'unknown-device'

    # Prompt if generic hostname
    if device_id.lower() in ['localhost', 'android', 'termux', 'pc', 'unknown-device', '']:
        print("┌────────────────────────────────────────────────────────────────┐")
        print("│ DEVICE IDENTIFICATION                                          │")
        print("└────────────────────────────────────────────────────────────────┘")
        print()
        print(f"Detected hostname: '{device_id}'")
        print()
        print("Please give this device a friendly name")
        print("(e.g., 'desktop', 'mobile', 'laptop', 'work-laptop'):")
        print()
        try:
            user_input = input("Device name: ").strip()
            if user_input:
                device_id = re.sub(r'[^a-zA-Z0-9-]', '-', user_input)
        except:
            pass  # Keep default if input fails
        print()

    # Save device ID
    device_file.parent.mkdir(exist_ok=True)
    device_file.write_text(device_id)
    subprocess.run(['git', 'add', str(device_file)], check=False)

    return device_id

device_id = get_or_create_device_id()
print(f"→ Device: {device_id}")
print()

# ============================================================================
# 3. Check for handoff file
# ============================================================================

handoff_file = Path('.convosync/session-handoff.md')

if not handoff_file.exists():
    print("┌────────────────────────────────────────────────────────────────┐")
    print("│ ℹ️  NO HANDOFFS FOUND                                          │")
    print("└────────────────────────────────────────────────────────────────┘")
    print()
    print("No session handoffs found in this repository.")
    print()
    print("This is normal for:")
    print("  • First time using ConvoSync on this project")
    print("  • No other devices have saved sessions yet")
    print()
    print("To create a handoff:")
    print("  1. Run: /convosync:generate-handoff")
    print("  2. Ask Claude to generate the handoff")
    print("  3. Run: /convosync:save \"your message\"")
    print()
    sys.exit(0)

# ============================================================================
# 4. Parse handoff file
# ============================================================================

def parse_handoffs(content):
    """Parse handoff markdown file into structured data."""
    handoffs = []

    # Split on handoff headers (## Handoff from {device})
    sections = re.split(r'^---\s*$', content, flags=re.MULTILINE)

    for section in sections:
        section = section.strip()
        if not section or section.startswith('# ConvoSync'):
            continue

        # Extract device from header
        device_match = re.search(r'^## Handoff from (.+?)$', section, re.MULTILINE)
        if not device_match:
            continue

        device = device_match.group(1).strip()

        # Extract metadata
        timestamp_match = re.search(r'\*\*Timestamp:\*\* (.+?)$', section, re.MULTILINE)
        commit_match = re.search(r'\*\*Commit:\*\* (.+?)$', section, re.MULTILINE)
        branch_match = re.search(r'\*\*Branch:\*\* (.+?)$', section, re.MULTILINE)

        timestamp = timestamp_match.group(1).strip() if timestamp_match else None
        commit = commit_match.group(1).strip() if commit_match else None
        branch = branch_match.group(1).strip() if branch_match else None

        # Extract content (everything after metadata)
        content_start = section.find('**Branch:**')
        if content_start == -1:
            content_start = section.find('**Commit:**')
        if content_start == -1:
            content_start = section.find('**Timestamp:**')

        if content_start != -1:
            # Find end of metadata line
            content_start = section.find('\n', content_start)
            if content_start != -1:
                handoff_content = section[content_start:].strip()
            else:
                handoff_content = ''
        else:
            handoff_content = section

        handoffs.append({
            'device': device,
            'timestamp': timestamp,
            'commit': commit,
            'branch': branch,
            'content': handoff_content,
            'raw': section
        })

    return handoffs

content = handoff_file.read_text()
handoffs = parse_handoffs(content)

print(f"→ Found {len(handoffs)} handoff(s)")
print()

if not handoffs:
    print("⚠️  Handoff file exists but contains no valid handoffs")
    sys.exit(0)

# ============================================================================
# 5. Smart cleanup: Keep latest from current device, all from others
# ============================================================================

def cleanup_handoffs(handoffs, current_device):
    """Keep only latest from current device, all from other devices."""
    from_current = [h for h in handoffs if h['device'] == current_device]
    from_others = [h for h in handoffs if h['device'] != current_device]

    if from_current:
        # Sort by timestamp (most recent first)
        from_current.sort(key=lambda h: h['timestamp'] or '', reverse=True)
        kept_from_current = [from_current[0]]  # Keep only most recent
        removed_count = len(from_current) - 1
    else:
        kept_from_current = []
        removed_count = 0

    cleaned = from_others + kept_from_current

    return cleaned, removed_count

cleaned_handoffs, removed_count = cleanup_handoffs(handoffs, device_id)

if removed_count > 0:
    print(f"→ Cleaned up {removed_count} old handoff(s) from this device")

    # Write cleaned handoffs back to file
    new_content = "# ConvoSync Session Handoffs\n\n"
    for h in cleaned_handoffs:
        new_content += "---\n"
        new_content += h['raw'] + "\n\n"

    handoff_file.write_text(new_content)

    # Commit cleaned file
    subprocess.run(['git', 'add', str(handoff_file)], check=False)
    try:
        subprocess.run(
            ['git', 'commit', '-m', f'chore: cleanup old handoffs from {device_id}'],
            check=False,
            capture_output=True
        )
        print("  Committed cleanup")
    except:
        pass  # OK if nothing to commit

    print()

# ============================================================================
# 6. Display handoffs from OTHER devices
# ============================================================================

other_handoffs = [h for h in cleaned_handoffs if h['device'] != device_id]

if not other_handoffs:
    print("┌────────────────────────────────────────────────────────────────┐")
    print("│ ℹ️  NO HANDOFFS FROM OTHER DEVICES                             │")
    print("└────────────────────────────────────────────────────────────────┘")
    print()
    print("All handoffs in this repository are from this device.")
    print()
    print("To sync from another device:")
    print("  1. Switch to another device")
    print("  2. Run: /convosync:generate-handoff")
    print("  3. Run: /convosync:save \"message\"")
    print("  4. Return to this device and run: /convosync:resume")
    print()
    sys.exit(0)

# Format timestamps nicely
def time_ago(timestamp_str):
    """Convert ISO timestamp to human-readable time ago."""
    if not timestamp_str:
        return "unknown time"

    try:
        # Parse ISO format timestamp
        ts = datetime.fromisoformat(timestamp_str.replace('Z', '+00:00'))
        now = datetime.now(timezone.utc)
        delta = now - ts

        seconds = delta.total_seconds()

        if seconds < 60:
            return "just now"
        elif seconds < 3600:
            minutes = int(seconds / 60)
            return f"{minutes} minute{'s' if minutes != 1 else ''} ago"
        elif seconds < 86400:
            hours = int(seconds / 3600)
            return f"{hours} hour{'s' if hours != 1 else ''} ago"
        else:
            days = int(seconds / 86400)
            return f"{days} day{'s' if days != 1 else ''} ago"
    except:
        return timestamp_str

print()
print("╔════════════════════════════════════════════════════════════════════╗")
print("║ 📝 SESSION HANDOFFS FROM OTHER DEVICES                             ║")
print("╚════════════════════════════════════════════════════════════════════╝")
print()

for i, handoff in enumerate(other_handoffs, 1):
    print(f"{'═' * 70}")
    print(f"📱 Handoff {i}/{len(other_handoffs)} from: {handoff['device']}")
    print(f"{'═' * 70}")
    print()
    print(f"⏰ {time_ago(handoff['timestamp'])}")
    if handoff['commit']:
        print(f"📌 Commit: {handoff['commit'][:7]}")
    if handoff['branch']:
        print(f"🌿 Branch: {handoff['branch']}")
    print()
    print(handoff['content'])
    print()

print("╔════════════════════════════════════════════════════════════════════╗")
print("║ ✅ CONTEXT RESTORED!                                               ║")
print("╚════════════════════════════════════════════════════════════════════╝")
print()
print(f"Loaded {len(other_handoffs)} handoff(s) from other device(s).")
print()
print("I can now reference the session context above to continue your work!")
print()
print("When you're done on this device:")
print("  1. Run: /convosync:generate-handoff")
print("  2. Run: /convosync:save \"your message\"")
print()

RESUME_SCRIPT

Read the full file on GitHub · 407 lines

Changes

What this file has done since we first saw it

Hashed on every crawl. A supply-chain change to an agent config is a question of when, not whether, so the history is kept rather than the latest state alone.

  1. 2d ago First seen · 407 lines · 0 tokens per session scan A c115681389ff

Subscribe to this mod's changes

resume is a command published in the GitHub repository convocli/convosync-plugin (4 stars, last pushed 10mo ago), licensed MIT. It costs nothing until one of its globs matches a file; then it loads 3,113 tokens. A static security scan graded it A with 1 finding (runs shell commands). No closer match exists in the catalogue, so it is treated as the original; first seen 2026-08-31.