ai_refactor 0.2.0 → 0.3.1

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: 758af953e626b18190ef1e2252e730ea636d773a53b63052aaa7aba6213b49a2
4
- data.tar.gz: 2bf63e217e7c3647e9cc26a9112f72eb19b81700f5784730a15ec0b229889963
3
+ metadata.gz: aae248c6a2c5342ea8990e2a672b455c48ec79645e8d88eb20f9afc1af9e7cce
4
+ data.tar.gz: f86584d15b6467600be12797e4f34bf4ce4545ad6be3bbaef8f9ce62817dd66f
5
5
  SHA512:
6
- metadata.gz: 76e034568b78234e7b66c756601be73976f3367c542112b6cfc88c47ab3edb31016a8507201eee382a33ed2c7d041707295fcf140cfecc859a1d6e5b99c52d35
7
- data.tar.gz: bb00b41760079e5203c5bcadbff10c09b7fc701ec12a1c25623dbff85862e6c3de0416d341ad41c22d03dad54fdadc97ccb33e2a02756186a65ad9e4dfb17d4d
6
+ metadata.gz: a4d1aa38dc497acd25038229da1fe3e46bcbb4dd26cf33770f2bfe1a8a44a48c656023a2b9342a0b81c9fb620ecc4bc28dba215ef2225da8e5e22b13a8d73d92
7
+ data.tar.gz: e1a7e38d44b83b81da06dcb684e8ce61532fe37818c1e4c41f14451923cd9ad9c3f78092fba5b0fe65e316c1fe75681bf34b81501d2f99694a6ceb85826cede3
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- ai_refactor (0.2.0)
4
+ ai_refactor (0.3.1)
5
5
  colorize (< 2.0)
6
6
  open3 (< 2.0)
7
7
  ruby-openai (>= 3.4.0, < 5.0)
data/README.md CHANGED
@@ -11,8 +11,9 @@ This is based on the assumption that the LLM AIs are pretty good at identifying
11
11
 
12
12
  Currently available:
13
13
 
14
- - `generic`
15
- - `rspec_to_minitest_rails`
14
+ - `rspec_to_minitest_rails`: convert RSpec specs to minitest tests in Rails apps
15
+ - `generic`: provide your own prompt for the AI and run against the input files
16
+
16
17
 
17
18
  ### `rspec_to_minitest_rails`
18
19
 
@@ -47,7 +48,20 @@ Done processing all files!
47
48
 
48
49
  Applies the refactor specified by prompting the AI with the user supplied prompt. You must supply a prompt file with the `-p` option.
49
50
 
50
- The output is written to `stdout`.
51
+ The output is written to `stdout`, or to a file with the `--output` option. If `--output` is used without a value it overwrites the input.
52
+
53
+ You can also output to a file using a template, `--output-template` to determine the output file name given a template string:
54
+
55
+ The template is used to create the output name, where the it can have substitutions, '[FILE]', '[NAME]', '[DIR]', '[REFACTOR]' & '[EXT]'.
56
+
57
+ Eg `--output-template "[DIR]/[NAME]_[REFACTOR][EXT]"`
58
+
59
+ eg for the input `my_dir/my_class.rb`
60
+ - `[FILE]`: `my_class.rb`
61
+ - `[NAME]`: `my_class`
62
+ - `[DIR]`: `my_dir`
63
+ - `[REFACTOR]`: `generic`
64
+ - `[EXT]`: `.rb`
51
65
 
52
66
  ## Installation
53
67
 
@@ -66,7 +80,7 @@ See `ai_refactor --help` for more information.
66
80
  ```
67
81
  Usage: ai_refactor REFACTOR_TYPE INPUT_FILE_OR_DIR [options]
68
82
 
69
- Where REFACTOR_TYPE is one of: ["generic", "rspec_to_minitest_rails", "minitest_to_rspec"]
83
+ Where REFACTOR_TYPE is one of: ["rspec_to_minitest_rails", "generic"]
70
84
 
71
85
  -p, --prompt PROMPT_FILE Specify path to a text file that contains the ChatGPT 'system' prompt.
72
86
  -c, --continue [MAX_MESSAGES] If ChatGPT stops generating due to the maximum token count being reached, continue to generate more messages, until a stop condition or MAX_MESSAGES. MAX_MESSAGES defaults to 3
@@ -77,6 +91,10 @@ Where REFACTOR_TYPE is one of: ["generic", "rspec_to_minitest_rails", "minitest_
77
91
  -v, --verbose Show extra output and progress info
78
92
  -d, --debug Show debugging output to help diagnose issues
79
93
  -h, --help Prints this help
94
+
95
+ For refactor type 'generic':
96
+ --output [FILE] Write output to file instead of stdout. If no path provided will overwrite input file (will prompt to overwrite existing files)
97
+ --output-template TEMPLATE Write outputs to files instead of stdout. The template is used to create the output name, where the it can have substitutions, '[FILE]', '[NAME]', '[DIR]', '[REFACTOR]' & '[EXT]'. Eg `[DIR]/[NAME]_[REFACTOR][EXT]` (will prompt to overwrite existing files)
80
98
  ```
81
99
 
82
100
 
data/exe/ai_refactor CHANGED
@@ -14,7 +14,6 @@ supported_names = AIRefactor::Refactors.names
14
14
  option_parser = OptionParser.new do |parser|
15
15
  parser.banner = "Usage: ai_refactor REFACTOR_TYPE INPUT_FILE_OR_DIR [options]\n\nWhere REFACTOR_TYPE is one of: #{supported_names}\n\n"
16
16
 
17
- # todo: support for a sort of generic process which uses a custom prompt file
18
17
  parser.on("-p", "--prompt PROMPT_FILE", String, "Specify path to a text file that contains the ChatGPT 'system' prompt.") do |f|
19
18
  options[:prompt_file_path] = f
20
19
  end
@@ -27,11 +26,11 @@ option_parser = OptionParser.new do |parser|
27
26
  options[:ai_model] = m
28
27
  end
29
28
 
30
- parser.on(nil, "--temperature TEMP", Float, "Specify the temperature parameter for ChatGPT (default 0.7).") do |p|
29
+ parser.on("--temperature TEMP", Float, "Specify the temperature parameter for ChatGPT (default 0.7).") do |p|
31
30
  options[:ai_temperature] = p
32
31
  end
33
32
 
34
- parser.on(nil, "--max-tokens MAX_TOKENS", Integer, "Specify the max number of tokens of output ChatGPT can generate. Max will depend on the size of the prompt (default 1500)") do |m|
33
+ parser.on("--max-tokens MAX_TOKENS", Integer, "Specify the max number of tokens of output ChatGPT can generate. Max will depend on the size of the prompt (default 1500)") do |m|
35
34
  options[:ai_max_tokens] = m
36
35
  end
37
36
 
@@ -47,18 +46,23 @@ option_parser = OptionParser.new do |parser|
47
46
  options[:debug] = true
48
47
  end
49
48
 
50
- supported_refactors.each do |_name, refactorer|
51
- refactorer.command_line_options.each do |option|
52
- parser.on(option[:short], option[:long], option[:type], option[:help]) do |o|
53
- options[option[:key]] = o
54
- end
55
- end
56
- end
57
-
58
49
  parser.on("-h", "--help", "Prints this help") do
59
50
  puts parser
60
51
  exit
61
52
  end
53
+
54
+ parser.separator ""
55
+
56
+ supported_refactors.each do |name, refactorer|
57
+ parser.separator "For refactor type '#{name}':" if refactorer.command_line_options.size.positive?
58
+ refactorer.command_line_options.each do |option|
59
+ args = [option[:long], option[:type], option[:help]]
60
+ args.unshift(option[:short]) if option[:short]
61
+ parser.on(*args) do |o|
62
+ options[option[:key]] = o.nil? ? true : o
63
+ end
64
+ end
65
+ end
62
66
  end
63
67
 
64
68
  option_parser.parse!
@@ -21,8 +21,26 @@ module AIRefactor
21
21
 
22
22
  private
23
23
 
24
+ def can_overwrite_output_file?(output_path)
25
+ logger.info "Do you wish to overwrite #{output_path}? (y/n)"
26
+ answer = $stdin.gets.chomp
27
+ unless answer == "y" || answer == "Y"
28
+ logger.warn "Skipping #{input_file}..."
29
+ self.failed_message = "Skipped as output file already exists"
30
+ return false
31
+ end
32
+ true
33
+ end
34
+
24
35
  def prompt_file_path
25
- self.class.prompt_file_path
36
+ file = if options && options[:prompt_file_path]&.length&.positive?
37
+ options[:prompt_file_path]
38
+ else
39
+ File.join(File.dirname(File.expand_path(__FILE__)), "refactors", "prompts", "#{refactor_name}.md")
40
+ end
41
+ file.tap do |prompt|
42
+ raise "No prompt file '#{prompt}' found for #{refactor_name}" unless File.exist?(prompt)
43
+ end
26
44
  end
27
45
 
28
46
  def ai_client
@@ -43,17 +61,6 @@ module AIRefactor
43
61
  .tr("-", "_")
44
62
  .downcase
45
63
  end
46
-
47
- def prompt_file_path
48
- file = if options[:prompt_file_path]&.length&.positive?
49
- options[:prompt_file_path]
50
- else
51
- File.join(File.dirname(File.expand_path(__FILE__)), "prompts", "#{refactor_name}.md")
52
- end
53
- file.tap do |prompt|
54
- raise "No prompt file '#{prompt}' found for #{refactor_name}" unless File.exist?(prompt)
55
- end
56
- end
57
64
  end
58
65
  end
59
66
  end
@@ -5,14 +5,20 @@ module AIRefactor
5
5
  class Generic < BaseRefactor
6
6
  def run
7
7
  logger.verbose "Generic refactor to #{input_file}... (using user supplied prompt #{prompt_file_path})"
8
+ logger.verbose "Write output to #{output_file_path}..." if output_file_path
8
9
 
9
10
  processor = AIRefactor::FileProcessor.new(
10
11
  input_path: input_file,
11
12
  prompt_file_path: prompt_file_path,
12
13
  ai_client: ai_client,
13
- logger: logger
14
+ logger: logger,
15
+ output_path: output_file_path
14
16
  )
15
17
 
18
+ if processor.output_exists?
19
+ return false unless can_overwrite_output_file?(output_file_path)
20
+ end
21
+
16
22
  logger.verbose "Converting #{input_file}..."
17
23
 
18
24
  begin
@@ -38,11 +44,38 @@ module AIRefactor
38
44
  return false
39
45
  end
40
46
 
41
- output_content
47
+ output_file_path ? true : output_content
42
48
  end
43
49
 
44
50
  private
45
51
 
52
+ def output_file_path
53
+ return output_file_path_from_template if output_template_path
54
+
55
+ path = options[:output_file_path]
56
+ return unless path
57
+
58
+ if path == true
59
+ input_file
60
+ else
61
+ path
62
+ end
63
+ end
64
+
65
+ def output_template_path
66
+ options[:output_template_path]
67
+ end
68
+
69
+ def output_file_path_from_template
70
+ path = output_template_path.gsub("[FILE]", File.basename(input_file))
71
+ .gsub("[NAME]", File.basename(input_file, ".*"))
72
+ .gsub("[DIR]", File.dirname(input_file))
73
+ .gsub("[REFACTOR]", self.class.refactor_name)
74
+ .gsub("[EXT]", File.extname(input_file))
75
+ raise "Output template could not be used" unless path.length.positive?
76
+ path
77
+ end
78
+
46
79
  def prompt_file_path
47
80
  specified_prompt_path = options[:prompt_file_path]
48
81
  if specified_prompt_path&.length&.positive?
@@ -58,8 +91,21 @@ module AIRefactor
58
91
  end
59
92
 
60
93
  class << self
61
- def prompt_file_path
62
- raise "Generic refactor requires prompt file to be user specified."
94
+ def command_line_options
95
+ [
96
+ {
97
+ key: :output_file_path,
98
+ long: "--output [FILE]",
99
+ type: String,
100
+ help: "Write output to file instead of stdout. If no path provided will overwrite input file (will prompt to overwrite existing files)"
101
+ },
102
+ {
103
+ key: :output_template_path,
104
+ long: "--output-template TEMPLATE",
105
+ type: String,
106
+ help: "Write outputs to files instead of stdout. The template is used to create the output name, where the it can have substitutions, '[FILE]', '[NAME]', '[DIR]', '[REFACTOR]' & '[EXT]'. Eg `[DIR]/[NAME]_[REFACTOR][EXT]` (will prompt to overwrite existing files)"
107
+ }
108
+ ]
63
109
  end
64
110
  end
65
111
  end
@@ -35,13 +35,7 @@ module AIRefactor
35
35
  )
36
36
 
37
37
  if processor.output_exists?
38
- logger.info "Do you wish to overwrite #{output_path}? (y/n)"
39
- answer = $stdin.gets.chomp
40
- unless answer == "y" || answer == "Y"
41
- logger.warn "Skipping #{input_file}..."
42
- self.failed_message = "Skipped as output test file already exists"
43
- return false
44
- end
38
+ return false unless can_overwrite_output_file?(output_path)
45
39
  end
46
40
 
47
41
  logger.verbose "Converting #{input_file}..."
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module AIRefactor
4
- VERSION = "0.2.0"
4
+ VERSION = "0.3.1"
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ai_refactor
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.3.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Stephen Ierodiaconou
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2023-05-24 00:00:00.000000000 Z
11
+ date: 2023-05-25 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: colorize