diff --git a/pkg/cli/cmd.go b/pkg/cli/cmd.go index c8f3dfa..3c47480 100644 --- a/pkg/cli/cmd.go +++ b/pkg/cli/cmd.go @@ -36,6 +36,7 @@ func init() { renameCmd.Flags().Bool("generate", false, "Generate a conversation title") rootCmd.AddCommand( + cloneCmd, continueCmd, editCmd, lsCmd, @@ -101,6 +102,17 @@ func lookupConversation(shortName string) *Conversation { return c } +func lookupConversationE(shortName string) (*Conversation, error) { + c, err := store.ConversationByShortName(shortName) + if err != nil { + return nil, fmt.Errorf("Could not lookup conversation: %v", err) + } + if c.ID == 0 { + return nil, fmt.Errorf("Conversation not found with short name: %s", shortName) + } + return c, nil +} + // handleConversationReply handles sending messages to an existing // conversation, optionally persisting them. It displays the entire // conversation before @@ -161,8 +173,10 @@ func inputFromArgsOrEditor(args []string, placeholder string, existingMessage st } var rootCmd = &cobra.Command{ - Use: "lmcli", - Long: `lmcli - command-line interface with Large Language Models.`, + Use: "lmcli [flags]", + Long: `lmcli - Large Language Model CLI`, + SilenceErrors: true, + SilenceUsage: true, Run: func(cmd *cobra.Command, args []string) { // execute `lm ls` by default }, @@ -314,6 +328,66 @@ var rmCmd = &cobra.Command{ }, } +var cloneCmd = &cobra.Command{ + Use: "clone ", + Short: "Clone conversations.", + Long: `Clones the provided conversation.`, + Args: func(cmd *cobra.Command, args []string) error { + argCount := 1 + if err := cobra.MinimumNArgs(argCount)(cmd, args); err != nil { + return err + } + return nil + }, + RunE: func(cmd *cobra.Command, args []string) error { + shortName := args[0] + toClone, err := lookupConversationE(shortName) + if err != nil { + return err + } + + messagesToCopy, err := store.Messages(toClone) + if err != nil { + return fmt.Errorf("Could not retrieve messages for conversation: %s", toClone.ShortName.String) + } + + clone := &Conversation{ + Title: toClone.Title + " - Clone", + } + if err := store.SaveConversation(clone); err != nil { + return fmt.Errorf("Cloud not create clone: %s", err) + } + + var errors []error + messageCnt := 0 + for _, message := range messagesToCopy { + newMessage := message + newMessage.ConversationID = clone.ID + newMessage.ID = 0 + if err := store.SaveMessage(&newMessage); err != nil { + errors = append(errors, err) + } else { + messageCnt++ + } + } + + if len(errors) > 0 { + return fmt.Errorf("Messages failed to be cloned: %v", errors) + } + + fmt.Printf("Cloned %d messages to: %s\n", messageCnt, clone.Title) + + return nil + }, + ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { + compMode := cobra.ShellCompDirectiveNoFileComp + if len(args) != 0 { + return nil, compMode + } + return store.ConversationShortNameCompletions(toComplete), compMode + }, +} + var viewCmd = &cobra.Command{ Use: "view ", Short: "View messages in a conversation",