simplecov-ai 0.10.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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 03eef8493da2d38018d6be59cde9dccf4cf0811cd916183fc99a3af4b194bef9
4
+ data.tar.gz: fe85e118ff57bf66a74a6bd9f27ee2f778b0f3a77d745e38dce36909a2cfb637
5
+ SHA512:
6
+ metadata.gz: 2ef39fea3f09836adb1b9ace8cc556578b0b301a9704168328193880370942d93d19e0ad527ed2f062ab67d669d2814a92fd9df421db4d8d5589a5a5b4b29eee
7
+ data.tar.gz: 6690600cd0625a0f3ed9df1b7a6aa65018b1b5bba9fd89f7e1eeb4f7db316110f6201f537bad18cc7adccde819b2d88cd6a3cde5fbd0ddf0f85f0a861d98edc2
checksums.yaml.gz.sig ADDED
Binary file
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Vitalii Lazebnyi
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 @@
1
+ # simplecov-ai
@@ -0,0 +1,25 @@
1
+ -----BEGIN CERTIFICATE-----
2
+ MIIEOTCCAqGgAwIBAgIUBONmsFo7fxLGkUHsKe65onH+5ogwDQYJKoZIhvcNAQEL
3
+ BQAwLDEqMCgGA1UEAwwhdml0YWxpaS5sYXplYm55aS5naXRodWJAZ21haWwuY29t
4
+ MB4XDTI2MDQxNTEzNTMyOVoXDTM2MDQxMjEzNTMyOVowLDEqMCgGA1UEAwwhdml0
5
+ YWxpaS5sYXplYm55aS5naXRodWJAZ21haWwuY29tMIIBojANBgkqhkiG9w0BAQEF
6
+ AAOCAY8AMIIBigKCAYEA5zdezJE+Zrsk9j53/IxBfRoaqvLcPvrcfl+EaEwWhIkV
7
+ 0+08GtgS9N7VpB8cgaH2rkLJPjHIetsN/g5GMkDRsbJNXMrPVhxe1e1lI/r6j0Tm
8
+ JD0PaU4r8VzitxkqY9BBmSI8GjDjAfrT1u5jSXH1iAtKUoq5F116uYrxbgiDpvqa
9
+ kUQYcTf+6cZaPlF4KKhULnhKqs8u/NxyH4vPZyxEfg/gA4bODvcjW1A6d59BTiLV
10
+ yrJPebwU+F+URb8aoQ4AGvPKFiG1Y1fxRHuPrOpyymFnBnjwgMyQkNHtzTeEriV9
11
+ z1BUb10Pb/pjLBCrOvnStTPmcm1GE8HL2psYvlLvBlYqq3gzpQPBBKE3Jefa7ilC
12
+ cYsBYOGpynpA9uu9cXKa4jtpPDGQ7Qrpnk9gHy/0xfbgLdAkRCoZJeR7wDL/1xmm
13
+ nXwcUOLSOBj1Y4P9M+uQSQUZFTAaLbwyaBfE1gvVjwbTv3+rNP1ck1hACt+numGG
14
+ m7R6MF+Hmh8pNnDBYpBNAgMBAAGjUzBRMB0GA1UdDgQWBBRbuaz1EhdG6T4KIeWr
15
+ ac8LULxO9zAfBgNVHSMEGDAWgBRbuaz1EhdG6T4KIeWrac8LULxO9zAPBgNVHRMB
16
+ Af8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBgQBgfGTDIMxlm6o8o7dzCR0HosRm
17
+ DSeUrx46EG1knTEqO05CooEHW98hrHa1/EwzkPaH1KhjjserQb6VtczMnySlfySu
18
+ HbKWAIaqzlpf8zaE5tCiAKgFKr77b2XB7xKt25p/Vf/Kn/RLm3+sYQ2izzzMimei
19
+ tBHo29cLV9bB/5HHFDwjrtdC5a0HJHiir0w4MCSDDGtnsKird4RKD2xESpoVjiNg
20
+ L9nEGk25YDeIfKn8UtxduMv53T86CiBSsDcEb6oVjNiMOA0HFucwFKX+Vy5u0/qx
21
+ ZRoLbZiCkTTGyNkBh4o6RCCTn37Lj98FBxYMbAHLNhEcKnAGxB7XP/CYsV4+QHOy
22
+ h0PctylhIvm24QeKgIWJUWamFPfqdvlP660T4umxl2wMqvNpWmGMmGTMCraoKwxl
23
+ zpp6uA15MXgTU7CxGivRgUKM64TqBMZKkOJcCtPkruSobxiR8cROrBNTqEbrmedM
24
+ 26EUEoxwDzfSzHU2SKz5pMR+8DClMUKB1rctg68=
25
+ -----END CERTIFICATE-----
@@ -0,0 +1,140 @@
1
+ # typed: strict
2
+ # frozen_string_literal: true
3
+
4
+ require 'parser/current'
5
+
6
+ module SimpleCov
7
+ module Formatter
8
+ class AIFormatter
9
+ # Employs statically-parsed Abstract Syntax Tree processing via the `parser` gem
10
+ # to correlate raw line-based deficits with high-level semantically meaningful concepts
11
+ # like Classes and Methods. This negates the line-number volatility often experienced
12
+ # by Large Language Models when patching test coverage.
13
+ class ASTResolver
14
+ extend T::Sig
15
+
16
+ # An immutable struct housing bounds, identification metrics, and static bypassing
17
+ # definitions derived from traversing the AST nodes.
18
+ class SemanticNode
19
+ extend T::Sig
20
+
21
+ sig { returns(String) }
22
+ attr_reader :name, :type
23
+
24
+ sig { returns(Integer) }
25
+ attr_reader :start_line, :end_line
26
+
27
+ sig { returns(T::Array[String]) }
28
+ attr_reader :bypasses
29
+
30
+ sig do
31
+ params(
32
+ name: String,
33
+ type: String,
34
+ start_line: Integer,
35
+ end_line: Integer,
36
+ bypasses: T::Array[String]
37
+ ).void
38
+ end
39
+ def initialize(name:, type:, start_line:, end_line:, bypasses: [])
40
+ @name = name
41
+ @type = type
42
+ @start_line = start_line
43
+ @end_line = end_line
44
+ @bypasses = bypasses
45
+ end
46
+ end
47
+
48
+ # Orchestrates the initial mapping algorithm on a target file to extract structural
49
+ # metadata, circumventing potential syntax violations explicitly.
50
+ #
51
+ # @param file_path [String] The absolute path to the Ruby script to parse.
52
+ # @return [Array<SemanticNode>] A collection of resolvable structural entities.
53
+ sig { params(file_path: String).returns(T::Array[SemanticNode]) }
54
+ def self.resolve(file_path)
55
+ return [] unless File.exist?(file_path)
56
+
57
+ begin
58
+ source = File.read(file_path)
59
+ ast, comments = Parser::CurrentRuby.parse_with_comments(source)
60
+ new.traverse(ast, comments)
61
+ rescue Parser::SyntaxError
62
+ []
63
+ end
64
+ end
65
+
66
+ # Recursively navigates an abstract node hierarchy, building SemanticNodes mappings
67
+ # around modules, classes, singleton, and instance methods while aggregating parent paths.
68
+ #
69
+ # @param node [Parser::AST::Node] The root AST node from which traversal executes.
70
+ # @param comments [Array<Parser::Source::Comment>] Lexical comments corresponding to nodes.
71
+ # @param context [String] An accumulated identifier linking namespaces to inner entities.
72
+ # @return [Array<SemanticNode>] Accumulation of all sub-tree defined endpoints.
73
+ sig do
74
+ params(node: Parser::AST::Node, comments: T::Array[Parser::Source::Comment],
75
+ context: String).returns(T::Array[SemanticNode])
76
+ end
77
+ def traverse(node, comments, context = '')
78
+ return [] unless node.is_a?(Parser::AST::Node)
79
+
80
+ nodes = T.let([], T::Array[SemanticNode])
81
+ current_context = context
82
+
83
+ case node.type
84
+ when :class, :module
85
+ const_node = T.cast(node.children[0], Parser::AST::Node)
86
+ const_node_loc = T.cast(const_node.loc, Parser::Source::Map::Constant)
87
+ const_node_name = T.cast(const_node_loc.name, Parser::Source::Range)
88
+ name = T.cast(const_node_name.source, String)
89
+ current_context = context.empty? ? name : "#{context}::#{name}"
90
+ nodes << build_node(node, comments, current_context, node.type.to_s.capitalize)
91
+ when :def
92
+ name = T.cast(node.children.first, Symbol).to_s
93
+ current_context = context.empty? ? "##{name}" : "#{context}##{name}"
94
+ nodes << build_node(node, comments, current_context, 'Instance Method')
95
+ when :defs
96
+ name = T.cast(node.children[1], Symbol).to_s
97
+ current_context = context.empty? ? ".#{name}" : "#{context}.#{name}"
98
+ nodes << build_node(node, comments, current_context, 'Singleton Method')
99
+ end
100
+
101
+ node.children.each do |child|
102
+ case child
103
+ when Parser::AST::Node
104
+ nodes.concat(traverse(child, comments, current_context))
105
+ end
106
+ end
107
+
108
+ nodes
109
+ end
110
+
111
+ private
112
+
113
+ sig do
114
+ params(node: Parser::AST::Node, comments: T::Array[Parser::Source::Comment], name: String,
115
+ type: String).returns(SemanticNode)
116
+ end
117
+ def build_node(node, comments, name, type)
118
+ node_loc = T.cast(node.loc, Parser::Source::Map)
119
+ node_line = T.cast(node_loc.line, Integer)
120
+ node_last_line = T.cast(node_loc.last_line, Integer)
121
+
122
+ bypasses = comments.select do |c|
123
+ c_loc = T.cast(c.loc, Parser::Source::Map)
124
+ c_line = T.cast(c_loc.line, Integer)
125
+ c_text = T.cast(c.text, String)
126
+ c_line >= node_line - 1 && c_line <= node_last_line + 1 && c_text.include?(':nocov:')
127
+ end.map { |c| T.cast(c.text, String).strip }
128
+
129
+ SemanticNode.new(
130
+ name: name,
131
+ type: type,
132
+ start_line: node_line,
133
+ end_line: node_last_line,
134
+ bypasses: bypasses
135
+ )
136
+ end
137
+ end
138
+ end
139
+ end
140
+ end
@@ -0,0 +1,57 @@
1
+ # typed: strict
2
+ # frozen_string_literal: true
3
+
4
+ module SimpleCov
5
+ module Formatter
6
+ class AIFormatter
7
+ # Encapsulates all global tuning parameters that dictate the execution size,
8
+ # structure, and output verbosity of the AST-driven Markdown report generator.
9
+ # Exposes strongly-typed attributes through Sorbet to preempt runtime invalidities.
10
+ class Configuration
11
+ extend T::Sig
12
+
13
+ # The absolute or relative system path where the final token-efficient markdown
14
+ # document acts as an artifact.
15
+ # @return [String]
16
+ sig { returns(String) }
17
+ attr_accessor :report_path
18
+
19
+ # The maximum allowed byte limit to prevent the generation pipeline from overflowing
20
+ # LLM token bounds before terminating the traversal algorithm.
21
+ # @return [Integer]
22
+ sig { returns(Integer) }
23
+ attr_accessor :max_file_size_kb
24
+
25
+ # Limits the number of lines included in code snippets to conserve token usage
26
+ # while maintaining enough structural context for the AI to reason about the logic.
27
+ sig { returns(Integer) }
28
+ attr_accessor :max_snippet_lines
29
+
30
+ # Determines whether the generated markdown report is printed directly to standard output,
31
+ # facilitating pipeline integrations where artifacts are piped rather than read from disk.
32
+ sig { returns(T::Boolean) }
33
+ attr_accessor :output_to_console
34
+
35
+ # Specifies the level of detail in the coverage report (e.g., :fine, :coarse)
36
+ # to balance between comprehensive reporting and strict token constraints.
37
+ sig { returns(Symbol) }
38
+ attr_accessor :granularity
39
+
40
+ # Controls whether to include lines skipped via coverage bypass directives (e.g., :nocov:),
41
+ # allowing the AI to audit skipped regions for potential testing mandate violations.
42
+ sig { returns(T::Boolean) }
43
+ attr_accessor :include_bypasses
44
+
45
+ sig { void }
46
+ def initialize
47
+ @report_path = T.let('coverage/ai_report.md', String)
48
+ @max_file_size_kb = T.let(50, Integer)
49
+ @max_snippet_lines = T.let(5, Integer)
50
+ @output_to_console = T.let(false, T::Boolean)
51
+ @granularity = T.let(:fine, Symbol)
52
+ @include_bypasses = T.let(true, T::Boolean)
53
+ end
54
+ end
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,155 @@
1
+ # typed: strict
2
+ # frozen_string_literal: true
3
+
4
+ module SimpleCov
5
+ module Formatter
6
+ class AIFormatter
7
+ # Responsible for compiling static text representations from evaluated coverage metrics,
8
+ # optimizing layout size, orchestrating string IO buffers, and halting upon token exhaustion.
9
+ # Serves as the primary mutation boundary to format AI consumption targets.
10
+ class MarkdownBuilder
11
+ extend T::Sig
12
+
13
+ # Initializes the Markdown sequence compilation.
14
+ #
15
+ # @param result [SimpleCov::Result] Application-wide coverage aggregation metrics
16
+ # @param config [Configuration] Pre-registered runtime toggles
17
+ sig { params(result: SimpleCov::Result, config: Configuration).void }
18
+ def initialize(result, config)
19
+ @result = T.let(result, SimpleCov::Result)
20
+ @config = T.let(config, Configuration)
21
+ @buffer = T.let(StringIO.new, StringIO)
22
+ @file_count = T.let(0, Integer)
23
+ @truncated = T.let(false, T::Boolean)
24
+ end
25
+
26
+ # Executes the primary buffer composition logic yielding a monolithic compiled output.
27
+ # Deficits are intrinsically sorted to surface the most crucial test gaps immediately.
28
+ #
29
+ # @return [String] Synthesized string digest of resolved target files and metrics
30
+ sig { returns(String) }
31
+ def build
32
+ write_header
33
+ write_deficits
34
+ write_bypasses if @config.include_bypasses
35
+ write_truncation_warning if @truncated
36
+ @buffer.string
37
+ end
38
+
39
+ private
40
+
41
+ sig { void }
42
+ def write_header
43
+ status = T.cast(@result.covered_percent, Float) >= 100.0 ? 'PASSED' : 'FAILED'
44
+ time_str = Time.now.to_s # UI timezone requirement
45
+
46
+ @buffer.puts '# AI Coverage Digest'
47
+ @buffer.puts "**Status:** #{status}"
48
+ @buffer.puts "**Global Line Coverage:** #{T.cast(@result.covered_percent, Float).round(1)}%"
49
+
50
+ branch_pct = begin
51
+ T.cast(@result.covered_branches, Float) / T.cast(@result.total_branches, Numeric) * 100
52
+ rescue StandardError
53
+ 0.0
54
+ end
55
+ @buffer.puts "**Global Branch Coverage:** #{T.cast(branch_pct, Float).round(1)}%"
56
+ @buffer.puts "**Generated At:** #{time_str}"
57
+ @buffer.puts ''
58
+ end
59
+
60
+ sig { void }
61
+ def write_deficits
62
+ @buffer.puts "## Coverage Deficits\n\n"
63
+
64
+ # SCMD-REQ-014: Sort by coverage percent ASC, then by filename
65
+ files = T.let(
66
+ T.cast(@result.files, T::Array[SimpleCov::SourceFile]).reject { |f| T.cast(f.covered_percent, Float) >= 100.0 }
67
+ .sort_by { |f| [T.cast(f.covered_percent, Float), T.cast(f.filename, String)] },
68
+ T::Array[SimpleCov::SourceFile]
69
+ )
70
+
71
+ files.each do |file|
72
+ # Check size limits SCMD-REQ-012
73
+ if @buffer.size / 1024.0 > @config.max_file_size_kb
74
+ @truncated = true
75
+ break
76
+ end
77
+
78
+ @buffer.puts "### `#{T.cast(file.project_filename, String)}`"
79
+
80
+ begin
81
+ nodes = ASTResolver.resolve(T.cast(file.filename, String))
82
+ rescue StandardError => e
83
+ @buffer.puts "- **ERROR:** AST Parsing Failed (`#{e.class}`)"
84
+ next
85
+ end
86
+
87
+ process_deficits(file, nodes)
88
+ end
89
+ end
90
+
91
+ sig { params(file: SimpleCov::SourceFile, nodes: T::Array[ASTResolver::SemanticNode]).void }
92
+ def process_deficits(file, nodes)
93
+ T.cast(file.missed_lines, T::Array[SimpleCov::SourceFile::Line]).each do |line|
94
+ line_num = T.cast(line.line_number, Integer)
95
+ node = nodes.find { |n| line_num >= n.start_line && line_num <= n.end_line }
96
+ node_name = node ? node.name : "Line #{line_num}"
97
+ @buffer.puts "- `#{node_name}`\n - **Line Deficit:** Unexecuted code."
98
+ end
99
+
100
+ if file.respond_to?(:branches) && T.cast(file.branches, T::Boolean)
101
+ T.cast(file.missed_branches, T::Array[SimpleCov::SourceFile::Branch]).each do |branch|
102
+ start_line = T.cast(branch.start_line, Integer)
103
+ end_line = T.cast(branch.end_line, Integer)
104
+ node = nodes.find { |n| start_line >= n.start_line && end_line <= n.end_line }
105
+ node_name = node ? node.name : "Lines #{start_line}-#{end_line}"
106
+ @buffer.puts "- `#{node_name}`\n - **Branch Deficit:** Missing coverage for conditional."
107
+ end
108
+ end
109
+
110
+ @buffer.puts ''
111
+ end
112
+
113
+ sig { void }
114
+ def write_bypasses
115
+ has_bypasses = T.let(false, T::Boolean)
116
+ bypass_buffer = T.let(StringIO.new, StringIO)
117
+
118
+ T.cast(@result.files, T::Array[SimpleCov::SourceFile]).each do |file|
119
+ begin
120
+ nodes = ASTResolver.resolve(T.cast(file.filename, String))
121
+ rescue StandardError
122
+ next
123
+ end
124
+
125
+ nodes_with_bypasses = nodes.select { |n| n.bypasses.any? }
126
+ next if nodes_with_bypasses.empty?
127
+
128
+ has_bypasses = true
129
+ write_file_bypasses(bypass_buffer, file, nodes_with_bypasses)
130
+ end
131
+
132
+ return unless has_bypasses
133
+
134
+ @buffer.puts "## Ignored Coverage Bypasses\n\n"
135
+ @buffer.puts bypass_buffer.string
136
+ end
137
+
138
+ sig { params(buffer: StringIO, file: SimpleCov::SourceFile, bypasses: T::Array[ASTResolver::SemanticNode]).void }
139
+ def write_file_bypasses(buffer, file, bypasses)
140
+ buffer.puts "### `#{file.project_filename}`"
141
+ bypasses.each do |node|
142
+ buffer.puts "- `#{node.name}`\n - **Bypass Present:** Contains `# :nocov:` directive artificially ignoring coverage."
143
+ end
144
+ buffer.puts ''
145
+ end
146
+
147
+ sig { void }
148
+ def write_truncation_warning
149
+ @buffer.puts '> **[WARNING] TRUNCATION NOTIFICATION:**'
150
+ @buffer.puts "> The total coverage deficit report exceeded the maximum token constraint (#{@config.max_file_size_kb} kB). The report was truncated. The deficits detailed above represent the lowest-coverage (most critical) files. Please resolve these deficits to reveal the remaining uncovered files in subsequent test runs."
151
+ end
152
+ end
153
+ end
154
+ end
155
+ end
@@ -0,0 +1,14 @@
1
+ # typed: strict
2
+ # frozen_string_literal: true
3
+
4
+ require 'sorbet-runtime'
5
+
6
+ module SimpleCov
7
+ module Formatter
8
+ class AIFormatter
9
+ # The semantic version identifier for the gem, used for dependency resolution
10
+ # and enforcing compatibility across upgrades.
11
+ VERSION = T.let('0.10.0', String)
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,64 @@
1
+ # typed: strict
2
+ # frozen_string_literal: true
3
+
4
+ require 'sorbet-runtime'
5
+ require 'simplecov'
6
+ require 'parser/current'
7
+
8
+ require_relative 'simplecov-ai/version'
9
+ require_relative 'simplecov-ai/configuration'
10
+ require_relative 'simplecov-ai/ast_resolver'
11
+ require_relative 'simplecov-ai/markdown_builder'
12
+
13
+ # The root namespace for the simplecov test coverage library.
14
+ module SimpleCov
15
+ # Namespace containing all reporting formatters.
16
+ module Formatter
17
+ # Transforms raw simplecov coverage results into an AI-optimized markdown digest.
18
+ # It addresses the context window limitations of LLMs by emitting token-efficient
19
+ # reports and relying on AST-based semantic mapping to pinpoint exact methods
20
+ # or classes with deficient coverage.
21
+ class AIFormatter
22
+ extend T::Sig
23
+
24
+ # Retrieves the global configuration for the AI formatter.
25
+ # The instantiation pattern ensures that defaults are securely lazily loaded
26
+ # before any coverage processing begins.
27
+ #
28
+ # @return [SimpleCov::Formatter::AIFormatter::Configuration] The active configuration state.
29
+ sig { returns(SimpleCov::Formatter::AIFormatter::Configuration) }
30
+ def self.configuration
31
+ @configuration ||= T.let(Configuration.new, T.nilable(Configuration))
32
+ end
33
+
34
+ # Yields the configuration object to a block, allowing consumers to override
35
+ # default formatting behaviors, output paths, and token constraints before execution.
36
+ #
37
+ # @yieldparam config [Configuration] The global formatter configuration.
38
+ # @return [void]
39
+ sig { params(blk: T.nilable(T.proc.params(config: Configuration).void)).void }
40
+ def self.configure(&blk)
41
+ blk&.call(configuration)
42
+ end
43
+
44
+ # Orchestrates the formatting lifecycle to synthesize the markdown output.
45
+ # It converts the raw result data using the internal MarkdownBuilder, enforces
46
+ # directory structures, and securely persists the result to disk to guarantee
47
+ # an idempotent and strictly defined output location.
48
+ #
49
+ # @param result [SimpleCov::Result] The test coverage outcome generated by SimpleCov.
50
+ # @return [void]
51
+ sig { params(result: SimpleCov::Result).void }
52
+ def format(result)
53
+ config = self.class.configuration
54
+ builder = MarkdownBuilder.new(result, config)
55
+ digest = builder.build
56
+
57
+ FileUtils.mkdir_p(File.dirname(config.report_path))
58
+ File.write(config.report_path, digest)
59
+
60
+ puts "\n[SimpleCov AI Formatter] Digest written to #{config.report_path}" if config.output_to_console
61
+ end
62
+ end
63
+ end
64
+ end
data.tar.gz.sig ADDED
Binary file
metadata ADDED
@@ -0,0 +1,303 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: simplecov-ai
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.10.0
5
+ platform: ruby
6
+ authors:
7
+ - Vitalii Lazebnyi
8
+ bindir: bin
9
+ cert_chain:
10
+ - |
11
+ -----BEGIN CERTIFICATE-----
12
+ MIIEOTCCAqGgAwIBAgIUBONmsFo7fxLGkUHsKe65onH+5ogwDQYJKoZIhvcNAQEL
13
+ BQAwLDEqMCgGA1UEAwwhdml0YWxpaS5sYXplYm55aS5naXRodWJAZ21haWwuY29t
14
+ MB4XDTI2MDQxNTEzNTMyOVoXDTM2MDQxMjEzNTMyOVowLDEqMCgGA1UEAwwhdml0
15
+ YWxpaS5sYXplYm55aS5naXRodWJAZ21haWwuY29tMIIBojANBgkqhkiG9w0BAQEF
16
+ AAOCAY8AMIIBigKCAYEA5zdezJE+Zrsk9j53/IxBfRoaqvLcPvrcfl+EaEwWhIkV
17
+ 0+08GtgS9N7VpB8cgaH2rkLJPjHIetsN/g5GMkDRsbJNXMrPVhxe1e1lI/r6j0Tm
18
+ JD0PaU4r8VzitxkqY9BBmSI8GjDjAfrT1u5jSXH1iAtKUoq5F116uYrxbgiDpvqa
19
+ kUQYcTf+6cZaPlF4KKhULnhKqs8u/NxyH4vPZyxEfg/gA4bODvcjW1A6d59BTiLV
20
+ yrJPebwU+F+URb8aoQ4AGvPKFiG1Y1fxRHuPrOpyymFnBnjwgMyQkNHtzTeEriV9
21
+ z1BUb10Pb/pjLBCrOvnStTPmcm1GE8HL2psYvlLvBlYqq3gzpQPBBKE3Jefa7ilC
22
+ cYsBYOGpynpA9uu9cXKa4jtpPDGQ7Qrpnk9gHy/0xfbgLdAkRCoZJeR7wDL/1xmm
23
+ nXwcUOLSOBj1Y4P9M+uQSQUZFTAaLbwyaBfE1gvVjwbTv3+rNP1ck1hACt+numGG
24
+ m7R6MF+Hmh8pNnDBYpBNAgMBAAGjUzBRMB0GA1UdDgQWBBRbuaz1EhdG6T4KIeWr
25
+ ac8LULxO9zAfBgNVHSMEGDAWgBRbuaz1EhdG6T4KIeWrac8LULxO9zAPBgNVHRMB
26
+ Af8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBgQBgfGTDIMxlm6o8o7dzCR0HosRm
27
+ DSeUrx46EG1knTEqO05CooEHW98hrHa1/EwzkPaH1KhjjserQb6VtczMnySlfySu
28
+ HbKWAIaqzlpf8zaE5tCiAKgFKr77b2XB7xKt25p/Vf/Kn/RLm3+sYQ2izzzMimei
29
+ tBHo29cLV9bB/5HHFDwjrtdC5a0HJHiir0w4MCSDDGtnsKird4RKD2xESpoVjiNg
30
+ L9nEGk25YDeIfKn8UtxduMv53T86CiBSsDcEb6oVjNiMOA0HFucwFKX+Vy5u0/qx
31
+ ZRoLbZiCkTTGyNkBh4o6RCCTn37Lj98FBxYMbAHLNhEcKnAGxB7XP/CYsV4+QHOy
32
+ h0PctylhIvm24QeKgIWJUWamFPfqdvlP660T4umxl2wMqvNpWmGMmGTMCraoKwxl
33
+ zpp6uA15MXgTU7CxGivRgUKM64TqBMZKkOJcCtPkruSobxiR8cROrBNTqEbrmedM
34
+ 26EUEoxwDzfSzHU2SKz5pMR+8DClMUKB1rctg68=
35
+ -----END CERTIFICATE-----
36
+ date: 1980-01-02 00:00:00.000000000 Z
37
+ dependencies:
38
+ - !ruby/object:Gem::Dependency
39
+ name: parser
40
+ requirement: !ruby/object:Gem::Requirement
41
+ requirements:
42
+ - - ">="
43
+ - !ruby/object:Gem::Version
44
+ version: 3.1.0
45
+ type: :runtime
46
+ prerelease: false
47
+ version_requirements: !ruby/object:Gem::Requirement
48
+ requirements:
49
+ - - ">="
50
+ - !ruby/object:Gem::Version
51
+ version: 3.1.0
52
+ - !ruby/object:Gem::Dependency
53
+ name: sorbet-runtime
54
+ requirement: !ruby/object:Gem::Requirement
55
+ requirements:
56
+ - - "~>"
57
+ - !ruby/object:Gem::Version
58
+ version: '0.5'
59
+ type: :runtime
60
+ prerelease: false
61
+ version_requirements: !ruby/object:Gem::Requirement
62
+ requirements:
63
+ - - "~>"
64
+ - !ruby/object:Gem::Version
65
+ version: '0.5'
66
+ - !ruby/object:Gem::Dependency
67
+ name: simplecov
68
+ requirement: !ruby/object:Gem::Requirement
69
+ requirements:
70
+ - - ">="
71
+ - !ruby/object:Gem::Version
72
+ version: 0.18.0
73
+ type: :runtime
74
+ prerelease: false
75
+ version_requirements: !ruby/object:Gem::Requirement
76
+ requirements:
77
+ - - ">="
78
+ - !ruby/object:Gem::Version
79
+ version: 0.18.0
80
+ - !ruby/object:Gem::Dependency
81
+ name: base64
82
+ requirement: !ruby/object:Gem::Requirement
83
+ requirements:
84
+ - - ">="
85
+ - !ruby/object:Gem::Version
86
+ version: '0'
87
+ type: :development
88
+ prerelease: false
89
+ version_requirements: !ruby/object:Gem::Requirement
90
+ requirements:
91
+ - - ">="
92
+ - !ruby/object:Gem::Version
93
+ version: '0'
94
+ - !ruby/object:Gem::Dependency
95
+ name: benchmark
96
+ requirement: !ruby/object:Gem::Requirement
97
+ requirements:
98
+ - - ">="
99
+ - !ruby/object:Gem::Version
100
+ version: '0'
101
+ type: :development
102
+ prerelease: false
103
+ version_requirements: !ruby/object:Gem::Requirement
104
+ requirements:
105
+ - - ">="
106
+ - !ruby/object:Gem::Version
107
+ version: '0'
108
+ - !ruby/object:Gem::Dependency
109
+ name: logger
110
+ requirement: !ruby/object:Gem::Requirement
111
+ requirements:
112
+ - - ">="
113
+ - !ruby/object:Gem::Version
114
+ version: '0'
115
+ type: :development
116
+ prerelease: false
117
+ version_requirements: !ruby/object:Gem::Requirement
118
+ requirements:
119
+ - - ">="
120
+ - !ruby/object:Gem::Version
121
+ version: '0'
122
+ - !ruby/object:Gem::Dependency
123
+ name: ostruct
124
+ requirement: !ruby/object:Gem::Requirement
125
+ requirements:
126
+ - - ">="
127
+ - !ruby/object:Gem::Version
128
+ version: '0'
129
+ type: :development
130
+ prerelease: false
131
+ version_requirements: !ruby/object:Gem::Requirement
132
+ requirements:
133
+ - - ">="
134
+ - !ruby/object:Gem::Version
135
+ version: '0'
136
+ - !ruby/object:Gem::Dependency
137
+ name: rspec
138
+ requirement: !ruby/object:Gem::Requirement
139
+ requirements:
140
+ - - "~>"
141
+ - !ruby/object:Gem::Version
142
+ version: '3.12'
143
+ type: :development
144
+ prerelease: false
145
+ version_requirements: !ruby/object:Gem::Requirement
146
+ requirements:
147
+ - - "~>"
148
+ - !ruby/object:Gem::Version
149
+ version: '3.12'
150
+ - !ruby/object:Gem::Dependency
151
+ name: rubocop
152
+ requirement: !ruby/object:Gem::Requirement
153
+ requirements:
154
+ - - "~>"
155
+ - !ruby/object:Gem::Version
156
+ version: '1.28'
157
+ type: :development
158
+ prerelease: false
159
+ version_requirements: !ruby/object:Gem::Requirement
160
+ requirements:
161
+ - - "~>"
162
+ - !ruby/object:Gem::Version
163
+ version: '1.28'
164
+ - !ruby/object:Gem::Dependency
165
+ name: rubocop-performance
166
+ requirement: !ruby/object:Gem::Requirement
167
+ requirements:
168
+ - - "~>"
169
+ - !ruby/object:Gem::Version
170
+ version: '1.14'
171
+ type: :development
172
+ prerelease: false
173
+ version_requirements: !ruby/object:Gem::Requirement
174
+ requirements:
175
+ - - "~>"
176
+ - !ruby/object:Gem::Version
177
+ version: '1.14'
178
+ - !ruby/object:Gem::Dependency
179
+ name: rubocop-rspec
180
+ requirement: !ruby/object:Gem::Requirement
181
+ requirements:
182
+ - - "~>"
183
+ - !ruby/object:Gem::Version
184
+ version: '2.11'
185
+ type: :development
186
+ prerelease: false
187
+ version_requirements: !ruby/object:Gem::Requirement
188
+ requirements:
189
+ - - "~>"
190
+ - !ruby/object:Gem::Version
191
+ version: '2.11'
192
+ - !ruby/object:Gem::Dependency
193
+ name: rubocop-thread_safety
194
+ requirement: !ruby/object:Gem::Requirement
195
+ requirements:
196
+ - - ">="
197
+ - !ruby/object:Gem::Version
198
+ version: '0'
199
+ type: :development
200
+ prerelease: false
201
+ version_requirements: !ruby/object:Gem::Requirement
202
+ requirements:
203
+ - - ">="
204
+ - !ruby/object:Gem::Version
205
+ version: '0'
206
+ - !ruby/object:Gem::Dependency
207
+ name: sorbet
208
+ requirement: !ruby/object:Gem::Requirement
209
+ requirements:
210
+ - - "~>"
211
+ - !ruby/object:Gem::Version
212
+ version: '0.5'
213
+ type: :development
214
+ prerelease: false
215
+ version_requirements: !ruby/object:Gem::Requirement
216
+ requirements:
217
+ - - "~>"
218
+ - !ruby/object:Gem::Version
219
+ version: '0.5'
220
+ - !ruby/object:Gem::Dependency
221
+ name: tsort
222
+ requirement: !ruby/object:Gem::Requirement
223
+ requirements:
224
+ - - ">="
225
+ - !ruby/object:Gem::Version
226
+ version: '0'
227
+ type: :development
228
+ prerelease: false
229
+ version_requirements: !ruby/object:Gem::Requirement
230
+ requirements:
231
+ - - ">="
232
+ - !ruby/object:Gem::Version
233
+ version: '0'
234
+ - !ruby/object:Gem::Dependency
235
+ name: yard
236
+ requirement: !ruby/object:Gem::Requirement
237
+ requirements:
238
+ - - ">="
239
+ - !ruby/object:Gem::Version
240
+ version: '0'
241
+ type: :development
242
+ prerelease: false
243
+ version_requirements: !ruby/object:Gem::Requirement
244
+ requirements:
245
+ - - ">="
246
+ - !ruby/object:Gem::Version
247
+ version: '0'
248
+ - !ruby/object:Gem::Dependency
249
+ name: yard-sorbet
250
+ requirement: !ruby/object:Gem::Requirement
251
+ requirements:
252
+ - - ">="
253
+ - !ruby/object:Gem::Version
254
+ version: '0'
255
+ type: :development
256
+ prerelease: false
257
+ version_requirements: !ruby/object:Gem::Requirement
258
+ requirements:
259
+ - - ">="
260
+ - !ruby/object:Gem::Version
261
+ version: '0'
262
+ description: Generates highly concise, deterministic Markdown coverage digests tailored
263
+ for LLMs and autonomous agents by matching coverage deficits to their AST semantic
264
+ boundaries rather than line numbers.
265
+ email:
266
+ - vitalii.lazebnyi.github@gmail.com
267
+ executables: []
268
+ extensions: []
269
+ extra_rdoc_files: []
270
+ files:
271
+ - LICENSE.txt
272
+ - README.md
273
+ - certs/simplecov-ai-public_cert.pem
274
+ - lib/simplecov-ai.rb
275
+ - lib/simplecov-ai/ast_resolver.rb
276
+ - lib/simplecov-ai/configuration.rb
277
+ - lib/simplecov-ai/markdown_builder.rb
278
+ - lib/simplecov-ai/version.rb
279
+ homepage: https://github.com/VitaliiLazebnyi/simplecov-ai
280
+ licenses:
281
+ - MIT
282
+ metadata:
283
+ source_code_uri: https://github.com/VitaliiLazebnyi/simplecov-ai
284
+ allowed_push_host: https://rubygems.org
285
+ rubygems_mfa_required: 'true'
286
+ rdoc_options: []
287
+ require_paths:
288
+ - lib
289
+ required_ruby_version: !ruby/object:Gem::Requirement
290
+ requirements:
291
+ - - ">="
292
+ - !ruby/object:Gem::Version
293
+ version: 3.0.0
294
+ required_rubygems_version: !ruby/object:Gem::Requirement
295
+ requirements:
296
+ - - ">="
297
+ - !ruby/object:Gem::Version
298
+ version: '0'
299
+ requirements: []
300
+ rubygems_version: 4.0.6
301
+ specification_version: 4
302
+ summary: An AI-optimized Markdown formatter for SimpleCov utilizing AST mapping.
303
+ test_files: []
metadata.gz.sig ADDED
Binary file