Skip to content

Commit ef92bae

Browse files
committed
Initial commit
0 parents  commit ef92bae

File tree

11 files changed

+399
-0
lines changed

11 files changed

+399
-0
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
ottodocs

.vscode/settings.json

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
{
2+
"cSpell.words": [
3+
"apikey",
4+
"gopenai",
5+
"openai",
6+
"ottodocs"
7+
]
8+
}

LICENSE

Whitespace-only changes.

cmd/ask.go

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
/*
2+
Copyright © 2023 NAME HERE <EMAIL ADDRESS>
3+
*/
4+
package cmd
5+
6+
import (
7+
"fmt"
8+
"os"
9+
10+
gopenai "github.com/CasualCodersProjects/gopenai"
11+
"github.com/chand1012/ottodocs/config"
12+
"github.com/spf13/cobra"
13+
)
14+
15+
var question string
16+
17+
// askCmd represents the ask command
18+
var askCmd = &cobra.Command{
19+
Use: "ask",
20+
Short: "Ask ChatGPT a question from the command line.",
21+
Long: `Ask ChatGPT a question from the command line.
22+
23+
If '-q' is not specified, the user will be prompted to enter a question.
24+
`,
25+
Run: func(cmd *cobra.Command, args []string) {
26+
conf, err := config.Load()
27+
if err != nil || conf.APIKey == "" {
28+
// if the API key is not set, prompt the user to login
29+
fmt.Println("Please login first.")
30+
fmt.Println("Run `ottodocs login` to login.")
31+
os.Exit(1)
32+
}
33+
34+
openai := gopenai.NewOpenAI(&gopenai.OpenAIOpts{
35+
APIKey: conf.APIKey,
36+
})
37+
38+
// if the question is not provided, prompt the user for it
39+
if question == "" {
40+
fmt.Print("What would you like to ask ChatGPT?\n> ")
41+
fmt.Scanln(&question)
42+
}
43+
44+
// ask ChatGPT the question
45+
resp, err := openai.CreateChatSimple(question, 0) // 0 sets the max tokens to 1024
46+
if err != nil {
47+
fmt.Printf("Error: %s", err)
48+
os.Exit(1)
49+
}
50+
51+
message := resp.Choices[0].Message.Content
52+
53+
fmt.Println(message)
54+
},
55+
}
56+
57+
func init() {
58+
rootCmd.AddCommand(askCmd)
59+
60+
// Here you will define your flags and configuration settings.
61+
62+
// Cobra supports Persistent Flags which will work for this command
63+
// and all subcommands, e.g.:
64+
// askCmd.PersistentFlags().String("foo", "", "A help for foo")
65+
66+
// Cobra supports local flags which will only run when this command
67+
// is called directly, e.g.:
68+
// askCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle")
69+
70+
askCmd.Flags().StringVarP(&question, "question", "q", "", "Question to ask ChatGPT")
71+
}

cmd/login.go

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
/*
2+
Copyright © 2023 NAME HERE <EMAIL ADDRESS>
3+
*/
4+
package cmd
5+
6+
import (
7+
"fmt"
8+
"os"
9+
10+
"github.com/chand1012/ottodocs/config"
11+
"github.com/spf13/cobra"
12+
)
13+
14+
var apiKey string
15+
16+
// loginCmd represents the login command
17+
var loginCmd = &cobra.Command{
18+
Use: "login",
19+
Short: "Add an API key to your configuration",
20+
Long: `Add an API key to your configuration.
21+
This API key will be used to authenticate with the OpenAI ChatGPT API.`,
22+
Run: func(cmd *cobra.Command, args []string) {
23+
// if the api key is not provided, prompt the user for it
24+
if apiKey == "" {
25+
fmt.Print("Please provide an API key: ")
26+
fmt.Scanln(&apiKey)
27+
}
28+
// save the API key to a configuration file at ~/.ottodocs/config.json
29+
conf, err := config.Load()
30+
if err != nil {
31+
fmt.Printf("Error: %s", err)
32+
os.Exit(1)
33+
}
34+
conf.APIKey = apiKey
35+
err = conf.Save()
36+
if err != nil {
37+
fmt.Printf("Error: %s", err)
38+
os.Exit(1)
39+
}
40+
fmt.Println("API key saved successfully!")
41+
},
42+
}
43+
44+
func init() {
45+
rootCmd.AddCommand(loginCmd)
46+
47+
loginCmd.Flags().StringVarP(&apiKey, "apikey", "k", "", "API key to add to configuration")
48+
}

cmd/prompt.go

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
/*
2+
Copyright © 2023 NAME HERE <EMAIL ADDRESS>
3+
*/
4+
package cmd
5+
6+
import (
7+
"fmt"
8+
"os"
9+
10+
"github.com/chand1012/git2gpt/prompt"
11+
"github.com/spf13/cobra"
12+
)
13+
14+
var repoPath string
15+
var preambleFile string
16+
var outputFile string
17+
var estimateTokens bool
18+
var ignoreFilePath string
19+
var ignoreGitignore bool
20+
var outputJSON bool
21+
22+
// promptCmd represents the prompt command
23+
var promptCmd = &cobra.Command{
24+
Use: "prompt",
25+
Short: "Generates a ChatGPT prompt from a given Git repo",
26+
Long: `Generates a ChatGPT prompt from a given Git repo.`,
27+
Run: func(cmd *cobra.Command, args []string) {
28+
repoPath = args[0]
29+
ignoreList := prompt.GenerateIgnoreList(repoPath, ignoreFilePath, !ignoreGitignore)
30+
repo, err := prompt.ProcessGitRepo(repoPath, ignoreList)
31+
if err != nil {
32+
fmt.Printf("Error: %s\n", err)
33+
os.Exit(1)
34+
}
35+
if outputJSON {
36+
output, err := prompt.MarshalRepo(repo)
37+
if err != nil {
38+
fmt.Printf("Error: %s\n", err)
39+
os.Exit(1)
40+
}
41+
if outputFile != "" {
42+
// if output file exists, throw error
43+
if _, err := os.Stat(outputFile); err == nil {
44+
fmt.Printf("Error: output file %s already exists\n", outputFile)
45+
os.Exit(1)
46+
}
47+
err = os.WriteFile(outputFile, []byte(output), 0644)
48+
if err != nil {
49+
fmt.Printf("Error: could not write to output file %s\n", outputFile)
50+
os.Exit(1)
51+
}
52+
} else {
53+
fmt.Println(string(output))
54+
}
55+
return
56+
}
57+
output, err := prompt.OutputGitRepo(repo, preambleFile)
58+
if err != nil {
59+
fmt.Printf("Error: %s\n", err)
60+
os.Exit(1)
61+
}
62+
if outputFile != "" {
63+
// if output file exists, throw error
64+
if _, err := os.Stat(outputFile); err == nil {
65+
fmt.Printf("Error: output file %s already exists\n", outputFile)
66+
os.Exit(1)
67+
}
68+
err = os.WriteFile(outputFile, []byte(output), 0644)
69+
if err != nil {
70+
fmt.Printf("Error: could not write to output file %s\n", outputFile)
71+
os.Exit(1)
72+
}
73+
} else {
74+
fmt.Println(output)
75+
}
76+
if estimateTokens {
77+
fmt.Printf("Estimated number of tokens: %d\n", prompt.EstimateTokens(output))
78+
}
79+
},
80+
}
81+
82+
func init() {
83+
rootCmd.AddCommand(promptCmd)
84+
85+
promptCmd.Flags().StringVarP(&preambleFile, "preamble", "p", "", "path to preamble text file")
86+
// output to file flag. Should be a string
87+
promptCmd.Flags().StringVarP(&outputFile, "output", "o", "", "path to output file")
88+
// estimate tokens. Should be a bool
89+
promptCmd.Flags().BoolVarP(&estimateTokens, "estimate", "e", false, "estimate the number of tokens in the output")
90+
// ignore file path. Should be a string
91+
promptCmd.Flags().StringVarP(&ignoreFilePath, "ignore", "i", "", "path to .gptignore file")
92+
// ignore gitignore. Should be a bool
93+
promptCmd.Flags().BoolVarP(&ignoreGitignore, "ignore-gitignore", "g", false, "ignore .gitignore file")
94+
// output JSON. Should be a bool
95+
promptCmd.Flags().BoolVarP(&outputJSON, "json", "j", false, "output JSON")
96+
}

cmd/root.go

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
/*
2+
Copyright © 2023 NAME HERE <EMAIL ADDRESS>
3+
*/
4+
package cmd
5+
6+
import (
7+
"os"
8+
9+
"github.com/spf13/cobra"
10+
)
11+
12+
// rootCmd represents the base command when called without any subcommands
13+
var rootCmd = &cobra.Command{
14+
Use: "ottodocs",
15+
Short: "Document your code with ease",
16+
Long: `Code documentation made easy using GPT.`,
17+
// Uncomment the following line if your bare application
18+
// has an action associated with it:
19+
// Run: func(cmd *cobra.Command, args []string) { },
20+
}
21+
22+
// Execute adds all child commands to the root command and sets flags appropriately.
23+
// This is called by main.main(). It only needs to happen once to the rootCmd.
24+
func Execute() {
25+
err := rootCmd.Execute()
26+
if err != nil {
27+
os.Exit(1)
28+
}
29+
}
30+
31+
func init() {
32+
// Here you will define your flags and configuration settings.
33+
// Cobra supports persistent flags, which, if defined here,
34+
// will be global for your application.
35+
36+
// rootCmd.PersistentFlags().StringVar(&cfgFile, "config", "", "config file (default is $HOME/.ottodocs.yaml)")
37+
38+
// Cobra also supports local flags, which will only run
39+
// when this action is called directly.
40+
// rootCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle")
41+
}

config/config.go

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
package config
2+
3+
import (
4+
"encoding/json"
5+
"os"
6+
"os/user"
7+
"path/filepath"
8+
)
9+
10+
// Config represents the configuration file
11+
type Config struct {
12+
APIKey string `json:"api_key"`
13+
}
14+
15+
// also returns the path to the config file
16+
func createIfNotExists() (string, error) {
17+
currentUser, err := user.Current()
18+
if err != nil {
19+
return "", err
20+
}
21+
homeDir := currentUser.HomeDir
22+
configDir := filepath.Join(homeDir, ".ottodocs")
23+
configPath := filepath.Join(configDir, "config.json")
24+
25+
// check if the config directory exists
26+
if _, err := os.Stat(configDir); os.IsNotExist(err) {
27+
// create the config directory
28+
err = os.Mkdir(configDir, 0755)
29+
if err != nil {
30+
return "", err
31+
}
32+
}
33+
34+
// check if the config file exists
35+
if _, err := os.Stat(configPath); os.IsNotExist(err) {
36+
// create the config file
37+
file, err := os.Create(configPath)
38+
if err != nil {
39+
return "", err
40+
}
41+
// add an empty config to the file
42+
blankConfig := Config{}
43+
err = json.NewEncoder(file).Encode(blankConfig)
44+
if err != nil {
45+
return "", err
46+
}
47+
defer file.Close()
48+
}
49+
50+
return configPath, nil
51+
}
52+
53+
// Load loads the configuration file
54+
func Load() (*Config, error) {
55+
// load the config file at path ~/.ottodocs/config.json
56+
// if the file or path does not exist, create it
57+
configPath, err := createIfNotExists()
58+
if err != nil {
59+
return nil, err
60+
}
61+
62+
// open the config file
63+
file, err := os.Open(configPath)
64+
if err != nil {
65+
return nil, err
66+
}
67+
68+
// decode the config file
69+
var config Config
70+
err = json.NewDecoder(file).Decode(&config)
71+
if err != nil {
72+
return nil, err
73+
}
74+
75+
return &config, nil
76+
}
77+
78+
// Save saves the configuration file
79+
func (c *Config) Save() error {
80+
configPath, err := createIfNotExists()
81+
if err != nil {
82+
return err
83+
}
84+
85+
// open the config file
86+
file, err := os.OpenFile(configPath, os.O_WRONLY, 0644)
87+
if err != nil {
88+
return err
89+
}
90+
91+
return json.NewEncoder(file).Encode(c)
92+
}

go.mod

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
module github.com/chand1012/ottodocs
2+
3+
go 1.20
4+
5+
require (
6+
github.com/CasualCodersProjects/gopenai v0.3.0
7+
github.com/chand1012/git2gpt v0.3.0
8+
github.com/spf13/cobra v1.6.1
9+
)
10+
11+
require (
12+
github.com/gobwas/glob v0.2.3 // indirect
13+
github.com/inconshreveable/mousetrap v1.0.1 // indirect
14+
github.com/spf13/pflag v1.0.5 // indirect
15+
)

go.sum

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
github.com/CasualCodersProjects/gopenai v0.3.0 h1:vTFe2Rl/At+LnRL0q/joNHYZGVQd19HXFaQT6aNnpWg=
2+
github.com/CasualCodersProjects/gopenai v0.3.0/go.mod h1:NGGTkFg60IYsXynphxExU11U4YqWDKEg/XqP9sVvwEY=
3+
github.com/chand1012/git2gpt v0.3.0 h1:6b0/e70eIBClunkzotkQywP3CUqPRqu1Grm0tw99QZ4=
4+
github.com/chand1012/git2gpt v0.3.0/go.mod h1:tT4D74zqhWjDkQCyYqmEclIhOqI3t+hEmUHDTPjiIr4=
5+
github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
6+
github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y=
7+
github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8=
8+
github.com/inconshreveable/mousetrap v1.0.1 h1:U3uMjPSQEBMNp1lFxmllqCPM6P5u/Xq7Pgzkat/bFNc=
9+
github.com/inconshreveable/mousetrap v1.0.1/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
10+
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
11+
github.com/spf13/cobra v1.6.1 h1:o94oiPyS4KD1mPy2fmcYYHHfCxLqYjJOhGsCHFZtEzA=
12+
github.com/spf13/cobra v1.6.1/go.mod h1:IOw/AERYS7UzyrGinqmz6HLUo219MORXGxhbaJUqzrY=
13+
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
14+
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
15+
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
16+
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

0 commit comments

Comments
 (0)