code_sage 0.1.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.
- checksums.yaml +7 -0
- data/.github/workflows/gem.yml +47 -0
- data/.gitignore +54 -0
- data/.rspec +3 -0
- data/CHANGELOG.md +24 -0
- data/Gemfile +11 -0
- data/Gemfile.lock +150 -0
- data/LICENSE +21 -0
- data/README.md +279 -0
- data/Rakefile +20 -0
- data/bin/console +10 -0
- data/bin/setup +8 -0
- data/code_sage.gemspec +39 -0
- data/exe/code_sage +6 -0
- data/lib/code_sage/cli.rb +161 -0
- data/lib/code_sage/config.rb +87 -0
- data/lib/code_sage/git_analyzer.rb +183 -0
- data/lib/code_sage/report_formatter.rb +171 -0
- data/lib/code_sage/reviewer.rb +226 -0
- data/lib/code_sage/version.rb +3 -0
- data/lib/code_sage.rb +15 -0
- metadata +179 -0
@@ -0,0 +1,226 @@
|
|
1
|
+
require 'llm_chain'
|
2
|
+
|
3
|
+
module CodeSage
|
4
|
+
class Reviewer
|
5
|
+
attr_reader :options
|
6
|
+
|
7
|
+
def initialize(options = {})
|
8
|
+
@options = {
|
9
|
+
branch: 'main',
|
10
|
+
files: nil,
|
11
|
+
format: 'console',
|
12
|
+
config_path: nil,
|
13
|
+
verbose: false,
|
14
|
+
enable_rag: false # Отключаем RAG по умолчанию
|
15
|
+
}.merge(options)
|
16
|
+
|
17
|
+
@git_analyzer = GitAnalyzer.new(@options)
|
18
|
+
@formatter = ReportFormatter.new(@options[:format])
|
19
|
+
@llm_chain = setup_llm_chain
|
20
|
+
end
|
21
|
+
|
22
|
+
def review
|
23
|
+
puts "🔍 Analyzing code changes..." if @options[:verbose]
|
24
|
+
|
25
|
+
changes = @git_analyzer.get_changes
|
26
|
+
|
27
|
+
if changes.empty?
|
28
|
+
return { success: true, message: "No changes to review" }
|
29
|
+
end
|
30
|
+
|
31
|
+
puts "📝 Found #{changes.length} changed files" if @options[:verbose]
|
32
|
+
|
33
|
+
reviews = []
|
34
|
+
|
35
|
+
changes.each do |change|
|
36
|
+
puts "Reviewing #{change[:file]}..." if @options[:verbose]
|
37
|
+
|
38
|
+
review = review_file(change)
|
39
|
+
reviews << review if review
|
40
|
+
end
|
41
|
+
|
42
|
+
report = generate_report(reviews)
|
43
|
+
output_report(report)
|
44
|
+
|
45
|
+
{ success: true, reviews: reviews, report: report }
|
46
|
+
rescue => e
|
47
|
+
{ success: false, error: e.message }
|
48
|
+
end
|
49
|
+
|
50
|
+
private
|
51
|
+
|
52
|
+
def setup_llm_chain
|
53
|
+
# Load configuration
|
54
|
+
config = @options[:config_path] ? Config.new(@options[:config_path]) : Config.new
|
55
|
+
|
56
|
+
llm_config = config.data['llm']
|
57
|
+
provider = llm_config['provider'] || 'openai'
|
58
|
+
model = llm_config['model'] || 'gpt-4'
|
59
|
+
|
60
|
+
# Configure LLM chain for code review using proper API
|
61
|
+
case provider.downcase
|
62
|
+
when 'openai'
|
63
|
+
LLMChain::Chain.new(
|
64
|
+
model: model,
|
65
|
+
retriever: false,
|
66
|
+
client_options: {
|
67
|
+
api_key: llm_config['api_key'] || ENV['OPENAI_API_KEY'],
|
68
|
+
temperature: llm_config['temperature'] || 0.1,
|
69
|
+
max_tokens: llm_config['max_tokens'] || 2000
|
70
|
+
}
|
71
|
+
)
|
72
|
+
when 'ollama'
|
73
|
+
# For local Ollama models
|
74
|
+
LLMChain::Chain.new(
|
75
|
+
model: model,
|
76
|
+
retriever: false,
|
77
|
+
client_options: {
|
78
|
+
temperature: llm_config['temperature'] || 0.1,
|
79
|
+
base_url: ENV['OLLAMA_BASE_URL'] || 'http://localhost:11434'
|
80
|
+
}
|
81
|
+
)
|
82
|
+
when 'qwen'
|
83
|
+
# For Qwen models via Ollama
|
84
|
+
LLMChain::Chain.new(
|
85
|
+
model: model.include?('qwen') ? model : 'qwen2:7b',
|
86
|
+
retriever: false,
|
87
|
+
client_options: {
|
88
|
+
temperature: llm_config['temperature'] || 0.1,
|
89
|
+
base_url: ENV['OLLAMA_BASE_URL'] || 'http://localhost:11434'
|
90
|
+
}
|
91
|
+
)
|
92
|
+
else
|
93
|
+
# Default to OpenAI
|
94
|
+
LLMChain::Chain.new(
|
95
|
+
model: 'gpt-4',
|
96
|
+
retriever: false,
|
97
|
+
client_options: {
|
98
|
+
api_key: ENV['OPENAI_API_KEY'],
|
99
|
+
temperature: 0.1
|
100
|
+
}
|
101
|
+
)
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
def build_system_message
|
106
|
+
<<~PROMPT
|
107
|
+
You are CodeSage, an expert code reviewer specializing in Ruby development.
|
108
|
+
|
109
|
+
Your role is to:
|
110
|
+
1. Identify potential bugs, security issues, and performance problems
|
111
|
+
2. Suggest improvements for code readability and maintainability
|
112
|
+
3. Check for adherence to Ruby best practices and conventions
|
113
|
+
4. Provide constructive feedback with specific examples
|
114
|
+
5. Highlight both positive aspects and areas for improvement
|
115
|
+
|
116
|
+
Focus on:
|
117
|
+
- Code quality and structure
|
118
|
+
- Potential runtime errors
|
119
|
+
- Security vulnerabilities
|
120
|
+
- Performance optimizations
|
121
|
+
- Ruby idioms and best practices
|
122
|
+
- Testing considerations
|
123
|
+
|
124
|
+
Provide your feedback in a structured format with clear categories and actionable suggestions.
|
125
|
+
PROMPT
|
126
|
+
end
|
127
|
+
|
128
|
+
def review_file(change)
|
129
|
+
prompt = build_file_review_prompt(change)
|
130
|
+
|
131
|
+
# Use llm_chain's ask method with system message context
|
132
|
+
full_prompt = "#{build_system_message}\n\n#{prompt}"
|
133
|
+
response = @llm_chain.ask(full_prompt)
|
134
|
+
|
135
|
+
{
|
136
|
+
file: change[:file],
|
137
|
+
change_type: change[:type],
|
138
|
+
lines_added: change[:lines_added],
|
139
|
+
lines_removed: change[:lines_removed],
|
140
|
+
review: response,
|
141
|
+
timestamp: Time.now
|
142
|
+
}
|
143
|
+
rescue => e
|
144
|
+
puts "Error reviewing #{change[:file]}: #{e.message}".colorize(:red)
|
145
|
+
nil
|
146
|
+
end
|
147
|
+
|
148
|
+
def build_file_review_prompt(change)
|
149
|
+
<<~PROMPT
|
150
|
+
Please review the following Ruby code changes:
|
151
|
+
|
152
|
+
File: #{change[:file]}
|
153
|
+
Change Type: #{change[:type]}
|
154
|
+
Lines Added: #{change[:lines_added]}
|
155
|
+
Lines Removed: #{change[:lines_removed]}
|
156
|
+
|
157
|
+
Code Diff:
|
158
|
+
```
|
159
|
+
#{change[:diff]}
|
160
|
+
```
|
161
|
+
|
162
|
+
Full File Context (if available):
|
163
|
+
```ruby
|
164
|
+
#{change[:content] || 'Not available'}
|
165
|
+
```
|
166
|
+
|
167
|
+
Please provide a comprehensive review focusing on:
|
168
|
+
1. Code quality and best practices
|
169
|
+
2. Potential bugs or issues
|
170
|
+
3. Security considerations
|
171
|
+
4. Performance implications
|
172
|
+
5. Suggestions for improvement
|
173
|
+
|
174
|
+
Format your response as structured feedback with clear sections.
|
175
|
+
PROMPT
|
176
|
+
end
|
177
|
+
|
178
|
+
def generate_report(reviews)
|
179
|
+
{
|
180
|
+
summary: generate_summary(reviews),
|
181
|
+
reviews: reviews,
|
182
|
+
metrics: calculate_metrics(reviews),
|
183
|
+
recommendations: generate_recommendations(reviews),
|
184
|
+
generated_at: Time.now
|
185
|
+
}
|
186
|
+
end
|
187
|
+
|
188
|
+
def generate_summary(reviews)
|
189
|
+
total_files = reviews.length
|
190
|
+
|
191
|
+
{
|
192
|
+
total_files_reviewed: total_files,
|
193
|
+
files_with_issues: reviews.count { |r| r[:review].include?('issue') || r[:review].include?('problem') },
|
194
|
+
total_lines_changed: reviews.sum { |r| r[:lines_added] + r[:lines_removed] }
|
195
|
+
}
|
196
|
+
end
|
197
|
+
|
198
|
+
def calculate_metrics(reviews)
|
199
|
+
{
|
200
|
+
avg_lines_per_file: if reviews.empty?
|
201
|
+
0
|
202
|
+
else
|
203
|
+
reviews.sum do |r|
|
204
|
+
r[:lines_added] + r[:lines_removed]
|
205
|
+
end / reviews.length
|
206
|
+
end,
|
207
|
+
files_by_type: reviews.group_by { |r| r[:change_type] }.transform_values(&:count)
|
208
|
+
}
|
209
|
+
end
|
210
|
+
|
211
|
+
def generate_recommendations(reviews)
|
212
|
+
# Extract common patterns and generate overall recommendations
|
213
|
+
# This could be enhanced with more sophisticated analysis
|
214
|
+
[
|
215
|
+
"Review suggested improvements for each file",
|
216
|
+
"Consider adding or updating tests for modified code",
|
217
|
+
"Ensure all security recommendations are addressed"
|
218
|
+
]
|
219
|
+
end
|
220
|
+
|
221
|
+
def output_report(report)
|
222
|
+
formatted_report = @formatter.format(report)
|
223
|
+
puts formatted_report
|
224
|
+
end
|
225
|
+
end
|
226
|
+
end
|
data/lib/code_sage.rb
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
require_relative "code_sage/version"
|
2
|
+
require_relative "code_sage/config"
|
3
|
+
require_relative "code_sage/cli"
|
4
|
+
require_relative "code_sage/reviewer"
|
5
|
+
require_relative "code_sage/git_analyzer"
|
6
|
+
require_relative "code_sage/report_formatter"
|
7
|
+
|
8
|
+
module CodeSage
|
9
|
+
class Error < StandardError; end
|
10
|
+
|
11
|
+
# Main entry point for the gem
|
12
|
+
def self.review(options = {})
|
13
|
+
Reviewer.new(options).review
|
14
|
+
end
|
15
|
+
end
|
metadata
ADDED
@@ -0,0 +1,179 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: code_sage
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- FuryCow
|
8
|
+
autorequire:
|
9
|
+
bindir: exe
|
10
|
+
cert_chain: []
|
11
|
+
date: 2025-07-01 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: llm_chain
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 0.5.2
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: 0.5.2
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: thor
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '1.0'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '1.0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: colorize
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0.8'
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0.8'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: rugged
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '1.0'
|
62
|
+
type: :runtime
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '1.0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: bundler
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - "~>"
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '2.0'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - "~>"
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '2.0'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: rake
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - "~>"
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '13.0'
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - "~>"
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '13.0'
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: rspec
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - "~>"
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '3.0'
|
104
|
+
type: :development
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - "~>"
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '3.0'
|
111
|
+
- !ruby/object:Gem::Dependency
|
112
|
+
name: pry
|
113
|
+
requirement: !ruby/object:Gem::Requirement
|
114
|
+
requirements:
|
115
|
+
- - ">="
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: '0'
|
118
|
+
type: :development
|
119
|
+
prerelease: false
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
121
|
+
requirements:
|
122
|
+
- - ">="
|
123
|
+
- !ruby/object:Gem::Version
|
124
|
+
version: '0'
|
125
|
+
description: Wisdom for your code - an intelligent code review assistant using LLM
|
126
|
+
email:
|
127
|
+
- info@furycow.com
|
128
|
+
executables:
|
129
|
+
- code_sage
|
130
|
+
extensions: []
|
131
|
+
extra_rdoc_files: []
|
132
|
+
files:
|
133
|
+
- ".github/workflows/gem.yml"
|
134
|
+
- ".gitignore"
|
135
|
+
- ".rspec"
|
136
|
+
- CHANGELOG.md
|
137
|
+
- Gemfile
|
138
|
+
- Gemfile.lock
|
139
|
+
- LICENSE
|
140
|
+
- README.md
|
141
|
+
- Rakefile
|
142
|
+
- bin/console
|
143
|
+
- bin/setup
|
144
|
+
- code_sage.gemspec
|
145
|
+
- exe/code_sage
|
146
|
+
- lib/code_sage.rb
|
147
|
+
- lib/code_sage/cli.rb
|
148
|
+
- lib/code_sage/config.rb
|
149
|
+
- lib/code_sage/git_analyzer.rb
|
150
|
+
- lib/code_sage/report_formatter.rb
|
151
|
+
- lib/code_sage/reviewer.rb
|
152
|
+
- lib/code_sage/version.rb
|
153
|
+
homepage: https://github.com/FuryCow/code_sage
|
154
|
+
licenses:
|
155
|
+
- MIT
|
156
|
+
metadata:
|
157
|
+
homepage_uri: https://github.com/FuryCow/code_sage
|
158
|
+
source_code_uri: https://github.com/FuryCow/code_sage
|
159
|
+
changelog_uri: https://github.com/FuryCow/code_sage/blob/master/CHANGELOG.md
|
160
|
+
post_install_message:
|
161
|
+
rdoc_options: []
|
162
|
+
require_paths:
|
163
|
+
- lib
|
164
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
165
|
+
requirements:
|
166
|
+
- - ">="
|
167
|
+
- !ruby/object:Gem::Version
|
168
|
+
version: 2.7.0
|
169
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
170
|
+
requirements:
|
171
|
+
- - ">="
|
172
|
+
- !ruby/object:Gem::Version
|
173
|
+
version: '0'
|
174
|
+
requirements: []
|
175
|
+
rubygems_version: 3.4.10
|
176
|
+
signing_key:
|
177
|
+
specification_version: 4
|
178
|
+
summary: AI-powered code review tool for Ruby
|
179
|
+
test_files: []
|