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.
@@ -0,0 +1,58 @@
1
+ # lib/aia/external/sgpt.rb
2
+
3
+ class AIA::External::Sgpt < AIA::External::Tool
4
+ def initialize
5
+ super
6
+ @role = :backend
7
+ @desc = "shell-gpt"
8
+ @url = "https://github.com/TheR1D/shell_gpt"
9
+ @install = "pip install shell-gpt"
10
+ end
11
+ end
12
+
13
+ __END__
14
+
15
+ Usage: sgpt [OPTIONS] [PROMPT]
16
+
17
+ ╭─ Arguments ─────────────────────────────────────────────────────────────────────────────────╮
18
+ │ prompt [PROMPT] The prompt to generate completions for. │
19
+ ╰─────────────────────────────────────────────────────────────────────────────────────────────╯
20
+ ╭─ Options ───────────────────────────────────────────────────────────────────────────────────╮
21
+ │ --model TEXT Large language model to use. │
22
+ │ [default: gpt-3.5-turbo] │
23
+ │ --temperature FLOAT RANGE [0.0<=x<=2.0] Randomness of generated │
24
+ │ output. │
25
+ │ [default: 0.1] │
26
+ │ --top-probability FLOAT RANGE [0.1<=x<=1.0] Limits highest probable │
27
+ │ tokens (words). │
28
+ │ [default: 1.0] │
29
+ │ --editor --no-editor Open $EDITOR to provide a │
30
+ │ prompt. │
31
+ │ [default: no-editor] │
32
+ │ --cache --no-cache Cache completion results. │
33
+ │ [default: cache] │
34
+ │ --help Show this message and exit. │
35
+ ╰─────────────────────────────────────────────────────────────────────────────────────────────╯
36
+ ╭─ Assistance Options ────────────────────────────────────────────────────────────────────────╮
37
+ │ --shell -s Generate and execute shell commands. │
38
+ │ --describe-shell -d Describe a shell command. │
39
+ │ --code --no-code Generate only code. [default: no-code] │
40
+ ╰─────────────────────────────────────────────────────────────────────────────────────────────╯
41
+ ╭─ Chat Options ──────────────────────────────────────────────────────────────────────────────╮
42
+ │ --chat TEXT Follow conversation with id, use "temp" for quick │
43
+ │ session. │
44
+ │ [default: None] │
45
+ │ --repl TEXT Start a REPL (Read–eval–print loop) session. │
46
+ │ [default: None] │
47
+ │ --show-chat TEXT Show all messages from provided chat id. │
48
+ │ [default: None] │
49
+ │ --list-chats --no-list-chats List all existing chat ids. │
50
+ │ [default: no-list-chats] │
51
+ ╰─────────────────────────────────────────────────────────────────────────────────────────────╯
52
+ ╭─ Role Options ──────────────────────────────────────────────────────────────────────────────╮
53
+ │ --role TEXT System role for GPT model. [default: None] │
54
+ │ --create-role TEXT Create role. [default: None] │
55
+ │ --show-role TEXT Show role. [default: None] │
56
+ │ --list-roles --no-list-roles List roles. [default: no-list-roles] │
57
+ ╰─────────────────────────────────────────────────────────────────────────────────────────────╯
58
+
@@ -0,0 +1,47 @@
1
+ # lib/aia/external/subl.rb
2
+
3
+ class AIA::External::Subl < AIA::External::Tool
4
+ def initialize
5
+ super
6
+ @role = :editor
7
+ @desc = "Sublime Text Editor"
8
+ @url = "https://www.sublimetext.com/"
9
+ @install = "echo 'Download from website'"
10
+ end
11
+
12
+
13
+ def open(file)
14
+ `#{name} #{file}`
15
+ end
16
+ end
17
+
18
+ __END__
19
+
20
+ $ subl --help
21
+ Sublime Text build 4166
22
+
23
+ Usage: subl [arguments] [files] Edit the given files
24
+ or: subl [arguments] [directories] Open the given directories
25
+ or: subl [arguments] -- [files] Edit files that may start with '-'
26
+ or: subl [arguments] - Edit stdin
27
+ or: subl [arguments] - >out Edit stdin and write the edit to stdout
28
+
29
+ Arguments:
30
+ --project <project>: Load the given project
31
+ --command <command>: Run the given command
32
+ -n or --new-window: Open a new window
33
+ --launch-or-new-window: Only open a new window if the application is open
34
+ -a or --add: Add folders to the current window
35
+ -w or --wait: Wait for the files to be closed before returning
36
+ -b or --background: Don't activate the application
37
+ -s or --stay: Keep the application activated after closing the file
38
+ --safe-mode: Launch using a sandboxed (clean) environment
39
+ -h or --help: Show help (this message) and exit
40
+ -v or --version: Show version and exit
41
+
42
+ --wait is implied if reading from stdin. Use --stay to not switch back
43
+ to the terminal when a file is closed (only relevant if waiting for a file).
44
+
45
+ Filenames may be given a :line or :line:column suffix
46
+
47
+
@@ -0,0 +1,37 @@
1
+ If you want to find out which classes have inherited from a class named `Tool`, you can leverage Ruby's `ObjectSpace` module to iterate through all the classes and select those that are descendants of `Tool`. Here's how you could write the code:
2
+
3
+ ```ruby
4
+ class Tool
5
+ # Tool class implementation
6
+ end
7
+
8
+ # Other classes that inherit from Tool
9
+ class Hammer < Tool; end
10
+ class Screwdriver < Tool; end
11
+ class Wrench < Tool; end
12
+
13
+ # Non-inheriting classes
14
+ class RandomClass; end
15
+
16
+ def find_descendants_of(klass)
17
+ ObjectSpace.each_object(Class).select { |c| c < klass }.map(&:name)
18
+ end
19
+
20
+ # Get the list of class names that inherit from Tool
21
+ descendant_classes = find_descendants_of(Tool)
22
+
23
+ # Format the list as markdown (as required)
24
+ markdown_list = descendant_classes.map { |name| "- #{name}" }.join("\n")
25
+ puts markdown_list
26
+ ```
27
+
28
+ When you run this code, you will get an output similar to the following (the actual order may vary):
29
+
30
+ ```
31
+ - Hammer
32
+ - Screwdriver
33
+ - Wrench
34
+ ```
35
+
36
+ This list shows the class names that have inherited from the `Tool` class, and it is formatted as a markdown list without enclosing backticks as requested.
37
+
@@ -0,0 +1,73 @@
1
+ # lib/aia/external/tool.rb
2
+
3
+ class AIA::External::Tool
4
+ @@subclasses = []
5
+
6
+ # This method is called whenever a subclass is created
7
+ def self.inherited(subclass)
8
+ @@subclasses << subclass
9
+ end
10
+
11
+
12
+ attr_reader :name, :description, :url
13
+
14
+ def initialize
15
+ @role = :role
16
+ @name = self.class.name.split('::').last.downcase
17
+ @desc = "description"
18
+ @url = "URL"
19
+ @install = "brew install #{name}"
20
+ end
21
+
22
+
23
+ def self.tools
24
+ @@subclasses.map(&:name)
25
+ end
26
+
27
+
28
+ def installed?
29
+ path = `which #{name}`.chomp
30
+ !path.empty? && File.executable?(path)
31
+ end
32
+
33
+
34
+ def help = `#{name} --help`
35
+ def version = `#{name} --version`
36
+
37
+
38
+ ###################################################
39
+ class << self
40
+ def verify_tools(tools)
41
+ missing_tools = tools.reject(&:installed?)
42
+ unless missing_tools.empty?
43
+ puts format_missing_tools_response(missing_tools)
44
+ end
45
+ end
46
+
47
+
48
+ def format_missing_tools_response(missing_tools)
49
+ response = <<~EOS
50
+
51
+ WARNING: AIA makes use of external CLI tools that are missing.
52
+
53
+ Please install the following tools:
54
+
55
+ EOS
56
+
57
+ missing_tools.each do |tool|
58
+ response << " #{tool.name}: install from #{tool.url}\n"
59
+ end
60
+
61
+ response
62
+ end
63
+ end
64
+ end
65
+
66
+
67
+ Pathname.new(__dir__)
68
+ .glob('*.rb')
69
+ .reject{|f| f.basename.to_s.end_with?('tool.rb') }
70
+ .each do |tool|
71
+ require_relative tool.basename.to_s
72
+ end
73
+
@@ -0,0 +1,259 @@
1
+ # lib/aia/external.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
+ =begin
8
+
9
+ There are at least 4 processes handled by external tools:
10
+
11
+ search .......... default PromptManager::Prompt or search_proc
12
+ review/select ... using fzf either exact or fuzzy
13
+ edit ............ ENV['EDITOR']
14
+ execute ......... mods or sgpt or ???
15
+ with different models / settings
16
+
17
+ sgpt is the executable for "shell-gpt" a python project
18
+
19
+ =end
20
+
21
+ module AIA::External
22
+ class Mods; end
23
+ class Fzf; end
24
+ class Rg; end
25
+ class Editor; end
26
+ end
27
+
28
+ module AIA::External
29
+ TOOLS = {
30
+ 'fzf' => [ 'Command-line fuzzy finder written in Go',
31
+ 'https://github.com/junegunn/fzf'],
32
+
33
+ 'mods' => [ 'AI on the command-line',
34
+ 'https://github.com/charmbracelet/mods'],
35
+
36
+ 'rg' => [ 'Search tool like grep and The Silver Searcher',
37
+ 'https://github.com/BurntSushi/ripgrep']
38
+ }
39
+
40
+
41
+ HELP = <<~EOS
42
+ External Tools Used
43
+ -------------------
44
+
45
+ To install the external CLI programs used by aia:
46
+ brew install #{TOOLS.keys.join(' ')}
47
+
48
+ #{TOOLS.to_a.map{|t| t.join("\n ") }.join("\n\n")}
49
+
50
+ A text editor whose executable is setup in the
51
+ system environment variable 'EDITOR' like this:
52
+
53
+ export EDITOR="#{ENV['EDITOR']}"
54
+
55
+ EOS
56
+
57
+
58
+ # Setup the AI CLI program with necessary variables
59
+ def setup_external_programs
60
+ verify_external_tools
61
+
62
+ ai_default_opts = "-m #{MODS_MODEL} --no-limit "
63
+ ai_default_opts += "-f " if markdown?
64
+ @ai_options = ai_default_opts.dup
65
+
66
+
67
+ @ai_options += @extra_options.join(' ')
68
+
69
+ @ai_command = "#{AI_CLI_PROGRAM} #{@ai_options} "
70
+ end
71
+
72
+
73
+ # Check if the external tools are present on the system
74
+ def verify_external_tools
75
+ missing_tools = []
76
+
77
+ TOOLS.each do |tool, url|
78
+ path = `which #{tool}`.chomp
79
+ if path.empty? || !File.executable?(path)
80
+ missing_tools << { name: tool, url: url }
81
+ end
82
+ end
83
+
84
+ if missing_tools.any?
85
+ puts format_missing_tools_response(missing_tools)
86
+ end
87
+ end
88
+
89
+
90
+ def format_missing_tools_response(missing_tools)
91
+ response = <<~EOS
92
+
93
+ WARNING: #{MY_NAME} makes use of a few external CLI tools.
94
+ #{MY_NAME} may not respond as designed without these.
95
+
96
+ The following tools are missing on your system:
97
+
98
+ EOS
99
+
100
+ missing_tools.each do |tool|
101
+ response << " #{tool[:name]}: install from #{tool[:url]}\n"
102
+ end
103
+
104
+ response
105
+ end
106
+
107
+
108
+ # Build the command to interact with the AI CLI program
109
+ def build_command
110
+ command = @ai_command + %Q["#{@prompt.to_s}"]
111
+
112
+ @arguments.each do |input_file|
113
+ file_path = Pathname.new(input_file)
114
+ abort("File does not exist: #{input_file}") unless file_path.exist?
115
+ command += " < #{input_file}"
116
+ end
117
+
118
+ command
119
+ end
120
+
121
+
122
+ # Execute the command and log the results
123
+ def send_prompt_to_external_command
124
+ command = build_command
125
+
126
+ puts command if verbose?
127
+ @result = `#{command}`
128
+
129
+ if output.nil?
130
+ puts @result
131
+ else
132
+ output.write @result
133
+ end
134
+
135
+ @result
136
+ end
137
+ end
138
+
139
+
140
+ __END__
141
+
142
+
143
+
144
+ MODS_MODEL = ENV['MODS_MODEL'] || 'gpt-4-1106-preview'
145
+
146
+ AI_CLI_PROGRAM = "mods"
147
+ ai_default_opts = "-m #{MODS_MODEL} --no-limit -f"
148
+ ai_options = ai_default_opts.dup
149
+
150
+ extra_inx = ARGV.index('--')
151
+
152
+ if extra_inx
153
+ ai_options += " " + ARGV[extra_inx+1..].join(' ')
154
+ ARGV.pop(ARGV.size - extra_inx)
155
+ end
156
+
157
+ AI_COMMAND = "#{AI_CLI_PROGRAM} #{ai_options} "
158
+ EDITOR = ENV['EDITOR']
159
+ PROMPT_DIR = HOME + ".prompts"
160
+ PROMPT_LOG = PROMPT_DIR + "_prompts.log"
161
+ PROMPT_EXTNAME = ".txt"
162
+ DEFAULTS_EXTNAME = ".json"
163
+ # SEARCH_COMMAND = "ag -l"
164
+ KEYWORD_REGEX = /(\[[A-Z _|]+\])/
165
+
166
+ AVAILABLE_PROMPTS = PROMPT_DIR
167
+ .children
168
+ .select{|c| PROMPT_EXTNAME == c.extname}
169
+ .map{|c| c.basename.to_s.split('.')[0]}
170
+
171
+ AVAILABLE_PROMPTS_HELP = AVAILABLE_PROMPTS
172
+ .map{|c| " * " + c}
173
+ .join("\n")
174
+
175
+
176
+ AI_CLI_PROGRAM_HELP = `#{AI_CLI_PROGRAM} --help`
177
+
178
+ HELP = <<EOHELP
179
+ AI CLI Program
180
+ ==============
181
+
182
+ The AI cli program being used is: #{AI_CLI_PROGRAM}
183
+
184
+ The defaul options to #{AI_CLI_PROGRAM} are:
185
+ "#{ai_default_opts}"
186
+
187
+ You can pass additional CLI options to #{AI_CLI_PROGRAM} like this:
188
+ "#{my_name} my options -- options for #{AI_CLI_PROGRAM}"
189
+
190
+ #{AI_CLI_PROGRAM_HELP}
191
+
192
+ EOHELP
193
+
194
+
195
+
196
+ AG_COMMAND = "ag --file-search-regex '\.txt$' e" # searching for the letter "e"
197
+ CD_COMMAND = "cd #{PROMPT_DIR}"
198
+ FIND_COMMAND = "find . -name '*.txt'"
199
+
200
+ FZF_OPTIONS = [
201
+ "--tabstop=2", # 2 soaces for a tab
202
+ "--header='Prompt contents below'",
203
+ "--header-first",
204
+ "--prompt='Search term: '",
205
+ '--delimiter :',
206
+ "--preview 'ww {1}'", # ww comes from the word_wrap gem
207
+ "--preview-window=down:50%:wrap"
208
+ ].join(' ')
209
+
210
+ FZF_OPTIONS += " --exact" unless fuzzy?
211
+
212
+ FZF_COMMAND = "#{CD_COMMAND} ; #{FIND_COMMAND} | fzf #{FZF_OPTIONS}"
213
+ AG_FZF_COMMAND = "#{CD_COMMAND} ; #{AG_COMMAND} | fzf #{FZF_OPTIONS}"
214
+
215
+ # use `ag` ti build a list of text lines from each prompt
216
+ # use `fzf` to search through that list to select a prompt file
217
+
218
+ def ag_fzf = `#{AG_FZF_COMMAND}`.split(':')&.first&.strip&.gsub('.txt','')
219
+
220
+
221
+ if configatron.prompt.empty?
222
+ unless first_argument_is_a_prompt?
223
+ configatron.prompt = ag_fzf
224
+ end
225
+ end
226
+
227
+ ###############################################
228
+
229
+
230
+
231
+ #!/usr/bin/env bash
232
+ # ~/scripts/ripfzfsubl
233
+ #
234
+ # Uses Sublime Text (subl) as the text editor
235
+ #
236
+ # brew install bat ripgrep fzf
237
+ #
238
+ # bat Clone of cat(1) with syntax highlighting and Git integration
239
+ # |__ https://github.com/sharkdp/bat
240
+ #
241
+ # ripgrep Search tool like grep and The Silver Searcher
242
+ # |__ https://github.com/BurntSushi/ripgrep
243
+ #
244
+ # fzf Command-line fuzzy finder written in Go
245
+ # |__ https://github.com/junegunn/fzf
246
+ #
247
+ #
248
+ # 1. Search for text in files using Ripgrep
249
+ # 2. Interactively narrow down the list using fzf
250
+ # 3. Open the file in Sublime Text Editor
251
+
252
+ rg --color=always --line-number --no-heading --smart-case "${*:-}" |
253
+ fzf --ansi \
254
+ --color "hl:-1:underline,hl+:-1:underline:reverse" \
255
+ --delimiter : \
256
+ --preview 'bat --color=always {1} --highlight-line {2}' \
257
+ --preview-window 'up,60%,border-bottom,+{2}+3/3,~3' \
258
+ --bind 'enter:become(subl {1}:{2})'
259
+
@@ -0,0 +1,43 @@
1
+ # lib/aia/external_two.rb
2
+ #
3
+ # Maybe something like this ...
4
+ #
5
+ # or a class structure based upon function where the external
6
+ # tool and its default options can be injected.
7
+ #
8
+ module AIA::External
9
+ EDITOR = ENV['EDITOR']
10
+
11
+
12
+ end
13
+
14
+ # Usage example:
15
+
16
+ # Verify and install tools if needed
17
+ mods = AIA::External::Mods.new
18
+ fzf = AIA::External::Fzf.new
19
+ rg = AIA::External::Rg.new
20
+ tools = [mods, fzf, rg]
21
+ AIA::External.verify_tools(tools)
22
+
23
+ # Build command for Mods tool with extra_options
24
+ extra_options = ['--some-extra-option']
25
+ mods_command = mods.command(extra_options)
26
+ puts "Mods command: #{mods_command}"
27
+
28
+ # Open a file with the system editor
29
+ AIA::External::Editor.open('path/to/file.txt')
30
+
31
+ # Search and select a file using Fzf tool
32
+ fzf_options = {
33
+ prompt_dir: 'path/to/prompts',
34
+ fuzzy: true
35
+ }
36
+ fzf_command = fzf.command(fzf_options)
37
+ puts "Fzf command: #{fzf_command}"
38
+
39
+ # Use Rg tool to search within files
40
+ search_term = 'search_query'
41
+ rg_command = rg.command(search_term, fzf_options: fzf.options)
42
+ puts "Rg command: #{rg_command}"
43
+
@@ -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'
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::External
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