← the whole session plugin/skills/session-titles-name/SKILL.md
Rename unnamed Claude Code sessions with intent-rich titles using batched haiku subagents. Dispatches as background orchestrator task.
/session-titles-name — Batch Session Renaming
Renames unnamed Claude Code sessions with rich intent-capturing titles using cost-optimized haiku subagents. Runs as a background /orchestrator task via sonnet subagent.
How It Works
- Scan all session JSONL files for those missing a
custom-titleentry - Extract first 3 real user messages (skip
isMeta,<command>,<local-command>,<task-notification>entries) - Batch sessions into groups of ~55 per haiku agent call
- Generate titles via parallel haiku subagents
- Write two JSONL entries per session:
custom-titleandagent-name
Title Quality Requirements
Each title must answer ALL of these:
- What was this session about and what outcome did it aspire to create?
- What question did it seek to answer?
- What overarching initiative did it connect to?
- What specific UX intent or possibility was it exploring?
- What did it accomplish or try to?
Rules:
- NO repo names (whatever your own projects are called) — noise, since they're implied by which project the session ran in
- NO fluff words unless they carry unique signal
- State the UNIQUE focus in terms of the OUTCOME it aspired to create
- Distinguish: researching vs planning vs building vs automating vs fixing vs exploring
- Be specific about WHAT, not generic categories
- Each title ends with
C{num}*L{num}(complexity 1-100, leverage 1-100)
Step 1: Find Unnamed Sessions
import json, os, glob
sessions_dir = os.path.expanduser("~/.claude/projects/<your-project-slug>/") # find yours with: ls ~/.claude/projects/
files = sorted(glob.glob(sessions_dir + "*.jsonl"), key=os.path.getmtime, reverse=True)[:1000]
unnamed = []
for f in files:
sid = os.path.basename(f).replace(".jsonl", "")
has_title = False
with open(f) as fh:
for line in fh:
d = json.loads(line)
if d.get("type") == "custom-title":
has_title = True
break
if not has_title:
msgs = []
with open(f) as fh:
for line in fh:
d = json.loads(line)
if d.get("type") == "user" and not d.get("isMeta"):
content = d.get("message", {}).get("content", "")
if isinstance(content, str) and "<command" not in content and "<local-command" not in content and "<task-notification" not in content and len(content) > 15:
msgs.append(content[:200].replace("\n", " "))
if len(msgs) >= 3:
break
preview = " | ".join(msgs)[:300]
if preview.strip():
unnamed.append((sid, preview))
Step 2: Build Batch Prompts
Split unnamed sessions into batches of ~55. Each batch prompt contains:
You are a session title generator for a person's Claude Code sessions...
[title quality requirements from above]
Format: Each title ends with C{num}*L{num} (complexity 1-100, leverage 1-100)
Output ONLY lines like:
SID:full-session-id TITLE:Your descriptive title here C45*L82
Sessions:
SID:{session-id}
Messages: {preview}
---
Write each batch to /tmp/session-batch-{i}.txt.
Step 3: Dispatch Parallel Haiku Agents
Launch one haiku Agent per batch file, all in parallel with run_in_background: true:
Agent(model: haiku, prompt: "Read /tmp/session-batch-{i}.txt and follow its instructions exactly. Output ONLY the SID:xxx TITLE:xxx lines.")
Step 4: Parse Results and Write JSONL
After all agents complete, read their output files. Parse lines matching:
SID:([a-f0-9-]+)\s+TITLE:(.+?C\d+\*L\d+)
For each match, append two entries to the session's JSONL file:
{"type": "custom-title", "customTitle": "...", "sessionId": "..."}
{"type": "agent-name", "agentName": "...", "sessionId": "..."}
Important: Remove any existing custom-title and agent-name entries first (rewrite the file without them, then append new ones).
Cost
- ~$0.02 per batch of 55 sessions (haiku pricing)
- 414 sessions = 8 batches = ~$0.16 total
- ~$0.0004 per session rename
Overwriting Existing Titles
To re-title sessions that already have names, modify Step 1 to skip the has_title check. The write step already handles removing old title entries.
Empty Sessions
Sessions with no real user messages (only system/meta/command entries) are skipped — nothing to derive intent from.