rigortype 0.2.7 → 0.2.8
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 +1 -1
- data/docs/manual/04-diagnostics.md +7 -4
- data/docs/manual/06-baseline.md +5 -2
- data/docs/manual/15-type-protection-coverage.md +6 -4
- data/lib/rigor/analysis/baseline.rb +56 -96
- data/lib/rigor/analysis/buffer_binding.rb +13 -20
- data/lib/rigor/analysis/check_rules/always_truthy_condition_collector.rb +29 -48
- data/lib/rigor/analysis/check_rules/dead_assignment_collector.rb +35 -54
- data/lib/rigor/analysis/check_rules/ivar_write_collector.rb +23 -37
- data/lib/rigor/analysis/check_rules/main_pass_collector.rb +16 -23
- data/lib/rigor/analysis/check_rules/rule_walk.rb +48 -71
- data/lib/rigor/analysis/check_rules/self_closedness_scanner.rb +19 -28
- data/lib/rigor/analysis/check_rules/unreachable_clause_collector.rb +48 -67
- data/lib/rigor/analysis/check_rules.rb +174 -274
- data/lib/rigor/analysis/dependency_recorder.rb +26 -38
- data/lib/rigor/analysis/dependency_source_inference/boundary_cross_reporter.rb +13 -25
- data/lib/rigor/analysis/dependency_source_inference/builder.rb +17 -30
- data/lib/rigor/analysis/dependency_source_inference/gem_resolver.rb +15 -26
- data/lib/rigor/analysis/dependency_source_inference/index.rb +45 -76
- data/lib/rigor/analysis/dependency_source_inference/return_type_heuristic.rb +23 -40
- data/lib/rigor/analysis/dependency_source_inference/walker.rb +50 -83
- data/lib/rigor/analysis/dependency_source_inference.rb +6 -10
- data/lib/rigor/analysis/diagnostic.rb +48 -75
- data/lib/rigor/analysis/erb_template_detector.rb +13 -18
- data/lib/rigor/analysis/fact_store.rb +12 -22
- data/lib/rigor/analysis/incremental.rb +47 -63
- data/lib/rigor/analysis/incremental_session.rb +74 -100
- data/lib/rigor/analysis/project_scan.rb +12 -23
- data/lib/rigor/analysis/result.rb +3 -5
- data/lib/rigor/analysis/rule_catalog.rb +67 -95
- data/lib/rigor/analysis/run_stats.rb +38 -61
- data/lib/rigor/analysis/runner/diagnostic_aggregator.rb +101 -160
- data/lib/rigor/analysis/runner/pool_coordinator.rb +121 -188
- data/lib/rigor/analysis/runner/project_pre_passes.rb +62 -96
- data/lib/rigor/analysis/runner/run_snapshots.rb +12 -18
- data/lib/rigor/analysis/runner.rb +220 -333
- data/lib/rigor/analysis/self_call_resolution_recorder.rb +30 -43
- data/lib/rigor/analysis/worker_session.rb +64 -102
- data/lib/rigor/ast/type_node.rb +6 -8
- data/lib/rigor/ast.rb +6 -10
- data/lib/rigor/bleeding_edge.rb +27 -35
- data/lib/rigor/builtins/hkt_builtins.rb +7 -14
- data/lib/rigor/builtins/imported_refinements.rb +106 -149
- data/lib/rigor/builtins/predefined_constant_refinements.rb +51 -65
- data/lib/rigor/builtins/regex_refinement.rb +32 -43
- data/lib/rigor/builtins/static_return_refinements.rb +57 -81
- data/lib/rigor/cache/descriptor.rb +58 -94
- data/lib/rigor/cache/incremental_snapshot.rb +32 -48
- data/lib/rigor/cache/rbs_cache_producer.rb +10 -15
- data/lib/rigor/cache/rbs_class_ancestor_table.rb +10 -16
- data/lib/rigor/cache/rbs_class_type_param_names.rb +10 -16
- data/lib/rigor/cache/rbs_constant_table.rb +8 -13
- data/lib/rigor/cache/rbs_descriptor.rb +8 -13
- data/lib/rigor/cache/rbs_environment.rb +11 -19
- data/lib/rigor/cache/rbs_environment_marshal_patch.rb +11 -19
- data/lib/rigor/cache/rbs_known_class_names.rb +8 -12
- data/lib/rigor/cache/store.rb +96 -151
- data/lib/rigor/cli/annotate_command.rb +51 -88
- data/lib/rigor/cli/baseline_command.rb +10 -17
- data/lib/rigor/cli/check_command.rb +121 -199
- data/lib/rigor/cli/check_runner_factory.rb +8 -14
- data/lib/rigor/cli/ci_detector.rb +9 -14
- data/lib/rigor/cli/command.rb +8 -14
- data/lib/rigor/cli/coverage_command.rb +19 -26
- data/lib/rigor/cli/coverage_mutation.rb +14 -19
- data/lib/rigor/cli/coverage_report.rb +0 -2
- data/lib/rigor/cli/coverage_scan.rb +7 -11
- data/lib/rigor/cli/diagnostic_formats.rb +43 -62
- data/lib/rigor/cli/diff_command.rb +10 -20
- data/lib/rigor/cli/docs_command.rb +26 -40
- data/lib/rigor/cli/doctor_command.rb +8 -9
- data/lib/rigor/cli/explain_command.rb +4 -7
- data/lib/rigor/cli/fused_protection_renderer.rb +4 -5
- data/lib/rigor/cli/fused_protection_report.rb +6 -8
- data/lib/rigor/cli/lsp_command.rb +11 -19
- data/lib/rigor/cli/mcp_command.rb +4 -6
- data/lib/rigor/cli/mutation_protection_renderer.rb +3 -4
- data/lib/rigor/cli/mutation_protection_report.rb +6 -9
- data/lib/rigor/cli/options.rb +11 -19
- data/lib/rigor/cli/plugin_command.rb +18 -30
- data/lib/rigor/cli/plugins_command.rb +29 -51
- data/lib/rigor/cli/plugins_renderer.rb +12 -20
- data/lib/rigor/cli/prism_colorizer.rb +13 -19
- data/lib/rigor/cli/protection_renderer.rb +5 -7
- data/lib/rigor/cli/protection_report.rb +27 -17
- data/lib/rigor/cli/renderable.rb +4 -6
- data/lib/rigor/cli/show_bleedingedge_command.rb +5 -8
- data/lib/rigor/cli/sig_gen_command.rb +14 -26
- data/lib/rigor/cli/skill_command.rb +30 -47
- data/lib/rigor/cli/skill_describe.rb +40 -64
- data/lib/rigor/cli/trace_command.rb +9 -16
- data/lib/rigor/cli/trace_renderer.rb +35 -51
- data/lib/rigor/cli/triage_command.rb +6 -10
- data/lib/rigor/cli/triage_renderer.rb +1 -2
- data/lib/rigor/cli/type_of_command.rb +14 -23
- data/lib/rigor/cli/type_of_renderer.rb +3 -5
- data/lib/rigor/cli/type_scan_command.rb +6 -10
- data/lib/rigor/cli/type_scan_renderer.rb +3 -5
- data/lib/rigor/cli/type_scan_report.rb +2 -3
- data/lib/rigor/cli/upgrade_command.rb +2 -3
- data/lib/rigor/cli.rb +16 -28
- data/lib/rigor/config_audit.rb +35 -44
- data/lib/rigor/configuration/dependencies.rb +41 -70
- data/lib/rigor/configuration/severity_profile.rb +28 -42
- data/lib/rigor/configuration.rb +150 -241
- data/lib/rigor/environment/bundle_sig_discovery.rb +74 -118
- data/lib/rigor/environment/class_registry.rb +19 -26
- data/lib/rigor/environment/constant_type_cache_holder.rb +14 -21
- data/lib/rigor/environment/hkt_registry_holder.rb +6 -12
- data/lib/rigor/environment/lockfile_resolver.rb +31 -47
- data/lib/rigor/environment/rbs_collection_discovery.rb +39 -63
- data/lib/rigor/environment/rbs_coverage_report.rb +24 -42
- data/lib/rigor/environment/rbs_loader.rb +420 -518
- data/lib/rigor/environment/reflection.rb +28 -53
- data/lib/rigor/environment/reporters.rb +13 -24
- data/lib/rigor/environment.rb +176 -291
- data/lib/rigor/flow_contribution/conflict.rb +16 -27
- data/lib/rigor/flow_contribution/element.rb +7 -12
- data/lib/rigor/flow_contribution/fact.rb +33 -57
- data/lib/rigor/flow_contribution/merge_result.rb +6 -9
- data/lib/rigor/flow_contribution/merger.rb +32 -47
- data/lib/rigor/flow_contribution.rb +37 -55
- data/lib/rigor/inference/acceptance.rb +133 -219
- data/lib/rigor/inference/block_parameter_binder.rb +52 -82
- data/lib/rigor/inference/body_fixpoint.rb +22 -30
- data/lib/rigor/inference/budget_trace.rb +43 -63
- data/lib/rigor/inference/builtins/array_catalog.rb +4 -6
- data/lib/rigor/inference/builtins/comparable_catalog.rb +4 -6
- data/lib/rigor/inference/builtins/complex_catalog.rb +13 -19
- data/lib/rigor/inference/builtins/date_catalog.rb +38 -67
- data/lib/rigor/inference/builtins/encoding_catalog.rb +22 -36
- data/lib/rigor/inference/builtins/enumerable_catalog.rb +4 -6
- data/lib/rigor/inference/builtins/exception_catalog.rb +38 -57
- data/lib/rigor/inference/builtins/hash_catalog.rb +9 -14
- data/lib/rigor/inference/builtins/method_catalog.rb +29 -48
- data/lib/rigor/inference/builtins/numeric_catalog.rb +13 -18
- data/lib/rigor/inference/builtins/pathname_catalog.rb +8 -13
- data/lib/rigor/inference/builtins/proc_catalog.rb +38 -62
- data/lib/rigor/inference/builtins/random_catalog.rb +21 -32
- data/lib/rigor/inference/builtins/range_catalog.rb +12 -21
- data/lib/rigor/inference/builtins/rational_catalog.rb +9 -15
- data/lib/rigor/inference/builtins/re_catalog.rb +29 -48
- data/lib/rigor/inference/builtins/set_catalog.rb +18 -27
- data/lib/rigor/inference/builtins/string_catalog.rb +12 -19
- data/lib/rigor/inference/builtins/struct_catalog.rb +18 -30
- data/lib/rigor/inference/builtins/time_catalog.rb +28 -44
- data/lib/rigor/inference/closure_escape_analyzer.rb +44 -67
- data/lib/rigor/inference/coverage_scanner.rb +13 -18
- data/lib/rigor/inference/def_return_typer.rb +8 -14
- data/lib/rigor/inference/dynamic_origin.rb +23 -20
- data/lib/rigor/inference/expression_typer.rb +774 -1152
- data/lib/rigor/inference/fallback.rb +8 -12
- data/lib/rigor/inference/fallback_tracer.rb +4 -10
- data/lib/rigor/inference/flow_tracer.rb +25 -36
- data/lib/rigor/inference/hkt_body.rb +45 -68
- data/lib/rigor/inference/hkt_body_parser.rb +33 -52
- data/lib/rigor/inference/hkt_reducer.rb +38 -59
- data/lib/rigor/inference/hkt_registry.rb +50 -76
- data/lib/rigor/inference/indexed_narrowing.rb +55 -81
- data/lib/rigor/inference/macro_block_self_type.rb +21 -34
- data/lib/rigor/inference/method_dispatcher/array_to_h_folding.rb +11 -18
- data/lib/rigor/inference/method_dispatcher/block_folding.rb +49 -83
- data/lib/rigor/inference/method_dispatcher/call_context.rb +16 -23
- data/lib/rigor/inference/method_dispatcher/cgi_folding.rb +13 -22
- data/lib/rigor/inference/method_dispatcher/constant_folding.rb +307 -454
- data/lib/rigor/inference/method_dispatcher/data_folding.rb +24 -34
- data/lib/rigor/inference/method_dispatcher/file_folding.rb +22 -37
- data/lib/rigor/inference/method_dispatcher/iterator_dispatch.rb +52 -81
- data/lib/rigor/inference/method_dispatcher/kernel_dispatch.rb +51 -78
- data/lib/rigor/inference/method_dispatcher/literal_string_folding.rb +60 -97
- data/lib/rigor/inference/method_dispatcher/math_folding.rb +23 -34
- data/lib/rigor/inference/method_dispatcher/member_shape_projection.rb +18 -26
- data/lib/rigor/inference/method_dispatcher/method_folding.rb +37 -71
- data/lib/rigor/inference/method_dispatcher/overload_selector.rb +114 -182
- data/lib/rigor/inference/method_dispatcher/rbs_dispatch.rb +122 -193
- data/lib/rigor/inference/method_dispatcher/receiver_affinity.rb +15 -24
- data/lib/rigor/inference/method_dispatcher/reduce_folding.rb +64 -93
- data/lib/rigor/inference/method_dispatcher/regexp_folding.rb +31 -45
- data/lib/rigor/inference/method_dispatcher/set_folding.rb +6 -9
- data/lib/rigor/inference/method_dispatcher/shape_dispatch.rb +356 -511
- data/lib/rigor/inference/method_dispatcher/shellwords_folding.rb +22 -32
- data/lib/rigor/inference/method_dispatcher/singleton_folding.rb +15 -21
- data/lib/rigor/inference/method_dispatcher/struct_folding.rb +66 -82
- data/lib/rigor/inference/method_dispatcher/time_folding.rb +10 -15
- data/lib/rigor/inference/method_dispatcher/uri_folding.rb +9 -13
- data/lib/rigor/inference/method_dispatcher.rb +378 -524
- data/lib/rigor/inference/method_parameter_binder.rb +81 -114
- data/lib/rigor/inference/multi_target_binder.rb +51 -68
- data/lib/rigor/inference/mutation_widening.rb +115 -158
- data/lib/rigor/inference/narrowing.rb +509 -727
- data/lib/rigor/inference/origin_lookup.rb +38 -0
- data/lib/rigor/inference/parameter_inference_collector.rb +100 -100
- data/lib/rigor/inference/precision_scanner.rb +26 -35
- data/lib/rigor/inference/project_patched_methods.rb +20 -32
- data/lib/rigor/inference/project_patched_scanner.rb +23 -37
- data/lib/rigor/inference/protection_scanner.rb +16 -18
- data/lib/rigor/inference/rbs_type_translator.rb +49 -66
- data/lib/rigor/inference/scope_indexer.rb +473 -817
- data/lib/rigor/inference/statement_evaluator.rb +706 -1111
- data/lib/rigor/inference/struct_fold_safety.rb +34 -48
- data/lib/rigor/inference/synthetic_method.rb +9 -16
- data/lib/rigor/inference/synthetic_method_index.rb +20 -35
- data/lib/rigor/inference/synthetic_method_scanner.rb +63 -103
- data/lib/rigor/language_server/buffer_resolution.rb +6 -9
- data/lib/rigor/language_server/buffer_table.rb +11 -18
- data/lib/rigor/language_server/completion_provider.rb +69 -116
- data/lib/rigor/language_server/debouncer.rb +16 -25
- data/lib/rigor/language_server/diagnostic_publisher.rb +26 -40
- data/lib/rigor/language_server/document_symbol_provider.rb +14 -19
- data/lib/rigor/language_server/folding_range_provider.rb +11 -19
- data/lib/rigor/language_server/hover_provider.rb +8 -11
- data/lib/rigor/language_server/hover_renderer.rb +56 -93
- data/lib/rigor/language_server/loop.rb +14 -19
- data/lib/rigor/language_server/project_context.rb +46 -74
- data/lib/rigor/language_server/selection_range_provider.rb +9 -12
- data/lib/rigor/language_server/server.rb +55 -83
- data/lib/rigor/language_server/signature_help_provider.rb +29 -46
- data/lib/rigor/language_server/synchronized_writer.rb +4 -7
- data/lib/rigor/language_server/uri.rb +8 -13
- data/lib/rigor/language_server.rb +4 -6
- data/lib/rigor/mcp/loop.rb +2 -3
- data/lib/rigor/mcp/server.rb +4 -7
- data/lib/rigor/mcp.rb +3 -6
- data/lib/rigor/plugin/access_denied_error.rb +5 -8
- data/lib/rigor/plugin/additional_initializer.rb +21 -31
- data/lib/rigor/plugin/base.rb +335 -518
- data/lib/rigor/plugin/blueprint.rb +14 -23
- data/lib/rigor/plugin/box.rb +18 -29
- data/lib/rigor/plugin/fact_store.rb +16 -26
- data/lib/rigor/plugin/inflector.rb +37 -53
- data/lib/rigor/plugin/io_boundary.rb +33 -56
- data/lib/rigor/plugin/isolation.rb +42 -55
- data/lib/rigor/plugin/load_error.rb +10 -15
- data/lib/rigor/plugin/loader.rb +30 -49
- data/lib/rigor/plugin/macro/block_as_method.rb +20 -32
- data/lib/rigor/plugin/macro/heredoc_template.rb +35 -58
- data/lib/rigor/plugin/macro/nested_class_template.rb +22 -36
- data/lib/rigor/plugin/macro/trait_registry.rb +34 -51
- data/lib/rigor/plugin/macro.rb +10 -15
- data/lib/rigor/plugin/manifest.rb +85 -144
- data/lib/rigor/plugin/node_context.rb +14 -22
- data/lib/rigor/plugin/node_rule_walk.rb +49 -74
- data/lib/rigor/plugin/protocol_contract.rb +25 -39
- data/lib/rigor/plugin/registry.rb +132 -205
- data/lib/rigor/plugin/services.rb +21 -33
- data/lib/rigor/plugin/source_rbs_synthesis_reporter.rb +11 -19
- data/lib/rigor/plugin/trust_policy.rb +24 -38
- data/lib/rigor/plugin/type_node_resolver.rb +15 -24
- data/lib/rigor/protection/diagnostic_oracle.rb +11 -13
- data/lib/rigor/protection/mutation_scanner.rb +27 -35
- data/lib/rigor/protection/mutator.rb +50 -70
- data/lib/rigor/protection/test_suite_oracle.rb +20 -27
- data/lib/rigor/rbs_extended/conformance_checker.rb +48 -68
- data/lib/rigor/rbs_extended/hkt_directives.rb +28 -54
- data/lib/rigor/rbs_extended/reporter.rb +24 -40
- data/lib/rigor/rbs_extended.rb +107 -197
- data/lib/rigor/reflection.rb +68 -86
- data/lib/rigor/scope/discovery_index.rb +14 -19
- data/lib/rigor/scope.rb +255 -310
- data/lib/rigor/sig_gen/classification.rb +6 -10
- data/lib/rigor/sig_gen/generator.rb +197 -323
- data/lib/rigor/sig_gen/layout_index.rb +12 -20
- data/lib/rigor/sig_gen/method_candidate.rb +12 -17
- data/lib/rigor/sig_gen/observation_collector.rb +38 -70
- data/lib/rigor/sig_gen/observed_call.rb +13 -23
- data/lib/rigor/sig_gen/path_mapper.rb +17 -29
- data/lib/rigor/sig_gen/renderer.rb +7 -13
- data/lib/rigor/sig_gen/type_elaborator.rb +15 -28
- data/lib/rigor/sig_gen/write_result.rb +8 -16
- data/lib/rigor/sig_gen/writer.rb +95 -174
- data/lib/rigor/sig_gen.rb +3 -6
- data/lib/rigor/signature_path_audit.rb +24 -30
- data/lib/rigor/source/constant_path.rb +10 -14
- data/lib/rigor/source/literals.rb +31 -45
- data/lib/rigor/source/node_locator.rb +9 -11
- data/lib/rigor/source/node_walker.rb +9 -13
- data/lib/rigor/source.rb +3 -4
- data/lib/rigor/testing.rb +16 -20
- data/lib/rigor/triage/catalogue.rb +38 -62
- data/lib/rigor/triage.rb +31 -52
- data/lib/rigor/trinary.rb +9 -13
- data/lib/rigor/type/acceptance_router.rb +4 -6
- data/lib/rigor/type/accepts_result.rb +10 -14
- data/lib/rigor/type/app.rb +19 -27
- data/lib/rigor/type/bot.rb +4 -6
- data/lib/rigor/type/bound_method.rb +10 -15
- data/lib/rigor/type/combinator.rb +165 -257
- data/lib/rigor/type/constant.rb +23 -34
- data/lib/rigor/type/data_class.rb +10 -15
- data/lib/rigor/type/data_instance.rb +14 -20
- data/lib/rigor/type/difference.rb +21 -32
- data/lib/rigor/type/dynamic.rb +3 -5
- data/lib/rigor/type/hash_shape.rb +32 -18
- data/lib/rigor/type/integer_range.rb +11 -16
- data/lib/rigor/type/intersection.rb +27 -42
- data/lib/rigor/type/nominal.rb +10 -15
- data/lib/rigor/type/plain_lattice.rb +9 -13
- data/lib/rigor/type/refined.rb +67 -114
- data/lib/rigor/type/singleton.rb +4 -6
- data/lib/rigor/type/struct_class.rb +11 -16
- data/lib/rigor/type/struct_instance.rb +15 -21
- data/lib/rigor/type/tuple.rb +14 -19
- data/lib/rigor/type/union.rb +30 -42
- data/lib/rigor/type_node/generic.rb +14 -26
- data/lib/rigor/type_node/identifier.rb +12 -19
- data/lib/rigor/type_node.rb +3 -12
- data/lib/rigor/value_semantics.rb +16 -21
- data/lib/rigor/version.rb +1 -1
- data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable/analyzer.rb +18 -30
- data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable/channel_discoverer.rb +16 -29
- data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable/channel_index.rb +12 -23
- data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable.rb +22 -39
- data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer/analyzer.rb +26 -46
- data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer/mailer_discoverer.rb +51 -87
- data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer/mailer_index.rb +10 -17
- data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer.rb +26 -39
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/analyzer.rb +118 -176
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/controller_discoverer.rb +30 -51
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/controller_index.rb +57 -92
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack.rb +71 -108
- data/plugins/rigor-activejob/lib/rigor/plugin/activejob/analyzer.rb +13 -22
- data/plugins/rigor-activejob/lib/rigor/plugin/activejob/job_discoverer.rb +15 -25
- data/plugins/rigor-activejob/lib/rigor/plugin/activejob/job_index.rb +8 -13
- data/plugins/rigor-activejob/lib/rigor/plugin/activejob.rb +16 -24
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/analyzer.rb +30 -47
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/model_discoverer.rb +79 -129
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/model_index.rb +36 -57
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/schema_parser.rb +27 -41
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/schema_table.rb +16 -24
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord.rb +148 -235
- data/plugins/rigor-activerecord/lib/rigor-activerecord.rb +3 -5
- data/plugins/rigor-activestorage/lib/rigor/plugin/activestorage/analyzer.rb +8 -14
- data/plugins/rigor-activestorage/lib/rigor/plugin/activestorage/attachment_discoverer.rb +9 -15
- data/plugins/rigor-activestorage/lib/rigor/plugin/activestorage/attachment_index.rb +4 -7
- data/plugins/rigor-activestorage/lib/rigor/plugin/activestorage.rb +23 -41
- data/plugins/rigor-activestorage/lib/rigor-activestorage.rb +3 -5
- data/plugins/rigor-activesupport-core-ext/lib/rigor/plugin/activesupport_core_ext.rb +6 -10
- data/plugins/rigor-activesupport-core-ext/lib/rigor-activesupport-core-ext.rb +8 -11
- data/plugins/rigor-devise/lib/rigor/plugin/devise.rb +30 -45
- data/plugins/rigor-devise/lib/rigor-devise.rb +3 -5
- data/plugins/rigor-dry-schema/lib/rigor/plugin/dry_schema/schema_scanner.rb +43 -69
- data/plugins/rigor-dry-schema/lib/rigor/plugin/dry_schema.rb +26 -35
- data/plugins/rigor-dry-schema/lib/rigor-dry-schema.rb +3 -5
- data/plugins/rigor-dry-struct/lib/rigor/plugin/dry_struct.rb +30 -47
- data/plugins/rigor-dry-struct/lib/rigor-dry-struct.rb +3 -5
- data/plugins/rigor-dry-types/lib/rigor/plugin/dry_types/alias_scanner.rb +67 -113
- data/plugins/rigor-dry-types/lib/rigor/plugin/dry_types.rb +29 -46
- data/plugins/rigor-dry-types/lib/rigor-dry-types.rb +3 -5
- data/plugins/rigor-dry-validation/lib/rigor/plugin/dry_validation/contract_scanner.rb +13 -20
- data/plugins/rigor-dry-validation/lib/rigor/plugin/dry_validation.rb +14 -23
- data/plugins/rigor-dry-validation/lib/rigor-dry-validation.rb +2 -3
- data/plugins/rigor-factorybot/lib/rigor/plugin/factorybot/analyzer.rb +22 -37
- data/plugins/rigor-factorybot/lib/rigor/plugin/factorybot/factory_discoverer.rb +28 -46
- data/plugins/rigor-factorybot/lib/rigor/plugin/factorybot/factory_index.rb +13 -17
- data/plugins/rigor-factorybot/lib/rigor/plugin/factorybot.rb +30 -47
- data/plugins/rigor-graphql/lib/rigor/plugin/graphql/type_scanner.rb +52 -80
- data/plugins/rigor-graphql/lib/rigor/plugin/graphql.rb +21 -31
- data/plugins/rigor-graphql/lib/rigor-graphql.rb +3 -5
- data/plugins/rigor-hanami/lib/rigor/plugin/hanami/action_checker.rb +11 -17
- data/plugins/rigor-hanami/lib/rigor/plugin/hanami.rb +20 -34
- data/plugins/rigor-mangrove/lib/rigor/plugin/mangrove.rb +45 -75
- data/plugins/rigor-minitest/lib/rigor/plugin/minitest/assertion_analyzer.rb +22 -36
- data/plugins/rigor-minitest/lib/rigor/plugin/minitest.rb +22 -41
- data/plugins/rigor-pundit/lib/rigor/plugin/pundit/analyzer.rb +17 -32
- data/plugins/rigor-pundit/lib/rigor/plugin/pundit/policy_discoverer.rb +6 -12
- data/plugins/rigor-pundit/lib/rigor/plugin/pundit/policy_index.rb +4 -8
- data/plugins/rigor-pundit/lib/rigor/plugin/pundit.rb +17 -32
- data/plugins/rigor-rails/lib/rigor-rails.rb +11 -18
- data/plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n/analyzer.rb +58 -92
- data/plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n/locale_index.rb +20 -34
- data/plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n/locale_loader.rb +20 -31
- data/plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n.rb +49 -82
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/analyzer.rb +55 -92
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/devise_routes.rb +51 -90
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/doorkeeper_routes.rb +17 -30
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/helper_discoverer.rb +38 -70
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/helper_table.rb +42 -74
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/routes_parser.rb +280 -497
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes.rb +54 -89
- data/plugins/rigor-rbs-inline/lib/rigor/plugin/rbs_inline.rb +32 -54
- data/plugins/rigor-rbs-inline/lib/rigor-rbs-inline.rb +8 -13
- data/plugins/rigor-rspec/lib/rigor/plugin/rspec/analyzer.rb +9 -16
- data/plugins/rigor-rspec/lib/rigor/plugin/rspec/let_scope_index.rb +24 -39
- data/plugins/rigor-rspec/lib/rigor/plugin/rspec/let_type_resolver.rb +13 -22
- data/plugins/rigor-rspec/lib/rigor/plugin/rspec/matcher_analyzer.rb +34 -56
- data/plugins/rigor-rspec/lib/rigor/plugin/rspec/scope_walker.rb +22 -42
- data/plugins/rigor-rspec/lib/rigor/plugin/rspec.rb +43 -75
- data/plugins/rigor-rspec-rails/lib/rigor/plugin/rspec_rails/have_http_status_analyzer.rb +27 -39
- data/plugins/rigor-rspec-rails/lib/rigor/plugin/rspec_rails/http_status_codes.rb +14 -19
- data/plugins/rigor-rspec-rails/lib/rigor/plugin/rspec_rails.rb +20 -34
- data/plugins/rigor-shoulda-matchers/lib/rigor/plugin/shoulda_matchers/analyzer.rb +28 -47
- data/plugins/rigor-shoulda-matchers/lib/rigor/plugin/shoulda_matchers.rb +21 -37
- data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq/analyzer.rb +13 -22
- data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq/worker_discoverer.rb +18 -31
- data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq/worker_index.rb +6 -10
- data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq.rb +14 -29
- data/plugins/rigor-sinatra/lib/rigor/plugin/sinatra.rb +23 -41
- data/plugins/rigor-sinatra/lib/rigor-sinatra.rb +3 -5
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/absurd_recognizer.rb +23 -38
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/assertion_recognizer.rb +57 -96
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/catalog.rb +22 -35
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/catalog_walker.rb +41 -69
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/method_signature.rb +6 -10
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/sig_parser.rb +24 -41
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/sigil_detector.rb +29 -50
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/type_translator.rb +55 -88
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet.rb +159 -262
- data/plugins/rigor-statesman/lib/rigor/plugin/statesman.rb +21 -29
- data/plugins/rigor-statesman/lib/rigor-statesman.rb +3 -5
- data/plugins/rigor-typescript-utility-types/lib/rigor/plugin/typescript_utility_types.rb +23 -37
- data/plugins/rigor-typescript-utility-types/lib/rigor-typescript-utility-types.rb +4 -6
- data/sig/rigor/scope.rbs +6 -0
- metadata +2 -1
|
@@ -4,20 +4,15 @@ require_relative "command"
|
|
|
4
4
|
|
|
5
5
|
module Rigor
|
|
6
6
|
class CLI
|
|
7
|
-
# `rigor skill` — discover and print the SKILL.md files
|
|
8
|
-
# bundled with the `rigortype` gem.
|
|
7
|
+
# `rigor skill` — discover and print the SKILL.md files bundled with the `rigortype` gem.
|
|
9
8
|
#
|
|
10
|
-
# Rigor ships a small set of Agent Skills under `skills/` that
|
|
11
|
-
#
|
|
12
|
-
#
|
|
13
|
-
#
|
|
14
|
-
# `mise` / `gem install` / etc. the SKILL files live inside the
|
|
15
|
-
# gem checkout — the project being analysed has no copy, so an
|
|
16
|
-
# AI agent has no a priori way to find them.
|
|
9
|
+
# Rigor ships a small set of Agent Skills under `skills/` that walk an AI coding agent through onboarding
|
|
10
|
+
# (`rigor-project-init`), baseline reduction (`rigor-baseline-reduce`), and authoring a plugin
|
|
11
|
+
# (`rigor-plugin-author`). When Rigor is installed via `mise` / `gem install` / etc. the SKILL files live inside the
|
|
12
|
+
# gem checkout — the project being analysed has no copy, so an AI agent has no a priori way to find them.
|
|
17
13
|
#
|
|
18
|
-
# Grammar (mirrors `rigor docs`): the positional slot is always a
|
|
19
|
-
# skill
|
|
20
|
-
# `list` or `path` can never be shadowed by a verb.
|
|
14
|
+
# Grammar (mirrors `rigor docs`): the positional slot is always a skill *name*; alternative outputs are flags, so a
|
|
15
|
+
# skill named `list` or `path` can never be shadowed by a verb.
|
|
21
16
|
#
|
|
22
17
|
# - `rigor skill` — list bundled skills (the default).
|
|
23
18
|
# - `rigor skill <name>` — print the SKILL.md body (header + body).
|
|
@@ -42,11 +37,9 @@ module Rigor
|
|
|
42
37
|
# the SKILL. Also spelled `describe`, and
|
|
43
38
|
# surfaced top-level as `rigor describe`.
|
|
44
39
|
#
|
|
45
|
-
# The pre-v0.3.0 verb spellings `rigor skill list` / `print <name>` /
|
|
46
|
-
#
|
|
47
|
-
#
|
|
48
|
-
# deprecations"). `describe` is a no-argument action, not a name-slot
|
|
49
|
-
# verb, so it stays first-class alongside `--describe`.
|
|
40
|
+
# The pre-v0.3.0 verb spellings `rigor skill list` / `print <name>` / `path <name>` still work but emit a stderr
|
|
41
|
+
# deprecation notice; they are removed in v0.3.0 (see docs/ROADMAP.md § "Scheduled CLI deprecations"). `describe` is
|
|
42
|
+
# a no-argument action, not a name-slot verb, so it stays first-class alongside `--describe`.
|
|
50
43
|
class SkillCommand < Command
|
|
51
44
|
USAGE = <<~USAGE
|
|
52
45
|
Usage: rigor skill [<name>] [--full <name>] [--path <name>] [--list] [--describe]
|
|
@@ -74,13 +67,12 @@ module Rigor
|
|
|
74
67
|
rigor skill path <name> -> rigor skill --path <name>
|
|
75
68
|
USAGE
|
|
76
69
|
|
|
77
|
-
# The bundled skills live at `<gem_root>/skills/`. From
|
|
78
|
-
#
|
|
70
|
+
# The bundled skills live at `<gem_root>/skills/`. From `lib/rigor/cli/skill_command.rb` that is three directories
|
|
71
|
+
# up.
|
|
79
72
|
SKILLS_ROOT = File.expand_path("../../../skills", __dir__)
|
|
80
73
|
|
|
81
|
-
# The verb subcommands the flags superseded keep working with a
|
|
82
|
-
#
|
|
83
|
-
# to the canonical advice printed and the flag it rewrites to.
|
|
74
|
+
# The verb subcommands the flags superseded keep working with a stderr deprecation notice until this version drops
|
|
75
|
+
# them. Each maps to the canonical advice printed and the flag it rewrites to.
|
|
84
76
|
LEGACY_VERB_REMOVAL = "v0.3.0"
|
|
85
77
|
LEGACY_VERBS = {
|
|
86
78
|
"list" => { old: "list", advice: "--list", flag: "--list" },
|
|
@@ -120,8 +112,8 @@ module Rigor
|
|
|
120
112
|
|
|
121
113
|
private
|
|
122
114
|
|
|
123
|
-
# Translate a deprecated verb spelling into its flag form, warning
|
|
124
|
-
#
|
|
115
|
+
# Translate a deprecated verb spelling into its flag form, warning once on stderr, so the dispatch above only
|
|
116
|
+
# handles canonical forms.
|
|
125
117
|
def rewrite_legacy_verb!
|
|
126
118
|
spec = LEGACY_VERBS[@argv.first]
|
|
127
119
|
return unless spec
|
|
@@ -159,14 +151,11 @@ module Rigor
|
|
|
159
151
|
0
|
|
160
152
|
end
|
|
161
153
|
|
|
162
|
-
# `rigor skill --full <name>` — the whole current procedure in one
|
|
163
|
-
#
|
|
164
|
-
#
|
|
165
|
-
#
|
|
166
|
-
#
|
|
167
|
-
# follows the version that shipped with the installed Rigor, without
|
|
168
|
-
# needing a file-reading tool or reading a possibly-stale co-located
|
|
169
|
-
# `references/`.
|
|
154
|
+
# `rigor skill --full <name>` — the whole current procedure in one call: the SKILL.md body followed by each
|
|
155
|
+
# `references/*.md` inline. This is what the thinned SKILL bodies point a *frozen* copy at — a vendored copy of a
|
|
156
|
+
# skill (installed via `npx skills`) may lag the gem, so re-fetching the complete body here guarantees the reader
|
|
157
|
+
# follows the version that shipped with the installed Rigor, without needing a file-reading tool or reading a
|
|
158
|
+
# possibly-stale co-located `references/`.
|
|
170
159
|
def run_full(name)
|
|
171
160
|
return usage_error("`--full` requires a skill name") if name.nil?
|
|
172
161
|
|
|
@@ -197,13 +186,10 @@ module Rigor
|
|
|
197
186
|
0
|
|
198
187
|
end
|
|
199
188
|
|
|
200
|
-
# `rigor skill --describe` (also spelled `describe`) — ADR-73's
|
|
201
|
-
#
|
|
202
|
-
#
|
|
203
|
-
#
|
|
204
|
-
# bundled skill's current frontmatter description, so the
|
|
205
|
-
# `rigor-next-steps` SKILL can route without copying any
|
|
206
|
-
# version-coupled guidance into itself.
|
|
189
|
+
# `rigor skill --describe` (also spelled `describe`) — ADR-73's live "brain", delegated to {SkillDescribe}: it
|
|
190
|
+
# probes the current project's state with cheap presence checks (it never runs `rigor check`), recommends the next
|
|
191
|
+
# skill to run, and prints every bundled skill's current frontmatter description, so the `rigor-next-steps` SKILL
|
|
192
|
+
# can route without copying any version-coupled guidance into itself.
|
|
207
193
|
def run_describe
|
|
208
194
|
require_relative "skill_describe"
|
|
209
195
|
|
|
@@ -211,10 +197,9 @@ module Rigor
|
|
|
211
197
|
0
|
|
212
198
|
end
|
|
213
199
|
|
|
214
|
-
# The header that precedes the SKILL.md body when an agent
|
|
215
|
-
#
|
|
216
|
-
#
|
|
217
|
-
# below `---` (the SKILL frontmatter marker) is unchanged.
|
|
200
|
+
# The header that precedes the SKILL.md body when an agent runs `rigor skill <name>`. Kept as `# `-prefixed
|
|
201
|
+
# comment lines so the combined output remains parseable as markdown — anything below `---` (the SKILL frontmatter
|
|
202
|
+
# marker) is unchanged.
|
|
218
203
|
def render_print_header(skill)
|
|
219
204
|
references_dir = File.join(File.dirname(skill.fetch(:path)), "references")
|
|
220
205
|
ref_line = if File.directory?(references_dir)
|
|
@@ -247,14 +232,12 @@ module Rigor
|
|
|
247
232
|
discover_skills.find { |s| s.fetch(:name) == name }
|
|
248
233
|
end
|
|
249
234
|
|
|
250
|
-
# The `references/*.md` files bundled alongside a skill, sorted so the
|
|
251
|
-
# `NN-` prefixes drive read order.
|
|
235
|
+
# The `references/*.md` files bundled alongside a skill, sorted so the `NN-` prefixes drive read order.
|
|
252
236
|
def reference_files(skill)
|
|
253
237
|
dir = File.join(File.dirname(skill.fetch(:path)), "references")
|
|
254
238
|
return [] unless File.directory?(dir)
|
|
255
239
|
|
|
256
|
-
# `Dir.glob` returns lexicographically sorted paths (Ruby 3.0+),
|
|
257
|
-
# so the `NN-` prefixes already drive read order.
|
|
240
|
+
# `Dir.glob` returns lexicographically sorted paths (Ruby 3.0+), so the `NN-` prefixes already drive read order.
|
|
258
241
|
Dir.glob(File.join(dir, "*.md"))
|
|
259
242
|
end
|
|
260
243
|
|
|
@@ -4,27 +4,21 @@ require "yaml"
|
|
|
4
4
|
|
|
5
5
|
module Rigor
|
|
6
6
|
class CLI
|
|
7
|
-
# The presence-only project-state probe behind `rigor skill describe`
|
|
8
|
-
#
|
|
9
|
-
#
|
|
10
|
-
# agent can run it freely at any point. Split from {SkillDescribe} so
|
|
11
|
-
# the routing/rendering class stays focused.
|
|
7
|
+
# The presence-only project-state probe behind `rigor skill describe` (ADR-73 WD2). It stats files and opens only
|
|
8
|
+
# config / CI / lockfiles — it runs no analysis — so it is cheap and side-effect-free, and an agent can run it
|
|
9
|
+
# freely at any point. Split from {SkillDescribe} so the routing/rendering class stays focused.
|
|
12
10
|
class ProjectStateProbe
|
|
13
|
-
# Config / baseline filenames the probe stats for. `CONFIG_FILENAMES`
|
|
14
|
-
#
|
|
15
|
-
#
|
|
16
|
-
# `rigor check` would auto-discover.
|
|
11
|
+
# Config / baseline filenames the probe stats for. `CONFIG_FILENAMES` mirrors `Configuration::DISCOVERY_ORDER`
|
|
12
|
+
# (developer-local override first, committed default second) so the probe agrees with what `rigor check` would
|
|
13
|
+
# auto-discover.
|
|
17
14
|
CONFIG_FILENAMES = %w[.rigor.yml .rigor.dist.yml].freeze
|
|
18
15
|
BASELINE_FILENAME = ".rigor-baseline.yml"
|
|
19
|
-
# `rbs collection install`'s lockfile — Rigor auto-detects it at the
|
|
20
|
-
#
|
|
21
|
-
# paths, so its presence is the signal that the gem-RBS gap has been
|
|
22
|
-
# addressed.
|
|
16
|
+
# `rbs collection install`'s lockfile — Rigor auto-detects it at the project root and feeds each gem's community
|
|
17
|
+
# RBS into the signature paths, so its presence is the signal that the gem-RBS gap has been addressed.
|
|
23
18
|
RBS_COLLECTION_LOCKFILE = "rbs_collection.lock.yaml"
|
|
24
19
|
|
|
25
|
-
# `Gemfile.lock` substrings that mark a Rails app, and the bundled
|
|
26
|
-
# Rails
|
|
27
|
-
# that has not enabled any Rails plugin (a `rigor-plugin-tune` cue).
|
|
20
|
+
# `Gemfile.lock` substrings that mark a Rails app, and the bundled Rails-family plugin ids — used to spot a
|
|
21
|
+
# configured Rails project that has not enabled any Rails plugin (a `rigor-plugin-tune` cue).
|
|
28
22
|
RAILS_LOCK_MARKERS = %w[railties actionpack activerecord actioncable].freeze
|
|
29
23
|
RAILS_PLUGIN_MARKERS = %w[
|
|
30
24
|
rigor-activerecord rigor-actionpack rigor-actionmailer rigor-activejob
|
|
@@ -53,11 +47,9 @@ module Rigor
|
|
|
53
47
|
|
|
54
48
|
private
|
|
55
49
|
|
|
56
|
-
# True when Rails is in `Gemfile.lock` but the (present) config
|
|
57
|
-
#
|
|
58
|
-
#
|
|
59
|
-
# would (the 20260620 field trial's strap case). Only fires on an
|
|
60
|
-
# already-configured project; an un-configured Rails app routes to
|
|
50
|
+
# True when Rails is in `Gemfile.lock` but the (present) config enables no Rails plugin — so `rigor-plugin-tune`
|
|
51
|
+
# (wiring the ActiveRecord / routes / i18n plugins) buys more than community RBS would (the 20260620 field trial's
|
|
52
|
+
# strap case). Only fires on an already-configured project; an un-configured Rails app routes to
|
|
61
53
|
# `rigor-project-init`, which selects the plugins itself.
|
|
62
54
|
def rails_unconfigured?(config)
|
|
63
55
|
return false if config.nil?
|
|
@@ -75,8 +67,7 @@ module Rigor
|
|
|
75
67
|
false
|
|
76
68
|
end
|
|
77
69
|
|
|
78
|
-
# `:wired` (a CI config mentions `rigor`), `:unwired` (a CI config
|
|
79
|
-
# exists but does not), or `:none`.
|
|
70
|
+
# `:wired` (a CI config mentions `rigor`), `:unwired` (a CI config exists but does not), or `:none`.
|
|
80
71
|
def ci_state
|
|
81
72
|
files = ci_config_files
|
|
82
73
|
return :none if files.empty?
|
|
@@ -84,11 +75,9 @@ module Rigor
|
|
|
84
75
|
files.any? { |path| file_mentions_rigor?(path) } ? :wired : :unwired
|
|
85
76
|
end
|
|
86
77
|
|
|
87
|
-
# Editor-LSP signal — `:wired` (a committed `.vscode/` config names
|
|
88
|
-
#
|
|
89
|
-
#
|
|
90
|
-
# configs live in the user's home, not the repo, so editor-setup is
|
|
91
|
-
# otherwise a catalogue-only destination.
|
|
78
|
+
# Editor-LSP signal — `:wired` (a committed `.vscode/` config names `rigor`), `:unwired` (a `.vscode/` is present
|
|
79
|
+
# but does not), or `:none`. Only VS Code is detectable here: Neovim / Emacs / Helix configs live in the user's
|
|
80
|
+
# home, not the repo, so editor-setup is otherwise a catalogue-only destination.
|
|
92
81
|
def editor_state
|
|
93
82
|
vscode = File.join(@root, ".vscode")
|
|
94
83
|
return :none unless File.directory?(vscode)
|
|
@@ -99,11 +88,9 @@ module Rigor
|
|
|
99
88
|
files.any? { |path| file_mentions_rigor?(path) } ? :wired : :unwired
|
|
100
89
|
end
|
|
101
90
|
|
|
102
|
-
# MCP-client signal — `:wired` (a committed project MCP config names
|
|
103
|
-
#
|
|
104
|
-
#
|
|
105
|
-
# `.cursor/mcp.json`); user-level client configs live in $HOME, so
|
|
106
|
-
# mcp-setup is otherwise a catalogue-only destination.
|
|
91
|
+
# MCP-client signal — `:wired` (a committed project MCP config names `rigor`), `:unwired` (one is present but does
|
|
92
|
+
# not), or `:none`. Only the project-scoped configs are detectable (`.mcp.json`, `.cursor/mcp.json`); user-level
|
|
93
|
+
# client configs live in $HOME, so mcp-setup is otherwise a catalogue-only destination.
|
|
107
94
|
def mcp_state
|
|
108
95
|
files = [".mcp.json", File.join(".cursor", "mcp.json")]
|
|
109
96
|
.map { |rel| File.join(@root, rel) }
|
|
@@ -127,20 +114,16 @@ module Rigor
|
|
|
127
114
|
end
|
|
128
115
|
end
|
|
129
116
|
|
|
130
|
-
# Builds the `rigor skill describe` report (ADR-73): a {ProjectStateProbe}
|
|
131
|
-
#
|
|
132
|
-
#
|
|
133
|
-
# {SkillCommand} so the command stays a thin dispatcher and this — the
|
|
134
|
-
# "live brain" — owns the routing.
|
|
117
|
+
# Builds the `rigor skill describe` report (ADR-73): a {ProjectStateProbe} snapshot, a recommended next skill, and
|
|
118
|
+
# the live catalogue of bundled skills with their current frontmatter descriptions. Extracted from {SkillCommand} so
|
|
119
|
+
# the command stays a thin dispatcher and this — the "live brain" — owns the routing.
|
|
135
120
|
class SkillDescribe
|
|
136
|
-
# The entry-point SKILL itself — excluded from the catalogue because
|
|
137
|
-
# it is the skill being run, not a destination.
|
|
121
|
+
# The entry-point SKILL itself — excluded from the catalogue because it is the skill being run, not a destination.
|
|
138
122
|
ENTRY_POINT_SKILL = "rigor-next-steps"
|
|
139
123
|
|
|
140
|
-
# Adoption-journey order for the catalogue and the order the
|
|
141
|
-
#
|
|
142
|
-
#
|
|
143
|
-
# agent can offer at any point, never a presence-recommended step.
|
|
124
|
+
# Adoption-journey order for the catalogue and the order the recommendation decision tree walks. `rigor-ask` sits
|
|
125
|
+
# last: it is the journey-agnostic "answer a question about Rigor" companion the agent can offer at any point,
|
|
126
|
+
# never a presence-recommended step.
|
|
144
127
|
CATALOG_ORDER = %w[
|
|
145
128
|
rigor-project-init
|
|
146
129
|
rigor-rbs-setup
|
|
@@ -180,17 +163,16 @@ module Rigor
|
|
|
180
163
|
|
|
181
164
|
private
|
|
182
165
|
|
|
183
|
-
# The skills offered as "what to do next", in adoption-journey order.
|
|
184
|
-
#
|
|
185
|
-
# the known journey, alphabetically.
|
|
166
|
+
# The skills offered as "what to do next", in adoption-journey order. The entry-point skill is excluded, and
|
|
167
|
+
# unknown skills sort after the known journey, alphabetically.
|
|
186
168
|
def catalog_skills
|
|
187
169
|
@skills
|
|
188
170
|
.reject { |skill| skill.fetch(:name) == ENTRY_POINT_SKILL }
|
|
189
171
|
.sort_by { |skill| [CATALOG_ORDER.index(skill.fetch(:name)) || CATALOG_ORDER.size, skill.fetch(:name)] }
|
|
190
172
|
end
|
|
191
173
|
|
|
192
|
-
# The decision tree (ADR-73 WD2). Returns `{ skill:, reason: }` for
|
|
193
|
-
#
|
|
174
|
+
# The decision tree (ADR-73 WD2). Returns `{ skill:, reason: }` for the recommended next step, or nil when no
|
|
175
|
+
# catalogue skill matches.
|
|
194
176
|
def recommend(state, catalog)
|
|
195
177
|
name, reason = recommended_name_and_reason(state)
|
|
196
178
|
skill = catalog.find { |candidate| candidate.fetch(:name) == name }
|
|
@@ -208,14 +190,11 @@ module Rigor
|
|
|
208
190
|
["rigor-rbs-setup", "your gems ship no community RBS yet — install it so Rigor stops typing them as Dynamic."]
|
|
209
191
|
elsif state.fetch(:ci) != :wired
|
|
210
192
|
["rigor-ci-setup", "Rigor is configured but not wired into CI — lock in the regression guard."]
|
|
211
|
-
# A present baseline is deliberately NOT a recommendation trigger.
|
|
212
|
-
#
|
|
213
|
-
#
|
|
214
|
-
#
|
|
215
|
-
#
|
|
216
|
-
# `rigor-baseline-reduce` stays in the catalogue for the intermediate
|
|
217
|
-
# user who *chooses* to invest in it; the headline routes to genuinely
|
|
218
|
-
# additive steps instead.
|
|
193
|
+
# A present baseline is deliberately NOT a recommendation trigger. A baseline is a healthy, finished onboarding
|
|
194
|
+
# state, not a problem to work off; pushing every baselined project to "reduce it" turns a working build into a
|
|
195
|
+
# chore and tempts scattering `# rigor:disable` through the code to make a number go down — means over ends.
|
|
196
|
+
# `rigor-baseline-reduce` stays in the catalogue for the intermediate user who *chooses* to invest in it; the
|
|
197
|
+
# headline routes to genuinely additive steps instead.
|
|
219
198
|
elsif state.fetch(:editor) == :unwired
|
|
220
199
|
["rigor-editor-setup",
|
|
221
200
|
"you have an editor config but no Rigor LSP — wire `rigor lsp` for live diagnostics and hover types."]
|
|
@@ -314,10 +293,8 @@ module Rigor
|
|
|
314
293
|
PROMPT
|
|
315
294
|
end
|
|
316
295
|
|
|
317
|
-
# The one-line essence of a skill's frontmatter `description`, read
|
|
318
|
-
#
|
|
319
|
-
# to the shipped skill. Drops the `Triggers:` / `NOT for` tail and
|
|
320
|
-
# caps the length.
|
|
296
|
+
# The one-line essence of a skill's frontmatter `description`, read live from the SKILL.md so the catalogue can
|
|
297
|
+
# never go stale relative to the shipped skill. Drops the `Triggers:` / `NOT for` tail and caps the length.
|
|
321
298
|
def catalog_blurb(path)
|
|
322
299
|
text = frontmatter(path).fetch("description", "").to_s.strip.tr("\n", " ").squeeze(" ")
|
|
323
300
|
head = text.split(/\s*Triggers?:/, 2).first.to_s.strip
|
|
@@ -331,9 +308,8 @@ module Rigor
|
|
|
331
308
|
"#{(text[0, limit] || '').rstrip}…"
|
|
332
309
|
end
|
|
333
310
|
|
|
334
|
-
# Parse a SKILL.md's leading `---`-delimited YAML frontmatter. Returns
|
|
335
|
-
#
|
|
336
|
-
# name-only entry rather than raising.
|
|
311
|
+
# Parse a SKILL.md's leading `---`-delimited YAML frontmatter. Returns {} on any missing or malformed block so the
|
|
312
|
+
# catalogue degrades to a name-only entry rather than raising.
|
|
337
313
|
def frontmatter(path)
|
|
338
314
|
text = File.read(path)
|
|
339
315
|
return {} unless text.start_with?("---\n")
|
|
@@ -15,22 +15,17 @@ require_relative "trace_renderer"
|
|
|
15
15
|
|
|
16
16
|
module Rigor
|
|
17
17
|
class CLI
|
|
18
|
-
# Executes the `rigor trace` command: re-runs the inference engine
|
|
19
|
-
#
|
|
20
|
-
#
|
|
21
|
-
#
|
|
22
|
-
# as a step-through terminal animation. A teaching probe: it shows
|
|
23
|
-
# HOW Rigor arrives at a type, where `rigor type-of` shows only the
|
|
24
|
-
# answer.
|
|
18
|
+
# Executes the `rigor trace` command: re-runs the inference engine over one file under
|
|
19
|
+
# {Rigor::Inference::FlowTracer} and replays the recorded event stream — scope binds, union formation, method
|
|
20
|
+
# dispatch, and (with `--verbose`) every expression enter/result — as a step-through terminal animation. A teaching
|
|
21
|
+
# probe: it shows HOW Rigor arrives at a type, where `rigor type-of` shows only the answer.
|
|
25
22
|
#
|
|
26
|
-
# The tracer is observational; this command never changes what
|
|
27
|
-
# `rigor check` would infer for the same file.
|
|
23
|
+
# The tracer is observational; this command never changes what `rigor check` would infer for the same file.
|
|
28
24
|
class TraceCommand < Command
|
|
29
25
|
USAGE = "Usage: rigor trace [options] FILE"
|
|
30
26
|
|
|
31
|
-
# Default frame kinds: the three teachable moments. :enter/:result
|
|
32
|
-
#
|
|
33
|
-
# opts into them.
|
|
27
|
+
# Default frame kinds: the three teachable moments. :enter/:result add one frame per literal and drown the signal;
|
|
28
|
+
# `--verbose` opts into them.
|
|
34
29
|
DEFAULT_KINDS = %i[bind union dispatch].freeze
|
|
35
30
|
VERBOSE_KINDS = (%i[enter result] + DEFAULT_KINDS).freeze
|
|
36
31
|
|
|
@@ -95,10 +90,8 @@ module Rigor
|
|
|
95
90
|
0
|
|
96
91
|
end
|
|
97
92
|
|
|
98
|
-
# Mirrors the single-file path `rigor type-of` takes: a
|
|
99
|
-
#
|
|
100
|
-
# statement-level evaluation of the whole program — but recorded
|
|
101
|
-
# under the FlowTracer.
|
|
93
|
+
# Mirrors the single-file path `rigor type-of` takes: a project-aware environment, an empty seed scope, one
|
|
94
|
+
# statement-level evaluation of the whole program — but recorded under the FlowTracer.
|
|
102
95
|
def record_events(root, file, configuration)
|
|
103
96
|
environment = Environment.for_project(
|
|
104
97
|
libraries: configuration.libraries,
|
|
@@ -4,24 +4,19 @@ require_relative "prism_colorizer"
|
|
|
4
4
|
|
|
5
5
|
module Rigor
|
|
6
6
|
class CLI
|
|
7
|
-
# Replays a `Rigor::Inference::FlowTracer` event stream as a terminal
|
|
8
|
-
#
|
|
9
|
-
#
|
|
10
|
-
#
|
|
11
|
-
# events), and an event panel describing the current step and the
|
|
12
|
-
# expression stack.
|
|
7
|
+
# Replays a `Rigor::Inference::FlowTracer` event stream as a terminal animation for `rigor trace`. Each frame draws
|
|
8
|
+
# a box-drawn screen: the source panel (syntax-coloured, current node range underlined) side-by-side with the scope
|
|
9
|
+
# panel (locals accumulated from :bind events), and an event panel describing the current step and the expression
|
|
10
|
+
# stack.
|
|
13
11
|
#
|
|
14
|
-
# The frame is fitted to the measured terminal: the source panel
|
|
15
|
-
#
|
|
16
|
-
#
|
|
17
|
-
#
|
|
18
|
-
# keeps a minimum height of {EVENT_PANE_MIN} rows so the narration
|
|
19
|
-
# never collapses to a sliver.
|
|
12
|
+
# The frame is fitted to the measured terminal: the source panel scrolls vertically (a window centred on the line
|
|
13
|
+
# under evaluation) instead of overflowing the screen height, over-long rows are clipped with an ellipsis instead of
|
|
14
|
+
# wrapping, and the event panel keeps a minimum height of {EVENT_PANE_MIN} rows so the narration never collapses to
|
|
15
|
+
# a sliver.
|
|
20
16
|
#
|
|
21
|
-
# Pure ANSI + `io/console` (both stdlib) per ADR-0's zero-runtime-
|
|
22
|
-
#
|
|
23
|
-
#
|
|
24
|
-
# 80×24 layout, which keeps the renderer deterministic under test.
|
|
17
|
+
# Pure ANSI + `io/console` (both stdlib) per ADR-0's zero-runtime-dependency policy. When the output is not a TTY
|
|
18
|
+
# the frames are printed sequentially without cursor control against a default 80×24 layout, which keeps the
|
|
19
|
+
# renderer deterministic under test.
|
|
25
20
|
class TraceRenderer
|
|
26
21
|
RESET = "\e[0m"
|
|
27
22
|
DIM = "\e[90m"
|
|
@@ -68,9 +63,8 @@ module Rigor
|
|
|
68
63
|
|
|
69
64
|
private
|
|
70
65
|
|
|
71
|
-
# `IO.console` reflects the controlling terminal even when stdout
|
|
72
|
-
#
|
|
73
|
-
# is only used for interactive replays.
|
|
66
|
+
# `IO.console` reflects the controlling terminal even when stdout is, say, a StringIO under test — which is why
|
|
67
|
+
# the measured size is only used for interactive replays.
|
|
74
68
|
def terminal_size
|
|
75
69
|
require "io/console"
|
|
76
70
|
size = IO.console&.winsize
|
|
@@ -81,9 +75,8 @@ module Rigor
|
|
|
81
75
|
DEFAULT_SIZE
|
|
82
76
|
end
|
|
83
77
|
|
|
84
|
-
# Replays :bind events into a per-frame snapshot of the locals
|
|
85
|
-
#
|
|
86
|
-
# first N events.
|
|
78
|
+
# Replays :bind events into a per-frame snapshot of the locals panel, so frame N shows exactly the bindings
|
|
79
|
+
# visible after the first N events.
|
|
87
80
|
def accumulate_locals(events)
|
|
88
81
|
locals = {}
|
|
89
82
|
events.map do |event|
|
|
@@ -109,34 +102,31 @@ module Rigor
|
|
|
109
102
|
@out.puts(bottom_border(left_width + right_width + 1))
|
|
110
103
|
end
|
|
111
104
|
|
|
112
|
-
# Screen budget: borders (3 rows) + event pane + the key-hint row
|
|
113
|
-
#
|
|
114
|
-
# source/scope body, floored at {BODY_HEIGHT_MIN}.
|
|
105
|
+
# Screen budget: borders (3 rows) + event pane + the key-hint row when stepping interactively; whatever remains
|
|
106
|
+
# belongs to the source/scope body, floored at {BODY_HEIGHT_MIN}.
|
|
115
107
|
def body_height_for(event_rows)
|
|
116
108
|
chrome = 3 + event_rows + (@interactive ? 1 : 0)
|
|
117
109
|
[@rows - chrome, BODY_HEIGHT_MIN].max
|
|
118
110
|
end
|
|
119
111
|
|
|
120
|
-
# The event pane keeps at least {EVENT_PANE_MIN} rows (padding with
|
|
121
|
-
#
|
|
122
|
-
# event kinds; over-long lines are clipped to the frame width.
|
|
112
|
+
# The event pane keeps at least {EVENT_PANE_MIN} rows (padding with blanks) so the bottom of the frame is visually
|
|
113
|
+
# stable across event kinds; over-long lines are clipped to the frame width.
|
|
123
114
|
def event_pane_lines(event, max_width)
|
|
124
115
|
lines = [describe_event(event), stack_line(event)].compact
|
|
125
116
|
lines << "" while lines.size < EVENT_PANE_MIN
|
|
126
117
|
lines.map { |line| clip(line, max_width) }
|
|
127
118
|
end
|
|
128
119
|
|
|
129
|
-
# Source rows win the width contest; the scope column keeps its
|
|
130
|
-
#
|
|
120
|
+
# Source rows win the width contest; the scope column keeps its minimum and absorbs whatever the source does not
|
|
121
|
+
# need.
|
|
131
122
|
def column_widths(source_rows, inner)
|
|
132
123
|
left_need = (source_rows.map { |raw, _| raw.length }.max || 0) + 1
|
|
133
124
|
left_width = left_need.clamp(24, [inner - SCOPE_WIDTH_MIN, 24].max)
|
|
134
125
|
[left_width, [inner - left_width, SCOPE_WIDTH_MIN].max]
|
|
135
126
|
end
|
|
136
127
|
|
|
137
|
-
# Vertical scroll: when the panel is taller than the window, keep
|
|
138
|
-
#
|
|
139
|
-
# the marker row directly follows).
|
|
128
|
+
# Vertical scroll: when the panel is taller than the window, keep a slice centred on the row under evaluation (the
|
|
129
|
+
# `▶` row, which the marker row directly follows).
|
|
140
130
|
def scroll_window(rows, height)
|
|
141
131
|
return rows if rows.size <= height
|
|
142
132
|
|
|
@@ -149,15 +139,14 @@ module Rigor
|
|
|
149
139
|
rows.map do |raw, painted|
|
|
150
140
|
next [raw, painted] if raw.length <= width
|
|
151
141
|
|
|
152
|
-
# Re-paint from the clipped raw text — clipping the painted
|
|
153
|
-
# string directly would cut ANSI escapes in half.
|
|
142
|
+
# Re-paint from the clipped raw text — clipping the painted string directly would cut ANSI escapes in half.
|
|
154
143
|
clipped = clip(raw, width)
|
|
155
144
|
[clipped, repaint_clipped(clipped)]
|
|
156
145
|
end
|
|
157
146
|
end
|
|
158
147
|
|
|
159
|
-
# A clipped row loses its highlight/marker alignment guarantees, so
|
|
160
|
-
#
|
|
148
|
+
# A clipped row loses its highlight/marker alignment guarantees, so it is repainted with plain syntax colouring
|
|
149
|
+
# (gutter dimmed).
|
|
161
150
|
def repaint_clipped(clipped)
|
|
162
151
|
gutter = clipped[0, 6]
|
|
163
152
|
rest = clipped[6..] || ""
|
|
@@ -175,9 +164,8 @@ module Rigor
|
|
|
175
164
|
"#{text[0, [width - 1, 0].max]}…"
|
|
176
165
|
end
|
|
177
166
|
|
|
178
|
-
# Each row is `[raw_text, painted_text]` so width math runs on the
|
|
179
|
-
#
|
|
180
|
-
# `▔▔▔` marker row injected after it.
|
|
167
|
+
# Each row is `[raw_text, painted_text]` so width math runs on the escape-free string. The row under the event's
|
|
168
|
+
# source range gets a `▔▔▔` marker row injected after it.
|
|
181
169
|
def source_panel_rows(event)
|
|
182
170
|
rows = []
|
|
183
171
|
location = event.location
|
|
@@ -200,9 +188,8 @@ module Rigor
|
|
|
200
188
|
[indent + ("▔" * width), indent + BOLD + ("▔" * width) + RESET]
|
|
201
189
|
end
|
|
202
190
|
|
|
203
|
-
# Highlights the in-range slice with reverse video; everything else
|
|
204
|
-
#
|
|
205
|
-
# slice (not the colorized string) so byte offsets stay honest.
|
|
191
|
+
# Highlights the in-range slice with reverse video; everything else gets Prism syntax colouring. The highlight is
|
|
192
|
+
# applied on the raw slice (not the colorized string) so byte offsets stay honest.
|
|
206
193
|
def paint_line(line, location, number)
|
|
207
194
|
return PrismColorizer.colorize(line) unless location && number == location[:start_line]
|
|
208
195
|
|
|
@@ -214,9 +201,8 @@ module Rigor
|
|
|
214
201
|
PrismColorizer.colorize(after).chomp
|
|
215
202
|
end
|
|
216
203
|
|
|
217
|
-
# Prism columns are BYTE columns — split the line with byteslice
|
|
218
|
-
#
|
|
219
|
-
# overrun) the highlight range. Returns `[before, slice, after]`.
|
|
204
|
+
# Prism columns are BYTE columns — split the line with byteslice so a multibyte character earlier on the line
|
|
205
|
+
# cannot shift (or overrun) the highlight range. Returns `[before, slice, after]`.
|
|
220
206
|
def split_at_range(line, location)
|
|
221
207
|
from = [location[:start_column], line.bytesize].min
|
|
222
208
|
to = location[:end_line] == location[:start_line] ? [location[:end_column], line.bytesize].min : line.bytesize
|
|
@@ -262,8 +248,7 @@ module Rigor
|
|
|
262
248
|
"┌#{pad_label("─ #{@file} ", left)}┬#{pad_label('─ scope ', right)}┐"
|
|
263
249
|
end
|
|
264
250
|
|
|
265
|
-
# Pads `label` with `─` to exactly `width` cells (truncating an
|
|
266
|
-
# over-long label so the frame never breaks).
|
|
251
|
+
# Pads `label` with `─` to exactly `width` cells (truncating an over-long label so the frame never breaks).
|
|
267
252
|
def pad_label(label, width)
|
|
268
253
|
label = "#{label[0, width - 2]} " if label.length > width
|
|
269
254
|
label + ("─" * [width - label.length, 0].max)
|
|
@@ -289,9 +274,8 @@ module Rigor
|
|
|
289
274
|
"└#{'─' * width}┘"
|
|
290
275
|
end
|
|
291
276
|
|
|
292
|
-
# Single-keystroke stepping via stdlib io/console; falls back to
|
|
293
|
-
#
|
|
294
|
-
# still claim to be TTYs). Returns false when the user quits.
|
|
277
|
+
# Single-keystroke stepping via stdlib io/console; falls back to line-buffered Enter when raw mode is unavailable
|
|
278
|
+
# (e.g. pipes that still claim to be TTYs). Returns false when the user quits.
|
|
295
279
|
def next_frame?
|
|
296
280
|
@out.print("#{DIM} [any key: next · q: quit]#{RESET}")
|
|
297
281
|
key = read_key
|
|
@@ -14,12 +14,10 @@ module Rigor
|
|
|
14
14
|
class CLI
|
|
15
15
|
# ADR-23 — executes `rigor triage`.
|
|
16
16
|
#
|
|
17
|
-
# Runs the same analysis as `rigor check`, then summarises the
|
|
18
|
-
#
|
|
19
|
-
#
|
|
20
|
-
#
|
|
21
|
-
# writes a baseline. Always exits 0 — it is an inspection
|
|
22
|
-
# command, not a gate (`rigor check` remains the gate).
|
|
17
|
+
# Runs the same analysis as `rigor check`, then summarises the diagnostic stream (rule distribution, per-file
|
|
18
|
+
# hotspots, heuristic hints) instead of printing the raw per-line list. Read-only and advisory (WD4): never edits
|
|
19
|
+
# config, never writes a baseline. Always exits 0 — it is an inspection command, not a gate (`rigor check` remains
|
|
20
|
+
# the gate).
|
|
23
21
|
class TriageCommand < Command
|
|
24
22
|
USAGE = "Usage: rigor triage [options] [paths]"
|
|
25
23
|
DEFAULT_SECTIONS = %i[distribution selectors hotspots hints].freeze
|
|
@@ -70,10 +68,8 @@ module Rigor
|
|
|
70
68
|
raise OptionParser::InvalidArgument, "unsupported format: #{options.fetch(:format)}"
|
|
71
69
|
end
|
|
72
70
|
|
|
73
|
-
# Sequential, cache-backed, no run-stats: triage only needs
|
|
74
|
-
# the
|
|
75
|
-
# distribution deterministic (the fork pool's cross-file
|
|
76
|
-
# divergence would skew the histogram).
|
|
71
|
+
# Sequential, cache-backed, no run-stats: triage only needs the diagnostic stream, and sequential keeps the rule
|
|
72
|
+
# distribution deterministic (the fork pool's cross-file divergence would skew the histogram).
|
|
77
73
|
def analyze(configuration)
|
|
78
74
|
runner = Analysis::Runner.new(
|
|
79
75
|
configuration: configuration,
|
|
@@ -6,8 +6,7 @@ require_relative "../triage"
|
|
|
6
6
|
|
|
7
7
|
module Rigor
|
|
8
8
|
class CLI
|
|
9
|
-
# ADR-23 — renders a {Rigor::Triage::Report} as the `rigor
|
|
10
|
-
# triage` text report or as `--format json`.
|
|
9
|
+
# ADR-23 — renders a {Rigor::Triage::Report} as the `rigor triage` text report or as `--format json`.
|
|
11
10
|
class TriageRenderer
|
|
12
11
|
BAR_WIDTH = 24
|
|
13
12
|
SELECTOR_ROWS = 15 # text-output cap; `--format json` carries the full list
|