@@ -7,6 +7,7 @@ import { Locale } from "../../util/locale"
77import { Flag } from "../../flag/flag"
88import { EOL } from "os"
99import path from "path"
10+ import * as prompts from "@clack/prompts"
1011
1112function pagerCmd ( ) : string [ ] {
1213 const lessOptions = [ "-R" , "-S" ]
@@ -38,7 +39,7 @@ function pagerCmd(): string[] {
3839export const SessionCommand = cmd ( {
3940 command : "session" ,
4041 describe : "manage sessions" ,
41- builder : ( yargs : Argv ) => yargs . command ( SessionListCommand ) . demandCommand ( ) ,
42+ builder : ( yargs : Argv ) => yargs . command ( SessionListCommand ) . command ( SessionExportCommand ) . demandCommand ( ) ,
4243 async handler ( ) { } ,
4344} )
4445
@@ -133,3 +134,119 @@ function formatSessionJSON(sessions: Session.Info[]): string {
133134 } ) )
134135 return JSON . stringify ( jsonData , null , 2 )
135136}
137+
138+ export const SessionExportCommand = cmd ( {
139+ command : "export [sessionID]" ,
140+ describe : "export session transcript to file" ,
141+ builder : ( yargs : Argv ) => {
142+ return yargs
143+ . positional ( "sessionID" , {
144+ describe : "session id to export" ,
145+ type : "string" ,
146+ } )
147+ . option ( "output" , {
148+ alias : "o" ,
149+ describe : "output file path" ,
150+ type : "string" ,
151+ } )
152+ . option ( "format" , {
153+ alias : "f" ,
154+ describe : "output format" ,
155+ type : "string" ,
156+ choices : [ "markdown" , "json" ] ,
157+ default : "markdown" ,
158+ } )
159+ } ,
160+ handler : async ( args ) => {
161+ await bootstrap ( process . cwd ( ) , async ( ) => {
162+ let sessionID = args . sessionID
163+
164+ if ( ! sessionID ) {
165+ prompts . intro ( "Export session" , {
166+ output : process . stderr ,
167+ } )
168+
169+ const sessions = [ ]
170+ for await ( const session of Session . list ( ) ) {
171+ sessions . push ( session )
172+ }
173+
174+ if ( sessions . length === 0 ) {
175+ prompts . log . error ( "No sessions found" , {
176+ output : process . stderr ,
177+ } )
178+ prompts . outro ( "Done" , {
179+ output : process . stderr ,
180+ } )
181+ return
182+ }
183+
184+ sessions . sort ( ( a , b ) => b . time . updated - a . time . updated )
185+
186+ const selectedSession = await prompts . autocomplete ( {
187+ message : "Select session to export" ,
188+ maxItems : 10 ,
189+ options : sessions . map ( ( session ) => ( {
190+ label : session . title ,
191+ value : session . id ,
192+ hint : `${ new Date ( session . time . updated ) . toLocaleString ( ) } • ${ session . id . slice ( - 8 ) } ` ,
193+ } ) ) ,
194+ output : process . stderr ,
195+ } )
196+
197+ if ( prompts . isCancel ( selectedSession ) ) {
198+ throw new UI . CancelledError ( )
199+ }
200+
201+ sessionID = selectedSession as string
202+ }
203+
204+ const sessionInfo = await Session . get ( sessionID ! )
205+
206+ let content : string
207+ let defaultExtension : string
208+
209+ if ( args . format === "json" ) {
210+ const sessionMessages = await Session . messages ( { sessionID : sessionID ! } )
211+ const exportData = {
212+ info : sessionInfo ,
213+ messages : sessionMessages . map ( ( msg ) => ( {
214+ info : msg . info ,
215+ parts : msg . parts ,
216+ } ) ) ,
217+ }
218+ content = JSON . stringify ( exportData , null , 2 )
219+ defaultExtension = "json"
220+ } else {
221+ content = await Session . exportMarkdown ( {
222+ sessionID : sessionID ! ,
223+ includeThinking : true ,
224+ includeToolDetails : true ,
225+ } )
226+ defaultExtension = "md"
227+ }
228+
229+ const outputPath = await ( async ( ) => {
230+ if ( args . output ) return args . output
231+ const defaultFilename = `session-${ sessionInfo . id . slice ( 0 , 8 ) } .${ defaultExtension } `
232+ const filenameInput = await prompts . text ( {
233+ message : "Export filename" ,
234+ defaultValue : defaultFilename ,
235+ output : process . stderr ,
236+ } )
237+
238+ if ( prompts . isCancel ( filenameInput ) ) {
239+ throw new UI . CancelledError ( )
240+ }
241+
242+ return filenameInput . trim ( )
243+ } ) ( )
244+
245+ await Bun . write ( outputPath , content )
246+
247+ prompts . outro ( `Session exported to ${ outputPath } ` , {
248+ output : process . stderr ,
249+ } )
250+ } )
251+ } ,
252+ } )
0 commit comments