Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,9 @@ Usage:
Flags:
-f, --format render markdown-formatted response (default true)
-h, --help help for this command
-m, --multiline read input as a multi-line string
-s, --style string markdown format style (ascii, dark, light, pink, notty, dracula) (default "auto")
-t, --term string multi-line input terminator (default "$")
-v, --version version for this command
```

Expand Down
57 changes: 48 additions & 9 deletions cli/chat.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package cli

import (
"errors"
"fmt"
"strings"

Expand All @@ -10,8 +11,10 @@ import (

// ChatOpts represents Chat configuration options.
type ChatOpts struct {
Format bool
Style string
Format bool
Style string
Multiline bool
Terminator string
}

// Chat controls the chat flow.
Expand Down Expand Up @@ -41,7 +44,7 @@ func NewChat(user string, model *gemini.ChatSession, opts *ChatOpts) (*Chat, err
// StartChat starts the chat loop.
func (c *Chat) StartChat() {
for {
message, ok := c.readLine()
message, ok := c.read()
if !ok {
continue
}
Expand All @@ -52,17 +55,40 @@ func (c *Chat) StartChat() {
}
}

func (c *Chat) read() (string, bool) {
if c.opts.Multiline {
return c.readMultiLine()
}
return c.readLine()
}

func (c *Chat) readLine() (string, bool) {
input, err := c.reader.Readline()
if err != nil {
fmt.Printf("%s%s\n", c.prompt.cli, err)
return "", false
return c.handleReadError(err)
}
input = strings.ReplaceAll(input, "\n", "")
if strings.TrimSpace(input) == "" {
return "", false
return validateInput(input)
}

func (c *Chat) readMultiLine() (string, bool) {
var builder strings.Builder
term := c.opts.Terminator
for {
input, err := c.reader.Readline()
if err != nil {
return c.handleReadError(err)
}
if strings.HasSuffix(input, term) {
builder.WriteString(strings.TrimSuffix(input, term))
break
}
if builder.Len() == 0 {
c.reader.SetPrompt(c.prompt.userNext)
}
builder.WriteString(input + "\n")
}
return input, true
c.reader.SetPrompt(c.prompt.user)
return validateInput(builder.String())
}

func (c *Chat) parseCommand(message string) command {
Expand All @@ -71,3 +97,16 @@ func (c *Chat) parseCommand(message string) command {
}
return newGeminiCommand(c.model, c.prompt, c.opts)
}

func (c *Chat) handleReadError(err error) (string, bool) {
if errors.Is(err, readline.ErrInterrupt) {
return systemCmdQuit, true
}
fmt.Printf("%s%s\n", c.prompt.cli, err)
return "", false
}

func validateInput(input string) (string, bool) {
input = strings.TrimSpace(input)
return input, input != ""
}
5 changes: 4 additions & 1 deletion cli/command.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,10 @@ import (
"google.golang.org/api/iterator"
)

const systemCmdPrefix = "!"
const (
systemCmdPrefix = "!"
systemCmdQuit = "!q"
)

type command interface {
run(message string) bool
Expand Down
14 changes: 8 additions & 6 deletions cli/prompt.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,17 +13,19 @@ const (
)

type prompt struct {
user string
gemini string
cli string
user string
userNext string
gemini string
cli string
}

func newPrompt(currentUser string) *prompt {
maxLength := maxLength(currentUser, geminiUser, cliUser)
return &prompt{
user: color.Blue(buildPrompt(currentUser, maxLength)),
gemini: color.Green(buildPrompt(geminiUser, maxLength)),
cli: color.Yellow(buildPrompt(cliUser, maxLength)),
user: color.Blue(buildPrompt(currentUser, maxLength)),
userNext: color.Blue(buildPrompt(strings.Repeat(" ", len(currentUser)), maxLength)),
gemini: color.Green(buildPrompt(geminiUser, maxLength)),
cli: color.Yellow(buildPrompt(cliUser, maxLength)),
}
}

Expand Down
5 changes: 3 additions & 2 deletions cmd/gemini/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,17 +15,18 @@ const (
apiKeyEnv = "GEMINI_API_KEY"
)

var opts = cli.ChatOpts{}

func run() int {
rootCmd := &cobra.Command{
Short: "Gemini CLI Tool",
Version: version,
}

var opts cli.ChatOpts
rootCmd.Flags().BoolVarP(&opts.Format, "format", "f", true, "render markdown-formatted response")
rootCmd.Flags().StringVarP(&opts.Style, "style", "s", "auto",
"markdown format style (ascii, dark, light, pink, notty, dracula)")
rootCmd.Flags().BoolVarP(&opts.Multiline, "multiline", "m", false, "read input as a multi-line string")
rootCmd.Flags().StringVarP(&opts.Terminator, "term", "t", "$", "multi-line input terminator")

rootCmd.RunE = func(_ *cobra.Command, _ []string) error {
apiKey := os.Getenv(apiKeyEnv)
Expand Down