aia 0.3.19 → 0.3.20

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 505546a036010eb8b0360f7fbfbb4c6494c311e98969c8095fa091882815aac2
4
- data.tar.gz: 6f9323e50879c3b51a5ead99c29b1b41fc377dc1957283edb13e03bb30c866db
3
+ metadata.gz: b3061141e7330122b3ec6ffd64cf967d8fb0c4f8741531337bf32bfba28ee4ea
4
+ data.tar.gz: b62080a48b5d4afefb9da0eb5bdc1deff20a8c666048a755b38f759815bea8f3
5
5
  SHA512:
6
- metadata.gz: 23926f77a010a023fd1fbbde152f83cfacd75f003bb2e060d4e2b4ca0cd7aecac6631b0520c5e3c6d56cbde31914f158f520f15bfa239a1a72bf682c5ea79032
7
- data.tar.gz: 9d76ab35f70a13a4320d9fb4ee5f3fd47e4039c95e99fc02ce858977e179d1891d44fb08cf7cfc17975e6828dbe28d01d9d778d949c371e898e847e7fa865020
6
+ metadata.gz: c2e83d7db907fe2e3c91aae7acad12bf1569071479f99ba70f3626c98640323148e83b46cda2aa2353afcad54d740116999399e8e646b7db02b4a7e42916d0fc
7
+ data.tar.gz: 3f327c042a67a574231d9e25a5ed8df83ca6d4cf28444ae92be20d241480143ce95ec4e4238aab70caecf0c9c64417d579c6e8fd3dd3e7928c64f8e56fb2196a
data/.semver CHANGED
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  :major: 0
3
3
  :minor: 3
4
- :patch: 19
4
+ :patch: 20
5
5
  :special: ''
6
6
  :metadata: ''
data/CHANGELOG.md CHANGED
@@ -1,4 +1,8 @@
1
1
  ## [Unreleased]
2
+ ## [0.3.20] 2023-12-28
3
+ - added work around to issue with multiple context files going to the `mods` backend
4
+ - added shellwords gem to santize prompt text on the command line
5
+
2
6
  ## [0.3.19] 2023-12-26
3
7
  - major code refactoring.
4
8
  - supports config files \*.yml, \*.yaml and \*.toml
data/lib/aia/main.rb CHANGED
@@ -20,9 +20,10 @@ class AIA::Main
20
20
  AIA::Cli.new(args)
21
21
 
22
22
  @logger = AIA::Logging.new(AIA.config.log_file)
23
- @tools = AIA::Tools.new
23
+ AIA::Tools.load_tools
24
24
 
25
- tools.class.verify_tools
25
+ # TODO: still should verify that the tools are ion the $PATH
26
+ # tools.class.verify_tools
26
27
  end
27
28
 
28
29
 
@@ -37,14 +38,30 @@ class AIA::Main
37
38
  # prompt keyword or process the prompt. Do not
38
39
  # want invalid files to make it this far.
39
40
 
41
+ found = AIA::Tools
42
+ .search_for(
43
+ name: AIA.config.backend,
44
+ role: :backend
45
+ )
46
+
47
+ if found.empty?
48
+ abort "There are no :backend tools named #{AIA.config.backend}"
49
+ end
50
+
51
+ if found.size > 1
52
+ abort "There are #{found.size} :backend tools with the name #{AIAA.config.backend}"
53
+ end
54
+
55
+ backend_klass = found.first.klass
56
+
57
+ abort "backend not found: #{AIA.config.backend}" if backend_klass.nil?
40
58
 
41
- mods = AIA::Mods.new(
42
- extra_options: AIA.config.extra,
59
+ backend = backend_klass.new(
43
60
  text: @prompt.to_s,
44
61
  files: AIA.config.arguments # FIXME: want validated context files
45
62
  )
46
63
 
47
- result = mods.run
64
+ result = backend.run
48
65
 
49
66
  AIA.config.output_file.write result
50
67
 
@@ -4,19 +4,21 @@
4
4
 
5
5
 
6
6
  class AIA::Editor < AIA::Tools
7
+
8
+ meta(
9
+ name: 'editor',
10
+ role: :editor,
11
+ desc: "Your default system $EDITOR",
12
+ url: "unknown",
13
+ install: "should already be installed",
14
+ )
15
+
7
16
  DEFAULT_PARAMETERS = ""
8
17
 
9
18
  attr_accessor :command
10
19
 
11
20
 
12
- def initialize(file: "")
13
- super
14
-
15
- @role = :editor
16
- @description = "Your default system $EDITOR"
17
- @url = "unknown"
18
- @install = "should already be installed"
19
-
21
+ def initialize(file: "")
20
22
  @file = file
21
23
 
22
24
  discover_editor
@@ -39,7 +41,7 @@ class AIA::Editor < AIA::Tools
39
41
 
40
42
 
41
43
  def build_command
42
- @command = "#{name} #{DEFAULT_PARAMETERS} #{@file}"
44
+ @command = "#{meta.name} #{DEFAULT_PARAMETERS} #{@file}"
43
45
  end
44
46
 
45
47
 
@@ -1,29 +1,28 @@
1
1
  # lib/aia/tools/mods.rb
2
2
 
3
3
  class AIA::Mods < AIA::Tools
4
+
5
+ meta(
6
+ name: 'mods',
7
+ role: :backend,
8
+ desc: 'AI on the command-line',
9
+ url: 'https://github.com/charmbracelet/mods',
10
+ install: 'brew install mods',
11
+ )
12
+
13
+
4
14
  DEFAULT_PARAMETERS = [
5
15
  "--no-limit" # no limit on input context
6
16
  ].join(' ').freeze
7
17
 
8
- attr_accessor :command, :extra_options, :text, :files
9
-
10
- # TODO: put the prompt text to be resolved into a
11
- # temporary text file then cat that file into mods.
12
- # This will keep from polluting the CLI history with
13
- # lots of text
18
+ attr_accessor :command, :text, :files
14
19
 
15
20
 
16
21
  def initialize(
17
- extra_options: "", # everything after -- on command line
18
22
  text: "", # prompt text after keyword replacement
19
23
  files: [] # context file paths (Array of Pathname)
20
24
  )
21
- super
22
- @role = :gen_ai
23
- @description = 'AI on the command-line'
24
- @url = 'https://github.com/charmbracelet/mods'
25
-
26
- @extra_options = extra_options
25
+
27
26
  @text = text
28
27
  @files = files
29
28
 
@@ -31,46 +30,84 @@ class AIA::Mods < AIA::Tools
31
30
  end
32
31
 
33
32
 
33
+ def sanitize(input)
34
+ Shellwords.escape(input)
35
+ end
36
+
37
+
34
38
  def build_command
35
39
  parameters = DEFAULT_PARAMETERS.dup + " "
36
- parameters += "-f " if ::AIA.config.markdown?
37
- parameters += "-m #{AIA.config.model} " if ::AIA.config.model
38
- parameters += @extra_options
40
+ parameters += "-f " if AIA.config.markdown?
41
+ parameters += "-m #{AIA.config.model} " if AIA.config.model
42
+ parameters += AIA.config.extra
39
43
  @command = "mods #{parameters} "
40
- @command += %Q["#{@text}"] # TODO: consider using the pipeline
41
-
42
- @files.each {|f| @command += " < #{f}" }
44
+ @command += sanitize(@text)
45
+
46
+ # context = @files.join(' ')
47
+ #
48
+ # unless context.empty?
49
+ # if @files.size > 1
50
+ # # FIXME: This syntax breaks mods which does not know how
51
+ # # to read the temporary file descriptor created
52
+ # # by the shell
53
+ # @command += " <(cat #{context})"
54
+ # else
55
+ # @command += " < #{context}"
56
+ # end
57
+ # end
43
58
 
44
59
  @command
45
60
  end
46
61
 
47
62
 
48
63
  def run
49
- `#{command}`
64
+ case @files.size
65
+ when 0
66
+ @result = `#{build_command}`
67
+ when 1
68
+ @result = `#{build_command} < #{@files.first}`
69
+ else
70
+ create_temp_file_with_contexts
71
+ run_mods_with_temp_file
72
+ clean_up_temp_file
73
+ end
74
+
75
+ @result
50
76
  end
51
- end
52
-
53
- __END__
54
-
77
+
78
+
79
+ # Create a temporary file that concatenates all contexts,
80
+ # to be used as STDIN for the 'mods' utility
81
+ def create_temp_file_with_contexts
82
+ @temp_file = Tempfile.new('mods-context')
83
+
84
+ @files.each do |file|
85
+ content = File.read(file)
86
+ @temp_file.write(content)
87
+ @temp_file.write("\n")
88
+ end
55
89
 
56
- # Execute the command and log the results
57
- def send_prompt_to_external_command
58
- command = build_command
90
+ @temp_file.close
91
+ end
92
+
59
93
 
60
- puts command if verbose?
94
+ # Run 'mods' with the temporary file as STDIN
95
+ def run_mods_with_temp_file
96
+ command = "#{build_command} < #{@temp_file.path}"
61
97
  @result = `#{command}`
98
+ end
99
+
62
100
 
63
- if @output.nil?
64
- puts @result
65
- else
66
- @output.write @result
67
- end
68
-
69
- @result
101
+ # Clean up the temporary file after use
102
+ def clean_up_temp_file
103
+ @temp_file.unlink if @temp_file
70
104
  end
105
+ end
71
106
 
107
+ __END__
72
108
 
73
109
 
110
+
74
111
 
75
112
 
76
113
  ##########################################################
@@ -1,12 +1,17 @@
1
1
  # lib/aia/tools/sgpt.rb
2
2
 
3
3
  class AIA::Sgpt < AIA::Tools
4
+
5
+ meta(
6
+ name: 'sgpt',
7
+ role: :backend,
8
+ desc: "shell-gpt",
9
+ url: "https://github.com/TheR1D/shell_gpt",
10
+ install: "pip install shell-gpt",
11
+ )
12
+
4
13
  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"
14
+ # TODO: something
10
15
  end
11
16
  end
12
17
 
@@ -1,6 +1,16 @@
1
1
  # lib/aia/tools/subl.rb
2
2
 
3
3
  class AIA::Subl < AIA::Tools
4
+
5
+ meta(
6
+ name: 'subl',
7
+ role: :editor,
8
+ desc: "Sublime Text Editor",
9
+ url: "https://www.sublimetext.com/",
10
+ install: "echo 'Download from website'",
11
+ )
12
+
13
+
4
14
  DEFAULT_PARAMETERS = [
5
15
  "--new-window", # Open a new window
6
16
  "--wait", # Wait for the files to be closed before returning
@@ -10,13 +20,6 @@ class AIA::Subl < AIA::Tools
10
20
 
11
21
 
12
22
  def initialize(file: "")
13
- super
14
-
15
- @role = :editor
16
- @desc = "Sublime Text Editor"
17
- @url = "https://www.sublimetext.com/"
18
- @install = "echo 'Download from website'"
19
-
20
23
  @file = file
21
24
 
22
25
  build_command
@@ -24,7 +27,7 @@ class AIA::Subl < AIA::Tools
24
27
 
25
28
 
26
29
  def build_command
27
- @command = "#{name} #{DEFAULT_PARAMETERS} #{@file}"
30
+ @command = "#{meta.name} #{DEFAULT_PARAMETERS} #{@file}"
28
31
  end
29
32
 
30
33
 
@@ -0,0 +1,97 @@
1
+ # lib/aia/tools.rb
2
+
3
+ ```ruby
4
+ require 'hashie'
5
+
6
+ module AIA
7
+ class Tools
8
+ @subclasses = {}
9
+
10
+ class << self
11
+ attr_reader :subclasses, :metadata
12
+
13
+ def inherited(subclass)
14
+ @subclasses[subclass.name.split('::').last.downcase] = subclass
15
+ subclass.instance_variable_set(:@metadata, Jashie::Mash.new)
16
+ end
17
+
18
+ def meta
19
+ @metadata ||= Jashie::Mash.new
20
+ end
21
+
22
+ def define_metadata(&block)
23
+ meta.instance_eval(&block)
24
+ end
25
+
26
+ def search_for(name: nil, role: nil)
27
+ return subclasses[name.downcase] if name
28
+ return subclasses.values.select { |subclass| subclass.meta.role == role } if role
29
+ end
30
+ end
31
+
32
+ def self.method_missing(name, *args, &block)
33
+ @metadata.public_send(name, *args, &block)
34
+ end
35
+
36
+ def self.respond_to_missing?(method_name, include_private = false)
37
+ @metadata.respond_to?(method_name) || super
38
+ end
39
+ end
40
+ end
41
+ ```
42
+
43
+ # lib/aia/tools/mods.rb
44
+
45
+ ```ruby
46
+ require_relative 'tools'
47
+
48
+ module AIA
49
+ class Mods < Tools
50
+ DEFAULT_PARAMETERS = "--no-limit".freeze
51
+
52
+ attr_accessor :command, :extra_options, :text, :files
53
+
54
+ define_metadata do
55
+ role :backend
56
+ desc 'AI on the command-line'
57
+ url 'https://github.com/charmbracelet/mods'
58
+ end
59
+
60
+ def initialize(extra_options: "", text: "", files: [])
61
+ @extra_options = extra_options
62
+ @text = text
63
+ @files = files
64
+ build_command
65
+ end
66
+
67
+ def build_command
68
+ parameters = DEFAULT_PARAMETERS.dup + " "
69
+ parameters += "-f " if ::AIA.config.markdown?
70
+ parameters += "-m #{AIA.config.model} " if ::AIA.config.model
71
+ parameters += @extra_options
72
+ @command = "mods #{parameters}"
73
+ @command += %Q["#{@text}"]
74
+
75
+ @files.each { |f| @command += " < #{f}" }
76
+
77
+ @command
78
+ end
79
+
80
+ def run
81
+ `#{@command}`
82
+ end
83
+ end
84
+ end
85
+ ```
86
+
87
+ ```ruby
88
+ # Example usage:
89
+ # mods_class = AIA::Tools.search_for(name: 'mods')
90
+ # mods_instance = mods_class.new(text: "Hello, mods!")
91
+ # result = mods_instance.run
92
+
93
+ # backend_tools = AIA::Tools.search_for(role: :backend)
94
+ ```
95
+
96
+ Note: The `Jashie::Mash` class is assumed to behave like `Hashie::Mash` (or similar) in providing a flexible object for storing metadata. You'll need to define `Jashie::Mash` or import a library that provides a similar functionality to match this example.
97
+
data/lib/aia/tools/vim.rb CHANGED
@@ -1,6 +1,15 @@
1
1
  # lib/aia/tools/vim.rb
2
2
 
3
3
  class AIA::Vim < AIA::Tools
4
+
5
+ meta(
6
+ name: 'vim',
7
+ role: :editor,
8
+ desc: "Vi IMproved (VIM)",
9
+ url: "https://www.vim.org",
10
+ install: "brew install vim",
11
+ )
12
+
4
13
  DEFAULT_PARAMETERS = [
5
14
  " ", # no parameters
6
15
  ].join(' ')
@@ -9,13 +18,6 @@ class AIA::Vim < AIA::Tools
9
18
 
10
19
 
11
20
  def initialize(file: "")
12
- super
13
-
14
- @role = :editor
15
- @description = "Vi IMproved (VIM)"
16
- @url = "https://www.vim.org"
17
- @install = "brew install vim"
18
-
19
21
  @file = file
20
22
 
21
23
  build_command
@@ -23,7 +25,7 @@ class AIA::Vim < AIA::Tools
23
25
 
24
26
 
25
27
  def build_command
26
- @command = "#{name} #{DEFAULT_PARAMETERS} #{@file}"
28
+ @command = "#{meta.name} #{DEFAULT_PARAMETERS} #{@file}"
27
29
  end
28
30
 
29
31
 
data/lib/aia/tools.rb CHANGED
@@ -1,77 +1,50 @@
1
1
  # lib/aia/tools.rb
2
2
 
3
- class AIA::Tools
4
- @@subclasses = []
5
-
6
- def self.inherited(subclass)
7
- @@subclasses << subclass
8
- end
3
+ require 'hashie'
9
4
 
10
- attr_accessor :role, :name, :description, :url, :install
11
-
12
-
13
- def initialize(*)
14
- @role = :role
15
- @name = self.class.name.split('::').last.downcase
16
- @description = "description"
17
- @url = "URL"
18
- @install = "brew install #{name}"
19
- end
5
+ class AIA::Tools
6
+ @@catalog = []
20
7
 
8
+ class << self
9
+ def inherited(subclass)
10
+ subclass_meta = Hashie::Mash.new(klass: subclass)
11
+ subclass.instance_variable_set(:@_metadata, subclass_meta)
21
12
 
22
- def installed?
23
- path = `which #{name}`.chomp
24
- !path.empty? && File.executable?(path)
25
- end
13
+ @@catalog << subclass_meta
14
+ end
26
15
 
27
16
 
28
- def help
29
- `#{name} --help`
30
- end
31
-
17
+ def meta(metadata = nil)
18
+ return @_metadata if metadata.nil?
32
19
 
33
- def version
34
- `#{name} --version`
35
- end
20
+ @_metadata = Hashie::Mash.new(metadata)
21
+ entry = @@catalog.detect { |item| item[:klass] == self }
22
+
23
+ entry.merge!(metadata) if entry
24
+ end
36
25
 
37
26
 
38
- #########################################
39
- class << self
40
- def tools
41
- @@subclasses.map(&:name)
27
+ def get_meta
28
+ @_metadata
42
29
  end
43
30
 
44
31
 
45
- def verify_tools
46
- missing_tools = @@subclasses.map(&:new).reject(&:installed?)
47
- unless missing_tools.empty?
48
- puts format_missing_tools_response(missing_tools)
32
+ def search_for(criteria = {})
33
+ @@catalog.select do |meta|
34
+ criteria.all? { |k, v| meta[k] == v }
49
35
  end
50
36
  end
51
37
 
52
38
 
53
- def format_missing_tools_response(missing_tools)
54
- response = <<~EOS
55
-
56
- WARNING: AIA makes use of external CLI tools that are missing.
57
-
58
- Please install the following tools:
39
+ def catalog
40
+ @@catalog
41
+ end
59
42
 
60
- EOS
61
43
 
62
- missing_tools.each do |tool|
63
- response << " #{tool.name}: install from #{tool.url}\n"
44
+ def load_tools
45
+ Dir.glob(File.join(File.dirname(__FILE__), 'tools', '*.rb')).each do |file|
46
+ require file
64
47
  end
65
-
66
- response
67
48
  end
68
49
  end
69
50
  end
70
-
71
-
72
- (Pathname.new(__dir__)+"tools")
73
- .glob('*.rb')
74
- .each do |tool|
75
- require_relative "tools/#{tool.basename.to_s}"
76
- end
77
-
data/lib/aia.rb CHANGED
@@ -6,6 +6,7 @@ include DebugMe
6
6
  require 'hashie'
7
7
  require 'pathname'
8
8
  require 'readline'
9
+ require 'shellwords'
9
10
  require 'tempfile'
10
11
 
11
12
  require 'prompt_manager'
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.3.19
4
+ version: 0.3.20
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-12-26 00:00:00.000000000 Z
11
+ date: 2023-12-28 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: hashie
@@ -52,6 +52,20 @@ dependencies:
52
52
  - - ">="
53
53
  - !ruby/object:Gem::Version
54
54
  version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: shellwords
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
55
69
  - !ruby/object:Gem::Dependency
56
70
  name: toml-rb
57
71
  requirement: !ruby/object:Gem::Requirement
@@ -172,6 +186,7 @@ files:
172
186
  - lib/aia/tools/mods.rb
173
187
  - lib/aia/tools/sgpt.rb
174
188
  - lib/aia/tools/subl.rb
189
+ - lib/aia/tools/temp.md
175
190
  - lib/aia/tools/vim.rb
176
191
  - lib/aia/version.rb
177
192
  - lib/core_ext/string_wrap.rb