@@ -5,7 +5,7 @@ import 'dotenv/config'
55import { execSync } from 'node:child_process'
66
77import { createOpenAI } from '@ai-sdk/openai'
8- import { confirm , log , outro , select , spinner , text } from '@clack/prompts'
8+ import { confirm , outro , select , spinner , text } from '@clack/prompts'
99import { CoreMessage , generateText } from 'ai'
1010import * as tools from 'cali-tools'
1111import chalk from 'chalk'
@@ -19,11 +19,16 @@ const MessageSchema = z.union([
1919 z . object ( { type : z . literal ( 'select' ) , content : z . string ( ) , options : z . array ( z . string ( ) ) } ) ,
2020 z . object ( { type : z . literal ( 'question' ) , content : z . string ( ) } ) ,
2121 z . object ( { type : z . literal ( 'confirmation' ) , content : z . string ( ) } ) ,
22- z . object ( { type : z . literal ( 'end' ) , content : z . string ( ) } ) ,
22+ z . object ( { type : z . literal ( 'end' ) } ) ,
2323] )
2424
2525console . clear ( )
2626
27+ process . on ( 'uncaughtException' , ( error ) => {
28+ console . error ( chalk . red ( error . message ) )
29+ console . log ( chalk . gray ( error . stack ) )
30+ } )
31+
2732console . log (
2833 retro ( `
2934 ██████╗ █████╗ ██╗ ██╗
@@ -43,38 +48,39 @@ console.log(
4348 ` )
4449)
4550
46- console . log ( )
47-
4851const OPENAI_API_KEY =
4952 process . env . OPENAI_API_KEY ||
5053 ( await ( async ( ) => {
51- let apiKey : string | symbol
52- do {
53- apiKey = await text ( {
54- message : dedent `
55- ${ chalk . bold ( 'Please provide your OpenAI API key.' ) }
56-
57- To skip this message, set ${ chalk . bold ( 'OPENAI_API_KEY' ) } env variable, and run again.
58-
59- You can do it in three ways:
60- - by creating an ${ chalk . bold ( '.env.local' ) } file (make sure to ${ chalk . bold ( '.gitignore' ) } it)
61- ${ chalk . gray ( `\`\`\`
62- OPENAI_API_KEY=<your-key>
63- \`\`\`
64- ` ) }
65- - by passing it inline:
66- ${ chalk . gray ( `\`\`\`
67- OPENAI_API_KEY=<your-key> npx cali
68- \`\`\`
69- ` ) }
70- - by setting it as an env variable in your shell (e.g. in ~/.zshrc or ~/.bashrc):
71- ${ chalk . gray ( `\`\`\`
72- export OPENAI_API_KEY=<your-key>
73- \`\`\`
74- ` ) } ,
75- ` ,
76- } )
77- } while ( typeof apiKey !== 'string' )
54+ const apiKey = await text ( {
55+ message : dedent `
56+ ${ chalk . bold ( 'Please provide your OpenAI API key.' ) }
57+
58+ To skip this message, set ${ chalk . bold ( 'OPENAI_API_KEY' ) } env variable, and run again.
59+
60+ You can do it in three ways:
61+ - by creating an ${ chalk . bold ( '.env.local' ) } file (make sure to ${ chalk . bold ( '.gitignore' ) } it)
62+ ${ chalk . gray ( `\`\`\`
63+ OPENAI_API_KEY=<your-key>
64+ \`\`\`
65+ ` ) }
66+ - by passing it inline:
67+ ${ chalk . gray ( `\`\`\`
68+ OPENAI_API_KEY=<your-key> npx cali
69+ \`\`\`
70+ ` ) }
71+ - by setting it as an env variable in your shell (e.g. in ~/.zshrc or ~/.bashrc):
72+ ${ chalk . gray ( `\`\`\`
73+ export OPENAI_API_KEY=<your-key>
74+ \`\`\`
75+ ` ) } ,
76+ ` ,
77+ validate : ( value ) => ( value . length > 0 ? undefined : 'Please provide a valid answer.' ) ,
78+ } )
79+
80+ if ( typeof apiKey === 'symbol' ) {
81+ outro ( chalk . gray ( 'Bye!' ) )
82+ process . exit ( 0 )
83+ }
7884
7985 const save = await confirm ( {
8086 message : 'Do you want to save it for future runs in `.env.local`?' ,
@@ -94,26 +100,31 @@ const openai = createOpenAI({
94100 apiKey : OPENAI_API_KEY ,
95101} )
96102
97- const question = await text ( {
98- message : 'What do you want to do today?' ,
99- placeholder : 'e.g. "Build the app" or "See available simulators"' ,
100- } )
103+ async function startSession ( ) : Promise < CoreMessage [ ] > {
104+ const question = await text ( {
105+ message : 'What do you want to do today?' ,
106+ placeholder : 'e.g. "Build the app" or "See available simulators"' ,
107+ validate : ( value ) => ( value . length > 0 ? undefined : 'Please provide a valid answer.' ) ,
108+ } )
101109
102- if ( typeof question === 'symbol' ) {
103- outro ( chalk . gray ( 'Bye!' ) )
104- process . exit ( 0 )
110+ if ( typeof question === 'symbol' ) {
111+ outro ( chalk . gray ( 'Bye!' ) )
112+ process . exit ( 0 )
113+ }
114+
115+ return [
116+ {
117+ role : 'system' ,
118+ content : 'What do you want to do today?' ,
119+ } ,
120+ {
121+ role : 'user' ,
122+ content : question ,
123+ } ,
124+ ]
105125}
106126
107- const messages : CoreMessage [ ] = [
108- {
109- role : 'system' ,
110- content : 'What do you want to do today?' ,
111- } ,
112- {
113- role : 'user' ,
114- content : question ,
115- } ,
116- ]
127+ let messages = await startSession ( )
117128
118129const s = spinner ( )
119130
@@ -195,20 +206,21 @@ while (true) {
195206 options : data . options . map ( ( option ) => ( { value : option , label : option } ) ) ,
196207 } )
197208 case 'question' :
198- return text ( { message : data . content } )
209+ return text ( {
210+ message : data . content ,
211+ validate : ( value ) => ( value . length > 0 ? undefined : 'Please provide a valid answer.' ) ,
212+ } )
199213 case 'confirmation' : {
200214 return confirm ( { message : data . content } ) . then ( ( answer ) => {
201215 return answer ? 'yes' : 'no'
202216 } )
203217 }
204- case 'end' :
205- log . step ( data . content )
206218 }
207219 } ) ( )
208220
209221 if ( typeof answer !== 'string' ) {
210- outro ( chalk . gray ( 'Bye!' ) )
211- break
222+ messages = await startSession ( )
223+ continue
212224 }
213225
214226 messages . push ( {
0 commit comments