mirror of
https://github.com/Pull-Pal/pull-pal.git
synced 2026-04-18 11:39:09 -04:00
It's working
Implement cobra and stuff Right now there is just one root command You can set a config file, or pass flags in via CLI Haven't figured out how to use env variables yet But if you run the root command, It'll check the repo for issues, and generate an LLM prompt You can copy-paste it to the LLM chat, then copy-paste the response to a different file Then press enter in the CLI tool, and it will parse the response and open a PR with the change
This commit is contained in:
@@ -1,7 +0,0 @@
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
func main() {
|
||||
fmt.Println("hello")
|
||||
}
|
||||
150
cmd/root.go
Normal file
150
cmd/root.go
Normal file
@@ -0,0 +1,150 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/mobyvb/pull-pal/pullpal"
|
||||
"github.com/mobyvb/pull-pal/vc"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/viper"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
// rootCmd represents the base command when called without any subcommands
|
||||
var rootCmd = &cobra.Command{
|
||||
Use: "pull-pal",
|
||||
Short: "A bot that uses large language models to act as a collaborator on a git project",
|
||||
Long: `A bot that uses large language models to act as a collaborator on a git project.
|
||||
|
||||
It can be used to:
|
||||
* Monitor a repository for open issues, and generate LLM prompts according to the issue details
|
||||
* Read an LLM response and process it into a new git commit and code change request on the version control server
|
||||
`,
|
||||
// Uncomment the following line if your bare application
|
||||
// has an action associated with it:
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
selfHandle := viper.GetString("handle")
|
||||
selfEmail := viper.GetString("email")
|
||||
repoDomain := viper.GetString("repo-domain")
|
||||
repoHandle := viper.GetString("repo-handle")
|
||||
repoName := viper.GetString("repo-name")
|
||||
githubToken := viper.GetString("github-token")
|
||||
localRepoPath := viper.GetString("local-repo-path")
|
||||
promptPath := viper.GetString("prompt-path")
|
||||
responsePath := viper.GetString("response-path")
|
||||
|
||||
log, err := zap.NewProduction()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
author := vc.Author{
|
||||
Email: selfEmail,
|
||||
Handle: selfHandle,
|
||||
Token: githubToken,
|
||||
}
|
||||
repo := vc.Repository{
|
||||
LocalPath: localRepoPath,
|
||||
HostDomain: repoDomain,
|
||||
Name: repoName,
|
||||
Owner: vc.Author{
|
||||
Handle: repoHandle,
|
||||
},
|
||||
}
|
||||
p, err := pullpal.NewPullPal(cmd.Context(), log.Named("pullpal"), author, repo)
|
||||
if err != nil {
|
||||
log.Error("error creating new pull pal", zap.Error(err))
|
||||
return
|
||||
}
|
||||
log.Info("Successfully initialized pull pal")
|
||||
|
||||
issue, changeRequest, err := p.PickIssueToFile(promptPath)
|
||||
if err != nil {
|
||||
log.Error("error selecting issue and/or generating prompt", zap.Error(err))
|
||||
return
|
||||
}
|
||||
|
||||
log.Info("Picked issue and created prompt", zap.String("issue ID", issue.ID), zap.String("prompt location", promptPath))
|
||||
|
||||
log.Info("Insert LLM response into response file", zap.String("response location", responsePath))
|
||||
|
||||
var input string
|
||||
log.Info("Press 'enter' when done.")
|
||||
fmt.Scanln(&input)
|
||||
|
||||
prURL, err := p.ProcessResponseFromFile(changeRequest, responsePath)
|
||||
if err != nil {
|
||||
log.Error("error parsing LLM response and/or making version control changes", zap.Error(err))
|
||||
return
|
||||
}
|
||||
|
||||
log.Info("Successfully opened a code change request", zap.String("Github PR link", prURL))
|
||||
},
|
||||
}
|
||||
|
||||
// Execute adds all child commands to the root command and sets flags appropriately.
|
||||
// This is called by main.main(). It only needs to happen once to the rootCmd.
|
||||
func Execute() {
|
||||
err := rootCmd.Execute()
|
||||
if err != nil {
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
var cfgFile string
|
||||
|
||||
func init() {
|
||||
cobra.OnInitialize(initConfig)
|
||||
|
||||
// TODO make config values requried
|
||||
rootCmd.PersistentFlags().StringVar(&cfgFile, "config", "", "config file (default is $HOME/.pull-pal.yaml)")
|
||||
|
||||
rootCmd.PersistentFlags().StringP("handle", "u", "HANDLE", "handle to use for version control actions")
|
||||
rootCmd.PersistentFlags().StringP("email", "e", "EMAIL", "email to use for version control actions")
|
||||
rootCmd.PersistentFlags().StringP("repo-domain", "d", "github.com", "domain for version control server")
|
||||
rootCmd.PersistentFlags().StringP("repo-handle", "o", "REPO-HANDLE", "handle of repository's owner on version control server")
|
||||
rootCmd.PersistentFlags().StringP("repo-name", "n", "REPO-NAME", "name of repository on version control server")
|
||||
rootCmd.PersistentFlags().StringP("github-token", "t", "GITHUB TOKEN", "token for authenticating Github actions")
|
||||
rootCmd.PersistentFlags().StringP("local-repo-path", "l", "/tmp/pullpalrepo/", "path where pull pal will check out a local copy of the repository")
|
||||
rootCmd.PersistentFlags().StringP("prompt-path", "p", "./path/to/prompt.txt", "path where pull pal will write the llm prompt")
|
||||
rootCmd.PersistentFlags().StringP("response-path", "r", "./path/to/response.txt", "path where pull pal will read the llm response from")
|
||||
|
||||
viper.BindPFlag("handle", rootCmd.PersistentFlags().Lookup("handle"))
|
||||
viper.BindPFlag("email", rootCmd.PersistentFlags().Lookup("email"))
|
||||
viper.BindPFlag("repo-domain", rootCmd.PersistentFlags().Lookup("repo-domain"))
|
||||
viper.BindPFlag("repo-handle", rootCmd.PersistentFlags().Lookup("repo-handle"))
|
||||
viper.BindPFlag("repo-name", rootCmd.PersistentFlags().Lookup("repo-name"))
|
||||
viper.BindPFlag("github-token", rootCmd.PersistentFlags().Lookup("github-token"))
|
||||
viper.BindPFlag("local-repo-path", rootCmd.PersistentFlags().Lookup("local-repo-path"))
|
||||
viper.BindPFlag("prompt-path", rootCmd.PersistentFlags().Lookup("prompt-path"))
|
||||
viper.BindPFlag("response-path", rootCmd.PersistentFlags().Lookup("response-path"))
|
||||
}
|
||||
|
||||
func initConfig() {
|
||||
fmt.Println("init")
|
||||
if cfgFile != "" {
|
||||
fmt.Println("cfg file exists")
|
||||
// Use config file from the flag.
|
||||
viper.SetConfigFile(cfgFile)
|
||||
} else {
|
||||
fmt.Println("cfg file empty")
|
||||
// Find home directory.
|
||||
home, err := os.UserHomeDir()
|
||||
cobra.CheckErr(err)
|
||||
|
||||
// Search config in home directory with name ".cobra" (without extension).
|
||||
viper.AddConfigPath(home)
|
||||
viper.SetConfigType("yaml")
|
||||
viper.SetConfigName(".pull-pal")
|
||||
}
|
||||
|
||||
// TODO figure out how to get env variables to work
|
||||
viper.SetEnvPrefix("pullpal")
|
||||
viper.AutomaticEnv()
|
||||
|
||||
if err := viper.ReadInConfig(); err == nil {
|
||||
fmt.Println("Using config file:", viper.ConfigFileUsed())
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user