split_session
Split Claude Code conversation sessions at a specific message to create new sessions, organizing discussions by topic or project phase.
Instructions
Split a session at a specific message, creating a new session with messages from that point onwards
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| project_name | Yes | Project folder name | |
| session_id | Yes | Session ID to split | |
| message_uuid | Yes | UUID of the message where the split starts (this message becomes the first message of the new session) |
Implementation Reference
- src/lib/session.ts:349-451 (handler)The main handler function that executes the split_session tool logic. It reads the session file, finds the split point by message UUID, handles special cases like continuation summaries by duplicating them, creates a new session file with subsequent messages, updates parentUuid chains, and migrates related agent files.export const splitSession = (projectName: string, sessionId: string, splitAtMessageUuid: string) => Effect.gen(function* () { const projectPath = path.join(getSessionsDir(), projectName) const filePath = path.join(projectPath, `${sessionId}.jsonl`) const content = yield* Effect.tryPromise(() => fs.readFile(filePath, 'utf-8')) const lines = content.trim().split('\n').filter(Boolean) // Parse all messages preserving their full structure const allMessages = lines.map((line) => JSON.parse(line) as Record<string, unknown>) // Find the split point const splitIndex = allMessages.findIndex((m) => m.uuid === splitAtMessageUuid) if (splitIndex === -1) { return { success: false, error: 'Message not found' } satisfies SplitSessionResult } if (splitIndex === 0) { return { success: false, error: 'Cannot split at first message' } satisfies SplitSessionResult } // Generate new session ID const newSessionId = crypto.randomUUID() // Check if the split message is a continuation summary const splitMessage = allMessages[splitIndex] const shouldDuplicate = isContinuationSummary(splitMessage) // Split messages - if continuation summary, include it in both sessions let remainingMessages: Record<string, unknown>[] const movedMessages = allMessages.slice(splitIndex) if (shouldDuplicate) { // Create a copy of the continuation message with new UUID for the original session const duplicatedMessage: Record<string, unknown> = { ...splitMessage, uuid: crypto.randomUUID(), sessionId: sessionId, // Keep original session ID } remainingMessages = [...allMessages.slice(0, splitIndex), duplicatedMessage] } else { remainingMessages = allMessages.slice(0, splitIndex) } // Update moved messages with new sessionId and fix first message's parentUuid const updatedMovedMessages = movedMessages.map((msg, index) => { const updated: Record<string, unknown> = { ...msg, sessionId: newSessionId } if (index === 0) { // First message of new session should have no parent updated.parentUuid = null } return updated }) // Write remaining messages to original file const remainingContent = remainingMessages.map((m) => JSON.stringify(m)).join('\n') + '\n' yield* Effect.tryPromise(() => fs.writeFile(filePath, remainingContent, 'utf-8')) // Write moved messages to new session file const newFilePath = path.join(projectPath, `${newSessionId}.jsonl`) const newContent = updatedMovedMessages.map((m) => JSON.stringify(m)).join('\n') + '\n' yield* Effect.tryPromise(() => fs.writeFile(newFilePath, newContent, 'utf-8')) // Update linked agent files that reference the old sessionId const agentFiles = yield* Effect.tryPromise(() => fs.readdir(projectPath)) const agentJsonlFiles = agentFiles.filter((f) => f.startsWith('agent-') && f.endsWith('.jsonl')) for (const agentFile of agentJsonlFiles) { const agentPath = path.join(projectPath, agentFile) const agentContent = yield* Effect.tryPromise(() => fs.readFile(agentPath, 'utf-8')) const agentLines = agentContent.trim().split('\n').filter(Boolean) if (agentLines.length === 0) continue const firstAgentMsg = JSON.parse(agentLines[0]) as { sessionId?: string } // If this agent belongs to the original session, check if it should be moved if (firstAgentMsg.sessionId === sessionId) { // Check if any message in moved messages is related to this agent const agentId = agentFile.replace('agent-', '').replace('.jsonl', '') const isRelatedToMoved = movedMessages.some( (msg) => (msg as { agentId?: string }).agentId === agentId ) if (isRelatedToMoved) { // Update all messages in this agent file to reference new sessionId const updatedAgentMessages = agentLines.map((line) => { const msg = JSON.parse(line) as Record<string, unknown> return JSON.stringify({ ...msg, sessionId: newSessionId }) }) const updatedAgentContent = updatedAgentMessages.join('\n') + '\n' yield* Effect.tryPromise(() => fs.writeFile(agentPath, updatedAgentContent, 'utf-8')) } } } return { success: true, newSessionId, newSessionPath: newFilePath, movedMessageCount: movedMessages.length, duplicatedSummary: shouldDuplicate, } satisfies SplitSessionResult })
- src/lib/session.ts:328-335 (schema)Output type definition for the splitSession function, used in the tool response.export interface SplitSessionResult { success: boolean newSessionId?: string newSessionPath?: string movedMessageCount?: number duplicatedSummary?: boolean error?: string }
- src/mcp/index.ts:170-189 (registration)MCP server tool registration for 'split_session', defining input schema with Zod and calling the splitSession handler.server.tool( 'split_session', 'Split a session at a specific message, creating a new session with messages from that point onwards', { project_name: z.string().describe('Project folder name'), session_id: z.string().describe('Session ID to split'), message_uuid: z .string() .describe( 'UUID of the message where the split starts (this message becomes the first message of the new session)' ), }, async ({ project_name, session_id, message_uuid }) => { const result = await Effect.runPromise( session.splitSession(project_name, session_id, message_uuid) ) return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }], } }
- src/lib/session.ts:338-347 (helper)Helper utility to detect if a message is a continuation summary, which determines whether to duplicate it during split.const isContinuationSummary = (msg: Record<string, unknown>): boolean => { // isCompactSummary flag is set by Claude Code for continuation summaries if (msg.isCompactSummary === true) return true // Fallback: check message content if (msg.type !== 'user') return false const message = msg.message as { content?: string } | undefined const content = message?.content ?? '' return content.startsWith('This session is being continued from') }