simplecov-mcp 1.0.0 → 2.0.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.
Files changed (164) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +98 -50
  3. data/docs/{ARCHITECTURE.md → dev/ARCHITECTURE.md} +11 -10
  4. data/docs/dev/BRANCH_ONLY_COVERAGE.md +158 -0
  5. data/docs/{DEVELOPMENT.md → dev/DEVELOPMENT.md} +2 -1
  6. data/docs/dev/README.md +10 -0
  7. data/docs/dev/RELEASING.md +146 -0
  8. data/docs/{arch-decisions → dev/arch-decisions}/001-x-arch-decision.md +3 -1
  9. data/docs/{arch-decisions → dev/arch-decisions}/002-x-arch-decision.md +7 -5
  10. data/docs/{arch-decisions → dev/arch-decisions}/003-x-arch-decision.md +2 -0
  11. data/docs/{arch-decisions → dev/arch-decisions}/004-x-arch-decision.md +6 -2
  12. data/docs/{arch-decisions → dev/arch-decisions}/005-x-arch-decision.md +4 -2
  13. data/docs/{arch-decisions → dev/arch-decisions}/README.md +3 -3
  14. data/docs/{presentations → dev/presentations}/simplecov-mcp-presentation.md +28 -22
  15. data/docs/fixtures/demo_project/README.md +9 -0
  16. data/docs/{ADVANCED_USAGE.md → user/ADVANCED_USAGE.md} +129 -319
  17. data/docs/user/CLI_FALLBACK_FOR_LLMS.md +34 -0
  18. data/docs/user/CLI_USAGE.md +750 -0
  19. data/docs/{ERROR_HANDLING.md → user/ERROR_HANDLING.md} +12 -12
  20. data/docs/user/EXAMPLES.md +588 -0
  21. data/docs/user/INSTALLATION.md +130 -0
  22. data/docs/{LIBRARY_API.md → user/LIBRARY_API.md} +90 -32
  23. data/docs/{MCP_INTEGRATION.md → user/MCP_INTEGRATION.md} +36 -34
  24. data/docs/user/README.md +14 -0
  25. data/docs/{TROUBLESHOOTING.md → user/TROUBLESHOOTING.md} +21 -100
  26. data/docs/user/V2-BREAKING-CHANGES.md +472 -0
  27. data/exe/simplecov-mcp +1 -1
  28. data/lib/simplecov_mcp/{cli_config.rb → app_config.rb} +12 -12
  29. data/lib/simplecov_mcp/app_context.rb +1 -1
  30. data/lib/simplecov_mcp/base_tool.rb +66 -38
  31. data/lib/simplecov_mcp/cli.rb +67 -123
  32. data/lib/simplecov_mcp/commands/base_command.rb +16 -27
  33. data/lib/simplecov_mcp/commands/command_factory.rb +8 -2
  34. data/lib/simplecov_mcp/commands/detailed_command.rb +16 -2
  35. data/lib/simplecov_mcp/commands/list_command.rb +1 -1
  36. data/lib/simplecov_mcp/commands/raw_command.rb +18 -2
  37. data/lib/simplecov_mcp/commands/summary_command.rb +20 -3
  38. data/lib/simplecov_mcp/commands/totals_command.rb +53 -0
  39. data/lib/simplecov_mcp/commands/uncovered_command.rb +24 -5
  40. data/lib/simplecov_mcp/commands/validate_command.rb +60 -0
  41. data/lib/simplecov_mcp/commands/version_command.rb +19 -4
  42. data/lib/simplecov_mcp/config_parser.rb +32 -0
  43. data/lib/simplecov_mcp/constants.rb +3 -3
  44. data/lib/simplecov_mcp/coverage_reporter.rb +31 -0
  45. data/lib/simplecov_mcp/error_handler.rb +81 -40
  46. data/lib/simplecov_mcp/error_handler_factory.rb +2 -2
  47. data/lib/simplecov_mcp/errors.rb +32 -20
  48. data/lib/simplecov_mcp/formatters/source_formatter.rb +23 -19
  49. data/lib/simplecov_mcp/formatters.rb +51 -0
  50. data/lib/simplecov_mcp/mcp_server.rb +9 -7
  51. data/lib/simplecov_mcp/mode_detector.rb +6 -5
  52. data/lib/simplecov_mcp/model.rb +122 -83
  53. data/lib/simplecov_mcp/option_normalizers.rb +39 -18
  54. data/lib/simplecov_mcp/option_parser_builder.rb +82 -65
  55. data/lib/simplecov_mcp/option_parsers/env_options_parser.rb +3 -5
  56. data/lib/simplecov_mcp/option_parsers/error_helper.rb +18 -17
  57. data/lib/simplecov_mcp/path_relativizer.rb +17 -14
  58. data/lib/simplecov_mcp/predicate_evaluator.rb +72 -0
  59. data/lib/simplecov_mcp/presenters/base_coverage_presenter.rb +1 -3
  60. data/lib/simplecov_mcp/presenters/coverage_detailed_presenter.rb +1 -3
  61. data/lib/simplecov_mcp/presenters/coverage_raw_presenter.rb +1 -3
  62. data/lib/simplecov_mcp/presenters/coverage_summary_presenter.rb +1 -3
  63. data/lib/simplecov_mcp/presenters/coverage_uncovered_presenter.rb +1 -3
  64. data/lib/simplecov_mcp/presenters/project_coverage_presenter.rb +1 -3
  65. data/lib/simplecov_mcp/presenters/project_totals_presenter.rb +27 -0
  66. data/lib/simplecov_mcp/resolvers/coverage_line_resolver.rb +14 -18
  67. data/lib/simplecov_mcp/resolvers/resultset_path_resolver.rb +7 -9
  68. data/lib/simplecov_mcp/resultset_loader.rb +20 -25
  69. data/lib/simplecov_mcp/staleness_checker.rb +50 -46
  70. data/lib/simplecov_mcp/table_formatter.rb +64 -0
  71. data/lib/simplecov_mcp/tools/all_files_coverage_tool.rb +20 -50
  72. data/lib/simplecov_mcp/tools/coverage_detailed_tool.rb +13 -7
  73. data/lib/simplecov_mcp/tools/coverage_raw_tool.rb +12 -7
  74. data/lib/simplecov_mcp/tools/coverage_summary_tool.rb +13 -8
  75. data/lib/simplecov_mcp/tools/coverage_table_tool.rb +20 -60
  76. data/lib/simplecov_mcp/tools/coverage_totals_tool.rb +44 -0
  77. data/lib/simplecov_mcp/tools/help_tool.rb +38 -66
  78. data/lib/simplecov_mcp/tools/uncovered_lines_tool.rb +13 -8
  79. data/lib/simplecov_mcp/tools/validate_tool.rb +72 -0
  80. data/lib/simplecov_mcp/tools/version_tool.rb +7 -14
  81. data/lib/simplecov_mcp/util.rb +18 -12
  82. data/lib/simplecov_mcp/version.rb +1 -1
  83. data/lib/simplecov_mcp.rb +23 -29
  84. data/spec/all_files_coverage_tool_spec.rb +4 -3
  85. data/spec/{cli_config_spec.rb → app_config_spec.rb} +31 -26
  86. data/spec/base_tool_spec.rb +17 -14
  87. data/spec/cli/show_default_report_spec.rb +2 -2
  88. data/spec/cli_enumerated_options_spec.rb +31 -9
  89. data/spec/cli_error_spec.rb +46 -23
  90. data/spec/cli_format_spec.rb +123 -0
  91. data/spec/cli_json_options_spec.rb +50 -0
  92. data/spec/cli_source_spec.rb +11 -63
  93. data/spec/cli_spec.rb +82 -97
  94. data/spec/cli_usage_spec.rb +15 -15
  95. data/spec/commands/base_command_spec.rb +12 -92
  96. data/spec/commands/command_factory_spec.rb +7 -3
  97. data/spec/commands/detailed_command_spec.rb +10 -24
  98. data/spec/commands/list_command_spec.rb +28 -0
  99. data/spec/commands/raw_command_spec.rb +43 -20
  100. data/spec/commands/summary_command_spec.rb +10 -23
  101. data/spec/commands/totals_command_spec.rb +34 -0
  102. data/spec/commands/uncovered_command_spec.rb +29 -23
  103. data/spec/commands/validate_command_spec.rb +213 -0
  104. data/spec/commands/version_command_spec.rb +38 -0
  105. data/spec/constants_spec.rb +3 -3
  106. data/spec/coverage_reporter_spec.rb +102 -0
  107. data/spec/coverage_table_tool_spec.rb +21 -10
  108. data/spec/coverage_totals_tool_spec.rb +37 -0
  109. data/spec/error_handler_spec.rb +120 -4
  110. data/spec/error_mode_spec.rb +18 -22
  111. data/spec/errors_edge_cases_spec.rb +101 -28
  112. data/spec/errors_stale_spec.rb +34 -0
  113. data/spec/file_based_mcp_tools_spec.rb +6 -6
  114. data/spec/fixtures/project1/lib/bar.rb +2 -0
  115. data/spec/fixtures/project1/lib/foo.rb +2 -0
  116. data/spec/help_tool_spec.rb +2 -18
  117. data/spec/integration_spec.rb +114 -170
  118. data/spec/logging_fallback_spec.rb +3 -3
  119. data/spec/mcp_server_integration_spec.rb +1 -1
  120. data/spec/mcp_server_spec.rb +70 -53
  121. data/spec/mode_detector_spec.rb +46 -41
  122. data/spec/model_error_handling_spec.rb +141 -82
  123. data/spec/model_staleness_spec.rb +13 -13
  124. data/spec/option_normalizers_spec.rb +111 -112
  125. data/spec/option_parsers/env_options_parser_spec.rb +25 -37
  126. data/spec/option_parsers/error_helper_spec.rb +56 -56
  127. data/spec/path_relativizer_spec.rb +15 -0
  128. data/spec/presenters/coverage_detailed_presenter_spec.rb +1 -1
  129. data/spec/presenters/coverage_summary_presenter_spec.rb +1 -1
  130. data/spec/presenters/coverage_uncovered_presenter_spec.rb +1 -1
  131. data/spec/presenters/project_coverage_presenter_spec.rb +9 -8
  132. data/spec/presenters/project_totals_presenter_spec.rb +144 -0
  133. data/spec/resolvers/coverage_line_resolver_spec.rb +261 -36
  134. data/spec/resolvers/resultset_path_resolver_spec.rb +13 -8
  135. data/spec/shared_examples/file_based_mcp_tools.rb +23 -18
  136. data/spec/shared_examples/formatted_command_examples.rb +64 -0
  137. data/spec/simple_cov_mcp_module_spec.rb +24 -3
  138. data/spec/simplecov_mcp/formatters/source_formatter_spec.rb +267 -0
  139. data/spec/simplecov_mcp/formatters_spec.rb +76 -0
  140. data/spec/simplecov_mcp/presenters/base_coverage_presenter_spec.rb +79 -0
  141. data/spec/simplecov_mcp_model_spec.rb +99 -49
  142. data/spec/simplecov_mcp_opts_spec.rb +42 -39
  143. data/spec/spec_helper.rb +27 -92
  144. data/spec/staleness_checker_spec.rb +10 -9
  145. data/spec/staleness_more_spec.rb +4 -4
  146. data/spec/support/cli_helpers.rb +22 -0
  147. data/spec/support/control_flow_helpers.rb +20 -0
  148. data/spec/support/fake_mcp.rb +40 -0
  149. data/spec/support/io_helpers.rb +29 -0
  150. data/spec/support/mcp_helpers.rb +35 -0
  151. data/spec/support/mcp_runner.rb +10 -8
  152. data/spec/support/mocking_helpers.rb +30 -0
  153. data/spec/table_format_spec.rb +70 -0
  154. data/spec/tools/validate_tool_spec.rb +132 -0
  155. data/spec/tools_error_handling_spec.rb +34 -48
  156. data/spec/util_spec.rb +5 -4
  157. data/spec/version_spec.rb +7 -7
  158. data/spec/version_tool_spec.rb +20 -22
  159. metadata +90 -23
  160. data/docs/BRANCH_ONLY_COVERAGE.md +0 -81
  161. data/docs/CLI_USAGE.md +0 -637
  162. data/docs/EXAMPLES.md +0 -430
  163. data/docs/INSTALLATION.md +0 -352
  164. data/spec/cli_success_predicate_spec.rb +0 -141
@@ -1,25 +1,27 @@
1
1
  # Error Handling Guide
2
2
 
3
- This tool provides different error handling behavior depending on how it's used:
3
+ [Back to main README](../README.md)
4
+
5
+ Error handling differs by usage mode:
4
6
 
5
7
  ## CLI Mode
6
8
 
7
- When used as a command-line tool, errors are displayed as user-friendly messages without stack traces:
9
+ Errors are displayed as user-friendly messages without stack traces:
8
10
 
9
11
  ```bash
10
12
  $ simplecov-mcp summary nonexistent.rb
11
13
  File error: No coverage data found for the specified file
12
14
  ```
13
15
 
14
- For debugging, use the `--error-mode trace` flag to see full stack traces:
16
+ For debugging, use the `--error-mode debug` flag to include stack traces in log output and display the first 5 lines of the backtrace in CLI output:
15
17
 
16
18
  ```bash
17
- $ simplecov-mcp --error-mode trace summary nonexistent.rb
19
+ $ simplecov-mcp --error-mode debug summary nonexistent.rb
18
20
  ```
19
21
 
20
22
  ## Library Mode
21
23
 
22
- When used as a Ruby library, callers work with `SimpleCovMcp::CoverageModel` directly. Errors raise custom exception classes that can be caught and handled:
24
+ Calls to `SimpleCovMcp::CoverageModel` raise custom exceptions you can handle programmatically:
23
25
 
24
26
  ```ruby
25
27
  handler = SimpleCovMcp::ErrorHandlerFactory.for_library # disables CLI-style logging
@@ -44,13 +46,11 @@ Available exception classes:
44
46
 
45
47
  ## MCP Server Mode
46
48
 
47
- When running as an MCP server, errors are handled internally and returned as structured responses to the MCP client. The MCP server uses:
48
-
49
- - **Logging enabled** - Errors are logged to `simplecov_mcp.log` in the current directory for server debugging
50
- - **Clean error messages** - User-friendly messages are returned to the client without stack traces by default
51
- - **Structured responses** - Errors are returned as proper MCP tool responses, not exceptions
49
+ Errors are returned as structured responses to the MCP client:
52
50
 
53
- The MCP server automatically configures error handling appropriately for server usage.
51
+ - **Logging enabled** - Errors go to `simplecov_mcp.log` in the current directory by default
52
+ - **Clean error messages** - User-friendly messages, no stack traces by default
53
+ - **Structured responses** - Tool responses instead of exceptions
54
54
 
55
55
  ## Custom Error Handlers
56
56
 
@@ -72,7 +72,7 @@ end
72
72
 
73
73
  ## Stale Coverage Errors
74
74
 
75
- When strict staleness checking is enabled (`--stale error`), the model (and CLI) raise a `CoverageDataStaleError` if a source file appears newer than the coverage data or the line counts differ.
75
+ When strict staleness checking is enabled (`--staleness error`), the model (and CLI) raise a `CoverageDataStaleError` if a source file appears newer than the coverage data or the line counts differ.
76
76
 
77
77
  - Enable per instance: `SimpleCovMcp::CoverageModel.new(staleness: 'error')`
78
78
 
@@ -0,0 +1,588 @@
1
+ # Examples and Recipes
2
+
3
+ [Back to main README](../README.md)
4
+
5
+ Practical examples for common tasks with simplecov-mcp. Examples are organized by skill level and use case.
6
+
7
+ > For brevity, these examples use `smcp`, an alias to the demo fixture with partial coverage:
8
+ > `alias smcp='simplecov-mcp --root docs/fixtures/demo_project'`
9
+ > Swap `smcp` for `simplecov-mcp` to run against your own project and resultset.
10
+
11
+ ## Table of Contents
12
+
13
+ - [Quick Start Examples](#quick-start-examples)
14
+ - [CLI Examples](#cli-examples)
15
+ - [Ruby Library Examples](#ruby-library-examples)
16
+ - [AI Assistant Prompts](#ai-assistant-prompts)
17
+ - [CI/CD Integration](#cicd-integration)
18
+ - [Advanced Usage](#advanced-usage)
19
+
20
+ ## Quick Start Examples
21
+
22
+ ### View All Coverage
23
+
24
+ ```bash
25
+ # Default: show all files, worst coverage first
26
+ smcp
27
+
28
+ # Show files with best coverage first
29
+ smcp -o d list # -o = --sort-order, d = descending
30
+
31
+ # Export to JSON for processing
32
+ smcp -fJ list > coverage-report.json
33
+ ```
34
+
35
+ ### Check Specific File
36
+
37
+ ```bash
38
+ # Quick summary
39
+ smcp summary app/models/order.rb
40
+
41
+ # See which lines aren't covered
42
+ smcp uncovered app/controllers/orders_controller.rb
43
+
44
+ # View uncovered code with context
45
+ smcp -s u -c 3 uncovered app/controllers/orders_controller.rb # -s = --source (u = uncovered), -c = --context-lines
46
+ ```
47
+
48
+ ### Find Coverage Gaps
49
+
50
+ ```bash
51
+ # Files with worst coverage
52
+ smcp list | head -10
53
+
54
+ # Only show files below 80%
55
+ smcp -fJ list | jq '.files[] | select(.percentage < 95)'
56
+
57
+ # Ruby alternative:
58
+ smcp -fJ list | ruby -r json -e '
59
+ JSON.parse($stdin.read)["files"].select { |f| f["percentage"] < 95 }.each do |f|
60
+ puts JSON.pretty_generate(f)
61
+ end
62
+ '
63
+
64
+ # Rexe alternative:
65
+ smcp -fJ list | rexe -ij -mb -oJ 'self["files"].select { |f| f["percentage"] < 95 }'
66
+
67
+ # Check specific directory
68
+ smcp -g "lib/payments/**/*.rb" list # -g = --tracked-globs
69
+ ```
70
+
71
+ ## CLI Examples
72
+
73
+ ### Coverage Analysis
74
+
75
+ **Detailed investigation:**
76
+ ```bash
77
+ # See detailed hit counts
78
+ smcp detailed lib/api/client.rb
79
+
80
+ # Show full source with coverage markers
81
+ smcp -s f summary lib/api/client.rb # f = full
82
+
83
+ # Focus on uncovered areas only
84
+ smcp -s u -c 5 uncovered lib/payments/refund_service.rb # u = uncovered
85
+ ```
86
+
87
+ ### Working with JSON Output
88
+
89
+ In addition to the benefit of JSON encoding being human readable, it can be used in single line commands to fetch and compute values using `jq`, Ruby's JSON library, or `rexe`.
90
+ Here are some examples:
91
+
92
+ **Parse and filter:**
93
+ ```bash
94
+ # Files below threshold
95
+ smcp -fJ list | jq '.files[] | select(.percentage < 95) | {file, coverage: .percentage}'
96
+
97
+ # Ruby alternative:
98
+ smcp -fJ list | ruby -r json -e '
99
+ JSON.parse($stdin.read)["files"].select { |f| f["percentage"] < 95 }.each do |f|
100
+ puts JSON.pretty_generate({file: f["file"], coverage: f["percentage"]})
101
+ end
102
+ '
103
+
104
+ # Rexe alternative:
105
+ smcp -fJ list | rexe -ij -mb -oJ '
106
+ self["files"].select { |f| f["percentage"] < 95 }.map do |f|
107
+ {file: f["file"], coverage: f["percentage"]}
108
+ end
109
+ '
110
+
111
+ # Count total uncovered lines
112
+ smcp -fJ totals | jq '.lines.uncovered'
113
+
114
+ # Ruby alternative:
115
+ smcp -fJ totals | ruby -r json -e '
116
+ puts JSON.parse($stdin.read)["lines"]["uncovered"]
117
+ '
118
+
119
+ # Rexe alternative:
120
+ smcp -fJ totals | rexe -ij -mb -op 'self["lines"]["uncovered"]'
121
+
122
+ # Group by directory (full path)
123
+ smcp -fJ list |
124
+ jq '.files
125
+ | map(. + {dir: (.file | split("/") | .[0:-1] | join("/"))})
126
+ | sort_by(.dir)
127
+ | group_by(.dir)
128
+ | map({dir: .[0].dir, avg: (map(.percentage) | add / length)})'
129
+
130
+ # Ruby alternative:
131
+ smcp -fJ list | ruby -r json -e '
132
+ grouped = JSON.parse($stdin.read)["files"]
133
+ .map { |f| f.merge("dir" => File.dirname(f["file"])) }
134
+ .group_by { |f| f["dir"] }
135
+ .map { |dir, files|
136
+ avg = files.sum { |f| f["percentage"] } / files.size
137
+ {dir: dir, avg: avg}
138
+ }
139
+ puts JSON.pretty_generate(grouped)
140
+ '
141
+
142
+ # Rexe alternative:
143
+ smcp -fJ list | rexe -ij -mb -oJ '
144
+ self["files"]
145
+ .map { |f| f.merge("dir" => File.dirname(f["file"])) }
146
+ .group_by { |f| f["dir"] }
147
+ .map { |dir, files|
148
+ avg = files.sum { |f| f["percentage"] } / files.size
149
+ {dir: dir, avg: avg}
150
+ }
151
+ '
152
+ ```
153
+
154
+ **Generate reports:**
155
+ ```bash
156
+ # Create markdown table
157
+ echo "| Coverage | File |" > report.md
158
+ echo "|----------|------|" >> report.md
159
+ smcp -fJ list | jq -r '.files[] | "| \(.percentage)% | \(.file) |"' >> report.md
160
+
161
+ # Ruby alternative:
162
+ smcp -fJ list | ruby -r json -e '
163
+ JSON.parse($stdin.read)["files"].each do |f|
164
+ puts "| #{f["percentage"]}% | #{f["file"]} |"
165
+ end
166
+ ' >> report.md
167
+
168
+ # Rexe alternative:
169
+ smcp -fJ list | rexe -ij -mb '
170
+ self["files"].each { |f| puts "| #{f["percentage"]}% | #{f["file"]} |" }
171
+ ' >> report.md
172
+
173
+ # Export for spreadsheet
174
+ smcp -fJ list | jq -r '.files[] | [.file, .percentage] | @csv' > coverage.csv
175
+
176
+ # Ruby alternative:
177
+ smcp -fJ list | ruby -r json -r csv -e '
178
+ JSON.parse($stdin.read)["files"].each do |f|
179
+ puts CSV.generate_line([f["file"], f["percentage"]]).chomp
180
+ end
181
+ ' > coverage.csv
182
+
183
+ # Rexe alternative:
184
+ smcp -fJ list | rexe -r csv -ij -mb '
185
+ self["files"].each { |f| puts CSV.generate_line([f["file"], f["percentage"]]).chomp }
186
+ ' > coverage.csv
187
+ ```
188
+
189
+ ## Ruby Library Examples
190
+
191
+ ### Basic Usage
192
+
193
+ ```ruby
194
+ require "simplecov_mcp"
195
+
196
+ root = "docs/fixtures/demo_project"
197
+ model = SimpleCovMcp::CoverageModel.new(root: root)
198
+
199
+ # Project totals
200
+ totals = model.project_totals
201
+ puts "Total files: #{totals['files']['total']}"
202
+ puts "Average coverage: #{totals['percentage']}%"
203
+
204
+ # Check specific file
205
+ summary = model.summary_for("app/models/order.rb")
206
+ puts "Coverage: #{summary['summary']['percentage']}%"
207
+
208
+ # Find uncovered lines
209
+ uncovered = model.uncovered_for("lib/payments/refund_service.rb")
210
+ puts "Uncovered lines: #{uncovered['uncovered'].join(', ')}"
211
+ ```
212
+
213
+ ### Filtering and Analysis
214
+
215
+ ```ruby
216
+ require "simplecov_mcp"
217
+
218
+ root = "docs/fixtures/demo_project"
219
+ model = SimpleCovMcp::CoverageModel.new(root: root)
220
+ all_files = model.all_files
221
+
222
+ # Find files below threshold
223
+ THRESHOLD = 80.0
224
+ low_coverage = all_files.select { |f| f['percentage'] < THRESHOLD }
225
+
226
+ if low_coverage.any?
227
+ puts "Files below #{THRESHOLD}%:"
228
+ low_coverage.each do |file|
229
+ puts " #{file['file']}: #{file['percentage']}%"
230
+ end
231
+ end
232
+
233
+ # Group by directory using totals command logic
234
+ dirs = %w[app lib lib/payments lib/ops/jobs].uniq
235
+ dirs.each do |dir|
236
+ pattern = File.join(dir, '**/*.rb')
237
+ totals = model.project_totals(tracked_globs: pattern)
238
+ puts "#{dir}: #{totals['percentage'].round(2)}% (#{totals['files']['total']} files)"
239
+ end
240
+ ```
241
+
242
+ ### Custom Formatting
243
+
244
+ ```ruby
245
+ require "simplecov_mcp"
246
+ require "pathname"
247
+
248
+ root = "docs/fixtures/demo_project"
249
+ model = SimpleCovMcp::CoverageModel.new(root: root)
250
+ all_files = model.all_files
251
+
252
+ # Filter to lib/payments (coverage data stores absolute paths)
253
+ lib_root = File.expand_path("lib/payments", File.expand_path(root, Dir.pwd))
254
+ lib_files = all_files.select { |f| f['file'].start_with?(lib_root) }
255
+
256
+ # Generate custom table
257
+ table = model.format_table(lib_files, sort_order: :ascending)
258
+ puts table
259
+
260
+ # Or create your own format
261
+ lib_files.each do |file|
262
+ status = file['percentage'] >= 90 ? '✓' : '⚠'
263
+ relative_path = Pathname.new(file['file']).relative_path_from(Pathname.pwd)
264
+ puts "#{status} #{relative_path}: #{file['percentage']}%"
265
+ end
266
+ ```
267
+
268
+ ## AI Assistant Prompts
269
+
270
+ ### Coverage Analysis
271
+
272
+ ```
273
+ Using simplecov-mcp, show me a table of all files sorted by coverage percentage.
274
+ ```
275
+
276
+ ```
277
+ Using simplecov-mcp, find the 10 files with the lowest coverage and create a markdown report with:
278
+ 1. File path
279
+ 2. Current coverage %
280
+ 3. Number of uncovered lines
281
+ ```
282
+
283
+ ```
284
+ Using simplecov-mcp, analyze the lib/payments/ directory and identify which files should be prioritized for additional testing based on coverage gaps and file complexity.
285
+ ```
286
+
287
+ ### Finding Specific Issues
288
+
289
+ ```
290
+ Using simplecov-mcp, show me the uncovered lines in app/controllers/orders_controller.rb with 5 lines of context around each uncovered block.
291
+ ```
292
+
293
+ ```
294
+ Using simplecov-mcp, find all files in lib/payments/ with less than 80% coverage and list the specific uncovered line numbers for each.
295
+ ```
296
+
297
+ ### Test Generation
298
+
299
+ ```
300
+ Using simplecov-mcp, identify the uncovered lines in lib/ops/jobs/report_job.rb and write RSpec tests to cover them.
301
+ ```
302
+
303
+ ```
304
+ Using simplecov-mcp, analyze coverage gaps in the app/controllers/ directory and generate a test plan prioritizing:
305
+ 1. Public API methods
306
+ 2. Error handling paths
307
+ 3. Edge cases
308
+ ```
309
+
310
+ ### Reporting
311
+
312
+ ```
313
+ Using simplecov-mcp, create a coverage report showing:
314
+ - Overall project coverage percentage
315
+ - Top 5 files with worst coverage
316
+ - Recommended next steps to improve coverage
317
+
318
+ Format as markdown.
319
+ ```
320
+
321
+ ## Test Run Integration
322
+
323
+ ### Display Low Coverage Files After RSpec
324
+
325
+ Add this to your `spec/spec_helper.rb` to automatically report files below a coverage threshold after each test run:
326
+
327
+ ```ruby
328
+ require 'simplecov'
329
+ SimpleCov.start do
330
+ enable_coverage :branch
331
+ add_filter %r{^/spec/}
332
+ track_files 'lib/**/*.rb' # Ensures new/untested files show up with 0%
333
+ end
334
+
335
+ # Report lowest coverage files at the end of the test run
336
+ SimpleCov.at_exit do
337
+ SimpleCov.result.format!
338
+ require 'simplecov_mcp'
339
+ report = SimpleCovMcp::CoverageReporter.report(threshold: 80, count: 5)
340
+ puts report if report
341
+ end
342
+ ```
343
+
344
+ This produces output like:
345
+
346
+ ```
347
+ Lowest coverage files (< 80%):
348
+ 0.0% lib/myapp/config_parser.rb
349
+ 19.3% lib/myapp/formatters/source_formatter.rb
350
+ 24.0% lib/myapp/model.rb
351
+ 26.0% lib/myapp/cli.rb
352
+ 45.2% lib/myapp/commands/base.rb
353
+ ```
354
+
355
+ **Parameters:**
356
+ - `threshold:` - Coverage percentage below which files are included (default: 80)
357
+ - `count:` - Maximum number of files to show (default: 5)
358
+
359
+ **Returns:** Formatted string, or `nil` if no files are below the threshold.
360
+
361
+ ## CI/CD Integration
362
+
363
+ ### GitHub Actions
364
+
365
+ **Fail on low coverage:**
366
+ ```yaml
367
+ name: Coverage Check
368
+
369
+ on: [push, pull_request]
370
+
371
+ jobs:
372
+ coverage:
373
+ runs-on: ubuntu-latest
374
+ steps:
375
+ - uses: actions/checkout@v3
376
+
377
+ - name: Setup Ruby
378
+ uses: ruby/setup-ruby@v1
379
+ with:
380
+ ruby-version: 3.3
381
+ bundler-cache: true
382
+
383
+ - name: Run tests
384
+ run: bundle exec rspec
385
+
386
+ - name: Install simplecov-mcp
387
+ run: gem install simplecov-mcp
388
+
389
+ - name: Check coverage threshold
390
+ run: |
391
+ smcp -fJ list > coverage.json
392
+ BELOW_THRESHOLD=$(jq '[.files[] | select(.percentage < 80)] | length' coverage.json)
393
+
394
+ # Ruby alternative:
395
+ # BELOW_THRESHOLD=$(ruby -r json -e '
396
+ # puts JSON.parse(File.read("coverage.json"))["files"].count { |f| f["percentage"] < 80 }
397
+ # ')
398
+
399
+ # Rexe alternative:
400
+ # BELOW_THRESHOLD=$(rexe -f coverage.json -op 'self["files"].count { |f| f["percentage"] < 80 }')
401
+
402
+ if [ "$BELOW_THRESHOLD" -gt 0 ]; then
403
+ echo "❌ $BELOW_THRESHOLD files below 80% coverage"
404
+ jq -r '.files[] | select(.percentage < 80) | "\(.file): \(.percentage)%"' coverage.json
405
+
406
+ # Ruby alternative:
407
+ # ruby -r json -e '
408
+ # JSON.parse(File.read("coverage.json"))["files"].select { |f| f["percentage"] < 80 }.each do |f|
409
+ # puts "#{f["file"]}: #{f["percentage"]}%"
410
+ # end
411
+ # '
412
+
413
+ # Rexe alternative:
414
+ # rexe -f coverage.json '
415
+ # self["files"].select { |f| f["percentage"] < 80 }.each do |f|
416
+ # puts "#{f["file"]}: #{f["percentage"]}%"
417
+ # end
418
+ # '
419
+
420
+ exit 1
421
+ fi
422
+ echo "✓ All files meet coverage threshold"
423
+
424
+ - name: Upload coverage report
425
+ uses: actions/upload-artifact@v3
426
+ with:
427
+ name: coverage-report
428
+ path: coverage.json
429
+ ```
430
+
431
+ **Check for stale coverage:**
432
+ ```yaml
433
+ - name: Verify coverage is fresh
434
+ run: simplecov-mcp --staleness error || exit 1
435
+ ```
436
+
437
+ ### GitLab CI
438
+
439
+ ```yaml
440
+ test:
441
+ image: ruby:3.3
442
+ before_script:
443
+ - gem install simplecov-mcp
444
+ script:
445
+ - bundle exec rspec
446
+ - simplecov-mcp --staleness error
447
+ artifacts:
448
+ paths:
449
+ - coverage/
450
+ reports:
451
+ coverage_report:
452
+ coverage_format: simplecov
453
+ path: coverage/.resultset.json
454
+ ```
455
+
456
+ ### Custom Success Predicate
457
+
458
+ ```ruby
459
+ # coverage_policy.rb
460
+ ->(model) do
461
+ all_files = model.all_files
462
+
463
+ # Must have at least 80% average coverage
464
+ totals = model.project_totals
465
+ return false if totals['percentage'] < 80.0
466
+
467
+ # No files below 60%
468
+ return false if all_files.any? { |f| f['percentage'] < 60.0 }
469
+
470
+ # lib/ files must average 90%
471
+ lib_totals = model.project_totals(tracked_globs: ['lib/**/*.rb'])
472
+ return false if lib_totals['percentage'] < 90.0
473
+
474
+ true
475
+ end
476
+ ```
477
+
478
+ ```bash
479
+ # Use in CI
480
+ smcp validate coverage_policy.rb
481
+ ```
482
+
483
+ ## Advanced Usage
484
+
485
+ ### Directory-Level Analysis
486
+
487
+ ```ruby
488
+ require "simplecov_mcp"
489
+
490
+ root = "docs/fixtures/demo_project"
491
+ model = SimpleCovMcp::CoverageModel.new(root: root)
492
+ all_files = model.all_files
493
+
494
+ # Calculate coverage by directory (uses the same data as `simplecov-mcp totals`)
495
+ patterns = %w[
496
+ app/**/*.rb
497
+ lib/payments/**/*.rb
498
+ lib/ops/jobs/**/*.rb
499
+ ]
500
+
501
+ results = patterns.map do |pattern|
502
+ totals = model.project_totals(tracked_globs: pattern)
503
+
504
+ {
505
+ directory: pattern,
506
+ files: totals['files']['total'],
507
+ coverage: totals['percentage'].round(2),
508
+ covered: totals['lines']['covered'],
509
+ total: totals['lines']['total']
510
+ }
511
+ end
512
+
513
+ # Sort by coverage ascending
514
+ results.sort_by { |r| r[:coverage] }.each do |r|
515
+ puts "#{r[:directory]}: #{r[:coverage]}% (#{r[:files]} files)"
516
+ end
517
+ ```
518
+
519
+ ### Coverage Delta Tracking
520
+
521
+ ```ruby
522
+ require "simplecov_mcp"
523
+ require "json"
524
+
525
+ # Save current coverage
526
+ root = "docs/fixtures/demo_project"
527
+ model = SimpleCovMcp::CoverageModel.new(root: root)
528
+ current = model.all_files
529
+
530
+ File.write("coverage_baseline.json", JSON.pretty_generate(current))
531
+
532
+ # Later, compare with baseline
533
+ baseline = JSON.parse(File.read("coverage_baseline.json"))
534
+
535
+ current.each do |file|
536
+ baseline_file = baseline.find { |f| f['file'] == file['file'] }
537
+ next unless baseline_file
538
+
539
+ delta = file['percentage'] - baseline_file['percentage']
540
+ if delta.abs > 0.1 # Changed by more than 0.1%
541
+ symbol = delta > 0 ? '↑' : '↓'
542
+ puts "#{symbol} #{file['file']}: #{baseline_file['percentage']}% → #{file['percentage']}%"
543
+ end
544
+ end
545
+ ```
546
+
547
+ ### Integration with Code Review
548
+
549
+ ```ruby
550
+ # pr_coverage_check.rb
551
+ require "simplecov_mcp"
552
+ require "json"
553
+
554
+ model = SimpleCovMcp::CoverageModel.new
555
+
556
+ # Get changed files from PR (example using git)
557
+ changed_files = `git diff --name-only origin/main`.split("\n")
558
+ changed_files.select! { |f| f.end_with?('.rb') }
559
+
560
+ puts "## Coverage Report for Changed Files\n\n"
561
+ puts "| File | Coverage | Status |"
562
+ puts "|------|----------|--------|"
563
+
564
+ changed_files.each do |file|
565
+ begin
566
+ summary = model.summary_for(file)
567
+ percentage = summary['summary']['percentage']
568
+ status = percentage >= 80 ? '✅' : '⚠️'
569
+ puts "| #{file} | #{percentage}% | #{status} |"
570
+ rescue
571
+ puts "| #{file} | N/A | ❌ No coverage |"
572
+ end
573
+ end
574
+ ```
575
+
576
+ ## Example Scripts
577
+
578
+ The [`/examples`](/examples) directory contains runnable scripts:
579
+
580
+ - **[filter_and_table_demo.rb](../examples/filter_and_table_demo.rb)** - Filter and format coverage data
581
+ - **[success_predicates/](/examples/success_predicates/)** - Custom coverage policy examples
582
+
583
+ ## Related Documentation
584
+
585
+ - [CLI Usage Guide](CLI_USAGE.md) - Complete command reference
586
+ - [Library API Guide](LIBRARY_API.md) - Ruby API documentation
587
+ - [MCP Integration](MCP_INTEGRATION.md) - AI assistant setup
588
+ - [Troubleshooting](TROUBLESHOOTING.md) - Common issues