roam-code 12.7.0__tar.gz → 12.8.0__tar.gz
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.
- {roam_code-12.7.0/src/roam_code.egg-info → roam_code-12.8.0}/PKG-INFO +29 -8
- {roam_code-12.7.0 → roam_code-12.8.0}/README.md +28 -7
- {roam_code-12.7.0 → roam_code-12.8.0}/pyproject.toml +1 -2
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/ask/recipes.py +22 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/catalog/python_idioms.py +81 -10
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/cli.py +4 -0
- roam_code-12.8.0/src/roam/commands/cmd_hover.py +130 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/commands/cmd_preflight.py +14 -1
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/commands/cmd_py_modern.py +8 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/commands/cmd_py_types.py +27 -0
- roam_code-12.8.0/src/roam/commands/cmd_pytest_fixtures.py +352 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/commands/cmd_search.py +0 -1
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/commands/cmd_supply_chain.py +6 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/commands/cmd_taint.py +8 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/commands/cmd_tour.py +12 -3
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/commands/cmd_ws.py +0 -2
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/commands/resolve.py +42 -3
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/competitor_site_data.py +6 -3
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/index/indexer.py +12 -0
- roam_code-12.8.0/src/roam/index/pytest_fixtures.py +281 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/index/relations.py +44 -4
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/mcp_extras/progress.py +0 -1
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/mcp_server.py +49 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/output/sarif.py +192 -0
- {roam_code-12.7.0 → roam_code-12.8.0/src/roam_code.egg-info}/PKG-INFO +29 -8
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam_code.egg-info/SOURCES.txt +7 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/tests/test_ask.py +5 -3
- {roam_code-12.7.0 → roam_code-12.8.0}/tests/test_batch_mcp.py +0 -1
- {roam_code-12.7.0 → roam_code-12.8.0}/tests/test_conventions_cmd.py +1 -2
- {roam_code-12.7.0 → roam_code-12.8.0}/tests/test_defer_loading.py +3 -0
- roam_code-12.8.0/tests/test_detector_precision.py +162 -0
- roam_code-12.8.0/tests/test_doc_consistency.py +273 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/tests/test_doctor.py +2 -3
- {roam_code-12.7.0 → roam_code-12.8.0}/tests/test_duplicates.py +1 -2
- {roam_code-12.7.0 → roam_code-12.8.0}/tests/test_foxpro.py +0 -2
- roam_code-12.8.0/tests/test_hover.py +84 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/tests/test_math.py +0 -1
- {roam_code-12.7.0 → roam_code-12.8.0}/tests/test_next_steps.py +3 -1
- roam_code-12.8.0/tests/test_pytest_fixtures.py +375 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/tests/test_rule_profiles.py +0 -1
- {roam_code-12.7.0 → roam_code-12.8.0}/tests/test_suggest_reviewers.py +0 -5
- {roam_code-12.7.0 → roam_code-12.8.0}/tests/test_trends.py +0 -1
- {roam_code-12.7.0 → roam_code-12.8.0}/LICENSE +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/setup.cfg +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/__init__.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/__main__.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/analysis/__init__.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/analysis/effects.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/analysis/taint.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/api.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/ask/__init__.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/ask/classifier.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/ask/runner.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/attest/__init__.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/attest/cga.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/bridges/__init__.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/bridges/base.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/bridges/bridge_config.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/bridges/bridge_django.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/bridges/bridge_protobuf.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/bridges/bridge_rest_api.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/bridges/bridge_salesforce.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/bridges/bridge_template.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/bridges/registry.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/catalog/__init__.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/catalog/detectors.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/catalog/fixes.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/catalog/smells.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/catalog/tasks.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/commands/__init__.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/commands/changed_files.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/commands/cmd_adrs.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/commands/cmd_adversarial.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/commands/cmd_affected.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/commands/cmd_affected_tests.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/commands/cmd_agent_context.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/commands/cmd_agent_export.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/commands/cmd_agent_plan.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/commands/cmd_ai_ratio.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/commands/cmd_ai_readiness.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/commands/cmd_alerts.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/commands/cmd_annotate.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/commands/cmd_api_changes.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/commands/cmd_api_drift.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/commands/cmd_ask.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/commands/cmd_attest.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/commands/cmd_auth_gaps.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/commands/cmd_bisect.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/commands/cmd_breaking.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/commands/cmd_budget.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/commands/cmd_bus_factor.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/commands/cmd_capsule.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/commands/cmd_cga.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/commands/cmd_check_rules.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/commands/cmd_ci_setup.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/commands/cmd_clean.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/commands/cmd_clones.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/commands/cmd_closure.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/commands/cmd_clusters.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/commands/cmd_codeowners.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/commands/cmd_complexity.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/commands/cmd_config.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/commands/cmd_congestion.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/commands/cmd_context.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/commands/cmd_conventions.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/commands/cmd_coupling.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/commands/cmd_coverage_gaps.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/commands/cmd_critique.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/commands/cmd_cut.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/commands/cmd_dark_matter.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/commands/cmd_dashboard.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/commands/cmd_dead.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/commands/cmd_debt.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/commands/cmd_deps.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/commands/cmd_describe.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/commands/cmd_dev_profile.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/commands/cmd_diagnose.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/commands/cmd_diff.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/commands/cmd_doc_staleness.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/commands/cmd_docs_coverage.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/commands/cmd_doctor.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/commands/cmd_drift.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/commands/cmd_duplicates.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/commands/cmd_effects.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/commands/cmd_endpoints.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/commands/cmd_entry_points.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/commands/cmd_eval_retrieve.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/commands/cmd_fan.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/commands/cmd_file.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/commands/cmd_fingerprint.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/commands/cmd_fitness.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/commands/cmd_flag_dead.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/commands/cmd_fleet.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/commands/cmd_fn_coupling.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/commands/cmd_forecast.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/commands/cmd_grep.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/commands/cmd_guard.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/commands/cmd_health.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/commands/cmd_hooks.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/commands/cmd_hotspots.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/commands/cmd_impact.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/commands/cmd_index.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/commands/cmd_index_bundle.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/commands/cmd_ingest_trace.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/commands/cmd_init.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/commands/cmd_intent.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/commands/cmd_invariants.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/commands/cmd_layers.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/commands/cmd_map.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/commands/cmd_math.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/commands/cmd_mcp_setup.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/commands/cmd_metrics.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/commands/cmd_migration_safety.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/commands/cmd_minimap.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/commands/cmd_missing_index.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/commands/cmd_module.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/commands/cmd_mutate.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/commands/cmd_n1.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/commands/cmd_oracle.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/commands/cmd_orchestrate.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/commands/cmd_orphan_routes.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/commands/cmd_over_fetch.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/commands/cmd_owner.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/commands/cmd_partition.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/commands/cmd_path_coverage.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/commands/cmd_patterns.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/commands/cmd_plan.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/commands/cmd_plan_refactor.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/commands/cmd_pr_diff.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/commands/cmd_pr_risk.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/commands/cmd_relate.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/commands/cmd_report.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/commands/cmd_reset.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/commands/cmd_retrieve.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/commands/cmd_risk.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/commands/cmd_rules.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/commands/cmd_safe_delete.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/commands/cmd_safe_zones.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/commands/cmd_sbom.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/commands/cmd_schema.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/commands/cmd_search_semantic.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/commands/cmd_secrets.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/commands/cmd_semantic_diff.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/commands/cmd_simulate.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/commands/cmd_simulate_departure.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/commands/cmd_sketch.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/commands/cmd_smells.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/commands/cmd_spectral.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/commands/cmd_split.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/commands/cmd_suggest_refactoring.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/commands/cmd_suggest_reviewers.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/commands/cmd_symbol.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/commands/cmd_syntax_check.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/commands/cmd_test_gaps.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/commands/cmd_test_scaffold.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/commands/cmd_testmap.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/commands/cmd_trace.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/commands/cmd_trends.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/commands/cmd_triage.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/commands/cmd_understand.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/commands/cmd_uses.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/commands/cmd_verify.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/commands/cmd_verify_imports.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/commands/cmd_vibe_check.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/commands/cmd_visualize.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/commands/cmd_vuln_map.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/commands/cmd_vuln_reach.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/commands/cmd_vulns.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/commands/cmd_watch.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/commands/cmd_weather.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/commands/cmd_why.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/commands/cmd_xlang.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/commands/codeowners_helpers.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/commands/context_helpers.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/commands/gate_presets.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/commands/graph_helpers.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/commands/metrics_history.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/commands/next_steps.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/commands/suppression.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/config.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/coverage_reports.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/critique/__init__.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/critique/aggregator.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/critique/checks.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/db/__init__.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/db/connection.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/db/queries.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/db/schema.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/eval/__init__.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/eval/harness.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/exit_codes.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/fleet/__init__.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/fleet/adapters.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/fleet/manifest.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/git_utils.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/graph/__init__.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/graph/anomaly.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/graph/builder.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/graph/clone_detect.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/graph/clusters.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/graph/cycles.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/graph/dark_matter.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/graph/diff.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/graph/fingerprint.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/graph/layers.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/graph/pagerank.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/graph/partition.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/graph/pathfinding.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/graph/propagation.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/graph/simulate.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/graph/spectral.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/graph/stats.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/index/__init__.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/index/complexity.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/index/discovery.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/index/django_post.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/index/file_roles.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/index/git_stats.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/index/gitignore.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/index/incremental.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/index/parser.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/index/symbols.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/index/test_conventions.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/languages/__init__.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/languages/apex_lang.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/languages/aura_lang.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/languages/base.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/languages/c_lang.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/languages/csharp_lang.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/languages/extractor_schema.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/languages/foxpro_lang.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/languages/generic_lang.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/languages/go_lang.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/languages/hcl_lang.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/languages/java_lang.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/languages/javascript_lang.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/languages/kotlin_lang.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/languages/php_lang.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/languages/python_lang.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/languages/query_engine.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/languages/registry.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/languages/ruby_lang.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/languages/rust_lang.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/languages/scala_lang.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/languages/sfxml_lang.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/languages/sql_lang.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/languages/swift_lang.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/languages/typescript_lang.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/languages/visualforce_lang.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/languages/yaml_lang.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/mcp_extras/__init__.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/mcp_extras/completions.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/mcp_extras/sampling.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/mcp_extras/session.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/mcp_extras/watcher.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/output/__init__.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/output/file_role_hints.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/output/formatter.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/output/mermaid.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/output/schema_registry.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/plugins.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/refactor/__init__.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/refactor/codegen.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/refactor/transforms.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/retrieve/__init__.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/retrieve/learned_ranker.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/retrieve/pipeline.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/retrieve/rerank.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/retrieve/seeds.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/retrieve/semantic.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/rules/__init__.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/rules/ast_match.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/rules/builtin.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/rules/dataflow.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/rules/engine.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/runtime/__init__.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/runtime/daemon.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/runtime/graph_backend.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/runtime/hotspots.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/runtime/lock_modes.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/runtime/lockmgr.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/runtime/trace_ingest.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/search/__init__.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/search/framework_packs.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/search/index_embeddings.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/search/onnx_embeddings.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/search/tfidf.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/security/__init__.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/security/aibom_extension.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/security/taint_classifier.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/security/taint_engine.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/security/vuln_reach.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/security/vuln_store.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/surface_counts.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/templates/__init__.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/templates/ci/__init__.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/workspace/__init__.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/workspace/aggregator.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/workspace/api_scanner.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/workspace/config.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam/workspace/db.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam_code.egg-info/dependency_links.txt +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam_code.egg-info/entry_points.txt +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam_code.egg-info/requires.txt +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/src/roam_code.egg-info/top_level.txt +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/tests/test_adrs.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/tests/test_adversarial.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/tests/test_affected.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/tests/test_agent_export.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/tests/test_agent_mode.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/tests/test_agent_plan_context.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/tests/test_ai_ratio.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/tests/test_ai_readiness.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/tests/test_alerts_cmd.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/tests/test_annotations.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/tests/test_anomaly.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/tests/test_api_changes.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/tests/test_api_drift.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/tests/test_attest.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/tests/test_auth_gaps.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/tests/test_backend_fixes_round2.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/tests/test_backend_fixes_round3.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/tests/test_basic.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/tests/test_bisect.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/tests/test_bridge_django.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/tests/test_bridges.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/tests/test_bridges_extended.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/tests/test_budget.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/tests/test_budget_flag.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/tests/test_budget_phase2.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/tests/test_bus_factor.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/tests/test_capsule.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/tests/test_cga.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/tests/test_check_rules.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/tests/test_ci_gate_eval.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/tests/test_ci_sarif_guard.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/tests/test_ci_setup.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/tests/test_clones.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/tests/test_closure.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/tests/test_codeowners.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/tests/test_commands_architecture.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/tests/test_commands_exploration.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/tests/test_commands_health.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/tests/test_commands_refactoring.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/tests/test_commands_workflow.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/tests/test_competitor_site_data.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/tests/test_comprehensive.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/tests/test_config.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/tests/test_congestion.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/tests/test_context_propagation.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/tests/test_coverage_gaps_cmd.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/tests/test_coverage_ingestion.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/tests/test_critique.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/tests/test_cut.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/tests/test_dark_matter.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/tests/test_dark_matter_helpers.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/tests/test_dashboard.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/tests/test_dataflow_dead.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/tests/test_dead_aging.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/tests/test_demo_gif_asset.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/tests/test_describe.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/tests/test_detail_flag_hints.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/tests/test_deterministic_output.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/tests/test_dev_profile.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/tests/test_difficulty_scoring.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/tests/test_doc_staleness.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/tests/test_docker_assets.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/tests/test_docs_coverage.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/tests/test_docs_site_quality.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/tests/test_drift.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/tests/test_effects.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/tests/test_effects_propagation.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/tests/test_endpoints.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/tests/test_entry_points_cmd.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/tests/test_eval_retrieve.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/tests/test_exclude_patterns.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/tests/test_exit_codes.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/tests/test_extractor_grammar_drift.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/tests/test_fallback_contracts.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/tests/test_file_roles.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/tests/test_fingerprint.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/tests/test_fixes.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/tests/test_flag_dead.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/tests/test_fleet.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/tests/test_fn_coupling.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/tests/test_forecast.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/tests/test_formatters.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/tests/test_framework_detection.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/tests/test_gate_presets.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/tests/test_git_utils.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/tests/test_guard.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/tests/test_health_gate.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/tests/test_hooks.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/tests/test_hotspots.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/tests/test_index.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/tests/test_index_bundle.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/tests/test_init_cmd.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/tests/test_install_check.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/tests/test_intent.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/tests/test_invariants.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/tests/test_json_contracts.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/tests/test_json_error_envelope.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/tests/test_kotlin_swift_extractors.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/tests/test_language_corpus.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/tests/test_languages.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/tests/test_library_api.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/tests/test_math_tips.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/tests/test_mcp_extras.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/tests/test_mcp_server.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/tests/test_mcp_setup.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/tests/test_mermaid.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/tests/test_metrics_cmd.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/tests/test_migration_safety.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/tests/test_minimap.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/tests/test_missing_index.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/tests/test_mutate.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/tests/test_n1.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/tests/test_onboard.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/tests/test_oracle.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/tests/test_orchestrate.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/tests/test_orphan_routes.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/tests/test_oss_bench_harness.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/tests/test_over_fetch.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/tests/test_pagerank_truncation.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/tests/test_partition.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/tests/test_path_coverage.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/tests/test_patterns_cmd.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/tests/test_performance.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/tests/test_personalized_pagerank.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/tests/test_plan.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/tests/test_plugin_discovery.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/tests/test_pr_comment_script.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/tests/test_pr_diff.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/tests/test_pr_risk_author.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/tests/test_progress.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/tests/test_progressive_disclosure.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/tests/test_properties.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/tests/test_python_extractor_v2.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/tests/test_python_idioms_e2e.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/tests/test_python_pivot.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/tests/test_readme_surface_consistency.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/tests/test_refactoring_intelligence.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/tests/test_relate.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/tests/test_report.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/tests/test_reset_clean.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/tests/test_resolve.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/tests/test_retrieve.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/tests/test_retrieve_cross_repo.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/tests/test_retrieve_seeds.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/tests/test_risk.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/tests/test_ruby.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/tests/test_rules.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/tests/test_rules_ast_match.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/tests/test_rules_community_pack.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/tests/test_rules_dataflow.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/tests/test_rules_symbol_requirements.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/tests/test_runtime.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/tests/test_runtime_score.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/tests/test_salesforce.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/tests/test_sarif_flag.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/tests/test_sbom.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/tests/test_scala.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/tests/test_schema_versioning.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/tests/test_search_explain.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/tests/test_secrets.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/tests/test_secrets_v2.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/tests/test_semantic_diff.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/tests/test_semantic_onnx.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/tests/test_semantic_search.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/tests/test_simulate.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/tests/test_simulate_departure.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/tests/test_sketch.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/tests/test_smells.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/tests/test_smoke.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/tests/test_sna_metrics.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/tests/test_spectral.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/tests/test_split_cmd.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/tests/test_sql.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/tests/test_supply_chain.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/tests/test_surface_counts.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/tests/test_syntax_check.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/tests/test_taint.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/tests/test_taint_analysis.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/tests/test_taint_classifier.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/tests/test_test_conventions.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/tests/test_test_gaps.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/tests/test_test_scaffold.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/tests/test_testmap.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/tests/test_top_flag_consistency.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/tests/test_tour_cmd.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/tests/test_trends_cohort.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/tests/test_triage.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/tests/test_uses_cmd.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/tests/test_v12_2.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/tests/test_v6_features.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/tests/test_v71_features.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/tests/test_v7_features.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/tests/test_v82_features.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/tests/test_verify.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/tests/test_verify_imports.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/tests/test_vibe_check.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/tests/test_visualize.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/tests/test_vuln.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/tests/test_vulns_cmd.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/tests/test_watch.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/tests/test_why.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/tests/test_workspace.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/tests/test_ws_resolve_fixes.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/tests/test_xlang.py +0 -0
- {roam_code-12.7.0 → roam_code-12.8.0}/tests/test_yaml_hcl.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: roam-code
|
|
3
|
-
Version: 12.
|
|
3
|
+
Version: 12.8.0
|
|
4
4
|
Summary: Instant codebase comprehension for AI coding agents
|
|
5
5
|
Author: CosmoHac
|
|
6
6
|
License-Expression: MIT
|
|
@@ -60,9 +60,11 @@ Dynamic: license-file
|
|
|
60
60
|
|
|
61
61
|
# roam-code
|
|
62
62
|
|
|
63
|
-
**
|
|
63
|
+
**Architectural sight for AI coding agents — before they edit.**
|
|
64
64
|
|
|
65
|
-
|
|
65
|
+
A local code graph (SQLite + tree-sitter + git history) that gives any agent — Claude Code, Cursor, Aider, Continue, your own — five high-leverage verbs: `understand`, `retrieve`, `context`, `preflight`, `critique`. The other 148 specialised commands are advanced surface for specialised workflows.
|
|
66
|
+
|
|
67
|
+
*154 commands · 120 MCP tools · 27 languages · 100% local · zero API keys*
|
|
66
68
|
|
|
67
69
|
[](https://pypi.org/project/roam-code/)
|
|
68
70
|
[](https://github.com/Cranot/roam-code/stargazers)
|
|
@@ -81,7 +83,7 @@ Roam is a structural intelligence engine for software. It pre-indexes your codeb
|
|
|
81
83
|
Unlike LSPs (editor-bound, language-specific) or Sourcegraph (hosted search), Roam provides architecture-level graph queries -- offline, cross-language, and compact. It goes beyond comprehension: Roam governs architecture through budget gates, simulates refactoring outcomes, orchestrates multi-agent swarms with zero-conflict guarantees, maps vulnerability reachability paths, and enables graph-level code editing without syntax errors.
|
|
82
84
|
|
|
83
85
|
```
|
|
84
|
-
Codebase ──> [Index] ──> Semantic Graph ──>
|
|
86
|
+
Codebase ──> [Index] ──> Semantic Graph ──> 152 Commands ──> AI Agent
|
|
85
87
|
│ │ │
|
|
86
88
|
tree-sitter symbols comprehend
|
|
87
89
|
27 languages + edges govern
|
|
@@ -89,6 +91,21 @@ Codebase ──> [Index] ──> Semantic Graph ──> 139 Commands ──> AI
|
|
|
89
91
|
runtime traces + architecture orchestrate
|
|
90
92
|
```
|
|
91
93
|
|
|
94
|
+
### Start here — the 5 verbs that cover ~80% of agent workflows
|
|
95
|
+
|
|
96
|
+
```bash
|
|
97
|
+
pip install roam-code
|
|
98
|
+
|
|
99
|
+
cd your-repo/
|
|
100
|
+
roam understand # 1. landing pad — what is this codebase?
|
|
101
|
+
roam retrieve "where is auth?" # 2. graph-aware retrieval for free-form tasks
|
|
102
|
+
roam context AuthService # 3. exact files+lines to read before changing
|
|
103
|
+
roam preflight AuthService # 4. blast radius + tests + complexity check
|
|
104
|
+
git diff | roam critique # 5. patch verifier — clones-not-edited, hot-path
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
That's the full mental model. The other CLI surface — `taint`, `fleet`, `cga`, `simulate`, `mutate`, `partition`, `attest`, `eval-retrieve`, `oracle`, `py-types`, `py-modern`, `dark-matter`, `clones`, `propagation`, `fingerprint`, etc. — is advanced surface for specialised workflows; you'll never need most of them.
|
|
108
|
+
|
|
92
109
|
### The problem
|
|
93
110
|
|
|
94
111
|
Coding agents explore codebases inefficiently: dozens of grep/read cycles, high token cost, no structural understanding. Roam replaces this with one graph query:
|
|
@@ -139,7 +156,7 @@ $ roam diff # blast radius of uncommitted changes
|
|
|
139
156
|
- **`personalized_pagerank()`** in `graph/pagerank.py`: NetworkX `personalization=` wrapper with empty-seed fallback to global PR; biases ranking toward query-relevant nodes for the retrieve reranker.
|
|
140
157
|
- **`.roam/config.toml`** (new): zero-dep TOML loader (stdlib `tomllib` → `tomli` → in-tree subset parser). Tunable retrieve weights (`alpha`/`beta`/`gamma`/`delta`/`epsilon`), `tokens_per_line`, `lexical_baseline`, `first_stage_token_cap`, `default_budget`, `default_k`, `default_rerank`.
|
|
141
158
|
- **DX corrections from dogfood pass**: `roam --detail <cmd>` is the canonical group-level flag; misleading "use --detail" hints in 7 commands rewritten to point users at `roam --detail <cmd>`. `--top N` aliased on `complexity`/`algo`/`rules` (`--top 0` means unlimited on `rules`). `roam fingerprint` no longer refuses graphs ≥5,000 symbols (new soft-warn threshold 20k, hard cap 100k).
|
|
142
|
-
- **
|
|
159
|
+
- **154 CLI commands, 120 MCP tools** (`fleet`, `ask`, `cga`, `eval-retrieve` remain CLI-only; v12 exposes `roam_retrieve`, `roam_critique`, `roam_fleet_plan`, plus 5 v12.1 boolean oracles (`roam_oracle_*`), `roam_taint_classify`, `roam_pytest_fixtures`, and `roam_hover` as MCP tools). 35-tool `core` preset is the default for token-budget-conscious clients.
|
|
143
160
|
|
|
144
161
|
## What's New in v11
|
|
145
162
|
|
|
@@ -310,7 +327,7 @@ roam health
|
|
|
310
327
|
|
|
311
328
|
## Commands
|
|
312
329
|
|
|
313
|
-
**Lead with the 5 verbs.** The [5 core commands](#core-commands) cover ~80% of agent workflows: `understand`, `context`, `retrieve`, `preflight`, `critique`. The remaining
|
|
330
|
+
**Lead with the 5 verbs.** The [5 core commands](#core-commands) cover ~80% of agent workflows: `understand`, `context`, `retrieve`, `preflight`, `critique`. The remaining 149 commands are detail surface for specialised workflows (taint, fleet, cga, oracle, eval, …) — they're called by agents on demand, not memorised. This is intentional design; under the hood the canonical surface is **154 commands organised into 7 categories**, but you don't need to know that to start.
|
|
314
331
|
|
|
315
332
|
<details>
|
|
316
333
|
<summary><strong>Full command reference</strong></summary>
|
|
@@ -349,6 +366,7 @@ roam health
|
|
|
349
366
|
| `roam file <path> [--full] [--changed] [--deps-of PATH]` | File skeleton: all definitions with signatures, cognitive load index, health score |
|
|
350
367
|
| `roam symbol <name> [--full]` | Symbol definition + callers + callees + metrics. Supports `file:symbol` disambiguation |
|
|
351
368
|
| `roam context <symbol> [--task MODE] [--for-file PATH]` | AI-optimized context: definition + callers + callees + files-to-read with line ranges |
|
|
369
|
+
| `roam hover <symbol>` | One-line architectural summary: kind, location, blast-radius bucket, top caller, top callee. Bounded at ~200 tokens for IDE hover panels |
|
|
352
370
|
| `roam retrieve <task> [--budget N] [--k N] [--seed-files PATH]` | Graph-aware context for free-form tasks: FTS5 + structural rerank (PageRank + clones) + token budget |
|
|
353
371
|
| `roam critique [--input DIFF] [--intent TEXT] [--high-callers N]` | Verify a patch against the graph: clones-not-edited + blast radius + intent-vs-semantic-diff. Pipe `git diff` in. Exit 5 on high severity. |
|
|
354
372
|
| `roam fleet plan <goal> [--n-agents N] [--adapter raw\|composio\|copilot]` | Graph-aware planner: Louvain partition + co-change + PageRank anchors → `.roam-fleet.json` for Composio/Copilot CLI/raw. |
|
|
@@ -402,6 +420,7 @@ roam health
|
|
|
402
420
|
| `roam complexity [--bumpy-road] [--include-tooling]` | Per-function cognitive complexity (SonarSource-compatible, triangular nesting penalty) + Halstead metrics (volume, difficulty, effort, bugs) + cyclomatic density |
|
|
403
421
|
| `roam py-types [--detail] [--include-tests] [--ci --min-coverage N]` | Python type-annotation health: % of public functions with full annotations, ``Any`` usage, legacy ``typing.Optional/Dict/List`` (PEP 585/604 modernisation candidates), per-file worst offenders. CI-gateable via ``--ci --min-coverage N`` (exit 5 below threshold). Default-excludes test files |
|
|
404
422
|
| `roam py-modern [--detail]` | Modern-Python adoption signal: counts walrus operator (PEP 572), match statements (PEP 634), PEP 604 ``X \| None``, PEP 585 ``dict[…]``, PEP 695 type aliases, f-strings vs ``.format()``. Reports type-modernisation % and f-string adoption % to gauge migration progress |
|
|
423
|
+
| `roam pytest-fixtures [SYMBOL] [--max-depth N]` | Inventory pytest fixture chains. With no SYMBOL, prints the project-wide fixture count and the top fixtures by dependent count. With a fixture or test name, walks the implicit fixture-parameter dependency graph to show what each test transitively requires. Resolves through ``conftest.py`` chains |
|
|
405
424
|
| `roam algo [--task T] [--confidence C] [--profile P]` | Algorithm anti-pattern detection: 23-pattern catalog detects suboptimal algorithms (O(n^2) loops, N+1 queries, quadratic string building, branching recursion, loop-invariant calls) and suggests better approaches with Big-O improvements. Confidence calibration via caller-count + runtime traces, evidence paths, impact scoring, framework-aware N+1 packs, and language-aware fix templates. Alias: `roam math` |
|
|
406
425
|
| `roam n1 [--confidence C] [--verbose]` | Implicit N+1 I/O detection: finds ORM model computed properties (`$appends`/accessors) that trigger lazy-loaded DB queries in collection contexts. Cross-references with eager loading config. Supports Laravel, Django, Rails, SQLAlchemy, JPA |
|
|
407
426
|
| `roam over-fetch [--threshold N] [--confidence C]` | Detect models serializing too many fields: large `$fillable` without `$hidden`/`$visible`, direct controller returns bypassing API Resources, poor exposed-to-hidden ratio |
|
|
@@ -654,7 +673,7 @@ The sentinel pair `<!-- roam:minimap -->` / `<!-- /roam:minimap -->` is replaced
|
|
|
654
673
|
|--------|-------------|
|
|
655
674
|
| `roam --json <command>` | Structured JSON output with consistent envelope |
|
|
656
675
|
| `roam --compact <command>` | Token-efficient output: TSV tables, minimal JSON envelope |
|
|
657
|
-
| `roam --sarif <command>` | SARIF 2.1.0 output for dead, health, complexity, rules, secrets,
|
|
676
|
+
| `roam --sarif <command>` | SARIF 2.1.0 output for dead, health, complexity, rules, secrets, algo, py-types, py-modern (GitHub/CI integration) |
|
|
658
677
|
| `roam health --gate` | CI quality gate. Reads `.roam-gates.yml` thresholds. Exit code 5 on failure |
|
|
659
678
|
|
|
660
679
|
</details>
|
|
@@ -950,7 +969,7 @@ ROAM_MCP_LITE=0 roam mcp
|
|
|
950
969
|
Core preset tools: `roam_affected_tests`, `roam_batch_get`, `roam_batch_search`, `roam_complete`, `roam_complexity_report`, `roam_context`, `roam_dead_code`, `roam_deps`, `roam_diagnose`, `roam_diagnose_issue`, `roam_diff`, `roam_expand_toolset`, `roam_explore`, `roam_file_info`, `roam_health`, `roam_impact`, `roam_pr_risk`, `roam_preflight`, `roam_prepare_change`, `roam_review_change`, `roam_search_symbol`, `roam_syntax_check`, `roam_trace`, `roam_understand`, `roam_uses`.
|
|
951
970
|
|
|
952
971
|
<details>
|
|
953
|
-
<summary><strong>MCP tool list (all
|
|
972
|
+
<summary><strong>MCP tool list (all 120)</strong></summary>
|
|
954
973
|
|
|
955
974
|
| Tool | Description |
|
|
956
975
|
|------|-------------|
|
|
@@ -961,6 +980,7 @@ Core preset tools: `roam_affected_tests`, `roam_batch_get`, `roam_batch_search`,
|
|
|
961
980
|
| `roam_search_symbol` | Find symbols by name |
|
|
962
981
|
| `roam_complete` | Prefix completion for symbols/paths/commands (FTS5-backed) |
|
|
963
982
|
| `roam_context` | Files-to-read for modifying a symbol |
|
|
983
|
+
| `roam_hover` | Single-line architectural summary — kind, blast-radius bucket, top caller, top callee |
|
|
964
984
|
| `roam_retrieve` | Graph-aware context for free-form tasks (FTS5 + structural rerank + token budget) |
|
|
965
985
|
| `roam_critique` | Verify a patch against the graph (clones-not-edited + blast radius) |
|
|
966
986
|
| `roam_fleet_plan` | Plan a multi-agent fleet — graph-aware partition emits .roam-fleet.json |
|
|
@@ -984,6 +1004,7 @@ Core preset tools: `roam_affected_tests`, `roam_batch_get`, `roam_batch_search`,
|
|
|
984
1004
|
| `roam_complexity_report` | Per-symbol cognitive complexity |
|
|
985
1005
|
| `roam_py_types` | Python type-annotation health (% public typed, Any, legacy typing) |
|
|
986
1006
|
| `roam_py_modern` | Modern-Python adoption (walrus, match, PEP 604/585/695, f-strings) |
|
|
1007
|
+
| `roam_pytest_fixtures` | pytest fixture chain — top fixtures by dependent count, or per-symbol dependency walk |
|
|
987
1008
|
| `roam_tour` | Auto-generated onboarding guide |
|
|
988
1009
|
| `roam_diagnose` | Root cause analysis for debugging |
|
|
989
1010
|
| `roam_visualize` | Generate Mermaid or DOT architecture diagrams |
|
|
@@ -2,9 +2,11 @@
|
|
|
2
2
|
|
|
3
3
|
# roam-code
|
|
4
4
|
|
|
5
|
-
**
|
|
5
|
+
**Architectural sight for AI coding agents — before they edit.**
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
A local code graph (SQLite + tree-sitter + git history) that gives any agent — Claude Code, Cursor, Aider, Continue, your own — five high-leverage verbs: `understand`, `retrieve`, `context`, `preflight`, `critique`. The other 148 specialised commands are advanced surface for specialised workflows.
|
|
8
|
+
|
|
9
|
+
*154 commands · 120 MCP tools · 27 languages · 100% local · zero API keys*
|
|
8
10
|
|
|
9
11
|
[](https://pypi.org/project/roam-code/)
|
|
10
12
|
[](https://github.com/Cranot/roam-code/stargazers)
|
|
@@ -23,7 +25,7 @@ Roam is a structural intelligence engine for software. It pre-indexes your codeb
|
|
|
23
25
|
Unlike LSPs (editor-bound, language-specific) or Sourcegraph (hosted search), Roam provides architecture-level graph queries -- offline, cross-language, and compact. It goes beyond comprehension: Roam governs architecture through budget gates, simulates refactoring outcomes, orchestrates multi-agent swarms with zero-conflict guarantees, maps vulnerability reachability paths, and enables graph-level code editing without syntax errors.
|
|
24
26
|
|
|
25
27
|
```
|
|
26
|
-
Codebase ──> [Index] ──> Semantic Graph ──>
|
|
28
|
+
Codebase ──> [Index] ──> Semantic Graph ──> 152 Commands ──> AI Agent
|
|
27
29
|
│ │ │
|
|
28
30
|
tree-sitter symbols comprehend
|
|
29
31
|
27 languages + edges govern
|
|
@@ -31,6 +33,21 @@ Codebase ──> [Index] ──> Semantic Graph ──> 139 Commands ──> AI
|
|
|
31
33
|
runtime traces + architecture orchestrate
|
|
32
34
|
```
|
|
33
35
|
|
|
36
|
+
### Start here — the 5 verbs that cover ~80% of agent workflows
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
pip install roam-code
|
|
40
|
+
|
|
41
|
+
cd your-repo/
|
|
42
|
+
roam understand # 1. landing pad — what is this codebase?
|
|
43
|
+
roam retrieve "where is auth?" # 2. graph-aware retrieval for free-form tasks
|
|
44
|
+
roam context AuthService # 3. exact files+lines to read before changing
|
|
45
|
+
roam preflight AuthService # 4. blast radius + tests + complexity check
|
|
46
|
+
git diff | roam critique # 5. patch verifier — clones-not-edited, hot-path
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
That's the full mental model. The other CLI surface — `taint`, `fleet`, `cga`, `simulate`, `mutate`, `partition`, `attest`, `eval-retrieve`, `oracle`, `py-types`, `py-modern`, `dark-matter`, `clones`, `propagation`, `fingerprint`, etc. — is advanced surface for specialised workflows; you'll never need most of them.
|
|
50
|
+
|
|
34
51
|
### The problem
|
|
35
52
|
|
|
36
53
|
Coding agents explore codebases inefficiently: dozens of grep/read cycles, high token cost, no structural understanding. Roam replaces this with one graph query:
|
|
@@ -81,7 +98,7 @@ $ roam diff # blast radius of uncommitted changes
|
|
|
81
98
|
- **`personalized_pagerank()`** in `graph/pagerank.py`: NetworkX `personalization=` wrapper with empty-seed fallback to global PR; biases ranking toward query-relevant nodes for the retrieve reranker.
|
|
82
99
|
- **`.roam/config.toml`** (new): zero-dep TOML loader (stdlib `tomllib` → `tomli` → in-tree subset parser). Tunable retrieve weights (`alpha`/`beta`/`gamma`/`delta`/`epsilon`), `tokens_per_line`, `lexical_baseline`, `first_stage_token_cap`, `default_budget`, `default_k`, `default_rerank`.
|
|
83
100
|
- **DX corrections from dogfood pass**: `roam --detail <cmd>` is the canonical group-level flag; misleading "use --detail" hints in 7 commands rewritten to point users at `roam --detail <cmd>`. `--top N` aliased on `complexity`/`algo`/`rules` (`--top 0` means unlimited on `rules`). `roam fingerprint` no longer refuses graphs ≥5,000 symbols (new soft-warn threshold 20k, hard cap 100k).
|
|
84
|
-
- **
|
|
101
|
+
- **154 CLI commands, 120 MCP tools** (`fleet`, `ask`, `cga`, `eval-retrieve` remain CLI-only; v12 exposes `roam_retrieve`, `roam_critique`, `roam_fleet_plan`, plus 5 v12.1 boolean oracles (`roam_oracle_*`), `roam_taint_classify`, `roam_pytest_fixtures`, and `roam_hover` as MCP tools). 35-tool `core` preset is the default for token-budget-conscious clients.
|
|
85
102
|
|
|
86
103
|
## What's New in v11
|
|
87
104
|
|
|
@@ -252,7 +269,7 @@ roam health
|
|
|
252
269
|
|
|
253
270
|
## Commands
|
|
254
271
|
|
|
255
|
-
**Lead with the 5 verbs.** The [5 core commands](#core-commands) cover ~80% of agent workflows: `understand`, `context`, `retrieve`, `preflight`, `critique`. The remaining
|
|
272
|
+
**Lead with the 5 verbs.** The [5 core commands](#core-commands) cover ~80% of agent workflows: `understand`, `context`, `retrieve`, `preflight`, `critique`. The remaining 149 commands are detail surface for specialised workflows (taint, fleet, cga, oracle, eval, …) — they're called by agents on demand, not memorised. This is intentional design; under the hood the canonical surface is **154 commands organised into 7 categories**, but you don't need to know that to start.
|
|
256
273
|
|
|
257
274
|
<details>
|
|
258
275
|
<summary><strong>Full command reference</strong></summary>
|
|
@@ -291,6 +308,7 @@ roam health
|
|
|
291
308
|
| `roam file <path> [--full] [--changed] [--deps-of PATH]` | File skeleton: all definitions with signatures, cognitive load index, health score |
|
|
292
309
|
| `roam symbol <name> [--full]` | Symbol definition + callers + callees + metrics. Supports `file:symbol` disambiguation |
|
|
293
310
|
| `roam context <symbol> [--task MODE] [--for-file PATH]` | AI-optimized context: definition + callers + callees + files-to-read with line ranges |
|
|
311
|
+
| `roam hover <symbol>` | One-line architectural summary: kind, location, blast-radius bucket, top caller, top callee. Bounded at ~200 tokens for IDE hover panels |
|
|
294
312
|
| `roam retrieve <task> [--budget N] [--k N] [--seed-files PATH]` | Graph-aware context for free-form tasks: FTS5 + structural rerank (PageRank + clones) + token budget |
|
|
295
313
|
| `roam critique [--input DIFF] [--intent TEXT] [--high-callers N]` | Verify a patch against the graph: clones-not-edited + blast radius + intent-vs-semantic-diff. Pipe `git diff` in. Exit 5 on high severity. |
|
|
296
314
|
| `roam fleet plan <goal> [--n-agents N] [--adapter raw\|composio\|copilot]` | Graph-aware planner: Louvain partition + co-change + PageRank anchors → `.roam-fleet.json` for Composio/Copilot CLI/raw. |
|
|
@@ -344,6 +362,7 @@ roam health
|
|
|
344
362
|
| `roam complexity [--bumpy-road] [--include-tooling]` | Per-function cognitive complexity (SonarSource-compatible, triangular nesting penalty) + Halstead metrics (volume, difficulty, effort, bugs) + cyclomatic density |
|
|
345
363
|
| `roam py-types [--detail] [--include-tests] [--ci --min-coverage N]` | Python type-annotation health: % of public functions with full annotations, ``Any`` usage, legacy ``typing.Optional/Dict/List`` (PEP 585/604 modernisation candidates), per-file worst offenders. CI-gateable via ``--ci --min-coverage N`` (exit 5 below threshold). Default-excludes test files |
|
|
346
364
|
| `roam py-modern [--detail]` | Modern-Python adoption signal: counts walrus operator (PEP 572), match statements (PEP 634), PEP 604 ``X \| None``, PEP 585 ``dict[…]``, PEP 695 type aliases, f-strings vs ``.format()``. Reports type-modernisation % and f-string adoption % to gauge migration progress |
|
|
365
|
+
| `roam pytest-fixtures [SYMBOL] [--max-depth N]` | Inventory pytest fixture chains. With no SYMBOL, prints the project-wide fixture count and the top fixtures by dependent count. With a fixture or test name, walks the implicit fixture-parameter dependency graph to show what each test transitively requires. Resolves through ``conftest.py`` chains |
|
|
347
366
|
| `roam algo [--task T] [--confidence C] [--profile P]` | Algorithm anti-pattern detection: 23-pattern catalog detects suboptimal algorithms (O(n^2) loops, N+1 queries, quadratic string building, branching recursion, loop-invariant calls) and suggests better approaches with Big-O improvements. Confidence calibration via caller-count + runtime traces, evidence paths, impact scoring, framework-aware N+1 packs, and language-aware fix templates. Alias: `roam math` |
|
|
348
367
|
| `roam n1 [--confidence C] [--verbose]` | Implicit N+1 I/O detection: finds ORM model computed properties (`$appends`/accessors) that trigger lazy-loaded DB queries in collection contexts. Cross-references with eager loading config. Supports Laravel, Django, Rails, SQLAlchemy, JPA |
|
|
349
368
|
| `roam over-fetch [--threshold N] [--confidence C]` | Detect models serializing too many fields: large `$fillable` without `$hidden`/`$visible`, direct controller returns bypassing API Resources, poor exposed-to-hidden ratio |
|
|
@@ -596,7 +615,7 @@ The sentinel pair `<!-- roam:minimap -->` / `<!-- /roam:minimap -->` is replaced
|
|
|
596
615
|
|--------|-------------|
|
|
597
616
|
| `roam --json <command>` | Structured JSON output with consistent envelope |
|
|
598
617
|
| `roam --compact <command>` | Token-efficient output: TSV tables, minimal JSON envelope |
|
|
599
|
-
| `roam --sarif <command>` | SARIF 2.1.0 output for dead, health, complexity, rules, secrets,
|
|
618
|
+
| `roam --sarif <command>` | SARIF 2.1.0 output for dead, health, complexity, rules, secrets, algo, py-types, py-modern (GitHub/CI integration) |
|
|
600
619
|
| `roam health --gate` | CI quality gate. Reads `.roam-gates.yml` thresholds. Exit code 5 on failure |
|
|
601
620
|
|
|
602
621
|
</details>
|
|
@@ -892,7 +911,7 @@ ROAM_MCP_LITE=0 roam mcp
|
|
|
892
911
|
Core preset tools: `roam_affected_tests`, `roam_batch_get`, `roam_batch_search`, `roam_complete`, `roam_complexity_report`, `roam_context`, `roam_dead_code`, `roam_deps`, `roam_diagnose`, `roam_diagnose_issue`, `roam_diff`, `roam_expand_toolset`, `roam_explore`, `roam_file_info`, `roam_health`, `roam_impact`, `roam_pr_risk`, `roam_preflight`, `roam_prepare_change`, `roam_review_change`, `roam_search_symbol`, `roam_syntax_check`, `roam_trace`, `roam_understand`, `roam_uses`.
|
|
893
912
|
|
|
894
913
|
<details>
|
|
895
|
-
<summary><strong>MCP tool list (all
|
|
914
|
+
<summary><strong>MCP tool list (all 120)</strong></summary>
|
|
896
915
|
|
|
897
916
|
| Tool | Description |
|
|
898
917
|
|------|-------------|
|
|
@@ -903,6 +922,7 @@ Core preset tools: `roam_affected_tests`, `roam_batch_get`, `roam_batch_search`,
|
|
|
903
922
|
| `roam_search_symbol` | Find symbols by name |
|
|
904
923
|
| `roam_complete` | Prefix completion for symbols/paths/commands (FTS5-backed) |
|
|
905
924
|
| `roam_context` | Files-to-read for modifying a symbol |
|
|
925
|
+
| `roam_hover` | Single-line architectural summary — kind, blast-radius bucket, top caller, top callee |
|
|
906
926
|
| `roam_retrieve` | Graph-aware context for free-form tasks (FTS5 + structural rerank + token budget) |
|
|
907
927
|
| `roam_critique` | Verify a patch against the graph (clones-not-edited + blast radius) |
|
|
908
928
|
| `roam_fleet_plan` | Plan a multi-agent fleet — graph-aware partition emits .roam-fleet.json |
|
|
@@ -926,6 +946,7 @@ Core preset tools: `roam_affected_tests`, `roam_batch_get`, `roam_batch_search`,
|
|
|
926
946
|
| `roam_complexity_report` | Per-symbol cognitive complexity |
|
|
927
947
|
| `roam_py_types` | Python type-annotation health (% public typed, Any, legacy typing) |
|
|
928
948
|
| `roam_py_modern` | Modern-Python adoption (walrus, match, PEP 604/585/695, f-strings) |
|
|
949
|
+
| `roam_pytest_fixtures` | pytest fixture chain — top fixtures by dependent count, or per-symbol dependency walk |
|
|
929
950
|
| `roam_tour` | Auto-generated onboarding guide |
|
|
930
951
|
| `roam_diagnose` | Root cause analysis for debugging |
|
|
931
952
|
| `roam_visualize` | Generate Mermaid or DOT architecture diagrams |
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "roam-code"
|
|
7
|
-
version = "12.
|
|
7
|
+
version = "12.8.0"
|
|
8
8
|
description = "Instant codebase comprehension for AI coding agents"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.9"
|
|
@@ -130,7 +130,6 @@ ignore = [
|
|
|
130
130
|
"E501", # line-too-long — handled by ruff format; remaining are long strings/SQL
|
|
131
131
|
"E731", # lambda-assignment — used for concise dispatch tables
|
|
132
132
|
"E741", # ambiguous variable names (G, l, etc.) — common in graph code
|
|
133
|
-
"F841", # unused-variable — often intentional destructuring
|
|
134
133
|
]
|
|
135
134
|
|
|
136
135
|
[tool.ruff.lint.per-file-ignores]
|
|
@@ -240,6 +240,28 @@ RECIPES: list[Recipe] = [
|
|
|
240
240
|
"attack-surface review. Failures gate-able in CI via exit 5."
|
|
241
241
|
),
|
|
242
242
|
),
|
|
243
|
+
Recipe(
|
|
244
|
+
name="fixture-impact",
|
|
245
|
+
intent=(
|
|
246
|
+
"Show what tests / fixtures break if a pytest fixture is renamed, removed, or has its return shape changed"
|
|
247
|
+
),
|
|
248
|
+
examples=(
|
|
249
|
+
"if I rename cli_runner what tests break",
|
|
250
|
+
"what depends on the cli_runner fixture",
|
|
251
|
+
"who uses indexed_project",
|
|
252
|
+
"blast radius of indexed_project",
|
|
253
|
+
"find tests that consume mock_db_session",
|
|
254
|
+
),
|
|
255
|
+
keywords=("fixture", "fixtures", "pytest", "conftest"),
|
|
256
|
+
commands=(("pytest-fixtures", ("{symbol}", "--reverse")),),
|
|
257
|
+
summary=(
|
|
258
|
+
"Walks the implicit pytest fixture-parameter dependency graph "
|
|
259
|
+
"in reverse — fixtures and tests that consume the named fixture "
|
|
260
|
+
"transitively. Pass the fixture name as an identifier (snake_case "
|
|
261
|
+
"or PascalCase). ``--json`` for the full list when output is "
|
|
262
|
+
"capped."
|
|
263
|
+
),
|
|
264
|
+
),
|
|
243
265
|
Recipe(
|
|
244
266
|
name="dead-code-sweep",
|
|
245
267
|
intent="Find unused or unreachable symbols ready for deletion",
|
|
@@ -210,19 +210,46 @@ def _project_root_for_conn(conn: sqlite3.Connection) -> str:
|
|
|
210
210
|
return parent
|
|
211
211
|
|
|
212
212
|
|
|
213
|
+
# Per-process cache of file-text reads. With 19 detectors each calling
|
|
214
|
+
# ``_file_text`` per file, an uncached implementation does 19×N disk
|
|
215
|
+
# reads. Cache keyed by ``(id(conn), file_id)`` so distinct DB
|
|
216
|
+
# connections don't collide. Cleared at module unload (or via
|
|
217
|
+
# ``_clear_file_text_cache()`` in tests). Bounded in size at 4096
|
|
218
|
+
# entries to avoid unbounded growth on huge repos; LRU-evicted.
|
|
219
|
+
from collections import OrderedDict as _OrderedDict
|
|
220
|
+
|
|
221
|
+
_FILE_TEXT_CACHE: _OrderedDict = _OrderedDict()
|
|
222
|
+
_FILE_TEXT_CACHE_MAX = 4096
|
|
223
|
+
|
|
224
|
+
|
|
225
|
+
def _clear_file_text_cache() -> None:
|
|
226
|
+
"""Clear the file-text cache. Call from tests that want clean state."""
|
|
227
|
+
_FILE_TEXT_CACHE.clear()
|
|
228
|
+
|
|
229
|
+
|
|
213
230
|
def _file_text(conn: sqlite3.Connection, file_id: int) -> str | None:
|
|
214
231
|
"""Read the source text of a file via roam.index — but the index
|
|
215
232
|
doesn't store source. Instead we read from disk via the file path
|
|
216
233
|
resolved against the project root (paths in the index are
|
|
217
234
|
project-relative, so a bare ``open(path)`` fails when the caller
|
|
218
235
|
isn't sitting at the project root).
|
|
219
|
-
|
|
236
|
+
|
|
237
|
+
Caches results across calls within a process so all 19+ detectors
|
|
238
|
+
pay the disk read once per file rather than 19+ times.
|
|
220
239
|
"""
|
|
240
|
+
cache_key = (id(conn), file_id)
|
|
241
|
+
cached = _FILE_TEXT_CACHE.get(cache_key)
|
|
242
|
+
if cached is not None:
|
|
243
|
+
# LRU bump
|
|
244
|
+
_FILE_TEXT_CACHE.move_to_end(cache_key)
|
|
245
|
+
return cached if cached != "" else None # sentinel for "tried, failed"
|
|
221
246
|
row = conn.execute("SELECT path FROM files WHERE id = ?", (file_id,)).fetchone()
|
|
222
247
|
if row is None:
|
|
248
|
+
_FILE_TEXT_CACHE[cache_key] = ""
|
|
223
249
|
return None
|
|
224
250
|
path = row[0]
|
|
225
251
|
if not path:
|
|
252
|
+
_FILE_TEXT_CACHE[cache_key] = ""
|
|
226
253
|
return None
|
|
227
254
|
# Resolve project-relative path via the DB's location.
|
|
228
255
|
root = _project_root_for_conn(conn)
|
|
@@ -233,9 +260,15 @@ def _file_text(conn: sqlite3.Connection, file_id: int) -> str | None:
|
|
|
233
260
|
path = _osp.join(root, path)
|
|
234
261
|
try:
|
|
235
262
|
with open(path, encoding="utf-8", errors="replace") as f:
|
|
236
|
-
|
|
263
|
+
text = f.read()
|
|
237
264
|
except OSError:
|
|
265
|
+
_FILE_TEXT_CACHE[cache_key] = ""
|
|
238
266
|
return None
|
|
267
|
+
_FILE_TEXT_CACHE[cache_key] = text
|
|
268
|
+
# LRU eviction
|
|
269
|
+
if len(_FILE_TEXT_CACHE) > _FILE_TEXT_CACHE_MAX:
|
|
270
|
+
_FILE_TEXT_CACHE.popitem(last=False)
|
|
271
|
+
return text
|
|
239
272
|
|
|
240
273
|
|
|
241
274
|
# Triple-quoted strings (greedy across newlines), single/double quoted
|
|
@@ -250,27 +283,39 @@ _SINGLE_QUOTE_RE = re.compile(r'("(?:\\.|[^"\\\n])*"|\'(?:\\.|[^\'\\\n])*\')')
|
|
|
250
283
|
_COMMENT_RE = re.compile(r"#[^\n]*")
|
|
251
284
|
|
|
252
285
|
|
|
286
|
+
_STRIP_CACHE: _OrderedDict = _OrderedDict()
|
|
287
|
+
_STRIP_CACHE_MAX = 4096
|
|
288
|
+
|
|
289
|
+
|
|
253
290
|
def _strip_strings_and_comments(text: str) -> str:
|
|
254
291
|
"""Replace strings + comments with same-length whitespace so the
|
|
255
292
|
detector regexes don't false-match inside docstrings or comments.
|
|
256
293
|
|
|
257
294
|
Length-preserving so ``text.count("\n", 0, match.start())`` still
|
|
258
|
-
yields the original line number.
|
|
259
|
-
|
|
260
|
-
|
|
295
|
+
yields the original line number. Cached because all detectors
|
|
296
|
+
that strip apply the same transform — without caching we re-strip
|
|
297
|
+
once per detector per file.
|
|
261
298
|
"""
|
|
262
299
|
if not text:
|
|
263
300
|
return text
|
|
301
|
+
# Cache key: id() of the original string (CPython gives unique
|
|
302
|
+
# ids while alive). Combined with len() to catch the rare case
|
|
303
|
+
# of id-reuse after string GC.
|
|
304
|
+
key = (id(text), len(text))
|
|
305
|
+
cached = _STRIP_CACHE.get(key)
|
|
306
|
+
if cached is not None:
|
|
307
|
+
_STRIP_CACHE.move_to_end(key)
|
|
308
|
+
return cached
|
|
264
309
|
|
|
265
310
|
def _blank(match: re.Match) -> str:
|
|
266
|
-
# Preserve newlines — re-blanking with spaces would collapse
|
|
267
|
-
# multi-line docstrings into one logical line for downstream
|
|
268
|
-
# regexes that use ``^`` / ``$`` anchors.
|
|
269
311
|
return "".join(" " if c != "\n" else "\n" for c in match.group(0))
|
|
270
312
|
|
|
271
313
|
out = _TRIPLE_QUOTE_RE.sub(_blank, text)
|
|
272
314
|
out = _SINGLE_QUOTE_RE.sub(_blank, out)
|
|
273
315
|
out = _COMMENT_RE.sub(_blank, out)
|
|
316
|
+
_STRIP_CACHE[key] = out
|
|
317
|
+
if len(_STRIP_CACHE) > _STRIP_CACHE_MAX:
|
|
318
|
+
_STRIP_CACHE.popitem(last=False)
|
|
274
319
|
return out
|
|
275
320
|
|
|
276
321
|
|
|
@@ -932,12 +977,25 @@ def detect_django_n1(conn: sqlite3.Connection) -> list[dict]:
|
|
|
932
977
|
text = _strip_strings_and_comments(text)
|
|
933
978
|
if not text:
|
|
934
979
|
continue
|
|
935
|
-
#
|
|
936
|
-
|
|
980
|
+
# Require a Django ORM hint anywhere in the file. Without
|
|
981
|
+
# ``.objects.`` (the manager indicator) or an explicit Django
|
|
982
|
+
# import, a ``.all()`` shape might be a custom collection or a
|
|
983
|
+
# different ORM — firing would be a false positive.
|
|
984
|
+
has_django_hint = ".objects." in text or "from django" in text or "import django" in text
|
|
985
|
+
if not has_django_hint:
|
|
937
986
|
continue
|
|
938
987
|
sym_index = _line_to_symbol(conn, file_id)
|
|
939
988
|
for match in _DJANGO_ALL_THEN_FOR.finditer(text):
|
|
940
989
|
line_no = text.count("\n", 0, match.start()) + 1
|
|
990
|
+
# Suppress when the queryset chain already eager-loads via
|
|
991
|
+
# select_related or prefetch_related. Look back from the
|
|
992
|
+
# ``.all()`` call to the assignment (or 200 chars max) for
|
|
993
|
+
# those calls. They defuse the N+1 — firing here would be a
|
|
994
|
+
# false positive.
|
|
995
|
+
chain_start = max(0, match.start() - 200)
|
|
996
|
+
chain = text[chain_start : match.start()]
|
|
997
|
+
if "select_related(" in chain or "prefetch_related(" in chain:
|
|
998
|
+
continue
|
|
941
999
|
sym = _enclosing_symbol(line_no, sym_index)
|
|
942
1000
|
if sym is None:
|
|
943
1001
|
continue
|
|
@@ -1013,6 +1071,19 @@ def detect_sqlalchemy_lazy(conn: sqlite3.Connection) -> list[dict]:
|
|
|
1013
1071
|
sym_index = _line_to_symbol(conn, file_id)
|
|
1014
1072
|
for match in _SQLALCHEMY_ALL_THEN_DOT.finditer(text):
|
|
1015
1073
|
line_no = text.count("\n", 0, match.start()) + 1
|
|
1074
|
+
# Suppress when the query expression already eager-loads via
|
|
1075
|
+
# joinedload / selectinload / contains_eager. Look back from
|
|
1076
|
+
# the ``.all()`` call to the start of the statement (or 200
|
|
1077
|
+
# chars, whichever is shorter).
|
|
1078
|
+
chain_start = max(0, match.start() - 200)
|
|
1079
|
+
chain = text[chain_start : match.start()]
|
|
1080
|
+
if (
|
|
1081
|
+
"joinedload(" in chain
|
|
1082
|
+
or "selectinload(" in chain
|
|
1083
|
+
or "contains_eager(" in chain
|
|
1084
|
+
or "subqueryload(" in chain
|
|
1085
|
+
):
|
|
1086
|
+
continue
|
|
1016
1087
|
sym = _enclosing_symbol(line_no, sym_index)
|
|
1017
1088
|
if sym is None:
|
|
1018
1089
|
continue
|
|
@@ -56,6 +56,8 @@ _COMMANDS = {
|
|
|
56
56
|
"complexity": ("roam.commands.cmd_complexity", "complexity"),
|
|
57
57
|
"py-types": ("roam.commands.cmd_py_types", "py_types"),
|
|
58
58
|
"py-modern": ("roam.commands.cmd_py_modern", "py_modern"),
|
|
59
|
+
"pytest-fixtures": ("roam.commands.cmd_pytest_fixtures", "pytest_fixtures"),
|
|
60
|
+
"hover": ("roam.commands.cmd_hover", "hover"),
|
|
59
61
|
"debt": ("roam.commands.cmd_debt", "debt"),
|
|
60
62
|
"conventions": ("roam.commands.cmd_conventions", "conventions"),
|
|
61
63
|
"bus-factor": ("roam.commands.cmd_bus_factor", "bus_factor"),
|
|
@@ -219,6 +221,7 @@ _CATEGORIES = {
|
|
|
219
221
|
"verify-imports",
|
|
220
222
|
"diff",
|
|
221
223
|
"context",
|
|
224
|
+
"hover",
|
|
222
225
|
"retrieve",
|
|
223
226
|
"critique",
|
|
224
227
|
"fleet",
|
|
@@ -245,6 +248,7 @@ _CATEGORIES = {
|
|
|
245
248
|
"complexity",
|
|
246
249
|
"py-types",
|
|
247
250
|
"py-modern",
|
|
251
|
+
"pytest-fixtures",
|
|
248
252
|
"algo",
|
|
249
253
|
"n1",
|
|
250
254
|
"over-fetch",
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
"""``roam hover`` — single-line architectural summary suitable for an
|
|
2
|
+
IDE hover panel or chat-inline reference.
|
|
3
|
+
|
|
4
|
+
Designed to fit in ~200 tokens regardless of the symbol. Where ``roam
|
|
5
|
+
context`` returns a full briefing (signature, callers, related files,
|
|
6
|
+
tests, model fields, etc.), ``roam hover`` returns the minimum useful
|
|
7
|
+
gloss: kind, qualified name, location, blast-radius bucket, top
|
|
8
|
+
caller, top callee. Pairs with a hover-on-symbol IDE plugin.
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
from __future__ import annotations
|
|
12
|
+
|
|
13
|
+
import click
|
|
14
|
+
|
|
15
|
+
from roam.commands.resolve import ensure_index, find_symbol, symbol_not_found
|
|
16
|
+
from roam.db.connection import open_db
|
|
17
|
+
from roam.output.formatter import abbrev_kind, json_envelope, loc, to_json
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def _blast_bucket(in_degree: int) -> str:
|
|
21
|
+
"""Coarse classifier: how nervous should an editor be about
|
|
22
|
+
changing this symbol? Buckets match ``roam impact`` thresholds."""
|
|
23
|
+
if in_degree >= 50:
|
|
24
|
+
return "large"
|
|
25
|
+
if in_degree >= 10:
|
|
26
|
+
return "moderate"
|
|
27
|
+
if in_degree >= 1:
|
|
28
|
+
return "small"
|
|
29
|
+
return "none"
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def _top_neighbour(conn, sym_id: int, *, direction: str) -> dict | None:
|
|
33
|
+
"""Highest-PageRank caller (direction='in') or callee (direction='out')."""
|
|
34
|
+
if direction == "in":
|
|
35
|
+
edge_clause = "e.target_id = ? AND s.id = e.source_id"
|
|
36
|
+
else:
|
|
37
|
+
edge_clause = "e.source_id = ? AND s.id = e.target_id"
|
|
38
|
+
rows = conn.execute(
|
|
39
|
+
f"""
|
|
40
|
+
SELECT s.id, s.name, s.qualified_name, s.kind, f.path AS file_path,
|
|
41
|
+
s.line_start, COALESCE(gm.pagerank, 0) AS pr
|
|
42
|
+
FROM edges e
|
|
43
|
+
JOIN symbols s ON {edge_clause}
|
|
44
|
+
JOIN files f ON s.file_id = f.id
|
|
45
|
+
LEFT JOIN graph_metrics gm ON gm.symbol_id = s.id
|
|
46
|
+
WHERE e.kind IN ('call', 'inherits', 'imports')
|
|
47
|
+
ORDER BY pr DESC
|
|
48
|
+
LIMIT 1
|
|
49
|
+
""",
|
|
50
|
+
(sym_id,),
|
|
51
|
+
).fetchall()
|
|
52
|
+
if not rows:
|
|
53
|
+
return None
|
|
54
|
+
r = rows[0]
|
|
55
|
+
return {
|
|
56
|
+
"name": r["qualified_name"] or r["name"],
|
|
57
|
+
"kind": r["kind"],
|
|
58
|
+
"file_path": r["file_path"],
|
|
59
|
+
"line_start": r["line_start"],
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
@click.command()
|
|
64
|
+
@click.argument("symbol")
|
|
65
|
+
@click.pass_context
|
|
66
|
+
def hover(ctx, symbol: str):
|
|
67
|
+
"""Show a one-line architectural summary for SYMBOL.
|
|
68
|
+
|
|
69
|
+
Output is bounded at ~200 tokens regardless of the symbol — kind,
|
|
70
|
+
qualified name, location, blast-radius bucket, top caller, top
|
|
71
|
+
callee. Designed for IDE hover panels and chat-inline references
|
|
72
|
+
where ``roam context`` is too verbose.
|
|
73
|
+
"""
|
|
74
|
+
json_mode = ctx.obj.get("json") if ctx.obj else False
|
|
75
|
+
ensure_index()
|
|
76
|
+
|
|
77
|
+
with open_db(readonly=True) as conn:
|
|
78
|
+
sym = find_symbol(conn, symbol)
|
|
79
|
+
if sym is None:
|
|
80
|
+
click.echo(symbol_not_found(conn, symbol, json_mode=json_mode))
|
|
81
|
+
raise SystemExit(1)
|
|
82
|
+
sym_id = sym["id"]
|
|
83
|
+
|
|
84
|
+
metrics = conn.execute(
|
|
85
|
+
"SELECT in_degree, out_degree, pagerank FROM graph_metrics WHERE symbol_id = ?",
|
|
86
|
+
(sym_id,),
|
|
87
|
+
).fetchone()
|
|
88
|
+
in_d = metrics["in_degree"] if metrics else 0
|
|
89
|
+
out_d = metrics["out_degree"] if metrics else 0
|
|
90
|
+
pr = float(metrics["pagerank"] or 0) if metrics else 0.0
|
|
91
|
+
|
|
92
|
+
bucket = _blast_bucket(in_d)
|
|
93
|
+
top_caller = _top_neighbour(conn, sym_id, direction="in")
|
|
94
|
+
top_callee = _top_neighbour(conn, sym_id, direction="out")
|
|
95
|
+
|
|
96
|
+
qn = sym["qualified_name"] or sym["name"]
|
|
97
|
+
file_loc = loc(sym["file_path"], sym["line_start"])
|
|
98
|
+
kind_short = abbrev_kind(sym["kind"])
|
|
99
|
+
|
|
100
|
+
if json_mode:
|
|
101
|
+
click.echo(
|
|
102
|
+
to_json(
|
|
103
|
+
json_envelope(
|
|
104
|
+
"hover",
|
|
105
|
+
summary={
|
|
106
|
+
"verdict": (f"{kind_short} {qn} — {bucket} blast radius ({in_d} in, {out_d} out)"),
|
|
107
|
+
"kind": sym["kind"],
|
|
108
|
+
"qualified_name": qn,
|
|
109
|
+
"file_path": sym["file_path"],
|
|
110
|
+
"line_start": sym["line_start"],
|
|
111
|
+
"in_degree": in_d,
|
|
112
|
+
"out_degree": out_d,
|
|
113
|
+
"pagerank": round(pr, 6),
|
|
114
|
+
"blast_bucket": bucket,
|
|
115
|
+
},
|
|
116
|
+
top_caller=top_caller,
|
|
117
|
+
top_callee=top_callee,
|
|
118
|
+
)
|
|
119
|
+
)
|
|
120
|
+
)
|
|
121
|
+
return
|
|
122
|
+
|
|
123
|
+
click.echo(f"{kind_short} {qn} {file_loc}")
|
|
124
|
+
click.echo(f" blast radius: {bucket} ({in_d} callers, {out_d} callees, pr={pr:.4f})")
|
|
125
|
+
if top_caller:
|
|
126
|
+
c_loc = loc(top_caller["file_path"], top_caller["line_start"])
|
|
127
|
+
click.echo(f" top caller: {top_caller['name']} {c_loc}")
|
|
128
|
+
if top_callee:
|
|
129
|
+
c_loc = loc(top_callee["file_path"], top_callee["line_start"])
|
|
130
|
+
click.echo(f" top callee: {top_callee['name']} {c_loc}")
|
|
@@ -196,6 +196,12 @@ def _check_blast_radius(conn, sym_ids, file_paths):
|
|
|
196
196
|
# ---------------------------------------------------------------------------
|
|
197
197
|
|
|
198
198
|
|
|
199
|
+
# Once the blast radius dumps every test file in the repo, the
|
|
200
|
+
# "Suggested tests:" line stops being a suggestion and becomes a wall
|
|
201
|
+
# of text. This cap is the boundary between actionable and unactionable.
|
|
202
|
+
_MAX_SUGGESTED_TEST_FILES = 15
|
|
203
|
+
|
|
204
|
+
|
|
199
205
|
def _check_affected_tests(conn, sym_ids, file_paths):
|
|
200
206
|
"""Find tests that need to run."""
|
|
201
207
|
results = _gather_affected_tests(conn, sym_ids, file_paths)
|
|
@@ -212,7 +218,13 @@ def _check_affected_tests(conn, sym_ids, file_paths):
|
|
|
212
218
|
seen.add(r["file"])
|
|
213
219
|
test_files.append(r["file"])
|
|
214
220
|
|
|
215
|
-
|
|
221
|
+
truncated_files = len(test_files) > _MAX_SUGGESTED_TEST_FILES
|
|
222
|
+
if truncated_files:
|
|
223
|
+
suggested = test_files[:_MAX_SUGGESTED_TEST_FILES]
|
|
224
|
+
suffix = f" # (+{len(test_files) - _MAX_SUGGESTED_TEST_FILES} more)"
|
|
225
|
+
pytest_cmd = "pytest " + " ".join(suggested) + suffix
|
|
226
|
+
else:
|
|
227
|
+
pytest_cmd = "pytest " + " ".join(test_files) if test_files else ""
|
|
216
228
|
severity = _test_severity(direct, transitive, colocated)
|
|
217
229
|
|
|
218
230
|
return {
|
|
@@ -222,6 +234,7 @@ def _check_affected_tests(conn, sym_ids, file_paths):
|
|
|
222
234
|
"total": len(results),
|
|
223
235
|
"test_files": test_files,
|
|
224
236
|
"pytest_command": pytest_cmd,
|
|
237
|
+
"pytest_command_truncated": truncated_files,
|
|
225
238
|
"severity": severity,
|
|
226
239
|
}
|
|
227
240
|
|