Skip to content

Commit 8c8fb46

Browse files
committed
feat: replace history command with convo command and add subcommands
- Added new dependencies: `github.com/atotto/clipboard`, `github.com/caarlos0/duration`, `github.com/charmbracelet/huh`, and `github.com/charmbracelet/x/exp/strings`. - Removed the `history` command and replaced it with a new `convo` command for managing chat conversations. - Added new subcommands for the `convo` command: `ls`, `rm`, and `show` to list, remove, and display conversations. - Updated the `go.mod` file to include new dependencies and remove unused ones. - Refactored the `chat_history_store.go` and `conversation.go` files to rename references from &#34;history&#34; to &#34;convo&#34;. - Added a new `DurationFlag` utility for handling duration-based command-line flags. - Removed unused UI chat-related files (`prompt.go`, `renderer.go`, and their tests). - Updated the `console` package to move confirmation message logic into a separate file and improve style handling. - Updated the `config.go` file to include new help messages for conversation management commands. Signed-off-by: codiing-hui <wecoding@yeah.net>
1 parent 6aa9d4d commit 8c8fb46

File tree

25 files changed

+495
-669
lines changed

25 files changed

+495
-669
lines changed

go.mod

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,14 +7,18 @@ require (
77
github.com/MakeNowJust/heredoc/v2 v2.0.1
88
github.com/PuerkitoBio/goquery v1.10.1
99
github.com/adrg/xdg v0.5.3
10+
github.com/atotto/clipboard v0.1.4
11+
github.com/caarlos0/duration v0.0.0-20241219124531-2bb7dc683aa4
1012
github.com/caarlos0/env/v9 v9.0.0
1113
github.com/caarlos0/go-shellwords v1.0.12
1214
github.com/caarlos0/timea.go v1.2.0
1315
github.com/charmbracelet/bubbles v0.20.0
1416
github.com/charmbracelet/bubbletea v1.2.5-0.20241205214244-9306010a31ee
1517
github.com/charmbracelet/glamour v0.8.0
18+
github.com/charmbracelet/huh v0.6.0
1619
github.com/charmbracelet/lipgloss v1.0.0
1720
github.com/charmbracelet/x/exp/ordered v0.1.0
21+
github.com/charmbracelet/x/exp/strings v0.0.0-20240722160745-212f7b056ed0
1822
github.com/coding-hui/common v0.8.7
1923
github.com/coding-hui/go-prompt v0.2.8
2024
github.com/coding-hui/wecoding-sdk-go v0.8.7
@@ -45,9 +49,9 @@ require (
4549
github.com/Masterminds/sprig/v3 v3.2.3 // indirect
4650
github.com/alecthomas/chroma/v2 v2.14.0 // indirect
4751
github.com/andybalholm/cascadia v1.3.3 // indirect
48-
github.com/atotto/clipboard v0.1.4 // indirect
4952
github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect
5053
github.com/aymerick/douceur v0.2.0 // indirect
54+
github.com/catppuccin/go v0.2.0 // indirect
5155
github.com/charmbracelet/x/ansi v0.4.5 // indirect
5256
github.com/charmbracelet/x/term v0.2.1 // indirect
5357
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
@@ -73,6 +77,7 @@ require (
7377
github.com/mattn/go-tty v0.0.7 // indirect
7478
github.com/microcosm-cc/bluemonday v1.0.27 // indirect
7579
github.com/mitchellh/copystructure v1.0.0 // indirect
80+
github.com/mitchellh/hashstructure/v2 v2.0.2 // indirect
7681
github.com/mitchellh/reflectwalk v1.0.0 // indirect
7782
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
7883
github.com/modern-go/reflect2 v1.0.2 // indirect
@@ -90,7 +95,6 @@ require (
9095
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
9196
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
9297
github.com/rivo/uniseg v0.4.7 // indirect
93-
github.com/sahilm/fuzzy v0.1.1 // indirect
9498
github.com/sashabaranov/go-openai v1.26.1 // indirect
9599
github.com/shopspring/decimal v1.2.0 // indirect
96100
github.com/sirupsen/logrus v1.9.3 // indirect

go.sum

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -39,19 +39,25 @@ github.com/bitly/go-simplejson v0.5.0/go.mod h1:cXHtHw4XUPsvGaxgjIAn8PhEWG9NfngE
3939
github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869/go.mod h1:Ekp36dRnpXw/yCqJaO+ZrUyxD+3VXMFFr56k5XYrpB4=
4040
github.com/bugsnag/bugsnag-go v1.4.0/go.mod h1:2oa8nejYd4cQ/b0hMIopN0lCRxU0bueqREvZLWFrtK8=
4141
github.com/bugsnag/panicwrap v1.2.0/go.mod h1:D/8v3kj0zr8ZAKg1AQ6crr+5VwKN5eIywRkfhyM/+dE=
42+
github.com/caarlos0/duration v0.0.0-20241219124531-2bb7dc683aa4 h1:7lx4z3y4Fdr9nDtIevBFdStIrvYA8A1bVwJMZo1qLWs=
43+
github.com/caarlos0/duration v0.0.0-20241219124531-2bb7dc683aa4/go.mod h1:mSkwb/eZEwOJJJ4tqAKiuhLIPe0e9+FKhlU0oMCpbf8=
4244
github.com/caarlos0/env/v9 v9.0.0 h1:SI6JNsOA+y5gj9njpgybykATIylrRMklbs5ch6wO6pc=
4345
github.com/caarlos0/env/v9 v9.0.0/go.mod h1:ye5mlCVMYh6tZ+vCgrs/B95sj88cg5Tlnc0XIzgZ020=
4446
github.com/caarlos0/go-shellwords v1.0.12 h1:HWrUnu6lGbWfrDcFiHcZiwOLzHWjjrPVehULaTFgPp8=
4547
github.com/caarlos0/go-shellwords v1.0.12/go.mod h1:bYeeX1GrTLPl5cAMYEzdm272qdsQAZiaHgeF0KTk1Gw=
4648
github.com/caarlos0/timea.go v1.2.0 h1:JkjyWSUheN4nGO/OmYVGKbEv4ozHP/zuTZWD5Ih3Gog=
4749
github.com/caarlos0/timea.go v1.2.0/go.mod h1:p4uopjR7K+y0Oxh7j0vLh3vSo58jjzOgXHKcyKwQjuY=
50+
github.com/catppuccin/go v0.2.0 h1:ktBeIrIP42b/8FGiScP9sgrWOss3lw0Z5SktRoithGA=
51+
github.com/catppuccin/go v0.2.0/go.mod h1:8IHJuMGaUUjQM82qBrGNBv7LFq6JI3NnQCF6MOlZjpc=
4852
github.com/certifi/gocertifi v0.0.0-20190105021004-abcd57078448/go.mod h1:GJKEexRPVJrBSOjoqN5VNOIKJ5Q3RViH6eu3puDRwx4=
4953
github.com/charmbracelet/bubbles v0.20.0 h1:jSZu6qD8cRQ6k9OMfR1WlM+ruM8fkPWkHvQWD9LIutE=
5054
github.com/charmbracelet/bubbles v0.20.0/go.mod h1:39slydyswPy+uVOHZ5x/GjwVAFkCsV8IIVy+4MhzwwU=
5155
github.com/charmbracelet/bubbletea v1.2.5-0.20241205214244-9306010a31ee h1:xNijbIIsd6zADvvqrQj3kfKmLqJshZpCspKAfspXkFU=
5256
github.com/charmbracelet/bubbletea v1.2.5-0.20241205214244-9306010a31ee/go.mod h1:Hbk5+oE4a7cDyjfdPi4sHZ42aGTMYcmHnVDhsRswn7A=
5357
github.com/charmbracelet/glamour v0.8.0 h1:tPrjL3aRcQbn++7t18wOpgLyl8wrOHUEDS7IZ68QtZs=
5458
github.com/charmbracelet/glamour v0.8.0/go.mod h1:ViRgmKkf3u5S7uakt2czJ272WSg2ZenlYEZXT2x7Bjw=
59+
github.com/charmbracelet/huh v0.6.0 h1:mZM8VvZGuE0hoDXq6XLxRtgfWyTI3b2jZNKh0xWmax8=
60+
github.com/charmbracelet/huh v0.6.0/go.mod h1:GGNKeWCeNzKpEOh/OJD8WBwTQjV3prFAtQPpLv+AVwU=
5561
github.com/charmbracelet/lipgloss v1.0.0 h1:O7VkGDvqEdGi93X+DeqsQ7PKHDgtQfF8j8/O2qFMQNg=
5662
github.com/charmbracelet/lipgloss v1.0.0/go.mod h1:U5fy9Z+C38obMs+T+tJqst9VGzlOYGj4ri9reL3qUlo=
5763
github.com/charmbracelet/x/ansi v0.4.5 h1:LqK4vwBNaXw2AyGIICa5/29Sbdq58GbGdFngSexTdRM=
@@ -60,6 +66,8 @@ github.com/charmbracelet/x/exp/golden v0.0.0-20240815200342-61de596daa2b h1:MnAM
6066
github.com/charmbracelet/x/exp/golden v0.0.0-20240815200342-61de596daa2b/go.mod h1:wDlXFlCrmJ8J+swcL/MnGUuYnqgQdW9rhSD61oNMb6U=
6167
github.com/charmbracelet/x/exp/ordered v0.1.0 h1:55/qLwjIh0gL0Vni+QAWk7T/qRVP6sBf+2agPBgnOFE=
6268
github.com/charmbracelet/x/exp/ordered v0.1.0/go.mod h1:5UHwmG+is5THxMyCJHNPCn2/ecI07aKNrW+LcResjJ8=
69+
github.com/charmbracelet/x/exp/strings v0.0.0-20240722160745-212f7b056ed0 h1:qko3AQ4gK1MTS/de7F5hPGx6/k1u0w4TeYmBFwzYVP4=
70+
github.com/charmbracelet/x/exp/strings v0.0.0-20240722160745-212f7b056ed0/go.mod h1:pBhA0ybfXv6hDjQUZ7hk1lVxBiUbupdw5R31yPUViVQ=
6371
github.com/charmbracelet/x/term v0.2.1 h1:AQeHeLZ1OqSXhrAWpYUtZyX1T3zVxfpZuEQMIQaGIAQ=
6472
github.com/charmbracelet/x/term v0.2.1/go.mod h1:oQ4enTYFV7QN4m0i9mzHrViD7TQKvNEEkHUMCmsxdUg=
6573
github.com/coding-hui/common v0.8.7 h1:f9iHZcdQLgRFW/nIJHVfBINuzzCKsUcwVIhfboxXe2s=
@@ -140,8 +148,6 @@ github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
140148
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
141149
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
142150
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
143-
github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
144-
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
145151
github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw=
146152
github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
147153
github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY=
@@ -169,6 +175,8 @@ github.com/mitchellh/copystructure v1.0.0 h1:Laisrj+bAB6b/yJwB5Bt3ITZhGJdqmxquMK
169175
github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw=
170176
github.com/mitchellh/go-wordwrap v1.0.1 h1:TLuKupo69TCn6TQSyGxwI1EblZZEsQ0vMlAFQflz0v0=
171177
github.com/mitchellh/go-wordwrap v1.0.1/go.mod h1:R62XHJLzvMFRBbcrT7m7WgmE1eOyTSsCt+hzestvNj0=
178+
github.com/mitchellh/hashstructure/v2 v2.0.2 h1:vGKWl0YJqUNxE8d+h8f6NJLcCJrgbhC4NcD46KavDd4=
179+
github.com/mitchellh/hashstructure/v2 v2.0.2/go.mod h1:MG3aRVU/N29oo/V/IhBX8GR/zz4kQkprJgF2EVszyDE=
172180
github.com/mitchellh/reflectwalk v1.0.0 h1:9D+8oIskB4VJBN5SFlmc27fSlIBZaov1Wpk/IfikLNY=
173181
github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
174182
github.com/moby/term v0.5.2 h1:6qk3FJAFDs6i/q3W/pQ97SX192qKfZgGjCQqfCJkgzQ=
@@ -225,8 +233,6 @@ github.com/rollbar/rollbar-go v1.0.2/go.mod h1:AcFs5f0I+c71bpHlXNNDbOWJiKwjFDtIS
225233
github.com/russross/blackfriday v1.6.0 h1:KqfZb0pUVN2lYqZUYRddxF4OR8ZMURnJIG5Y3VRLtww=
226234
github.com/russross/blackfriday v1.6.0/go.mod h1:ti0ldHuxg49ri4ksnFxlkCfN+hvslNlmVHqNRXXJNAY=
227235
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
228-
github.com/sahilm/fuzzy v0.1.1 h1:ceu5RHF8DGgoi+/dR5PsECjCDH1BE3Fnmpo7aVXOdRA=
229-
github.com/sahilm/fuzzy v0.1.1/go.mod h1:VFvziUEIMCrT6A6tw2RFIXPXXmzXbOsSHF0DOI8ZK9Y=
230236
github.com/sashabaranov/go-openai v1.26.1 h1:B5plrmc/r7hKgYX69oT2VSt5w0O6u9BJYTjB8lNCesI=
231237
github.com/sashabaranov/go-openai v1.26.1/go.mod h1:lj5b/K+zjTSFxVLijLSTDZuP7adOgerWeFyZLUhAKRg=
232238
github.com/shopspring/decimal v1.2.0 h1:abSATXmQEYyShuxI4/vyW3tV1MrKAJzCZ/0zLUXYbsQ=

internal/cli/cli.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ import (
2121
"github.com/coding-hui/ai-terminal/internal/cli/commit"
2222
"github.com/coding-hui/ai-terminal/internal/cli/completion"
2323
"github.com/coding-hui/ai-terminal/internal/cli/configure"
24-
"github.com/coding-hui/ai-terminal/internal/cli/history"
24+
"github.com/coding-hui/ai-terminal/internal/cli/convo"
2525
"github.com/coding-hui/ai-terminal/internal/cli/hook"
2626
"github.com/coding-hui/ai-terminal/internal/cli/loadctx"
2727
"github.com/coding-hui/ai-terminal/internal/cli/manpage"
@@ -101,7 +101,7 @@ func NewAICommand(in io.Reader, out, errOut io.Writer) *cobra.Command {
101101
Commands: []*cobra.Command{
102102
coder.NewCmdCoder(&cfg),
103103
ask.NewCmdASK(ioStreams, &cfg),
104-
history.NewCmdHistory(ioStreams),
104+
convo.NewCmdConversation(ioStreams, &cfg),
105105
commit.NewCmdCommit(ioStreams, &cfg),
106106
review.NewCmdCommit(ioStreams),
107107
loadctx.NewCmdContext(ioStreams, &cfg),

internal/cli/convo/convo.go

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
package convo
2+
3+
import (
4+
"github.com/spf13/cobra"
5+
6+
"github.com/coding-hui/ai-terminal/internal/options"
7+
"github.com/coding-hui/ai-terminal/internal/util/genericclioptions"
8+
)
9+
10+
// NewCmdConversation returns a cobra command for manager convo.
11+
func NewCmdConversation(ioStreams genericclioptions.IOStreams, cfg *options.Config) *cobra.Command {
12+
cmd := &cobra.Command{
13+
Use: "convo",
14+
Short: "Managing chat conversations.",
15+
Run: func(cmd *cobra.Command, args []string) {
16+
_ = cmd.Help()
17+
},
18+
}
19+
20+
cmd.AddCommand(newCmdLsConversation(ioStreams, cfg))
21+
cmd.AddCommand(newCmdRemoveConversation(ioStreams, cfg))
22+
cmd.AddCommand(newCmdShowConversation(ioStreams, cfg))
23+
24+
return cmd
25+
}

internal/cli/convo/ls.go

Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
1+
package convo
2+
3+
import (
4+
"context"
5+
"errors"
6+
"fmt"
7+
"os"
8+
9+
"github.com/atotto/clipboard"
10+
timeago "github.com/caarlos0/timea.go"
11+
"github.com/charmbracelet/huh"
12+
"github.com/muesli/termenv"
13+
"github.com/spf13/cobra"
14+
15+
"github.com/coding-hui/ai-terminal/internal/convo"
16+
"github.com/coding-hui/ai-terminal/internal/options"
17+
"github.com/coding-hui/ai-terminal/internal/ui/console"
18+
"github.com/coding-hui/ai-terminal/internal/util/genericclioptions"
19+
"github.com/coding-hui/ai-terminal/internal/util/term"
20+
)
21+
22+
type ls struct{}
23+
24+
func newCmdLsConversation(ioStreams genericclioptions.IOStreams, cfg *options.Config) *cobra.Command {
25+
o := &ls{}
26+
cmd := &cobra.Command{
27+
Use: "ls",
28+
Short: "Show chat conversations.",
29+
Example: `# Managing conversations:
30+
ai convo ls`,
31+
RunE: func(cmd *cobra.Command, _ []string) error {
32+
return o.Run(ioStreams, cfg)
33+
},
34+
}
35+
36+
return cmd
37+
}
38+
39+
// Run executes convo command.
40+
func (o *ls) Run(ioStreams genericclioptions.IOStreams, cfg *options.Config) error {
41+
store, err := convo.GetConversationStore(cfg)
42+
if err != nil {
43+
return err
44+
}
45+
46+
conversations, err := store.ListConversations(context.Background())
47+
if err != nil {
48+
return err
49+
}
50+
51+
if len(conversations) == 0 {
52+
fmt.Fprintln(ioStreams.ErrOut, "No conversations found.")
53+
return nil
54+
}
55+
56+
if term.IsInputTTY() && term.IsOutputTTY() {
57+
selectFromList(conversations)
58+
return nil
59+
}
60+
61+
printList(conversations)
62+
63+
return nil
64+
}
65+
66+
func makeOptions(conversations []convo.Conversation) []huh.Option[string] {
67+
opts := make([]huh.Option[string], 0, len(conversations))
68+
for _, c := range conversations {
69+
timea := console.StdoutStyles().Timeago.Render(timeago.Of(c.UpdatedAt))
70+
left := console.StdoutStyles().SHA1.Render(c.ID[:convo.Sha1short])
71+
right := console.StdoutStyles().ConversationList.Render(c.Title, timea)
72+
if c.Model != nil {
73+
right += console.StdoutStyles().Comment.Render(*c.Model)
74+
}
75+
opts = append(opts, huh.NewOption(left+" "+right, c.ID))
76+
}
77+
return opts
78+
}
79+
80+
func selectFromList(conversations []convo.Conversation) {
81+
var selected string
82+
if err := huh.NewForm(
83+
huh.NewGroup(
84+
huh.NewSelect[string]().
85+
Title("Conversations").
86+
Value(&selected).
87+
Options(makeOptions(conversations)...),
88+
),
89+
).Run(); err != nil {
90+
if !errors.Is(err, huh.ErrUserAborted) {
91+
fmt.Fprintln(os.Stderr, err.Error())
92+
}
93+
return
94+
}
95+
96+
_ = clipboard.WriteAll(selected)
97+
termenv.Copy(selected)
98+
console.PrintConfirmation("COPIED", selected)
99+
// suggest actions to use this conversation ID
100+
fmt.Println(console.StdoutStyles().Comment.Render(
101+
"You can use this conversation ID with the following commands:",
102+
))
103+
104+
type suggestion struct {
105+
cmd string
106+
usage string
107+
}
108+
109+
suggestions := []suggestion{
110+
{
111+
cmd: "show-convo",
112+
usage: "ai convo show",
113+
},
114+
{
115+
cmd: "continue",
116+
usage: "ai ask --continue",
117+
},
118+
{
119+
cmd: "rm-convo",
120+
usage: "ai convo rm",
121+
},
122+
}
123+
for _, flag := range suggestions {
124+
fmt.Printf(
125+
" %-44s %s\n",
126+
console.StdoutStyles().Flag.Render(flag.usage),
127+
console.StdoutStyles().FlagDesc.Render(options.Help[flag.cmd]),
128+
)
129+
}
130+
}
131+
132+
func printList(conversations []convo.Conversation) {
133+
for _, conversation := range conversations {
134+
_, _ = fmt.Fprintf(
135+
os.Stdout,
136+
"%s\t%s\t%s\n",
137+
console.StdoutStyles().SHA1.Render(conversation.ID[:convo.Sha1short]),
138+
conversation.Title,
139+
console.StdoutStyles().Timeago.Render(timeago.Of(conversation.UpdatedAt)),
140+
)
141+
}
142+
}

0 commit comments

Comments
 (0)