MCPcopy Index your code
hub / github.com/codeaashu/claude-code / createFork

Function createFork

src/commands/branch/branch.ts:61–173  ·  view source on GitHub ↗

* Creates a fork of the current conversation by copying from the transcript file. * Preserves all original metadata (timestamps, gitBranch, etc.) while updating * sessionId and adding forkedFrom traceability.

(customTitle?: string)

Source from the content-addressed store, hash-verified

59 * sessionId and adding forkedFrom traceability.
60 */
61async function createFork(customTitle?: string): Promise<{
62 sessionId: UUID
63 title: string | undefined
64 forkPath: string
65 serializedMessages: SerializedMessage[]
66 contentReplacementRecords: ContentReplacementEntry['replacements']
67}> {
68 const forkSessionId = randomUUID() as UUID
69 const originalSessionId = getSessionId()
70 const projectDir = getProjectDir(getOriginalCwd())
71 const forkSessionPath = getTranscriptPathForSession(forkSessionId)
72 const currentTranscriptPath = getTranscriptPath()
73
74 // Ensure project directory exists
75 await mkdir(projectDir, { recursive: true, mode: 0o700 })
76
77 // Read current transcript file
78 let transcriptContent: Buffer
79 try {
80 transcriptContent = await readFile(currentTranscriptPath)
81 } catch {
82 throw new Error('No conversation to branch')
83 }
84
85 if (transcriptContent.length === 0) {
86 throw new Error('No conversation to branch')
87 }
88
89 // Parse all transcript entries (messages + metadata entries like content-replacement)
90 const entries = parseJSONL<Entry>(transcriptContent)
91
92 // Filter to only main conversation messages (exclude sidechains and non-message entries)
93 const mainConversationEntries = entries.filter(
94 (entry): entry is TranscriptMessage =>
95 isTranscriptMessage(entry) && !entry.isSidechain,
96 )
97
98 // Content-replacement entries for the original session. These record which
99 // tool_result blocks were replaced with previews by the per-message budget.
100 // Without them in the fork JSONL, `claude -r {forkId}` reconstructs state
101 // with an empty replacements Map → previously-replaced results are classified
102 // as FROZEN and sent as full content (prompt cache miss + permanent overage).
103 // sessionId must be rewritten since loadTranscriptFile keys lookup by the
104 // session's messages' sessionId.
105 const contentReplacementRecords = entries
106 .filter(
107 (entry): entry is ContentReplacementEntry =>
108 entry.type === 'content-replacement' &&
109 entry.sessionId === originalSessionId,
110 )
111 .flatMap(entry => entry.replacements)
112
113 if (mainConversationEntries.length === 0) {
114 throw new Error('No messages to branch')
115 }
116
117 // Build forked entries with new sessionId and preserved metadata
118 let parentUuid: UUID | null = null

Callers 1

callFunction · 0.85

Calls 11

parseJSONLFunction · 0.90
getSessionIdFunction · 0.85
getOriginalCwdFunction · 0.85
getTranscriptPathFunction · 0.85
mkdirFunction · 0.85
readFileFunction · 0.85
isTranscriptMessageFunction · 0.85
jsonStringifyFunction · 0.85
getProjectDirFunction · 0.50
pushMethod · 0.45

Tested by

no test coverage detected