Prefixes every Claude Code session title with its project folder: 'Bee Buzz · fix login flow'.

Claude Code session titles that start with the project folder.
Fix login flow → Bee Buzz · Fix login flow
Triage flaky eval → guardian · Triage flaky eval
When you run five sessions across three repos, /resume and the session list stop being a wall of near-identical titles.
At the prompt of a Claude Code terminal session:
/plugin install session-namer --marketplace vigneshbarani24/claude-session-namer
Answer y to add the marketplace, then pick a scope (user scope covers every project). It's active immediately.
Uninstall with /plugin uninstall session-namer.
The mod hooks SessionStart and UserPromptSubmit and returns sessionTitle with the project folder put in front of the title the session already has, whether Claude Code generated it or you set it with /rename.
<repo>/.claude/worktrees/<name> is named after <repo>.Set them in /config or under pluginConfigs.session-namer.options in settings.
| Option | Default | Effect |
|---|---|---|
depth | 1 | Trailing folders to show. 2 gives Amazon Build 2026/Bee Buzz. |
separator | · | Text between the folder and the name. |
A Claude Code build with function hooks (mods). Built and tested on Claude Code 2.1.295.
claude plugin validate .
claude plugin test .
claude --plugin-dir .
Once Claude Code has loaded the mod it lays its types in .claude-plugin/types/, and npx tsc -p . type-checks it.
MIT
hooks/register.ts 35 lines1import type { Register } from 'claude-code'
2
3// A worktree session runs in <repo>/.claude/worktrees/<name>; the repo folder is the useful name.
4const WORKTREE = /[\\/]\.claude[\\/]worktrees[\\/][^\\/]+[\\/]?$/
5
6export function projectLabel(cwd: string, depth: number): string {
7 const segments = cwd.replace(WORKTREE, '').split(/[\\/]+/).filter(s => s !== '' && !/^[A-Za-z]:$/.test(s))
8 return segments.slice(-Math.max(1, Math.floor(depth))).join('/')
9}
10
11export function prefixedTitle(title: string | undefined, label: string, separator: string): string | undefined {
12 const name = title?.trim()
13 if (!name || label === '') return undefined
14 if (name === label || name.startsWith(label + separator)) return undefined
15 return label + separator + name
16}
17
18export const register: Register = (on, options) => {
19 const depth = typeof options.depth === 'number' ? options.depth : 1
20 const separator = typeof options.separator === 'string' && options.separator !== '' ? options.separator : ' · '
21
22 // Only prefix a title that exists: setting one where there is none would pre-empt Claude Code's own naming.
23 on('classic.SessionStart', async ($, e, next) => {
24 const result = await next(e)
25 const sessionTitle = prefixedTitle(result.sessionTitle ?? e.session_title, projectLabel(e.cwd, depth), separator)
26 return sessionTitle === undefined ? result : { ...result, sessionTitle }
27 }).catch(($, e, next) => next(e)) // a naming failure must never hold up a session or a prompt
28
29 on('classic.UserPromptSubmit', async ($, e, next) => {
30 const result = await next(e)
31 const sessionTitle = prefixedTitle(result.sessionTitle ?? e.session_title, projectLabel(e.cwd, depth), separator)
32 return sessionTitle === undefined ? result : { ...result, sessionTitle }
33 }).catch(($, e, next) => next(e))
34}
35