aia 0.5.18 → 0.8.1
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/.envrc +1 -0
- data/.version +1 -1
- data/CHANGELOG.md +39 -5
- data/README.md +388 -219
- data/Rakefile +16 -5
- data/_notes.txt +231 -0
- data/bin/aia +3 -2
- data/examples/README.md +140 -0
- data/examples/headlines +21 -0
- data/lib/aia/ai_client_adapter.rb +210 -0
- data/lib/aia/chat_processor_service.rb +120 -0
- data/lib/aia/config.rb +473 -4
- data/lib/aia/context_manager.rb +58 -0
- data/lib/aia/directive_processor.rb +267 -0
- data/lib/aia/{tools/fzf.rb → fzf.rb} +9 -17
- data/lib/aia/history_manager.rb +85 -0
- data/lib/aia/prompt_handler.rb +178 -0
- data/lib/aia/session.rb +215 -0
- data/lib/aia/shell_command_executor.rb +109 -0
- data/lib/aia/ui_presenter.rb +110 -0
- data/lib/aia/utility.rb +24 -0
- data/lib/aia/version.rb +9 -6
- data/lib/aia.rb +57 -61
- data/lib/extensions/openstruct_merge.rb +44 -0
- metadata +29 -42
- data/LICENSE.txt +0 -21
- data/doc/aia_and_pre_compositional_prompts.md +0 -474
- data/lib/aia/clause.rb +0 -7
- data/lib/aia/cli.rb +0 -452
- data/lib/aia/directives.rb +0 -142
- data/lib/aia/dynamic_content.rb +0 -26
- data/lib/aia/logging.rb +0 -62
- data/lib/aia/main.rb +0 -265
- data/lib/aia/prompt.rb +0 -275
- data/lib/aia/tools/backend_common.rb +0 -58
- data/lib/aia/tools/client.rb +0 -197
- data/lib/aia/tools/editor.rb +0 -52
- data/lib/aia/tools/glow.rb +0 -90
- data/lib/aia/tools/llm.rb +0 -77
- data/lib/aia/tools/mods.rb +0 -100
- data/lib/aia/tools/sgpt.rb +0 -79
- data/lib/aia/tools/subl.rb +0 -68
- data/lib/aia/tools/vim.rb +0 -93
- data/lib/aia/tools.rb +0 -88
- data/lib/aia/user_query.rb +0 -21
- data/lib/core_ext/string_wrap.rb +0 -73
- data/lib/core_ext/tty-spinner_log.rb +0 -25
- data/man/aia.1 +0 -272
- data/man/aia.1.md +0 -236
data/lib/aia/cli.rb
DELETED
@@ -1,452 +0,0 @@
|
|
1
|
-
# lib/aia/cli.rb
|
2
|
-
|
3
|
-
HOME = Pathname.new(ENV['HOME'])
|
4
|
-
MY_NAME = 'aia'
|
5
|
-
|
6
|
-
|
7
|
-
require 'hashie'
|
8
|
-
require 'pathname'
|
9
|
-
require 'yaml'
|
10
|
-
require 'toml-rb'
|
11
|
-
|
12
|
-
|
13
|
-
class AIA::Cli
|
14
|
-
CF_FORMATS = %w[yml yaml toml]
|
15
|
-
ENV_PREFIX = self.name.split('::').first.upcase + "_"
|
16
|
-
MAN_PAGE_PATH = Pathname.new(__dir__) + '../../man/aia.1'
|
17
|
-
|
18
|
-
|
19
|
-
def initialize(args)
|
20
|
-
args = args.split(' ') if args.is_a? String
|
21
|
-
|
22
|
-
setup_options_with_defaults(args) # 1. defaults
|
23
|
-
load_env_options # 2. over-ride with envars
|
24
|
-
process_command_line_arguments # 3. over-ride with command line options
|
25
|
-
|
26
|
-
# 4. over-ride everything with config file
|
27
|
-
load_config_file unless AIA.config.config_file.nil?
|
28
|
-
|
29
|
-
convert_to_pathname_objects
|
30
|
-
error_on_invalid_option_combinations
|
31
|
-
setup_prompt_manager
|
32
|
-
execute_immediate_commands
|
33
|
-
end
|
34
|
-
|
35
|
-
|
36
|
-
def convert_pathname_objects!(converting_to_pathname: true)
|
37
|
-
path_keys = AIA.config.keys.grep(/_(dir|file)\z/)
|
38
|
-
path_keys.each do |key|
|
39
|
-
case AIA.config[key]
|
40
|
-
when String
|
41
|
-
AIA.config[key] = string_to_pathname(AIA.config[key])
|
42
|
-
when Pathname
|
43
|
-
AIA.config[key] = pathname_to_string(AIA.config[key]) unless converting_to_pathname
|
44
|
-
end
|
45
|
-
end
|
46
|
-
end
|
47
|
-
|
48
|
-
|
49
|
-
def error_on_invalid_option_combinations
|
50
|
-
# --chat is intended as an interactive exchange
|
51
|
-
if AIA.config.chat?
|
52
|
-
unless AIA.config.next.empty?
|
53
|
-
abort "ERROR: Cannot use --next with --chat"
|
54
|
-
end
|
55
|
-
unless STDOUT == AIA.config.out_file
|
56
|
-
abort "ERROR: Cannot use --out_file with --chat"
|
57
|
-
end
|
58
|
-
unless AIA.config.pipeline.empty?
|
59
|
-
abort "ERROR: Cannot use --pipeline with --chat"
|
60
|
-
end
|
61
|
-
end
|
62
|
-
|
63
|
-
# --next says which prompt to process next
|
64
|
-
# but --pipeline gives an entire sequence of prompts for processing
|
65
|
-
unless AIA.config.next.empty?
|
66
|
-
unless AIA.config.pipeline.empty?
|
67
|
-
abort "ERROR: Cannot use --pipeline with --next"
|
68
|
-
end
|
69
|
-
end
|
70
|
-
end
|
71
|
-
|
72
|
-
def string_to_pathname(string)
|
73
|
-
['~/', '$HOME/'].each do |prefix|
|
74
|
-
if string.start_with? prefix
|
75
|
-
string = string.gsub(prefix, HOME.to_s+'/')
|
76
|
-
break
|
77
|
-
end
|
78
|
-
end
|
79
|
-
|
80
|
-
pathname = Pathname.new(string)
|
81
|
-
pathname.relative? ? Pathname.pwd + pathname : pathname
|
82
|
-
end
|
83
|
-
|
84
|
-
|
85
|
-
def pathname_to_string(pathname)
|
86
|
-
pathname.to_s
|
87
|
-
end
|
88
|
-
|
89
|
-
|
90
|
-
def convert_to_pathname_objects
|
91
|
-
convert_pathname_objects!(converting_to_pathname: true)
|
92
|
-
end
|
93
|
-
|
94
|
-
|
95
|
-
def convert_from_pathname_objects
|
96
|
-
convert_pathname_objects!(converting_to_pathname: false)
|
97
|
-
end
|
98
|
-
|
99
|
-
|
100
|
-
def load_env_options
|
101
|
-
known_keys = @options.keys
|
102
|
-
|
103
|
-
keys = ENV.keys
|
104
|
-
.select{|k| k.start_with?(ENV_PREFIX)}
|
105
|
-
.map{|k| k.gsub(ENV_PREFIX,'').downcase.to_sym}
|
106
|
-
|
107
|
-
keys.each do |key|
|
108
|
-
envar_key = ENV_PREFIX + key.to_s.upcase
|
109
|
-
if known_keys.include?(key)
|
110
|
-
AIA.config[key] = ENV[envar_key]
|
111
|
-
elsif known_keys.include?("#{key}?".to_sym)
|
112
|
-
key = "#{key}?".to_sym
|
113
|
-
AIA.config[key] = %w[true t yes yea y 1].include?(ENV[envar_key].strip.downcase) ? true : false
|
114
|
-
else
|
115
|
-
# This is a new config key
|
116
|
-
AIA.config[key] = ENV[envar_key]
|
117
|
-
end
|
118
|
-
end
|
119
|
-
end
|
120
|
-
|
121
|
-
|
122
|
-
def replace_erb_in_config_file
|
123
|
-
content = Pathname.new(AIA.config.config_file).read
|
124
|
-
content = ERB.new(content).result(binding)
|
125
|
-
AIA.config.config_file = AIA.config.config_file.to_s.gsub('.erb', '')
|
126
|
-
Pathname.new(AIA.config.config_file).write content
|
127
|
-
end
|
128
|
-
|
129
|
-
|
130
|
-
def load_config_file
|
131
|
-
if AIA.config.config_file.to_s.end_with?(".erb")
|
132
|
-
replace_erb_in_config_file
|
133
|
-
end
|
134
|
-
|
135
|
-
AIA.config.config_file = Pathname.new(AIA.config.config_file)
|
136
|
-
if AIA.config.config_file.exist?
|
137
|
-
AIA.config.merge! parse_config_file
|
138
|
-
else
|
139
|
-
abort "Config file does not exist: #{AIA.config.config_file}"
|
140
|
-
end
|
141
|
-
end
|
142
|
-
|
143
|
-
|
144
|
-
def setup_options_with_defaults(args)
|
145
|
-
# TODO: This structure if flat; consider making it
|
146
|
-
# at least two levels totake advantage of
|
147
|
-
# YAML and TOML capabilities to isolate
|
148
|
-
# common options within a section.
|
149
|
-
#
|
150
|
-
@options = {
|
151
|
-
# Default
|
152
|
-
# Key Value, switches
|
153
|
-
arguments: [args], # NOTE: after process, prompt_id and context_files will be left
|
154
|
-
directives: [[]], # an empty Array as the default value
|
155
|
-
extra: [''], #
|
156
|
-
#
|
157
|
-
model: ["gpt-4-1106-preview", "--llm --model"],
|
158
|
-
speech_model: ["tts-1", "--sm --speech_model"],
|
159
|
-
voice: ["alloy", "--voice"],
|
160
|
-
#
|
161
|
-
transcription_model: ["wisper-1", "--tm --transcription_model"],
|
162
|
-
#
|
163
|
-
dump_file: [nil, "--dump"],
|
164
|
-
completion: [nil, "--completion"],
|
165
|
-
#
|
166
|
-
chat?: [false, "--chat"],
|
167
|
-
debug?: [false, "-d --debug"],
|
168
|
-
edit?: [false, "-e --edit"],
|
169
|
-
erb?: [false, "--erb"],
|
170
|
-
fuzzy?: [false, "-f --fuzzy"],
|
171
|
-
help?: [false, "-h --help"],
|
172
|
-
markdown?: [true, "-m --markdown --no-markdown --md --no-md"],
|
173
|
-
render?: [false, "--render"],
|
174
|
-
shell?: [false, "--shell"],
|
175
|
-
speak?: [false, "--speak"],
|
176
|
-
terse?: [false, "--terse"],
|
177
|
-
verbose?: [false, "-v --verbose"],
|
178
|
-
version?: [false, "--version"],
|
179
|
-
#
|
180
|
-
next: ['', "-n --next"],
|
181
|
-
pipeline: [[], "--pipeline"],
|
182
|
-
role: ['', "-r --role"],
|
183
|
-
#
|
184
|
-
config_file:[nil, "-c --config_file"],
|
185
|
-
prompts_dir:["~/.prompts", "-p --prompts_dir"],
|
186
|
-
roles_dir: ["~/.prompts/roles", "--roles_dir"],
|
187
|
-
out_file: [STDOUT, "-o --out_file --no-out_file"],
|
188
|
-
log_file: ["~/.prompts/_prompts.log", "-l --log_file --no-log_file"],
|
189
|
-
#
|
190
|
-
backend: ['mods', "-b --be --backend --no-backend"],
|
191
|
-
#
|
192
|
-
# text2image related ...
|
193
|
-
#
|
194
|
-
image_size: ['', '--is --image_size'],
|
195
|
-
image_quality: ['', '--iq --image_quality'],
|
196
|
-
}
|
197
|
-
|
198
|
-
AIA.config = AIA::Config.new(@options.transform_values { |values| values.first })
|
199
|
-
end
|
200
|
-
|
201
|
-
|
202
|
-
def arguments
|
203
|
-
AIA.config.arguments
|
204
|
-
end
|
205
|
-
|
206
|
-
|
207
|
-
def execute_immediate_commands
|
208
|
-
show_usage if AIA.config.help?
|
209
|
-
show_version if AIA.config.version?
|
210
|
-
dump_config_file if AIA.config.dump_file
|
211
|
-
show_completion if AIA.config.completion
|
212
|
-
end
|
213
|
-
|
214
|
-
|
215
|
-
def dump_config_file
|
216
|
-
a_hash = prepare_config_as_hash
|
217
|
-
|
218
|
-
dump_file = Pathname.new AIA.config.dump_file
|
219
|
-
extname = dump_file.extname.to_s.downcase
|
220
|
-
|
221
|
-
case extname
|
222
|
-
when '.yml', '.yaml'
|
223
|
-
dump_file.write YAML.dump(a_hash)
|
224
|
-
when '.toml'
|
225
|
-
dump_file.write TomlRB.dump(a_hash)
|
226
|
-
else
|
227
|
-
abort "Invalid config file format (#{extname}) request. Only #{CF_FORMATS.join(', ')} are supported."
|
228
|
-
end
|
229
|
-
|
230
|
-
exit
|
231
|
-
end
|
232
|
-
|
233
|
-
|
234
|
-
def prepare_config_as_hash
|
235
|
-
convert_from_pathname_objects
|
236
|
-
|
237
|
-
a_hash = AIA.config.to_h
|
238
|
-
a_hash['dump'] = nil
|
239
|
-
|
240
|
-
%w[ arguments config_file dump_file ].each do |unwanted_key|
|
241
|
-
a_hash.delete(unwanted_key)
|
242
|
-
end
|
243
|
-
|
244
|
-
a_hash
|
245
|
-
end
|
246
|
-
|
247
|
-
|
248
|
-
def process_command_line_arguments
|
249
|
-
# get the options meant for the backend AI command
|
250
|
-
# doing this first in case there are any options that conflict
|
251
|
-
# between frontend and backend.
|
252
|
-
extract_extra_options
|
253
|
-
|
254
|
-
@options.keys.each do |option|
|
255
|
-
check_for option
|
256
|
-
end
|
257
|
-
|
258
|
-
bad_options = arguments.select{|a| a.start_with?('-')}
|
259
|
-
|
260
|
-
unless bad_options.empty?
|
261
|
-
puts <<~EOS
|
262
|
-
|
263
|
-
ERROR: Unknown options: #{bad_options.join(' ')}
|
264
|
-
|
265
|
-
EOS
|
266
|
-
|
267
|
-
show_error_usage
|
268
|
-
|
269
|
-
exit
|
270
|
-
end
|
271
|
-
|
272
|
-
# After all other arguments
|
273
|
-
# are processed, check for role parameter.
|
274
|
-
check_for_role_parameter
|
275
|
-
end
|
276
|
-
|
277
|
-
|
278
|
-
def check_for(option_sym)
|
279
|
-
# sometimes @options has stuff that is not a command line option
|
280
|
-
return if @options[option_sym].nil? || @options[option_sym].size <= 1
|
281
|
-
|
282
|
-
boolean = option_sym.to_s.end_with?('?')
|
283
|
-
switches = @options[option_sym][1].split
|
284
|
-
|
285
|
-
switches.each do |switch|
|
286
|
-
if arguments.include?(switch)
|
287
|
-
index = arguments.index(switch)
|
288
|
-
|
289
|
-
if boolean
|
290
|
-
AIA.config[option_sym] = switch.include?('-no-') ? false : true
|
291
|
-
arguments.slice!(index,1)
|
292
|
-
else
|
293
|
-
if switch.include?('-no-')
|
294
|
-
AIA.config[option_sym] = switch.include?('out_file') ? STDOUT : nil
|
295
|
-
arguments.slice!(index,1)
|
296
|
-
else
|
297
|
-
value = arguments[index + 1]
|
298
|
-
if value.nil? || value.start_with?('-')
|
299
|
-
abort "ERROR: #{option_sym} requires a parameter value"
|
300
|
-
elsif "--pipeline" == switch
|
301
|
-
prompt_sequence = value.split(',')
|
302
|
-
AIA.config[option_sym] = prompt_sequence
|
303
|
-
arguments.slice!(index,2)
|
304
|
-
else
|
305
|
-
AIA.config[option_sym] = value
|
306
|
-
arguments.slice!(index,2)
|
307
|
-
end
|
308
|
-
end
|
309
|
-
end
|
310
|
-
|
311
|
-
break
|
312
|
-
end
|
313
|
-
end
|
314
|
-
end
|
315
|
-
|
316
|
-
|
317
|
-
def check_for_role_parameter
|
318
|
-
role = AIA.config.role
|
319
|
-
return if role.empty?
|
320
|
-
|
321
|
-
role_path = string_to_pathname(AIA.config.roles_dir) + "#{role}.txt"
|
322
|
-
|
323
|
-
unless role_path.exist?
|
324
|
-
puts "Role prompt '#{role}' not found. Invoking fzf to choose a role..."
|
325
|
-
invoke_fzf_to_choose_role
|
326
|
-
end
|
327
|
-
end
|
328
|
-
|
329
|
-
|
330
|
-
def invoke_fzf_to_choose_role
|
331
|
-
roles_path = string_to_pathname AIA.config.roles_dir
|
332
|
-
|
333
|
-
available_roles = roles_path
|
334
|
-
.children
|
335
|
-
.select { |f| '.txt' == f.extname}
|
336
|
-
.map{|role| role.basename.to_s.gsub('.txt','')}
|
337
|
-
|
338
|
-
fzf = AIA::Fzf.new(
|
339
|
-
list: available_roles,
|
340
|
-
directory: roles_path,
|
341
|
-
prompt: 'Select Role:',
|
342
|
-
extension: '.txt'
|
343
|
-
)
|
344
|
-
|
345
|
-
chosen_role = fzf.run
|
346
|
-
|
347
|
-
if chosen_role.nil?
|
348
|
-
abort("No role selected. Exiting...")
|
349
|
-
else
|
350
|
-
AIA.config.role = chosen_role
|
351
|
-
puts "Role changed to '#{chosen_role}'."
|
352
|
-
end
|
353
|
-
end
|
354
|
-
|
355
|
-
|
356
|
-
def show_error_usage
|
357
|
-
puts <<~ERROR_USAGE
|
358
|
-
|
359
|
-
Usage: aia [options] PROMPT_ID [CONTEXT_FILE(s)] [-- EXTERNAL_OPTIONS]"
|
360
|
-
Try 'aia --help' for more information."
|
361
|
-
|
362
|
-
ERROR_USAGE
|
363
|
-
end
|
364
|
-
|
365
|
-
|
366
|
-
# aia usage is maintained in a man page
|
367
|
-
def show_usage
|
368
|
-
@options[:help?][0] = false
|
369
|
-
puts `man #{MAN_PAGE_PATH}`
|
370
|
-
show_verbose_usage if AIA.config.verbose?
|
371
|
-
exit
|
372
|
-
end
|
373
|
-
alias_method :show_help, :show_usage
|
374
|
-
|
375
|
-
|
376
|
-
def show_verbose_usage
|
377
|
-
puts <<~EOS
|
378
|
-
|
379
|
-
======================================
|
380
|
-
== Currently selected Backend: #{AIA.config.backend} ==
|
381
|
-
======================================
|
382
|
-
|
383
|
-
EOS
|
384
|
-
puts `mods --help` if "mods" == AIA.config.backend
|
385
|
-
puts `sgpt --help` if "sgpt" == AIA.config.backend
|
386
|
-
puts
|
387
|
-
end
|
388
|
-
# alias_method :show_verbose_help, :show_verbose_usage
|
389
|
-
|
390
|
-
|
391
|
-
def show_completion
|
392
|
-
shell = AIA.config.completion
|
393
|
-
script = Pathname.new(__dir__) + "aia_completion.#{shell}"
|
394
|
-
|
395
|
-
if script.exist?
|
396
|
-
puts
|
397
|
-
puts script.read
|
398
|
-
puts
|
399
|
-
else
|
400
|
-
STDERR.puts <<~EOS
|
401
|
-
|
402
|
-
ERROR: The shell '#{shell}' is not supported.
|
403
|
-
|
404
|
-
EOS
|
405
|
-
end
|
406
|
-
|
407
|
-
exit
|
408
|
-
end
|
409
|
-
|
410
|
-
|
411
|
-
def show_version
|
412
|
-
puts AIA::VERSION
|
413
|
-
exit
|
414
|
-
end
|
415
|
-
|
416
|
-
|
417
|
-
def setup_prompt_manager
|
418
|
-
@prompt = nil
|
419
|
-
|
420
|
-
PromptManager::Prompt.storage_adapter =
|
421
|
-
PromptManager::Storage::FileSystemAdapter.config do |config|
|
422
|
-
config.prompts_dir = AIA.config.prompts_dir
|
423
|
-
config.prompt_extension = '.txt'
|
424
|
-
config.params_extension = '.json'
|
425
|
-
config.search_proc = nil
|
426
|
-
# TODO: add the rgfzf script for search_proc
|
427
|
-
end.new
|
428
|
-
end
|
429
|
-
|
430
|
-
|
431
|
-
# Get the additional CLI arguments intended for the
|
432
|
-
# backend gen-AI processor.
|
433
|
-
def extract_extra_options
|
434
|
-
extra_index = arguments.index('--')
|
435
|
-
|
436
|
-
if extra_index
|
437
|
-
AIA.config.extra = arguments.slice!(extra_index..-1)[1..].join(' ')
|
438
|
-
end
|
439
|
-
end
|
440
|
-
|
441
|
-
|
442
|
-
def parse_config_file
|
443
|
-
case AIA.config.config_file.extname.downcase
|
444
|
-
when '.yaml', '.yml'
|
445
|
-
YAML.safe_load(AIA.config.config_file.read)
|
446
|
-
when '.toml'
|
447
|
-
TomlRB.parse(AIA.config.config_file.read)
|
448
|
-
else
|
449
|
-
abort "Unsupported config file type: #{AIA.config.config_file.extname}"
|
450
|
-
end
|
451
|
-
end
|
452
|
-
end
|
data/lib/aia/directives.rb
DELETED
@@ -1,142 +0,0 @@
|
|
1
|
-
# lib/aia/directives.rb
|
2
|
-
|
3
|
-
require 'hashie'
|
4
|
-
|
5
|
-
=begin
|
6
|
-
AIA.config.directives is an Array of Arrays. An
|
7
|
-
entry looks like this:
|
8
|
-
[directive, parameters]
|
9
|
-
where both are String objects
|
10
|
-
=end
|
11
|
-
|
12
|
-
|
13
|
-
class AIA::Directives
|
14
|
-
def execute_my_directives
|
15
|
-
return if AIA.config.directives.nil? || AIA.config.directives.empty?
|
16
|
-
|
17
|
-
result = ""
|
18
|
-
not_mine = []
|
19
|
-
|
20
|
-
AIA.config.directives.each do |entry|
|
21
|
-
directive = entry[0].to_sym
|
22
|
-
parameters = entry[1]
|
23
|
-
|
24
|
-
if respond_to? directive
|
25
|
-
output = send(directive, parameters)
|
26
|
-
result << "#{output}\n" unless output.nil?
|
27
|
-
else
|
28
|
-
not_mine << entry
|
29
|
-
end
|
30
|
-
end
|
31
|
-
|
32
|
-
AIA.config.directives = not_mine
|
33
|
-
|
34
|
-
result.empty? ? nil : result
|
35
|
-
end
|
36
|
-
|
37
|
-
|
38
|
-
def box(what)
|
39
|
-
f = what[0]
|
40
|
-
bar = "#{f}"*what.size
|
41
|
-
puts "#{bar}\n#{what}\n#{bar}"
|
42
|
-
end
|
43
|
-
|
44
|
-
|
45
|
-
# Allows a prompt to change its configuration environment
|
46
|
-
def config(what)
|
47
|
-
parts = what.split(' ')
|
48
|
-
item = parts.shift
|
49
|
-
parts.shift if %w[:= =].include? parts[0]
|
50
|
-
|
51
|
-
if '<<' == parts[0]
|
52
|
-
parts.shift
|
53
|
-
value = parts.join
|
54
|
-
if AIA.config(item).is_a?(Array)
|
55
|
-
AIA.config[item] << value
|
56
|
-
else
|
57
|
-
AIA.config[item] = [ value ]
|
58
|
-
end
|
59
|
-
else
|
60
|
-
value = parts.join
|
61
|
-
if item.end_with?('?')
|
62
|
-
AIA.config[item] = %w[1 y yea yes t true].include?(value.downcase)
|
63
|
-
elsif item.end_with?('_file')
|
64
|
-
if "STDOUT" == value.upcase
|
65
|
-
AIA.config[item] = STDOUT
|
66
|
-
elsif "STDERR" == value.upcase
|
67
|
-
AIA.config[item] = STDERR
|
68
|
-
else
|
69
|
-
AIA.config[item] = value.start_with?('/') ?
|
70
|
-
Pathname.new(value) :
|
71
|
-
Pathname.pwd + value
|
72
|
-
end
|
73
|
-
elsif %w[next pipeline].include? item.downcase
|
74
|
-
pipeline(value)
|
75
|
-
else
|
76
|
-
AIA.config[item] = value
|
77
|
-
end
|
78
|
-
end
|
79
|
-
|
80
|
-
nil
|
81
|
-
end
|
82
|
-
|
83
|
-
|
84
|
-
# TODO: we need a way to submit CLI arguments into
|
85
|
-
# the next prompt(s) from the main prompt.
|
86
|
-
# currently the config for subsequent prompts
|
87
|
-
# is expected to be set within those prompts.
|
88
|
-
# Maybe something like:
|
89
|
-
# //next prompt_id CLI args
|
90
|
-
# This would mean that the pipeline would be:
|
91
|
-
# //pipeline id1 cli args, id2 cli args, id3 cli args
|
92
|
-
#
|
93
|
-
|
94
|
-
# TODO: Change AIA.config.pipline Array to be an Array of arrays
|
95
|
-
# where each entry is:
|
96
|
-
# [prompt_id, cli_args]
|
97
|
-
# This means that:
|
98
|
-
# entry = AIA.config.pipeline.shift
|
99
|
-
# entry.is_A?(Sring) ? 'old format' : 'new format'
|
100
|
-
#
|
101
|
-
|
102
|
-
# //next id
|
103
|
-
# //pipeline id1,id2, id3 , id4
|
104
|
-
def pipeline(what)
|
105
|
-
return if what.empty?
|
106
|
-
AIA.config.pipeline << what.split(',').map(&:strip)
|
107
|
-
AIA.config.pipeline.flatten!
|
108
|
-
end
|
109
|
-
alias_method :next, :pipeline
|
110
|
-
|
111
|
-
# when path_to_file is relative it will be
|
112
|
-
# relative to the PWD.
|
113
|
-
#
|
114
|
-
# TODO: Consider an AIA_INCLUDE_DIR --include_dir
|
115
|
-
# option to be used for all relative include paths
|
116
|
-
#
|
117
|
-
def include(path_to_file)
|
118
|
-
path = Pathname.new path_to_file
|
119
|
-
if path.exist? && path.readable?
|
120
|
-
content = path.readlines.reject do |a_line|
|
121
|
-
a_line.strip.start_with?(AIA::Prompt::COMMENT_SIGNAL) ||
|
122
|
-
a_line.strip.start_with?(AIA::Prompt::DIRECTIVE_SIGNAL)
|
123
|
-
end.join("\n")
|
124
|
-
else
|
125
|
-
abort "ERROR: could not include #{path_to_file}"
|
126
|
-
end
|
127
|
-
|
128
|
-
content
|
129
|
-
end
|
130
|
-
|
131
|
-
|
132
|
-
def shell(command)
|
133
|
-
`#{command}`
|
134
|
-
end
|
135
|
-
|
136
|
-
|
137
|
-
def ruby(code)
|
138
|
-
output = eval(code)
|
139
|
-
|
140
|
-
output.is_a?(String) ? output : nil
|
141
|
-
end
|
142
|
-
end
|
data/lib/aia/dynamic_content.rb
DELETED
@@ -1,26 +0,0 @@
|
|
1
|
-
# aia/lib/aia/dynamic_content.rb
|
2
|
-
|
3
|
-
require 'erb'
|
4
|
-
|
5
|
-
module AIA::DynamicContent
|
6
|
-
|
7
|
-
# inserts environment variables (envars) and dynamic content into a prompt
|
8
|
-
# replaces patterns like $HOME and ${HOME} with the value of ENV['HOME']
|
9
|
-
# replaces patterns like $(shell command) with the output of the shell command
|
10
|
-
#
|
11
|
-
def render_env(a_string)
|
12
|
-
a_string.gsub(/\$(\w+|\{\w+\})/) do |match|
|
13
|
-
ENV[match.tr('$', '').tr('{}', '')]
|
14
|
-
end.gsub(/\$\((.*?)\)/) do |match|
|
15
|
-
`#{match[2..-2]}`.chomp
|
16
|
-
end
|
17
|
-
end
|
18
|
-
|
19
|
-
|
20
|
-
# Need to use instance variables in assignments
|
21
|
-
# to maintain binding from one follow up prompt
|
22
|
-
# to another.
|
23
|
-
def render_erb(the_prompt_text)
|
24
|
-
ERB.new(the_prompt_text).result(binding)
|
25
|
-
end
|
26
|
-
end
|
data/lib/aia/logging.rb
DELETED
@@ -1,62 +0,0 @@
|
|
1
|
-
# lib/aia/logging.rb
|
2
|
-
|
3
|
-
require 'logger'
|
4
|
-
|
5
|
-
class AIA::Logging
|
6
|
-
attr_accessor :logger
|
7
|
-
|
8
|
-
def initialize(log_file_path)
|
9
|
-
@logger = if log_file_path
|
10
|
-
Logger.new(
|
11
|
-
log_file_path, # path/to/file
|
12
|
-
'weekly', # rotation interval
|
13
|
-
'a' # append to existing file
|
14
|
-
)
|
15
|
-
else
|
16
|
-
# SMELL: Looks like you get logging whether you want it or not
|
17
|
-
# TODO: when path is nil create a fake logger
|
18
|
-
# that does nothing
|
19
|
-
Logger.new(STDOUT) # Fall back to standard output if path is nil or invalid
|
20
|
-
end
|
21
|
-
|
22
|
-
configure_logger
|
23
|
-
end
|
24
|
-
|
25
|
-
def prompt_result(prompt, result)
|
26
|
-
logger.info( <<~EOS
|
27
|
-
PROMPT ID #{prompt.id}
|
28
|
-
PATH: #{prompt.path}
|
29
|
-
KEYWORDS: #{prompt.keywords.join(', ')}
|
30
|
-
|
31
|
-
#{prompt.to_s}
|
32
|
-
|
33
|
-
RESULT:
|
34
|
-
#{result}
|
35
|
-
|
36
|
-
|
37
|
-
EOS
|
38
|
-
)
|
39
|
-
rescue StandardError => e
|
40
|
-
logger.error("Failed to log the result. Error: #{e.message}")
|
41
|
-
end
|
42
|
-
|
43
|
-
|
44
|
-
def debug(msg) = logger.debug(msg)
|
45
|
-
def info(msg) = logger.info(msg)
|
46
|
-
def warn(msg) = logger.warn(msg)
|
47
|
-
def error(msg) = logger.error(msg)
|
48
|
-
def fatal(msg) = logger.fatal(msg)
|
49
|
-
|
50
|
-
private
|
51
|
-
|
52
|
-
def configure_logger
|
53
|
-
@logger.formatter = proc do |severity, datetime, _progname, msg|
|
54
|
-
formatted_datetime = datetime.strftime("%Y-%m-%d %H:%M:%S")
|
55
|
-
"[#{formatted_datetime}] #{severity}: #{msg}\n"
|
56
|
-
end
|
57
|
-
@logger.level = Logger::DEBUG
|
58
|
-
end
|
59
|
-
end
|
60
|
-
|
61
|
-
|
62
|
-
|