llm_fixer 0.0.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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: b0ad882d017de42f02930ba978b352b1fd1b089095b5de7fdbd6aadebce6e574
4
+ data.tar.gz: 9cde3148d984a28e3366a5b837e93a0f919d9d30dfe28cc16e444ea550a07985
5
+ SHA512:
6
+ metadata.gz: db7fe05f51c1cc138e993bae90ca6cbfcd0e3c125309ca82bc302d16e6f4448fe409e4afc82b015c9e8319332abf77aec869f7cbf331218d7c166d2d516b97aa
7
+ data.tar.gz: ccc4e7d472cb87840daa4e9db929bab22cfa510649945e94d7f9d09217f6e416de20ae77683962db359296bbb232377a7de0488a4d912ccc4abb3206bfcec58a
data/.rubocop.yml ADDED
@@ -0,0 +1,26 @@
1
+ # @format
2
+
3
+ inherit_from:
4
+ - https://raw.githubusercontent.com/pixta-dev/pixta-rubocop/master/rubocop.yml
5
+
6
+ # Normally additional configs of AllCops/Include and AllCops/Exclude override to RuboCops's defaults.
7
+ # But you'd like to apply both RuboCops's defaults and addtional configs of AllCops/Include and AllCops/Exclude.
8
+ # ref: https://docs.rubocop.org/en/stable/configuration/#unusual-files-that-would-not-be-included-by-default
9
+ inherit_mode:
10
+ merge:
11
+ - Include
12
+ - Exclude
13
+
14
+ AllCops:
15
+ TargetRubyVersion: 3.4
16
+ Exclude:
17
+ - "Gemfile"
18
+ - "bin/*"
19
+ - "config/**/*"
20
+ - "db/**/*"
21
+ - "deploy/**/*"
22
+ NewCops: enable
23
+
24
+ Rails/Output:
25
+ Enabled: false
26
+ AutoCorrect: false
data/Gemfile ADDED
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ source 'https://rubygems.org'
4
+ gemspec
5
+
6
+ gem 'pry'
7
+ gem 'pry-byebug'
8
+ gem 'rspec'
9
+ gem 'rubocop'
10
+ gem 'rubocop-rails'
11
+ gem 'rubocop-rspec'
12
+ gem 'rubocop-rspec_rails'
13
+ gem 'rubocop-performance'
data/Gemfile.lock ADDED
@@ -0,0 +1,137 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ llm_fixer (0.0.1)
5
+ colorize
6
+ ruby-openai
7
+ thor
8
+
9
+ GEM
10
+ remote: https://rubygems.org/
11
+ specs:
12
+ activesupport (8.0.1)
13
+ base64
14
+ benchmark (>= 0.3)
15
+ bigdecimal
16
+ concurrent-ruby (~> 1.0, >= 1.3.1)
17
+ connection_pool (>= 2.2.5)
18
+ drb
19
+ i18n (>= 1.6, < 2)
20
+ logger (>= 1.4.2)
21
+ minitest (>= 5.1)
22
+ securerandom (>= 0.3)
23
+ tzinfo (~> 2.0, >= 2.0.5)
24
+ uri (>= 0.13.1)
25
+ ast (2.4.2)
26
+ base64 (0.2.0)
27
+ benchmark (0.4.0)
28
+ bigdecimal (3.1.9)
29
+ byebug (11.1.3)
30
+ coderay (1.1.3)
31
+ colorize (1.1.0)
32
+ concurrent-ruby (1.3.5)
33
+ connection_pool (2.5.0)
34
+ diff-lcs (1.6.0)
35
+ drb (2.2.1)
36
+ event_stream_parser (1.0.0)
37
+ faraday (2.12.2)
38
+ faraday-net_http (>= 2.0, < 3.5)
39
+ json
40
+ logger
41
+ faraday-multipart (1.1.0)
42
+ multipart-post (~> 2.0)
43
+ faraday-net_http (3.4.0)
44
+ net-http (>= 0.5.0)
45
+ i18n (1.14.7)
46
+ concurrent-ruby (~> 1.0)
47
+ json (2.9.1)
48
+ language_server-protocol (3.17.0.3)
49
+ logger (1.6.6)
50
+ method_source (1.1.0)
51
+ minitest (5.25.4)
52
+ multipart-post (2.4.1)
53
+ net-http (0.6.0)
54
+ uri
55
+ parallel (1.26.3)
56
+ parser (3.3.6.0)
57
+ ast (~> 2.4.1)
58
+ racc
59
+ pry (0.14.2)
60
+ coderay (~> 1.1)
61
+ method_source (~> 1.0)
62
+ pry-byebug (3.10.1)
63
+ byebug (~> 11.0)
64
+ pry (>= 0.13, < 0.15)
65
+ racc (1.8.1)
66
+ rack (3.1.11)
67
+ rainbow (3.1.1)
68
+ regexp_parser (2.10.0)
69
+ rspec (3.13.0)
70
+ rspec-core (~> 3.13.0)
71
+ rspec-expectations (~> 3.13.0)
72
+ rspec-mocks (~> 3.13.0)
73
+ rspec-core (3.13.3)
74
+ rspec-support (~> 3.13.0)
75
+ rspec-expectations (3.13.3)
76
+ diff-lcs (>= 1.2.0, < 2.0)
77
+ rspec-support (~> 3.13.0)
78
+ rspec-mocks (3.13.2)
79
+ diff-lcs (>= 1.2.0, < 2.0)
80
+ rspec-support (~> 3.13.0)
81
+ rspec-support (3.13.2)
82
+ rubocop (1.70.0)
83
+ json (~> 2.3)
84
+ language_server-protocol (>= 3.17.0)
85
+ parallel (~> 1.10)
86
+ parser (>= 3.3.0.2)
87
+ rainbow (>= 2.2.2, < 4.0)
88
+ regexp_parser (>= 2.9.3, < 3.0)
89
+ rubocop-ast (>= 1.36.2, < 2.0)
90
+ ruby-progressbar (~> 1.7)
91
+ unicode-display_width (>= 2.4.0, < 4.0)
92
+ rubocop-ast (1.37.0)
93
+ parser (>= 3.3.1.0)
94
+ rubocop-performance (1.23.1)
95
+ rubocop (>= 1.48.1, < 2.0)
96
+ rubocop-ast (>= 1.31.1, < 2.0)
97
+ rubocop-rails (2.29.1)
98
+ activesupport (>= 4.2.0)
99
+ rack (>= 1.1)
100
+ rubocop (>= 1.52.0, < 2.0)
101
+ rubocop-ast (>= 1.31.1, < 2.0)
102
+ rubocop-rspec (3.0.4)
103
+ rubocop (~> 1.61)
104
+ rubocop-rspec_rails (2.30.0)
105
+ rubocop (~> 1.61)
106
+ rubocop-rspec (~> 3, >= 3.0.1)
107
+ ruby-openai (7.4.0)
108
+ event_stream_parser (>= 0.3.0, < 2.0.0)
109
+ faraday (>= 1)
110
+ faraday-multipart (>= 1)
111
+ ruby-progressbar (1.13.0)
112
+ securerandom (0.4.1)
113
+ thor (1.3.2)
114
+ tzinfo (2.0.6)
115
+ concurrent-ruby (~> 1.0)
116
+ unicode-display_width (3.1.4)
117
+ unicode-emoji (~> 4.0, >= 4.0.4)
118
+ unicode-emoji (4.0.4)
119
+ uri (1.0.3)
120
+
121
+ PLATFORMS
122
+ arm64-darwin-23
123
+ ruby
124
+
125
+ DEPENDENCIES
126
+ llm_fixer!
127
+ pry
128
+ pry-byebug
129
+ rspec
130
+ rubocop
131
+ rubocop-performance
132
+ rubocop-rails
133
+ rubocop-rspec
134
+ rubocop-rspec_rails
135
+
136
+ BUNDLED WITH
137
+ 2.6.2
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 kaiba
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,64 @@
1
+ <!-- @format -->
2
+
3
+ # LLM fixer
4
+
5
+ 静的解析エラーを自動的に修正するツールです。
6
+
7
+ ## インストール
8
+
9
+ Gemfile に以下を追加します:
10
+
11
+ ```ruby
12
+ gem 'llm_fixer'
13
+ ```
14
+
15
+ そして以下を実行します:
16
+
17
+ ```bash
18
+ $ bundle install
19
+ ```
20
+
21
+ または、以下のようにインストールします:
22
+
23
+ ```bash
24
+ $ gem install llm_fixer
25
+ ```
26
+
27
+ ## 環境変数
28
+
29
+ 以下の環境変数を使用して動作をカスタマイズできます:
30
+
31
+ - `LLM_API_KEY` : LLM API キー
32
+ - `LLM_API_BASE` : LLM API のベース URL
33
+ - `LLM_MODEL` : 使用する LLM モデル
34
+
35
+ ## 使用方法
36
+
37
+ 環境変数を設定します:
38
+
39
+ ### ChatGPT
40
+
41
+ ```bash
42
+ export LLM_API_KEY="your-api-key-here"
43
+ export LLM_API_BASE="https://api.openai.com/v1"
44
+ export LLM_MODEL="gpt-4o"
45
+ ```
46
+
47
+ ### Google Gemini
48
+
49
+ ```bash
50
+ export LLM_API_KEY="your-api-key-here"
51
+ export LLM_API_BASE="https://generativelanguage.googleapis.com/v1beta"
52
+ export LLM_MODEL="gemini-2.0-pro-exp"
53
+ ```
54
+
55
+ ### 実行
56
+
57
+ 静的解析ツールコマンド(例:RuboCop)を実行します:
58
+
59
+ ```bash
60
+ llm_fixer fix your-lint-or-test-command path/to/target
61
+
62
+ # Rubocop例
63
+ llm_fixer fix bundle exec rubocop path/to/target.rb
64
+ ```
data/Rakefile ADDED
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "bundler/gem_tasks"
4
+ require "rake/testtask"
5
+
6
+ Rake::TestTask.new(:test) do |t|
7
+ t.libs << "test"
8
+ t.libs << "lib"
9
+ t.test_files = FileList["test/**/*_test.rb"]
10
+ end
11
+
12
+ task default: :test
data/bin/llm_fixer ADDED
@@ -0,0 +1,38 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require 'thor'
5
+ require_relative '../lib/llm_fixer/static_analysis_fixer'
6
+
7
+ class CLI < Thor
8
+ desc 'fix COMMAND [ARGS...]', 'Static analysis errors are automatically fixed by LLM'
9
+ method_option :command, type: :string, required: false
10
+
11
+ def fix(*args)
12
+ %w[LLM_API_BASE LLM_MODEL LLM_API_KEY].each do |key|
13
+ unless ENV[key]
14
+ puts "#{key} is not set"
15
+ exit 1
16
+ end
17
+ end
18
+
19
+
20
+ puts "args: #{args.inspect}"
21
+ if args.empty?
22
+ puts 'Usage: llm_fixer fix COMMAND [ARGS...]'
23
+ exit 1
24
+ end
25
+
26
+ puts "command: #{args.join(' ')}"
27
+ fixer = LlmFixer::StaticAnalysisFixer.new(ENV.fetch('LLM_API_KEY', nil))
28
+ success = fixer.fix_file(args)
29
+ exit(1) unless success
30
+ end
31
+
32
+ def self.exit_on_failure?
33
+ true
34
+ end
35
+ end
36
+
37
+ puts 'start llm_fixer'
38
+ CLI.start(ARGV)
@@ -0,0 +1,132 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "openai"
4
+ require "open3"
5
+ require "tempfile"
6
+ require "pry"
7
+ require "erb"
8
+ require "colorize"
9
+ require "ostruct"
10
+
11
+ module LlmFixer
12
+ class StaticAnalysisFixer
13
+ DEFAULT_MODEL = "gpt-4o"
14
+ DEFAULT_API_BASE = "https://api.openai.com/v1"
15
+
16
+ def initialize(api_key)
17
+ @client = OpenAI::Client.new(
18
+ access_token: ENV.fetch("LLM_API_KEY", api_key),
19
+ uri_base: ENV.fetch("LLM_API_BASE", DEFAULT_API_BASE),
20
+ request_timeout: 600,
21
+ )
22
+ @model = ENV.fetch("LLM_MODEL", DEFAULT_MODEL)
23
+ puts "LLM model: #{@model}"
24
+ end
25
+
26
+ def fix_file(args)
27
+ command = args.join(" ")
28
+ output, succeeded = run_command(command)
29
+ if succeeded
30
+ puts "No errors found"
31
+ return true
32
+ end
33
+
34
+ # Search for existing file paths from command line arguments in reverse order
35
+ file_path = args.reverse.find { |arg| File.exist?(arg) }
36
+ return false unless file_path
37
+
38
+ # Request correction from LLM
39
+ result = generate_fix(file_path, command, output)
40
+ return false unless result
41
+
42
+ File.write(file_path, result)
43
+
44
+ # Check again
45
+ _, succeeded = run_command(command)
46
+ if succeeded
47
+ puts "#{file_path} was fixed!".green
48
+ true
49
+ else
50
+ puts "#{file_path} was not fixed!".red
51
+ false
52
+ end
53
+ end
54
+
55
+ private
56
+
57
+ def run_command(command)
58
+ command_parts = command.split
59
+ command_path = `which #{command_parts[0]}`.strip
60
+ return ["Command not found: #{command_parts[0]}", false] if command_path.empty?
61
+
62
+ output, status = Open3.capture2(ENV.to_h, command_path, *command_parts[1..])
63
+ succeeded = status.exitstatus.zero?
64
+ unless succeeded
65
+ puts "After correction, there are still errors:".red
66
+ puts output
67
+ end
68
+ [output, succeeded]
69
+ end
70
+
71
+ # rubocop:disable Metrics/MethodLength
72
+ def generate_fix(file_path, command, error_output)
73
+ file_content = File.read(file_path)
74
+ messages = build_messages(file_path, command, error_output, file_content)
75
+ puts messages
76
+ full_response = ""
77
+ puts "===== Start generating fix ====="
78
+ @client.chat(
79
+ parameters: {
80
+ model: @model,
81
+ messages: messages,
82
+ stream: proc { |chunk|
83
+ content = chunk.dig("choices", 0, "delta", "content")
84
+ full_response += content if content
85
+ },
86
+ },
87
+ )
88
+
89
+ full_response.strip!
90
+ # remove markdown syntax
91
+ if full_response.start_with?("```")
92
+ full_response = "#{full_response.split("\n")[1..-2].join("\n")}\n"
93
+ end
94
+ puts full_response
95
+ puts "===== End generating fix ====="
96
+ full_response
97
+ end
98
+ # rubocop:enable Metrics/MethodLength
99
+
100
+ def build_messages(file_path, command, error_output, file_content)
101
+ templates = {
102
+ system: "fix_prompt_system.erb",
103
+ user: "fix_prompt_user.erb",
104
+ }
105
+
106
+ messages = []
107
+ b = binding
108
+ system_template = ERB.new(File.read(File.join(File.dirname(__FILE__),
109
+ "../templates/#{templates[:system]}")))
110
+ messages << { role: "system", content: system_template.result(b) }
111
+ user_template = ERB.new(File.read(File.join(File.dirname(__FILE__),
112
+ "../templates/#{templates[:user]}")))
113
+ messages << { role: "user", content: user_template.result(b) }
114
+
115
+ messages
116
+ end
117
+
118
+ # LLMが警告以外の場所も直した上、差分として上げなかったため、不正なパッチになってしまったので一旦ファイル丸ごと出力されることしにた
119
+ def apply_patch(patch)
120
+ Tempfile.create("patch") do |f|
121
+ f.write(patch)
122
+ f.close
123
+ patch_command = "patch -f --no-backup-if-mismatch -p1 < #{f.path}"
124
+ _, stderr, status = Open3.capture3(patch_command)
125
+ unless status.exitstatus.zero?
126
+ puts "Failed to apply patch: #{stderr}".colorize(:red)
127
+ exit 1
128
+ end
129
+ end
130
+ end
131
+ end
132
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module LlmFixer
4
+ VERSION = "0.0.1"
5
+ end
@@ -0,0 +1,7 @@
1
+ You are a code repair assistant. Your task is to fix static analysis errors in code.
2
+ You should:
3
+ - Fix only the error locations
4
+ - Delete unnecessary lines instead of commenting them out
5
+ - Generate fixed full source code
6
+
7
+ The command, FileContent, and ErrorOutput will be provided in the user message.
@@ -0,0 +1,11 @@
1
+ TargetFile:
2
+ <%= file_path %>
3
+
4
+ ExecuteCommand:
5
+ <%= command %>
6
+
7
+ FileContent:
8
+ <%= file_content %>
9
+
10
+ ErrorOutput:
11
+ <%= error_output %>
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ RSpec.describe "基本的な算術演算" do
4
+ let!(:result) { 1 + 1 }
5
+ describe "足し算" do
6
+ it "1 + 1 は 2 になること" do
7
+ expect(1 + 1).to eq(result)
8
+ end
9
+ end
10
+ end
metadata ADDED
@@ -0,0 +1,102 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: llm_fixer
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - kaiba
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2025-03-11 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: colorize
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: ruby-openai
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: thor
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ description: A tool that uses LLM to automatically fix errors detected by static analysis
56
+ tools (such as RuboCop).
57
+ email:
58
+ - kaibadash@gmail.com
59
+ executables:
60
+ - llm_fixer
61
+ extensions: []
62
+ extra_rdoc_files: []
63
+ files:
64
+ - ".rubocop.yml"
65
+ - Gemfile
66
+ - Gemfile.lock
67
+ - LICENSE
68
+ - README.md
69
+ - Rakefile
70
+ - bin/llm_fixer
71
+ - lib/llm_fixer/static_analysis_fixer.rb
72
+ - lib/llm_fixer/version.rb
73
+ - lib/templates/fix_prompt_system.erb
74
+ - lib/templates/fix_prompt_user.erb
75
+ - sample/sample_spec.rb
76
+ homepage: https://github.com/kaibadash/llm_fixer
77
+ licenses:
78
+ - MIT
79
+ metadata:
80
+ homepage_uri: https://github.com/kaibadash/llm_fixer
81
+ source_code_uri: https://github.com/kaibadash/llm_fixer
82
+ rubygems_mfa_required: 'true'
83
+ post_install_message:
84
+ rdoc_options: []
85
+ require_paths:
86
+ - lib
87
+ required_ruby_version: !ruby/object:Gem::Requirement
88
+ requirements:
89
+ - - ">="
90
+ - !ruby/object:Gem::Version
91
+ version: 2.6.0
92
+ required_rubygems_version: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ requirements: []
98
+ rubygems_version: 3.5.3
99
+ signing_key:
100
+ specification_version: 4
101
+ summary: LLM tool to automatically fix static analysis errors
102
+ test_files: []