41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
|
# File 'lib/n2b/base.rb', line 41
def get_config(reconfigure: false, advanced_flow: false)
config = load_config
api_key = ENV['CLAUDE_API_KEY'] || config['access_key'] _model = config['model']
is_first_time_core_setup = config['llm'].nil?
if api_key.nil? || api_key == '' || config['llm'].nil? || reconfigure
puts "--- N2B Core LLM Configuration ---"
print "Choose a language model to use (1:claude, 2:openai, 3:gemini, 4:openrouter, 5:ollama) [current: #{config['llm']}]: "
llm_choice = $stdin.gets.chomp
llm_choice = config['llm'] if llm_choice.empty? && !config['llm'].nil?
unless ['claude', 'openai', 'gemini', 'openrouter', 'ollama', '1', '2', '3', '4', '5'].include?(llm_choice)
puts "Invalid language model selection. Defaulting to 'claude' or previous if available."
llm_choice = config['llm'] || 'claude' end
selected_llm = case llm_choice
when '1', 'claude' then 'claude'
when '2', 'openai' then 'openai'
when '3', 'gemini' then 'gemini'
when '4', 'openrouter' then 'openrouter'
when '5', 'ollama' then 'ollama'
else config['llm'] || 'claude' end
config['llm'] = selected_llm
if selected_llm == 'ollama'
puts "\n--- Ollama Specific Configuration ---"
puts "Ollama typically runs locally and doesn't require an API key."
current_model = config['model']
model_choice = N2B::ModelConfig.get_model_choice(selected_llm, current_model) config['model'] = model_choice
current_ollama_api_url = config['ollama_api_url'] || N2M::Llm::Ollama::DEFAULT_OLLAMA_API_URI
print "Enter Ollama API base URL [current: #{current_ollama_api_url}]: "
ollama_api_url_input = $stdin.gets.chomp
config['ollama_api_url'] = ollama_api_url_input.empty? ? current_ollama_api_url : ollama_api_url_input
config.delete('access_key') else
puts "\n--- #{selected_llm.capitalize} Specific Configuration ---"
current_api_key = config['access_key']
print "Enter your #{selected_llm} API key #{ current_api_key.nil? || current_api_key.empty? ? '' : '[leave blank to keep current]' }: "
api_key_input = $stdin.gets.chomp
config['access_key'] = api_key_input if !api_key_input.empty?
config['access_key'] = current_api_key if api_key_input.empty? && !current_api_key.nil? && !current_api_key.empty?
if config['access_key'].nil? || config['access_key'].empty?
puts "API key is required for #{selected_llm}."
exit 1
end
current_model = config['model']
model_choice = N2B::ModelConfig.get_model_choice(selected_llm, current_model) config['model'] = model_choice
if selected_llm == 'openrouter'
current_site_url = config['openrouter_site_url'] || ""
print "Enter your OpenRouter Site URL (optional, for HTTP-Referer) [current: #{current_site_url}]: "
openrouter_site_url_input = $stdin.gets.chomp
config['openrouter_site_url'] = openrouter_site_url_input.empty? ? current_site_url : openrouter_site_url_input
current_site_name = config['openrouter_site_name'] || ""
print "Enter your OpenRouter Site Name (optional, for X-Title) [current: #{current_site_name}]: "
openrouter_site_name_input = $stdin.gets.chomp
config['openrouter_site_name'] = openrouter_site_name_input.empty? ? current_site_name : openrouter_site_name_input
end
end
prompt_for_advanced = advanced_flow || is_first_time_core_setup
if prompt_for_advanced
puts "\n--- Advanced Settings ---"
print "Would you like to configure advanced settings (e.g., Jira or GitHub integration, privacy)? (y/n) [default: n]: "
choice = $stdin.gets.chomp.downcase
if choice == 'y'
current_tracker = config['issue_tracker'] || 'none'
print "\nSelect issue tracker to integrate (none, jira, github) [current: #{current_tracker}]: "
tracker_choice = $stdin.gets.chomp.downcase
tracker_choice = current_tracker if tracker_choice.empty?
config['issue_tracker'] = tracker_choice
case tracker_choice
when 'jira'
puts "\n--- Jira Integration ---"
puts "You can generate a Jira API token here: https://support.atlassian.com/atlassian-account/docs/manage-api-tokens-for-your-atlassian-account/"
config['jira'] ||= {}
print "Jira Domain (e.g., your-company.atlassian.net) [current: #{config['jira']['domain']}]: "
config['jira']['domain'] = $stdin.gets.chomp.then { |val| val.empty? ? config['jira']['domain'] : val }
print "Jira Email Address [current: #{config['jira']['email']}]: "
config['jira']['email'] = $stdin.gets.chomp.then { |val| val.empty? ? config['jira']['email'] : val }
print "Jira API Token #{config['jira']['api_key'] ? '[leave blank to keep current]' : ''}: "
api_token_input = $stdin.gets.chomp
config['jira']['api_key'] = api_token_input if !api_token_input.empty?
print "Default Jira Project Key (optional, e.g., MYPROJ) [current: #{config['jira']['default_project']}]: "
config['jira']['default_project'] = $stdin.gets.chomp.then { |val| val.empty? ? config['jira']['default_project'] : val }
when 'github'
puts "\n--- GitHub Integration ---"
config['github'] ||= {}
print "GitHub Repository (owner/repo) [current: #{config['github']['repo']}]: "
config['github']['repo'] = $stdin.gets.chomp.then { |val| val.empty? ? config['github']['repo'] : val }
print "GitHub Access Token #{config['github']['access_token'] ? '[leave blank to keep current]' : ''}: "
token_input = $stdin.gets.chomp
config['github']['access_token'] = token_input if !token_input.empty?
else
config['jira'] ||= {}
config['github'] ||= {}
end
puts "\n--- Privacy Settings ---"
config['privacy'] ||= {}
puts "Allow sending shell command history to the LLM? (true/false) [current: #{config['privacy']['send_shell_history']}]"
config['privacy']['send_shell_history'] = $stdin.gets.chomp.then { |val| val.empty? ? config['privacy']['send_shell_history'] : (val.downcase == 'true') }
puts "Allow sending LLM interaction history (your prompts and LLM responses) to the LLM? (true/false) [current: #{config['privacy']['send_llm_history']}]"
config['privacy']['send_llm_history'] = $stdin.gets.chomp.then { |val| val.empty? ? config['privacy']['send_llm_history'] : (val.downcase == 'true') }
puts "Allow sending current directory to the LLM? (true/false) [current: #{config['privacy']['send_current_directory']}]"
config['privacy']['send_current_directory'] = $stdin.gets.chomp.then { |val| val.empty? ? config['privacy']['send_current_directory'] : (val.downcase == 'true') }
puts "Append n2b generated commands to your shell history file? (true/false) [current: #{config['append_to_shell_history']}]"
config['append_to_shell_history'] = $stdin.gets.chomp.then { |val| val.empty? ? config['append_to_shell_history'] : (val.downcase == 'true') }
config['privacy']['append_to_shell_history'] = config['append_to_shell_history']
prompt_for_editor_config(config)
else config['jira'] ||= {}
config['github'] ||= {}
config['issue_tracker'] ||= 'none'
config['privacy'] ||= {}
config['privacy']['send_shell_history'] = config['privacy']['send_shell_history'] || false
config['privacy']['send_llm_history'] = config['privacy']['send_llm_history'] || true config['privacy']['send_current_directory'] = config['privacy']['send_current_directory'] || true config['append_to_shell_history'] = config['append_to_shell_history'] || false
config['privacy']['append_to_shell_history'] = config['append_to_shell_history']
end
else config['jira'] ||= {}
config['github'] ||= {}
config['issue_tracker'] ||= 'none'
config['privacy'] ||= {}
config['privacy']['send_shell_history'] = config['privacy']['send_shell_history'] || false
config['privacy']['send_llm_history'] = config['privacy']['send_llm_history'] || true
config['privacy']['send_current_directory'] = config['privacy']['send_current_directory'] || true
config['append_to_shell_history'] = config['append_to_shell_history'] || false
config['privacy']['append_to_shell_history'] = config['append_to_shell_history']
end
config['editor'] ||= {}
config['editor']['command'] ||= nil config['editor']['type'] ||= nil config['editor']['configured'] ||= false
validation_errors = validate_config(config)
if validation_errors.any?
puts "\n⚠️ Configuration validation warnings:"
validation_errors.each { |error| puts " - #{error}" }
puts "Configuration saved with warnings."
end
puts "\nConfiguration saved to #{self.class.config_file}"
FileUtils.mkdir_p(File.dirname(self.class.config_file)) unless File.exist?(File.dirname(self.class.config_file))
File.write(self.class.config_file, config.to_yaml)
else
config['jira'] ||= {}
config['github'] ||= {}
config['issue_tracker'] ||= 'none'
config['privacy'] ||= {}
config['privacy']['send_shell_history'] = config['privacy']['send_shell_history'] || false
config['privacy']['send_llm_history'] = config['privacy']['send_llm_history'] || true
config['privacy']['send_current_directory'] = config['privacy']['send_current_directory'] || true
current_append_setting = config.key?('append_to_shell_history') ? config['append_to_shell_history'] : false
config['append_to_shell_history'] = current_append_setting
config['privacy']['append_to_shell_history'] = config['privacy']['append_to_shell_history'] || current_append_setting
config['editor'] ||= {}
config['editor']['command'] ||= nil
config['editor']['type'] ||= nil
config['editor']['configured'] ||= false
end
config
end
|