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
@@ -1,102 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'json'
4
- require 'time'
5
- require 'pathname'
6
- require 'mcp'
7
- require 'mcp/server/transports/stdio_transport'
8
- require 'awesome_print'
9
-
10
- require_relative 'simple_cov_mcp/version'
11
- require_relative 'simple_cov_mcp/util'
12
- require_relative 'simple_cov_mcp/errors'
13
- require_relative 'simple_cov_mcp/error_handler'
14
- require_relative 'simple_cov_mcp/error_handler_factory'
15
- require_relative 'simple_cov_mcp/model'
16
- require_relative 'simple_cov_mcp/base_tool'
17
- require_relative 'simple_cov_mcp/tools/coverage_raw_tool'
18
- require_relative 'simple_cov_mcp/tools/coverage_summary_tool'
19
- require_relative 'simple_cov_mcp/tools/uncovered_lines_tool'
20
- require_relative 'simple_cov_mcp/tools/coverage_detailed_tool'
21
- require_relative 'simple_cov_mcp/tools/all_files_coverage_tool'
22
- require_relative 'simple_cov_mcp/tools/coverage_table_tool'
23
- require_relative 'simple_cov_mcp/tools/version_tool'
24
- require_relative 'simple_cov_mcp/tools/help_tool'
25
- require_relative 'simple_cov_mcp/mcp_server'
26
- require_relative 'simple_cov_mcp/cli'
27
-
28
- module SimpleCovMcp
29
- def self.run(argv)
30
- # Determine whether to run CLI or MCP server based on arguments and environment
31
- if should_run_cli?(argv)
32
- CoverageCLI.new(error_handler: ErrorHandlerFactory.for_cli).run(argv)
33
- else
34
- MCPServer.new.run
35
- end
36
- end
37
-
38
- # For library usage, allow configuration of error handling.
39
- # This method is intended for applications that want to embed simplecov-mcp
40
- # functionality without the CLI behavior of showing friendly error messages
41
- # and exiting. Instead, it raises custom exceptions that can be caught.
42
- #
43
- # Usage:
44
- # # Basic usage - raises custom exceptions on errors
45
- # SimpleCov::Mcp.run_as_library(['summary', 'lib/foo.rb'])
46
- #
47
- # # With custom error handler (e.g., disable logging)
48
- # handler = SimpleCov::Mcp::ErrorHandler.new(log_errors: false)
49
- # SimpleCov::Mcp.run_as_library(['summary', 'lib/foo.rb'], error_handler: handler)
50
- #
51
- # # Exception handling
52
- # begin
53
- # SimpleCov::Mcp.run_as_library(['summary', 'missing.rb'])
54
- # rescue SimpleCov::Mcp::FileError => e
55
- # puts "File not found: #{e.user_friendly_message}"
56
- # rescue SimpleCov::Mcp::CoverageDataError => e
57
- # puts "Coverage issue: #{e.user_friendly_message}"
58
- # end
59
- def self.run_as_library(argv, error_handler: nil)
60
- # Set global error handler for library usage (affects shared components like MCP tools)
61
- SimpleCovMcp.error_handler = error_handler || ErrorHandlerFactory.for_library
62
-
63
- model = CoverageModel.new
64
- execute_library_command(model, argv)
65
- rescue SimpleCovMcp::Error => e
66
- raise e # Re-raise custom errors for library users to catch
67
- rescue => e
68
- SimpleCovMcp.error_handler.handle_error(e, context: 'library execution')
69
- raise e # Re-raise for library users to handle
70
- end
71
-
72
- private
73
-
74
- def self.execute_library_command(model, argv)
75
- return model.all_files if argv.empty?
76
-
77
- unless argv.length >= 2
78
- raise UsageError.new("Invalid arguments. Use: [] for all files, or [command, path] for specific file")
79
- end
80
-
81
- command, path = argv[0], argv[1]
82
- case command
83
- when 'summary' then model.summary_for(path)
84
- when 'raw' then model.raw_for(path)
85
- when 'uncovered' then model.uncovered_for(path)
86
- when 'detailed' then model.detailed_for(path)
87
- else
88
- raise UsageError.new("Unknown command: #{command}. Use: summary, raw, uncovered, or detailed")
89
- end
90
- end
91
-
92
- def self.should_run_cli?(argv)
93
- # Force CLI mode if environment variable is set
94
- return true if ENV['SIMPLECOV_MCP_CLI'] == '1'
95
-
96
- # If a subcommand is provided, run CLI
97
- return true if CoverageCLI::SUBCOMMANDS.include?(argv[0])
98
-
99
- # If interactive TTY, prefer CLI; else (e.g., pipes), run MCP server
100
- STDIN.tty?
101
- end
102
- end
@@ -1,36 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'spec_helper'
4
- require 'simple_cov_mcp/tools/coverage_detailed_tool'
5
-
6
- RSpec.describe SimpleCovMcp::Tools::CoverageDetailedTool do
7
- let(:server_context) { instance_double('ServerContext').as_null_object }
8
-
9
- before do
10
- stub_const('MCP::Tool::Response', Struct.new(:payload))
11
- end
12
-
13
- it 'returns JSON as an application/json resource' do
14
- model = instance_double(SimpleCovMcp::CoverageModel)
15
- allow(SimpleCovMcp::CoverageModel).to receive(:new).and_return(model)
16
- allow(model).to receive(:detailed_for).with('lib/foo.rb').and_return(
17
- {
18
- 'file' => '/abs/path/lib/foo.rb',
19
- 'lines' => [
20
- { 'line' => 1, 'hits' => 1, 'covered' => true },
21
- { 'line' => 2, 'hits' => 0, 'covered' => false }
22
- ],
23
- 'summary' => { 'covered' => 1, 'total' => 2, 'pct' => 50.0 }
24
- }
25
- )
26
-
27
- response = described_class.call(path: 'lib/foo.rb', server_context: server_context)
28
- item = response.payload.first
29
- expect(item['type']).to eq('resource')
30
- expect(item['resource']).to include('mimeType' => 'application/json', 'name' => 'coverage_detailed.json')
31
- data = JSON.parse(item['resource']['text'])
32
- expect(data).to include('file', 'lines', 'summary')
33
- expect(data['lines']).to be_an(Array)
34
- end
35
- end
36
-
@@ -1,32 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'spec_helper'
4
- require 'simple_cov_mcp/tools/coverage_raw_tool'
5
-
6
- RSpec.describe SimpleCovMcp::Tools::CoverageRawTool do
7
- let(:server_context) { instance_double('ServerContext').as_null_object }
8
-
9
- before do
10
- stub_const('MCP::Tool::Response', Struct.new(:payload))
11
- end
12
-
13
- it 'returns JSON as an application/json resource' do
14
- model = instance_double(SimpleCovMcp::CoverageModel)
15
- allow(SimpleCovMcp::CoverageModel).to receive(:new).and_return(model)
16
- allow(model).to receive(:raw_for).with('lib/foo.rb').and_return(
17
- {
18
- 'file' => '/abs/path/lib/foo.rb',
19
- 'lines' => [nil, 1, 0]
20
- }
21
- )
22
-
23
- response = described_class.call(path: 'lib/foo.rb', server_context: server_context)
24
- item = response.payload.first
25
- expect(item['type']).to eq('resource')
26
- expect(item['resource']).to include('mimeType' => 'application/json', 'name' => 'coverage_raw.json')
27
- data = JSON.parse(item['resource']['text'])
28
- expect(data).to include('file', 'lines')
29
- expect(data['lines']).to be_an(Array)
30
- end
31
- end
32
-
@@ -1,39 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'spec_helper'
4
- require 'simple_cov_mcp/tools/coverage_summary_tool'
5
-
6
- RSpec.describe SimpleCovMcp::Tools::CoverageSummaryTool do
7
- let(:server_context) { instance_double('ServerContext').as_null_object }
8
-
9
- before do
10
- response_class = Class.new do
11
- attr_reader :payload, :meta
12
- def initialize(payload, meta: nil)
13
- @payload = payload
14
- @meta = meta
15
- end
16
- end
17
- stub_const('MCP::Tool::Response', response_class)
18
- end
19
-
20
- it 'returns JSON as an application/json resource' do
21
- model = instance_double(SimpleCovMcp::CoverageModel)
22
- allow(SimpleCovMcp::CoverageModel).to receive(:new).and_return(model)
23
- allow(model).to receive(:summary_for).with('lib/foo.rb').and_return(
24
- {
25
- 'file' => '/abs/path/lib/foo.rb',
26
- 'summary' => { 'covered' => 10, 'total' => 12, 'pct' => 83.33 }
27
- }
28
- )
29
-
30
- response = described_class.call(path: 'lib/foo.rb', server_context: server_context)
31
- item = response.payload.first
32
- expect(item['type']).to eq('resource')
33
- expect(item['resource']).to include('mimeType' => 'application/json', 'name' => 'coverage_summary.json')
34
- data = JSON.parse(item['resource']['text'])
35
- expect(data).to include('file', 'summary')
36
- expect(data['summary']).to include('covered', 'total', 'pct')
37
- end
38
- end
39
-
@@ -1,13 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'spec_helper'
4
-
5
- RSpec.describe 'Legacy require shim' do
6
- it "defines SimpleCov::Mcp aliasing SimpleCovMcp" do
7
- # Ensure the legacy file itself is executed for coverage and behavior
8
- load File.expand_path('../../lib/simple_cov/mcp.rb', __FILE__)
9
- expect(defined?(SimpleCov::Mcp)).to be_truthy
10
- expect(SimpleCov::Mcp).to eq(SimpleCovMcp)
11
- end
12
- end
13
-
@@ -1,33 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'spec_helper'
4
- require 'simple_cov_mcp/tools/uncovered_lines_tool'
5
-
6
- RSpec.describe SimpleCovMcp::Tools::UncoveredLinesTool do
7
- let(:server_context) { instance_double('ServerContext').as_null_object }
8
-
9
- before do
10
- stub_const('MCP::Tool::Response', Struct.new(:payload))
11
- end
12
-
13
- it 'returns JSON as an application/json resource' do
14
- model = instance_double(SimpleCovMcp::CoverageModel)
15
- allow(SimpleCovMcp::CoverageModel).to receive(:new).and_return(model)
16
- allow(model).to receive(:uncovered_for).with('lib/foo.rb').and_return(
17
- {
18
- 'file' => '/abs/path/lib/foo.rb',
19
- 'uncovered' => [5, 9, 12],
20
- 'summary' => { 'covered' => 10, 'total' => 12, 'pct' => 83.33 }
21
- }
22
- )
23
-
24
- response = described_class.call(path: 'lib/foo.rb', server_context: server_context)
25
- item = response.payload.first
26
- expect(item['type']).to eq('resource')
27
- expect(item['resource']).to include('mimeType' => 'application/json', 'name' => 'uncovered_lines.json')
28
- data = JSON.parse(item['resource']['text'])
29
- expect(data).to include('file', 'uncovered', 'summary')
30
- expect(data['uncovered']).to eq([5, 9, 12])
31
- end
32
- end
33
-