Skip to content

Commit

Permalink
feat: add a system command to toggle input mode
Browse files Browse the repository at this point in the history
  • Loading branch information
reugn committed Mar 5, 2024
1 parent 3f9b170 commit 4a5a6e6
Show file tree
Hide file tree
Showing 3 changed files with 42 additions and 32 deletions.
9 changes: 5 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,10 +36,11 @@ To use `gemini-cli`, you'll need an API key set in the `GEMINI_API_KEY` environm
The system chat message must begin with an exclamation mark and is used for internal operations.
A short list of supported system commands:

| Command | Descripton |
| --- | --- |
| !q | Quit the application |
| !p | Purge the chat history |
| Command | Descripton
| --- | ---
| !q | Quit the application
| !p | Delete the history used as chat context by the model
| !m | Toggle input mode (single-line <-> multi-line)

### CLI help
```console
Expand Down
7 changes: 5 additions & 2 deletions cli/chat.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,9 @@ func NewChat(user string, model *gemini.ChatSession, opts *ChatOpts) (*Chat, err
}
prompt := newPrompt(user)
reader.SetPrompt(prompt.user)
if opts.Multiline {
reader.HistoryDisable()
}
return &Chat{
model: model,
prompt: prompt,
Expand Down Expand Up @@ -93,9 +96,9 @@ func (c *Chat) readMultiLine() (string, bool) {

func (c *Chat) parseCommand(message string) command {
if strings.HasPrefix(message, systemCmdPrefix) {
return newSystemCommand(c.model, c.prompt)
return newSystemCommand(c)
}
return newGeminiCommand(c.model, c.prompt, c.opts)
return newGeminiCommand(c)
}

func (c *Chat) handleReadError(err error) (string, bool) {
Expand Down
58 changes: 32 additions & 26 deletions cli/command.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,78 +10,84 @@ import (

"github.com/charmbracelet/glamour"
"github.com/reugn/gemini-cli/cli/color"
"github.com/reugn/gemini-cli/gemini"
"google.golang.org/api/iterator"
)

const (
systemCmdPrefix = "!"
systemCmdQuit = "!q"
systemCmdPrefix = "!"
systemCmdQuit = "!q"
systemCmdPurgeHistory = "!p"
systemCmdToggleInputMode = "!m"
)

type command interface {
run(message string) bool
}

type systemCommand struct {
model *gemini.ChatSession
prompt *prompt
chat *Chat
}

var _ command = (*systemCommand)(nil)

func newSystemCommand(model *gemini.ChatSession, prompt *prompt) command {
func newSystemCommand(chat *Chat) command {
return &systemCommand{
model: model,
prompt: prompt,
chat: chat,
}
}

func (c *systemCommand) run(message string) bool {
message = strings.TrimPrefix(message, systemCmdPrefix)
switch message {
case "q":
case systemCmdQuit:
c.print("Exiting gemini-cli...")
return true
case "p":
c.model.ClearHistory()
case systemCmdPurgeHistory:
c.chat.model.ClearHistory()
c.print("Cleared the chat history.")
case systemCmdToggleInputMode:
if c.chat.opts.Multiline {
c.print("Switched to single-line input mode.")
c.chat.reader.HistoryEnable()
c.chat.opts.Multiline = false
} else {
c.print("Switched to multi-line input mode.")
// disable history for multi-line messages since it is
// unusable for future requests
c.chat.reader.HistoryDisable()
c.chat.opts.Multiline = true
}
default:
c.print("Unknown system command.")
}
return false
}

func (c *systemCommand) print(message string) {
fmt.Printf("%s%s\n", c.prompt.cli, message)
fmt.Printf("%s%s\n", c.chat.prompt.cli, message)
}

type geminiCommand struct {
model *gemini.ChatSession
prompt *prompt
chat *Chat
spinner *spinner
writer *bufio.Writer
opts *ChatOpts
}

var _ command = (*geminiCommand)(nil)

func newGeminiCommand(model *gemini.ChatSession, prompt *prompt, opts *ChatOpts) command {
func newGeminiCommand(chat *Chat) command {
writer := bufio.NewWriter(os.Stdout)
return &geminiCommand{
model: model,
prompt: prompt,
chat: chat,
spinner: newSpinner(5, time.Second, writer),
writer: writer,
opts: opts,
}
}

func (c *geminiCommand) run(message string) bool {
c.printFlush(c.prompt.gemini)
c.printFlush(c.chat.prompt.gemini)
c.spinner.start()
if c.opts.Format {
// requires full markdown for formatting
if c.chat.opts.Format {
// requires the entire response to be formatted
c.runBlocking(message)
} else {
c.runStreaming(message)
Expand All @@ -90,7 +96,7 @@ func (c *geminiCommand) run(message string) bool {
}

func (c *geminiCommand) runBlocking(message string) {
response, err := c.model.SendMessage(message)
response, err := c.chat.model.SendMessage(message)
c.spinner.stop()
if err != nil {
fmt.Println(color.Red(err.Error()))
Expand All @@ -101,7 +107,7 @@ func (c *geminiCommand) runBlocking(message string) {
buf.WriteString(fmt.Sprintf("%s", part))
}
}
output, err := glamour.Render(buf.String(), c.opts.Style)
output, err := glamour.Render(buf.String(), c.chat.opts.Style)
if err != nil {
fmt.Printf(color.Red("Failed to format: %s\n"), err)
fmt.Println(buf.String())
Expand All @@ -112,7 +118,7 @@ func (c *geminiCommand) runBlocking(message string) {
}

func (c *geminiCommand) runStreaming(message string) {
responseIterator := c.model.SendMessageStream(message)
responseIterator := c.chat.model.SendMessageStream(message)
c.spinner.stop()
for {
response, err := responseIterator.Next()
Expand Down

0 comments on commit 4a5a6e6

Please sign in to comment.