rigortype 0.1.15 → 0.1.17
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.
- checksums.yaml +4 -4
- data/README.md +4 -2
- data/exe/rigor +19 -0
- data/lib/rigor/analysis/check_rules/self_closedness_scanner.rb +100 -0
- data/lib/rigor/analysis/check_rules/unreachable_clause_collector.rb +209 -0
- data/lib/rigor/analysis/check_rules.rb +174 -71
- data/lib/rigor/analysis/dependency_recorder.rb +122 -0
- data/lib/rigor/analysis/diagnostic.rb +58 -0
- data/lib/rigor/analysis/incremental.rb +162 -0
- data/lib/rigor/analysis/incremental_session.rb +337 -0
- data/lib/rigor/analysis/rule_catalog.rb +48 -0
- data/lib/rigor/analysis/runner.rb +485 -29
- data/lib/rigor/analysis/self_call_resolution_recorder.rb +121 -0
- data/lib/rigor/analysis/worker_session.rb +3 -2
- data/lib/rigor/builtins/static_return_refinements.rb +7 -1
- data/lib/rigor/cache/descriptor.rb +56 -51
- data/lib/rigor/cache/incremental_snapshot.rb +147 -0
- data/lib/rigor/cache/rbs_cache_producer.rb +30 -0
- data/lib/rigor/cache/rbs_class_ancestor_table.rb +2 -8
- data/lib/rigor/cache/rbs_class_type_param_names.rb +2 -8
- data/lib/rigor/cache/rbs_constant_table.rb +2 -8
- data/lib/rigor/cache/rbs_environment.rb +2 -8
- data/lib/rigor/cache/rbs_instance_definitions.rb +3 -16
- data/lib/rigor/cache/rbs_known_class_names.rb +2 -8
- data/lib/rigor/cache/store.rb +99 -1
- data/lib/rigor/cli/annotate_command.rb +2 -7
- data/lib/rigor/cli/baseline_command.rb +2 -7
- data/lib/rigor/cli/command.rb +47 -0
- data/lib/rigor/cli/coverage_command.rb +3 -23
- data/lib/rigor/cli/coverage_renderer.rb +3 -8
- data/lib/rigor/cli/diff_command.rb +3 -7
- data/lib/rigor/cli/explain_command.rb +2 -7
- data/lib/rigor/cli/lsp_command.rb +3 -7
- data/lib/rigor/cli/mcp_command.rb +3 -7
- data/lib/rigor/cli/options.rb +57 -0
- data/lib/rigor/cli/plugin_command.rb +3 -7
- data/lib/rigor/cli/plugins_command.rb +52 -10
- data/lib/rigor/cli/plugins_renderer.rb +86 -1
- data/lib/rigor/cli/renderable.rb +26 -0
- data/lib/rigor/cli/sig_gen_command.rb +2 -7
- data/lib/rigor/cli/skill_command.rb +3 -7
- data/lib/rigor/cli/triage_command.rb +2 -7
- data/lib/rigor/cli/type_of_command.rb +5 -38
- data/lib/rigor/cli/type_of_renderer.rb +4 -9
- data/lib/rigor/cli/type_scan_command.rb +3 -23
- data/lib/rigor/cli/type_scan_renderer.rb +4 -9
- data/lib/rigor/cli.rb +260 -48
- data/lib/rigor/configuration/dependencies.rb +18 -1
- data/lib/rigor/configuration/severity_profile.rb +22 -3
- data/lib/rigor/configuration.rb +13 -3
- data/lib/rigor/environment/rbs_loader.rb +335 -4
- data/lib/rigor/environment.rb +8 -2
- data/lib/rigor/inference/block_parameter_binder.rb +1 -2
- data/lib/rigor/inference/budget_trace.rb +137 -0
- data/lib/rigor/inference/builtins/array_catalog.rb +2 -5
- data/lib/rigor/inference/builtins/comparable_catalog.rb +2 -5
- data/lib/rigor/inference/builtins/complex_catalog.rb +2 -5
- data/lib/rigor/inference/builtins/date_catalog.rb +2 -5
- data/lib/rigor/inference/builtins/encoding_catalog.rb +2 -5
- data/lib/rigor/inference/builtins/enumerable_catalog.rb +2 -5
- data/lib/rigor/inference/builtins/exception_catalog.rb +2 -5
- data/lib/rigor/inference/builtins/hash_catalog.rb +2 -5
- data/lib/rigor/inference/builtins/method_catalog.rb +15 -0
- data/lib/rigor/inference/builtins/numeric_catalog.rb +21 -93
- data/lib/rigor/inference/builtins/pathname_catalog.rb +2 -5
- data/lib/rigor/inference/builtins/proc_catalog.rb +2 -5
- data/lib/rigor/inference/builtins/random_catalog.rb +2 -5
- data/lib/rigor/inference/builtins/range_catalog.rb +2 -5
- data/lib/rigor/inference/builtins/rational_catalog.rb +2 -5
- data/lib/rigor/inference/builtins/re_catalog.rb +2 -5
- data/lib/rigor/inference/builtins/set_catalog.rb +2 -5
- data/lib/rigor/inference/builtins/string_catalog.rb +2 -5
- data/lib/rigor/inference/builtins/struct_catalog.rb +2 -5
- data/lib/rigor/inference/builtins/time_catalog.rb +2 -5
- data/lib/rigor/inference/expression_typer.rb +149 -22
- data/lib/rigor/inference/hkt_reducer.rb +2 -0
- data/lib/rigor/inference/method_dispatcher/block_folding.rb +5 -1
- data/lib/rigor/inference/method_dispatcher/call_context.rb +65 -0
- data/lib/rigor/inference/method_dispatcher/cgi_folding.rb +11 -10
- data/lib/rigor/inference/method_dispatcher/constant_folding.rb +12 -6
- data/lib/rigor/inference/method_dispatcher/data_folding.rb +246 -0
- data/lib/rigor/inference/method_dispatcher/file_folding.rb +6 -2
- data/lib/rigor/inference/method_dispatcher/iterator_dispatch.rb +6 -2
- data/lib/rigor/inference/method_dispatcher/kernel_dispatch.rb +4 -1
- data/lib/rigor/inference/method_dispatcher/literal_string_folding.rb +4 -1
- data/lib/rigor/inference/method_dispatcher/math_folding.rb +6 -6
- data/lib/rigor/inference/method_dispatcher/method_folding.rb +12 -7
- data/lib/rigor/inference/method_dispatcher/overload_selector.rb +23 -6
- data/lib/rigor/inference/method_dispatcher/rbs_dispatch.rb +100 -23
- data/lib/rigor/inference/method_dispatcher/regexp_folding.rb +9 -9
- data/lib/rigor/inference/method_dispatcher/set_folding.rb +6 -6
- data/lib/rigor/inference/method_dispatcher/shape_dispatch.rb +120 -9
- data/lib/rigor/inference/method_dispatcher/shellwords_folding.rb +12 -12
- data/lib/rigor/inference/method_dispatcher/singleton_folding.rb +49 -0
- data/lib/rigor/inference/method_dispatcher/time_folding.rb +6 -6
- data/lib/rigor/inference/method_dispatcher/uri_folding.rb +9 -9
- data/lib/rigor/inference/method_dispatcher.rb +147 -60
- data/lib/rigor/inference/narrowing.rb +202 -5
- data/lib/rigor/inference/precision_scanner.rb +60 -1
- data/lib/rigor/inference/scope_indexer.rb +257 -11
- data/lib/rigor/inference/statement_evaluator.rb +110 -26
- data/lib/rigor/inference/synthetic_method_index.rb +23 -4
- data/lib/rigor/inference/synthetic_method_scanner.rb +148 -14
- data/lib/rigor/language_server/buffer_resolution.rb +33 -0
- data/lib/rigor/language_server/completion_provider.rb +4 -4
- data/lib/rigor/language_server/document_symbol_provider.rb +4 -4
- data/lib/rigor/language_server/folding_range_provider.rb +4 -4
- data/lib/rigor/language_server/hover_provider.rb +4 -4
- data/lib/rigor/language_server/selection_range_provider.rb +4 -4
- data/lib/rigor/language_server/signature_help_provider.rb +4 -4
- data/lib/rigor/plugin/additional_initializer.rb +108 -0
- data/lib/rigor/plugin/base.rb +337 -2
- data/lib/rigor/plugin/box.rb +64 -0
- data/lib/rigor/plugin/inflector.rb +121 -0
- data/lib/rigor/plugin/isolation.rb +191 -0
- data/lib/rigor/plugin/macro/nested_class_template.rb +140 -0
- data/lib/rigor/plugin/macro.rb +1 -0
- data/lib/rigor/plugin/manifest.rb +120 -23
- data/lib/rigor/plugin/node_context.rb +62 -0
- data/lib/rigor/plugin/registry.rb +49 -1
- data/lib/rigor/plugin.rb +3 -0
- data/lib/rigor/rbs_extended/conformance_checker.rb +208 -0
- data/lib/rigor/rbs_extended.rb +39 -0
- data/lib/rigor/scope.rb +123 -9
- data/lib/rigor/sig_gen/generator.rb +2 -3
- data/lib/rigor/sig_gen/observation_collector.rb +2 -2
- data/lib/rigor/source/literals.rb +118 -0
- data/lib/rigor/source/node_walker.rb +26 -0
- data/lib/rigor/source.rb +1 -0
- data/lib/rigor/type/acceptance_router.rb +19 -0
- data/lib/rigor/type/accepts_result.rb +3 -10
- data/lib/rigor/type/app.rb +3 -7
- data/lib/rigor/type/bot.rb +2 -3
- data/lib/rigor/type/bound_method.rb +5 -12
- data/lib/rigor/type/combinator.rb +23 -1
- data/lib/rigor/type/constant.rb +2 -3
- data/lib/rigor/type/data_class.rb +80 -0
- data/lib/rigor/type/data_instance.rb +100 -0
- data/lib/rigor/type/difference.rb +5 -10
- data/lib/rigor/type/dynamic.rb +5 -10
- data/lib/rigor/type/hash_shape.rb +5 -15
- data/lib/rigor/type/integer_range.rb +5 -10
- data/lib/rigor/type/intersection.rb +5 -10
- data/lib/rigor/type/nominal.rb +5 -10
- data/lib/rigor/type/refined.rb +5 -10
- data/lib/rigor/type/singleton.rb +5 -10
- data/lib/rigor/type/top.rb +2 -3
- data/lib/rigor/type/tuple.rb +5 -10
- data/lib/rigor/type/union.rb +69 -10
- data/lib/rigor/type.rb +2 -0
- data/lib/rigor/value_semantics.rb +77 -0
- data/lib/rigor/version.rb +1 -1
- data/lib/rigor.rb +2 -0
- data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable/analyzer.rb +31 -53
- data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable.rb +21 -23
- data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer/analyzer.rb +38 -59
- data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer/mailer_discoverer.rb +7 -13
- data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer.rb +22 -33
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/analyzer.rb +298 -413
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack.rb +69 -71
- data/plugins/rigor-activejob/lib/rigor/plugin/activejob/analyzer.rb +24 -34
- data/plugins/rigor-activejob/lib/rigor/plugin/activejob.rb +18 -16
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/analyzer.rb +4 -46
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/model_discoverer.rb +4 -4
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/model_index.rb +1 -1
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord.rb +17 -12
- data/plugins/rigor-activestorage/lib/rigor/plugin/activestorage/analyzer.rb +2 -8
- data/plugins/rigor-activestorage/lib/rigor/plugin/activestorage/attachment_discoverer.rb +2 -7
- data/plugins/rigor-activestorage/lib/rigor/plugin/activestorage.rb +2 -6
- data/plugins/rigor-dry-schema/lib/rigor/plugin/dry_schema/schema_scanner.rb +4 -3
- data/plugins/rigor-dry-validation/lib/rigor/plugin/dry_validation.rb +5 -1
- data/plugins/rigor-factorybot/lib/rigor/plugin/factorybot/analyzer.rb +40 -45
- data/plugins/rigor-factorybot/lib/rigor/plugin/factorybot/factory_discoverer.rb +7 -17
- data/plugins/rigor-factorybot/lib/rigor/plugin/factorybot.rb +20 -42
- data/plugins/rigor-graphql/lib/rigor/plugin/graphql/type_scanner.rb +7 -4
- data/plugins/rigor-hanami/lib/rigor/plugin/hanami/action_checker.rb +4 -8
- data/plugins/rigor-mangrove/lib/rigor/plugin/mangrove.rb +188 -0
- data/plugins/rigor-mangrove/lib/rigor-mangrove.rb +3 -0
- data/plugins/rigor-minitest/lib/rigor/plugin/minitest/assertion_analyzer.rb +4 -0
- data/plugins/rigor-minitest/lib/rigor/plugin/minitest.rb +24 -8
- data/plugins/rigor-pundit/lib/rigor/plugin/pundit/analyzer.rb +31 -48
- data/plugins/rigor-pundit/lib/rigor/plugin/pundit.rb +21 -23
- data/plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n/analyzer.rb +54 -82
- data/plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n.rb +25 -25
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/analyzer.rb +63 -147
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/devise_routes.rb +4 -17
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/routes_parser.rb +23 -114
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes.rb +48 -33
- data/plugins/rigor-rbs-inline/lib/rigor/plugin/rbs_inline.rb +0 -1
- data/plugins/rigor-rspec/lib/rigor/plugin/rspec/let_scope_index.rb +6 -3
- data/plugins/rigor-rspec/lib/rigor/plugin/rspec/scope_walker.rb +4 -2
- data/plugins/rigor-rspec/lib/rigor/plugin/rspec.rb +13 -12
- data/plugins/rigor-rspec-rails/lib/rigor/plugin/rspec_rails/have_http_status_analyzer.rb +28 -40
- data/plugins/rigor-rspec-rails/lib/rigor/plugin/rspec_rails/http_status_codes.rb +44 -47
- data/plugins/rigor-rspec-rails/lib/rigor/plugin/rspec_rails.rb +11 -10
- data/plugins/rigor-shoulda-matchers/lib/rigor/plugin/shoulda_matchers/analyzer.rb +45 -87
- data/plugins/rigor-shoulda-matchers/lib/rigor/plugin/shoulda_matchers.rb +11 -12
- data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq/analyzer.rb +29 -42
- data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq.rb +20 -19
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/catalog_walker.rb +73 -0
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/type_translator.rb +43 -1
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet.rb +21 -29
- data/plugins/rigor-statesman/lib/rigor/plugin/statesman.rb +36 -96
- data/sig/rigor/cache.rbs +19 -0
- data/sig/rigor/inference.rbs +22 -0
- data/sig/rigor/plugin/access_denied_error.rbs +3 -1
- data/sig/rigor/plugin/base.rbs +58 -3
- data/sig/rigor/plugin/io_boundary.rbs +3 -0
- data/sig/rigor/plugin/manifest.rbs +31 -1
- data/sig/rigor/rbs_extended.rbs +2 -0
- data/sig/rigor/scope.rbs +5 -0
- data/sig/rigor/source.rbs +12 -0
- data/sig/rigor/type.rbs +58 -1
- data/sig/rigor.rbs +11 -1
- data/skills/rigor-plugin-author/SKILL.md +13 -9
- data/skills/rigor-plugin-author/references/01-plan-and-scaffold.md +6 -5
- data/skills/rigor-plugin-author/references/02-walker-and-types.md +159 -75
- data/skills/rigor-plugin-author/references/03-test-and-ship.md +3 -3
- metadata +73 -2
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/inflector.rb +0 -114
|
@@ -9,6 +9,7 @@ require_relative "../plugin/services"
|
|
|
9
9
|
require_relative "../reflection"
|
|
10
10
|
require_relative "../type/combinator"
|
|
11
11
|
require_relative "plugins_renderer"
|
|
12
|
+
require_relative "command"
|
|
12
13
|
|
|
13
14
|
module Rigor
|
|
14
15
|
class CLI
|
|
@@ -32,7 +33,15 @@ module Rigor
|
|
|
32
33
|
# `trait_registries:` / `external_files:` /
|
|
33
34
|
# `type_node_resolvers:` / `hkt_registrations:` /
|
|
34
35
|
# `hkt_definitions:` / `protocol_contracts:` /
|
|
35
|
-
# `source_rbs_synthesizer:`)
|
|
36
|
+
# `source_rbs_synthesizer:`);
|
|
37
|
+
# - the ADR-37 narrow extension protocols read off the plugin
|
|
38
|
+
# class — `node_rule` node types, `dynamic_return` receivers,
|
|
39
|
+
# `type_specifier` methods.
|
|
40
|
+
#
|
|
41
|
+
# `--capabilities` switches to a focused catalogue of just the
|
|
42
|
+
# narrow-protocol gate values + produced/consumed facts (ADR-37
|
|
43
|
+
# § "Machine-readable capability catalogue") — the AI-legibility
|
|
44
|
+
# surface that lets an agent enumerate what every plugin does.
|
|
36
45
|
#
|
|
37
46
|
# Output formats: `text` (default, human-readable table) and
|
|
38
47
|
# `json` (for tooling — SKILLs, CI gates, editor integrations).
|
|
@@ -52,15 +61,9 @@ module Rigor
|
|
|
52
61
|
# the RBS environment without conflict (requires constructing
|
|
53
62
|
# the Environment, which is heavier than the loader-only
|
|
54
63
|
# pass this slice does).
|
|
55
|
-
class PluginsCommand
|
|
64
|
+
class PluginsCommand < Command # rubocop:disable Metrics/ClassLength
|
|
56
65
|
USAGE = "Usage: rigor plugins [options]"
|
|
57
66
|
|
|
58
|
-
def initialize(argv:, out: $stdout, err: $stderr)
|
|
59
|
-
@argv = argv
|
|
60
|
-
@out = out
|
|
61
|
-
@err = err
|
|
62
|
-
end
|
|
63
|
-
|
|
64
67
|
# @return [Integer] CLI exit status.
|
|
65
68
|
def run
|
|
66
69
|
options = parse_options
|
|
@@ -69,7 +72,7 @@ module Rigor
|
|
|
69
72
|
rows = build_rows(configuration)
|
|
70
73
|
|
|
71
74
|
renderer = PluginsRenderer.new(rows: rows, configuration_path: config_path)
|
|
72
|
-
@out.puts(
|
|
75
|
+
@out.puts(render(renderer, options))
|
|
73
76
|
|
|
74
77
|
any_load_errors = rows.any? { |row| row.fetch(:status) == :load_error }
|
|
75
78
|
return 1 if any_load_errors && options.fetch(:strict)
|
|
@@ -79,13 +82,32 @@ module Rigor
|
|
|
79
82
|
|
|
80
83
|
private
|
|
81
84
|
|
|
85
|
+
# Picks the renderer view. `--capabilities` switches to the
|
|
86
|
+
# focused extension-protocol catalogue (ADR-37 § "Machine-readable
|
|
87
|
+
# capability catalogue") — per plugin, only the gate values that
|
|
88
|
+
# tell a reader (or an AI agent) exactly what the plugin
|
|
89
|
+
# contributes: the node-rule node types, the dynamic-return
|
|
90
|
+
# receivers, the type-specifier methods, and the produced /
|
|
91
|
+
# consumed facts. The default view stays the full activation report.
|
|
92
|
+
def render(renderer, options)
|
|
93
|
+
json = options.fetch(:format) == "json"
|
|
94
|
+
if options.fetch(:capabilities)
|
|
95
|
+
json ? renderer.capabilities_json : renderer.capabilities_text
|
|
96
|
+
else
|
|
97
|
+
json ? renderer.json : renderer.text
|
|
98
|
+
end
|
|
99
|
+
end
|
|
100
|
+
|
|
82
101
|
def parse_options
|
|
83
|
-
options = { config: nil, format: "text", strict: false }
|
|
102
|
+
options = { config: nil, format: "text", strict: false, capabilities: false }
|
|
84
103
|
OptionParser.new do |opts|
|
|
85
104
|
opts.banner = USAGE
|
|
86
105
|
opts.on("--config=PATH", "Path to the Rigor configuration file") { |v| options[:config] = v }
|
|
87
106
|
opts.on("--format=FORMAT", "Output format: text (default) or json") { |v| options[:format] = v }
|
|
88
107
|
opts.on("--strict", "Exit 1 if any plugin failed to load (CI gate)") { options[:strict] = true }
|
|
108
|
+
opts.on("--capabilities", "Emit the per-plugin extension-protocol catalogue (ADR-37)") do
|
|
109
|
+
options[:capabilities] = true
|
|
110
|
+
end
|
|
89
111
|
end.parse!(@argv)
|
|
90
112
|
validate!(options)
|
|
91
113
|
options
|
|
@@ -165,9 +187,25 @@ module Rigor
|
|
|
165
187
|
manifest = plugin.manifest
|
|
166
188
|
identity_fields(gem_name, manifest, config)
|
|
167
189
|
.merge(extension_fields(plugin, manifest))
|
|
190
|
+
.merge(narrow_protocol_fields(plugin))
|
|
168
191
|
.merge(load_error: nil)
|
|
169
192
|
end
|
|
170
193
|
|
|
194
|
+
# ADR-37 narrow extension protocols. Unlike the 10 declarative
|
|
195
|
+
# manifest fields, these are class-level DSLs (`node_rule` /
|
|
196
|
+
# `dynamic_return` / `type_specifier`), so they are read off the
|
|
197
|
+
# plugin class rather than the manifest. The gate values — node
|
|
198
|
+
# types, receiver class names, specified method names — are the
|
|
199
|
+
# greppable, enumerable surface the capability catalogue exposes.
|
|
200
|
+
def narrow_protocol_fields(plugin)
|
|
201
|
+
klass = plugin.class
|
|
202
|
+
{
|
|
203
|
+
node_rule_types: klass.node_rules.map { |r| r[:node_type].name }.uniq,
|
|
204
|
+
dynamic_return_receivers: klass.dynamic_returns.flat_map { |r| r[:receivers] }.uniq,
|
|
205
|
+
type_specifier_methods: klass.type_specifiers.flat_map { |r| r[:methods] }.map(&:to_s).uniq
|
|
206
|
+
}
|
|
207
|
+
end
|
|
208
|
+
|
|
171
209
|
def identity_fields(gem_name, manifest, config)
|
|
172
210
|
{
|
|
173
211
|
gem: gem_name,
|
|
@@ -225,6 +263,9 @@ module Rigor
|
|
|
225
263
|
hkt_definitions: 0,
|
|
226
264
|
protocol_contracts: 0,
|
|
227
265
|
source_rbs_synthesizer: false,
|
|
266
|
+
node_rule_types: [],
|
|
267
|
+
dynamic_return_receivers: [],
|
|
268
|
+
type_specifier_methods: [],
|
|
228
269
|
load_error: error&.message || "plugin did not register or could not be matched to a registered class"
|
|
229
270
|
}
|
|
230
271
|
end
|
|
@@ -299,6 +340,7 @@ module Rigor
|
|
|
299
340
|
external_files: 0, type_node_resolvers: 0,
|
|
300
341
|
hkt_registrations: 0, hkt_definitions: 0,
|
|
301
342
|
protocol_contracts: 0, source_rbs_synthesizer: false,
|
|
343
|
+
node_rule_types: [], dynamic_return_receivers: [], type_specifier_methods: [],
|
|
302
344
|
load_error: error.message
|
|
303
345
|
}
|
|
304
346
|
end
|
|
@@ -13,7 +13,7 @@ module Rigor
|
|
|
13
13
|
# tooling (SKILLs, CI, editor integrations) while text is
|
|
14
14
|
# for interactive inspection. Rows are printed in the order
|
|
15
15
|
# the loader resolved them.
|
|
16
|
-
class PluginsRenderer
|
|
16
|
+
class PluginsRenderer # rubocop:disable Metrics/ClassLength
|
|
17
17
|
def initialize(rows:, configuration_path:)
|
|
18
18
|
@rows = rows
|
|
19
19
|
@configuration_path = configuration_path
|
|
@@ -42,8 +42,74 @@ module Rigor
|
|
|
42
42
|
)
|
|
43
43
|
end
|
|
44
44
|
|
|
45
|
+
# ADR-37 § "Machine-readable capability catalogue" — the focused
|
|
46
|
+
# per-plugin extension-protocol dump. Only loaded plugins appear
|
|
47
|
+
# (a plugin that failed to load contributes no capabilities), and
|
|
48
|
+
# each carries only the gate values an agent enumerates to learn
|
|
49
|
+
# what the plugin does: node-rule node types, dynamic-return
|
|
50
|
+
# receivers, type-specifier methods, and produced / consumed facts.
|
|
51
|
+
def capabilities_json
|
|
52
|
+
JSON.pretty_generate(
|
|
53
|
+
{
|
|
54
|
+
"configuration" => @configuration_path,
|
|
55
|
+
"capabilities" => loaded_rows.map { |row| capabilities_json_for(row) }
|
|
56
|
+
}
|
|
57
|
+
)
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def capabilities_text
|
|
61
|
+
lines = ["Plugin capability catalogue (ADR-37 narrow extension protocols)", ""]
|
|
62
|
+
loaded = loaded_rows
|
|
63
|
+
if loaded.empty?
|
|
64
|
+
lines << " (no plugins loaded)"
|
|
65
|
+
else
|
|
66
|
+
loaded.each_with_index do |row, index|
|
|
67
|
+
lines.concat(capability_lines(row))
|
|
68
|
+
lines << "" unless index == loaded.size - 1
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
lines.join("\n")
|
|
72
|
+
end
|
|
73
|
+
|
|
45
74
|
private
|
|
46
75
|
|
|
76
|
+
def loaded_rows
|
|
77
|
+
@rows.select { |r| r[:status] == :loaded }
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def capability_lines(row)
|
|
81
|
+
lines = [" #{row[:id]} v#{row[:version]} (#{row[:gem]})"]
|
|
82
|
+
capability_surfaces(row).each { |surface| lines << " #{surface}" }
|
|
83
|
+
lines << " (no narrow extension protocols declared)" if lines.size == 1
|
|
84
|
+
lines
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
# The non-empty capability surfaces for a plugin, each as a
|
|
88
|
+
# `label: a, b, c` string. Data-driven so the catalogue stays a
|
|
89
|
+
# single source of truth shared between the text and JSON views.
|
|
90
|
+
def capability_surfaces(row)
|
|
91
|
+
[
|
|
92
|
+
["node_rule", row[:node_rule_types]],
|
|
93
|
+
["dynamic_return receivers", row[:dynamic_return_receivers]],
|
|
94
|
+
["type_specifier methods", row[:type_specifier_methods]],
|
|
95
|
+
["produces", row[:produces]],
|
|
96
|
+
["consumes", row[:consumes]]
|
|
97
|
+
].filter_map { |label, values| "#{label}: #{values.join(', ')}" if values.any? }
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
def capabilities_json_for(row)
|
|
101
|
+
{
|
|
102
|
+
"id" => row[:id],
|
|
103
|
+
"gem" => row[:gem],
|
|
104
|
+
"version" => row[:version],
|
|
105
|
+
"node_rule_types" => row[:node_rule_types],
|
|
106
|
+
"dynamic_return_receivers" => row[:dynamic_return_receivers],
|
|
107
|
+
"type_specifier_methods" => row[:type_specifier_methods],
|
|
108
|
+
"produces" => row[:produces],
|
|
109
|
+
"consumes" => row[:consumes]
|
|
110
|
+
}
|
|
111
|
+
end
|
|
112
|
+
|
|
47
113
|
def header
|
|
48
114
|
loaded = @rows.count { |r| r[:status] == :loaded }
|
|
49
115
|
errored = @rows.count { |r| r[:status] == :load_error }
|
|
@@ -99,6 +165,22 @@ module Rigor
|
|
|
99
165
|
lines << " owns_receivers: #{row[:owns_receivers].join(', ')}" if row[:owns_receivers].any?
|
|
100
166
|
lines << " produces: #{row[:produces].join(', ')}" if row[:produces].any?
|
|
101
167
|
lines << " consumes: #{row[:consumes].join(', ')}" if row[:consumes].any?
|
|
168
|
+
lines.concat(narrow_protocol_lines(row))
|
|
169
|
+
lines
|
|
170
|
+
end
|
|
171
|
+
|
|
172
|
+
# ADR-37 narrow extension protocols (node_rule / dynamic_return /
|
|
173
|
+
# type_specifier). Surfaced in the full report alongside the
|
|
174
|
+
# declarative surfaces; `--capabilities` is the focused view.
|
|
175
|
+
def narrow_protocol_lines(row)
|
|
176
|
+
lines = []
|
|
177
|
+
lines << " node_rule: #{row[:node_rule_types].join(', ')}" if row[:node_rule_types].any?
|
|
178
|
+
if row[:dynamic_return_receivers].any?
|
|
179
|
+
lines << " dynamic_return receivers: #{row[:dynamic_return_receivers].join(', ')}"
|
|
180
|
+
end
|
|
181
|
+
if row[:type_specifier_methods].any?
|
|
182
|
+
lines << " type_specifier methods: #{row[:type_specifier_methods].join(', ')}"
|
|
183
|
+
end
|
|
102
184
|
lines
|
|
103
185
|
end
|
|
104
186
|
|
|
@@ -157,6 +239,9 @@ module Rigor
|
|
|
157
239
|
"hkt_definitions" => row[:hkt_definitions],
|
|
158
240
|
"protocol_contracts" => row[:protocol_contracts],
|
|
159
241
|
"source_rbs_synthesizer" => row[:source_rbs_synthesizer],
|
|
242
|
+
"node_rule_types" => row[:node_rule_types],
|
|
243
|
+
"dynamic_return_receivers" => row[:dynamic_return_receivers],
|
|
244
|
+
"type_specifier_methods" => row[:type_specifier_methods],
|
|
160
245
|
"load_error" => row[:load_error]
|
|
161
246
|
}
|
|
162
247
|
end
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "optionparser"
|
|
4
|
+
|
|
5
|
+
module Rigor
|
|
6
|
+
class CLI
|
|
7
|
+
# Output-format dispatch shared by the `--format text|json` renderers.
|
|
8
|
+
#
|
|
9
|
+
# Each renderer included this and then implemented `render_text` /
|
|
10
|
+
# `render_json`; the `render(data, format:)` entry point — route by
|
|
11
|
+
# the format string, raise one consistent `OptionParser::InvalidArgument`
|
|
12
|
+
# on anything else — was copied verbatim into every one. Centralising
|
|
13
|
+
# it keeps the unsupported-format wording and the text/json contract
|
|
14
|
+
# in a single place as new renderers and formats are added.
|
|
15
|
+
module Renderable
|
|
16
|
+
def render(data, format:)
|
|
17
|
+
case format
|
|
18
|
+
when "text" then render_text(data)
|
|
19
|
+
when "json" then render_json(data)
|
|
20
|
+
else
|
|
21
|
+
raise OptionParser::InvalidArgument, "unsupported format: #{format}"
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
@@ -4,6 +4,7 @@ require "optionparser"
|
|
|
4
4
|
|
|
5
5
|
require_relative "../configuration"
|
|
6
6
|
require_relative "../sig_gen"
|
|
7
|
+
require_relative "command"
|
|
7
8
|
|
|
8
9
|
module Rigor
|
|
9
10
|
class CLI
|
|
@@ -34,19 +35,13 @@ module Rigor
|
|
|
34
35
|
# `--params=observed-strict` stays reserved-but-inert until
|
|
35
36
|
# the capability-role catalog ships (rejected with a usage
|
|
36
37
|
# error so the surface stays stable).
|
|
37
|
-
class SigGenCommand
|
|
38
|
+
class SigGenCommand < Command
|
|
38
39
|
USAGE = "Usage: rigor sig-gen [options] [paths]"
|
|
39
40
|
|
|
40
41
|
VALID_MODES = %w[print diff write].freeze
|
|
41
42
|
VALID_PARAM_POLICIES = %w[untyped observed observed-strict].freeze
|
|
42
43
|
VALID_FORMATS = %w[text json].freeze
|
|
43
44
|
|
|
44
|
-
def initialize(argv:, out:, err:)
|
|
45
|
-
@argv = argv
|
|
46
|
-
@out = out
|
|
47
|
-
@err = err
|
|
48
|
-
end
|
|
49
|
-
|
|
50
45
|
# @return [Integer] CLI exit status.
|
|
51
46
|
def run
|
|
52
47
|
options = parse_options
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require_relative "command"
|
|
4
|
+
|
|
3
5
|
require "optparse"
|
|
4
6
|
|
|
5
7
|
module Rigor
|
|
@@ -29,7 +31,7 @@ module Rigor
|
|
|
29
31
|
# as input to a Read tool.
|
|
30
32
|
#
|
|
31
33
|
# `rigor skill` with no subcommand is an alias for `list`.
|
|
32
|
-
class SkillCommand
|
|
34
|
+
class SkillCommand < Command
|
|
33
35
|
USAGE = <<~USAGE
|
|
34
36
|
Usage: rigor skill <subcommand> [args]
|
|
35
37
|
|
|
@@ -48,12 +50,6 @@ module Rigor
|
|
|
48
50
|
# `lib/rigor/cli/skill_command.rb` that is three directories up.
|
|
49
51
|
SKILLS_ROOT = File.expand_path("../../../skills", __dir__)
|
|
50
52
|
|
|
51
|
-
def initialize(argv:, out: $stdout, err: $stderr)
|
|
52
|
-
@argv = argv
|
|
53
|
-
@out = out
|
|
54
|
-
@err = err
|
|
55
|
-
end
|
|
56
|
-
|
|
57
53
|
# @return [Integer] CLI exit status.
|
|
58
54
|
def run
|
|
59
55
|
subcommand = @argv.shift || "list"
|
|
@@ -7,6 +7,7 @@ require_relative "../analysis/runner"
|
|
|
7
7
|
require_relative "../cache/store"
|
|
8
8
|
require_relative "../triage"
|
|
9
9
|
require_relative "triage_renderer"
|
|
10
|
+
require_relative "command"
|
|
10
11
|
|
|
11
12
|
module Rigor
|
|
12
13
|
class CLI
|
|
@@ -18,16 +19,10 @@ module Rigor
|
|
|
18
19
|
# Read-only and advisory (WD4): never edits config, never
|
|
19
20
|
# writes a baseline. Always exits 0 — it is an inspection
|
|
20
21
|
# command, not a gate (`rigor check` remains the gate).
|
|
21
|
-
class TriageCommand
|
|
22
|
+
class TriageCommand < Command
|
|
22
23
|
USAGE = "Usage: rigor triage [options] [paths]"
|
|
23
24
|
DEFAULT_SECTIONS = %i[distribution hotspots hints].freeze
|
|
24
25
|
|
|
25
|
-
def initialize(argv:, out:, err:)
|
|
26
|
-
@argv = argv
|
|
27
|
-
@out = out
|
|
28
|
-
@err = err
|
|
29
|
-
end
|
|
30
|
-
|
|
31
26
|
# @return [Integer] CLI exit status (always 0).
|
|
32
27
|
def run
|
|
33
28
|
options = parse_options
|
|
@@ -11,6 +11,8 @@ require_relative "../source/node_locator"
|
|
|
11
11
|
require_relative "../inference/fallback_tracer"
|
|
12
12
|
require_relative "../inference/scope_indexer"
|
|
13
13
|
require_relative "type_of_renderer"
|
|
14
|
+
require_relative "command"
|
|
15
|
+
require_relative "options"
|
|
14
16
|
|
|
15
17
|
module Rigor
|
|
16
18
|
class CLI
|
|
@@ -25,21 +27,15 @@ module Rigor
|
|
|
25
27
|
# dispatching and lets us evolve the type-of UX (extra flags, watch mode,
|
|
26
28
|
# streaming output) without bloating the CLI shell. Output formatting is
|
|
27
29
|
# delegated to {TypeOfRenderer}.
|
|
28
|
-
class TypeOfCommand
|
|
30
|
+
class TypeOfCommand < Command
|
|
29
31
|
USAGE = "Usage: rigor type-of [options] FILE:LINE:COL"
|
|
30
32
|
|
|
31
33
|
Result = Data.define(:file, :line, :column, :node, :type, :tracer)
|
|
32
34
|
|
|
33
|
-
def initialize(argv:, out:, err:)
|
|
34
|
-
@argv = argv
|
|
35
|
-
@out = out
|
|
36
|
-
@err = err
|
|
37
|
-
end
|
|
38
|
-
|
|
39
35
|
# @return [Integer] CLI exit status.
|
|
40
36
|
def run
|
|
41
37
|
options = parse_options
|
|
42
|
-
buffer = resolve_buffer_binding(options)
|
|
38
|
+
buffer = Options.resolve_buffer_binding(options, err: @err)
|
|
43
39
|
return CLI::EXIT_USAGE if buffer == :usage_error
|
|
44
40
|
|
|
45
41
|
target = parse_position_argument(@argv)
|
|
@@ -58,42 +54,13 @@ module Rigor
|
|
|
58
54
|
opts.on("--format=FORMAT", "Output format: text or json") { |value| options[:format] = value }
|
|
59
55
|
opts.on("--trace", "Record fail-soft fallbacks via FallbackTracer") { options[:trace] = true }
|
|
60
56
|
opts.on("--config=PATH", "Path to the Rigor configuration file") { |value| options[:config] = value }
|
|
61
|
-
|
|
62
|
-
"Editor mode: read source bytes from PATH instead of --instead-of (paired)") do |value|
|
|
63
|
-
options[:tmp_file] = value
|
|
64
|
-
end
|
|
65
|
-
opts.on("--instead-of=PATH",
|
|
66
|
-
"Editor mode: the logical project path the buffer represents (paired with --tmp-file)") do |value|
|
|
67
|
-
options[:instead_of] = value
|
|
68
|
-
end
|
|
57
|
+
Options.add_editor_mode(opts, options)
|
|
69
58
|
end
|
|
70
59
|
parser.parse!(@argv)
|
|
71
60
|
|
|
72
61
|
options
|
|
73
62
|
end
|
|
74
63
|
|
|
75
|
-
# Mirrors `Rigor::CLI#resolve_buffer_binding` (the `check`
|
|
76
|
-
# path). Returns nil / BufferBinding / :usage_error. The
|
|
77
|
-
# symbol return path lets the caller translate to
|
|
78
|
-
# `CLI::EXIT_USAGE` without raising.
|
|
79
|
-
def resolve_buffer_binding(options)
|
|
80
|
-
tmp = options[:tmp_file]
|
|
81
|
-
instead = options[:instead_of]
|
|
82
|
-
return nil if tmp.nil? && instead.nil?
|
|
83
|
-
|
|
84
|
-
if tmp.nil? || instead.nil?
|
|
85
|
-
@err.puts("--tmp-file and --instead-of must appear together")
|
|
86
|
-
return :usage_error
|
|
87
|
-
end
|
|
88
|
-
|
|
89
|
-
unless File.file?(tmp)
|
|
90
|
-
@err.puts("--tmp-file #{tmp.inspect}: no such file or not readable")
|
|
91
|
-
return :usage_error
|
|
92
|
-
end
|
|
93
|
-
|
|
94
|
-
Rigor::Analysis::BufferBinding.new(logical_path: instead, physical_path: tmp)
|
|
95
|
-
end
|
|
96
|
-
|
|
97
64
|
def execute(target:, options:, buffer: nil)
|
|
98
65
|
file, line, column = target
|
|
99
66
|
# Under editor mode the logical `file` may not exist on disk
|
|
@@ -3,6 +3,8 @@
|
|
|
3
3
|
require "json"
|
|
4
4
|
require "optionparser"
|
|
5
5
|
|
|
6
|
+
require_relative "renderable"
|
|
7
|
+
|
|
6
8
|
module Rigor
|
|
7
9
|
class CLI
|
|
8
10
|
# Renders a `TypeOfCommand::Result` as either human-readable text or a
|
|
@@ -12,19 +14,12 @@ module Rigor
|
|
|
12
14
|
# output formats (sexp, lsp-style hover payloads, color decoration) can
|
|
13
15
|
# plug in without disturbing argument parsing or the inference call site.
|
|
14
16
|
class TypeOfRenderer
|
|
17
|
+
include Renderable
|
|
18
|
+
|
|
15
19
|
def initialize(out:)
|
|
16
20
|
@out = out
|
|
17
21
|
end
|
|
18
22
|
|
|
19
|
-
def render(result, format:)
|
|
20
|
-
case format
|
|
21
|
-
when "text" then render_text(result)
|
|
22
|
-
when "json" then render_json(result)
|
|
23
|
-
else
|
|
24
|
-
raise OptionParser::InvalidArgument, "unsupported format: #{format}"
|
|
25
|
-
end
|
|
26
|
-
end
|
|
27
|
-
|
|
28
23
|
private
|
|
29
24
|
|
|
30
25
|
def render_text(result)
|
|
@@ -9,6 +9,7 @@ require_relative "../inference/coverage_scanner"
|
|
|
9
9
|
require_relative "../scope"
|
|
10
10
|
require_relative "type_scan_renderer"
|
|
11
11
|
require_relative "type_scan_report"
|
|
12
|
+
require_relative "command"
|
|
12
13
|
|
|
13
14
|
module Rigor
|
|
14
15
|
class CLI
|
|
@@ -19,21 +20,15 @@ module Rigor
|
|
|
19
20
|
# the inference engine's directly recognized classes. It is the project's
|
|
20
21
|
# primary CI gate for tracking how much of an input source the engine can
|
|
21
22
|
# name without falling back to `Dynamic[Top]`.
|
|
22
|
-
class TypeScanCommand
|
|
23
|
+
class TypeScanCommand < Command
|
|
23
24
|
USAGE = "Usage: rigor type-scan [options] PATH..."
|
|
24
25
|
|
|
25
26
|
LocatedEvent = Data.define(:file, :event)
|
|
26
27
|
|
|
27
|
-
def initialize(argv:, out:, err:)
|
|
28
|
-
@argv = argv
|
|
29
|
-
@out = out
|
|
30
|
-
@err = err
|
|
31
|
-
end
|
|
32
|
-
|
|
33
28
|
# @return [Integer] CLI exit status.
|
|
34
29
|
def run
|
|
35
30
|
options = parse_options
|
|
36
|
-
paths = collect_paths(@argv)
|
|
31
|
+
paths = collect_paths(@argv, command_name: "type-scan")
|
|
37
32
|
return CLI::EXIT_USAGE if paths.nil?
|
|
38
33
|
return usage_error if paths.empty?
|
|
39
34
|
|
|
@@ -67,21 +62,6 @@ module Rigor
|
|
|
67
62
|
options
|
|
68
63
|
end
|
|
69
64
|
|
|
70
|
-
def collect_paths(args)
|
|
71
|
-
paths = []
|
|
72
|
-
args.each do |arg|
|
|
73
|
-
if File.directory?(arg)
|
|
74
|
-
paths.concat(Dir.glob(File.join(arg, "**/*.rb")))
|
|
75
|
-
elsif File.file?(arg)
|
|
76
|
-
paths << arg
|
|
77
|
-
else
|
|
78
|
-
@err.puts("type-scan: not a file or directory: #{arg}")
|
|
79
|
-
return nil
|
|
80
|
-
end
|
|
81
|
-
end
|
|
82
|
-
paths.uniq
|
|
83
|
-
end
|
|
84
|
-
|
|
85
65
|
def usage_error
|
|
86
66
|
@err.puts("type-scan: at least one path is required")
|
|
87
67
|
@err.puts(USAGE)
|
|
@@ -3,6 +3,8 @@
|
|
|
3
3
|
require "json"
|
|
4
4
|
require "optionparser"
|
|
5
5
|
|
|
6
|
+
require_relative "renderable"
|
|
7
|
+
|
|
6
8
|
module Rigor
|
|
7
9
|
class CLI
|
|
8
10
|
# Renders a `TypeScanCommand::Report` as either a terminal-friendly text
|
|
@@ -11,19 +13,12 @@ module Rigor
|
|
|
11
13
|
# the two formats stay in lockstep; that pairing is why this class is a
|
|
12
14
|
# bit longer than the default class-length budget.
|
|
13
15
|
class TypeScanRenderer
|
|
16
|
+
include Renderable
|
|
17
|
+
|
|
14
18
|
def initialize(out:)
|
|
15
19
|
@out = out
|
|
16
20
|
end
|
|
17
21
|
|
|
18
|
-
def render(report, format:)
|
|
19
|
-
case format
|
|
20
|
-
when "text" then render_text(report)
|
|
21
|
-
when "json" then render_json(report)
|
|
22
|
-
else
|
|
23
|
-
raise OptionParser::InvalidArgument, "unsupported format: #{format}"
|
|
24
|
-
end
|
|
25
|
-
end
|
|
26
|
-
|
|
27
22
|
private
|
|
28
23
|
|
|
29
24
|
def render_text(report)
|