cov-loupe 3.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 (171) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +21 -0
  3. data/README.md +329 -0
  4. data/docs/dev/ARCHITECTURE.md +80 -0
  5. data/docs/dev/BRANCH_ONLY_COVERAGE.md +158 -0
  6. data/docs/dev/DEVELOPMENT.md +83 -0
  7. data/docs/dev/README.md +10 -0
  8. data/docs/dev/RELEASING.md +146 -0
  9. data/docs/dev/arch-decisions/001-x-arch-decision.md +95 -0
  10. data/docs/dev/arch-decisions/002-x-arch-decision.md +159 -0
  11. data/docs/dev/arch-decisions/003-x-arch-decision.md +165 -0
  12. data/docs/dev/arch-decisions/004-x-arch-decision.md +203 -0
  13. data/docs/dev/arch-decisions/005-x-arch-decision.md +189 -0
  14. data/docs/dev/arch-decisions/README.md +60 -0
  15. data/docs/dev/presentations/cov-loupe-presentation.md +255 -0
  16. data/docs/fixtures/demo_project/README.md +9 -0
  17. data/docs/user/ADVANCED_USAGE.md +777 -0
  18. data/docs/user/CLI_FALLBACK_FOR_LLMS.md +34 -0
  19. data/docs/user/CLI_USAGE.md +750 -0
  20. data/docs/user/ERROR_HANDLING.md +93 -0
  21. data/docs/user/EXAMPLES.md +588 -0
  22. data/docs/user/INSTALLATION.md +130 -0
  23. data/docs/user/LIBRARY_API.md +693 -0
  24. data/docs/user/MCP_INTEGRATION.md +490 -0
  25. data/docs/user/README.md +14 -0
  26. data/docs/user/TROUBLESHOOTING.md +197 -0
  27. data/docs/user/V2-BREAKING-CHANGES.md +472 -0
  28. data/exe/cov-loupe +23 -0
  29. data/lib/cov_loupe/app_config.rb +56 -0
  30. data/lib/cov_loupe/app_context.rb +26 -0
  31. data/lib/cov_loupe/base_tool.rb +102 -0
  32. data/lib/cov_loupe/cli.rb +178 -0
  33. data/lib/cov_loupe/commands/base_command.rb +67 -0
  34. data/lib/cov_loupe/commands/command_factory.rb +45 -0
  35. data/lib/cov_loupe/commands/detailed_command.rb +38 -0
  36. data/lib/cov_loupe/commands/list_command.rb +13 -0
  37. data/lib/cov_loupe/commands/raw_command.rb +38 -0
  38. data/lib/cov_loupe/commands/summary_command.rb +41 -0
  39. data/lib/cov_loupe/commands/totals_command.rb +53 -0
  40. data/lib/cov_loupe/commands/uncovered_command.rb +45 -0
  41. data/lib/cov_loupe/commands/validate_command.rb +60 -0
  42. data/lib/cov_loupe/commands/version_command.rb +33 -0
  43. data/lib/cov_loupe/config_parser.rb +32 -0
  44. data/lib/cov_loupe/constants.rb +22 -0
  45. data/lib/cov_loupe/coverage_reporter.rb +31 -0
  46. data/lib/cov_loupe/error_handler.rb +165 -0
  47. data/lib/cov_loupe/error_handler_factory.rb +31 -0
  48. data/lib/cov_loupe/errors.rb +191 -0
  49. data/lib/cov_loupe/formatters/source_formatter.rb +152 -0
  50. data/lib/cov_loupe/formatters.rb +51 -0
  51. data/lib/cov_loupe/mcp_server.rb +42 -0
  52. data/lib/cov_loupe/mode_detector.rb +56 -0
  53. data/lib/cov_loupe/model.rb +339 -0
  54. data/lib/cov_loupe/option_normalizers.rb +113 -0
  55. data/lib/cov_loupe/option_parser_builder.rb +147 -0
  56. data/lib/cov_loupe/option_parsers/env_options_parser.rb +48 -0
  57. data/lib/cov_loupe/option_parsers/error_helper.rb +110 -0
  58. data/lib/cov_loupe/path_relativizer.rb +64 -0
  59. data/lib/cov_loupe/predicate_evaluator.rb +72 -0
  60. data/lib/cov_loupe/presenters/base_coverage_presenter.rb +42 -0
  61. data/lib/cov_loupe/presenters/coverage_detailed_presenter.rb +14 -0
  62. data/lib/cov_loupe/presenters/coverage_raw_presenter.rb +14 -0
  63. data/lib/cov_loupe/presenters/coverage_summary_presenter.rb +14 -0
  64. data/lib/cov_loupe/presenters/coverage_uncovered_presenter.rb +14 -0
  65. data/lib/cov_loupe/presenters/project_coverage_presenter.rb +50 -0
  66. data/lib/cov_loupe/presenters/project_totals_presenter.rb +27 -0
  67. data/lib/cov_loupe/resolvers/coverage_line_resolver.rb +122 -0
  68. data/lib/cov_loupe/resolvers/resolver_factory.rb +28 -0
  69. data/lib/cov_loupe/resolvers/resultset_path_resolver.rb +76 -0
  70. data/lib/cov_loupe/resultset_loader.rb +131 -0
  71. data/lib/cov_loupe/staleness_checker.rb +247 -0
  72. data/lib/cov_loupe/table_formatter.rb +64 -0
  73. data/lib/cov_loupe/tools/all_files_coverage_tool.rb +51 -0
  74. data/lib/cov_loupe/tools/coverage_detailed_tool.rb +35 -0
  75. data/lib/cov_loupe/tools/coverage_raw_tool.rb +34 -0
  76. data/lib/cov_loupe/tools/coverage_summary_tool.rb +34 -0
  77. data/lib/cov_loupe/tools/coverage_table_tool.rb +50 -0
  78. data/lib/cov_loupe/tools/coverage_totals_tool.rb +44 -0
  79. data/lib/cov_loupe/tools/help_tool.rb +115 -0
  80. data/lib/cov_loupe/tools/uncovered_lines_tool.rb +34 -0
  81. data/lib/cov_loupe/tools/validate_tool.rb +72 -0
  82. data/lib/cov_loupe/tools/version_tool.rb +32 -0
  83. data/lib/cov_loupe/util.rb +88 -0
  84. data/lib/cov_loupe/version.rb +5 -0
  85. data/lib/cov_loupe.rb +140 -0
  86. data/spec/MCP_INTEGRATION_TESTS_README.md +111 -0
  87. data/spec/TIMESTAMPS.md +48 -0
  88. data/spec/all_files_coverage_tool_spec.rb +53 -0
  89. data/spec/app_config_spec.rb +142 -0
  90. data/spec/base_tool_spec.rb +62 -0
  91. data/spec/cli/show_default_report_spec.rb +33 -0
  92. data/spec/cli_enumerated_options_spec.rb +90 -0
  93. data/spec/cli_error_spec.rb +184 -0
  94. data/spec/cli_format_spec.rb +123 -0
  95. data/spec/cli_json_options_spec.rb +50 -0
  96. data/spec/cli_source_spec.rb +44 -0
  97. data/spec/cli_spec.rb +192 -0
  98. data/spec/cli_table_spec.rb +28 -0
  99. data/spec/cli_usage_spec.rb +42 -0
  100. data/spec/commands/base_command_spec.rb +107 -0
  101. data/spec/commands/command_factory_spec.rb +76 -0
  102. data/spec/commands/detailed_command_spec.rb +34 -0
  103. data/spec/commands/list_command_spec.rb +28 -0
  104. data/spec/commands/raw_command_spec.rb +69 -0
  105. data/spec/commands/summary_command_spec.rb +34 -0
  106. data/spec/commands/totals_command_spec.rb +34 -0
  107. data/spec/commands/uncovered_command_spec.rb +55 -0
  108. data/spec/commands/validate_command_spec.rb +213 -0
  109. data/spec/commands/version_command_spec.rb +38 -0
  110. data/spec/constants_spec.rb +61 -0
  111. data/spec/cov_loupe/formatters/source_formatter_spec.rb +267 -0
  112. data/spec/cov_loupe/formatters_spec.rb +76 -0
  113. data/spec/cov_loupe/presenters/base_coverage_presenter_spec.rb +79 -0
  114. data/spec/cov_loupe_model_spec.rb +454 -0
  115. data/spec/cov_loupe_module_spec.rb +37 -0
  116. data/spec/cov_loupe_opts_spec.rb +185 -0
  117. data/spec/coverage_reporter_spec.rb +102 -0
  118. data/spec/coverage_table_tool_spec.rb +59 -0
  119. data/spec/coverage_totals_tool_spec.rb +37 -0
  120. data/spec/error_handler_spec.rb +197 -0
  121. data/spec/error_mode_spec.rb +139 -0
  122. data/spec/errors_edge_cases_spec.rb +312 -0
  123. data/spec/errors_stale_spec.rb +83 -0
  124. data/spec/file_based_mcp_tools_spec.rb +99 -0
  125. data/spec/fixtures/project1/lib/bar.rb +5 -0
  126. data/spec/fixtures/project1/lib/foo.rb +6 -0
  127. data/spec/help_tool_spec.rb +26 -0
  128. data/spec/integration_spec.rb +789 -0
  129. data/spec/logging_fallback_spec.rb +128 -0
  130. data/spec/mcp_logging_spec.rb +44 -0
  131. data/spec/mcp_server_integration_spec.rb +23 -0
  132. data/spec/mcp_server_spec.rb +106 -0
  133. data/spec/mode_detector_spec.rb +153 -0
  134. data/spec/model_error_handling_spec.rb +269 -0
  135. data/spec/model_staleness_spec.rb +79 -0
  136. data/spec/option_normalizers_spec.rb +203 -0
  137. data/spec/option_parsers/env_options_parser_spec.rb +221 -0
  138. data/spec/option_parsers/error_helper_spec.rb +222 -0
  139. data/spec/path_relativizer_spec.rb +98 -0
  140. data/spec/presenters/coverage_detailed_presenter_spec.rb +19 -0
  141. data/spec/presenters/coverage_raw_presenter_spec.rb +15 -0
  142. data/spec/presenters/coverage_summary_presenter_spec.rb +15 -0
  143. data/spec/presenters/coverage_uncovered_presenter_spec.rb +16 -0
  144. data/spec/presenters/project_coverage_presenter_spec.rb +87 -0
  145. data/spec/presenters/project_totals_presenter_spec.rb +144 -0
  146. data/spec/resolvers/coverage_line_resolver_spec.rb +282 -0
  147. data/spec/resolvers/resolver_factory_spec.rb +61 -0
  148. data/spec/resolvers/resultset_path_resolver_spec.rb +60 -0
  149. data/spec/resultset_loader_spec.rb +167 -0
  150. data/spec/shared_examples/README.md +115 -0
  151. data/spec/shared_examples/coverage_presenter_examples.rb +66 -0
  152. data/spec/shared_examples/file_based_mcp_tools.rb +179 -0
  153. data/spec/shared_examples/formatted_command_examples.rb +64 -0
  154. data/spec/shared_examples/mcp_tool_text_json_response.rb +16 -0
  155. data/spec/spec_helper.rb +127 -0
  156. data/spec/staleness_checker_spec.rb +374 -0
  157. data/spec/staleness_more_spec.rb +42 -0
  158. data/spec/support/cli_helpers.rb +22 -0
  159. data/spec/support/control_flow_helpers.rb +20 -0
  160. data/spec/support/fake_mcp.rb +40 -0
  161. data/spec/support/io_helpers.rb +29 -0
  162. data/spec/support/mcp_helpers.rb +35 -0
  163. data/spec/support/mcp_runner.rb +66 -0
  164. data/spec/support/mocking_helpers.rb +30 -0
  165. data/spec/table_format_spec.rb +70 -0
  166. data/spec/tools/validate_tool_spec.rb +132 -0
  167. data/spec/tools_error_handling_spec.rb +130 -0
  168. data/spec/util_spec.rb +154 -0
  169. data/spec/version_spec.rb +123 -0
  170. data/spec/version_tool_spec.rb +141 -0
  171. metadata +290 -0
@@ -0,0 +1,472 @@
1
+ # V2.0 Breaking Changes Guide
2
+
3
+ This document describes all breaking changes introduced in version 2.0.0 of cov-loupe. These changes improve consistency, clarity, and alignment with Ruby conventions.
4
+
5
+ ---
6
+
7
+ ## Table of Contents
8
+
9
+ - [Command Line Interface Changes](#command-line-interface-changes)
10
+ - [Options Must Precede Subcommands](#options-must-precede-subcommands)
11
+ - [--stale Renamed to --staleness](#--stale-renamed-to---staleness)
12
+ - [--source-context Renamed to --context-lines](#--source-context-renamed-to---context-lines)
13
+ - [--source Now Requires Explicit Mode](#--source-now-requires-explicit-mode)
14
+ - [--json Replaced with --format](#--json-replaced-with---format)
15
+ - [Error Mode Values Changed](#error-mode-values-changed)
16
+ - [--success-predicate Replaced with validate Subcommand](#--success-predicate-replaced-with-validate-subcommand)
17
+ - [Default Sort Order Changed](#default-sort-order-changed)
18
+ - [MCP Tool Changes](#mcp-tool-changes)
19
+ - [stale Parameter Renamed to staleness](#stale-parameter-renamed-to-staleness)
20
+ - [Error Mode Values Changed](#error-mode-values-changed-1)
21
+ - [MCP Tool Arguments Use Symbols](#mcp-tool-arguments-use-symbols)
22
+ - [Ruby API Changes](#ruby-api-changes)
23
+ - [CLIConfig Renamed to AppConfig](#cliconfig-renamed-to-appconfig)
24
+ - [AppConfig Field Changes](#appconfig-field-changes)
25
+ - [Behavioral Changes](#behavioral-changes)
26
+ - [Context Lines Validation](#context-lines-validation)
27
+ - [Migration Guide](#migration-guide)
28
+
29
+ ---
30
+
31
+ ## Command Line Interface Changes
32
+
33
+ ### Options Must Precede Subcommands
34
+
35
+ **Change:** Global options must now appear *before* the subcommand, not after.
36
+
37
+ **Rationale:** This aligns with standard Unix command conventions (e.g., `git`, `docker`) and simplifies argument parsing.
38
+
39
+ **Before (v1.x):**
40
+ ```bash
41
+ cov-loupe list --resultset coverage --format json
42
+ cov-loupe summary lib/foo.rb --json
43
+ ```
44
+
45
+ **After (v2.x):**
46
+ ```bash
47
+ cov-loupe --resultset coverage --format json list
48
+ cov-loupe --format json summary lib/foo.rb
49
+ ```
50
+
51
+ **Migration:** Move all global options (flags like `-r`, `-f`, `-S`, etc.) before the subcommand name.
52
+
53
+ ---
54
+
55
+ ### --stale Renamed to --staleness
56
+
57
+ **Change:** The `--stale` option has been renamed to `--staleness`. The short form `-S` is preserved.
58
+
59
+ **Rationale:** Better describes what the option controls (staleness detection mode) and aligns with internal naming conventions.
60
+
61
+ **Before (v1.x):**
62
+ ```bash
63
+ cov-loupe --stale error list
64
+ ```
65
+
66
+ **After (v2.x):**
67
+ ```bash
68
+ cov-loupe --staleness error list
69
+ # OR use the short form:
70
+ cov-loupe -S error list
71
+ ```
72
+
73
+ **Migration:** Replace `--stale` with `--staleness` (or continue using `-S`).
74
+
75
+ ---
76
+
77
+ ### --source-context Renamed to --context-lines
78
+
79
+ **Change:** The `--source-context` option has been renamed to `--context-lines`.
80
+
81
+ **Rationale:** More concise and clearer about what the option controls.
82
+
83
+ **Before (v1.x):**
84
+ ```bash
85
+ cov-loupe --source uncovered --source-context 3 uncovered lib/foo.rb
86
+ ```
87
+
88
+ **After (v2.x):**
89
+ ```bash
90
+ cov-loupe --source uncovered --context-lines 3 uncovered lib/foo.rb
91
+ # OR use the short form:
92
+ cov-loupe -s uncovered -c 3 uncovered lib/foo.rb
93
+ ```
94
+
95
+ **Migration:** Replace `--source-context` with `--context-lines` (or use `-c`).
96
+
97
+ ---
98
+
99
+ ### --source Now Requires Explicit Mode
100
+
101
+ **Change:** The `--source` option now requires an explicit mode argument (`full` or `uncovered`).
102
+
103
+ **Rationale:** Eliminates ambiguity about what source display mode is being used.
104
+
105
+ **Before (v1.x):**
106
+ ```bash
107
+ # Implied 'full' mode
108
+ cov-loupe --source summary lib/foo.rb
109
+ ```
110
+
111
+ **After (v2.x):**
112
+ ```bash
113
+ # Must specify mode explicitly
114
+ cov-loupe --source full summary lib/foo.rb
115
+ # OR
116
+ cov-loupe --source uncovered summary lib/foo.rb
117
+ ```
118
+
119
+ **Migration:** Add an explicit mode (`full` or `uncovered`) after `--source`.
120
+
121
+ ---
122
+
123
+ ### --json Replaced with --format
124
+
125
+ **Change:** The `--json` flag (and related `-j`, `-J`, `--pretty-json` flags) have been removed. Use `--format` instead.
126
+
127
+ **Rationale:** Supports multiple output formats beyond JSON (YAML, awesome_print, etc.) with a consistent interface.
128
+
129
+ **Before (v1.x):**
130
+ ```bash
131
+ cov-loupe --json list
132
+ cov-loupe -j summary lib/foo.rb
133
+ cov-loupe --pretty-json list
134
+ ```
135
+
136
+ **After (v2.x):**
137
+ ```bash
138
+ cov-loupe --format json list
139
+ cov-loupe -f j summary lib/foo.rb # Short form
140
+ cov-loupe --format pretty-json list
141
+ cov-loupe -f J list # Short form for pretty-json
142
+ ```
143
+
144
+ **Available formats:**
145
+ - `table` (default) - Human-readable table format
146
+ - `json` or `j` - Single-line JSON
147
+ - `pretty-json` or `J` - Pretty-printed JSON
148
+ - `yaml` or `y` - YAML format
149
+ - `awesome-print` or `ap` - Colored awesome_print format (requires `awesome_print` gem)
150
+
151
+ **Migration:** Replace `--json` with `--format json` (or `-f j`). Replace `--pretty-json` with `--format pretty-json` (or `-f J`).
152
+
153
+ ---
154
+
155
+ ### Error Mode Values Changed
156
+
157
+ **Change:** Error mode enum values have been renamed for clarity:
158
+ - `on` → `log`
159
+ - `trace` → `debug`
160
+
161
+ The old values are **no longer supported**.
162
+
163
+ **Rationale:** More descriptive names that better communicate what each mode does.
164
+
165
+ **Before (v1.x):**
166
+ ```bash
167
+ cov-loupe --error-mode on list
168
+ cov-loupe --error-mode trace list
169
+ ```
170
+
171
+ **After (v2.x):**
172
+ ```bash
173
+ cov-loupe --error-mode log list
174
+ cov-loupe --error-mode debug list
175
+ # OR use short forms:
176
+ cov-loupe --error-mode l list
177
+ cov-loupe --error-mode d list
178
+ ```
179
+
180
+ **Error modes:**
181
+ - `off` (or `o`) - Silent, no error logging
182
+ - `log` (or `l`) - Log errors to file (default)
183
+ - `debug` (or `d`) - Verbose logging with backtraces
184
+
185
+ **Migration:** Replace `--error-mode on` with `--error-mode log`. Replace `--error-mode trace` with `--error-mode debug`.
186
+
187
+ ---
188
+
189
+ ### --success-predicate Replaced with validate Subcommand
190
+
191
+ **Change:** The `--success-predicate` flag has been removed. Use the `validate` subcommand instead.
192
+
193
+ **Rationale:** Better fits the subcommand paradigm and provides a clearer interface for policy validation.
194
+
195
+ **Before (v1.x):**
196
+ ```bash
197
+ cov-loupe --success-predicate policy.rb
198
+ ```
199
+
200
+ **After (v2.x):**
201
+ ```bash
202
+ # File-based policy
203
+ cov-loupe validate policy.rb
204
+
205
+ # Inline policy (new feature)
206
+ cov-loupe validate -i '->(m) { m.all_files.all? { |f| f["percentage"] >= 80 } }'
207
+ ```
208
+
209
+ **Migration:** Replace `--success-predicate FILE` with `validate FILE`.
210
+
211
+ ---
212
+
213
+ ### Default Sort Order Changed
214
+
215
+ **Change:** The default sort order for the `list` command changed from `ascending` to `descending`.
216
+
217
+ **Rationale:** Most users want to see best-covered files first, not worst-covered files first.
218
+
219
+ **Before (v1.x):**
220
+ ```bash
221
+ # Shows worst coverage first by default
222
+ cov-loupe list
223
+ ```
224
+
225
+ **After (v2.x):**
226
+ ```bash
227
+ # Shows best coverage first by default
228
+ cov-loupe list
229
+
230
+ # To get old behavior (worst first):
231
+ cov-loupe --sort-order ascending list
232
+ cov-loupe -o a list # Short form
233
+ ```
234
+
235
+ **Migration:** If you relied on ascending order (worst coverage first), explicitly specify `--sort-order ascending` or `-o a`.
236
+
237
+ ---
238
+
239
+ ## MCP Tool Changes
240
+
241
+ ### stale Parameter Renamed to staleness
242
+
243
+ **Change:** All MCP tools that accepted a `stale` parameter now use `staleness` instead.
244
+
245
+ **Rationale:** Aligns with the `CoverageModel` API and CLI option naming.
246
+
247
+ **Before (v1.x):**
248
+ ```json
249
+ {
250
+ "jsonrpc": "2.0",
251
+ "method": "tools/call",
252
+ "params": {
253
+ "name": "coverage_summary_tool",
254
+ "arguments": {
255
+ "path": "lib/foo.rb",
256
+ "stale": "error"
257
+ }
258
+ }
259
+ }
260
+ ```
261
+
262
+ **After (v2.x):**
263
+ ```json
264
+ {
265
+ "jsonrpc": "2.0",
266
+ "method": "tools/call",
267
+ "params": {
268
+ "name": "coverage_summary_tool",
269
+ "arguments": {
270
+ "path": "lib/foo.rb",
271
+ "staleness": "error"
272
+ }
273
+ }
274
+ }
275
+ ```
276
+
277
+ **Affected tools:** All file-based tools (`coverage_summary_tool`, `coverage_detailed_tool`, `coverage_raw_tool`, `uncovered_lines_tool`) and aggregate tools (`all_files_coverage_tool`, `coverage_totals_tool`).
278
+
279
+ **Migration:** Replace `"stale"` with `"staleness"` in all MCP tool calls.
280
+
281
+ ---
282
+
283
+ ### Error Mode Values Changed
284
+
285
+ **Change:** Error mode enum values changed from `['off', 'on', 'trace']` to `['off', 'log', 'debug']`.
286
+
287
+ **Rationale:** More descriptive names matching CLI changes.
288
+
289
+ **Before (v1.x):**
290
+ ```json
291
+ {
292
+ "error_mode": "on"
293
+ }
294
+ ```
295
+
296
+ **After (v2.x):**
297
+ ```json
298
+ {
299
+ "error_mode": "log"
300
+ }
301
+ ```
302
+
303
+ **Migration:** Replace `"on"` with `"log"`, replace `"trace"` with `"debug"`.
304
+
305
+ ---
306
+
307
+ ### MCP Tool Arguments Use Symbols
308
+
309
+ **Change:** Internally, MCP tools now normalize enum arguments to symbols (`:off`, `:error`, `:log`, `:debug`) for consistency with the Ruby API.
310
+
311
+ **Impact:** This is mostly an internal change. MCP clients still send strings in JSON, but if you're using the tools programmatically in Ruby, be aware of the symbol usage.
312
+
313
+ **Migration:** No action needed for MCP clients. For Ruby API users, see [Ruby API Changes](#ruby-api-changes).
314
+
315
+ ---
316
+
317
+ ## Ruby API Changes
318
+
319
+ ### CLIConfig Renamed to AppConfig
320
+
321
+ **Change:** The `CLIConfig` class has been renamed to `AppConfig`.
322
+
323
+ **Rationale:** The configuration is now used by both CLI and MCP modes, not just CLI.
324
+
325
+ **Before (v1.x):**
326
+ ```ruby
327
+ require 'cov_loupe/cli_config'
328
+ config = CovLoupe::CLIConfig.new(root: '.', json: true)
329
+ ```
330
+
331
+ **After (v2.x):**
332
+ ```ruby
333
+ require 'cov_loupe/app_config'
334
+ config = CovLoupe::AppConfig.new(root: '.', format: :json)
335
+ ```
336
+
337
+ **Migration:** Replace `CLIConfig` with `AppConfig` in your code. Update require statements from `'cov_loupe/cli_config'` to `'cov_loupe/app_config'`.
338
+
339
+ ---
340
+
341
+ ### AppConfig Field Changes
342
+
343
+ **Change:** Several `AppConfig` fields have been renamed or changed:
344
+
345
+ | Old Field (v1.x) | New Field (v2.x) | Type Change |
346
+ |-----------------------|------------------|--------------------------------------|
347
+ | `json` | `format` | `Boolean` → `Symbol` (`:json`, `:table`, etc.) |
348
+ | `stale_mode` | `staleness` | Name change only |
349
+ | `success_predicate` | (removed) | Moved to `validate` subcommand |
350
+ | (new) | `show_version` | New field for `-v`/`--version` |
351
+
352
+ **Default value changes:**
353
+ - `sort_order`: Changed from `:ascending` to `:descending`
354
+ - `error_mode`: Changed from `:on` to `:log`
355
+
356
+ **Before (v1.x):**
357
+ ```ruby
358
+ config = CovLoupe::CLIConfig.new(
359
+ json: true,
360
+ stale_mode: :error,
361
+ error_mode: :on,
362
+ sort_order: :ascending
363
+ )
364
+ ```
365
+
366
+ **After (v2.x):**
367
+ ```ruby
368
+ config = CovLoupe::AppConfig.new(
369
+ format: :json,
370
+ staleness: :error,
371
+ error_mode: :log,
372
+ sort_order: :descending # New default
373
+ )
374
+ ```
375
+
376
+ **Migration:** Update field names when constructing `AppConfig`. Note the new defaults.
377
+
378
+ ---
379
+
380
+ ## Behavioral Changes
381
+
382
+ ### Context Lines Validation
383
+
384
+ **Change:** The `--context-lines` option (formerly `--source-context`) now raises an `ArgumentError` if given a negative value. Previously, negative values were silently clamped to zero.
385
+
386
+ **Rationale:** Fail fast and provide clear feedback for invalid input.
387
+
388
+ **Before (v1.x):**
389
+ ```bash
390
+ # Silently clamped to 0
391
+ cov-loupe --source-context -5 uncovered lib/foo.rb
392
+ ```
393
+
394
+ **After (v2.x):**
395
+ ```bash
396
+ # Raises ArgumentError
397
+ cov-loupe --context-lines -5 uncovered lib/foo.rb
398
+ # Error: Context lines must be non-negative (got: -5)
399
+ ```
400
+
401
+ **Migration:** Ensure `--context-lines` values are non-negative (>= 0).
402
+
403
+ ---
404
+
405
+ ## Migration Guide
406
+
407
+ ### Quick Checklist
408
+
409
+ - [ ] Move all global options before subcommands in CLI invocations
410
+ - [ ] Replace `--stale` with `--staleness` (or continue using `-S`)
411
+ - [ ] Replace `--source-context` with `--context-lines` (or use `-c`)
412
+ - [ ] Add explicit mode to `--source` (either `full` or `uncovered`)
413
+ - [ ] Replace `--json` with `--format json` (or `-f j`)
414
+ - [ ] Replace `--error-mode on` with `--error-mode log`
415
+ - [ ] Replace `--error-mode trace` with `--error-mode debug`
416
+ - [ ] Replace `--success-predicate FILE` with `validate FILE`
417
+ - [ ] Update MCP tool calls: rename `stale` to `staleness`
418
+ - [ ] Update MCP tool calls: replace error mode `"on"` with `"log"`, `"trace"` with `"debug"`
419
+ - [ ] Update Ruby code: rename `CLIConfig` to `AppConfig`
420
+ - [ ] Update Ruby code: rename `json` field to `format`, `stale_mode` to `staleness`
421
+ - [ ] Explicitly set `--sort-order ascending` if you need worst-coverage-first sorting
422
+ - [ ] Ensure `--context-lines` values are non-negative
423
+
424
+ ### Script Migration Example
425
+
426
+ **Before (v1.x):**
427
+ ```bash
428
+ #!/bin/bash
429
+ cov-loupe list --json --stale error --sort-order ascending
430
+ cov-loupe summary lib/foo.rb --json
431
+ cov-loupe uncovered lib/bar.rb --source=uncovered --source-context 3
432
+ cov-loupe --success-predicate policy.rb
433
+ ```
434
+
435
+ **After (v2.x):**
436
+ ```bash
437
+ #!/bin/bash
438
+ cov-loupe --format json --staleness error --sort-order ascending list
439
+ cov-loupe --format json summary lib/foo.rb
440
+ cov-loupe --source uncovered --context-lines 3 uncovered lib/bar.rb
441
+ cov-loupe validate policy.rb
442
+ ```
443
+
444
+ ### Environment Variable Migration
445
+
446
+ **Before (v1.x):**
447
+ ```bash
448
+ export COV_LOUPE_OPTS="--stale error --json"
449
+ ```
450
+
451
+ **After (v2.x):**
452
+ ```bash
453
+ export COV_LOUPE_OPTS="--staleness error --format json"
454
+ ```
455
+
456
+ ---
457
+
458
+ ## Getting Help
459
+
460
+ If you encounter issues migrating to v2.0:
461
+
462
+ 1. Check the [TROUBLESHOOTING.md](TROUBLESHOOTING.md) guide
463
+ 2. Review the [CLI_USAGE.md](CLI_USAGE.md) for complete CLI reference
464
+ 3. See [MCP_INTEGRATION.md](MCP_INTEGRATION.md) for MCP tool documentation
465
+ 4. Open an issue at https://github.com/keithrbennett/cov-loupe/issues
466
+
467
+ ---
468
+
469
+ **See also:**
470
+ - [RELEASE_NOTES.md](../../RELEASE_NOTES.md) - Full release notes with new features
471
+ - [CLI_USAGE.md](CLI_USAGE.md) - Complete CLI reference
472
+ - [MCP_INTEGRATION.md](MCP_INTEGRATION.md) - MCP tool reference
data/exe/cov-loupe ADDED
@@ -0,0 +1,23 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ # Make it safe to run via symlink by resolving the real path
5
+ root = File.expand_path('..', File.realpath(__FILE__))
6
+ lib = File.join(root, 'lib')
7
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
8
+
9
+ # Fail fast with a helpful message if Ruby is too old for this gem
10
+ begin
11
+ require 'rubygems'
12
+ if Gem::Version.new(RUBY_VERSION) < Gem::Version.new('3.2')
13
+ $stderr.puts "cov-loupe requires Ruby >= 3.2 (current: #{RUBY_VERSION})."
14
+ $stderr.puts 'Please run with a supported Ruby version (e.g., via rbenv, rvm, asdf).'
15
+ exit 1
16
+ end
17
+ rescue
18
+ # If anything goes wrong, let the app load and potentially fail with a more specific error.
19
+ end
20
+
21
+ require 'cov_loupe'
22
+
23
+ CovLoupe.run(ARGV)
@@ -0,0 +1,56 @@
1
+ # frozen_string_literal: true
2
+
3
+ module CovLoupe
4
+ # Configuration container for application options (used by both CLI and MCP modes)
5
+ # Uses Struct for simplicity and built-in functionality
6
+ AppConfig = Struct.new(
7
+ :root,
8
+ :resultset,
9
+ :format,
10
+ :sort_order,
11
+ :source_mode,
12
+ :source_context,
13
+ :color,
14
+ :error_mode,
15
+ :staleness,
16
+ :tracked_globs,
17
+ :log_file,
18
+ :show_version,
19
+ keyword_init: true
20
+ ) do
21
+ # Set sensible defaults - ALL SYMBOLS FOR ENUMS
22
+ def initialize(
23
+ root: '.',
24
+ resultset: nil,
25
+ format: :table,
26
+ sort_order: :descending,
27
+ source_mode: nil,
28
+ source_context: 2,
29
+ color: $stdout.tty?,
30
+ error_mode: :log,
31
+ staleness: :off,
32
+ tracked_globs: nil,
33
+ log_file: nil,
34
+ show_version: false
35
+ )
36
+ super
37
+ end
38
+
39
+ # Convenience method for CoverageModel initialization
40
+ def model_options
41
+ {
42
+ root: root,
43
+ resultset: resultset,
44
+ staleness: staleness,
45
+ tracked_globs: tracked_globs
46
+ }
47
+ end
48
+
49
+ # Convenience method for SourceFormatter initialization
50
+ def formatter_options
51
+ {
52
+ color_enabled: color
53
+ }
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ module CovLoupe
4
+ # Encapsulates per-request configuration such as error handling and logging.
5
+ class AppContext
6
+ attr_reader :error_handler, :log_target, :mode
7
+
8
+ def initialize(error_handler:, log_target: nil, mode: :library)
9
+ @error_handler = error_handler
10
+ @log_target = log_target
11
+ @mode = mode
12
+ end
13
+
14
+ def with_error_handler(handler)
15
+ self.class.new(error_handler: handler, log_target: log_target, mode: mode)
16
+ end
17
+
18
+ def with_log_target(target)
19
+ self.class.new(error_handler: error_handler, log_target: target, mode: mode)
20
+ end
21
+
22
+ def mcp_mode? = mode == :mcp
23
+ def cli_mode? = mode == :cli
24
+ def library_mode? = mode == :library
25
+ end
26
+ end
@@ -0,0 +1,102 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'mcp'
4
+ require 'json'
5
+ require_relative 'errors'
6
+ require_relative 'error_handler'
7
+
8
+ module CovLoupe
9
+ class BaseTool < ::MCP::Tool
10
+ COMMON_PROPERTIES = {
11
+ root: {
12
+ type: 'string',
13
+ description: 'Project root used to resolve relative paths ' \
14
+ '(defaults to current workspace).',
15
+ default: '.'
16
+ },
17
+ resultset: {
18
+ type: 'string',
19
+ description: 'Path to the SimpleCov .resultset.json file (absolute or relative to root).'
20
+ },
21
+ staleness: {
22
+ type: 'string',
23
+ description: 'How to handle missing/outdated coverage data. ' \
24
+ "'off' skips checks; 'error' raises.",
25
+ enum: [:off, :error],
26
+ default: :off
27
+ },
28
+ error_mode: {
29
+ type: 'string',
30
+ description: "Error handling mode: 'off' (silent), 'log' (log errors), " \
31
+ "'debug' (verbose with backtraces).",
32
+ enum: %w[off log debug],
33
+ default: 'log'
34
+ }
35
+ }.freeze
36
+
37
+ ERROR_MODE_PROPERTY = COMMON_PROPERTIES[:error_mode].freeze
38
+
39
+ TRACKED_GLOBS_PROPERTY = {
40
+ type: 'array',
41
+ description: 'Glob patterns for files that should exist in the coverage report' \
42
+ '(helps flag new files).',
43
+ items: { type: 'string' }
44
+ }.freeze
45
+
46
+ PATH_PROPERTY = {
47
+ type: 'string',
48
+ description: 'Repo-relative or absolute path to the file whose coverage data you need.',
49
+ examples: ['lib/cov_loupe/model.rb']
50
+ }.freeze
51
+
52
+ def self.coverage_schema(additional_properties: {}, required: [])
53
+ {
54
+ type: 'object',
55
+ additionalProperties: false,
56
+ properties: COMMON_PROPERTIES.merge(additional_properties),
57
+ required: required
58
+ }.freeze
59
+ end
60
+
61
+ FILE_INPUT_SCHEMA = coverage_schema(
62
+ additional_properties: { path: PATH_PROPERTY },
63
+ required: ['path']
64
+ )
65
+ def self.input_schema_def = FILE_INPUT_SCHEMA
66
+
67
+ # Wrap tool execution with consistent error handling.
68
+ # Yields to the block and rescues any error, delegating to handle_mcp_error.
69
+ # This eliminates duplicate rescue blocks across all tools.
70
+ def self.with_error_handling(tool_name, error_mode:)
71
+ yield
72
+ rescue => e
73
+ handle_mcp_error(e, tool_name, error_mode: error_mode)
74
+ end
75
+
76
+ # Handle errors consistently across all MCP tools
77
+ # Returns an MCP::Tool::Response with appropriate error message
78
+ def self.handle_mcp_error(error, tool_name, error_mode: :log)
79
+ # Create error handler with the specified mode
80
+ error_handler = ErrorHandlerFactory.for_mcp_server(error_mode: error_mode.to_sym)
81
+
82
+ # Normalize to a CovLoupe::Error so we can handle/log uniformly
83
+ normalized = error.is_a?(CovLoupe::Error) \
84
+ ? error : error_handler.convert_standard_error(error)
85
+ log_mcp_error(normalized, tool_name, error_handler)
86
+ ::MCP::Tool::Response.new([{ 'type' => 'text', 'text' => "Error: #{normalized.user_friendly_message}" }])
87
+ end
88
+
89
+ # Respond with JSON as a resource to avoid clients mutating content types.
90
+ # The resource embeds the JSON string with a clear MIME type.
91
+ def self.respond_json(payload, name: 'data.json', pretty: false)
92
+ json = pretty ? JSON.pretty_generate(payload) : JSON.generate(payload)
93
+ ::MCP::Tool::Response.new([{ 'type' => 'text', 'text' => json }])
94
+ end
95
+
96
+ def self.log_mcp_error(error, tool_name, error_handler)
97
+ # Use the provided error handler for logging
98
+ error_handler.send(:log_error, error, tool_name)
99
+ end
100
+ private_class_method :log_mcp_error
101
+ end
102
+ end