n2b 0.7.1 → 2.1.0
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 +291 -118
- data/bin/n2b-test-github +22 -0
- data/lib/n2b/base.rb +346 -55
- data/lib/n2b/cli.rb +60 -404
- data/lib/n2b/config/models.yml +19 -3
- data/lib/n2b/github_client.rb +391 -0
- data/lib/n2b/jira_client.rb +238 -38
- data/lib/n2b/llm/claude.rb +1 -1
- data/lib/n2b/llm/gemini.rb +7 -2
- data/lib/n2b/llm/open_ai.rb +1 -1
- data/lib/n2b/llm/vertex_ai.rb +225 -0
- data/lib/n2b/merge_cli.rb +1774 -136
- data/lib/n2b/message_utils.rb +59 -0
- data/lib/n2b/model_config.rb +8 -0
- data/lib/n2b/templates/diff_system_prompt.txt +40 -20
- data/lib/n2b/templates/github_comment.txt +67 -0
- data/lib/n2b/templates/jira_comment.txt +7 -0
- data/lib/n2b/templates/merge_conflict_prompt.txt +2 -2
- data/lib/n2b/version.rb +1 -1
- data/lib/n2b.rb +1 -0
- metadata +21 -2
data/lib/n2b/cli.rb
CHANGED
@@ -1,5 +1,3 @@
|
|
1
|
-
require_relative 'jira_client' # For N2B::JiraClient
|
2
|
-
|
3
1
|
module N2B
|
4
2
|
class CLI < Base
|
5
3
|
def self.run(args)
|
@@ -16,281 +14,27 @@ module N2B
|
|
16
14
|
config = get_config(reconfigure: @options[:config], advanced_flow: @options[:advanced_config])
|
17
15
|
user_input = @args.join(' ') # All remaining args form user input/prompt addition
|
18
16
|
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
17
|
+
# Diff functionality has been removed from N2B::CLI
|
18
|
+
# if @options[:diff]
|
19
|
+
# handle_diff_analysis(config)
|
20
|
+
# els
|
21
|
+
if user_input.empty? # No input text after options
|
22
|
+
# If config mode was chosen, it's handled by get_config.
|
23
|
+
# If not, and no input, prompt for it.
|
24
|
+
unless @options[:config] # Don't prompt if only -c or --advanced-config was used
|
25
|
+
puts "Enter your natural language command (or type 'exit' or 'quit'):"
|
26
|
+
input_text = $stdin.gets.chomp
|
27
|
+
exit if ['exit', 'quit'].include?(input_text.downcase)
|
28
|
+
process_natural_language_command(input_text, config)
|
29
|
+
end
|
30
|
+
else # Natural language command provided as argument
|
26
31
|
process_natural_language_command(user_input, config)
|
27
32
|
end
|
28
33
|
end
|
29
34
|
|
30
35
|
protected
|
31
36
|
|
32
|
-
|
33
|
-
vcs_type = get_vcs_type
|
34
|
-
if vcs_type == :none
|
35
|
-
puts "Error: Not a git or hg repository."
|
36
|
-
exit 1
|
37
|
-
end
|
38
|
-
|
39
|
-
# Get requirements file from parsed options
|
40
|
-
requirements_filepath = @options[:requirements]
|
41
|
-
user_prompt_addition = @args.join(' ') # All remaining args are user prompt addition
|
42
|
-
|
43
|
-
# Jira Ticket Information
|
44
|
-
jira_ticket = @options[:jira_ticket]
|
45
|
-
jira_update_flag = @options[:jira_update] # true, false, or nil
|
46
|
-
|
47
|
-
requirements_content = nil # Initialize requirements_content
|
48
|
-
|
49
|
-
if jira_ticket
|
50
|
-
puts "Jira ticket specified: #{jira_ticket}"
|
51
|
-
if config['jira'] && config['jira']['domain'] && config['jira']['email'] && config['jira']['api_key']
|
52
|
-
begin
|
53
|
-
jira_client = N2B::JiraClient.new(config) # Pass the whole config
|
54
|
-
puts "Fetching Jira ticket details..."
|
55
|
-
# If a requirements file is also provided, the Jira ticket will take precedence.
|
56
|
-
# Or, we could append/prepend. For now, Jira overwrites.
|
57
|
-
requirements_content = jira_client.fetch_ticket(jira_ticket)
|
58
|
-
puts "Successfully fetched Jira ticket details."
|
59
|
-
# The fetched content is now in requirements_content and will be passed to analyze_diff
|
60
|
-
rescue N2B::JiraClient::JiraApiError => e
|
61
|
-
puts "Error fetching Jira ticket: #{e.message}"
|
62
|
-
puts "Proceeding with diff analysis without Jira ticket details."
|
63
|
-
rescue ArgumentError => e # Catches missing Jira config in JiraClient.new
|
64
|
-
puts "Jira configuration error: #{e.message}"
|
65
|
-
puts "Please ensure Jira is configured correctly using 'n2b -c'."
|
66
|
-
puts "Proceeding with diff analysis without Jira ticket details."
|
67
|
-
rescue StandardError => e
|
68
|
-
puts "An unexpected error occurred while fetching Jira ticket: #{e.message}"
|
69
|
-
puts "Proceeding with diff analysis without Jira ticket details."
|
70
|
-
end
|
71
|
-
else
|
72
|
-
puts "Jira configuration is missing or incomplete in N2B settings."
|
73
|
-
puts "Please configure Jira using 'n2b -c' to fetch ticket details."
|
74
|
-
puts "Proceeding with diff analysis without Jira ticket details."
|
75
|
-
end
|
76
|
-
# Handling of jira_update_flag can be done elsewhere, e.g., after analysis
|
77
|
-
if jira_update_flag == true
|
78
|
-
puts "Note: Jira ticket update (--jira-update) is flagged."
|
79
|
-
# Actual update logic will be separate
|
80
|
-
elsif jira_update_flag == false
|
81
|
-
puts "Note: Jira ticket will not be updated (--jira-no-update)."
|
82
|
-
end
|
83
|
-
end
|
84
|
-
|
85
|
-
# Load requirements from file if no Jira ticket was fetched or if specifically desired even with Jira.
|
86
|
-
# Current logic: Jira fetch, if successful, populates requirements_content.
|
87
|
-
# If Jira not specified, or fetch failed, try to load from file.
|
88
|
-
if requirements_content.nil? && requirements_filepath
|
89
|
-
if File.exist?(requirements_filepath)
|
90
|
-
puts "Loading requirements from file: #{requirements_filepath}"
|
91
|
-
requirements_content = File.read(requirements_filepath)
|
92
|
-
else
|
93
|
-
puts "Error: Requirements file not found: #{requirements_filepath}"
|
94
|
-
# Decide if to exit or proceed. For now, proceed.
|
95
|
-
puts "Proceeding with diff analysis without file-based requirements."
|
96
|
-
end
|
97
|
-
elsif requirements_content && requirements_filepath
|
98
|
-
puts "Note: Both Jira ticket and requirements file were provided. Using Jira ticket content for analysis."
|
99
|
-
end
|
100
|
-
|
101
|
-
diff_output = execute_vcs_diff(vcs_type, @options[:branch])
|
102
|
-
analysis_result = analyze_diff(diff_output, config, user_prompt_addition, requirements_content) # Store the result
|
103
|
-
|
104
|
-
# --- Jira Update Logic ---
|
105
|
-
if jira_ticket && analysis_result && !analysis_result.empty?
|
106
|
-
# Check if Jira config is valid for updating
|
107
|
-
if config['jira'] && config['jira']['domain'] && config['jira']['email'] && config['jira']['api_key']
|
108
|
-
jira_comment_data = format_analysis_for_jira(analysis_result)
|
109
|
-
proceed_with_update = false
|
110
|
-
|
111
|
-
if jira_update_flag == true # --jira-update used
|
112
|
-
proceed_with_update = true
|
113
|
-
elsif jira_update_flag.nil? # Neither --jira-update nor --jira-no-update used
|
114
|
-
puts "\nWould you like to update Jira ticket #{jira_ticket} with this analysis? (y/n)"
|
115
|
-
user_choice = $stdin.gets.chomp.downcase
|
116
|
-
proceed_with_update = user_choice == 'y'
|
117
|
-
end # If jira_update_flag is false, proceed_with_update remains false
|
118
|
-
|
119
|
-
if proceed_with_update
|
120
|
-
begin
|
121
|
-
# Re-instantiate JiraClient or use an existing one if available and in scope
|
122
|
-
# For safety and simplicity here, re-instantiate with current config.
|
123
|
-
update_jira_client = N2B::JiraClient.new(config)
|
124
|
-
puts "Updating Jira ticket #{jira_ticket}..."
|
125
|
-
if update_jira_client.update_ticket(jira_ticket, jira_comment_data)
|
126
|
-
puts "Jira ticket #{jira_ticket} updated successfully."
|
127
|
-
else
|
128
|
-
# update_ticket currently returns true/false, but might raise error for http issues
|
129
|
-
puts "Failed to update Jira ticket #{jira_ticket}. The client did not report an error, but the update may not have completed."
|
130
|
-
end
|
131
|
-
rescue N2B::JiraClient::JiraApiError => e
|
132
|
-
puts "Error updating Jira ticket: #{e.message}"
|
133
|
-
rescue ArgumentError => e # From JiraClient.new if config is suddenly invalid
|
134
|
-
puts "Jira configuration error before update: #{e.message}"
|
135
|
-
rescue StandardError => e
|
136
|
-
puts "An unexpected error occurred while updating Jira ticket: #{e.message}"
|
137
|
-
end
|
138
|
-
else
|
139
|
-
puts "Jira ticket update skipped."
|
140
|
-
end
|
141
|
-
else
|
142
|
-
puts "Jira configuration is missing or incomplete. Cannot proceed with Jira update."
|
143
|
-
end
|
144
|
-
elsif jira_ticket && (analysis_result.nil? || analysis_result.empty?)
|
145
|
-
puts "Skipping Jira update as analysis result was empty or not generated."
|
146
|
-
end
|
147
|
-
# --- End of Jira Update Logic ---
|
148
|
-
|
149
|
-
analysis_result # Return analysis_result from handle_diff_analysis
|
150
|
-
end
|
151
|
-
|
152
|
-
def get_vcs_type
|
153
|
-
if Dir.exist?(File.join(Dir.pwd, '.git'))
|
154
|
-
:git
|
155
|
-
elsif Dir.exist?(File.join(Dir.pwd, '.hg'))
|
156
|
-
:hg
|
157
|
-
else
|
158
|
-
:none
|
159
|
-
end
|
160
|
-
end
|
161
|
-
|
162
|
-
def execute_vcs_diff(vcs_type, branch_option = nil)
|
163
|
-
case vcs_type
|
164
|
-
when :git
|
165
|
-
if branch_option
|
166
|
-
target_branch = branch_option == 'auto' ? detect_git_default_branch : branch_option
|
167
|
-
if target_branch
|
168
|
-
# Validate that the target branch exists
|
169
|
-
unless validate_git_branch_exists(target_branch)
|
170
|
-
puts "Error: Branch '#{target_branch}' does not exist."
|
171
|
-
puts "Available branches:"
|
172
|
-
puts `git branch -a`.lines.map(&:strip).reject(&:empty?)
|
173
|
-
exit 1
|
174
|
-
end
|
175
|
-
|
176
|
-
puts "Comparing current branch against '#{target_branch}'..."
|
177
|
-
`git diff #{target_branch}...HEAD`
|
178
|
-
else
|
179
|
-
puts "Could not detect default branch, falling back to HEAD diff..."
|
180
|
-
`git diff HEAD`
|
181
|
-
end
|
182
|
-
else
|
183
|
-
`git diff HEAD`
|
184
|
-
end
|
185
|
-
when :hg
|
186
|
-
if branch_option
|
187
|
-
target_branch = branch_option == 'auto' ? detect_hg_default_branch : branch_option
|
188
|
-
if target_branch
|
189
|
-
# Validate that the target branch exists
|
190
|
-
unless validate_hg_branch_exists(target_branch)
|
191
|
-
puts "Error: Branch '#{target_branch}' does not exist."
|
192
|
-
puts "Available branches:"
|
193
|
-
puts `hg branches`.lines.map(&:strip).reject(&:empty?)
|
194
|
-
exit 1
|
195
|
-
end
|
196
|
-
|
197
|
-
puts "Comparing current branch against '#{target_branch}'..."
|
198
|
-
`hg diff -r #{target_branch}`
|
199
|
-
else
|
200
|
-
puts "Could not detect default branch, falling back to standard diff..."
|
201
|
-
`hg diff`
|
202
|
-
end
|
203
|
-
else
|
204
|
-
`hg diff`
|
205
|
-
end
|
206
|
-
else
|
207
|
-
"" # Should not happen if get_vcs_type logic is correct and checked before calling
|
208
|
-
end
|
209
|
-
end
|
210
|
-
|
211
|
-
def detect_git_default_branch
|
212
|
-
# Try multiple methods to detect the default branch
|
213
|
-
|
214
|
-
# Method 1: Check origin/HEAD symbolic ref
|
215
|
-
result = `git symbolic-ref refs/remotes/origin/HEAD 2>/dev/null`.strip
|
216
|
-
if $?.success? && !result.empty?
|
217
|
-
return result.split('/').last
|
218
|
-
end
|
219
|
-
|
220
|
-
# Method 2: Check remote show origin
|
221
|
-
result = `git remote show origin 2>/dev/null | grep "HEAD branch"`.strip
|
222
|
-
if $?.success? && !result.empty?
|
223
|
-
match = result.match(/HEAD branch:\s*(\w+)/)
|
224
|
-
return match[1] if match
|
225
|
-
end
|
226
|
-
|
227
|
-
# Method 3: Check if common default branches exist
|
228
|
-
['main', 'master'].each do |branch|
|
229
|
-
result = `git rev-parse --verify origin/#{branch} 2>/dev/null`
|
230
|
-
if $?.success?
|
231
|
-
return branch
|
232
|
-
end
|
233
|
-
end
|
234
|
-
|
235
|
-
# Method 4: Fallback - check local branches
|
236
|
-
['main', 'master'].each do |branch|
|
237
|
-
result = `git rev-parse --verify #{branch} 2>/dev/null`
|
238
|
-
if $?.success?
|
239
|
-
return branch
|
240
|
-
end
|
241
|
-
end
|
242
|
-
|
243
|
-
# If all else fails, return nil
|
244
|
-
nil
|
245
|
-
end
|
246
|
-
|
247
|
-
def detect_hg_default_branch
|
248
|
-
# Method 1: Check current branch (if it's 'default', that's the main branch)
|
249
|
-
result = `hg branch 2>/dev/null`.strip
|
250
|
-
if $?.success? && result == 'default'
|
251
|
-
return 'default'
|
252
|
-
end
|
253
|
-
|
254
|
-
# Method 2: Look for 'default' branch in branch list
|
255
|
-
result = `hg branches 2>/dev/null`
|
256
|
-
if $?.success? && result.include?('default')
|
257
|
-
return 'default'
|
258
|
-
end
|
259
|
-
|
260
|
-
# Method 3: Check if there are any branches at all
|
261
|
-
result = `hg branches 2>/dev/null`.strip
|
262
|
-
if $?.success? && !result.empty?
|
263
|
-
# Get the first branch (usually the main one)
|
264
|
-
first_branch = result.lines.first&.split&.first
|
265
|
-
return first_branch if first_branch
|
266
|
-
end
|
267
|
-
|
268
|
-
# Fallback to 'default' (standard hg main branch name)
|
269
|
-
'default'
|
270
|
-
end
|
271
|
-
|
272
|
-
def validate_git_branch_exists(branch)
|
273
|
-
# Check if branch exists locally
|
274
|
-
_result = `git rev-parse --verify #{branch} 2>/dev/null`
|
275
|
-
return true if $?.success?
|
276
|
-
|
277
|
-
# Check if branch exists on remote
|
278
|
-
_result = `git rev-parse --verify origin/#{branch} 2>/dev/null`
|
279
|
-
return true if $?.success?
|
280
|
-
|
281
|
-
false
|
282
|
-
end
|
283
|
-
|
284
|
-
def validate_hg_branch_exists(branch)
|
285
|
-
# Check if branch exists in hg branches
|
286
|
-
result = `hg branches 2>/dev/null`
|
287
|
-
if $?.success?
|
288
|
-
return result.lines.any? { |line| line.strip.start_with?(branch) }
|
289
|
-
end
|
290
|
-
|
291
|
-
# If we can't list branches, assume it exists (hg is more permissive)
|
292
|
-
true
|
293
|
-
end
|
37
|
+
# All diff-related methods like handle_diff_analysis, get_vcs_type, etc., are removed.
|
294
38
|
|
295
39
|
private
|
296
40
|
|
@@ -450,6 +194,19 @@ REQUIREMENTS_BLOCK
|
|
450
194
|
improvements.map(&:strip).reject(&:empty?)
|
451
195
|
end
|
452
196
|
|
197
|
+
def format_analysis_for_github(analysis_result)
|
198
|
+
return "No analysis result available." if analysis_result.nil? || analysis_result.empty?
|
199
|
+
|
200
|
+
{
|
201
|
+
implementation_summary: analysis_result['ticket_implementation_summary']&.strip,
|
202
|
+
technical_summary: analysis_result['summary']&.strip,
|
203
|
+
issues: format_issues_for_adf(analysis_result['errors']),
|
204
|
+
improvements: format_improvements_for_adf(analysis_result['improvements']),
|
205
|
+
test_coverage: analysis_result['test_coverage']&.strip,
|
206
|
+
requirements_evaluation: analysis_result['requirements_evaluation']&.strip
|
207
|
+
}
|
208
|
+
end
|
209
|
+
|
453
210
|
def extract_json_from_response(response)
|
454
211
|
# First try to parse the response as-is
|
455
212
|
begin
|
@@ -538,49 +295,15 @@ REQUIREMENTS_BLOCK
|
|
538
295
|
context_sections
|
539
296
|
end
|
540
297
|
|
541
|
-
def call_llm_for_diff_analysis(prompt, config)
|
542
|
-
begin
|
543
|
-
llm_service_name = config['llm']
|
544
|
-
llm = case llm_service_name
|
545
|
-
when 'openai'
|
546
|
-
N2M::Llm::OpenAi.new(config)
|
547
|
-
when 'claude'
|
548
|
-
N2M::Llm::Claude.new(config)
|
549
|
-
when 'gemini'
|
550
|
-
N2M::Llm::Gemini.new(config)
|
551
|
-
when 'openrouter'
|
552
|
-
N2M::Llm::OpenRouter.new(config)
|
553
|
-
when 'ollama'
|
554
|
-
N2M::Llm::Ollama.new(config)
|
555
|
-
else
|
556
|
-
# Should not happen if config is validated, but as a safeguard:
|
557
|
-
raise N2B::Error, "Unsupported LLM service: #{llm_service_name}"
|
558
|
-
end
|
559
|
-
|
560
|
-
puts "🔍 AI is analyzing your code diff..."
|
561
|
-
response_json_str = analyze_diff_with_spinner(llm, prompt)
|
562
|
-
response_json_str
|
563
|
-
rescue N2B::LlmApiError => e # This catches errors from analyze_code_diff
|
564
|
-
puts "Error communicating with the LLM: #{e.message}"
|
565
|
-
|
566
|
-
# Check if it might be a model-related error
|
567
|
-
if e.message.include?('model') || e.message.include?('Model') || e.message.include?('invalid') || e.message.include?('not found')
|
568
|
-
puts "\nThis might be due to an invalid or unsupported model configuration."
|
569
|
-
puts "Run 'n2b -c' to reconfigure your model settings."
|
570
|
-
end
|
571
|
-
|
572
|
-
return '{"summary": "Error: Could not analyze diff due to LLM API error.", "errors": [], "improvements": []}'
|
573
|
-
end
|
574
|
-
end
|
575
|
-
|
576
298
|
def append_to_llm_history_file(commands)
|
577
|
-
File.open(
|
299
|
+
File.open(self.class.history_file, 'a') do |file|
|
578
300
|
file.puts(commands)
|
579
301
|
end
|
580
302
|
end
|
581
|
-
|
303
|
+
|
582
304
|
def read_llm_history_file
|
583
|
-
|
305
|
+
history_file_path = self.class.history_file
|
306
|
+
history = File.read(history_file_path) if File.exist?(history_file_path)
|
584
307
|
history ||= ''
|
585
308
|
# limit to 20 most recent commands
|
586
309
|
history.split("\n").last(20).join("\n")
|
@@ -591,19 +314,21 @@ REQUIREMENTS_BLOCK
|
|
591
314
|
llm_service_name = config['llm']
|
592
315
|
llm = case llm_service_name
|
593
316
|
when 'openai'
|
594
|
-
|
317
|
+
N2B::Llm::OpenAi.new(config)
|
595
318
|
when 'claude'
|
596
|
-
|
597
|
-
when 'gemini'
|
598
|
-
|
319
|
+
N2B::Llm::Claude.new(config)
|
320
|
+
when 'gemini' # This is for API Key based Gemini
|
321
|
+
N2B::Llm::Gemini.new(config)
|
322
|
+
when 'vertexai' # This is for Credential File based Vertex AI
|
323
|
+
N2B::Llm::VertexAi.new(config)
|
599
324
|
when 'openrouter'
|
600
|
-
|
325
|
+
N2B::Llm::OpenRouter.new(config)
|
601
326
|
when 'ollama'
|
602
|
-
|
327
|
+
N2B::Llm::Ollama.new(config)
|
603
328
|
else
|
604
329
|
# Fallback or error, though config validation should prevent this
|
605
330
|
puts "Warning: Unsupported LLM service '#{llm_service_name}' configured. Falling back to Claude."
|
606
|
-
|
331
|
+
N2B::Llm::Claude.new(config)
|
607
332
|
end
|
608
333
|
|
609
334
|
# This content is specific to bash command generation
|
@@ -782,31 +507,6 @@ REQUIREMENTS_BLOCK
|
|
782
507
|
end
|
783
508
|
end
|
784
509
|
|
785
|
-
def analyze_diff_with_spinner(llm, prompt)
|
786
|
-
spinner_chars = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏']
|
787
|
-
spinner_thread = Thread.new do
|
788
|
-
i = 0
|
789
|
-
while true
|
790
|
-
print "\r🔍 #{spinner_chars[i % spinner_chars.length]} Analyzing diff..."
|
791
|
-
$stdout.flush
|
792
|
-
sleep(0.1)
|
793
|
-
i += 1
|
794
|
-
end
|
795
|
-
end
|
796
|
-
|
797
|
-
begin
|
798
|
-
result = llm.analyze_code_diff(prompt)
|
799
|
-
spinner_thread.kill
|
800
|
-
print "\r#{' ' * 30}\r" # Clear the spinner line
|
801
|
-
puts "✅ Diff analysis complete!"
|
802
|
-
result
|
803
|
-
rescue => e
|
804
|
-
spinner_thread.kill
|
805
|
-
print "\r#{' ' * 30}\r" # Clear the spinner line
|
806
|
-
raise e
|
807
|
-
end
|
808
|
-
end
|
809
|
-
|
810
510
|
def attempt_json_repair_for_commands(malformed_response, llm)
|
811
511
|
repair_prompt = <<~PROMPT
|
812
512
|
The following response was supposed to be valid JSON with keys "commands" (array) and "explanation" (string), but it has formatting issues. Please fix it and return ONLY the corrected JSON:
|
@@ -850,63 +550,41 @@ REQUIREMENTS_BLOCK
|
|
850
550
|
options = {
|
851
551
|
execute: false,
|
852
552
|
config: nil,
|
853
|
-
diff: false,
|
854
|
-
requirements: nil,
|
855
|
-
branch: nil,
|
856
|
-
jira_ticket: nil,
|
857
|
-
jira_update: nil, #
|
858
|
-
advanced_config: false
|
553
|
+
# diff: false, # Removed
|
554
|
+
# requirements: nil, # Removed
|
555
|
+
# branch: nil, # Removed
|
556
|
+
# jira_ticket: nil, # Removed
|
557
|
+
# jira_update: nil, # Removed
|
558
|
+
advanced_config: false
|
859
559
|
}
|
860
560
|
|
861
561
|
parser = OptionParser.new do |opts|
|
862
562
|
opts.banner = "Usage: n2b [options] [natural language command]"
|
863
563
|
|
864
|
-
opts.on('-x', '--execute', 'Execute the commands after confirmation') do
|
564
|
+
opts.on('-x', '--execute', 'Execute the translated commands after confirmation.') do
|
865
565
|
options[:execute] = true
|
866
566
|
end
|
867
567
|
|
868
|
-
|
869
|
-
options[:diff] = true
|
870
|
-
end
|
871
|
-
|
872
|
-
opts.on('-b', '--branch [BRANCH]', 'Compare against branch (default: auto-detect main/master)') do |branch|
|
873
|
-
options[:branch] = branch || 'auto'
|
874
|
-
end
|
875
|
-
|
876
|
-
opts.on('-r', '--requirements FILE', 'Requirements file for diff analysis') do |file|
|
877
|
-
options[:requirements] = file
|
878
|
-
end
|
568
|
+
# Removed options: -d, --diff, -b, --branch, -r, --requirements, -j, --jira, --jira-update, --jira-no-update
|
879
569
|
|
880
|
-
opts.on('-
|
881
|
-
options[:
|
882
|
-
end
|
883
|
-
|
884
|
-
opts.on('--jira-update', 'Update the linked Jira ticket (requires -j)') do
|
885
|
-
options[:jira_update] = true
|
570
|
+
opts.on('-c', '--config', 'Configure N2B (API key, model, privacy settings, etc.).') do
|
571
|
+
options[:config] = true
|
886
572
|
end
|
887
573
|
|
888
|
-
opts.on('--
|
889
|
-
options[:
|
574
|
+
opts.on('--advanced-config', 'Access advanced configuration options.') do
|
575
|
+
options[:advanced_config] = true
|
576
|
+
options[:config] = true # --advanced-config implies -c
|
890
577
|
end
|
891
578
|
|
892
|
-
opts.
|
579
|
+
opts.on_tail('-h', '--help', 'Show this help message.') do
|
893
580
|
puts opts
|
894
581
|
exit
|
895
582
|
end
|
896
583
|
|
897
|
-
opts.
|
898
|
-
puts "n2b version #{N2B::VERSION}"
|
584
|
+
opts.on_tail('-v', '--version', 'Show version.') do
|
585
|
+
puts "n2b version #{N2B::VERSION}" # Assuming N2B::VERSION is defined
|
899
586
|
exit
|
900
587
|
end
|
901
|
-
|
902
|
-
opts.on('-c', '--config', 'Configure the API key and model') do
|
903
|
-
options[:config] = true
|
904
|
-
end
|
905
|
-
|
906
|
-
opts.on('--advanced-config', 'Access advanced configuration options including Jira and privacy settings') do
|
907
|
-
options[:advanced_config] = true
|
908
|
-
options[:config] = true # Forcing config mode if advanced is chosen
|
909
|
-
end
|
910
588
|
end
|
911
589
|
|
912
590
|
begin
|
@@ -916,36 +594,14 @@ REQUIREMENTS_BLOCK
|
|
916
594
|
puts ""
|
917
595
|
puts parser.help
|
918
596
|
exit 1
|
919
|
-
|
920
|
-
|
921
|
-
# Validate option combinations
|
922
|
-
if options[:branch] && !options[:diff]
|
923
|
-
puts "Error: --branch option can only be used with --diff"
|
924
|
-
puts ""
|
925
|
-
puts parser.help
|
926
|
-
exit 1
|
927
|
-
end
|
928
|
-
|
929
|
-
if options[:jira_update] == true && options[:jira_ticket].nil?
|
930
|
-
puts "Error: --jira-update option requires a Jira ticket to be specified with -j or --jira."
|
931
|
-
puts ""
|
932
|
-
puts parser.help
|
933
|
-
exit 1
|
934
|
-
end
|
935
|
-
|
936
|
-
if options[:jira_update] == false && options[:jira_ticket].nil?
|
937
|
-
puts "Error: --jira-no-update option requires a Jira ticket to be specified with -j or --jira."
|
597
|
+
rescue OptionParser::MissingArgument => e
|
598
|
+
puts "Error: #{e.message}"
|
938
599
|
puts ""
|
939
600
|
puts parser.help
|
940
601
|
exit 1
|
941
602
|
end
|
942
603
|
|
943
|
-
|
944
|
-
puts "Error: --jira-update and --jira-no-update are mutually exclusive."
|
945
|
-
puts ""
|
946
|
-
puts parser.help
|
947
|
-
exit 1
|
948
|
-
end
|
604
|
+
# Removed validation logic for diff/jira related options
|
949
605
|
|
950
606
|
options
|
951
607
|
end
|
data/lib/n2b/config/models.yml
CHANGED
@@ -25,9 +25,25 @@ openai:
|
|
25
25
|
|
26
26
|
gemini:
|
27
27
|
suggested:
|
28
|
-
gemini-2.
|
29
|
-
gemini-2.
|
30
|
-
|
28
|
+
gemini-2.0-flash: "gemini-2.0-flash-001"
|
29
|
+
gemini-2.0-flash-lite: "gemini-2.0-flash-lite-001"
|
30
|
+
gemini-2.5-flash: "gemini-2.5-flash-preview"
|
31
|
+
gemini-2.5-pro: "gemini-2.5-pro-preview"
|
32
|
+
# Legacy models (may not be available in all regions)
|
33
|
+
gemini-2.5-flash-05-20: "gemini-2.5-flash-preview-05-20"
|
34
|
+
gemini-2.5-pro-05-06: "gemini-2.5-pro-preview-05-06"
|
35
|
+
default: "gemini-2.0-flash"
|
36
|
+
|
37
|
+
vertexai:
|
38
|
+
suggested:
|
39
|
+
gemini-2.0-flash: "gemini-2.0-flash-001"
|
40
|
+
gemini-2.0-flash-lite: "gemini-2.0-flash-lite-001"
|
41
|
+
gemini-2.5-flash: "gemini-2.5-flash-preview"
|
42
|
+
gemini-2.5-pro: "gemini-2.5-pro-preview"
|
43
|
+
# Legacy models (may not be available in all regions)
|
44
|
+
gemini-2.5-flash-05-20: "gemini-2.5-flash-preview-05-20"
|
45
|
+
gemini-2.5-pro-05-06: "gemini-2.5-pro-preview-05-06"
|
46
|
+
default: "gemini-2.0-flash"
|
31
47
|
|
32
48
|
openrouter:
|
33
49
|
suggested:
|