aia 0.0.3 → 0.0.5
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 +91 -25
- data/bin/aia +3 -5
- data/lib/aia/cli.rb +209 -0
- data/lib/aia/configuration.rb +44 -0
- data/lib/aia/external_commands.rb +116 -0
- data/lib/aia/logging.rb +22 -0
- data/lib/aia/main.rb +39 -0
- data/lib/aia/prompt_processing.rb +158 -0
- data/lib/aia/version.rb +2 -1
- data/lib/aia.rb +4 -381
- data/lib/modularization_plan.md +126 -0
- metadata +9 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a080fd671c1e5e3f9420f036892afb69a8d4190745da8bbce82620356ae2ad10
|
4
|
+
data.tar.gz: ffe746cbcb3a24216f39f4ec220b962828ab925041fe6f97f006676988006690
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 6ac1c123e391a981800ac8777e31de96cc68af4747e0756902c3f3bd6eaa3fef4c23e425ff7d951c63f93ee5dbb85c1573f8434958dd3e6ce142579f52bf7bd6
|
7
|
+
data.tar.gz: ed7e48ffa949db795884eb0d552a3ac312079dce7152fd45643191f2907ff5a697d334cc26a492cee44a649420fecaf9d6063dfc72ff82e7bd226dc2a77e81c6
|
data/README.md
CHANGED
@@ -31,48 +31,114 @@ TODO: don't forget to mention have access token (API keys) setup as envars for t
|
|
31
31
|
|
32
32
|
## Usage
|
33
33
|
|
34
|
-
|
34
|
+
```text
|
35
|
+
$ aia --help
|
36
|
+
|
37
|
+
aia v0.0.5
|
38
|
+
|
39
|
+
Usage: aia [options] prompt_id [context_file]* [-- external_options+]
|
40
|
+
|
41
|
+
Options
|
42
|
+
-------
|
43
|
+
|
44
|
+
Edit the Prompt File -e --edit
|
45
|
+
default: false
|
46
|
+
|
47
|
+
Turn On Debugging -d --debug
|
48
|
+
default: false
|
49
|
+
|
50
|
+
Be Verbose -v --verbose
|
51
|
+
default: false
|
35
52
|
|
36
|
-
|
53
|
+
Print Version --version
|
54
|
+
default: false
|
55
|
+
|
56
|
+
Show Usage -h --help
|
57
|
+
default: false
|
58
|
+
|
59
|
+
Use Fuzzy Matching --fuzzy
|
60
|
+
default: false
|
61
|
+
|
62
|
+
Out FILENAME -o --output --no-output
|
63
|
+
default: ./temp.md
|
64
|
+
|
65
|
+
Log FILEPATH -l --log --no-log
|
66
|
+
default: $HOME/.prompts/_prompts.log
|
67
|
+
|
68
|
+
Format with Markdown -m --markdown --no-markdown --md --no-md
|
69
|
+
default: true
|
70
|
+
```
|
37
71
|
|
38
|
-
|
39
|
-
TODO: consider a --no-log option to turn off logging
|
72
|
+
Turn on `verbose` with `help` to see more usage information that includes system environment variables and external CLI tools that are used.
|
40
73
|
|
41
|
-
|
74
|
+
```text
|
75
|
+
$ aia --help --verbose
|
76
|
+
```
|
42
77
|
|
43
|
-
|
78
|
+
## System Environment Variables (envars)
|
44
79
|
|
80
|
+
From the verbose help text ...
|
45
81
|
|
46
82
|
```text
|
47
|
-
$ aia -h
|
48
|
-
Use generative AI with saved parameterized prompts
|
49
83
|
|
50
|
-
|
84
|
+
System Environment Variables Used
|
85
|
+
---------------------------------
|
51
86
|
|
52
|
-
|
87
|
+
The OUTPUT and PROMPT_LOG envars can be overridden
|
88
|
+
by cooresponding options on the command line.
|
53
89
|
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
90
|
+
Name Default Value
|
91
|
+
-------------- -------------------------
|
92
|
+
PROMPTS_DIR $HOME/.prompts_dir
|
93
|
+
AI_CLI_PROGRAM mods
|
94
|
+
EDITOR edit
|
95
|
+
MODS_MODEL gpt-4-1106-preview
|
96
|
+
OUTPUT ./temp.md
|
97
|
+
PROMPT_LOG $PROMPTS_DIR/_prompts.log
|
59
98
|
|
60
|
-
|
61
|
-
|
62
|
-
|
99
|
+
These two are required for access the OpenAI
|
100
|
+
services. The have the same value but different
|
101
|
+
programs use different envar names.
|
63
102
|
|
64
|
-
|
65
|
-
|
103
|
+
To get an OpenAI access key/token (same thing)
|
104
|
+
you must first create an account at OpenAI.
|
105
|
+
Here is the link: https://platform.openai.com/docs/overview
|
106
|
+
|
107
|
+
OPENAI_ACCESS_TOKEN
|
108
|
+
OPENAI_API_KEY
|
109
|
+
```
|
66
110
|
|
67
|
-
|
111
|
+
## External CLI Tools Used
|
112
|
+
|
113
|
+
From the verbose help text ...
|
114
|
+
|
115
|
+
```text
|
116
|
+
External Tools Used
|
117
|
+
-------------------
|
68
118
|
|
69
|
-
|
70
|
-
|
119
|
+
To install the external CLI programs used by aia:
|
120
|
+
brew install fzf mods rg
|
121
|
+
|
122
|
+
fzf
|
123
|
+
Command-line fuzzy finder written in Go
|
124
|
+
https://github.com/junegunn/fzf
|
125
|
+
|
126
|
+
mods
|
127
|
+
AI on the command-line
|
128
|
+
https://github.com/charmbracelet/mods
|
129
|
+
|
130
|
+
rg
|
131
|
+
Search tool like grep and The Silver Searcher
|
132
|
+
https://github.com/BurntSushi/ripgrep
|
133
|
+
|
134
|
+
A text editor whose executable is setup in the
|
135
|
+
system environment variable 'EDITOR' like this:
|
136
|
+
|
137
|
+
export EDITOR="subl -w"
|
71
138
|
|
72
|
-
You can pass additional CLI options to mods like this:
|
73
|
-
"aia my options -- options for mods"
|
74
139
|
```
|
75
140
|
|
141
|
+
|
76
142
|
## Development
|
77
143
|
|
78
144
|
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake test` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
data/bin/aia
CHANGED
data/lib/aia/cli.rb
ADDED
@@ -0,0 +1,209 @@
|
|
1
|
+
# lib/aia/cli.rb
|
2
|
+
|
3
|
+
module AIA::Cli
|
4
|
+
def setup_cli_options(args)
|
5
|
+
@arguments = args
|
6
|
+
@options = {
|
7
|
+
# Value
|
8
|
+
edit?: [false, "-e --edit", "Edit the Prompt File"],
|
9
|
+
debug?: [false, "-d --debug", "Turn On Debugging"],
|
10
|
+
verbose?: [false, "-v --verbose", "Be Verbose"],
|
11
|
+
version?: [false, "--version", "Print Version"],
|
12
|
+
help?: [false, "-h --help", "Show Usage"],
|
13
|
+
fuzzy?: [false, "--fuzzy", "Use Fuzzy Matching"],
|
14
|
+
# TODO: Consider dropping output in favor of always
|
15
|
+
# going to STDOUT so user can redirect or pipe somewhere else
|
16
|
+
output: [OUTPUT,"-o --output --no-output", "Out FILENAME"],
|
17
|
+
log: [PROMPT_LOG,"-l --log --no-log", "Log FILEPATH"],
|
18
|
+
markdown?: [true, "-m --markdown --no-markdown --md --no-md", "Format with Markdown"],
|
19
|
+
}
|
20
|
+
|
21
|
+
# Array(String)
|
22
|
+
@extra_options = [] # intended for the backend AI processor
|
23
|
+
|
24
|
+
build_reader_methods # for the @options keys
|
25
|
+
process_arguments
|
26
|
+
end
|
27
|
+
|
28
|
+
|
29
|
+
def usage
|
30
|
+
usage = "\n#{MY_NAME} v#{AIA::VERSION}\n\n"
|
31
|
+
usage += "Usage: #{MY_NAME} [options] prompt_id [context_file]* [-- external_options+]\n\n"
|
32
|
+
usage += usage_options
|
33
|
+
usage += "\n"
|
34
|
+
usage += usage_notes if verbose?
|
35
|
+
|
36
|
+
usage
|
37
|
+
end
|
38
|
+
|
39
|
+
|
40
|
+
def usage_options
|
41
|
+
options = [
|
42
|
+
"Options",
|
43
|
+
"-------",
|
44
|
+
""
|
45
|
+
]
|
46
|
+
|
47
|
+
max_size = @options.values.map{|o| o[2].size}.max + 2
|
48
|
+
|
49
|
+
@options.values.each do |o|
|
50
|
+
pad_size = max_size - o[2].size
|
51
|
+
options << o[2] + (" "*pad_size) + o[1]
|
52
|
+
|
53
|
+
default = o[0]
|
54
|
+
default = "./" + default.basename.to_s if o[1].include?('output')
|
55
|
+
default = default.is_a?(Pathname) ? "$HOME/" + default.relative_path_from(HOME).to_s : default
|
56
|
+
|
57
|
+
options << " default: #{default}\n"
|
58
|
+
end
|
59
|
+
|
60
|
+
options.join("\n")
|
61
|
+
end
|
62
|
+
|
63
|
+
|
64
|
+
def usage_notes
|
65
|
+
<<~EOS
|
66
|
+
#{usage_envars}
|
67
|
+
#{AIA::ExternalCommands::HELP}
|
68
|
+
EOS
|
69
|
+
end
|
70
|
+
|
71
|
+
|
72
|
+
def usage_envars
|
73
|
+
<<~EOS
|
74
|
+
System Environment Variables Used
|
75
|
+
---------------------------------
|
76
|
+
|
77
|
+
The OUTPUT and PROMPT_LOG envars can be overridden
|
78
|
+
by cooresponding options on the command line.
|
79
|
+
|
80
|
+
Name Default Value
|
81
|
+
-------------- -------------------------
|
82
|
+
PROMPTS_DIR $HOME/.prompts_dir
|
83
|
+
AI_CLI_PROGRAM mods
|
84
|
+
EDITOR edit
|
85
|
+
MODS_MODEL gpt-4-1106-preview
|
86
|
+
OUTPUT ./temp.md
|
87
|
+
PROMPT_LOG $PROMPTS_DIR/_prompts.log
|
88
|
+
|
89
|
+
These two are required for access the OpenAI
|
90
|
+
services. The have the same value but different
|
91
|
+
programs use different envar names.
|
92
|
+
|
93
|
+
To get an OpenAI access key/token (same thing)
|
94
|
+
you must first create an account at OpenAI.
|
95
|
+
Here is the link: https://platform.openai.com/docs/overview
|
96
|
+
|
97
|
+
OPENAI_ACCESS_TOKEN
|
98
|
+
OPENAI_API_KEY
|
99
|
+
|
100
|
+
EOS
|
101
|
+
end
|
102
|
+
|
103
|
+
|
104
|
+
def build_reader_methods
|
105
|
+
@options.keys.each do |key|
|
106
|
+
define_singleton_method(key) do
|
107
|
+
@options[key][0]
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
|
113
|
+
def process_arguments
|
114
|
+
@options.keys.each do |option|
|
115
|
+
check_for option
|
116
|
+
end
|
117
|
+
|
118
|
+
# get the options meant for the backend AI command
|
119
|
+
extract_extra_options
|
120
|
+
|
121
|
+
bad_options = @arguments.select{|a| a.start_with?('-')}
|
122
|
+
|
123
|
+
unless bad_options.empty?
|
124
|
+
puts <<~EOS
|
125
|
+
|
126
|
+
ERROR: Unknown options: #{bad_options.join(' ')}
|
127
|
+
|
128
|
+
EOS
|
129
|
+
|
130
|
+
show_usage
|
131
|
+
|
132
|
+
exit
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
|
137
|
+
def check_for(option_sym)
|
138
|
+
boolean = option_sym.to_s.end_with?('?')
|
139
|
+
switches = @options[option_sym][1].split
|
140
|
+
|
141
|
+
switches.each do |switch|
|
142
|
+
if @arguments.include?(switch)
|
143
|
+
index = @arguments.index(switch)
|
144
|
+
|
145
|
+
if boolean
|
146
|
+
@options[option_sym][0] = switch.include?('-no-') ? false : true
|
147
|
+
@arguments.slice!(index,1)
|
148
|
+
else
|
149
|
+
if switch.include?('-no-')
|
150
|
+
@options[option_sym][0] = nil
|
151
|
+
@arguments.slice!(index,1)
|
152
|
+
else
|
153
|
+
@options[option_sym][0] = @arguments[index + 1]
|
154
|
+
@arguments.slice!(index,2)
|
155
|
+
end
|
156
|
+
end
|
157
|
+
|
158
|
+
break
|
159
|
+
end
|
160
|
+
end
|
161
|
+
end
|
162
|
+
|
163
|
+
|
164
|
+
|
165
|
+
def show_usage
|
166
|
+
@options[:help?][0] = false
|
167
|
+
puts usage
|
168
|
+
exit
|
169
|
+
end
|
170
|
+
alias_method :show_help, :show_usage
|
171
|
+
|
172
|
+
|
173
|
+
def show_version
|
174
|
+
puts AIA::VERSION
|
175
|
+
exit
|
176
|
+
end
|
177
|
+
end
|
178
|
+
|
179
|
+
|
180
|
+
__END__
|
181
|
+
|
182
|
+
|
183
|
+
# TODO: Consider using this history process to preload the default
|
184
|
+
# so that an up arrow will bring the previous answer into
|
185
|
+
# the read buffer for line editing.
|
186
|
+
# Instead of usin the .history file just push the default
|
187
|
+
# value from the JSON file.
|
188
|
+
|
189
|
+
while input = Readline.readline('> ', true)
|
190
|
+
# Skip empty entries and duplicates
|
191
|
+
if input.empty? || Readline::HISTORY.to_a[-2] == input
|
192
|
+
Readline::HISTORY.pop
|
193
|
+
end
|
194
|
+
break if input == 'exit'
|
195
|
+
|
196
|
+
# Do something with the input
|
197
|
+
puts "You entered: #{input}"
|
198
|
+
|
199
|
+
# Save the history in case you want to preserve it for the next sessions
|
200
|
+
File.open('.history', 'a') { |f| f.puts(input) }
|
201
|
+
end
|
202
|
+
|
203
|
+
# Load history from file at the beginning of the program
|
204
|
+
if File.exist?('.history')
|
205
|
+
File.readlines('.history').each do |line|
|
206
|
+
Readline::HISTORY.push(line.chomp)
|
207
|
+
end
|
208
|
+
end
|
209
|
+
|
@@ -0,0 +1,44 @@
|
|
1
|
+
# lib/aia/configuration.rb
|
2
|
+
|
3
|
+
HOME = Pathname.new(ENV['HOME'])
|
4
|
+
PROMPTS_DIR = Pathname.new(ENV['PROMPTS_DIR'] || (HOME + ".prompts_dir"))
|
5
|
+
|
6
|
+
AI_CLI_PROGRAM = "mods"
|
7
|
+
EDITOR = ENV['EDITOR'] || 'edit'
|
8
|
+
MY_NAME = "aia"
|
9
|
+
MODS_MODEL = ENV['MODS_MODEL'] || 'gpt-4-1106-preview'
|
10
|
+
OUTPUT = Pathname.pwd + "temp.md"
|
11
|
+
PROMPT_LOG = PROMPTS_DIR + "_prompts.log"
|
12
|
+
|
13
|
+
|
14
|
+
module AIA::Configuration
|
15
|
+
def setup_configuration
|
16
|
+
@prompt = nil
|
17
|
+
|
18
|
+
PromptManager::Prompt.storage_adapter =
|
19
|
+
PromptManager::Storage::FileSystemAdapter.config do |config|
|
20
|
+
config.prompts_dir = PROMPTS_DIR
|
21
|
+
config.prompt_extension = '.txt'
|
22
|
+
config.params_extension = '.json'
|
23
|
+
config.search_proc = nil
|
24
|
+
# TODO: add the rgfzz script for search_proc
|
25
|
+
end.new
|
26
|
+
end
|
27
|
+
|
28
|
+
|
29
|
+
# Get the additional CLI arguments intended for the
|
30
|
+
# backend gen-AI processor.
|
31
|
+
def extract_extra_options
|
32
|
+
extra_index = @arguments.index('--')
|
33
|
+
if extra_index.nil?
|
34
|
+
@extra_options = []
|
35
|
+
else
|
36
|
+
@extra_options = @arguments.slice!(extra_index..-1)[1..]
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
|
41
|
+
|
42
|
+
|
43
|
+
|
44
|
+
end
|
@@ -0,0 +1,116 @@
|
|
1
|
+
# lib/aia/external_commands.rb
|
2
|
+
|
3
|
+
# TODO: move stuff associated with the CLI options for
|
4
|
+
# external commands to this module.
|
5
|
+
# Is the EDITOR considered an external command? Yes.
|
6
|
+
|
7
|
+
module AIA::ExternalCommands
|
8
|
+
TOOLS = {
|
9
|
+
'fzf' => [ 'Command-line fuzzy finder written in Go',
|
10
|
+
'https://github.com/junegunn/fzf'],
|
11
|
+
|
12
|
+
'mods' => [ 'AI on the command-line',
|
13
|
+
'https://github.com/charmbracelet/mods'],
|
14
|
+
|
15
|
+
'rg' => [ 'Search tool like grep and The Silver Searcher',
|
16
|
+
'https://github.com/BurntSushi/ripgrep']
|
17
|
+
}
|
18
|
+
|
19
|
+
|
20
|
+
HELP = <<~EOS
|
21
|
+
External Tools Used
|
22
|
+
-------------------
|
23
|
+
|
24
|
+
To install the external CLI programs used by aia:
|
25
|
+
brew install #{TOOLS.keys.join(' ')}
|
26
|
+
|
27
|
+
#{TOOLS.to_a.map{|t| t.join("\n ") }.join("\n\n")}
|
28
|
+
|
29
|
+
A text editor whose executable is setup in the
|
30
|
+
system environment variable 'EDITOR' like this:
|
31
|
+
|
32
|
+
export EDITOR="#{ENV['EDITOR']}"
|
33
|
+
|
34
|
+
EOS
|
35
|
+
|
36
|
+
|
37
|
+
# Setup the AI CLI program with necessary variables
|
38
|
+
def setup_external_programs
|
39
|
+
verify_external_tools
|
40
|
+
|
41
|
+
ai_default_opts = "-m #{MODS_MODEL} --no-limit "
|
42
|
+
ai_default_opts += "-f " if markdown?
|
43
|
+
@ai_options = ai_default_opts.dup
|
44
|
+
|
45
|
+
|
46
|
+
@ai_options += @extra_options.join(' ')
|
47
|
+
|
48
|
+
@ai_command = "#{AI_CLI_PROGRAM} #{@ai_options} "
|
49
|
+
end
|
50
|
+
|
51
|
+
|
52
|
+
# Check if the external tools are present on the system
|
53
|
+
def verify_external_tools
|
54
|
+
missing_tools = []
|
55
|
+
|
56
|
+
TOOLS.each do |tool, url|
|
57
|
+
path = `which #{tool}`.chomp
|
58
|
+
if path.empty? || !File.executable?(path)
|
59
|
+
missing_tools << { name: tool, url: url }
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
if missing_tools.any?
|
64
|
+
puts format_missing_tools_response(missing_tools)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
|
69
|
+
def format_missing_tools_response(missing_tools)
|
70
|
+
response = <<~EOS
|
71
|
+
|
72
|
+
WARNING: #{MY_NAME} makes use of a few external CLI tools.
|
73
|
+
#{MY_NAME} may not respond as designed without these.
|
74
|
+
|
75
|
+
The following tools are missing on your system:
|
76
|
+
|
77
|
+
EOS
|
78
|
+
|
79
|
+
missing_tools.each do |tool|
|
80
|
+
response << " #{tool[:name]}: install from #{tool[:url]}\n"
|
81
|
+
end
|
82
|
+
|
83
|
+
response
|
84
|
+
end
|
85
|
+
|
86
|
+
|
87
|
+
# Build the command to interact with the AI CLI program
|
88
|
+
def build_command
|
89
|
+
command = @ai_command + %Q["#{@prompt.to_s}"]
|
90
|
+
|
91
|
+
@arguments.each do |input_file|
|
92
|
+
file_path = Pathname.new(input_file)
|
93
|
+
abort("File does not exist: #{input_file}") unless file_path.exist?
|
94
|
+
command += " < #{input_file}"
|
95
|
+
end
|
96
|
+
|
97
|
+
command
|
98
|
+
end
|
99
|
+
|
100
|
+
|
101
|
+
# Execute the command and log the results
|
102
|
+
def send_prompt_to_external_command
|
103
|
+
command = build_command
|
104
|
+
|
105
|
+
puts command if verbose?
|
106
|
+
@result = `#{command}`
|
107
|
+
|
108
|
+
if output.nil?
|
109
|
+
puts @result
|
110
|
+
else
|
111
|
+
output.write @result
|
112
|
+
end
|
113
|
+
|
114
|
+
@result
|
115
|
+
end
|
116
|
+
end
|
data/lib/aia/logging.rb
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
# lib/aia/logging.rb
|
2
|
+
|
3
|
+
module AIA::Logging
|
4
|
+
def log_result
|
5
|
+
return if log.nil?
|
6
|
+
|
7
|
+
f = File.open(log, "ab")
|
8
|
+
|
9
|
+
f.write <<~EOS
|
10
|
+
=======================================
|
11
|
+
== #{Time.now}
|
12
|
+
== #{@prompt.path}
|
13
|
+
|
14
|
+
PROMPT:
|
15
|
+
#{@prompt}
|
16
|
+
|
17
|
+
RESULT:
|
18
|
+
#{@result}
|
19
|
+
|
20
|
+
EOS
|
21
|
+
end
|
22
|
+
end
|
data/lib/aia/main.rb
ADDED
@@ -0,0 +1,39 @@
|
|
1
|
+
# lib/aia/main.rb
|
2
|
+
|
3
|
+
module AIA ; end
|
4
|
+
|
5
|
+
require_relative 'configuration'
|
6
|
+
|
7
|
+
require_relative 'cli'
|
8
|
+
require_relative 'prompt_processing'
|
9
|
+
require_relative 'external_commands'
|
10
|
+
require_relative 'logging'
|
11
|
+
|
12
|
+
# Everything is being handled within the context
|
13
|
+
# of a single class.
|
14
|
+
|
15
|
+
class AIA::Main
|
16
|
+
include AIA::Configuration
|
17
|
+
include AIA::Cli
|
18
|
+
include AIA::PromptProcessing
|
19
|
+
include AIA::ExternalCommands
|
20
|
+
include AIA::Logging
|
21
|
+
|
22
|
+
|
23
|
+
def initialize(args= ARGV)
|
24
|
+
setup_configuration
|
25
|
+
setup_cli_options(args)
|
26
|
+
setup_external_programs
|
27
|
+
end
|
28
|
+
|
29
|
+
|
30
|
+
def call
|
31
|
+
show_usage if help?
|
32
|
+
show_version if version?
|
33
|
+
|
34
|
+
get_prompt
|
35
|
+
process_prompt
|
36
|
+
send_prompt_to_external_command
|
37
|
+
log_result unless log.nil?
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,158 @@
|
|
1
|
+
# lib/aia/prompt_processing.rb
|
2
|
+
|
3
|
+
module AIA::PromptProcessing
|
4
|
+
|
5
|
+
# Fetch the first argument which should be the prompt id
|
6
|
+
def get_prompt
|
7
|
+
prompt_id = @arguments.shift
|
8
|
+
|
9
|
+
# TODO: or maybe go to a generic search and select process
|
10
|
+
|
11
|
+
abort("Please provide a prompt id") unless prompt_id
|
12
|
+
|
13
|
+
search_for_a_matching_prompt(prompt_id) unless existing_prompt?(prompt_id)
|
14
|
+
edit_prompt if edit?
|
15
|
+
end
|
16
|
+
|
17
|
+
|
18
|
+
# Check if a prompt with the given id already exists
|
19
|
+
def existing_prompt?(prompt_id)
|
20
|
+
@prompt = PromptManager::Prompt.get(id: prompt_id)
|
21
|
+
true
|
22
|
+
rescue ArgumentError
|
23
|
+
false
|
24
|
+
end
|
25
|
+
|
26
|
+
|
27
|
+
# Process the prompt's associated keywords and parameters
|
28
|
+
def process_prompt
|
29
|
+
unless @prompt.keywords.empty?
|
30
|
+
replace_keywords
|
31
|
+
@prompt.build
|
32
|
+
@prompt.save
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
|
37
|
+
|
38
|
+
def replace_keywords
|
39
|
+
print "\nQuit #{MY_NAME} with a CNTL-D or a CNTL-C\n\n"
|
40
|
+
|
41
|
+
defaults = @prompt.parameters
|
42
|
+
|
43
|
+
@prompt.keywords.each do |kw|
|
44
|
+
defaults[kw] = keyword_value(kw, defaults[kw])
|
45
|
+
end
|
46
|
+
|
47
|
+
@prompt.parameters = defaults
|
48
|
+
end
|
49
|
+
|
50
|
+
|
51
|
+
|
52
|
+
|
53
|
+
# query the user for a value to the keyword allow the
|
54
|
+
# reuse of the previous value shown as the default
|
55
|
+
def keyword_value(kw, default)
|
56
|
+
label = "Default: "
|
57
|
+
puts "Parameter #{kw} ..."
|
58
|
+
default_wrapped = default.wrap(indent: label.size)
|
59
|
+
default_wrapped[0..label.size] = label
|
60
|
+
puts default_wrapped
|
61
|
+
|
62
|
+
begin
|
63
|
+
a_string = Readline.readline("\n-=> ", false)
|
64
|
+
rescue Interrupt
|
65
|
+
a_string = nil
|
66
|
+
end
|
67
|
+
|
68
|
+
if a_string.nil?
|
69
|
+
puts "okay. Come back soon."
|
70
|
+
exit
|
71
|
+
end
|
72
|
+
|
73
|
+
puts
|
74
|
+
a_string.empty? ? default : a_string
|
75
|
+
end
|
76
|
+
|
77
|
+
|
78
|
+
# Search for a prompt with a matching id or keyword
|
79
|
+
def search_for_a_matching_prompt(prompt_id)
|
80
|
+
# TODO: using the rgfzf version of the search_proc should only
|
81
|
+
# return a single prompt_id
|
82
|
+
found_prompts = PromptManager::Prompt.search(prompt_id)
|
83
|
+
|
84
|
+
if found_prompts.empty?
|
85
|
+
if edit?
|
86
|
+
create_prompt(prompt_id)
|
87
|
+
edit_prompt
|
88
|
+
else
|
89
|
+
abort <<~EOS
|
90
|
+
|
91
|
+
No prompts where found for: #{prompt_id}
|
92
|
+
To create a prompt with this ID use the --edit option
|
93
|
+
like this:
|
94
|
+
#{MY_NAME} #{prompt_id} --edit
|
95
|
+
|
96
|
+
EOS
|
97
|
+
end
|
98
|
+
else
|
99
|
+
prompt_id = 1 == found_prompts.size ? found_prompts.first : handle_multiple_prompts(found_prompts, prompt_id)
|
100
|
+
@prompt = PromptManager::Prompt.get(id: prompt_id)
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
|
105
|
+
def handle_multiple_prompts(found_these, while_looking_for_this)
|
106
|
+
raise ArgumentError, "Argument is not an Array" unless found_these.is_a?(Array)
|
107
|
+
|
108
|
+
# TODO: Make this a class constant for defaults; make the header content
|
109
|
+
# a parameter so it can be varied.
|
110
|
+
fzf_options = [
|
111
|
+
"--tabstop=2", # 2 soaces for a tab
|
112
|
+
"--header='Prompt IDs which contain: #{while_looking_for_this}\nPress ESC to cancel.'",
|
113
|
+
"--header-first",
|
114
|
+
"--prompt='Search term: '",
|
115
|
+
'--delimiter :',
|
116
|
+
"--preview 'cat $PROMPTS_DIR/{1}.txt'",
|
117
|
+
"--preview-window=down:50%:wrap"
|
118
|
+
].join(' ')
|
119
|
+
|
120
|
+
|
121
|
+
# Create a temporary file to hold the list of strings
|
122
|
+
temp_file = Tempfile.new('fzf-input')
|
123
|
+
|
124
|
+
begin
|
125
|
+
# Write all strings to the temp file
|
126
|
+
temp_file.puts(found_these)
|
127
|
+
temp_file.close
|
128
|
+
|
129
|
+
# Execute fzf command-line utility to allow selection
|
130
|
+
selected = `cat #{temp_file.path} | fzf #{fzf_options}`.strip
|
131
|
+
|
132
|
+
# Check if fzf actually returned a string; if not, return nil
|
133
|
+
result = selected.empty? ? nil : selected
|
134
|
+
ensure
|
135
|
+
# Ensure that the tempfile is closed and unlinked
|
136
|
+
temp_file.unlink
|
137
|
+
end
|
138
|
+
|
139
|
+
exit unless result
|
140
|
+
|
141
|
+
result
|
142
|
+
end
|
143
|
+
|
144
|
+
|
145
|
+
def create_prompt(prompt_id)
|
146
|
+
@prompt = PromptManager::Prompt.create(id: prompt_id)
|
147
|
+
# TODO: consider a configurable prompt template
|
148
|
+
# ERB ???
|
149
|
+
end
|
150
|
+
|
151
|
+
|
152
|
+
def edit_prompt
|
153
|
+
`#{EDITOR} #{@prompt.path}`
|
154
|
+
@options[:edit?][0] = false
|
155
|
+
@prompt = PromptManager::Prompt.get(id: @prompt.id)
|
156
|
+
end
|
157
|
+
|
158
|
+
end
|
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
|
-
|
@@ -0,0 +1,126 @@
|
|
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
|
+
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: aia
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.5
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Dewayne VanHoozer
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2023-11-
|
11
|
+
date: 2023-11-25 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: prompt_manager
|
@@ -103,8 +103,15 @@ files:
|
|
103
103
|
- bin/aia
|
104
104
|
- bin/aia_completion.sh
|
105
105
|
- lib/aia.rb
|
106
|
+
- lib/aia/cli.rb
|
107
|
+
- lib/aia/configuration.rb
|
108
|
+
- lib/aia/external_commands.rb
|
109
|
+
- lib/aia/logging.rb
|
110
|
+
- lib/aia/main.rb
|
111
|
+
- lib/aia/prompt_processing.rb
|
106
112
|
- lib/aia/version.rb
|
107
113
|
- lib/core_ext/string_wrap.rb
|
114
|
+
- lib/modularization_plan.md
|
108
115
|
homepage: https://github.com/MadBomber/aia
|
109
116
|
licenses:
|
110
117
|
- MIT
|