11#!/usr/bin/env node
22import fs from 'fs' ;
33import { pathToFileURL } from 'url'
4+ import { KeyvFile } from 'keyv-file' ;
45import ChatGPTClient from '../src/ChatGPTClient.js' ;
56import boxen from 'boxen' ;
67import ora from 'ora' ;
78import clipboard from 'clipboardy' ;
89import inquirer from 'inquirer' ;
9- import { KeyvFile } from 'keyv-file ' ;
10+ import inquirerAutocompletePrompt from 'inquirer-autocomplete-prompt ' ;
1011
1112const arg = process . argv . find ( ( arg ) => arg . startsWith ( '--settings' ) ) ;
1213let path ;
@@ -41,26 +42,83 @@ if (settings.storageFilePath && !settings.cacheOptions.store) {
4142 }
4243
4344 settings . cacheOptions . store = new KeyvFile ( { filename : settings . storageFilePath } ) ;
44- // TODO: actually do something with this
4545}
4646
47+ let conversationId = null ;
48+ let parentMessageId = null ;
49+
50+ const availableCommands = [
51+ {
52+ name : '!resume - Resume last conversation' ,
53+ value : '!resume' ,
54+ } ,
55+ {
56+ name : '!new - Start new conversation' ,
57+ value : '!new' ,
58+ } ,
59+ {
60+ name : '!copy - Copy conversation to clipboard' ,
61+ value : '!copy' ,
62+ } ,
63+ {
64+ name : '!delete-all - Delete all conversations' ,
65+ value : '!delete-all' ,
66+ } ,
67+ {
68+ name : '!exit - Exit ChatGPT CLI' ,
69+ value : '!exit' ,
70+ } ,
71+ ] ;
72+
73+ inquirer . registerPrompt ( 'autocomplete' , inquirerAutocompletePrompt ) ;
74+
4775const chatGptClient = new ChatGPTClient ( settings . openaiApiKey , settings . chatGptClient , settings . cacheOptions ) ;
4876
4977console . log ( boxen ( 'ChatGPT CLI' , { padding : 0.7 , margin : 1 , borderStyle : 'double' , dimBorder : true } ) ) ;
5078
5179await conversation ( ) ;
5280
53- async function conversation ( conversationId = null , parentMessageId = null ) {
54- const { message } = await inquirer . prompt ( [
81+ async function conversation ( ) {
82+ console . log ( 'Type "!" to access the command menu.' ) ;
83+ const prompt = inquirer . prompt ( [
5584 {
56- type : 'input ' ,
85+ type : 'autocomplete ' ,
5786 name : 'message' ,
5887 message : 'Write a message:' ,
88+ searchText : '' ,
89+ emptyText : '' ,
90+ source : ( answers , input ) => {
91+ return Promise . resolve (
92+ input ? availableCommands . filter ( ( command ) => command . value . startsWith ( input ) ) : [ ]
93+ ) ;
94+ }
5995 } ,
6096 ] ) ;
61- if ( message === '!exit' ) {
62- return true ;
97+ // hiding the ugly autocomplete hint
98+ prompt . ui . activePrompt . firstRender = false ;
99+ let { message } = await prompt ;
100+ message = message . trim ( ) ;
101+ if ( ! message ) {
102+ return conversation ( ) ;
103+ }
104+ if ( message . startsWith ( '!' ) ) {
105+ switch ( message ) {
106+ case '!resume' :
107+ return resumeConversation ( ) ;
108+ case '!new' :
109+ return newConversation ( ) ;
110+ case '!copy' :
111+ return copyConversation ( ) ;
112+ case '!delete-all' :
113+ return deleteAllConversations ( ) ;
114+ case '!exit' :
115+ return true ;
116+ }
63117 }
118+ return onMessage ( message ) ;
119+ }
120+
121+ async function onMessage ( message ) {
64122 const chatGptLabel = settings . chatGptClient ?. chatGptLabel || 'ChatGPT' ;
65123 const spinner = ora ( `${ chatGptLabel } is typing...` ) ;
66124 spinner . prefixText = '\n' ;
@@ -71,10 +129,68 @@ async function conversation(conversationId = null, parentMessageId = null) {
71129 spinner . stop ( ) ;
72130 conversationId = response . conversationId ;
73131 parentMessageId = response . messageId ;
132+ await chatGptClient . conversationsCache . set ( 'lastConversation' , {
133+ conversationId,
134+ parentMessageId,
135+ } ) ;
74136 console . log ( boxen ( response . response , { title : chatGptLabel , padding : 0.7 , margin : 1 , dimBorder : true } ) ) ;
75137 } catch ( error ) {
76138 spinner . stop ( ) ;
77- console . log ( boxen ( error ?. json ?. error ?. message || error . body , { title : 'Error' , padding : 0.7 , margin : 1 , borderColor : 'red' } ) ) ;
139+ logError ( error ?. json ?. error ?. message || error . body ) ;
140+ }
141+ return conversation ( ) ;
142+ }
143+
144+ async function resumeConversation ( ) {
145+ ( { conversationId, parentMessageId } = ( await chatGptClient . conversationsCache . get ( 'lastConversation' ) ) || { } ) ;
146+ if ( conversationId ) {
147+ logSuccess ( `Resumed conversation ${ conversationId } .` ) ;
148+ } else {
149+ logWarning ( 'No conversation to resume.' ) ;
150+ }
151+ return conversation ( ) ;
152+ }
153+
154+ async function newConversation ( ) {
155+ conversationId = null ;
156+ parentMessageId = null ;
157+ logSuccess ( 'Started new conversation.' ) ;
158+ return conversation ( ) ;
159+ }
160+
161+ async function deleteAllConversations ( ) {
162+ await chatGptClient . conversationsCache . clear ( ) ;
163+ logSuccess ( 'Deleted all conversations.' ) ;
164+ return conversation ( ) ;
165+ }
166+
167+ async function copyConversation ( ) {
168+ if ( ! conversationId ) {
169+ logWarning ( 'No conversation to copy.' ) ;
170+ return conversation ( ) ;
171+ }
172+ const { messages } = await chatGptClient . conversationsCache . get ( conversationId ) ;
173+ // get the last message ID
174+ const lastMessageId = messages [ messages . length - 1 ] . id ;
175+ const orderedMessages = ChatGPTClient . getMessagesForConversation ( messages , lastMessageId ) ;
176+ const conversationString = orderedMessages . map ( ( message ) => `#### ${ message . role } :\n${ message . message } ` ) . join ( '\n\n' ) ;
177+ try {
178+ await clipboard . write ( `${ conversationString } \n\n----\nMade with ChatGPT CLI: <https://github.com/waylaidwanderer/node-chatgpt-api>` ) ;
179+ logSuccess ( 'Copied conversation to clipboard.' ) ;
180+ } catch ( error ) {
181+ logError ( error ?. message || error ) ;
78182 }
79- return conversation ( conversationId , parentMessageId ) ;
183+ return conversation ( ) ;
184+ }
185+
186+ function logError ( message ) {
187+ console . log ( boxen ( message , { title : 'Error' , padding : 0.7 , margin : 1 , borderColor : 'red' } ) ) ;
188+ }
189+
190+ function logSuccess ( message ) {
191+ console . log ( boxen ( message , { title : 'Success' , padding : 0.7 , margin : 1 , borderColor : 'green' } ) ) ;
192+ }
193+
194+ function logWarning ( message ) {
195+ console . log ( boxen ( message , { title : 'Warning' , padding : 0.7 , margin : 1 , borderColor : 'yellow' } ) ) ;
80196}
0 commit comments