aia 0.3.20 → 0.4.2
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/.semver +2 -2
- data/CHANGELOG.md +8 -0
- data/README.md +184 -93
- data/lib/aia/cli.rb +11 -10
- data/lib/aia/directives.rb +66 -0
- data/lib/aia/main.rb +94 -13
- data/lib/aia/prompt.rb +279 -0
- data/lib/aia/tools/backend_common.rb +76 -0
- data/lib/aia/tools/mods.rb +47 -118
- data/lib/aia/tools/sgpt.rb +19 -3
- data/lib/aia/tools.rb +2 -0
- data/lib/aia.rb +1 -1
- data/man/aia.1 +54 -42
- data/man/aia.1.md +62 -38
- metadata +19 -5
- data/lib/aia/prompt_processing.rb +0 -416
- data/lib/aia/tools/temp.md +0 -97
- data/lib/modularization_plan.md +0 -126
@@ -1,416 +0,0 @@
|
|
1
|
-
# lib/aia/prompt_processing.rb
|
2
|
-
|
3
|
-
module AIA::PromptProcessing
|
4
|
-
KW_HISTORY_MAX = 5
|
5
|
-
|
6
|
-
# Fetch the first argument which should be the prompt id
|
7
|
-
def get_prompt
|
8
|
-
prompt_id = AIA.config.arguments.shift
|
9
|
-
|
10
|
-
# TODO: or maybe go to a generic search and select process
|
11
|
-
|
12
|
-
# TODO: if external options were provided but no prompt id
|
13
|
-
# then by pass prompt process and send the extra
|
14
|
-
# options to the backend. For example:
|
15
|
-
# aia -- --settings
|
16
|
-
# should send --settings to mods and nothing else
|
17
|
-
|
18
|
-
abort("Please provide a prompt id") unless prompt_id
|
19
|
-
|
20
|
-
search_for_a_matching_prompt(prompt_id) unless existing_prompt?(prompt_id)
|
21
|
-
edit_prompt if AIA.config.edit?
|
22
|
-
end
|
23
|
-
|
24
|
-
|
25
|
-
# Check if a prompt with the given id already exists
|
26
|
-
def existing_prompt?(prompt_id)
|
27
|
-
@prompt = PromptManager::Prompt.get(id: prompt_id)
|
28
|
-
|
29
|
-
# FIXME: Kludge until prompt_manager is changed
|
30
|
-
# prompt_manager v0.3.0 now supports this feature.
|
31
|
-
# keeping the kludge in for legacy JSON files
|
32
|
-
# files which have not yet been reformatted.
|
33
|
-
@prompt.keywords.each do |kw|
|
34
|
-
if @prompt.parameters[kw].nil? || @prompt.parameters[kw].empty?
|
35
|
-
@prompt.parameters[kw] = []
|
36
|
-
else
|
37
|
-
@prompt.parameters[kw] = Array(@prompt.parameters[kw])
|
38
|
-
end
|
39
|
-
end
|
40
|
-
|
41
|
-
true
|
42
|
-
rescue ArgumentError
|
43
|
-
false
|
44
|
-
end
|
45
|
-
|
46
|
-
|
47
|
-
# Process the prompt's associated keywords and parameters
|
48
|
-
def process_prompt
|
49
|
-
unless @prompt.keywords.empty?
|
50
|
-
replace_keywords
|
51
|
-
@prompt.build
|
52
|
-
@prompt.save
|
53
|
-
end
|
54
|
-
end
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
def replace_keywords
|
59
|
-
puts
|
60
|
-
puts "ID: #{@prompt.id}"
|
61
|
-
|
62
|
-
show_prompt_without_comments
|
63
|
-
|
64
|
-
puts "\nPress up/down arrow to scroll through history."
|
65
|
-
puts "Type new input or edit the current input."
|
66
|
-
puts "Quit #{MY_NAME} with a CNTL-D or a CNTL-C"
|
67
|
-
puts
|
68
|
-
@prompt.keywords.each do |kw|
|
69
|
-
value = keyword_value(kw, @prompt.parameters[kw])
|
70
|
-
|
71
|
-
unless value.nil? || value.strip.empty?
|
72
|
-
value_inx = @prompt.parameters[kw].index(value)
|
73
|
-
|
74
|
-
if value_inx
|
75
|
-
@prompt.parameters[kw].delete_at(value_inx)
|
76
|
-
end
|
77
|
-
|
78
|
-
# The most recent value for this kw will always be
|
79
|
-
# in the last position
|
80
|
-
@prompt.parameters[kw] << value
|
81
|
-
@prompt.parameters[kw].shift if @prompt.parameters[kw].size > KW_HISTORY_MAX
|
82
|
-
end
|
83
|
-
end
|
84
|
-
end
|
85
|
-
|
86
|
-
|
87
|
-
# query the user for a value to the keyword allow the
|
88
|
-
# reuse of the previous value shown as the default
|
89
|
-
def keyword_value(kw, history_array)
|
90
|
-
|
91
|
-
Readline::HISTORY.clear
|
92
|
-
Array(history_array).each { |entry| Readline::HISTORY.push(entry) unless entry.nil? || entry.empty? }
|
93
|
-
|
94
|
-
puts "Parameter #{kw} ..."
|
95
|
-
|
96
|
-
begin
|
97
|
-
a_string = Readline.readline("\n-=> ", true)
|
98
|
-
rescue Interrupt
|
99
|
-
a_string = nil
|
100
|
-
end
|
101
|
-
|
102
|
-
if a_string.nil?
|
103
|
-
puts "okay. Come back soon."
|
104
|
-
exit
|
105
|
-
end
|
106
|
-
|
107
|
-
puts
|
108
|
-
a_string.empty? ? default : a_string
|
109
|
-
end
|
110
|
-
|
111
|
-
|
112
|
-
# Search for a prompt with a matching id or keyword
|
113
|
-
def search_for_a_matching_prompt(prompt_id)
|
114
|
-
# TODO: using the rgfzf version of the search_proc should only
|
115
|
-
# return a single prompt_id
|
116
|
-
found_prompts = PromptManager::Prompt.search(prompt_id)
|
117
|
-
|
118
|
-
if found_prompts.empty?
|
119
|
-
if edit?
|
120
|
-
create_prompt(prompt_id)
|
121
|
-
edit_prompt
|
122
|
-
else
|
123
|
-
abort <<~EOS
|
124
|
-
|
125
|
-
No prompts where found for: #{prompt_id}
|
126
|
-
To create a prompt with this ID use the --edit option
|
127
|
-
like this:
|
128
|
-
#{MY_NAME} #{prompt_id} --edit
|
129
|
-
|
130
|
-
EOS
|
131
|
-
end
|
132
|
-
else
|
133
|
-
prompt_id = 1 == found_prompts.size ? found_prompts.first : handle_multiple_prompts(found_prompts, prompt_id)
|
134
|
-
@prompt = PromptManager::Prompt.get(id: prompt_id)
|
135
|
-
end
|
136
|
-
end
|
137
|
-
|
138
|
-
|
139
|
-
def handle_multiple_prompts(found_these, while_looking_for_this)
|
140
|
-
raise ArgumentError, "Argument is not an Array" unless found_these.is_a?(Array)
|
141
|
-
|
142
|
-
# TODO: Make this a class constant for defaults; make the header content
|
143
|
-
# a parameter so it can be varied.
|
144
|
-
fzf_options = [
|
145
|
-
"--tabstop=2", # 2 soaces for a tab
|
146
|
-
"--header='Prompt IDs which contain: #{while_looking_for_this}\nPress ESC to cancel.'",
|
147
|
-
"--header-first",
|
148
|
-
"--prompt='Search term: '",
|
149
|
-
'--delimiter :',
|
150
|
-
"--preview 'cat $PROMPTS_DIR/{1}.txt'",
|
151
|
-
"--preview-window=down:50%:wrap"
|
152
|
-
].join(' ')
|
153
|
-
|
154
|
-
|
155
|
-
# Create a temporary file to hold the list of strings
|
156
|
-
temp_file = Tempfile.new('fzf-input')
|
157
|
-
|
158
|
-
begin
|
159
|
-
# Write all strings to the temp file
|
160
|
-
temp_file.puts(found_these)
|
161
|
-
temp_file.close
|
162
|
-
|
163
|
-
# Execute fzf command-line utility to allow selection
|
164
|
-
selected = `cat #{temp_file.path} | fzf #{fzf_options}`.strip
|
165
|
-
|
166
|
-
# Check if fzf actually returned a string; if not, return nil
|
167
|
-
result = selected.empty? ? nil : selected
|
168
|
-
ensure
|
169
|
-
# Ensure that the tempfile is closed and unlinked
|
170
|
-
temp_file.unlink
|
171
|
-
end
|
172
|
-
|
173
|
-
exit unless result
|
174
|
-
|
175
|
-
result
|
176
|
-
end
|
177
|
-
|
178
|
-
|
179
|
-
def create_prompt(prompt_id)
|
180
|
-
@prompt = PromptManager::Prompt.create(id: prompt_id)
|
181
|
-
# TODO: consider a configurable prompt template
|
182
|
-
# ERB ???
|
183
|
-
end
|
184
|
-
|
185
|
-
|
186
|
-
def edit_prompt
|
187
|
-
# FIXME: replace with the editor from the configuration
|
188
|
-
|
189
|
-
@editor = AIA::Subl.new(
|
190
|
-
file: @prompt.path
|
191
|
-
)
|
192
|
-
|
193
|
-
@editor.run # blocks until file is closed
|
194
|
-
|
195
|
-
@options[:edit?][0] = false # turn off the --edit switch
|
196
|
-
|
197
|
-
# reload the edited prompt
|
198
|
-
@prompt = PromptManager::Prompt.get(id: @prompt.id)
|
199
|
-
end
|
200
|
-
|
201
|
-
|
202
|
-
def show_prompt_without_comments
|
203
|
-
puts remove_comments.wrap(indent: 4)
|
204
|
-
end
|
205
|
-
|
206
|
-
|
207
|
-
def remove_comments
|
208
|
-
lines = @prompt.text
|
209
|
-
.split("\n")
|
210
|
-
.reject{|a_line| a_line.strip.start_with?('#')}
|
211
|
-
|
212
|
-
# Remove empty lines at the start of the prompt
|
213
|
-
#
|
214
|
-
lines = lines.drop_while(&:empty?)
|
215
|
-
|
216
|
-
# Drop all the lines at __END__ and after
|
217
|
-
#
|
218
|
-
logical_end_inx = lines.index("__END__")
|
219
|
-
|
220
|
-
if logical_end_inx
|
221
|
-
lines[0...logical_end_inx] # NOTE: ... means to not include last index
|
222
|
-
else
|
223
|
-
lines
|
224
|
-
end.join("\n")
|
225
|
-
end
|
226
|
-
end
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
__END__
|
231
|
-
|
232
|
-
# lib/aia/prompt_processing.rb
|
233
|
-
|
234
|
-
class AIA::PromptProcessing
|
235
|
-
KW_HISTORY_MAX = 5
|
236
|
-
|
237
|
-
def initialize(arguments, options)
|
238
|
-
@arguments = arguments
|
239
|
-
@options = options
|
240
|
-
@prompt = nil
|
241
|
-
end
|
242
|
-
|
243
|
-
def execute
|
244
|
-
get_prompt
|
245
|
-
process_prompt
|
246
|
-
end
|
247
|
-
|
248
|
-
private
|
249
|
-
|
250
|
-
def get_prompt
|
251
|
-
prompt_id = @arguments.shift
|
252
|
-
abort("Please provide a prompt id") unless prompt_id
|
253
|
-
search_for_a_matching_prompt(prompt_id) unless existing_prompt?(prompt_id)
|
254
|
-
edit_prompt if edit?
|
255
|
-
end
|
256
|
-
|
257
|
-
def existing_prompt?(prompt_id)
|
258
|
-
@prompt = PromptManager::Prompt.get(id: prompt_id)
|
259
|
-
@prompt.keywords.each do |kw|
|
260
|
-
@prompt.parameters[kw] = Array(@prompt.parameters[kw])
|
261
|
-
end
|
262
|
-
true
|
263
|
-
rescue ArgumentError
|
264
|
-
false
|
265
|
-
end
|
266
|
-
|
267
|
-
def process_prompt
|
268
|
-
return if @prompt.keywords.empty?
|
269
|
-
replace_keywords
|
270
|
-
@prompt.build
|
271
|
-
@prompt.save
|
272
|
-
end
|
273
|
-
|
274
|
-
def replace_keywords
|
275
|
-
puts "ID: #{@prompt.id}"
|
276
|
-
show_prompt_without_comments
|
277
|
-
puts_instructions
|
278
|
-
@prompt.keywords.each do |kw|
|
279
|
-
value = keyword_value(kw, @prompt.parameters[kw])
|
280
|
-
update_keyword_history(kw, value) unless value.nil? || value.strip.empty?
|
281
|
-
end
|
282
|
-
end
|
283
|
-
|
284
|
-
def puts_instructions
|
285
|
-
puts "\nPress up/down arrow to scroll through history."
|
286
|
-
puts "Type new input or edit the current input."
|
287
|
-
puts "Quit #{MY_NAME} with a CNTL-D or a CNTL-C"
|
288
|
-
puts
|
289
|
-
end
|
290
|
-
|
291
|
-
def update_keyword_history(kw, value)
|
292
|
-
params = @prompt.parameters[kw]
|
293
|
-
params.delete(value)
|
294
|
-
params << value
|
295
|
-
params.shift if params.size > KW_HISTORY_MAX
|
296
|
-
end
|
297
|
-
|
298
|
-
def keyword_value(kw, history_array)
|
299
|
-
Readline::HISTORY.clear
|
300
|
-
Array(history_array).each { |entry| Readline::HISTORY.push(entry) unless entry.nil? || entry.empty? }
|
301
|
-
puts "Parameter #{kw} ..."
|
302
|
-
begin
|
303
|
-
a_string = Readline.readline("\n-=> ", true)
|
304
|
-
rescue Interrupt
|
305
|
-
a_string = nil
|
306
|
-
end
|
307
|
-
abort("okay. Come back soon.") if a_string.nil?
|
308
|
-
a_string.empty? ? history_array.first : a_string
|
309
|
-
end
|
310
|
-
|
311
|
-
def search_for_a_matching_prompt(prompt_id)
|
312
|
-
found_prompts = PromptManager::Prompt.search(prompt_id)
|
313
|
-
handle_no_prompts_found(prompt_id) if found_prompts.empty?
|
314
|
-
prompt_id = found_prompts.size == 1 ? found_prompts.first : handle_multiple_prompts(found_prompts, prompt_id)
|
315
|
-
@prompt = PromptManager::Prompt.get(id: prompt_id)
|
316
|
-
end
|
317
|
-
|
318
|
-
def handle_no_prompts_found(prompt_id)
|
319
|
-
if edit?
|
320
|
-
create_prompt(prompt_id)
|
321
|
-
edit_prompt
|
322
|
-
else
|
323
|
-
abort_no_prompts_error(prompt_id)
|
324
|
-
end
|
325
|
-
end
|
326
|
-
|
327
|
-
def abort_no_prompts_error(prompt_id)
|
328
|
-
abort <<~EOS
|
329
|
-
|
330
|
-
No prompts were found for: #{prompt_id}
|
331
|
-
To create a prompt with this ID use the --edit option
|
332
|
-
like this:
|
333
|
-
#{MY_NAME} #{prompt_id} --edit
|
334
|
-
|
335
|
-
EOS
|
336
|
-
end
|
337
|
-
|
338
|
-
def handle_multiple_prompts(found_these, while_looking_for_this)
|
339
|
-
raise ArgumentError, "Argument is not an Array" unless found_these.is_a?(Array)
|
340
|
-
result = execute_fzf(found_these, while_looking_for_this)
|
341
|
-
abort unless result
|
342
|
-
result
|
343
|
-
end
|
344
|
-
|
345
|
-
def execute_fzf(found_these, while_looking_for_this)
|
346
|
-
fzf_options = build_fzf_options(while_looking_for_this)
|
347
|
-
temp_file = create_tempfile_with_entries(found_these)
|
348
|
-
selected = `cat #{temp_file.path} | fzf #{fzf_options}`.strip
|
349
|
-
temp_file.unlink
|
350
|
-
selected.empty? ? nil : selected
|
351
|
-
end
|
352
|
-
|
353
|
-
def build_fzf_options(search_term)
|
354
|
-
[
|
355
|
-
"--tabstop=2",
|
356
|
-
"--header='Prompt IDs which contain: #{search_term}\nPress ESC to cancel.'",
|
357
|
-
"--header-first",
|
358
|
-
"--prompt='Search term: '",
|
359
|
-
'--delimiter :',
|
360
|
-
"--preview 'cat $PROMPTS_DIR/{1}.txt'",
|
361
|
-
"--preview-window=down:50%:wrap"
|
362
|
-
].join(' ')
|
363
|
-
end
|
364
|
-
|
365
|
-
def create_tempfile_with_entries(entries)
|
366
|
-
temp_file = Tempfile.new('fzf-input')
|
367
|
-
temp_file.puts(entries)
|
368
|
-
temp_file.close
|
369
|
-
temp_file
|
370
|
-
end
|
371
|
-
|
372
|
-
def create_prompt(prompt_id)
|
373
|
-
@prompt = PromptManager::Prompt.create(id: prompt_id)
|
374
|
-
# Additional prompt config...
|
375
|
-
end
|
376
|
-
|
377
|
-
|
378
|
-
|
379
|
-
def edit_prompt
|
380
|
-
# FIXME: replace with the editor from the configuration
|
381
|
-
|
382
|
-
@editor = AIA::Subl.new(
|
383
|
-
file: @prompt.path
|
384
|
-
)
|
385
|
-
|
386
|
-
@editor.run # blocks until file is closed
|
387
|
-
|
388
|
-
@options[:edit?][0] = false # turn off the --edit switch
|
389
|
-
|
390
|
-
# reload the edited prompt
|
391
|
-
@prompt = PromptManager::Prompt.get(id: @prompt.id)
|
392
|
-
end
|
393
|
-
|
394
|
-
|
395
|
-
def show_prompt_without_comments
|
396
|
-
puts remove_comments.wrap(indent: 4)
|
397
|
-
end
|
398
|
-
|
399
|
-
def remove_comments
|
400
|
-
lines = @prompt.text.lines
|
401
|
-
.reject { |a_line| a_line.strip.start_with?('#') }
|
402
|
-
.drop_while(&:empty?)
|
403
|
-
logical_end_inx = lines.index("__END__")
|
404
|
-
lines = lines[0...logical_end_inx] if logical_end_inx
|
405
|
-
lines.join("\n")
|
406
|
-
end
|
407
|
-
|
408
|
-
def edit?
|
409
|
-
@options[:edit?] && @options[:edit?][0] == true
|
410
|
-
end
|
411
|
-
end
|
412
|
-
|
413
|
-
|
414
|
-
|
415
|
-
|
416
|
-
|
data/lib/aia/tools/temp.md
DELETED
@@ -1,97 +0,0 @@
|
|
1
|
-
# lib/aia/tools.rb
|
2
|
-
|
3
|
-
```ruby
|
4
|
-
require 'hashie'
|
5
|
-
|
6
|
-
module AIA
|
7
|
-
class Tools
|
8
|
-
@subclasses = {}
|
9
|
-
|
10
|
-
class << self
|
11
|
-
attr_reader :subclasses, :metadata
|
12
|
-
|
13
|
-
def inherited(subclass)
|
14
|
-
@subclasses[subclass.name.split('::').last.downcase] = subclass
|
15
|
-
subclass.instance_variable_set(:@metadata, Jashie::Mash.new)
|
16
|
-
end
|
17
|
-
|
18
|
-
def meta
|
19
|
-
@metadata ||= Jashie::Mash.new
|
20
|
-
end
|
21
|
-
|
22
|
-
def define_metadata(&block)
|
23
|
-
meta.instance_eval(&block)
|
24
|
-
end
|
25
|
-
|
26
|
-
def search_for(name: nil, role: nil)
|
27
|
-
return subclasses[name.downcase] if name
|
28
|
-
return subclasses.values.select { |subclass| subclass.meta.role == role } if role
|
29
|
-
end
|
30
|
-
end
|
31
|
-
|
32
|
-
def self.method_missing(name, *args, &block)
|
33
|
-
@metadata.public_send(name, *args, &block)
|
34
|
-
end
|
35
|
-
|
36
|
-
def self.respond_to_missing?(method_name, include_private = false)
|
37
|
-
@metadata.respond_to?(method_name) || super
|
38
|
-
end
|
39
|
-
end
|
40
|
-
end
|
41
|
-
```
|
42
|
-
|
43
|
-
# lib/aia/tools/mods.rb
|
44
|
-
|
45
|
-
```ruby
|
46
|
-
require_relative 'tools'
|
47
|
-
|
48
|
-
module AIA
|
49
|
-
class Mods < Tools
|
50
|
-
DEFAULT_PARAMETERS = "--no-limit".freeze
|
51
|
-
|
52
|
-
attr_accessor :command, :extra_options, :text, :files
|
53
|
-
|
54
|
-
define_metadata do
|
55
|
-
role :backend
|
56
|
-
desc 'AI on the command-line'
|
57
|
-
url 'https://github.com/charmbracelet/mods'
|
58
|
-
end
|
59
|
-
|
60
|
-
def initialize(extra_options: "", text: "", files: [])
|
61
|
-
@extra_options = extra_options
|
62
|
-
@text = text
|
63
|
-
@files = files
|
64
|
-
build_command
|
65
|
-
end
|
66
|
-
|
67
|
-
def build_command
|
68
|
-
parameters = DEFAULT_PARAMETERS.dup + " "
|
69
|
-
parameters += "-f " if ::AIA.config.markdown?
|
70
|
-
parameters += "-m #{AIA.config.model} " if ::AIA.config.model
|
71
|
-
parameters += @extra_options
|
72
|
-
@command = "mods #{parameters}"
|
73
|
-
@command += %Q["#{@text}"]
|
74
|
-
|
75
|
-
@files.each { |f| @command += " < #{f}" }
|
76
|
-
|
77
|
-
@command
|
78
|
-
end
|
79
|
-
|
80
|
-
def run
|
81
|
-
`#{@command}`
|
82
|
-
end
|
83
|
-
end
|
84
|
-
end
|
85
|
-
```
|
86
|
-
|
87
|
-
```ruby
|
88
|
-
# Example usage:
|
89
|
-
# mods_class = AIA::Tools.search_for(name: 'mods')
|
90
|
-
# mods_instance = mods_class.new(text: "Hello, mods!")
|
91
|
-
# result = mods_instance.run
|
92
|
-
|
93
|
-
# backend_tools = AIA::Tools.search_for(role: :backend)
|
94
|
-
```
|
95
|
-
|
96
|
-
Note: The `Jashie::Mash` class is assumed to behave like `Hashie::Mash` (or similar) in providing a flexible object for storing metadata. You'll need to define `Jashie::Mash` or import a library that provides a similar functionality to match this example.
|
97
|
-
|
data/lib/modularization_plan.md
DELETED
@@ -1,126 +0,0 @@
|
|
1
|
-
## Suggested Refactoring into Modules
|
2
|
-
|
3
|
-
### ConfigurationModule
|
4
|
-
|
5
|
-
This module could encapsulate all the constants and environment-dependent settings.
|
6
|
-
|
7
|
-
```ruby
|
8
|
-
module Configuration
|
9
|
-
HOME = Pathname.new(ENV['HOME'])
|
10
|
-
PROMPTS_DIR = Pathname.new(ENV['PROMPTS_DIR'] || (HOME + ".prompts_dir"))
|
11
|
-
AI_CLI_PROGRAM = "mods"
|
12
|
-
EDITOR = ENV['EDITOR'] || 'edit'
|
13
|
-
MY_NAME = Pathname.new(__FILE__).basename.to_s.split('.')[0]
|
14
|
-
MODS_MODEL = ENV['MODS_MODEL'] || 'gpt-4-1106-preview'
|
15
|
-
OUTPUT = Pathname.pwd + "temp.md"
|
16
|
-
PROMPT_LOG = PROMPTS_DIR + "_prompts.log"
|
17
|
-
USAGE = <<~EOUSAGE
|
18
|
-
AI Assistant (aia)
|
19
|
-
==================
|
20
|
-
The AI cli program being used is: #{AI_CLI_PROGRAM}
|
21
|
-
You can pass additional CLI options to #{AI_CLI_PROGRAM} like this:
|
22
|
-
"#{MY_NAME} my options -- options for #{AI_CLI_PROGRAM}"
|
23
|
-
EOUSAGE
|
24
|
-
end
|
25
|
-
```
|
26
|
-
|
27
|
-
### OptionParsingModule
|
28
|
-
|
29
|
-
This module could manage the parsing of command-line arguments and configuring the options for the application.
|
30
|
-
|
31
|
-
```ruby
|
32
|
-
module OptionParsing
|
33
|
-
def build_reader_methods
|
34
|
-
# ... method definition ...
|
35
|
-
end
|
36
|
-
|
37
|
-
def process_arguments
|
38
|
-
# ... method definition ...
|
39
|
-
end
|
40
|
-
|
41
|
-
def check_for(an_option)
|
42
|
-
# ... method definition ...
|
43
|
-
end
|
44
|
-
|
45
|
-
def process_option(option_sym, switches)
|
46
|
-
# ... method definition ...
|
47
|
-
end
|
48
|
-
end
|
49
|
-
```
|
50
|
-
|
51
|
-
### CommandLineInterfaceModule
|
52
|
-
|
53
|
-
This module would manage interactions with the command-line interface including editing of prompts and selection processes.
|
54
|
-
|
55
|
-
```ruby
|
56
|
-
module CommandLineInterface
|
57
|
-
def keyword_value(kw, default)
|
58
|
-
# ... method definition ...
|
59
|
-
end
|
60
|
-
|
61
|
-
def handle_multiple_prompts(found_these, while_looking_for_this)
|
62
|
-
# ... method definition ...
|
63
|
-
end
|
64
|
-
end
|
65
|
-
```
|
66
|
-
|
67
|
-
### LoggingModule
|
68
|
-
|
69
|
-
Responsible for logging the results of the command.
|
70
|
-
|
71
|
-
```ruby
|
72
|
-
module Logging
|
73
|
-
def write_to_log(answer)
|
74
|
-
# ... method definition ...
|
75
|
-
end
|
76
|
-
end
|
77
|
-
```
|
78
|
-
|
79
|
-
### AICommandModule
|
80
|
-
|
81
|
-
Manages the building and execution of the AI CLI command.
|
82
|
-
|
83
|
-
```ruby
|
84
|
-
module AICommand
|
85
|
-
def setup_cli_program
|
86
|
-
# ... method definition ...
|
87
|
-
end
|
88
|
-
|
89
|
-
def build_command
|
90
|
-
# ... method definition ...
|
91
|
-
end
|
92
|
-
|
93
|
-
def execute_and_log_command(command)
|
94
|
-
# ... method definition ...
|
95
|
-
end
|
96
|
-
end
|
97
|
-
```
|
98
|
-
|
99
|
-
### PromptProcessingModule
|
100
|
-
|
101
|
-
Handles prompt retrieval, existing check, and keyword processing.
|
102
|
-
|
103
|
-
```ruby
|
104
|
-
module PromptProcessing
|
105
|
-
def existing_prompt?(prompt_id)
|
106
|
-
# ... method definition ...
|
107
|
-
end
|
108
|
-
|
109
|
-
def process_prompt
|
110
|
-
# ... method definition ...
|
111
|
-
end
|
112
|
-
|
113
|
-
def replace_keywords
|
114
|
-
# ... method definition ...
|
115
|
-
end
|
116
|
-
|
117
|
-
def search_for_a_matching_prompt(prompt_id)
|
118
|
-
# ... method definition ...
|
119
|
-
end
|
120
|
-
end
|
121
|
-
```
|
122
|
-
|
123
|
-
Each module should only contain the methods relevant to that module's purpose. After defining these modules, they can be included in the `AIA::Main` class where appropriate. Note that the method `get_prompt_id` didn't fit neatly into one of the outlined modules; it may remain in the main class or be included in a module if additional context becomes available or if it can be logically grouped with similar methods.
|
124
|
-
|
125
|
-
The `__END__` block and the Readline history management could be encapsulated into a separate module for terminal interactions if that block grows in complexity or moves out of the overall class definition.
|
126
|
-
|