commitgpt 0.2.0 → 0.3.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +208 -70
- data/bin/aicm +1 -1
- data/commitgpt.gemspec +36 -0
- data/lib/commitgpt/cli.rb +54 -9
- data/lib/commitgpt/commit_ai.rb +403 -69
- data/lib/commitgpt/config_manager.rb +164 -0
- data/lib/commitgpt/provider_presets.rb +19 -0
- data/lib/commitgpt/setup_wizard.rb +317 -0
- data/lib/commitgpt/string.rb +4 -0
- data/lib/commitgpt/version.rb +1 -1
- data/lib/commitgpt.rb +2 -2
- metadata +24 -13
- data/.rspec +0 -3
- data/.rubocop.yml +0 -19
- data/CHANGELOG.md +0 -5
- data/CODE_OF_CONDUCT.md +0 -84
- data/Gemfile +0 -19
- data/Gemfile.lock +0 -73
- data/LICENSE.txt +0 -21
- data/Rakefile +0 -12
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'yaml'
|
|
4
|
+
require 'fileutils'
|
|
5
|
+
require_relative 'provider_presets'
|
|
6
|
+
require_relative 'string'
|
|
7
|
+
|
|
8
|
+
module CommitGpt
|
|
9
|
+
# Manages configuration files for CommitGPT
|
|
10
|
+
class ConfigManager
|
|
11
|
+
class << self
|
|
12
|
+
# Get the config directory path
|
|
13
|
+
def config_dir
|
|
14
|
+
File.expand_path('~/.config/commitgpt')
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
# Get main config file path
|
|
18
|
+
def main_config_path
|
|
19
|
+
File.join(config_dir, 'config.yml')
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
# Get local config file path
|
|
23
|
+
def local_config_path
|
|
24
|
+
File.join(config_dir, 'config.local.yml')
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
# Ensure config directory exists
|
|
28
|
+
def ensure_config_dir
|
|
29
|
+
FileUtils.mkdir_p(config_dir)
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
# Check if config files exist
|
|
33
|
+
def config_exists?
|
|
34
|
+
File.exist?(main_config_path)
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
# Load and merge configuration files
|
|
38
|
+
def load_config
|
|
39
|
+
return nil unless config_exists?
|
|
40
|
+
|
|
41
|
+
main_config = YAML.load_file(main_config_path) || {}
|
|
42
|
+
local_config = File.exist?(local_config_path) ? YAML.load_file(local_config_path) : {}
|
|
43
|
+
|
|
44
|
+
merge_configs(main_config, local_config)
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
# Get active provider configuration
|
|
48
|
+
def get_active_provider_config
|
|
49
|
+
config = load_config
|
|
50
|
+
return nil if config.nil? || config['active_provider'].nil?
|
|
51
|
+
|
|
52
|
+
active_provider = config['active_provider']
|
|
53
|
+
providers = config['providers'] || []
|
|
54
|
+
|
|
55
|
+
providers.find { |p| p['name'] == active_provider }
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
# Save main config
|
|
59
|
+
def save_main_config(config)
|
|
60
|
+
ensure_config_dir
|
|
61
|
+
File.write(main_config_path, config.to_yaml)
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
# Save local config
|
|
65
|
+
def save_local_config(config)
|
|
66
|
+
ensure_config_dir
|
|
67
|
+
File.write(local_config_path, config.to_yaml)
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
# Generate default configuration files
|
|
71
|
+
def generate_default_configs
|
|
72
|
+
ensure_config_dir
|
|
73
|
+
|
|
74
|
+
# Generate main config with all providers but empty models
|
|
75
|
+
providers = PROVIDER_PRESETS.map do |preset|
|
|
76
|
+
{
|
|
77
|
+
'name' => preset[:value],
|
|
78
|
+
'model' => '',
|
|
79
|
+
'diff_len' => 32_768,
|
|
80
|
+
'base_url' => preset[:base_url]
|
|
81
|
+
}
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
main_config = {
|
|
85
|
+
'providers' => providers,
|
|
86
|
+
'active_provider' => ''
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
# Generate local config with empty API keys
|
|
90
|
+
local_providers = PROVIDER_PRESETS.map do |preset|
|
|
91
|
+
{
|
|
92
|
+
'name' => preset[:value],
|
|
93
|
+
'api_key' => ''
|
|
94
|
+
}
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
local_config = {
|
|
98
|
+
'providers' => local_providers
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
save_main_config(main_config)
|
|
102
|
+
save_local_config(local_config)
|
|
103
|
+
|
|
104
|
+
# Remind user to add config.local.yml to .gitignore
|
|
105
|
+
puts '▲ Generated default configuration files.'.green
|
|
106
|
+
puts '▲ Remember to add ~/.config/commitgpt/config.local.yml to your .gitignore'.yellow
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
# Get list of configured providers (with API keys)
|
|
110
|
+
def configured_providers
|
|
111
|
+
config = load_config
|
|
112
|
+
return [] if config.nil?
|
|
113
|
+
|
|
114
|
+
providers = config['providers'] || []
|
|
115
|
+
providers.select { |p| p['api_key'] && !p['api_key'].empty? }
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
# Update provider configuration
|
|
119
|
+
def update_provider(provider_name, main_attrs = {}, local_attrs = {})
|
|
120
|
+
# Update main config
|
|
121
|
+
main_config = YAML.load_file(main_config_path)
|
|
122
|
+
provider = main_config['providers'].find { |p| p['name'] == provider_name }
|
|
123
|
+
provider&.merge!(main_attrs)
|
|
124
|
+
save_main_config(main_config)
|
|
125
|
+
|
|
126
|
+
# Update local config
|
|
127
|
+
local_config = File.exist?(local_config_path) ? YAML.load_file(local_config_path) : { 'providers' => [] }
|
|
128
|
+
local_provider = local_config['providers'].find { |p| p['name'] == provider_name }
|
|
129
|
+
if local_provider
|
|
130
|
+
local_provider.merge!(local_attrs)
|
|
131
|
+
else
|
|
132
|
+
local_config['providers'] << { 'name' => provider_name }.merge(local_attrs)
|
|
133
|
+
end
|
|
134
|
+
save_local_config(local_config)
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
# Set active provider
|
|
138
|
+
def set_active_provider(provider_name)
|
|
139
|
+
main_config = YAML.load_file(main_config_path)
|
|
140
|
+
main_config['active_provider'] = provider_name
|
|
141
|
+
save_main_config(main_config)
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
private
|
|
145
|
+
|
|
146
|
+
# Merge main config with local config (local overrides main)
|
|
147
|
+
def merge_configs(main_config, local_config)
|
|
148
|
+
result = main_config.dup
|
|
149
|
+
|
|
150
|
+
# Merge provider-specific settings
|
|
151
|
+
main_providers = main_config['providers'] || []
|
|
152
|
+
local_providers = local_config['providers'] || []
|
|
153
|
+
|
|
154
|
+
merged_providers = main_providers.map do |main_provider|
|
|
155
|
+
local_provider = local_providers.find { |lp| lp['name'] == main_provider['name'] }
|
|
156
|
+
local_provider ? main_provider.merge(local_provider) : main_provider
|
|
157
|
+
end
|
|
158
|
+
|
|
159
|
+
result['providers'] = merged_providers
|
|
160
|
+
result
|
|
161
|
+
end
|
|
162
|
+
end
|
|
163
|
+
end
|
|
164
|
+
end
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module CommitGpt
|
|
4
|
+
# Provider presets for common AI providers
|
|
5
|
+
PROVIDER_PRESETS = [
|
|
6
|
+
{ label: 'Cerebras', value: 'cerebras', base_url: 'https://api.cerebras.ai/v1' },
|
|
7
|
+
{ label: 'Ollama', value: 'ollama', base_url: 'http://127.0.0.1:11434/v1' },
|
|
8
|
+
{ label: 'OpenAI', value: 'openai', base_url: 'https://api.openai.com/v1' },
|
|
9
|
+
{ label: 'LLaMa.cpp', value: 'llamacpp', base_url: 'http://127.0.0.1:8080/v1' },
|
|
10
|
+
{ label: 'LM Studio', value: 'lmstudio', base_url: 'http://127.0.0.1:1234/v1' },
|
|
11
|
+
{ label: 'Llamafile', value: 'llamafile', base_url: 'http://127.0.0.1:8080/v1' },
|
|
12
|
+
{ label: 'DeepSeek', value: 'deepseek', base_url: 'https://api.deepseek.com' },
|
|
13
|
+
{ label: 'Groq', value: 'groq', base_url: 'https://api.groq.com/openai/v1' },
|
|
14
|
+
{ label: 'Mistral', value: 'mistral', base_url: 'https://api.mistral.ai/v1' },
|
|
15
|
+
{ label: 'Anthropic (Claude)', value: 'anthropic', base_url: 'https://api.anthropic.com/v1' },
|
|
16
|
+
{ label: 'OpenRouter', value: 'openrouter', base_url: 'https://openrouter.ai/api/v1' },
|
|
17
|
+
{ label: 'Google AI', value: 'gemini', base_url: 'https://generativelanguage.googleapis.com/v1beta/openai' }
|
|
18
|
+
].freeze
|
|
19
|
+
end
|
|
@@ -0,0 +1,317 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'tty-prompt'
|
|
4
|
+
require 'httparty'
|
|
5
|
+
require 'timeout'
|
|
6
|
+
require_relative 'config_manager'
|
|
7
|
+
require_relative 'provider_presets'
|
|
8
|
+
require_relative 'string'
|
|
9
|
+
|
|
10
|
+
module CommitGpt
|
|
11
|
+
# Interactive setup wizard for configuring AI providers
|
|
12
|
+
class SetupWizard
|
|
13
|
+
def initialize
|
|
14
|
+
@prompt = TTY::Prompt.new
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
# Main entry point for setup
|
|
18
|
+
def run
|
|
19
|
+
ConfigManager.ensure_config_dir
|
|
20
|
+
ConfigManager.generate_default_configs unless ConfigManager.config_exists?
|
|
21
|
+
|
|
22
|
+
provider_choice = select_provider
|
|
23
|
+
configure_provider(provider_choice)
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
# Switch to a different configured provider
|
|
27
|
+
def switch_provider
|
|
28
|
+
configured = ConfigManager.configured_providers
|
|
29
|
+
|
|
30
|
+
if configured.empty?
|
|
31
|
+
puts "▲ No providers configured. Please run 'aicm setup' first.".red
|
|
32
|
+
return
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
choices = configured.map do |p|
|
|
36
|
+
preset = PROVIDER_PRESETS.find { |pr| pr[:value] == p['name'] }
|
|
37
|
+
{ name: preset ? preset[:label] : p['name'], value: p['name'] }
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
selected = @prompt.select('Choose your provider:', choices)
|
|
41
|
+
|
|
42
|
+
# Get current config for this provider
|
|
43
|
+
config = ConfigManager.load_config
|
|
44
|
+
provider = config['providers'].find { |p| p['name'] == selected }
|
|
45
|
+
|
|
46
|
+
# Fetch models and let user select
|
|
47
|
+
models = fetch_models_with_timeout(provider['base_url'], provider['api_key'])
|
|
48
|
+
return if models.nil?
|
|
49
|
+
|
|
50
|
+
model = select_model(models, provider['model'])
|
|
51
|
+
|
|
52
|
+
# Prompt for diff length
|
|
53
|
+
diff_len = prompt_diff_len(provider['diff_len'] || 32_768)
|
|
54
|
+
|
|
55
|
+
# Update config
|
|
56
|
+
ConfigManager.update_provider(selected, { 'model' => model, 'diff_len' => diff_len })
|
|
57
|
+
reset_provider_inference_params(selected)
|
|
58
|
+
ConfigManager.set_active_provider(selected)
|
|
59
|
+
|
|
60
|
+
preset = PROVIDER_PRESETS.find { |pr| pr[:value] == selected }
|
|
61
|
+
provider_label = preset ? preset[:label] : selected
|
|
62
|
+
|
|
63
|
+
puts "\nModel selected: #{model}".green
|
|
64
|
+
puts "Setup complete ✅ You're now using #{provider_label}.".green
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
# Change model for the active provider
|
|
68
|
+
def change_model
|
|
69
|
+
provider_config = ConfigManager.get_active_provider_config
|
|
70
|
+
|
|
71
|
+
if provider_config.nil? || provider_config['api_key'].nil? || provider_config['api_key'].empty?
|
|
72
|
+
puts "▲ No active provider configured. Please run 'aicm setup'.".red
|
|
73
|
+
return
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
# Fetch models and let user select
|
|
77
|
+
models = fetch_models_with_timeout(provider_config['base_url'], provider_config['api_key'])
|
|
78
|
+
return if models.nil?
|
|
79
|
+
|
|
80
|
+
model = select_model(models, provider_config['model'])
|
|
81
|
+
|
|
82
|
+
# Update config
|
|
83
|
+
ConfigManager.update_provider(provider_config['name'], { 'model' => model })
|
|
84
|
+
reset_provider_inference_params(provider_config['name'])
|
|
85
|
+
|
|
86
|
+
puts "\nModel selected: #{model}".green
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
private
|
|
90
|
+
|
|
91
|
+
# Select provider from list
|
|
92
|
+
def select_provider
|
|
93
|
+
config = ConfigManager.load_config
|
|
94
|
+
configured = config ? (config['providers'] || []) : []
|
|
95
|
+
|
|
96
|
+
choices = PROVIDER_PRESETS.map do |preset|
|
|
97
|
+
# Check if this provider already has an API key
|
|
98
|
+
provider_config = configured.find { |p| p['name'] == preset[:value] }
|
|
99
|
+
has_key = provider_config && provider_config['api_key'] && !provider_config['api_key'].empty?
|
|
100
|
+
|
|
101
|
+
label = preset[:label]
|
|
102
|
+
label = "✅ #{label}" if has_key
|
|
103
|
+
label = "#{label} (recommended)" if preset[:value] == 'cerebras'
|
|
104
|
+
label = "#{label} (local)" if %w[ollama llamacpp lmstudio llamafile].include?(preset[:value])
|
|
105
|
+
|
|
106
|
+
{ name: label, value: preset[:value] }
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
choices << { name: 'Custom (OpenAI-compatible)', value: 'custom' }
|
|
110
|
+
|
|
111
|
+
@prompt.select('Choose your AI provider:', choices, per_page: 15)
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
# Configure selected provider
|
|
115
|
+
def configure_provider(provider_name)
|
|
116
|
+
if provider_name == 'custom'
|
|
117
|
+
configure_custom_provider
|
|
118
|
+
return
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
preset = PROVIDER_PRESETS.find { |p| p[:value] == provider_name }
|
|
122
|
+
base_url = preset[:base_url]
|
|
123
|
+
provider_label = preset[:label]
|
|
124
|
+
|
|
125
|
+
# Get existing API key if any
|
|
126
|
+
config = ConfigManager.load_config
|
|
127
|
+
existing_provider = config['providers'].find { |p| p['name'] == provider_name } if config
|
|
128
|
+
|
|
129
|
+
# Prompt for API key
|
|
130
|
+
api_key = prompt_api_key(provider_label, existing_provider&.dig('api_key'))
|
|
131
|
+
return if api_key.nil? # User cancelled
|
|
132
|
+
|
|
133
|
+
# Fetch models with timeout
|
|
134
|
+
models = fetch_models_with_timeout(base_url, api_key)
|
|
135
|
+
return if models.nil?
|
|
136
|
+
|
|
137
|
+
# Let user select model
|
|
138
|
+
model = select_model(models, existing_provider&.dig('model'))
|
|
139
|
+
|
|
140
|
+
# Prompt for diff length
|
|
141
|
+
diff_len = prompt_diff_len(existing_provider&.dig('diff_len') || 32_768)
|
|
142
|
+
|
|
143
|
+
# Save configuration
|
|
144
|
+
ConfigManager.update_provider(
|
|
145
|
+
provider_name,
|
|
146
|
+
{ 'model' => model, 'diff_len' => diff_len },
|
|
147
|
+
{ 'api_key' => api_key }
|
|
148
|
+
)
|
|
149
|
+
ConfigManager.set_active_provider(provider_name)
|
|
150
|
+
|
|
151
|
+
puts "\nModel selected: #{model}".green
|
|
152
|
+
puts "✅ Setup complete! You're now using #{provider_label}.".green
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
# Configure custom provider
|
|
156
|
+
def configure_custom_provider
|
|
157
|
+
provider_name = @prompt.ask('Enter provider name:') do |q|
|
|
158
|
+
q.required true
|
|
159
|
+
q.modify :strip, :down
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
base_url = @prompt.ask('Enter base URL:') do |q|
|
|
163
|
+
q.required true
|
|
164
|
+
q.default 'http://localhost:8080/v1'
|
|
165
|
+
end
|
|
166
|
+
|
|
167
|
+
api_key = @prompt.mask('Enter your API key (optional):') { |q| q.echo false }
|
|
168
|
+
|
|
169
|
+
# Fetch models
|
|
170
|
+
models = fetch_models_with_timeout(base_url, api_key)
|
|
171
|
+
return if models.nil?
|
|
172
|
+
|
|
173
|
+
model = select_model(models)
|
|
174
|
+
diff_len = prompt_diff_len(32_768)
|
|
175
|
+
|
|
176
|
+
# Add to presets dynamically (just for this session)
|
|
177
|
+
# Save to config
|
|
178
|
+
config = ConfigManager.load_config || { 'providers' => [], 'active_provider' => '' }
|
|
179
|
+
|
|
180
|
+
# Add or update provider in main config
|
|
181
|
+
existing = config['providers'].find { |p| p['name'] == provider_name }
|
|
182
|
+
if existing
|
|
183
|
+
existing.merge!({ 'model' => model, 'diff_len' => diff_len, 'base_url' => base_url })
|
|
184
|
+
else
|
|
185
|
+
config['providers'] << {
|
|
186
|
+
'name' => provider_name,
|
|
187
|
+
'model' => model,
|
|
188
|
+
'diff_len' => diff_len,
|
|
189
|
+
'base_url' => base_url
|
|
190
|
+
}
|
|
191
|
+
end
|
|
192
|
+
config['active_provider'] = provider_name
|
|
193
|
+
ConfigManager.save_main_config(config)
|
|
194
|
+
|
|
195
|
+
# Update local config
|
|
196
|
+
local_config = if File.exist?(ConfigManager.local_config_path)
|
|
197
|
+
YAML.load_file(ConfigManager.local_config_path)
|
|
198
|
+
else
|
|
199
|
+
{ 'providers' => [] }
|
|
200
|
+
end
|
|
201
|
+
|
|
202
|
+
local_existing = local_config['providers'].find { |p| p['name'] == provider_name }
|
|
203
|
+
if local_existing
|
|
204
|
+
local_existing['api_key'] = api_key
|
|
205
|
+
else
|
|
206
|
+
local_config['providers'] << { 'name' => provider_name, 'api_key' => api_key }
|
|
207
|
+
end
|
|
208
|
+
ConfigManager.save_local_config(local_config)
|
|
209
|
+
|
|
210
|
+
puts "\nModel selected: #{model}".green
|
|
211
|
+
puts "✅ Setup complete! You're now using #{provider_name}.".green
|
|
212
|
+
end
|
|
213
|
+
|
|
214
|
+
# Prompt for API key
|
|
215
|
+
def prompt_api_key(_provider_name, existing_key)
|
|
216
|
+
message = if existing_key && !existing_key.empty?
|
|
217
|
+
'Enter your API key (press Enter to keep existing):'
|
|
218
|
+
else
|
|
219
|
+
'Enter your API key:'
|
|
220
|
+
end
|
|
221
|
+
|
|
222
|
+
key = @prompt.mask(message) { |q| q.echo false }
|
|
223
|
+
|
|
224
|
+
# If user pressed Enter and there's an existing key, use it
|
|
225
|
+
if key.empty? && existing_key && !existing_key.empty?
|
|
226
|
+
existing_key
|
|
227
|
+
else
|
|
228
|
+
key
|
|
229
|
+
end
|
|
230
|
+
end
|
|
231
|
+
|
|
232
|
+
# Fetch models from provider with timeout
|
|
233
|
+
def fetch_models_with_timeout(base_url, api_key)
|
|
234
|
+
puts 'Fetching available models...'.gray
|
|
235
|
+
|
|
236
|
+
models = nil
|
|
237
|
+
begin
|
|
238
|
+
Timeout.timeout(5) do
|
|
239
|
+
headers = {
|
|
240
|
+
'Content-Type' => 'application/json',
|
|
241
|
+
'User-Agent' => "Ruby/#{RUBY_VERSION}"
|
|
242
|
+
}
|
|
243
|
+
headers['Authorization'] = "Bearer #{api_key}" if api_key && !api_key.empty?
|
|
244
|
+
|
|
245
|
+
response = HTTParty.get("#{base_url}/models", headers: headers)
|
|
246
|
+
|
|
247
|
+
if response.code == 200
|
|
248
|
+
models = response['data'] || []
|
|
249
|
+
models = models.map { |m| m['id'] }.compact.sort
|
|
250
|
+
else
|
|
251
|
+
puts "▲ Failed to fetch models: HTTP #{response.code}".red
|
|
252
|
+
return nil
|
|
253
|
+
end
|
|
254
|
+
end
|
|
255
|
+
rescue Timeout::Error
|
|
256
|
+
puts '▲ Connection timeout (5s). Please check your network, base_url, and api_key.'.red
|
|
257
|
+
exit(0)
|
|
258
|
+
rescue StandardError => e
|
|
259
|
+
puts "▲ Error fetching models: #{e.message}".red
|
|
260
|
+
exit(0)
|
|
261
|
+
end
|
|
262
|
+
|
|
263
|
+
if models.nil? || models.empty?
|
|
264
|
+
puts '▲ No models found. Please check your configuration.'.red
|
|
265
|
+
exit(0)
|
|
266
|
+
end
|
|
267
|
+
|
|
268
|
+
models
|
|
269
|
+
end
|
|
270
|
+
|
|
271
|
+
# Let user select a model
|
|
272
|
+
def select_model(models, current_model = nil)
|
|
273
|
+
choices = models.map { |m| { name: m, value: m } }
|
|
274
|
+
choices << { name: 'Custom model name...', value: :custom }
|
|
275
|
+
|
|
276
|
+
# Set default to current model if it exists
|
|
277
|
+
default_index = if current_model && models.include?(current_model)
|
|
278
|
+
models.index(current_model) + 1 # +1 for 1-based index
|
|
279
|
+
else
|
|
280
|
+
1
|
|
281
|
+
end
|
|
282
|
+
|
|
283
|
+
selected = @prompt.select('Choose your model:', choices, per_page: 15, default: default_index)
|
|
284
|
+
|
|
285
|
+
if selected == :custom
|
|
286
|
+
@prompt.ask('Enter custom model name:') do |q|
|
|
287
|
+
q.required true
|
|
288
|
+
q.modify :strip
|
|
289
|
+
end
|
|
290
|
+
else
|
|
291
|
+
selected
|
|
292
|
+
end
|
|
293
|
+
end
|
|
294
|
+
|
|
295
|
+
# Prompt for diff length
|
|
296
|
+
def prompt_diff_len(default = 32_768)
|
|
297
|
+
answer = @prompt.ask('Set the maximum diff length (Bytes) for generating commit message:') do |q|
|
|
298
|
+
q.default default.to_s
|
|
299
|
+
q.convert :int
|
|
300
|
+
end
|
|
301
|
+
|
|
302
|
+
answer || default
|
|
303
|
+
end
|
|
304
|
+
|
|
305
|
+
def reset_provider_inference_params(provider_name)
|
|
306
|
+
config = YAML.load_file(ConfigManager.main_config_path)
|
|
307
|
+
return unless config && config['providers']
|
|
308
|
+
|
|
309
|
+
provider = config['providers'].find { |p| p['name'] == provider_name }
|
|
310
|
+
if provider
|
|
311
|
+
provider.delete('can_disable_reasoning')
|
|
312
|
+
provider.delete('max_tokens')
|
|
313
|
+
ConfigManager.save_main_config(config)
|
|
314
|
+
end
|
|
315
|
+
end
|
|
316
|
+
end
|
|
317
|
+
end
|
data/lib/commitgpt/string.rb
CHANGED
data/lib/commitgpt/version.rb
CHANGED
data/lib/commitgpt.rb
CHANGED
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: commitgpt
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.3.3
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Peng Zhang
|
|
@@ -15,28 +15,42 @@ dependencies:
|
|
|
15
15
|
requirements:
|
|
16
16
|
- - "~>"
|
|
17
17
|
- !ruby/object:Gem::Version
|
|
18
|
-
version: '0.
|
|
18
|
+
version: '0.24'
|
|
19
19
|
type: :runtime
|
|
20
20
|
prerelease: false
|
|
21
21
|
version_requirements: !ruby/object:Gem::Requirement
|
|
22
22
|
requirements:
|
|
23
23
|
- - "~>"
|
|
24
24
|
- !ruby/object:Gem::Version
|
|
25
|
-
version: '0.
|
|
25
|
+
version: '0.24'
|
|
26
26
|
- !ruby/object:Gem::Dependency
|
|
27
27
|
name: thor
|
|
28
28
|
requirement: !ruby/object:Gem::Requirement
|
|
29
29
|
requirements:
|
|
30
30
|
- - "~>"
|
|
31
31
|
- !ruby/object:Gem::Version
|
|
32
|
-
version: '1.
|
|
32
|
+
version: '1.4'
|
|
33
33
|
type: :runtime
|
|
34
34
|
prerelease: false
|
|
35
35
|
version_requirements: !ruby/object:Gem::Requirement
|
|
36
36
|
requirements:
|
|
37
37
|
- - "~>"
|
|
38
38
|
- !ruby/object:Gem::Version
|
|
39
|
-
version: '1.
|
|
39
|
+
version: '1.4'
|
|
40
|
+
- !ruby/object:Gem::Dependency
|
|
41
|
+
name: tty-prompt
|
|
42
|
+
requirement: !ruby/object:Gem::Requirement
|
|
43
|
+
requirements:
|
|
44
|
+
- - "~>"
|
|
45
|
+
- !ruby/object:Gem::Version
|
|
46
|
+
version: '0.23'
|
|
47
|
+
type: :runtime
|
|
48
|
+
prerelease: false
|
|
49
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
50
|
+
requirements:
|
|
51
|
+
- - "~>"
|
|
52
|
+
- !ruby/object:Gem::Version
|
|
53
|
+
version: '0.23'
|
|
40
54
|
description: A CLI that writes your git commit messages for you with AI. Never write
|
|
41
55
|
a commit message again.
|
|
42
56
|
email:
|
|
@@ -46,20 +60,16 @@ executables:
|
|
|
46
60
|
extensions: []
|
|
47
61
|
extra_rdoc_files: []
|
|
48
62
|
files:
|
|
49
|
-
- ".rspec"
|
|
50
|
-
- ".rubocop.yml"
|
|
51
|
-
- CHANGELOG.md
|
|
52
|
-
- CODE_OF_CONDUCT.md
|
|
53
|
-
- Gemfile
|
|
54
|
-
- Gemfile.lock
|
|
55
63
|
- LICENSE
|
|
56
|
-
- LICENSE.txt
|
|
57
64
|
- README.md
|
|
58
|
-
- Rakefile
|
|
59
65
|
- bin/aicm
|
|
66
|
+
- commitgpt.gemspec
|
|
60
67
|
- lib/commitgpt.rb
|
|
61
68
|
- lib/commitgpt/cli.rb
|
|
62
69
|
- lib/commitgpt/commit_ai.rb
|
|
70
|
+
- lib/commitgpt/config_manager.rb
|
|
71
|
+
- lib/commitgpt/provider_presets.rb
|
|
72
|
+
- lib/commitgpt/setup_wizard.rb
|
|
63
73
|
- lib/commitgpt/string.rb
|
|
64
74
|
- lib/commitgpt/version.rb
|
|
65
75
|
homepage: https://github.com/ZPVIP/commitgpt
|
|
@@ -69,6 +79,7 @@ metadata:
|
|
|
69
79
|
homepage_uri: https://github.com/ZPVIP/commitgpt
|
|
70
80
|
source_code_uri: https://github.com/ZPVIP/commitgpt
|
|
71
81
|
changelog_uri: https://github.com/ZPVIP/commitgpt/blob/master/CHANGELOG.md
|
|
82
|
+
rubygems_mfa_required: 'true'
|
|
72
83
|
rdoc_options: []
|
|
73
84
|
require_paths:
|
|
74
85
|
- lib
|
data/.rspec
DELETED
data/.rubocop.yml
DELETED
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
AllCops:
|
|
2
|
-
TargetRubyVersion: 2.6
|
|
3
|
-
|
|
4
|
-
Style/StringLiterals:
|
|
5
|
-
Enabled: true
|
|
6
|
-
EnforcedStyle: double_quotes
|
|
7
|
-
|
|
8
|
-
Style/StringLiteralsInInterpolation:
|
|
9
|
-
Enabled: true
|
|
10
|
-
EnforcedStyle: double_quotes
|
|
11
|
-
|
|
12
|
-
Layout/LineLength:
|
|
13
|
-
Max: 160
|
|
14
|
-
|
|
15
|
-
MethodLength:
|
|
16
|
-
Max: 20
|
|
17
|
-
|
|
18
|
-
Metrics/BlockLength:
|
|
19
|
-
Max: 100
|