simplecov-mcp 0.3.0 → 1.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 (152) hide show
  1. checksums.yaml +4 -4
  2. data/LICENSE +21 -0
  3. data/README.md +173 -356
  4. data/docs/ADVANCED_USAGE.md +967 -0
  5. data/docs/ARCHITECTURE.md +79 -0
  6. data/docs/BRANCH_ONLY_COVERAGE.md +81 -0
  7. data/docs/CLI_USAGE.md +637 -0
  8. data/docs/DEVELOPMENT.md +82 -0
  9. data/docs/ERROR_HANDLING.md +93 -0
  10. data/docs/EXAMPLES.md +430 -0
  11. data/docs/INSTALLATION.md +352 -0
  12. data/docs/LIBRARY_API.md +635 -0
  13. data/docs/MCP_INTEGRATION.md +488 -0
  14. data/docs/TROUBLESHOOTING.md +276 -0
  15. data/docs/arch-decisions/001-x-arch-decision.md +93 -0
  16. data/docs/arch-decisions/002-x-arch-decision.md +157 -0
  17. data/docs/arch-decisions/003-x-arch-decision.md +163 -0
  18. data/docs/arch-decisions/004-x-arch-decision.md +199 -0
  19. data/docs/arch-decisions/005-x-arch-decision.md +187 -0
  20. data/docs/arch-decisions/README.md +60 -0
  21. data/docs/presentations/simplecov-mcp-presentation.md +249 -0
  22. data/exe/simplecov-mcp +4 -4
  23. data/lib/simplecov_mcp/app_context.rb +26 -0
  24. data/lib/simplecov_mcp/base_tool.rb +74 -0
  25. data/lib/simplecov_mcp/cli.rb +234 -0
  26. data/lib/simplecov_mcp/cli_config.rb +56 -0
  27. data/lib/simplecov_mcp/commands/base_command.rb +78 -0
  28. data/lib/simplecov_mcp/commands/command_factory.rb +39 -0
  29. data/lib/simplecov_mcp/commands/detailed_command.rb +24 -0
  30. data/lib/simplecov_mcp/commands/list_command.rb +13 -0
  31. data/lib/simplecov_mcp/commands/raw_command.rb +22 -0
  32. data/lib/simplecov_mcp/commands/summary_command.rb +24 -0
  33. data/lib/simplecov_mcp/commands/uncovered_command.rb +26 -0
  34. data/lib/simplecov_mcp/commands/version_command.rb +18 -0
  35. data/lib/simplecov_mcp/constants.rb +22 -0
  36. data/lib/simplecov_mcp/error_handler.rb +124 -0
  37. data/lib/simplecov_mcp/error_handler_factory.rb +31 -0
  38. data/lib/simplecov_mcp/errors.rb +179 -0
  39. data/lib/simplecov_mcp/formatters/source_formatter.rb +148 -0
  40. data/lib/simplecov_mcp/mcp_server.rb +40 -0
  41. data/lib/simplecov_mcp/mode_detector.rb +55 -0
  42. data/lib/simplecov_mcp/model.rb +300 -0
  43. data/lib/simplecov_mcp/option_normalizers.rb +92 -0
  44. data/lib/simplecov_mcp/option_parser_builder.rb +134 -0
  45. data/lib/simplecov_mcp/option_parsers/env_options_parser.rb +50 -0
  46. data/lib/simplecov_mcp/option_parsers/error_helper.rb +109 -0
  47. data/lib/simplecov_mcp/path_relativizer.rb +61 -0
  48. data/lib/simplecov_mcp/presenters/base_coverage_presenter.rb +44 -0
  49. data/lib/simplecov_mcp/presenters/coverage_detailed_presenter.rb +16 -0
  50. data/lib/simplecov_mcp/presenters/coverage_raw_presenter.rb +16 -0
  51. data/lib/simplecov_mcp/presenters/coverage_summary_presenter.rb +16 -0
  52. data/lib/simplecov_mcp/presenters/coverage_uncovered_presenter.rb +16 -0
  53. data/lib/simplecov_mcp/presenters/project_coverage_presenter.rb +52 -0
  54. data/lib/simplecov_mcp/resolvers/coverage_line_resolver.rb +126 -0
  55. data/lib/simplecov_mcp/resolvers/resolver_factory.rb +28 -0
  56. data/lib/simplecov_mcp/resolvers/resultset_path_resolver.rb +78 -0
  57. data/lib/simplecov_mcp/resultset_loader.rb +136 -0
  58. data/lib/simplecov_mcp/staleness_checker.rb +243 -0
  59. data/lib/{simple_cov_mcp → simplecov_mcp}/tools/all_files_coverage_tool.rb +31 -13
  60. data/lib/{simple_cov_mcp → simplecov_mcp}/tools/coverage_detailed_tool.rb +7 -7
  61. data/lib/{simple_cov_mcp → simplecov_mcp}/tools/coverage_raw_tool.rb +7 -7
  62. data/lib/{simple_cov_mcp → simplecov_mcp}/tools/coverage_summary_tool.rb +7 -7
  63. data/lib/simplecov_mcp/tools/coverage_table_tool.rb +90 -0
  64. data/lib/{simple_cov_mcp → simplecov_mcp}/tools/help_tool.rb +13 -4
  65. data/lib/{simple_cov_mcp → simplecov_mcp}/tools/uncovered_lines_tool.rb +7 -7
  66. data/lib/{simple_cov_mcp → simplecov_mcp}/tools/version_tool.rb +11 -3
  67. data/lib/simplecov_mcp/util.rb +82 -0
  68. data/lib/{simple_cov_mcp → simplecov_mcp}/version.rb +1 -1
  69. data/lib/simplecov_mcp.rb +144 -2
  70. data/spec/MCP_INTEGRATION_TESTS_README.md +111 -0
  71. data/spec/TIMESTAMPS.md +48 -0
  72. data/spec/all_files_coverage_tool_spec.rb +29 -25
  73. data/spec/base_tool_spec.rb +11 -10
  74. data/spec/cli/show_default_report_spec.rb +33 -0
  75. data/spec/cli_config_spec.rb +137 -0
  76. data/spec/cli_enumerated_options_spec.rb +68 -0
  77. data/spec/cli_error_spec.rb +105 -47
  78. data/spec/cli_source_spec.rb +82 -23
  79. data/spec/cli_spec.rb +140 -5
  80. data/spec/cli_success_predicate_spec.rb +141 -0
  81. data/spec/cli_table_spec.rb +1 -1
  82. data/spec/cli_usage_spec.rb +10 -26
  83. data/spec/commands/base_command_spec.rb +187 -0
  84. data/spec/commands/command_factory_spec.rb +72 -0
  85. data/spec/commands/detailed_command_spec.rb +48 -0
  86. data/spec/commands/raw_command_spec.rb +46 -0
  87. data/spec/commands/summary_command_spec.rb +47 -0
  88. data/spec/commands/uncovered_command_spec.rb +49 -0
  89. data/spec/constants_spec.rb +61 -0
  90. data/spec/coverage_table_tool_spec.rb +17 -33
  91. data/spec/error_handler_spec.rb +22 -13
  92. data/spec/error_mode_spec.rb +143 -0
  93. data/spec/errors_edge_cases_spec.rb +239 -0
  94. data/spec/errors_stale_spec.rb +2 -2
  95. data/spec/file_based_mcp_tools_spec.rb +99 -0
  96. data/spec/fixtures/project1/lib/bar.rb +0 -1
  97. data/spec/fixtures/project1/lib/foo.rb +0 -1
  98. data/spec/help_tool_spec.rb +11 -17
  99. data/spec/integration_spec.rb +845 -0
  100. data/spec/logging_fallback_spec.rb +128 -0
  101. data/spec/mcp_logging_spec.rb +44 -0
  102. data/spec/mcp_server_integration_spec.rb +23 -0
  103. data/spec/mcp_server_spec.rb +15 -4
  104. data/spec/mode_detector_spec.rb +148 -0
  105. data/spec/model_error_handling_spec.rb +210 -0
  106. data/spec/model_staleness_spec.rb +40 -10
  107. data/spec/option_normalizers_spec.rb +204 -0
  108. data/spec/option_parsers/env_options_parser_spec.rb +233 -0
  109. data/spec/option_parsers/error_helper_spec.rb +222 -0
  110. data/spec/path_relativizer_spec.rb +83 -0
  111. data/spec/presenters/coverage_detailed_presenter_spec.rb +19 -0
  112. data/spec/presenters/coverage_raw_presenter_spec.rb +15 -0
  113. data/spec/presenters/coverage_summary_presenter_spec.rb +15 -0
  114. data/spec/presenters/coverage_uncovered_presenter_spec.rb +16 -0
  115. data/spec/presenters/project_coverage_presenter_spec.rb +86 -0
  116. data/spec/resolvers/coverage_line_resolver_spec.rb +57 -0
  117. data/spec/resolvers/resolver_factory_spec.rb +61 -0
  118. data/spec/resolvers/resultset_path_resolver_spec.rb +55 -0
  119. data/spec/resultset_loader_spec.rb +167 -0
  120. data/spec/shared_examples/README.md +115 -0
  121. data/spec/shared_examples/coverage_presenter_examples.rb +66 -0
  122. data/spec/shared_examples/file_based_mcp_tools.rb +174 -0
  123. data/spec/shared_examples/mcp_tool_text_json_response.rb +16 -0
  124. data/spec/simple_cov_mcp_module_spec.rb +16 -0
  125. data/spec/simplecov_mcp_model_spec.rb +340 -9
  126. data/spec/simplecov_mcp_opts_spec.rb +182 -0
  127. data/spec/spec_helper.rb +147 -4
  128. data/spec/staleness_checker_spec.rb +373 -0
  129. data/spec/staleness_more_spec.rb +16 -13
  130. data/spec/support/mcp_runner.rb +64 -0
  131. data/spec/tools_error_handling_spec.rb +144 -0
  132. data/spec/util_spec.rb +109 -34
  133. data/spec/version_spec.rb +117 -9
  134. data/spec/version_tool_spec.rb +131 -10
  135. metadata +120 -63
  136. data/lib/simple_cov/mcp.rb +0 -9
  137. data/lib/simple_cov_mcp/base_tool.rb +0 -70
  138. data/lib/simple_cov_mcp/cli.rb +0 -390
  139. data/lib/simple_cov_mcp/error_handler.rb +0 -131
  140. data/lib/simple_cov_mcp/error_handler_factory.rb +0 -38
  141. data/lib/simple_cov_mcp/errors.rb +0 -176
  142. data/lib/simple_cov_mcp/mcp_server.rb +0 -30
  143. data/lib/simple_cov_mcp/model.rb +0 -104
  144. data/lib/simple_cov_mcp/staleness_checker.rb +0 -125
  145. data/lib/simple_cov_mcp/tools/coverage_table_tool.rb +0 -61
  146. data/lib/simple_cov_mcp/util.rb +0 -122
  147. data/lib/simple_cov_mcp.rb +0 -102
  148. data/spec/coverage_detailed_tool_spec.rb +0 -36
  149. data/spec/coverage_raw_tool_spec.rb +0 -32
  150. data/spec/coverage_summary_tool_spec.rb +0 -39
  151. data/spec/legacy_shim_spec.rb +0 -13
  152. data/spec/uncovered_lines_tool_spec.rb +0 -33
@@ -0,0 +1,79 @@
1
+ # Architecture
2
+
3
+ simplecov-mcp is organized around a single coverage data model that feeds three delivery channels: a command-line interface, an MCP server for LLM agents, and a light-weight Ruby API. The codebase is intentionally modular—shared logic for loading, normalizing, and validating SimpleCov data lives in `lib/simplecov_mcp/`, while adapters wrap that core for each runtime mode.
4
+
5
+ ## Runtime Entry Points
6
+
7
+ - **Executable** – `exe/simplecov-mcp` bootstraps the gem, enforces Ruby >= 3.2, and delegates to `SimpleCovMcp.run(ARGV)`.
8
+ - **Mode Negotiation** – `SimpleCovMcp.run` inspects environment defaults from `SIMPLECOV_MCP_OPTS`, checks for CLI subcommands, and defaults to CLI mode when STDIN is a TTY. Otherwise it instantiates `SimpleCovMcp::MCPServer` for MCP protocol communication over STDIO.
9
+ - **Embedded Usage** – Applications embed the gem by instantiating `SimpleCovMcp::CoverageModel` directly, optionally wrapping work in `SimpleCovMcp.with_context` to install a library-oriented error handler.
10
+
11
+ ## Coverage Data Pipeline
12
+
13
+ 1. **Resultset discovery** – The tool locates the `.resultset.json` file by checking a series of default paths or by using a path specified by the user. For a detailed explanation of the configuration options, see the [Configuring the Resultset](../README.md#configuring-the-resultset) section in the main README.
14
+ 2. **Parsing and normalization** – `CoverageModel` loads the chosen resultset once, selects the first suite that exposes `coverage`, and maps all file keys to absolute paths anchored at the configured project root. Timestamps are cached for staleness checks.
15
+ 3. **Path relativizing** – `PathRelativizer` produces relative paths for user-facing payloads without mutating the canonical data. Tool responses pass through `CoverageModel#relativize` before leaving the process.
16
+ 4. **Derived metrics** – `CovUtil.summary`, `CovUtil.uncovered`, and `CovUtil.detailed` compute coverage stats from the raw `lines` arrays. `CoverageModel` exposes `summary_for`, `uncovered_for`, `detailed_for`, and `raw_for` helpers that wrap these utilities.
17
+ 5. **Staleness detection** – `StalenessChecker` compares source mtimes/line counts to coverage metadata. CLI flags and MCP arguments can promote warnings to hard failures (`--stale error`) or simply mark rows as stale for display.
18
+
19
+ ## Interfaces
20
+
21
+ ### CLI (`SimpleCovMcp::CoverageCLI`)
22
+
23
+ - Builds on Ruby’s `OptionParser`, with global options such as `--resultset`, `--stale`, `--json`, and `--source` modes.
24
+ - Subcommands (`list`, `summary`, `raw`, `uncovered`, `detailed`, `version`) translate to calls on `CoverageModel`.
25
+ - Uses `ErrorHandlerFactory.for_cli` to convert unexpected exceptions into friendly user messages while honoring `--error-mode`.
26
+ - Formatting logic (tables, JSON) lives in the model to keep presentation consistent with MCP responses.
27
+
28
+ ### MCP Server (`SimpleCovMcp::MCPServer`)
29
+
30
+ - Assembles a list of tool classes and mounts them in `MCP::Server` using STDIO transport.
31
+ - Relies on the same core model; each tool instance recreates `CoverageModel` with the arguments provided by the MCP client, keeping the server stateless between requests.
32
+ - Error handling delegates to `BaseTool.handle_mcp_error`, which swaps in the MCP-specific handler and emits `MCP::Tool::Response` objects.
33
+
34
+ ### Library API
35
+
36
+ - Consuming code instantiates `CoverageModel` directly for fine-grained control over coverage queries.
37
+ - Use `SimpleCovMcp::ErrorHandlerFactory.for_library` together with `SimpleCovMcp.with_context` when an embedded caller wants to suppress CLI-friendly error logging.
38
+
39
+ ## MCP Tool Stack
40
+
41
+ - `SimpleCovMcp::BaseTool` centralizes JSON schema definitions, error conversion, and response serialization for the MCP protocol.
42
+ - Individual tools reside in `lib/simplecov_mcp/tools/` and follow a consistent shape: define an input schema, call into `CoverageModel`, then serialize via `respond_json`. Examples include `AllFilesCoverageTool`, `CoverageSummaryTool`, and `UncoveredLinesTool`.
43
+ - Tools are registered in `SimpleCovMcp::MCPServer#run`. Adding a new tool only requires creating a subclass and appending it to that list.
44
+
45
+ ## Error Handling & Logging
46
+
47
+ - Custom exceptions under `lib/simplecov_mcp/errors.rb` capture context for configuration issues, missing files, stale coverage, and general runtime errors. Each implements `#user_friendly_message` for consistent UX.
48
+ - `SimpleCovMcp::ErrorHandler` encapsulates logging and severity decisions. Modes (`:off`, `:on`, `:trace`) control whether errors are recorded and whether stack traces are included.
49
+ - Runtime configuration (error handlers, log destinations) flows through `SimpleCovMcp::AppContext`. Entry points push a context with `SimpleCovMcp.with_context`, which stores the active configuration in a thread-local slot (`SimpleCovMcp.context`). Nested calls automatically restore the previous context when the block exits, ensuring isolation even when multiple callers share a process or thread.
50
+ - Two helper accessors clarify intent:
51
+ - `SimpleCovMcp.default_log_file` / `default_log_file=` adjust the baseline log sink that future contexts inherit.
52
+ - `SimpleCovMcp.active_log_file` / `active_log_file=` mutate only the current context (or create one on demand) so the change applies immediately without touching the default.
53
+ - `ErrorHandlerFactory` wires the appropriate handler per runtime: CLI, MCP server, or embedded library, each of which installs its handler inside a fresh `AppContext` before executing user work.
54
+ - Diagnostics are written via `CovUtil.log` to `simplecov_mcp.log` in the current directory by default; override with CLI `--log-file`, set `SimpleCovMcp.default_log_file` for future contexts, or temporarily tweak `SimpleCovMcp.active_log_file` when a caller needs a different destination mid-run.
55
+
56
+ ## Configuration Surface
57
+
58
+ - **Environment defaults** – `SIMPLECOV_MCP_OPTS` applies baseline CLI flags before parsing the actual command line.
59
+ - **Resultset overrides** – The location of the `.resultset.json` file can be specified via CLI options or in the MCP configuration. See [Configuring the Resultset](../README.md#configuring-the-resultset) for details.
60
+ - **Tracked globs** – For project staleness checks, `tracked_globs` ensures new files raise alerts when absent from coverage.
61
+ - **Colorized source** – CLI-only flags (`--source`, `--source-context`, `--color`) enhance human-readable reports when working locally.
62
+
63
+ ## Repository Layout Highlights
64
+
65
+ - `lib/simplecov_mcp/` – Core runtime (model, utilities, error handling, CLI, MCP server, tools).
66
+ - `lib/simplecov_mcp.rb` – Primary public entry point required by gem consumers.
67
+ - `docs/` – User-facing guides (usage, installation, troubleshooting, architecture).
68
+ - `spec/` – RSpec suite with fixtures under `spec/fixtures/` for deterministic coverage data.
69
+ - `scripts/` – Helper scripts (e.g., `scripts/setup_codex_cloud.sh`).
70
+
71
+ ## Extending the System
72
+
73
+ 1. Add or update data processing inside `CoverageModel` or `CovUtil` when a new metric is needed.
74
+ 2. Surface that metric through the desired interface: CLI option/subcommand, new MCP tool, or library helper.
75
+ 3. Register the new tool in `MCPServer`, or update CLI option parsing in `CoverageCLI`.
76
+ 4. Provide tests under `spec/` mirroring the lib path (`spec/lib/simplecov_mcp/..._spec.rb`).
77
+ 5. Update documentation to reflect the new capability.
78
+
79
+ By funnelling every interface through the shared `CoverageModel`, simplecov-mcp guarantees that CLI users, MCP clients, and embedding libraries all observe identical coverage semantics and staleness rules, while still allowing each adapter to tailor presentation and error handling to its audience.
@@ -0,0 +1,81 @@
1
+ # Branch-Only Coverage Handling
2
+
3
+ SimpleCov can collect *line* coverage, *branch* coverage, or both. Projects that
4
+ enable the `:branch` mode without `:line` produce a `.resultset.json` where the
5
+ per-file entries contain a `branches` hash while the legacy `lines` array is
6
+ `null`. This document explains that format and why SimpleCov MCP synthesizes a
7
+ line array on the fly so downstream consumers continue to work.
8
+
9
+ ## SimpleCov branch payloads
10
+
11
+ Each resultset groups coverage by absolute (or project-relative) path. A branch
12
+ only entry looks like:
13
+
14
+ ```json
15
+ {
16
+ "lib/example.rb": {
17
+ "lines": null,
18
+ "branches": {
19
+ "[:if, 0, 12, 4, 20, 29]": {
20
+ "[:then, 1, 13, 6, 13, 18]": 4,
21
+ "[:else, 2, 15, 6, 15, 16]": 0
22
+ }
23
+ }
24
+ }
25
+ }
26
+ ```
27
+
28
+ Key observations:
29
+
30
+ - The outer hash key is either a stringified Ruby array or the raw array itself:
31
+ `[:if, branch_id, line, column, end_line, end_column]`. Array structure:
32
+ - index 0: branch type (`:if`, `:case`, `:unless`, etc.)
33
+ - index 1: internal branch identifier
34
+ - **index 2: line where the branch starts** (the field we care about for coverage)
35
+ - index 3: column where the branch starts
36
+ - index 4: line where the branch expression ends
37
+ - index 5: column where the branch expression ends
38
+ - SimpleCov reuses a single branch tuple for all recorded runs.
39
+ - The nested hash contains targets (`[:then, …]`, `[:else, …]`, `[:when, …]`, etc.)
40
+ Each target tuple mirrors the same shape—branch type at index 0, an internal
41
+ identifier at 1, then the start/end positions for that branch leg.
42
+ The integer value is the execution count for that branch leg. SimpleCov MCP
43
+ sums these counts per line when synthesizing fallback line data.
44
+
45
+ ## Synthesizing line coverage
46
+
47
+ SimpleCov MCP exposes APIs and CLI tooling built around line-oriented coverage.
48
+ To support branch-only projects, we reconstruct a minimal line array by summing
49
+ branch hits per line. The resolver:
50
+
51
+ 1. Normalizes branch tuples, whether they arrive as arrays or strings.
52
+ 2. Extracts the line token (element `2`) and converts it to an integer.
53
+ 3. Aggregates hit counts across all branch targets for that line.
54
+ 4. Builds a dense array up to the highest line seen. Each populated slot stores
55
+ the total branch hits recorded for that line. Slots remain `nil` when a line
56
+ never appeared in the branch data.
57
+
58
+ This produces the familiar SimpleCov shape (array index → line number - 1), so
59
+ utilities like `summary`, `uncovered`, and staleness checks keep working.
60
+
61
+ ## Code map
62
+
63
+ Relevant implementation points:
64
+
65
+ - `lib/simplecov_mcp/resolvers/coverage_line_resolver.rb` — entry point that
66
+ synthesizes the array when `lines` is missing.
67
+ - Specs covering branch-only behaviour live in
68
+ `spec/resolvers/coverage_line_resolver_spec.rb` and
69
+ `spec/simplecov_mcp_model_spec.rb`.
70
+ - Fixture data resides at
71
+ `spec/fixtures/branch_only_project/coverage/.resultset.json`.
72
+
73
+ Implementation notes:
74
+
75
+ - The resolver must return `nil` when a path fails to match. `CovUtil.lookup_lines`
76
+ tries alternate keys (absolute, project-relative, cwd-stripped). Returning
77
+ `nil` allows those attempts to continue, ultimately raising the expected
78
+ `FileError` only after every variant is exhausted.
79
+ - SimpleCov may evolve the branch tuple format. When that happens, extend
80
+ `extract_line_number` to recognize the new shape and refresh this document so
81
+ future maintainers understand which forms are supported.