cleo_quality_review 0.1.0 → 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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 63b36db131462e8bedbfe7e7f1d42084a4f600464a6a6b881231a9254d0f4e5b
4
- data.tar.gz: 5722693b1abeaee01930f71c0e3d5d57333677c2d62bd3f4eaffc613094e3e7b
3
+ metadata.gz: 2f7c748370590a154e611c6302e8bbfd9e5c3f8dc1b756e938ccba09101b6ee5
4
+ data.tar.gz: 85e837b45af4b31c23e9f59d613653a481a5e172da0581df8edc96945c4f409c
5
5
  SHA512:
6
- metadata.gz: 31cf5bfcac10c05eb3f1297b132e60b1b730c951c382351879750f51362419df464c103f368e3571dfc6545070f37281827e477cf80af07420d08c8a3a7e4e37
7
- data.tar.gz: 956cba96beddc95e78ec88b27d0c7e2b25f0b94dd997f8a5ee8b137ab2a34ee7be42e75fb18300fdd5abfc5260978b428d280e6359f730c0cad8b406c58f40cc
6
+ metadata.gz: 7f5f53a18a2cd9ef9e6e7c3456b416d4b2f0dcab0d36d3c4d75e70c1c97bb408659918c89d57ab284faf05e7111cd06f0fc39f34f1be0d08f611f48945fe9ace
7
+ data.tar.gz: dabda2763af548f1ef2be712d6b3a0ec8ca1ca03330a249ea91b35972176a3f37785677dd39334ff207aae8374d586358a0c1c3a1a402f0e9ac94ab1c4dadff5
@@ -24,7 +24,6 @@ Gem::Specification.new do |spec|
24
24
  spec.executables = ["check_quality"]
25
25
  spec.require_paths = ["lib"]
26
26
 
27
- spec.add_dependency "debride"
28
27
  spec.add_dependency "fasterer"
29
28
  spec.add_dependency "flog"
30
29
  spec.add_dependency "reek"
@@ -2,7 +2,7 @@
2
2
 
3
3
  require "digest"
4
4
 
5
- require_relative "target_resolver"
5
+ require_relative "git_diff_base"
6
6
 
7
7
  module CleoQualityReview
8
8
  ##
@@ -11,9 +11,13 @@ module CleoQualityReview
11
11
  ##
12
12
  # @param [Array<String>] target_files files included in the review
13
13
  # @param [CommandRunner] command_runner for executing git commands
14
- def initialize(target_files:, command_runner:)
14
+ # @param [String] base_ref git ref to compare against
15
+ # @param [Boolean] strict_base whether unresolved refs should raise
16
+ def initialize(target_files:, command_runner:, base_ref: GitDiffBase::DEFAULT_BASE_REF, strict_base: false)
15
17
  @target_files = target_files
16
18
  @command_runner = command_runner
19
+ @base_ref = base_ref || GitDiffBase::DEFAULT_BASE_REF
20
+ @strict_base = strict_base
17
21
  end
18
22
 
19
23
  ##
@@ -30,7 +34,7 @@ module CleoQualityReview
30
34
 
31
35
  private
32
36
 
33
- attr_reader :command_runner, :target_files
37
+ attr_reader :command_runner, :target_files, :base_ref, :strict_base
34
38
 
35
39
  def tracked_changes_diff
36
40
  command = ["git", "diff", diff_base]
@@ -56,12 +60,7 @@ module CleoQualityReview
56
60
  end
57
61
 
58
62
  def diff_base
59
- @diff_base ||= begin
60
- result = command_runner.run("git", "merge-base", TargetResolver::BASE_REF, "HEAD")
61
- base = result.stdout.strip
62
-
63
- result.success? && !base.empty? ? base : TargetResolver::BASE_REF
64
- end
63
+ @diff_base ||= GitDiffBase.resolve(command_runner: command_runner, base_ref: base_ref, strict: strict_base)
65
64
  end
66
65
  end
67
66
  end
@@ -9,7 +9,6 @@ module CleoQualityReview
9
9
  require_relative "checks/reek"
10
10
  require_relative "checks/flog"
11
11
  require_relative "checks/fasterer"
12
- require_relative "checks/debride"
13
12
 
14
13
  class << self
15
14
  ##
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ module CleoQualityReview
4
+ ##
5
+ # Resolves git comparison bases for changed-file and diff capture flows
6
+ module GitDiffBase
7
+ DEFAULT_BASE_REF = "origin/main"
8
+
9
+ module_function
10
+
11
+ ##
12
+ # @param [CommandRunner] command_runner for executing git commands
13
+ # @param [String] base_ref git ref to compare against
14
+ # @param [Boolean] strict whether unresolved refs should raise
15
+ # @return [String] merge-base SHA, or the base ref when non-strict resolution fails
16
+ def resolve(command_runner:, base_ref:, strict:)
17
+ result = command_runner.run("git", "merge-base", base_ref, "HEAD")
18
+ base = result.stdout.strip
19
+
20
+ return base if result.success? && !base.empty?
21
+
22
+ raise ArgumentError, "Could not resolve quality review base ref: #{base_ref}" if strict
23
+
24
+ base_ref
25
+ end
26
+ end
27
+ end
@@ -2,6 +2,8 @@
2
2
 
3
3
  require "optparse"
4
4
 
5
+ require_relative "git_diff_base"
6
+
5
7
  module CleoQualityReview
6
8
  ##
7
9
  # Parses command-line options for the quality review CLI
@@ -23,7 +25,7 @@ module CleoQualityReview
23
25
  # @return [Array<String>] checks to exclude
24
26
  # @!attribute [r] changed
25
27
  # @return [Boolean] whether to filter to changed files only
26
- ParseResult = Struct.new(:format, :checks, :files, :exclude, :changed, :log, :review_id, :review_file, keyword_init: true) do
28
+ ParseResult = Struct.new(:format, :checks, :files, :exclude, :changed, :base, :log, :review_id, :review_file, keyword_init: true) do
27
29
  ##
28
30
  # @return [String] validated review_id
29
31
  # @raise [OptionParser::MissingArgument] if review_id is blank
@@ -58,6 +60,7 @@ module CleoQualityReview
58
60
  @files = []
59
61
  @exclude = []
60
62
  @changed = false
63
+ @base = GitDiffBase::DEFAULT_BASE_REF
61
64
  @log = false
62
65
  @review_id = nil
63
66
  @review_file = nil
@@ -78,6 +81,7 @@ module CleoQualityReview
78
81
  files: files,
79
82
  exclude: exclude,
80
83
  changed: changed,
84
+ base: base,
81
85
  log: log,
82
86
  review_id: review_id,
83
87
  review_file: review_file,
@@ -86,7 +90,7 @@ module CleoQualityReview
86
90
 
87
91
  private
88
92
 
89
- attr_reader :argv, :format, :checks, :files, :exclude, :changed, :log, :review_id, :review_file
93
+ attr_reader :argv, :format, :checks, :files, :exclude, :changed, :base, :log, :review_id, :review_file
90
94
 
91
95
  def parser
92
96
  OptionParser.new do |opts|
@@ -116,7 +120,7 @@ module CleoQualityReview
116
120
  end
117
121
 
118
122
  def register_checks_option(opts)
119
- opts.on("-c", "--checks CHECKS", Array, "Checks to run: all, reek, flog, fasterer, debride") { |values| checks.concat(values) }
123
+ opts.on("-c", "--checks CHECKS", Array, "Checks to run: all, reek, flog, fasterer") { |values| checks.concat(values) }
120
124
  end
121
125
 
122
126
  def register_only_option(opts)
@@ -124,7 +128,7 @@ module CleoQualityReview
124
128
  end
125
129
 
126
130
  def register_exclude_option(opts)
127
- opts.on("-x", "--exclude CHECKS", Array, "Checks to exclude: reek, flog, fasterer, debride") { |values| exclude.concat(values) }
131
+ opts.on("-x", "--exclude CHECKS", Array, "Checks to exclude: reek, flog, fasterer") { |values| exclude.concat(values) }
128
132
  end
129
133
 
130
134
  def register_target_options(opts)
@@ -132,9 +136,13 @@ module CleoQualityReview
132
136
  files.concat(values)
133
137
  end
134
138
 
135
- opts.on("--changed", "Only check files changed from main branch") do
139
+ opts.on("--changed", "Only check files changed from the base ref") do
136
140
  @changed = true
137
141
  end
142
+
143
+ opts.on("--base REF", "Git ref to compare changed files against") do |value|
144
+ @base = value
145
+ end
138
146
  end
139
147
 
140
148
  def register_output_options(opts)
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative "target_resolver"
3
+ require_relative "git_diff_base"
4
4
 
5
5
  module CleoQualityReview
6
6
  ##
@@ -46,7 +46,7 @@ module CleoQualityReview
46
46
  end
47
47
 
48
48
  def diff_section
49
- fenced("Git diff against #{TargetResolver::BASE_REF}", "diff", artifacts.changes_diff)
49
+ fenced("Git diff against #{run.base_ref || GitDiffBase::DEFAULT_BASE_REF}", "diff", artifacts.changes_diff)
50
50
  end
51
51
 
52
52
  def check_outputs_section
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require_relative "git_diff_base"
4
+
3
5
  module CleoQualityReview
4
6
  ##
5
7
  # Value object representing a quality review run with its configuration and results
@@ -25,6 +27,7 @@ module CleoQualityReview
25
27
  Run = Struct.new(
26
28
  :timestamp,
27
29
  :review_id,
30
+ :base_ref,
28
31
  :format,
29
32
  :checks,
30
33
  :target_files,
@@ -42,6 +45,7 @@ module CleoQualityReview
42
45
  {
43
46
  timestamp: timestamp,
44
47
  review_id: review_id,
48
+ base_ref: comparison_base_ref,
45
49
  format: format,
46
50
  checks: checks,
47
51
  target_files: target_files,
@@ -68,11 +72,18 @@ module CleoQualityReview
68
72
  def manifest_data
69
73
  {
70
74
  review_id: review_id,
75
+ base_ref: comparison_base_ref,
71
76
  timestamp: timestamp,
72
77
  checks: checks,
73
78
  target_files: target_files,
74
79
  ruby_files: ruby_files,
75
80
  }
76
81
  end
82
+
83
+ private
84
+
85
+ def comparison_base_ref
86
+ base_ref || GitDiffBase::DEFAULT_BASE_REF
87
+ end
77
88
  end
78
89
  end
@@ -5,6 +5,7 @@ require "fileutils"
5
5
 
6
6
  require_relative "result"
7
7
  require_relative "run"
8
+ require_relative "git_diff_base"
8
9
  require_relative "run_artifacts/raw_check_outputs"
9
10
 
10
11
  module CleoQualityReview
@@ -79,6 +80,7 @@ module CleoQualityReview
79
80
  Run.new(
80
81
  timestamp: manifest.fetch("timestamp"),
81
82
  review_id: manifest.fetch("review_id"),
83
+ base_ref: manifest.fetch("base_ref", GitDiffBase::DEFAULT_BASE_REF),
82
84
  format: format,
83
85
  checks: manifest.fetch("checks", []),
84
86
  target_files: target_files,
@@ -6,6 +6,7 @@ require "json"
6
6
  require_relative "changes_diff"
7
7
  require_relative "checks"
8
8
  require_relative "command_runner"
9
+ require_relative "git_diff_base"
9
10
  require_relative "run"
10
11
  require_relative "run_artifacts"
11
12
  require_relative "target_resolver"
@@ -16,12 +17,13 @@ module CleoQualityReview
16
17
  class Runner
17
18
  ##
18
19
  # Grouped values resolved at the start of an analysis run
19
- AnalysisContext = Struct.new(:timestamp, :target, :changes, :review_id, :check_classes, keyword_init: true) do
20
+ AnalysisContext = Struct.new(:timestamp, :base_ref, :target, :changes, :review_id, :check_classes, keyword_init: true) do
20
21
  ##
21
22
  # @return [Hash] run construction attributes derived from this context
22
23
  def run_attributes
23
24
  {
24
25
  timestamp: timestamp,
26
+ base_ref: base_ref,
25
27
  review_id: review_id,
26
28
  checks: check_classes.map(&:check_name),
27
29
  target_files: target.files,
@@ -69,21 +71,26 @@ module CleoQualityReview
69
71
 
70
72
  AnalysisContext.new(
71
73
  timestamp: timestamp,
74
+ base_ref: base_ref,
72
75
  target: target,
73
76
  changes: changes,
74
- review_id: review_id_for(changes, check_classes),
77
+ review_id: review_id_for(changes, check_classes, base_ref: base_ref),
75
78
  check_classes: check_classes,
76
79
  )
77
80
  end
78
81
 
79
82
  def resolve_target
80
83
  files = options.files
81
- changed = options.changed || files.empty?
82
- TargetResolver.new(command_runner: command_runner).resolve(files, changed: changed)
84
+ TargetResolver.new(command_runner: command_runner, base_ref: base_ref).resolve(files, changed: changed_mode?)
83
85
  end
84
86
 
85
87
  def changes_diff(target)
86
- ChangesDiff.new(target_files: target.files, command_runner: command_runner)
88
+ ChangesDiff.new(
89
+ target_files: target.files,
90
+ command_runner: command_runner,
91
+ base_ref: base_ref,
92
+ strict_base: changed_mode?,
93
+ )
87
94
  end
88
95
 
89
96
  def prepare_artifacts(context)
@@ -146,13 +153,24 @@ module CleoQualityReview
146
153
  artifacts.write_run(run)
147
154
  end
148
155
 
149
- def review_id_for(changes, check_classes)
156
+ def review_id_for(changes, check_classes, base_ref:)
157
+ payload = {
158
+ diff: changes.to_s,
159
+ checks: check_classes.map(&:check_name).sort,
160
+ }
161
+ payload[:base_ref] = base_ref unless base_ref == GitDiffBase::DEFAULT_BASE_REF
162
+
150
163
  Digest::SHA256.hexdigest(
151
- JSON.generate(
152
- diff: changes.to_s,
153
- checks: check_classes.map(&:check_name).sort,
154
- ),
164
+ JSON.generate(payload),
155
165
  )
156
166
  end
167
+
168
+ def changed_mode?
169
+ options.changed || options.files.empty?
170
+ end
171
+
172
+ def base_ref
173
+ options.base || GitDiffBase::DEFAULT_BASE_REF
174
+ end
157
175
  end
158
176
  end
@@ -1,12 +1,13 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require_relative "configuration"
4
+ require_relative "git_diff_base"
4
5
 
5
6
  module CleoQualityReview
6
7
  ##
7
8
  # Resolves target files for quality review based on git changes and configuration
8
9
  class TargetResolver
9
- BASE_REF = "origin/main"
10
+ BASE_REF = GitDiffBase::DEFAULT_BASE_REF
10
11
 
11
12
  ##
12
13
  # Value object containing resolved file lists
@@ -20,9 +21,11 @@ module CleoQualityReview
20
21
  ##
21
22
  # @param [CommandRunner] command_runner for executing git commands
22
23
  # @param [Configuration] configuration file filtering configuration
23
- def initialize(command_runner:, configuration: Configuration.load)
24
+ # @param [String] base_ref git ref to compare changed files against
25
+ def initialize(command_runner:, configuration: Configuration.load, base_ref: GitDiffBase::DEFAULT_BASE_REF)
24
26
  @command_runner = command_runner
25
27
  @configuration = configuration
28
+ @base_ref = base_ref || GitDiffBase::DEFAULT_BASE_REF
26
29
  end
27
30
 
28
31
  ##
@@ -41,7 +44,7 @@ module CleoQualityReview
41
44
 
42
45
  private
43
46
 
44
- attr_reader :command_runner, :configuration
47
+ attr_reader :command_runner, :configuration, :base_ref
45
48
 
46
49
  def resolve_target_files(files, changed:)
47
50
  candidates = resolve_candidates(files, changed: changed)
@@ -91,12 +94,7 @@ module CleoQualityReview
91
94
  end
92
95
 
93
96
  def diff_base
94
- @diff_base ||= begin
95
- result = command_runner.run("git", "merge-base", BASE_REF, "HEAD")
96
- base = result.stdout.strip
97
-
98
- result.success? && !base.empty? ? base : BASE_REF
99
- end
97
+ @diff_base ||= GitDiffBase.resolve(command_runner: command_runner, base_ref: base_ref, strict: true)
100
98
  end
101
99
 
102
100
  def expand_target_paths(paths)
@@ -3,5 +3,5 @@
3
3
  module CleoQualityReview
4
4
  ##
5
5
  # Gem version
6
- VERSION = "0.1.0"
6
+ VERSION = "0.3.0"
7
7
  end
@@ -14,7 +14,6 @@ module CleoQualityReview
14
14
  Checks.register("Reek", Checks::Reek, tool_type: :smell_detection)
15
15
  Checks.register("Flog", Checks::Flog, tool_type: :complexity)
16
16
  Checks.register("Fasterer", Checks::Fasterer, tool_type: :performance)
17
- Checks.register("Debride", Checks::Debride, tool_type: :dead_code)
18
17
 
19
18
  ##
20
19
  # Register all supported LLM APIs for formatting output here
data/prompts/agent.md CHANGED
@@ -7,7 +7,6 @@ Analyze the raw tool outputs and git diff provided. Prioritize actionable issues
7
7
  - **Flog**: Ignore scores below 40.0
8
8
  - **Reek**: Focus on FeatureEnvy, TooManyStatements, DuplicateMethodCall, NestedIterators, LongParameterList
9
9
  - **Fasterer**: Include all performance suggestions
10
- - **Debride**: Treat as lower-confidence static dead-code detection. Include only findings that are clearly actionable and avoid recommending deletion without checking dynamic call paths.
11
10
 
12
11
  ## Output Format
13
12
 
@@ -21,7 +20,7 @@ Output valid JSON matching this exact schema:
21
20
  "target_files": [<file paths from metadata>],
22
21
  "findings": [
23
22
  {
24
- "tool_name": "<reek|flog|fasterer|debride>",
23
+ "tool_name": "<reek|flog|fasterer>",
25
24
  "tool_type": "<smell_detection|complexity|performance|dead_code>",
26
25
  "check": "<specific check type>",
27
26
  "filepath": "<relative file path>",
@@ -33,7 +32,7 @@ Output valid JSON matching this exact schema:
33
32
  "check_outputs": [
34
33
  {
35
34
  "check_name": "<check name>",
36
- "tool_name": "<reek|flog|fasterer|debride>",
35
+ "tool_name": "<reek|flog|fasterer>",
37
36
  "tool_type": "<smell_detection|complexity|performance|dead_code>",
38
37
  "extension": "<json|txt>",
39
38
  "path": "<raw output artifact path>",
data/prompts/github.md CHANGED
@@ -17,10 +17,6 @@ Reek:
17
17
  Fasterer:
18
18
  Severity: Low
19
19
 
20
- Debride:
21
- Severity: Low
22
- Notes: Lower-confidence static dead-code signal. Only report when the finding is specific, actionable, and unlikely to be a dynamic Rails call.
23
-
24
20
 
25
21
  You MUST NOT return so many items that the feedback is noisy and confusing. Limit yourself to maximum 10 comments.
26
22
 
data/prompts/human.md CHANGED
@@ -1,10 +1,10 @@
1
1
  You are reviewing a local code change for code quality.
2
2
 
3
- The files provided include git diffs for local code changes, as well as generated output files from various code quality assessment tools including (but not limited to) Reek, Flog, Fasterer, Debride, etc.
3
+ The files provided include git diffs for local code changes, as well as generated output files from various code quality assessment tools including (but not limited to) Reek, Flog, Fasterer, etc.
4
4
 
5
5
  Your task is to parse the static output files generated by these tools, and provide feedback to the human user. The diff provided is to allow you to map tool output to changes in the code.
6
6
 
7
- YOU MUST NOT comment on the code diff itself, unless the comment is in relatinon to an issue reported by a tool.
7
+ YOU MUST NOT comment on the code diff itself, unless the comment is in relation to an issue reported by a tool.
8
8
 
9
9
  Prioritize issues that are likely to matter to maintainability, correctness, readability, or long-term ownership.
10
10
 
@@ -12,9 +12,8 @@ Avoid repeating tool output mechanically. If multiple issues of the same sort ar
12
12
 
13
13
  If a tool finding is low value or likely a false positive, say so briefly or omit it.
14
14
 
15
- Debride findings are lower-confidence static dead-code candidates. Do not recommend deleting code unless the finding is clearly supported by the changed code and dynamic call paths have been considered.
16
15
 
17
- The output will be printed in a unix terminal, and so colour-coded feedback is preferrable.
16
+ The output will be printed in a Unix terminal, and so colour-coded feedback is preferable.
18
17
 
19
18
  1. Highest-impact issues first, with file and line references as clickable links when available.
20
19
  2. Suggested changes that are specific enough for an engineer or coding agent to implement.
data/prompts/pr_review.md CHANGED
@@ -1,6 +1,6 @@
1
1
  You are the pipeline interface between code quality tools and GitHub pull request review comments.
2
2
 
3
- You will collate data from code quality tools including Reek, Flog, Fasterer, and Debride. The raw output is noisy, so your job is to identify only the most useful comments for the engineer whose PR triggered this flow.
3
+ You will collate data from code quality tools including Reek, Flog, and Fasterer. The raw output is noisy, so your job is to identify only the most useful comments for the engineer whose PR triggered this flow.
4
4
 
5
5
  You MUST NOT comment on the code diff itself unless the comment is directly supported by a tool finding.
6
6
 
@@ -9,7 +9,6 @@ You MUST NOT comment on the code diff itself unless the comment is directly supp
9
9
  - **Flog**: Ignore scores below 40.0. Prioritize high-complexity methods because they are the most expensive to maintain.
10
10
  - **Reek**: Prefer actionable smells such as FeatureEnvy, TooManyStatements, DuplicateMethodCall, NestedIterators, and LongParameterList.
11
11
  - **Fasterer**: Low severity. Include only when the finding is clearly on code changed by this PR and the fix is straightforward.
12
- - **Debride**: Lower-confidence static dead-code signal. Include only when the candidate method is clearly made obsolete by this PR, and do not suggest deletion without noting possible dynamic Rails calls.
13
12
 
14
13
  ## Comment Selection
15
14
 
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: cleo_quality_review
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Gavin Morrice
@@ -9,20 +9,6 @@ bindir: exe
9
9
  cert_chain: []
10
10
  date: 1980-01-02 00:00:00.000000000 Z
11
11
  dependencies:
12
- - !ruby/object:Gem::Dependency
13
- name: debride
14
- requirement: !ruby/object:Gem::Requirement
15
- requirements:
16
- - - ">="
17
- - !ruby/object:Gem::Version
18
- version: '0'
19
- type: :runtime
20
- prerelease: false
21
- version_requirements: !ruby/object:Gem::Requirement
22
- requirements:
23
- - - ">="
24
- - !ruby/object:Gem::Version
25
- version: '0'
26
12
  - !ruby/object:Gem::Dependency
27
13
  name: fasterer
28
14
  requirement: !ruby/object:Gem::Requirement
@@ -80,7 +66,6 @@ files:
80
66
  - lib/cleo_quality_review.rb
81
67
  - lib/cleo_quality_review/changes_diff.rb
82
68
  - lib/cleo_quality_review/checks.rb
83
- - lib/cleo_quality_review/checks/debride.rb
84
69
  - lib/cleo_quality_review/checks/fasterer.rb
85
70
  - lib/cleo_quality_review/checks/flog.rb
86
71
  - lib/cleo_quality_review/checks/quality_check.rb
@@ -92,6 +77,7 @@ files:
92
77
  - lib/cleo_quality_review/configuration.rb
93
78
  - lib/cleo_quality_review/diff_map.rb
94
79
  - lib/cleo_quality_review/formatter.rb
80
+ - lib/cleo_quality_review/git_diff_base.rb
95
81
  - lib/cleo_quality_review/github_review_builder.rb
96
82
  - lib/cleo_quality_review/github_review_publisher.rb
97
83
  - lib/cleo_quality_review/llm_client.rb
@@ -1,65 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "json"
4
-
5
- require_relative "quality_check"
6
-
7
- module CleoQualityReview
8
- module Checks
9
- ##
10
- # Quality check implementation for Debride unused-code analyzer
11
- class Debride < QualityCheck
12
- self.check_name = "dead_code"
13
- self.tool_name = "debride"
14
- self.output_extension = "json"
15
-
16
- private
17
-
18
- def command(files)
19
- [ruby_executable, gem_executable("debride", "debride"), "--json", "--rails", *files]
20
- end
21
-
22
- def parse(stdout, stderr)
23
- findings = missing_methods(stdout).flat_map do |class_name, methods|
24
- results_for_class(class_name, methods)
25
- end
26
- return findings unless findings.empty? && stderr.to_s.strip != ""
27
-
28
- [result(check: "Execution error", message: stderr, filepath: nil)]
29
- end
30
-
31
- def missing_methods(stdout)
32
- parsed = JSON.parse(stdout.to_s)
33
- missing = parsed.fetch("missing", {})
34
- return {} unless missing.is_a?(Hash)
35
-
36
- missing
37
- rescue JSON::ParserError
38
- {}
39
- end
40
-
41
- def results_for_class(class_name, methods)
42
- Array(methods).map { |entry| method_to_result(class_name, entry) }
43
- end
44
-
45
- def method_to_result(class_name, entry)
46
- method_name, location = Array(entry)
47
- filepath, line = parse_location(location)
48
-
49
- result(
50
- check: "PotentialDeadMethod",
51
- message: "#{class_name}##{method_name} might not be called",
52
- filepath: filepath,
53
- line: line,
54
- )
55
- end
56
-
57
- def parse_location(location)
58
- match = location.to_s.match(/\A(?<filepath>.*):(?<line>\d+)(?:-\d+)?\z/)
59
- return [nil, nil] unless match
60
-
61
- match.values_at(:filepath, :line)
62
- end
63
- end
64
- end
65
- end