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