cov-loupe 3.0.0 → 4.0.0.pre

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 (281) hide show
  1. checksums.yaml +4 -4
  2. data/AGENTS.md +230 -0
  3. data/CLAUDE.md +5 -0
  4. data/CODE_OF_CONDUCT.md +62 -0
  5. data/CONTRIBUTING.md +102 -0
  6. data/GEMINI.md +5 -0
  7. data/README.md +154 -51
  8. data/RELEASE_NOTES.md +452 -0
  9. data/dev/images/cov-loupe-icon-lores.png +0 -0
  10. data/dev/images/cov-loupe-icon-square.png +0 -0
  11. data/dev/images/cov-loupe-icon.png +0 -0
  12. data/dev/images/cov-loupe-logo.png +0 -0
  13. data/dev/prompts/README.md +74 -0
  14. data/dev/prompts/archive/architectural-review-and-actions-prompt.md +53 -0
  15. data/dev/prompts/archive/investigate-and-report-issues-prompt.md +33 -0
  16. data/dev/prompts/archive/produce-action-items-prompt.md +25 -0
  17. data/dev/prompts/guidelines/ai-code-evaluator-guidelines.md +337 -0
  18. data/dev/prompts/improve/refactor-test-suite.md +18 -0
  19. data/dev/prompts/improve/simplify-code-logic.md +133 -0
  20. data/dev/prompts/improve/update-documentation.md +21 -0
  21. data/dev/prompts/review/comprehensive-codebase-review.md +176 -0
  22. data/dev/prompts/review/identify-action-items.md +143 -0
  23. data/dev/prompts/review/verify-code-changes.md +54 -0
  24. data/dev/prompts/validate/create-screencast-outline.md +234 -0
  25. data/dev/prompts/validate/test-documentation-examples.md +180 -0
  26. data/docs/QUICKSTART.md +63 -0
  27. data/docs/assets/images/cov-loupe-logo-lores.png +0 -0
  28. data/docs/assets/images/cov-loupe-logo.png +0 -0
  29. data/docs/assets/images/favicon.png +0 -0
  30. data/docs/assets/stylesheets/branding.css +16 -0
  31. data/docs/assets/stylesheets/extra.css +15 -0
  32. data/docs/code_of_conduct.md +1 -0
  33. data/docs/contributing.md +1 -0
  34. data/docs/dev/ARCHITECTURE.md +56 -11
  35. data/docs/dev/DEVELOPMENT.md +116 -12
  36. data/docs/dev/FUTURE_ENHANCEMENTS.md +14 -0
  37. data/docs/dev/README.md +3 -2
  38. data/docs/dev/RELEASING.md +2 -0
  39. data/docs/dev/arch-decisions/README.md +10 -7
  40. data/docs/dev/arch-decisions/application-architecture.md +259 -0
  41. data/docs/dev/arch-decisions/coverage-data-quality.md +193 -0
  42. data/docs/dev/arch-decisions/output-character-mode.md +217 -0
  43. data/docs/dev/arch-decisions/path-resolution.md +90 -0
  44. data/docs/dev/arch-decisions/{004-x-arch-decision.md → policy-validation.md} +32 -28
  45. data/docs/dev/arch-decisions/{005-x-arch-decision.md → simplecov-integration.md} +47 -44
  46. data/docs/dev/presentations/cov-loupe-presentation.md +15 -13
  47. data/docs/examples/mcp-inputs.md +3 -0
  48. data/docs/examples/prompts.md +3 -0
  49. data/docs/examples/success_predicates.md +3 -0
  50. data/docs/fixtures/demo_project/.resultset.json +170 -0
  51. data/docs/fixtures/demo_project/README.md +6 -0
  52. data/docs/fixtures/demo_project/app/controllers/admin/audit_logs_controller.rb +19 -0
  53. data/docs/fixtures/demo_project/app/controllers/orders_controller.rb +26 -0
  54. data/docs/fixtures/demo_project/app/models/order.rb +20 -0
  55. data/docs/fixtures/demo_project/app/models/user.rb +19 -0
  56. data/docs/fixtures/demo_project/lib/api/client.rb +22 -0
  57. data/docs/fixtures/demo_project/lib/ops/jobs/cleanup_job.rb +16 -0
  58. data/docs/fixtures/demo_project/lib/ops/jobs/report_job.rb +17 -0
  59. data/docs/fixtures/demo_project/lib/payments/processor.rb +15 -0
  60. data/docs/fixtures/demo_project/lib/payments/refund_service.rb +15 -0
  61. data/docs/fixtures/demo_project/lib/payments/reporting/exporter.rb +16 -0
  62. data/docs/index.md +1 -0
  63. data/docs/license.md +3 -0
  64. data/docs/release_notes.md +3 -0
  65. data/docs/user/ADVANCED_USAGE.md +208 -115
  66. data/docs/user/CLI_FALLBACK_FOR_LLMS.md +2 -0
  67. data/docs/user/CLI_USAGE.md +276 -101
  68. data/docs/user/ERROR_HANDLING.md +4 -4
  69. data/docs/user/EXAMPLES.md +121 -128
  70. data/docs/user/INSTALLATION.md +9 -28
  71. data/docs/user/LIBRARY_API.md +227 -122
  72. data/docs/user/MCP_INTEGRATION.md +114 -203
  73. data/docs/user/README.md +5 -1
  74. data/docs/user/TROUBLESHOOTING.md +49 -27
  75. data/docs/user/installing-a-prelease-version-of-covloupe.md +43 -0
  76. data/docs/user/{V2-BREAKING-CHANGES.md → migrations/MIGRATING_TO_V2.md} +62 -72
  77. data/docs/user/migrations/MIGRATING_TO_V3.md +72 -0
  78. data/docs/user/migrations/MIGRATING_TO_V4.md +591 -0
  79. data/docs/user/migrations/README.md +22 -0
  80. data/docs/user/prompts/README.md +9 -0
  81. data/docs/user/prompts/non-web-coverage-analysis-prompt.md +103 -0
  82. data/docs/user/prompts/rails-coverage-analysis-prompt.md +94 -0
  83. data/docs/user/prompts/use-cli-not-mcp-prompt.md +53 -0
  84. data/examples/cli_demo.sh +77 -0
  85. data/examples/filter_and_table_demo-output.md +114 -0
  86. data/examples/filter_and_table_demo.rb +174 -0
  87. data/examples/fixtures/demo_project/coverage/.resultset.json +10 -0
  88. data/examples/mcp-inputs/README.md +66 -0
  89. data/examples/mcp-inputs/coverage_detailed.json +1 -0
  90. data/examples/mcp-inputs/coverage_raw.json +1 -0
  91. data/examples/mcp-inputs/coverage_summary.json +1 -0
  92. data/examples/mcp-inputs/list.json +1 -0
  93. data/examples/mcp-inputs/uncovered_lines.json +1 -0
  94. data/examples/prompts/README.md +27 -0
  95. data/examples/prompts/custom_resultset.txt +2 -0
  96. data/examples/prompts/detailed_with_source.txt +2 -0
  97. data/examples/prompts/list_lowest.txt +2 -0
  98. data/examples/prompts/summary.txt +2 -0
  99. data/examples/prompts/uncovered.txt +2 -0
  100. data/examples/success_predicates/README.md +198 -0
  101. data/examples/success_predicates/all_files_above_threshold_predicate.rb +21 -0
  102. data/examples/success_predicates/directory_specific_thresholds_predicate.rb +30 -0
  103. data/examples/success_predicates/project_coverage_minimum_predicate.rb +6 -0
  104. data/lib/cov_loupe/base_tool.rb +229 -20
  105. data/lib/cov_loupe/cli.rb +132 -23
  106. data/lib/cov_loupe/commands/base_command.rb +25 -6
  107. data/lib/cov_loupe/commands/command_factory.rb +0 -1
  108. data/lib/cov_loupe/commands/detailed_command.rb +10 -5
  109. data/lib/cov_loupe/commands/list_command.rb +2 -1
  110. data/lib/cov_loupe/commands/raw_command.rb +7 -5
  111. data/lib/cov_loupe/commands/summary_command.rb +12 -7
  112. data/lib/cov_loupe/commands/totals_command.rb +74 -10
  113. data/lib/cov_loupe/commands/uncovered_command.rb +7 -5
  114. data/lib/cov_loupe/commands/validate_command.rb +11 -3
  115. data/lib/cov_loupe/commands/version_command.rb +6 -4
  116. data/lib/cov_loupe/{app_config.rb → config/app_config.rb} +13 -5
  117. data/lib/cov_loupe/config/app_context.rb +43 -0
  118. data/lib/cov_loupe/config/boolean_type.rb +91 -0
  119. data/lib/cov_loupe/config/logger.rb +92 -0
  120. data/lib/cov_loupe/{option_normalizers.rb → config/option_normalizers.rb} +55 -24
  121. data/lib/cov_loupe/{option_parser_builder.rb → config/option_parser_builder.rb} +46 -24
  122. data/lib/cov_loupe/coverage/coverage_calculator.rb +53 -0
  123. data/lib/cov_loupe/coverage/coverage_reporter.rb +63 -0
  124. data/lib/cov_loupe/coverage/coverage_table_formatter.rb +133 -0
  125. data/lib/cov_loupe/{error_handler.rb → errors/error_handler.rb} +21 -33
  126. data/lib/cov_loupe/{errors.rb → errors/errors.rb} +48 -71
  127. data/lib/cov_loupe/formatters/formatters.rb +75 -0
  128. data/lib/cov_loupe/formatters/source_formatter.rb +18 -7
  129. data/lib/cov_loupe/formatters/table_formatter.rb +80 -0
  130. data/lib/cov_loupe/loaders/all.rb +15 -0
  131. data/lib/cov_loupe/loaders/all_cli.rb +10 -0
  132. data/lib/cov_loupe/loaders/all_mcp.rb +23 -0
  133. data/lib/cov_loupe/loaders/resultset_loader.rb +147 -0
  134. data/lib/cov_loupe/mcp_server.rb +3 -2
  135. data/lib/cov_loupe/model/model.rb +520 -0
  136. data/lib/cov_loupe/model/model_data.rb +13 -0
  137. data/lib/cov_loupe/model/model_data_cache.rb +116 -0
  138. data/lib/cov_loupe/option_parsers/env_options_parser.rb +17 -6
  139. data/lib/cov_loupe/option_parsers/error_helper.rb +16 -10
  140. data/lib/cov_loupe/output_chars.rb +192 -0
  141. data/lib/cov_loupe/paths/glob_utils.rb +100 -0
  142. data/lib/cov_loupe/{path_relativizer.rb → paths/path_relativizer.rb} +5 -13
  143. data/lib/cov_loupe/paths/path_utils.rb +265 -0
  144. data/lib/cov_loupe/paths/volume_case_sensitivity.rb +173 -0
  145. data/lib/cov_loupe/presenters/base_coverage_presenter.rb +9 -13
  146. data/lib/cov_loupe/presenters/coverage_payload_presenter.rb +21 -0
  147. data/lib/cov_loupe/presenters/payload_caching.rb +23 -0
  148. data/lib/cov_loupe/presenters/project_coverage_presenter.rb +73 -21
  149. data/lib/cov_loupe/presenters/project_totals_presenter.rb +16 -10
  150. data/lib/cov_loupe/repositories/coverage_repository.rb +149 -0
  151. data/lib/cov_loupe/resolvers/coverage_line_resolver.rb +90 -76
  152. data/lib/cov_loupe/resolvers/{resolver_factory.rb → resolver_helpers.rb} +6 -5
  153. data/lib/cov_loupe/resolvers/resultset_path_resolver.rb +40 -12
  154. data/lib/cov_loupe/scripts/command_execution.rb +113 -0
  155. data/lib/cov_loupe/scripts/latest_ci_status.rb +97 -0
  156. data/lib/cov_loupe/scripts/pre_release_check.rb +164 -0
  157. data/lib/cov_loupe/scripts/setup_doc_server.rb +23 -0
  158. data/lib/cov_loupe/scripts/start_doc_server.rb +24 -0
  159. data/lib/cov_loupe/staleness/stale_status.rb +23 -0
  160. data/lib/cov_loupe/staleness/staleness_checker.rb +328 -0
  161. data/lib/cov_loupe/staleness/staleness_message_formatter.rb +91 -0
  162. data/lib/cov_loupe/tools/coverage_detailed_tool.rb +14 -15
  163. data/lib/cov_loupe/tools/coverage_raw_tool.rb +14 -14
  164. data/lib/cov_loupe/tools/coverage_summary_tool.rb +16 -16
  165. data/lib/cov_loupe/tools/coverage_table_tool.rb +139 -21
  166. data/lib/cov_loupe/tools/coverage_totals_tool.rb +31 -13
  167. data/lib/cov_loupe/tools/help_tool.rb +16 -20
  168. data/lib/cov_loupe/tools/list_tool.rb +65 -0
  169. data/lib/cov_loupe/tools/uncovered_lines_tool.rb +14 -14
  170. data/lib/cov_loupe/tools/validate_tool.rb +18 -24
  171. data/lib/cov_loupe/tools/version_tool.rb +8 -3
  172. data/lib/cov_loupe/version.rb +1 -1
  173. data/lib/cov_loupe.rb +83 -55
  174. metadata +184 -154
  175. data/docs/dev/BRANCH_ONLY_COVERAGE.md +0 -158
  176. data/docs/dev/arch-decisions/001-x-arch-decision.md +0 -95
  177. data/docs/dev/arch-decisions/002-x-arch-decision.md +0 -159
  178. data/docs/dev/arch-decisions/003-x-arch-decision.md +0 -165
  179. data/lib/cov_loupe/app_context.rb +0 -26
  180. data/lib/cov_loupe/constants.rb +0 -22
  181. data/lib/cov_loupe/coverage_reporter.rb +0 -31
  182. data/lib/cov_loupe/formatters.rb +0 -51
  183. data/lib/cov_loupe/mode_detector.rb +0 -56
  184. data/lib/cov_loupe/model.rb +0 -339
  185. data/lib/cov_loupe/presenters/coverage_detailed_presenter.rb +0 -14
  186. data/lib/cov_loupe/presenters/coverage_raw_presenter.rb +0 -14
  187. data/lib/cov_loupe/presenters/coverage_summary_presenter.rb +0 -14
  188. data/lib/cov_loupe/presenters/coverage_uncovered_presenter.rb +0 -14
  189. data/lib/cov_loupe/resultset_loader.rb +0 -131
  190. data/lib/cov_loupe/staleness_checker.rb +0 -247
  191. data/lib/cov_loupe/table_formatter.rb +0 -64
  192. data/lib/cov_loupe/tools/all_files_coverage_tool.rb +0 -51
  193. data/lib/cov_loupe/util.rb +0 -88
  194. data/spec/MCP_INTEGRATION_TESTS_README.md +0 -111
  195. data/spec/TIMESTAMPS.md +0 -48
  196. data/spec/all_files_coverage_tool_spec.rb +0 -53
  197. data/spec/app_config_spec.rb +0 -142
  198. data/spec/base_tool_spec.rb +0 -62
  199. data/spec/cli/show_default_report_spec.rb +0 -33
  200. data/spec/cli_enumerated_options_spec.rb +0 -90
  201. data/spec/cli_error_spec.rb +0 -184
  202. data/spec/cli_format_spec.rb +0 -123
  203. data/spec/cli_json_options_spec.rb +0 -50
  204. data/spec/cli_source_spec.rb +0 -44
  205. data/spec/cli_spec.rb +0 -192
  206. data/spec/cli_table_spec.rb +0 -28
  207. data/spec/cli_usage_spec.rb +0 -42
  208. data/spec/commands/base_command_spec.rb +0 -107
  209. data/spec/commands/command_factory_spec.rb +0 -76
  210. data/spec/commands/detailed_command_spec.rb +0 -34
  211. data/spec/commands/list_command_spec.rb +0 -28
  212. data/spec/commands/raw_command_spec.rb +0 -69
  213. data/spec/commands/summary_command_spec.rb +0 -34
  214. data/spec/commands/totals_command_spec.rb +0 -34
  215. data/spec/commands/uncovered_command_spec.rb +0 -55
  216. data/spec/commands/validate_command_spec.rb +0 -213
  217. data/spec/commands/version_command_spec.rb +0 -38
  218. data/spec/constants_spec.rb +0 -61
  219. data/spec/cov_loupe/formatters/source_formatter_spec.rb +0 -267
  220. data/spec/cov_loupe/formatters_spec.rb +0 -76
  221. data/spec/cov_loupe/presenters/base_coverage_presenter_spec.rb +0 -79
  222. data/spec/cov_loupe_model_spec.rb +0 -454
  223. data/spec/cov_loupe_module_spec.rb +0 -37
  224. data/spec/cov_loupe_opts_spec.rb +0 -185
  225. data/spec/coverage_reporter_spec.rb +0 -102
  226. data/spec/coverage_table_tool_spec.rb +0 -59
  227. data/spec/coverage_totals_tool_spec.rb +0 -37
  228. data/spec/error_handler_spec.rb +0 -197
  229. data/spec/error_mode_spec.rb +0 -139
  230. data/spec/errors_edge_cases_spec.rb +0 -312
  231. data/spec/errors_stale_spec.rb +0 -83
  232. data/spec/file_based_mcp_tools_spec.rb +0 -99
  233. data/spec/help_tool_spec.rb +0 -26
  234. data/spec/integration_spec.rb +0 -789
  235. data/spec/logging_fallback_spec.rb +0 -128
  236. data/spec/mcp_logging_spec.rb +0 -44
  237. data/spec/mcp_server_integration_spec.rb +0 -23
  238. data/spec/mcp_server_spec.rb +0 -106
  239. data/spec/mode_detector_spec.rb +0 -153
  240. data/spec/model_error_handling_spec.rb +0 -269
  241. data/spec/model_staleness_spec.rb +0 -79
  242. data/spec/option_normalizers_spec.rb +0 -203
  243. data/spec/option_parsers/env_options_parser_spec.rb +0 -221
  244. data/spec/option_parsers/error_helper_spec.rb +0 -222
  245. data/spec/path_relativizer_spec.rb +0 -98
  246. data/spec/presenters/coverage_detailed_presenter_spec.rb +0 -19
  247. data/spec/presenters/coverage_raw_presenter_spec.rb +0 -15
  248. data/spec/presenters/coverage_summary_presenter_spec.rb +0 -15
  249. data/spec/presenters/coverage_uncovered_presenter_spec.rb +0 -16
  250. data/spec/presenters/project_coverage_presenter_spec.rb +0 -87
  251. data/spec/presenters/project_totals_presenter_spec.rb +0 -144
  252. data/spec/resolvers/coverage_line_resolver_spec.rb +0 -282
  253. data/spec/resolvers/resolver_factory_spec.rb +0 -61
  254. data/spec/resolvers/resultset_path_resolver_spec.rb +0 -60
  255. data/spec/resultset_loader_spec.rb +0 -167
  256. data/spec/shared_examples/README.md +0 -115
  257. data/spec/shared_examples/coverage_presenter_examples.rb +0 -66
  258. data/spec/shared_examples/file_based_mcp_tools.rb +0 -179
  259. data/spec/shared_examples/formatted_command_examples.rb +0 -64
  260. data/spec/shared_examples/mcp_tool_text_json_response.rb +0 -16
  261. data/spec/spec_helper.rb +0 -127
  262. data/spec/staleness_checker_spec.rb +0 -374
  263. data/spec/staleness_more_spec.rb +0 -42
  264. data/spec/support/cli_helpers.rb +0 -22
  265. data/spec/support/control_flow_helpers.rb +0 -20
  266. data/spec/support/fake_mcp.rb +0 -40
  267. data/spec/support/io_helpers.rb +0 -29
  268. data/spec/support/mcp_helpers.rb +0 -35
  269. data/spec/support/mcp_runner.rb +0 -66
  270. data/spec/support/mocking_helpers.rb +0 -30
  271. data/spec/table_format_spec.rb +0 -70
  272. data/spec/tools/validate_tool_spec.rb +0 -132
  273. data/spec/tools_error_handling_spec.rb +0 -130
  274. data/spec/util_spec.rb +0 -154
  275. data/spec/version_spec.rb +0 -123
  276. data/spec/version_tool_spec.rb +0 -141
  277. /data/{spec/fixtures/project1 → examples/fixtures/demo_project}/lib/bar.rb +0 -0
  278. /data/{spec/fixtures/project1 → examples/fixtures/demo_project}/lib/foo.rb +0 -0
  279. /data/lib/cov_loupe/{config_parser.rb → config/config_parser.rb} +0 -0
  280. /data/lib/cov_loupe/{predicate_evaluator.rb → config/predicate_evaluator.rb} +0 -0
  281. /data/lib/cov_loupe/{error_handler_factory.rb → errors/error_handler_factory.rb} +0 -0
@@ -0,0 +1,90 @@
1
+ # Path Resolution Strategy
2
+
3
+ [Back to ADR Index](README.md)
4
+
5
+ ## Cross-OS Coverage Data Support
6
+
7
+ ### Status
8
+ **Accepted** (2025-12-26)
9
+
10
+ ### Context
11
+
12
+ SimpleCov's `.resultset.json` files contain file paths as keys in the coverage data hash. These paths are typically absolute paths from the machine where tests were run. Different operating systems use different path separators:
13
+ - Unix/Linux/macOS: `/`
14
+ - Windows: `\` (backslash)
15
+
16
+ Early versions of cov-loupe included path normalization logic that converted backslashes to forward slashes, enabling cross-platform path matching. This was motivated by a theoretical use case: analyzing a `.resultset.json` file generated on one OS (e.g., Windows) on a different OS (e.g., Linux/macOS).
17
+
18
+ Upon closer examination, this cross-OS scenario is unrealistic for several reasons:
19
+
20
+ 1. **CI/CD workflows** – While coverage files might be generated in CI (often Linux), developers typically either:
21
+ - Re-run tests locally to generate fresh coverage
22
+ - View coverage reports rendered by SimpleCov's HTML formatter
23
+ - Use hosted coverage services (Codecov, Coveralls)
24
+
25
+ 2. **Docker development** – When tests run in containers, volume mounting ensures paths already match the host environment
26
+
27
+ 3. **Path structure differences** – Cross-OS paths differ in more ways than just separators (drive letters, root paths, etc.), making simple separator normalization insufficient
28
+
29
+ 4. **Same-machine path variations** – The legitimate use case is handling different working directories or relative vs absolute paths on the *same* machine, not across different OSes
30
+
31
+ ### Decision
32
+
33
+ **Do not support analyzing `.resultset.json` files across different operating systems.**
34
+
35
+ Specifically:
36
+ - Normalize backslashes to forward slashes on Windows only
37
+ - Apply case-insensitive path matching on Windows (filesystem is case-insensitive)
38
+ - Apply case-sensitive path matching on Unix (filesystem is case-sensitive)
39
+ - Keep path resolution focused on same-OS scenarios:
40
+ - Exact absolute path matching
41
+ - Relative path matching (stripping project root)
42
+ - Trust that paths in coverage data use the native separator for the OS where cov-loupe runs
43
+
44
+ ### Consequences
45
+
46
+ **Benefits:**
47
+ - **Simpler code** – No need for path separator normalization logic
48
+ - **Clearer semantics** – Path matching behavior is more predictable
49
+ - **Fewer edge cases** – No ambiguity around mixed separator styles
50
+ - **Honest API** – We don't promise cross-OS compatibility we can't fully deliver
51
+
52
+ **Trade-offs:**
53
+ - Users cannot analyze Windows `.resultset.json` files on Linux/macOS or vice versa
54
+ - This is acceptable because this scenario is impractical anyway
55
+ - Users who encounter this should re-run tests in their current environment
56
+
57
+ **No impact on:**
58
+ - Same-OS path resolution (absolute, relative)
59
+ - Normal development workflows
60
+ - CI/CD integration
61
+ - Docker/container-based development
62
+
63
+ ### Implementation
64
+
65
+ Path normalization is centralized in the `PathUtils` module (`lib/cov_loupe/path_utils.rb`). It handles:
66
+ - Normalizing path separators (backslashes to forward slashes on Windows)
67
+ - Case normalization for case-insensitive volumes (autodetected or explicit)
68
+ - Path cleaning
69
+
70
+ `CoverageLineResolver` delegates all path normalization to `PathUtils.normalize`, avoiding scattered platform checks and keeping the resolver logic focused on lookup strategies.
71
+
72
+ #### Path Comparison Strategy
73
+
74
+ Path comparison uses a **normalize-for-comparison-only** approach rather than normalize-and-store:
75
+
76
+ - **Original paths are preserved** - `PathUtils.expand` preserves the original case to avoid corrupting displayed file paths
77
+ - **Normalization happens at comparison time** - The `normalized_start_with?` helper method normalizes both paths internally for comparison but doesn't modify the originals
78
+ - **Boundary checking** - Prevents false matches where a path starts with a prefix string but isn't actually within that directory (e.g., `/home/user/project` should not match `/home/user/project-backup/file.rb`)
79
+
80
+ This approach ensures:
81
+ 1. User-visible paths maintain original casing for better UX
82
+ 2. Path matching works correctly on case-insensitive volumes (Windows, most macOS)
83
+ 3. Mixed path separators (forward slash vs backslash) are handled transparently
84
+ 4. Directory boundary checking prevents incorrect prefix matches
85
+
86
+ ### References
87
+
88
+ - Implementation: `lib/cov_loupe/resolvers/coverage_line_resolver.rb` (delegates to `PathUtils`)
89
+ - Central Logic: `lib/cov_loupe/path_utils.rb`
90
+ - Related tests removed: `spec/resolvers/coverage_line_resolver_spec.rb` (cross-OS separator normalization context)
@@ -1,14 +1,18 @@
1
- # ADR 004: Ruby `instance_eval` for Success Predicates
1
+ # Policy Validation
2
2
 
3
- [Back to main README](../../README.md)
3
+ [Back to main README](../../index.md)
4
4
 
5
- ## Status
5
+ This document describes how cov-loupe allows users to define and enforce custom coverage policies through success predicates.
6
+
7
+ ## Ruby `instance_eval` for Success Predicates
8
+
9
+ ### Status
6
10
 
7
11
  Accepted
8
12
 
9
- ## Context
13
+ ### Context
10
14
 
11
- SimpleCov MCP needed a mechanism for users to define custom coverage policies beyond simple percentage thresholds. Different projects have different requirements:
15
+ cov-loupe needed a mechanism for users to define custom coverage policies beyond simple percentage thresholds. Different projects have different requirements:
12
16
 
13
17
  - Some want all files above 80%, others allow a few files below threshold
14
18
  - Some need different thresholds for different directories (e.g., 90% for API code, 60% for legacy)
@@ -22,7 +26,7 @@ We considered several approaches:
22
26
  3. **Ruby file evaluation**: Load and execute arbitrary Ruby code that returns a callable predicate
23
27
  4. **Sandboxed DSL**: Use a restricted Ruby environment (e.g., `$SAFE` levels, isolated VMs)
24
28
 
25
- ### Key Requirements
29
+ #### Key Requirements
26
30
 
27
31
  - Flexibility: Support arbitrarily complex coverage policies
28
32
  - Simplicity: Easy for users to write and understand
@@ -30,7 +34,7 @@ We considered several approaches:
30
34
  - CI/CD integration: Clear exit codes (0 = pass, 1 = fail, 2 = error)
31
35
  - Access to coverage data: Predicates need access to the full `CoverageModel` API
32
36
 
33
- ### Why Not a Custom DSL?
37
+ #### Why Not a Custom DSL?
34
38
 
35
39
  A custom DSL would be:
36
40
  - Limited in expressiveness (hard to predict all future use cases)
@@ -38,7 +42,7 @@ A custom DSL would be:
38
42
  - More maintenance burden (parsing, validation, documentation)
39
43
  - Still vulnerable to injection if it allowed any dynamic computation
40
44
 
41
- ### Why Not Sandboxing?
45
+ #### Why Not Sandboxing?
42
46
 
43
47
  Ruby's sandboxing options are limited:
44
48
  - `$SAFE` levels were deprecated and removed in Ruby 2.7+
@@ -46,13 +50,13 @@ Ruby's sandboxing options are limited:
46
50
  - Any Turing-complete sandbox can be escaped given enough effort
47
51
  - True security requires not executing untrusted code at all
48
52
 
49
- ## Decision
53
+ ### Decision
50
54
 
51
55
  We chose to **evaluate Ruby files using `instance_eval`** with prominent security warnings rather than attempting to create a false sense of security through incomplete sandboxing.
52
56
 
53
- ### Implementation
57
+ #### Implementation
54
58
 
55
- The implementation is in `lib/cov_loupe/cli.rb:191-214`:
59
+ The implementation is in `lib/cov_loupe/cli.rb` (`CoverageCLI#load_success_predicate`):
56
60
 
57
61
  ```ruby
58
62
  def load_success_predicate(path)
@@ -97,11 +101,11 @@ rescue => e
97
101
  end
98
102
  ```
99
103
 
100
- ### Security Model: Treat as Executable Code
104
+ #### Security Model: Treat as Executable Code
101
105
 
102
106
  Rather than pretending to sandbox untrusted code, we treat success predicates **exactly like any other Ruby code in the project**:
103
107
 
104
- 1. **Prominent warnings** in documentation (examples/success_predicates/README.md:5-17):
108
+ 1. **Prominent warnings** in documentation (highlighted near the top of `examples/success_predicates/README.md`):
105
109
  ```
106
110
  ⚠️ SECURITY WARNING
107
111
 
@@ -116,14 +120,14 @@ Rather than pretending to sandbox untrusted code, we treat success predicates **
116
120
  3. **CI/CD best practices**: Same permissions model as running tests themselves
117
121
  4. **Example predicates**: Well-documented examples showing safe patterns
118
122
 
119
- ### Predicate API
123
+ #### Predicate API
120
124
 
121
125
  Success predicates must be callable (lambda, proc, or object with `#call` method):
122
126
 
123
127
  **Lambda example:**
124
128
  ```ruby
125
129
  ->(model) do
126
- model.all_files.all? { |f| f['percentage'] >= 80 }
130
+ model.list.all? { |f| f['percentage'] >= 80 }
127
131
  end
128
132
  ```
129
133
 
@@ -133,7 +137,7 @@ end
133
137
 
134
138
  class CoveragePolicy
135
139
  def call(model)
136
- api_files = model.all_files.select { |f| f['file'].start_with?('lib/api/') }
140
+ api_files = model.list['files'].select { |f| f['file'].start_with?('lib/api/') }
137
141
  api_files.all? { |f| f['percentage'] >= 90 }
138
142
  end
139
143
  end
@@ -142,14 +146,14 @@ AllFilesAboveThreshold.new
142
146
  ```
143
147
 
144
148
  The predicate receives a full `CoverageModel` instance with access to:
145
- - `all_files(tracked_globs:, sort_order:)` - All file coverage data
149
+ - `list(tracked_globs:, sort_order:)` - All file coverage data
146
150
  - `summary_for(path)` - Coverage summary for a specific file
147
151
  - `uncovered_for(path)` - Uncovered lines for a specific file
148
152
  - `detailed_for(path)` - Per-line coverage data
149
153
 
150
- ## Consequences
154
+ ### Consequences
151
155
 
152
- ### Positive
156
+ #### Positive
153
157
 
154
158
  1. **Maximum flexibility**: Users can express arbitrarily complex coverage policies using full Ruby
155
159
  2. **Familiar tooling**: Users can debug predicates with standard Ruby tools (pry, byebug, etc.)
@@ -158,20 +162,20 @@ The predicate receives a full `CoverageModel` instance with access to:
158
162
  5. **Composability**: Users can require other libraries, define helper methods, etc.
159
163
  6. **Excellent examples**: We provide 5+ well-documented example predicates
160
164
 
161
- ### Negative
165
+ #### Negative
162
166
 
163
167
  1. **Security responsibility**: Users must understand the security implications
164
168
  2. **Potential misuse**: Users might mistakenly trust untrusted predicate files
165
169
  3. **No isolation**: Buggy predicates can access/modify anything in the system
166
170
  4. **Documentation burden**: Must clearly communicate security model
167
171
 
168
- ### Trade-offs
172
+ #### Trade-offs
169
173
 
170
174
  - **Versus custom DSL**: More powerful and debuggable, but requires user awareness of security
171
175
  - **Versus plugin architecture**: Simpler (no gem dependencies, no protocol to learn), but same security profile
172
176
  - **Versus incomplete sandboxing**: Honest about capabilities rather than security theater
173
177
 
174
- ### Threat Model
178
+ #### Threat Model
175
179
 
176
180
  This approach is **appropriate** when:
177
181
  - Predicate files are stored in version control with code review
@@ -183,7 +187,7 @@ This approach is **inappropriate** when:
183
187
  - Allowing users to upload predicates via web interface
184
188
  - Running in a multi-tenant environment without isolation
185
189
 
186
- ### Future Considerations
190
+ #### Future Considerations
187
191
 
188
192
  If demand arises for truly untrusted predicate execution, alternatives include:
189
193
 
@@ -193,11 +197,11 @@ If demand arises for truly untrusted predicate execution, alternatives include:
193
197
 
194
198
  However, for the primary use case (CI/CD policy enforcement), the current approach is simpler and more flexible than these alternatives.
195
199
 
196
- ## References
200
+ ### References
197
201
 
198
- - Implementation: `lib/cov_loupe/cli.rb:191-214` (load predicate), `lib/cov_loupe/cli.rb:179-189` (execute)
199
- - Security warnings: `examples/success_predicates/README.md:5-17`
202
+ - Implementation: `lib/cov_loupe/cli.rb` (`CoverageCLI#load_success_predicate` and `#run_success_predicate`)
203
+ - Security warnings: `examples/success_predicates/README.md`
200
204
  - Example predicates: `examples/success_predicates/*.rb`
201
205
  - CoverageModel API: `lib/cov_loupe/model.rb`
202
- - CLI config: `lib/cov_loupe/cli_config.rb:18` (success_predicate field)
203
- - Option parsing: `lib/cov_loupe/option_parser_builder.rb` (--success-predicate flag)
206
+ - CLI config: `lib/cov_loupe/cli_config.rb` (`success_predicate` field)
207
+ - Option parsing: `lib/cov_loupe/option_parser_builder.rb` (`--success-predicate` flag)
@@ -1,22 +1,26 @@
1
- # ADR 005: No SimpleCov Runtime Dependency
1
+ # SimpleCov Integration
2
2
 
3
- [Back to main README](../../README.md)
3
+ [Back to main README](../../index.md)
4
4
 
5
- ## Status
5
+ This document describes how cov-loupe integrates with SimpleCov and manages its dependency on the SimpleCov gem.
6
6
 
7
- Replaced – cov-loupe now requires SimpleCov at runtime so that multi-suite resultsets can be merged using SimpleCov’s combine helpers.
7
+ ## SimpleCov Runtime Dependency
8
8
 
9
- ## Context
9
+ ### Status
10
10
 
11
- SimpleCov MCP provides tooling for inspecting SimpleCov coverage reports. When designing the gem, we had to decide whether to depend on SimpleCov as a runtime dependency.
11
+ **Replaced** cov-loupe now requires SimpleCov at runtime so that multi-suite resultsets can be merged using SimpleCov's combine helpers.
12
12
 
13
- ### Alternative Approaches
13
+ ### Original Context
14
+
15
+ cov-loupe provides tooling for inspecting SimpleCov coverage reports. When designing the gem, we had to decide whether to depend on SimpleCov as a runtime dependency.
16
+
17
+ #### Alternative Approaches
14
18
 
15
19
  1. **Runtime dependency on SimpleCov**: Use SimpleCov's API to read and process coverage data
16
20
  2. **Development-only dependency**: Read SimpleCov's `.resultset.json` files directly without requiring SimpleCov at runtime
17
21
  3. **Support multiple coverage formats**: Parse coverage data from multiple tools (SimpleCov, Coverage, etc.)
18
22
 
19
- ### Key Considerations
23
+ #### Key Considerations
20
24
 
21
25
  **Dependency weight**: SimpleCov itself has dependencies:
22
26
  - `docile` (~> 1.1)
@@ -25,7 +29,7 @@ SimpleCov MCP provides tooling for inspecting SimpleCov coverage reports. When d
25
29
 
26
30
  **Use case separation**:
27
31
  - SimpleCov is needed when **running tests** to collect coverage
28
- - SimpleCov MCP is needed when **inspecting coverage** after tests complete
32
+ - cov-loupe is needed when **inspecting coverage** after tests complete
29
33
  - These are temporally separated activities
30
34
 
31
35
  **Deployment contexts**:
@@ -38,33 +42,32 @@ SimpleCov MCP provides tooling for inspecting SimpleCov coverage reports. When d
38
42
  - The format is simple JSON with predictable structure
39
43
  - Breaking changes would affect all SimpleCov users, so the format is unlikely to change
40
44
 
41
- ## Decision
45
+ ### Original Decision
42
46
 
43
- We chose to **make SimpleCov a development dependency only** and read `.resultset.json` files directly using Ruby's standard library JSON parser.
47
+ We initially chose to **make SimpleCov a development dependency only** and read `.resultset.json` files directly using Ruby's standard library JSON parser.
44
48
 
45
- ### Implementation
49
+ ### Revision: SimpleCov as Runtime Dependency
46
50
 
47
- The gem only depends on `mcp` at runtime (cov-loupe.gemspec:26):
48
- ```ruby
49
- # Runtime deps (stdlib: json, time, pathname)
50
- spec.add_runtime_dependency 'mcp', '~> 0.3'
51
- spec.add_development_dependency 'simplecov', '~> 0.21'
52
- ```
51
+ cov-loupe now depends on SimpleCov at runtime for the following reasons:
52
+
53
+ 1. **Multi-suite merging**: Projects using multiple test suites (e.g., RSpec + Minitest) produce separate coverage results that must be merged using SimpleCov's `SimpleCov::ResultMerger.merge_results`
54
+ 2. **Consistent calculations**: SimpleCov's coverage percentage algorithms handle edge cases that are difficult to replicate correctly
55
+ 3. **Format compatibility**: Changes to SimpleCov's internal data structures are automatically handled by using its API
56
+
57
+ ### Current Implementation
58
+
59
+ cov-loupe currently depends on `amazing_print`, `mcp`, and `simplecov` at runtime.
53
60
 
54
- Coverage data is read directly from JSON files (lib/cov_loupe/model.rb:34-42):
61
+ Coverage data is read directly from JSON files by `CovLoupe::CoverageModel#load_coverage_data`:
55
62
  ```ruby
56
- rs = CovUtil.find_resultset(@root, resultset: resultset)
57
- raw = JSON.parse(File.read(rs))
58
- # SimpleCov typically writes a single test suite entry to .resultset.json
59
- # Find the first entry that has coverage data (skip comment entries)
60
- _suite, data = raw.find { |k, v| v.is_a?(Hash) && v.key?('coverage') }
61
- raise "No test suite with coverage data found in resultset file: #{rs}" unless data
62
- cov = data['coverage'] or raise "No 'coverage' key found in resultset file: #{rs}"
63
- @cov = cov.transform_keys { |k| File.absolute_path(k, @root) }
64
- @cov_timestamp = (data['timestamp'] || data['created_at'] || 0).to_i
63
+ rs = Resolvers::ResolverHelpers.find_resultset(@root, resultset: resultset)
64
+ loaded = ResultsetLoader.load(resultset_path: rs)
65
+ coverage_map = loaded.coverage_map or raise(CoverageDataError, "No 'coverage' key found in resultset file: #{rs}")
66
+ @cov = coverage_map.transform_keys { |k| File.absolute_path(k, @root) }
67
+ @cov_timestamp = loaded.timestamp
65
68
  ```
66
69
 
67
- Coverage calculations use simple algorithms (lib/cov_loupe/util.rb:42-71):
70
+ Coverage calculations use simple algorithms in `CovLoupe::CoverageCalculator` (`summary`, `uncovered`, `detailed`):
68
71
  ```ruby
69
72
  def summary(arr)
70
73
  total = 0
@@ -121,9 +124,9 @@ Where:
121
124
 
122
125
  ### Resultset Discovery
123
126
 
124
- We implement flexible discovery of `.resultset.json` files (lib/cov_loupe/util.rb:6-10):
127
+ We implement flexible discovery of `.resultset.json` files via `Resolvers::ResultsetPathResolver::DEFAULT_CANDIDATES`:
125
128
  ```ruby
126
- RESULTSET_CANDIDATES = [
129
+ DEFAULT_CANDIDATES = [
127
130
  '.resultset.json',
128
131
  'coverage/.resultset.json',
129
132
  'tmp/.resultset.json'
@@ -132,9 +135,9 @@ RESULTSET_CANDIDATES = [
132
135
 
133
136
  This supports common SimpleCov configurations without requiring SimpleCov to be loaded.
134
137
 
135
- ## Consequences
138
+ ### Consequences
136
139
 
137
- ### Positive
140
+ #### Positive (Original Development-Only Approach)
138
141
 
139
142
  1. **Lightweight installation**: No transitive dependencies beyond `mcp` gem
140
143
  2. **Deployment flexibility**: Can analyze coverage in environments without test dependencies
@@ -143,7 +146,7 @@ This supports common SimpleCov configurations without requiring SimpleCov to be
143
146
  5. **CI/CD optimization**: Analysis jobs don't need full test suite dependencies
144
147
  6. **Production-safe**: Can be deployed to production environments if needed (e.g., for monitoring)
145
148
 
146
- ### Negative
149
+ #### Negative (Original Development-Only Approach)
147
150
 
148
151
  1. **Format dependency**: Tightly coupled to SimpleCov's JSON format
149
152
  2. **Breaking changes risk**: If SimpleCov changes `.resultset.json` structure, we must adapt
@@ -151,18 +154,18 @@ This supports common SimpleCov configurations without requiring SimpleCov to be
151
154
  4. **Duplicate logic**: Coverage percentage calculations reimplemented (though simple)
152
155
  5. **Maintenance**: Must track SimpleCov format changes manually
153
156
 
154
- ### Trade-offs
157
+ #### Trade-offs (Current Runtime Dependency Approach)
155
158
 
156
- - **Versus runtime dependency**: Lighter weight but less resilient to format changes
159
+ - **Versus development-only dependency**: Heavier installation footprint, but better multi-suite support and format compatibility
157
160
  - **Versus multi-format support**: Simpler implementation but locked to SimpleCov ecosystem
158
- - **Versus using SimpleCov API**: More flexible deployment but requires understanding the file format
161
+ - **Versus custom merging logic**: More reliable but requires SimpleCov at runtime
159
162
 
160
163
  ### Risk Mitigation
161
164
 
162
165
  1. **Format stability**: SimpleCov has maintained `.resultset.json` compatibility for years
163
166
  2. **Simple format**: JSON structure is straightforward and unlikely to change dramatically
164
167
  3. **Development dependency**: We still use SimpleCov in our own tests, so format changes would be detected immediately
165
- 4. **Documentation**: CLAUDE.md documents the format dependency explicitly
168
+ 4. **Documentation**: AGENTS.md documents the format dependency explicitly
166
169
  5. **Error handling**: Robust error messages when format doesn't match expectations
167
170
 
168
171
  ### Format Evolution Strategy
@@ -176,14 +179,14 @@ If SimpleCov's format changes:
176
179
 
177
180
  - Only supports SimpleCov (not Coverage gem, other tools)
178
181
  - Assumes standard `.resultset.json` locations
179
- - No support for merged coverage from multiple test runs (SimpleCov handles this before writing JSON)
182
+ - Multi-suite merging requires SimpleCov runtime dependency
180
183
  - No support for branch coverage (SimpleCov feature not widely used yet)
181
184
 
182
- ## References
185
+ ### References
183
186
 
184
- - Gemspec dependencies: `cov-loupe.gemspec:26-28`
185
- - JSON parsing: `lib/cov_loupe/model.rb:34-57`
186
- - Coverage calculations: `lib/cov_loupe/util.rb:42-71`
187
- - Resultset discovery: `lib/cov_loupe/util.rb:6-10`, `lib/cov_loupe/util.rb:34-36`
187
+ - Gemspec dependencies: `cov-loupe.gemspec` (`spec.add_dependency` entries)
188
+ - JSON parsing: `lib/cov_loupe/resultset_loader.rb` (`ResultsetLoader.load`)
189
+ - Coverage calculations: `lib/cov_loupe/coverage_calculator.rb` (`CoverageCalculator.summary`, `.uncovered`, `.detailed`)
190
+ - Resultset discovery: `lib/cov_loupe/resolvers/resultset_path_resolver.rb` (`ResultsetPathResolver::DEFAULT_CANDIDATES`)
188
191
  - SimpleCov format documentation: https://github.com/simplecov-ruby/simplecov
189
192
  - Development usage: Uses SimpleCov in `spec/spec_helper.rb` to test itself
@@ -1,4 +1,4 @@
1
- [Back to main README](../../README.md)
1
+ [Back to main README](../../index.md)
2
2
 
3
3
  ---
4
4
  marp: true
@@ -9,7 +9,7 @@ backgroundColor: #fff
9
9
  color: #333
10
10
  ---
11
11
 
12
- # SimpleCovMCP
12
+ # CovLoupe
13
13
  ### MCP Server, CLI, and Library for SimpleCov Ruby Test Coverage
14
14
 
15
15
  - Keith Bennett
@@ -17,7 +17,7 @@ color: #333
17
17
 
18
18
  ---
19
19
 
20
- ## What is SimpleCov MCP?
20
+ ## What is cov-loupe?
21
21
 
22
22
  A **three-in-one** gem that makes SimpleCov coverage data accessible to:
23
23
 
@@ -59,7 +59,7 @@ This code base requires a Ruby version >= 3.2.0, because this is required by the
59
59
 
60
60
  ```bash
61
61
  # Test the MCP server manually
62
- echo '{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"coverage_summary_tool","arguments":{"path":"lib/cov_loupe/model.rb"}}}' | cov-loupe
62
+ echo '{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"coverage_summary_tool","arguments":{"path":"lib/cov_loupe/model.rb"}}}' | cov-loupe -m mcp
63
63
  ```
64
64
 
65
65
  **What AI agents can do:**
@@ -73,12 +73,13 @@ echo '{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"coverage_s
73
73
 
74
74
  | Tool | Purpose | Example CLI Command |
75
75
  |---------------------------|----------------------------|------------------------------------------------------|
76
- | `all_files_coverage_tool` | Project-wide coverage data | `cov-loupe list` |
76
+ | `list_tool` | Project-wide coverage data | `cov-loupe list` |
77
77
  | `coverage_detailed_tool` | Per-line hit counts | `cov-loupe detailed lib/cov_loupe/model.rb` |
78
78
  | `coverage_raw_tool` | Raw SimpleCov lines array | `cov-loupe raw lib/cov_loupe/model.rb` |
79
79
  | `coverage_summary_tool` | Get coverage % for a file | `cov-loupe summary lib/cov_loupe/model.rb` |
80
80
  | `coverage_table_tool` | Formatted coverage table | `cov-loupe list` |
81
81
  | `coverage_totals_tool` | Aggregated line totals | `cov-loupe totals` |
82
+ | `validate_tool` | Validate coverage policy | `cov-loupe validate coverage_policy.rb` |
82
83
  | `help_tool` | Tool usage guidance | `cov-loupe --help` |
83
84
  | `uncovered_lines_tool` | Find missing test coverage | `cov-loupe uncovered lib/cov_loupe/cli.rb` |
84
85
  | `version_tool` | Display version info | `cov-loupe version` |
@@ -89,14 +90,14 @@ echo '{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"coverage_s
89
90
  ###
90
91
 
91
92
  ```bash
92
- # Show all files, worst coverage first
93
+ # Show all files, best coverage first
93
94
  cov-loupe
94
95
 
95
96
  # Focus on a specific file
96
97
  cov-loupe summary lib/cov_loupe/cli.rb
97
98
 
98
99
  # Find untested lines with source context
99
- cov-loupe uncovered lib/cov_loupe/cli.rb --source=uncovered --source-context 3
100
+ cov-loupe uncovered lib/cov_loupe/cli.rb --source=uncovered --context-lines 3
100
101
 
101
102
  # JSON for scripts
102
103
  cov-loupe -fJ | jq '.files[] | select(.percentage < 80)'
@@ -124,7 +125,7 @@ cov-loupe --resultset coverage-all/
124
125
  cov-loupe --sort-order d
125
126
 
126
127
  # Staleness checking (file newer than coverage?)
127
- cov-loupe --staleness error
128
+ cov-loupe --raise-on-stale true
128
129
 
129
130
  # Track new files missing from coverage
130
131
  cov-loupe --tracked-globs "lib/**/tools/*.rb"
@@ -141,7 +142,7 @@ require 'cov_loupe'
141
142
  model = CovLoupe::CoverageModel.new
142
143
 
143
144
  # Get project overview
144
- files = model.all_files
145
+ files = model.list
145
146
  puts "Lowest coverage: #{files.first['percentage']}%"
146
147
 
147
148
  # Focus on specific concerns
@@ -162,7 +163,7 @@ puts "Missing lines: #{uncovered['uncovered'].inspect}"
162
163
  ```ruby
163
164
  require 'cov_loupe'
164
165
 
165
- files = CovLoupe::CoverageModel.new.all_files
166
+ files = CovLoupe::CoverageModel.new.list
166
167
  critical, other = files.partition { |f| f['file'].include?('/lib/critical/') }
167
168
 
168
169
  fails = critical.select { |f| f['percentage'] < 100.0 } +
@@ -192,18 +193,19 @@ lib/cov_loupe
192
193
  ├── model.rb
193
194
  ├── path_relativizer.rb
194
195
  ├── staleness_checker.rb
196
+ ├── version.rb
195
197
  ├── tools
196
- │ ├── all_files_coverage_tool.rb
198
+ │ ├── list_tool.rb
197
199
  │ ├── coverage_detailed_tool.rb
198
200
  │ ├── coverage_raw_tool.rb
199
201
  │ ├── coverage_summary_tool.rb
200
202
  │ ├── coverage_table_tool.rb
201
203
  │ ├── coverage_totals_tool.rb
204
+ │ ├── validate_tool.rb
202
205
  │ ├── help_tool.rb
203
206
  │ ├── uncovered_lines_tool.rb
204
207
  │ └── version_tool.rb
205
- ├── util.rb
206
- └── version.rb
208
+ └── util.rb
207
209
  ```
208
210
 
209
211
  **Clean separation:** CLI ↔ Model ↔ MCP Tools
@@ -0,0 +1,3 @@
1
+ # MCP Input Examples
2
+
3
+ {% include-markdown "../../examples/mcp-inputs/README.md" %}
@@ -0,0 +1,3 @@
1
+ # Prompt Examples
2
+
3
+ {% include-markdown "../../examples/prompts/README.md" %}
@@ -0,0 +1,3 @@
1
+ # Success Predicate Examples
2
+
3
+ {% include-markdown "../../examples/success_predicates/README.md" %}