Skip to content

Commit c6a077d

Browse files
committed
"Update llm package to use HTTP provider and add support for Ollama, OpenAI, and custom providers"
1 parent 68c8ef4 commit c6a077d

11 files changed

Lines changed: 285 additions & 238 deletions

File tree

cmd/generate.go

Lines changed: 39 additions & 111 deletions
Original file line numberDiff line numberDiff line change
@@ -2,124 +2,52 @@ package cmd
22

33
import (
44
"fmt"
5-
"os"
65
"os/exec"
6+
"strings"
7+
8+
"github.com/spf13/cobra"
9+
710
"smartcommit/config"
811
"smartcommit/diff"
912
"smartcommit/llm"
10-
"smartcommit/prompt"
11-
"time"
12-
13-
promptui "github.com/manifoldco/promptui"
14-
"github.com/spf13/cobra"
1513
)
1614

17-
var yesFlag bool
18-
19-
var GenerateCmd = &cobra.Command{
20-
Use: "generate",
21-
Short: "Generate a commit message using AI",
22-
Long: `Generate a commit message from staged Git changes using a local or remote LLM.
23-
24-
By default, it launches an interactive flow.
25-
Use --yes or -y to skip the prompt and commit directly.`,
26-
Run: func(cmd *cobra.Command, args []string) {
27-
cfg := config.LoadOrDefault()
28-
29-
diffText, err := diff.GetStagedDiff()
30-
if err != nil || len(diffText) == 0 {
31-
fmt.Println("❌ No staged changes found.")
32-
return
33-
}
34-
35-
promptText := prompt.Build(diffText, cfg.SystemPrompt)
36-
37-
provider, err := llm.GetProvider(cfg)
38-
if err != nil {
39-
fmt.Println("❌", err)
40-
return
41-
}
42-
43-
message, err := provider.Generate(promptText)
44-
if err != nil {
45-
fmt.Println("❌ Generation failed:", err)
46-
return
47-
}
48-
49-
if yesFlag {
50-
fmt.Println("💡 Generated Commit Message:")
51-
fmt.Println("----------------------------------")
52-
fmt.Println(message)
53-
fmt.Println("----------------------------------")
54-
runGitCommit(message)
55-
return
56-
}
57-
58-
for {
59-
fmt.Println("\n💡 Generated Commit Message:")
60-
fmt.Println("----------------------------------")
61-
fmt.Println(message)
62-
fmt.Println("----------------------------------")
63-
fmt.Print("Choose [c]ommit, [e]dit message, [r]egenerate, [q]uit: ")
64-
65-
var choice string
66-
fmt.Scanln(&choice)
67-
68-
switch choice {
69-
case "c":
70-
runGitCommit(message)
71-
return
72-
case "e":
73-
message = editMessage(message)
74-
case "r":
75-
fmt.Print("\n🔄 Regenerating")
76-
for i := 0; i < 3; i++ {
77-
fmt.Print(".")
78-
time.Sleep(200 * time.Millisecond)
79-
}
80-
message, err = provider.Generate(promptText)
81-
if err != nil {
82-
fmt.Println("❌ Regeneration failed:", err)
83-
continue
84-
}
85-
case "q":
86-
return
87-
default:
88-
fmt.Println("❓ Invalid choice.")
89-
}
90-
}
91-
},
15+
var generateCmd = &cobra.Command{
16+
Use: "generate",
17+
Short: "Generate & commit a message from your diff",
18+
Run: func(cmd *cobra.Command, args []string) {
19+
cfg := config.LoadOrDefault()
20+
21+
d, err := diff.GetStagedDiff()
22+
if err != nil || d == "" {
23+
fmt.Println("❌ No staged changes.")
24+
return
25+
}
26+
27+
provider, err := llm.GetProvider(cfg)
28+
if err != nil {
29+
fmt.Println("❌", err)
30+
return
31+
}
32+
33+
prompt := cfg.SystemPrompt + "\n\nDiff:\n" + d
34+
msg, err := provider.Generate(prompt)
35+
if err != nil {
36+
fmt.Println("❌ Generation failed:", err)
37+
return
38+
}
39+
msg = strings.TrimSpace(msg)
40+
41+
fmt.Println("\n💡 Commit message:\n\n", msg, "\n")
42+
git := exec.Command("git", "commit", "-m", msg)
43+
git.Stdout = cmd.OutOrStdout()
44+
git.Stderr = cmd.OutOrStderr()
45+
if err := git.Run(); err != nil {
46+
fmt.Println("❌ Git commit failed:", err)
47+
}
48+
},
9249
}
9350

9451
func init() {
95-
GenerateCmd.Flags().BoolVarP(&yesFlag, "yes", "y", false, "Autogenerate and commit without prompting")
96-
}
97-
98-
func runGitCommit(msg string) {
99-
fmt.Println("✅ Committing with:")
100-
fmt.Println(msg)
101-
_ = os.WriteFile(".git/COMMIT_EDITMSG", []byte(msg), 0644)
102-
_ = executeCommand("git", "commit", "-F", ".git/COMMIT_EDITMSG")
103-
}
104-
105-
func executeCommand(name string, args ...string) error {
106-
c := exec.Command(name, args...)
107-
c.Stdin = os.Stdin
108-
c.Stdout = os.Stdout
109-
c.Stderr = os.Stderr
110-
return c.Run()
111-
}
112-
113-
func editMessage(current string) string {
114-
prompt := promptui.Prompt{
115-
Label: "Edit Commit Message",
116-
Default: current,
117-
AllowEdit: true,
118-
}
119-
result, err := prompt.Run()
120-
if err != nil {
121-
fmt.Println("⚠️ Edit cancelled.")
122-
return current
123-
}
124-
return result
52+
rootCmd.AddCommand(generateCmd)
12553
}

cmd/root.go

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
package cmd
2+
3+
import "github.com/spf13/cobra"
4+
5+
var rootCmd = &cobra.Command{
6+
Use: "smartcommit",
7+
Short: "AI‑powered Git commit message generator",
8+
Long: `
9+
SmartCommit reads your staged Git diff and uses any LLM (local or HTTP API)
10+
to generate a meaningful commit message, then commits it for you.
11+
12+
Examples:
13+
# Configure your LLM provider and API key:
14+
smartcommit setup
15+
16+
# Generate & commit a message:
17+
smartcommit generate
18+
19+
If you ever need assistance, run:
20+
smartcommit --help
21+
or see:
22+
https://github.com/your‑repo/smartcommit#readme
23+
`,
24+
}
25+
26+
27+
func Execute() error {
28+
return rootCmd.Execute()
29+
}

cmd/setup.go

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
// File: cmd/setup.go
2+
package cmd
3+
4+
import (
5+
"fmt"
6+
"os"
7+
8+
"github.com/manifoldco/promptui"
9+
"github.com/spf13/cobra"
10+
11+
"smartcommit/config"
12+
)
13+
14+
var setupCmd = &cobra.Command{
15+
Use: "setup",
16+
Short: "Configure LLM provider and settings",
17+
Long: `Interactive setup for SmartCommit.
18+
19+
This will prompt you to configure:
20+
• Provider (ollama or http)
21+
• Model name (e.g. llama3, gpt-3.5-turbo, gemini)
22+
• API Key (for HTTP-based providers)
23+
• Base URL (your LLM endpoint)
24+
25+
Need help? Run 'smartcommit --help' or see:
26+
https://github.com/your-repo/smartcommit#readme`,
27+
Run: func(cmd *cobra.Command, args []string) {
28+
fmt.Println("🔧 SmartCommit Setup — press Ctrl+C to abort, or run 'smartcommit --help' for guidance.")
29+
30+
cfg := config.LoadOrDefault()
31+
32+
// 1) Provider selection
33+
providers := []string{"ollama", "http"}
34+
defaultIdx := 0
35+
for i, p := range providers {
36+
if p == cfg.Provider {
37+
defaultIdx = i
38+
break
39+
}
40+
}
41+
providerSelect := promptui.Select{
42+
Label: "Choose LLM provider",
43+
Items: providers,
44+
CursorPos: defaultIdx,
45+
Stdout: os.Stdout,
46+
}
47+
_, provider, err := providerSelect.Run()
48+
if err != nil {
49+
fmt.Println("Setup aborted.")
50+
os.Exit(1)
51+
}
52+
cfg.Provider = provider
53+
54+
// 2) Model name
55+
modelPrompt := promptui.Prompt{
56+
Label: "Model name",
57+
Default: cfg.Model,
58+
Stdout: os.Stdout,
59+
}
60+
if result, err := modelPrompt.Run(); err == nil {
61+
cfg.Model = result
62+
}
63+
64+
// 3) API Key (only for http)
65+
if cfg.Provider == "http" {
66+
keyPrompt := promptui.Prompt{
67+
Label: "API Key (for HTTP, leave blank to skip)",
68+
Default: cfg.APIKey,
69+
Mask: '*',
70+
Stdout: os.Stdout,
71+
}
72+
if result, err := keyPrompt.Run(); err == nil {
73+
cfg.APIKey = result
74+
}
75+
}
76+
77+
// 4) Base URL
78+
urlPrompt := promptui.Prompt{
79+
Label: "Base URL",
80+
Default: cfg.BaseURL,
81+
Stdout: os.Stdout,
82+
}
83+
if result, err := urlPrompt.Run(); err == nil {
84+
cfg.BaseURL = result
85+
}
86+
87+
// 5) Save configuration
88+
if err := config.Save(cfg); err != nil {
89+
fmt.Println("❌ Failed to save config:", err)
90+
os.Exit(1)
91+
}
92+
93+
fmt.Println("✅ Configuration saved.")
94+
},
95+
}
96+
97+
func init() {
98+
rootCmd.AddCommand(setupCmd)
99+
}

0 commit comments

Comments
 (0)