aia 0.0.4 → 0.3.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/CHANGELOG.md +4 -0
- data/README.md +115 -27
- data/bin/aia +2 -4
- data/lib/aia/cli.rb +179 -0
- data/lib/aia/configuration.rb +44 -0
- data/lib/aia/external/,keep +0 -0
- data/lib/aia/external/.irbrc +11 -0
- data/lib/aia/external/ag.rb +103 -0
- data/lib/aia/external/bat.rb +189 -0
- data/lib/aia/external/fzf.rb +147 -0
- data/lib/aia/external/glow.rb +37 -0
- data/lib/aia/external/mods.rb +57 -0
- data/lib/aia/external/rg.rb +1163 -0
- data/lib/aia/external/sgpt.rb +58 -0
- data/lib/aia/external/subl.rb +47 -0
- data/lib/aia/external/temp.md +37 -0
- data/lib/aia/external/tool.rb +73 -0
- data/lib/aia/external.rb +259 -0
- data/lib/aia/external_two.rb +43 -0
- data/lib/aia/logging.rb +22 -0
- data/lib/aia/main.rb +39 -0
- data/lib/aia/prompt_processing.rb +212 -0
- data/lib/aia/version.rb +2 -1
- data/lib/aia.rb +4 -381
- data/lib/modularization_plan.md +126 -0
- metadata +22 -2
@@ -0,0 +1,212 @@
|
|
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 = @arguments.shift
|
9
|
+
|
10
|
+
# TODO: or maybe go to a generic search and select process
|
11
|
+
|
12
|
+
abort("Please provide a prompt id") unless prompt_id
|
13
|
+
|
14
|
+
search_for_a_matching_prompt(prompt_id) unless existing_prompt?(prompt_id)
|
15
|
+
edit_prompt if edit?
|
16
|
+
end
|
17
|
+
|
18
|
+
|
19
|
+
# Check if a prompt with the given id already exists
|
20
|
+
def existing_prompt?(prompt_id)
|
21
|
+
@prompt = PromptManager::Prompt.get(id: prompt_id)
|
22
|
+
|
23
|
+
# FIXME: Kludge until prompt_manager is changed
|
24
|
+
# prompt_manager v0.3.0 now supports this feature.
|
25
|
+
# keeping the kludge in for legacy JSON files
|
26
|
+
# files which have not yet been reformatted.
|
27
|
+
@prompt.keywords.each do |kw|
|
28
|
+
if @prompt.parameters[kw].nil? || @prompt.parameters[kw].empty?
|
29
|
+
@prompt.parameters[kw] = []
|
30
|
+
else
|
31
|
+
@prompt.parameters[kw] = Array(@prompt.parameters[kw])
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
true
|
36
|
+
rescue ArgumentError
|
37
|
+
false
|
38
|
+
end
|
39
|
+
|
40
|
+
|
41
|
+
# Process the prompt's associated keywords and parameters
|
42
|
+
def process_prompt
|
43
|
+
unless @prompt.keywords.empty?
|
44
|
+
replace_keywords
|
45
|
+
@prompt.build
|
46
|
+
@prompt.save
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
|
51
|
+
|
52
|
+
def replace_keywords
|
53
|
+
puts
|
54
|
+
puts "ID: #{@prompt.id}"
|
55
|
+
|
56
|
+
show_prompt_without_comments
|
57
|
+
|
58
|
+
puts "\nPress up/down arrow to scroll through history."
|
59
|
+
puts "Type new input or edit the current input."
|
60
|
+
puts "Quit #{MY_NAME} with a CNTL-D or a CNTL-C"
|
61
|
+
puts
|
62
|
+
@prompt.keywords.each do |kw|
|
63
|
+
value = keyword_value(kw, @prompt.parameters[kw])
|
64
|
+
|
65
|
+
unless value.nil? || value.strip.empty?
|
66
|
+
value_inx = @prompt.parameters[kw].index(value)
|
67
|
+
|
68
|
+
if value_inx
|
69
|
+
@prompt.parameters[kw].delete_at(value_inx)
|
70
|
+
end
|
71
|
+
|
72
|
+
# The most recent value for this kw will always be
|
73
|
+
# in the last position
|
74
|
+
@prompt.parameters[kw] << value
|
75
|
+
@prompt.parameters[kw].shift if @prompt.parameters[kw].size > KW_HISTORY_MAX
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
|
81
|
+
# query the user for a value to the keyword allow the
|
82
|
+
# reuse of the previous value shown as the default
|
83
|
+
def keyword_value(kw, history_array)
|
84
|
+
|
85
|
+
Readline::HISTORY.clear
|
86
|
+
Array(history_array).each { |entry| Readline::HISTORY.push(entry) unless entry.nil? || entry.empty? }
|
87
|
+
|
88
|
+
puts "Parameter #{kw} ..."
|
89
|
+
|
90
|
+
begin
|
91
|
+
a_string = Readline.readline("\n-=> ", true)
|
92
|
+
rescue Interrupt
|
93
|
+
a_string = nil
|
94
|
+
end
|
95
|
+
|
96
|
+
if a_string.nil?
|
97
|
+
puts "okay. Come back soon."
|
98
|
+
exit
|
99
|
+
end
|
100
|
+
|
101
|
+
puts
|
102
|
+
a_string.empty? ? default : a_string
|
103
|
+
end
|
104
|
+
|
105
|
+
|
106
|
+
# Search for a prompt with a matching id or keyword
|
107
|
+
def search_for_a_matching_prompt(prompt_id)
|
108
|
+
# TODO: using the rgfzf version of the search_proc should only
|
109
|
+
# return a single prompt_id
|
110
|
+
found_prompts = PromptManager::Prompt.search(prompt_id)
|
111
|
+
|
112
|
+
if found_prompts.empty?
|
113
|
+
if edit?
|
114
|
+
create_prompt(prompt_id)
|
115
|
+
edit_prompt
|
116
|
+
else
|
117
|
+
abort <<~EOS
|
118
|
+
|
119
|
+
No prompts where found for: #{prompt_id}
|
120
|
+
To create a prompt with this ID use the --edit option
|
121
|
+
like this:
|
122
|
+
#{MY_NAME} #{prompt_id} --edit
|
123
|
+
|
124
|
+
EOS
|
125
|
+
end
|
126
|
+
else
|
127
|
+
prompt_id = 1 == found_prompts.size ? found_prompts.first : handle_multiple_prompts(found_prompts, prompt_id)
|
128
|
+
@prompt = PromptManager::Prompt.get(id: prompt_id)
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
|
133
|
+
def handle_multiple_prompts(found_these, while_looking_for_this)
|
134
|
+
raise ArgumentError, "Argument is not an Array" unless found_these.is_a?(Array)
|
135
|
+
|
136
|
+
# TODO: Make this a class constant for defaults; make the header content
|
137
|
+
# a parameter so it can be varied.
|
138
|
+
fzf_options = [
|
139
|
+
"--tabstop=2", # 2 soaces for a tab
|
140
|
+
"--header='Prompt IDs which contain: #{while_looking_for_this}\nPress ESC to cancel.'",
|
141
|
+
"--header-first",
|
142
|
+
"--prompt='Search term: '",
|
143
|
+
'--delimiter :',
|
144
|
+
"--preview 'cat $PROMPTS_DIR/{1}.txt'",
|
145
|
+
"--preview-window=down:50%:wrap"
|
146
|
+
].join(' ')
|
147
|
+
|
148
|
+
|
149
|
+
# Create a temporary file to hold the list of strings
|
150
|
+
temp_file = Tempfile.new('fzf-input')
|
151
|
+
|
152
|
+
begin
|
153
|
+
# Write all strings to the temp file
|
154
|
+
temp_file.puts(found_these)
|
155
|
+
temp_file.close
|
156
|
+
|
157
|
+
# Execute fzf command-line utility to allow selection
|
158
|
+
selected = `cat #{temp_file.path} | fzf #{fzf_options}`.strip
|
159
|
+
|
160
|
+
# Check if fzf actually returned a string; if not, return nil
|
161
|
+
result = selected.empty? ? nil : selected
|
162
|
+
ensure
|
163
|
+
# Ensure that the tempfile is closed and unlinked
|
164
|
+
temp_file.unlink
|
165
|
+
end
|
166
|
+
|
167
|
+
exit unless result
|
168
|
+
|
169
|
+
result
|
170
|
+
end
|
171
|
+
|
172
|
+
|
173
|
+
def create_prompt(prompt_id)
|
174
|
+
@prompt = PromptManager::Prompt.create(id: prompt_id)
|
175
|
+
# TODO: consider a configurable prompt template
|
176
|
+
# ERB ???
|
177
|
+
end
|
178
|
+
|
179
|
+
|
180
|
+
def edit_prompt
|
181
|
+
`#{EDITOR} #{@prompt.path}`
|
182
|
+
@options[:edit?][0] = false
|
183
|
+
@prompt = PromptManager::Prompt.get(id: @prompt.id)
|
184
|
+
end
|
185
|
+
|
186
|
+
|
187
|
+
def show_prompt_without_comments
|
188
|
+
puts remove_comments.wrap(indent: 4)
|
189
|
+
end
|
190
|
+
|
191
|
+
|
192
|
+
def remove_comments
|
193
|
+
lines = @prompt.text
|
194
|
+
.split("\n")
|
195
|
+
.reject{|a_line| a_line.strip.start_with?('#')}
|
196
|
+
|
197
|
+
# Remove empty lines at the start of the prompt
|
198
|
+
#
|
199
|
+
lines = lines.drop_while(&:empty?)
|
200
|
+
|
201
|
+
# Drop all the lines at __END__ and after
|
202
|
+
#
|
203
|
+
logical_end_inx = lines.index("__END__")
|
204
|
+
|
205
|
+
if logical_end_inx
|
206
|
+
lines[0...logical_end_inx] # NOTE: ... means to not include last index
|
207
|
+
else
|
208
|
+
lines
|
209
|
+
end.join("\n")
|
210
|
+
end
|
211
|
+
end
|
212
|
+
|
data/lib/aia/version.rb
CHANGED
data/lib/aia.rb
CHANGED
@@ -1,397 +1,20 @@
|
|
1
1
|
# lib/aia.rb
|
2
2
|
|
3
|
-
require 'amazing_print'
|
4
3
|
require 'pathname'
|
5
4
|
require 'readline'
|
6
5
|
require 'tempfile'
|
7
6
|
|
8
|
-
|
9
|
-
require 'debug_me'
|
10
|
-
include DebugMe
|
11
|
-
|
12
|
-
$DEBUG_ME = true # ARGV.include?("--debug") || ARGV.include?("-d")
|
13
|
-
|
14
7
|
require 'prompt_manager'
|
15
8
|
require 'prompt_manager/storage/file_system_adapter'
|
16
9
|
|
17
10
|
require_relative "aia/version"
|
11
|
+
require_relative "aia/main"
|
18
12
|
require_relative "core_ext/string_wrap"
|
19
13
|
|
20
14
|
module AIA
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
AI_CLI_PROGRAM = "mods"
|
26
|
-
EDITOR = ENV['EDITOR'] || 'edit'
|
27
|
-
MY_NAME = Pathname.new(__FILE__).basename.to_s.split('.')[0]
|
28
|
-
MODS_MODEL = ENV['MODS_MODEL'] || 'gpt-4-1106-preview'
|
29
|
-
OUTPUT = Pathname.pwd + "temp.md"
|
30
|
-
PROMPT_LOG = PROMPTS_DIR + "_prompts.log"
|
31
|
-
|
32
|
-
|
33
|
-
# TODO: write the usage text
|
34
|
-
USAGE = <<~EOUSAGE
|
35
|
-
AI Assistant (aia)
|
36
|
-
==================
|
37
|
-
|
38
|
-
The AI cli program being used is: #{AI_CLI_PROGRAM}
|
39
|
-
|
40
|
-
You can pass additional CLI options to #{AI_CLI_PROGRAM} like this:
|
41
|
-
"#{MY_NAME} my options -- options for #{AI_CLI_PROGRAM}"
|
42
|
-
EOUSAGE
|
43
|
-
|
44
|
-
|
45
|
-
def initialize(args= ARGV)
|
46
|
-
@prompt = nil
|
47
|
-
@arguments = args
|
48
|
-
@options = {
|
49
|
-
edit?: false,
|
50
|
-
debug?: false,
|
51
|
-
verbose?: false,
|
52
|
-
version?: false,
|
53
|
-
help?: false,
|
54
|
-
fuzzy?: false,
|
55
|
-
markdown?: true,
|
56
|
-
output: OUTPUT,
|
57
|
-
log: PROMPT_LOG,
|
58
|
-
}
|
59
|
-
@extra_options = [] # intended for the backend AI processor
|
60
|
-
|
61
|
-
build_reader_methods # for the @options keys
|
62
|
-
process_arguments
|
63
|
-
|
64
|
-
PromptManager::Prompt.storage_adapter =
|
65
|
-
PromptManager::Storage::FileSystemAdapter.config do |config|
|
66
|
-
config.prompts_dir = PROMPTS_DIR
|
67
|
-
config.prompt_extension = '.txt'
|
68
|
-
config.params_extension = '.json'
|
69
|
-
config.search_proc = nil
|
70
|
-
# TODO: add the rgfzz script for search_proc
|
71
|
-
end.new
|
72
|
-
|
73
|
-
setup_cli_program
|
74
|
-
end
|
75
|
-
|
76
|
-
|
77
|
-
def build_reader_methods
|
78
|
-
@options.keys.each do |key|
|
79
|
-
define_singleton_method(key) do
|
80
|
-
@options[key]
|
81
|
-
end
|
82
|
-
end
|
83
|
-
end
|
84
|
-
|
85
|
-
|
86
|
-
def process_arguments
|
87
|
-
@options.keys.each do |option|
|
88
|
-
check_for option
|
89
|
-
end
|
90
|
-
|
91
|
-
# get the options meant for the backend AI command
|
92
|
-
extract_extra_options
|
93
|
-
|
94
|
-
bad_options = @arguments.select{|a| a.start_with?('-')}
|
95
|
-
|
96
|
-
unless bad_options.empty?
|
97
|
-
puts <<~EOS
|
98
|
-
|
99
|
-
ERROR: Unknown options: #{bad_options.join(' ')}
|
100
|
-
|
101
|
-
EOS
|
102
|
-
|
103
|
-
show_usage
|
104
|
-
|
105
|
-
exit
|
106
|
-
end
|
107
|
-
end
|
108
|
-
|
109
|
-
|
110
|
-
def check_for(an_option)
|
111
|
-
switches = [
|
112
|
-
"--#{an_option}".gsub('?',''), # Dropping ? in case of a boolean
|
113
|
-
"--no-#{an_option}".gsub('?',''),
|
114
|
-
"-#{an_option.to_s[0]}" # SMELL: -v for both --verbose and --version
|
115
|
-
]
|
116
|
-
|
117
|
-
process_option(an_option, switches)
|
118
|
-
end
|
119
|
-
|
120
|
-
|
121
|
-
def process_option(option_sym, switches)
|
122
|
-
boolean = option_sym.to_s.end_with?('?')
|
123
|
-
|
124
|
-
switches.each do |switch|
|
125
|
-
if @arguments.include?(switch)
|
126
|
-
index = @arguments.index(switch)
|
127
|
-
|
128
|
-
if boolean
|
129
|
-
@options[option_sym] = switch.include?('-no-') ? false : true
|
130
|
-
@arguments.slice!(index,1)
|
131
|
-
else
|
132
|
-
if switch.include?('-no-')
|
133
|
-
@option[option_sym] = nil
|
134
|
-
@arguments.slice!(index,1)
|
135
|
-
else
|
136
|
-
@option[option_sym] = @arguments[index + 1]
|
137
|
-
@arguments.slice!(index,2)
|
138
|
-
end
|
139
|
-
end
|
140
|
-
|
141
|
-
break
|
142
|
-
end
|
143
|
-
end
|
144
|
-
end
|
145
|
-
|
146
|
-
|
147
|
-
def show_usage
|
148
|
-
puts USAGE
|
149
|
-
exit
|
150
|
-
end
|
151
|
-
|
152
|
-
|
153
|
-
def show_version
|
154
|
-
puts VERSION
|
155
|
-
exit
|
156
|
-
end
|
157
|
-
|
158
|
-
|
159
|
-
def call
|
160
|
-
show_usage if help?
|
161
|
-
show_version if version?
|
162
|
-
|
163
|
-
prompt_id = get_prompt_id
|
164
|
-
|
165
|
-
search_for_a_matching_prompt(prompt_id) unless existing_prompt?(prompt_id)
|
166
|
-
process_prompt
|
167
|
-
execute_and_log_command(build_command)
|
168
|
-
end
|
169
|
-
|
170
|
-
|
171
|
-
####################################################
|
172
|
-
private
|
173
|
-
|
174
|
-
# Setup the AI CLI program with necessary variables
|
175
|
-
def setup_cli_program
|
176
|
-
|
177
|
-
ai_default_opts = "-m #{MODS_MODEL} --no-limit "
|
178
|
-
ai_default_opts += "-f " if markdown?
|
179
|
-
@ai_options = ai_default_opts.dup
|
180
|
-
|
181
|
-
|
182
|
-
@ai_options += @extra_options.join(' ')
|
183
|
-
|
184
|
-
@ai_command = "#{AI_CLI_PROGRAM} #{@ai_options} "
|
185
|
-
end
|
186
|
-
|
187
|
-
|
188
|
-
# Get the additional CLI arguments intended for the
|
189
|
-
# backend gen-AI processor.
|
190
|
-
def extract_extra_options
|
191
|
-
extra_index = @arguments.index('--')
|
192
|
-
if extra_index.nil?
|
193
|
-
@extra_options = []
|
194
|
-
else
|
195
|
-
@extra_options = @arguments.slice!(extra_index..-1)[1..]
|
196
|
-
end
|
197
|
-
end
|
198
|
-
|
199
|
-
|
200
|
-
# Fetch the first argument which should be the prompt id
|
201
|
-
def get_prompt_id
|
202
|
-
prompt_id = @arguments.shift
|
203
|
-
|
204
|
-
# TODO: or maybe go to a search and select process
|
205
|
-
|
206
|
-
abort("Please provide a prompt id") unless prompt_id
|
207
|
-
prompt_id
|
208
|
-
end
|
209
|
-
|
210
|
-
|
211
|
-
# Check if a prompt with the given id already exists
|
212
|
-
def existing_prompt?(prompt_id)
|
213
|
-
@prompt = PromptManager::Prompt.get(id: prompt_id)
|
214
|
-
true
|
215
|
-
rescue ArgumentError
|
216
|
-
false
|
217
|
-
end
|
218
|
-
|
219
|
-
|
220
|
-
# Process the prompt's associated keywords and parameters
|
221
|
-
def process_prompt
|
222
|
-
unless @prompt.keywords.empty?
|
223
|
-
replace_keywords
|
224
|
-
@prompt.build
|
225
|
-
@prompt.save
|
226
|
-
end
|
227
|
-
end
|
228
|
-
|
229
|
-
|
230
|
-
def replace_keywords
|
231
|
-
print "\nQuit #{MY_NAME} with a CNTL-D or a CNTL-C\n\n"
|
232
|
-
|
233
|
-
defaults = @prompt.parameters
|
234
|
-
|
235
|
-
@prompt.keywords.each do |kw|
|
236
|
-
defaults[kw] = keyword_value(kw, defaults[kw])
|
237
|
-
end
|
238
|
-
|
239
|
-
@prompt.parameters = defaults
|
240
|
-
end
|
241
|
-
|
242
|
-
|
243
|
-
# query the user for a value to the keyword allow the
|
244
|
-
# reuse of the previous value shown as the default
|
245
|
-
def keyword_value(kw, default)
|
246
|
-
label = "Default: "
|
247
|
-
puts "Parameter #{kw} ..."
|
248
|
-
default_wrapped = default.wrap(indent: label.size)
|
249
|
-
default_wrapped[0..label.size] = label
|
250
|
-
puts default_wrapped
|
251
|
-
|
252
|
-
begin
|
253
|
-
a_string = Readline.readline("\n-=> ", false)
|
254
|
-
rescue Interrupt
|
255
|
-
a_string = nil
|
256
|
-
end
|
257
|
-
|
258
|
-
if a_string.nil?
|
259
|
-
puts "okay. Come back soon."
|
260
|
-
exit
|
261
|
-
end
|
262
|
-
|
263
|
-
|
264
|
-
puts
|
265
|
-
a_string.empty? ? default : a_string
|
266
|
-
end
|
267
|
-
|
268
|
-
|
269
|
-
# Search for a prompt with a matching id or keyword
|
270
|
-
def search_for_a_matching_prompt(prompt_id)
|
271
|
-
# TODO: using the rgfzf version of the search_proc should only
|
272
|
-
# return a single prompt_id
|
273
|
-
found_prompts = PromptManager::Prompt.search(prompt_id)
|
274
|
-
prompt_id = found_prompts.size == 1 ? found_prompts.first : handle_multiple_prompts(found_prompts, prompt_id)
|
275
|
-
@prompt = PromptManager::Prompt.get(id: prompt_id)
|
276
|
-
end
|
277
|
-
|
278
|
-
|
279
|
-
def handle_multiple_prompts(found_these, while_looking_for_this)
|
280
|
-
raise ArgumentError, "Argument is not an Array" unless found_these.is_a?(Array)
|
281
|
-
|
282
|
-
# TODO: Make this a class constant for defaults; make the header content
|
283
|
-
# a parameter so it can be varied.
|
284
|
-
fzf_options = [
|
285
|
-
"--tabstop=2", # 2 soaces for a tab
|
286
|
-
"--header='Prompt IDs which contain: #{while_looking_for_this}\nPress ESC to cancel.'",
|
287
|
-
"--header-first",
|
288
|
-
"--prompt='Search term: '",
|
289
|
-
'--delimiter :',
|
290
|
-
"--preview 'cat $PROMPTS_DIR/{1}.txt'",
|
291
|
-
"--preview-window=down:50%:wrap"
|
292
|
-
].join(' ')
|
293
|
-
|
294
|
-
|
295
|
-
# Create a temporary file to hold the list of strings
|
296
|
-
temp_file = Tempfile.new('fzf-input')
|
297
|
-
|
298
|
-
begin
|
299
|
-
# Write all strings to the temp file
|
300
|
-
temp_file.puts(found_these)
|
301
|
-
temp_file.close
|
302
|
-
|
303
|
-
# Execute fzf command-line utility to allow selection
|
304
|
-
selected = `cat #{temp_file.path} | fzf #{fzf_options}`.strip
|
305
|
-
|
306
|
-
# Check if fzf actually returned a string; if not, return nil
|
307
|
-
result = selected.empty? ? nil : selected
|
308
|
-
ensure
|
309
|
-
# Ensure that the tempfile is closed and unlinked
|
310
|
-
temp_file.unlink
|
311
|
-
end
|
312
|
-
|
313
|
-
exit unless result
|
314
|
-
|
315
|
-
result
|
316
|
-
end
|
317
|
-
|
318
|
-
|
319
|
-
# Build the command to interact with the AI CLI program
|
320
|
-
def build_command
|
321
|
-
command = @ai_command + %Q["#{@prompt.to_s}"]
|
322
|
-
|
323
|
-
@arguments.each do |input_file|
|
324
|
-
file_path = Pathname.new(input_file)
|
325
|
-
abort("File does not exist: #{input_file}") unless file_path.exist?
|
326
|
-
command += " < #{input_file}"
|
327
|
-
end
|
328
|
-
|
329
|
-
command
|
330
|
-
end
|
331
|
-
|
332
|
-
|
333
|
-
# Execute the command and log the results
|
334
|
-
def execute_and_log_command(command)
|
335
|
-
puts command if verbose?
|
336
|
-
result = `#{command}`
|
337
|
-
output.write result
|
338
|
-
|
339
|
-
write_to_log(result) unless log.nil?
|
340
|
-
end
|
341
|
-
|
342
|
-
|
343
|
-
def write_to_log(answer)
|
344
|
-
f = File.open(log, "ab")
|
345
|
-
|
346
|
-
f.write <<~EOS
|
347
|
-
=======================================
|
348
|
-
== #{Time.now}
|
349
|
-
== #{@prompt.path}
|
350
|
-
|
351
|
-
PROMPT:
|
352
|
-
#{@prompt}
|
353
|
-
|
354
|
-
RESULT:
|
355
|
-
#{answer}
|
356
|
-
|
357
|
-
EOS
|
358
|
-
end
|
15
|
+
def self.run(args=ARGV)
|
16
|
+
args = args.split(' ') if args.is_a?(String)
|
17
|
+
AIA::Main.new(args).call
|
359
18
|
end
|
360
19
|
end
|
361
20
|
|
362
|
-
|
363
|
-
# Create an instance of the Main class and run the program
|
364
|
-
AIA::Main.new.call if $PROGRAM_NAME == __FILE__
|
365
|
-
|
366
|
-
|
367
|
-
__END__
|
368
|
-
|
369
|
-
|
370
|
-
# TODO: Consider using this history process to preload the default
|
371
|
-
# so that an up arrow will bring the previous answer into
|
372
|
-
# the read buffer for line editing.
|
373
|
-
# Instead of usin the .history file just push the default
|
374
|
-
# value from the JSON file.
|
375
|
-
|
376
|
-
while input = Readline.readline('> ', true)
|
377
|
-
# Skip empty entries and duplicates
|
378
|
-
if input.empty? || Readline::HISTORY.to_a[-2] == input
|
379
|
-
Readline::HISTORY.pop
|
380
|
-
end
|
381
|
-
break if input == 'exit'
|
382
|
-
|
383
|
-
# Do something with the input
|
384
|
-
puts "You entered: #{input}"
|
385
|
-
|
386
|
-
# Save the history in case you want to preserve it for the next sessions
|
387
|
-
File.open('.history', 'a') { |f| f.puts(input) }
|
388
|
-
end
|
389
|
-
|
390
|
-
# Load history from file at the beginning of the program
|
391
|
-
if File.exist?('.history')
|
392
|
-
File.readlines('.history').each do |line|
|
393
|
-
Readline::HISTORY.push(line.chomp)
|
394
|
-
end
|
395
|
-
end
|
396
|
-
|
397
|
-
|