simcov-ai-formatter
A SimpleCov formatter that emits a JSON format optimized for AI / LLM consumption — per-file summaries, uncovered ranges, and optional source snippets.
Why this exists
SimpleCov's .resultset.json records coverage as position-dependent arrays:
{ "RSpec": { "coverage": { "/abs/path/foo.rb": { "lines": [null, 1, 0, 0, null, 5] } } } }
lines is a 1-indexed hit-count array (null = irrelevant, 0 = uncovered, Integer >= 1 = hit count).
For an LLM to figure out "which file has which uncovered lines" from this shape, it has to scan arrays, compute summaries, and normalize paths every single time.
This gem does that preprocessing once, deterministically, and in a token-efficient way.
Installation
gem install simcov-ai-formatter
Or in Gemfile:
group :development, :test do
gem "simcov-ai-formatter"
end
Output schema
Default
{
"schema_version": 1,
"suite": "RSpec",
"root": "/Users/me/proj",
"summary": {
"total_files": 42,
"relevant_lines": 1830,
"covered_lines": 1644,
"missed_lines": 186,
"coverage_percentage": 89.84
},
"files": {
"lib/foo.rb": {
"relevant_lines": 50,
"covered_lines": 45,
"missed_lines": 5,
"coverage_percentage": 90.0,
"uncovered_ranges": [
{ "start": 12, "end": 14 },
{ "start": 88, "end": 88 }
],
"uncovered_lines": [12, 13, 14, 88]
}
}
}
When multiple suites are merged, a top-level "suites_merged": ["RSpec", "Cucumber"] is emitted.
With with_source: true, context: 2
Each uncovered_ranges entry gains a source array:
{
"start": 12, "end": 14,
"source": [
{ "line": 10, "text": "def parse(input)", "covered": true },
{ "line": 11, "text": " return nil if input.nil?", "covered": true },
{ "line": 12, "text": " raise ArgumentError", "covered": false },
{ "line": 13, "text": " log_error(input)", "covered": false },
{ "line": 14, "text": " nil", "covered": false },
{ "line": 15, "text": "end", "covered": true }
]
}
If the source file is missing, the range gets "source": null, "source_error": "missing" and a warning summary is emitted to stderr (processing continues).
Branch coverage
If branches exists in the resultset, each file gets a branches_raw field containing the resultset's original key shape unchanged.
Structured form ({ type: "if", line: ..., then_hits: ..., else_hits: ... }) is planned for v0.2.0.
Schema details
relevant_linesexcludesnullentries (matches SimpleCov convention).- Files with all
nulllines (comments / blanks only) reportrelevant_lines: 0, coverage_percentage: 100.0and are excluded from the project-level denominator. coverage_percentageis rounded to 2 decimal places.- Files outside
root(e.g. third-party gems) are kept under keys of the form!abs:/abs/path. - The output contains no timestamp — the JSON is deterministic.
As a SimpleCov formatter
Plug simcov-ai-formatter into SimpleCov's formatter pipeline to emit the AI-friendly JSON automatically as part of your test run.
# spec/spec_helper.rb (or .simplecov)
require "simplecov"
require "simcov_ai_formatter/simple_cov_formatter"
SimpleCov.start do
# ... your usual SimpleCov config
end
# Replace the default formatter
SimpleCov.formatter = SimcovAiFormatter::SimpleCovFormatter
# Or run alongside the HTML formatter
SimpleCov.formatters = SimpleCov::Formatter::MultiFormatter.create([
SimpleCov::Formatter::HTMLFormatter,
SimcovAiFormatter::SimpleCovFormatter
])
After tests finish, the AI-friendly JSON is written to coverage/.resultset.ai.json (or wherever SimpleCov.coverage_path points).
Configuration
Set class-level attributes before SimpleCov.start:
SimcovAiFormatter::SimpleCovFormatter.with_source = true
SimcovAiFormatter::SimpleCovFormatter.context = 3
SimcovAiFormatter::SimpleCovFormatter.pretty = false
SimcovAiFormatter::SimpleCovFormatter.output_path = "tmp/coverage.ai.json" # nil = coverage/.resultset.ai.json
Programmatic use
The same logic is callable from Ruby:
require "simcov_ai_formatter"
result = SimcovAiFormatter.format(
"coverage/.resultset.json",
root: Dir.pwd,
with_source: true,
context: 2
)
# result is a Hash
puts result["summary"]["coverage_percentage"]
Development
bundle install
bundle exec rake test # run all tests
UPDATE_GOLDEN=1 bundle exec rake test # regenerate golden files
License
MIT