Welcome to the final chapter of our series!
In Chapter 4: State & Metadata Preservation, we ensured that when we fork a conversation, we preserve all the complex "hidden" data and tool outputs. We have a perfect digital clone of the conversation history.
However, we are left with one human problem: What do we call this new file?
In this chapter, we will explore Dynamic Session Naming. We will learn how the system automatically generates readable, unique titles for your branches (like "React Fix (Branch 2)") so you never have to worry about accidentally overwriting your work.
Imagine you are on your computer's desktop. You right-click and create a "New Folder." What happens if you do it again? The operating system doesn't scream at you or crash. It simply names the second one "New Folder (2)."
The Problem:
/branch fix-login-bug-v2. They just want to type /branch and keep working.a1b2-c3d4 internally. These are great for computers, but terrible for humans. You want to see "Project A (Branch)," not "Session a1b2."The Solution: We create a utility that acts like the Operating System. It looks at the existing files, spots duplicates, and automatically appends a counter (1, 2, 3...) to ensure every branch has a unique identity.
There are three steps to generating a good name.
If the user provides a name (e.g., /branch MyTest), we use that.
But if they leave it blank, we look at the First Prompt of the conversation. We take the first few words of the very first message you sent (e.g., "Write a poem about cats") and use that as the title.
To distinguish the copy from the original, we always append a suffix.
Write a poemWrite a poem (Branch)Before saving, we check our database of sessions.
Here is what happens when the naming utility runs:
Let's look at the code inside branch.ts to see how this is implemented.
deriveFirstPrompt)First, we need a fallback name if the user didn't provide one. We look at the first message in the chat history.
export function deriveFirstPrompt(firstUserMessage): string {
// 1. Get the text content of the message
const content = firstUserMessage?.message?.content
if (!content) return 'Branched conversation'
// 2. Clean it up: Remove extra spaces and newlines
// 3. Cut it off at 100 characters so the title isn't huge
return content.replace(/\s+/g, ' ').trim().slice(0, 100)
}
Explanation:
firstUserMessage: The very first thing you typed in the chat.replace(/\s+/g, ' '): If you pasted a 50-line error log, this squashes all those newlines into single spaces so the title fits on one line.getUniqueForkName)This is the "smart" part of the system. It ensures we never overwrite data.
First, we check the most obvious name:
async function getUniqueForkName(baseName: string): Promise<string> {
const candidateName = `${baseName} (Branch)`
// Check the storage: Is this name taken exactly?
const exists = await searchSessionsByCustomTitle(candidateName, { exact: true })
// If nobody is using it, we take it!
if (exists.length === 0) {
return candidateName
}
// ... otherwise, we need to count.
}
If the basic name is taken, we look for existing numbered branches to find the next available slot.
// ... inside getUniqueForkName
// Find all existing sessions that look like "Name (Branch X)"
const existingForks = await searchSessionsByCustomTitle(`${baseName} (Branch`)
// Create a list of numbers that are already taken
const usedNumbers = new Set<number>()
// (Complex logic here extracts numbers like 2, 3, 4 from the names)
// ...
// Find the first free number
let nextNumber = 2
while (usedNumbers.has(nextNumber)) {
nextNumber++ // If 2 is taken, try 3...
}
return `${baseName} (Branch ${nextNumber})`
Explanation:
searchSessionsByCustomTitle: This searches our persistent storage (which we discussed in Chapter 3: Transcript Persistence Model).while loop: This keeps counting up until it finds a number that isn't in the usedNumbers list.Finally, back in the main command function, we save this new title to the session metadata.
// 1. Get the base name (User input OR first prompt)
const baseName = customTitle ?? firstPrompt
// 2. Calculate the unique name (e.g., "Project (Branch 2)")
const effectiveTitle = await getUniqueForkName(baseName)
// 3. Save it permanently to disk
await saveCustomTitle(sessionId, effectiveTitle, forkPath)
Beginner Note: saveCustomTitle writes a small metadata file next to the transcript so the application remembers "Session 550e84..." is actually named "Project (Branch 2)".
Congratulations! You have completed the Branch project tutorial.
In this series, we have built a complete feature from scratch:
/branch.You now understand the full lifecycle of a complex CLI feature, from the user typing a command to the bits being written on the hard drive. Happy coding!
Generated by Code IQ