roam-code 12.1.0__tar.gz → 12.2.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.1.0/src/roam_code.egg-info → roam_code-12.2.0}/PKG-INFO +21 -8
- {roam_code-12.1.0 → roam_code-12.2.0}/README.md +11 -7
- {roam_code-12.1.0 → roam_code-12.2.0}/pyproject.toml +24 -1
- {roam_code-12.1.0 → roam_code-12.2.0}/src/roam/attest/cga.py +34 -9
- {roam_code-12.1.0 → roam_code-12.2.0}/src/roam/commands/cmd_cga.py +21 -4
- {roam_code-12.1.0 → roam_code-12.2.0}/src/roam/commands/cmd_mcp_setup.py +39 -2
- {roam_code-12.1.0 → roam_code-12.2.0}/src/roam/commands/cmd_retrieve.py +7 -2
- {roam_code-12.1.0 → roam_code-12.2.0}/src/roam/commands/cmd_sbom.py +26 -2
- {roam_code-12.1.0 → roam_code-12.2.0}/src/roam/competitor_site_data.py +1 -1
- {roam_code-12.1.0 → roam_code-12.2.0}/src/roam/config.py +4 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/src/roam/graph/clusters.py +63 -8
- {roam_code-12.1.0 → roam_code-12.2.0}/src/roam/mcp_server.py +215 -2
- roam_code-12.2.0/src/roam/retrieve/learned_ranker.py +241 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/src/roam/retrieve/pipeline.py +24 -1
- {roam_code-12.1.0 → roam_code-12.2.0}/src/roam/retrieve/rerank.py +48 -1
- roam_code-12.2.0/src/roam/retrieve/semantic.py +194 -0
- roam_code-12.2.0/src/roam/runtime/daemon.py +100 -0
- roam_code-12.2.0/src/roam/runtime/graph_backend.py +92 -0
- roam_code-12.2.0/src/roam/runtime/lock_modes.py +50 -0
- roam_code-12.2.0/src/roam/runtime/lockmgr.py +136 -0
- roam_code-12.2.0/src/roam/security/aibom_extension.py +249 -0
- {roam_code-12.1.0 → roam_code-12.2.0/src/roam_code.egg-info}/PKG-INFO +21 -8
- {roam_code-12.1.0 → roam_code-12.2.0}/src/roam_code.egg-info/SOURCES.txt +8 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/src/roam_code.egg-info/requires.txt +13 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/tests/test_batch_mcp.py +9 -1
- {roam_code-12.1.0 → roam_code-12.2.0}/tests/test_mcp_server.py +14 -2
- {roam_code-12.1.0 → roam_code-12.2.0}/tests/test_readme_surface_consistency.py +7 -1
- {roam_code-12.1.0 → roam_code-12.2.0}/tests/test_retrieve.py +1 -0
- roam_code-12.2.0/tests/test_v12_2.py +300 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/LICENSE +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/setup.cfg +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/src/roam/__init__.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/src/roam/__main__.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/src/roam/analysis/__init__.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/src/roam/analysis/effects.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/src/roam/analysis/taint.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/src/roam/api.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/src/roam/ask/__init__.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/src/roam/ask/classifier.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/src/roam/ask/recipes.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/src/roam/ask/runner.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/src/roam/attest/__init__.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/src/roam/bridges/__init__.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/src/roam/bridges/base.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/src/roam/bridges/bridge_config.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/src/roam/bridges/bridge_django.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/src/roam/bridges/bridge_protobuf.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/src/roam/bridges/bridge_rest_api.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/src/roam/bridges/bridge_salesforce.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/src/roam/bridges/bridge_template.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/src/roam/bridges/registry.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/src/roam/catalog/__init__.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/src/roam/catalog/detectors.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/src/roam/catalog/fixes.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/src/roam/catalog/smells.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/src/roam/catalog/tasks.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/src/roam/cli.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/src/roam/commands/__init__.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/src/roam/commands/changed_files.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/src/roam/commands/cmd_adrs.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/src/roam/commands/cmd_adversarial.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/src/roam/commands/cmd_affected.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/src/roam/commands/cmd_affected_tests.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/src/roam/commands/cmd_agent_context.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/src/roam/commands/cmd_agent_export.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/src/roam/commands/cmd_agent_plan.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/src/roam/commands/cmd_ai_ratio.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/src/roam/commands/cmd_ai_readiness.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/src/roam/commands/cmd_alerts.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/src/roam/commands/cmd_annotate.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/src/roam/commands/cmd_api_changes.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/src/roam/commands/cmd_api_drift.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/src/roam/commands/cmd_ask.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/src/roam/commands/cmd_attest.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/src/roam/commands/cmd_auth_gaps.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/src/roam/commands/cmd_bisect.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/src/roam/commands/cmd_breaking.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/src/roam/commands/cmd_budget.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/src/roam/commands/cmd_bus_factor.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/src/roam/commands/cmd_capsule.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/src/roam/commands/cmd_check_rules.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/src/roam/commands/cmd_ci_setup.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/src/roam/commands/cmd_clean.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/src/roam/commands/cmd_clones.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/src/roam/commands/cmd_closure.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/src/roam/commands/cmd_clusters.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/src/roam/commands/cmd_codeowners.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/src/roam/commands/cmd_complexity.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/src/roam/commands/cmd_config.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/src/roam/commands/cmd_congestion.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/src/roam/commands/cmd_context.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/src/roam/commands/cmd_conventions.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/src/roam/commands/cmd_coupling.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/src/roam/commands/cmd_coverage_gaps.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/src/roam/commands/cmd_critique.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/src/roam/commands/cmd_cut.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/src/roam/commands/cmd_dark_matter.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/src/roam/commands/cmd_dashboard.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/src/roam/commands/cmd_dead.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/src/roam/commands/cmd_debt.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/src/roam/commands/cmd_deps.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/src/roam/commands/cmd_describe.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/src/roam/commands/cmd_dev_profile.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/src/roam/commands/cmd_diagnose.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/src/roam/commands/cmd_diff.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/src/roam/commands/cmd_doc_staleness.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/src/roam/commands/cmd_docs_coverage.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/src/roam/commands/cmd_doctor.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/src/roam/commands/cmd_drift.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/src/roam/commands/cmd_duplicates.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/src/roam/commands/cmd_effects.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/src/roam/commands/cmd_endpoints.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/src/roam/commands/cmd_entry_points.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/src/roam/commands/cmd_eval_retrieve.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/src/roam/commands/cmd_fan.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/src/roam/commands/cmd_file.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/src/roam/commands/cmd_fingerprint.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/src/roam/commands/cmd_fitness.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/src/roam/commands/cmd_flag_dead.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/src/roam/commands/cmd_fleet.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/src/roam/commands/cmd_fn_coupling.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/src/roam/commands/cmd_forecast.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/src/roam/commands/cmd_grep.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/src/roam/commands/cmd_guard.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/src/roam/commands/cmd_health.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/src/roam/commands/cmd_hooks.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/src/roam/commands/cmd_hotspots.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/src/roam/commands/cmd_impact.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/src/roam/commands/cmd_index.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/src/roam/commands/cmd_index_bundle.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/src/roam/commands/cmd_ingest_trace.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/src/roam/commands/cmd_init.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/src/roam/commands/cmd_intent.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/src/roam/commands/cmd_invariants.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/src/roam/commands/cmd_layers.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/src/roam/commands/cmd_map.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/src/roam/commands/cmd_math.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/src/roam/commands/cmd_metrics.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/src/roam/commands/cmd_migration_safety.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/src/roam/commands/cmd_minimap.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/src/roam/commands/cmd_missing_index.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/src/roam/commands/cmd_module.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/src/roam/commands/cmd_mutate.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/src/roam/commands/cmd_n1.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/src/roam/commands/cmd_oracle.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/src/roam/commands/cmd_orchestrate.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/src/roam/commands/cmd_orphan_routes.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/src/roam/commands/cmd_over_fetch.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/src/roam/commands/cmd_owner.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/src/roam/commands/cmd_partition.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/src/roam/commands/cmd_path_coverage.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/src/roam/commands/cmd_patterns.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/src/roam/commands/cmd_plan.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/src/roam/commands/cmd_plan_refactor.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/src/roam/commands/cmd_pr_diff.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/src/roam/commands/cmd_pr_risk.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/src/roam/commands/cmd_preflight.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/src/roam/commands/cmd_relate.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/src/roam/commands/cmd_report.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/src/roam/commands/cmd_reset.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/src/roam/commands/cmd_risk.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/src/roam/commands/cmd_rules.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/src/roam/commands/cmd_safe_delete.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/src/roam/commands/cmd_safe_zones.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/src/roam/commands/cmd_schema.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/src/roam/commands/cmd_search.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/src/roam/commands/cmd_search_semantic.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/src/roam/commands/cmd_secrets.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/src/roam/commands/cmd_semantic_diff.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/src/roam/commands/cmd_simulate.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/src/roam/commands/cmd_simulate_departure.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/src/roam/commands/cmd_sketch.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/src/roam/commands/cmd_smells.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/src/roam/commands/cmd_spectral.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/src/roam/commands/cmd_split.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/src/roam/commands/cmd_suggest_refactoring.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/src/roam/commands/cmd_suggest_reviewers.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/src/roam/commands/cmd_supply_chain.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/src/roam/commands/cmd_symbol.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/src/roam/commands/cmd_syntax_check.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/src/roam/commands/cmd_taint.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/src/roam/commands/cmd_test_gaps.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/src/roam/commands/cmd_test_scaffold.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/src/roam/commands/cmd_testmap.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/src/roam/commands/cmd_tour.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/src/roam/commands/cmd_trace.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/src/roam/commands/cmd_trends.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/src/roam/commands/cmd_triage.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/src/roam/commands/cmd_understand.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/src/roam/commands/cmd_uses.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/src/roam/commands/cmd_verify.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/src/roam/commands/cmd_verify_imports.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/src/roam/commands/cmd_vibe_check.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/src/roam/commands/cmd_visualize.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/src/roam/commands/cmd_vuln_map.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/src/roam/commands/cmd_vuln_reach.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/src/roam/commands/cmd_vulns.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/src/roam/commands/cmd_watch.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/src/roam/commands/cmd_weather.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/src/roam/commands/cmd_why.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/src/roam/commands/cmd_ws.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/src/roam/commands/cmd_xlang.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/src/roam/commands/codeowners_helpers.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/src/roam/commands/context_helpers.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/src/roam/commands/gate_presets.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/src/roam/commands/graph_helpers.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/src/roam/commands/metrics_history.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/src/roam/commands/next_steps.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/src/roam/commands/resolve.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/src/roam/commands/suppression.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/src/roam/coverage_reports.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/src/roam/critique/__init__.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/src/roam/critique/aggregator.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/src/roam/critique/checks.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/src/roam/db/__init__.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/src/roam/db/connection.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/src/roam/db/queries.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/src/roam/db/schema.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/src/roam/eval/__init__.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/src/roam/eval/harness.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/src/roam/exit_codes.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/src/roam/fleet/__init__.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/src/roam/fleet/adapters.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/src/roam/fleet/manifest.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/src/roam/git_utils.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/src/roam/graph/__init__.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/src/roam/graph/anomaly.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/src/roam/graph/builder.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/src/roam/graph/clone_detect.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/src/roam/graph/cycles.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/src/roam/graph/dark_matter.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/src/roam/graph/diff.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/src/roam/graph/fingerprint.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/src/roam/graph/layers.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/src/roam/graph/pagerank.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/src/roam/graph/partition.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/src/roam/graph/pathfinding.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/src/roam/graph/propagation.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/src/roam/graph/simulate.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/src/roam/graph/spectral.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/src/roam/graph/stats.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/src/roam/index/__init__.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/src/roam/index/complexity.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/src/roam/index/discovery.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/src/roam/index/django_post.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/src/roam/index/file_roles.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/src/roam/index/git_stats.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/src/roam/index/gitignore.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/src/roam/index/incremental.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/src/roam/index/indexer.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/src/roam/index/parser.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/src/roam/index/relations.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/src/roam/index/symbols.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/src/roam/index/test_conventions.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/src/roam/languages/__init__.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/src/roam/languages/apex_lang.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/src/roam/languages/aura_lang.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/src/roam/languages/base.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/src/roam/languages/c_lang.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/src/roam/languages/csharp_lang.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/src/roam/languages/extractor_schema.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/src/roam/languages/foxpro_lang.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/src/roam/languages/generic_lang.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/src/roam/languages/go_lang.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/src/roam/languages/hcl_lang.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/src/roam/languages/java_lang.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/src/roam/languages/javascript_lang.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/src/roam/languages/kotlin_lang.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/src/roam/languages/php_lang.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/src/roam/languages/python_lang.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/src/roam/languages/query_engine.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/src/roam/languages/registry.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/src/roam/languages/ruby_lang.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/src/roam/languages/rust_lang.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/src/roam/languages/scala_lang.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/src/roam/languages/sfxml_lang.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/src/roam/languages/sql_lang.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/src/roam/languages/swift_lang.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/src/roam/languages/typescript_lang.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/src/roam/languages/visualforce_lang.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/src/roam/languages/yaml_lang.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/src/roam/mcp_extras/__init__.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/src/roam/mcp_extras/completions.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/src/roam/mcp_extras/progress.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/src/roam/mcp_extras/sampling.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/src/roam/mcp_extras/session.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/src/roam/mcp_extras/watcher.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/src/roam/output/__init__.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/src/roam/output/formatter.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/src/roam/output/mermaid.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/src/roam/output/sarif.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/src/roam/output/schema_registry.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/src/roam/plugins.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/src/roam/refactor/__init__.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/src/roam/refactor/codegen.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/src/roam/refactor/transforms.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/src/roam/retrieve/__init__.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/src/roam/retrieve/seeds.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/src/roam/rules/__init__.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/src/roam/rules/ast_match.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/src/roam/rules/builtin.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/src/roam/rules/dataflow.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/src/roam/rules/engine.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/src/roam/runtime/__init__.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/src/roam/runtime/hotspots.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/src/roam/runtime/trace_ingest.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/src/roam/search/__init__.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/src/roam/search/framework_packs.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/src/roam/search/index_embeddings.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/src/roam/search/onnx_embeddings.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/src/roam/search/tfidf.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/src/roam/security/__init__.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/src/roam/security/taint_classifier.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/src/roam/security/taint_engine.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/src/roam/security/vuln_reach.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/src/roam/security/vuln_store.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/src/roam/surface_counts.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/src/roam/templates/__init__.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/src/roam/templates/ci/__init__.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/src/roam/workspace/__init__.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/src/roam/workspace/aggregator.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/src/roam/workspace/api_scanner.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/src/roam/workspace/config.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/src/roam/workspace/db.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/src/roam_code.egg-info/dependency_links.txt +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/src/roam_code.egg-info/entry_points.txt +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/src/roam_code.egg-info/top_level.txt +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/tests/test_adrs.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/tests/test_adversarial.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/tests/test_affected.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/tests/test_agent_export.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/tests/test_agent_mode.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/tests/test_agent_plan_context.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/tests/test_ai_ratio.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/tests/test_ai_readiness.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/tests/test_alerts_cmd.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/tests/test_annotations.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/tests/test_anomaly.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/tests/test_api_changes.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/tests/test_api_drift.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/tests/test_ask.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/tests/test_attest.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/tests/test_auth_gaps.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/tests/test_backend_fixes_round2.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/tests/test_backend_fixes_round3.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/tests/test_basic.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/tests/test_bisect.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/tests/test_bridge_django.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/tests/test_bridges.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/tests/test_bridges_extended.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/tests/test_budget.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/tests/test_budget_flag.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/tests/test_budget_phase2.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/tests/test_bus_factor.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/tests/test_capsule.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/tests/test_cga.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/tests/test_check_rules.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/tests/test_ci_gate_eval.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/tests/test_ci_sarif_guard.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/tests/test_ci_setup.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/tests/test_clones.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/tests/test_closure.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/tests/test_codeowners.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/tests/test_commands_architecture.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/tests/test_commands_exploration.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/tests/test_commands_health.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/tests/test_commands_refactoring.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/tests/test_commands_workflow.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/tests/test_competitor_site_data.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/tests/test_comprehensive.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/tests/test_config.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/tests/test_congestion.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/tests/test_context_propagation.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/tests/test_conventions_cmd.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/tests/test_coverage_gaps_cmd.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/tests/test_coverage_ingestion.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/tests/test_critique.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/tests/test_cut.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/tests/test_dark_matter.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/tests/test_dark_matter_helpers.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/tests/test_dashboard.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/tests/test_dataflow_dead.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/tests/test_dead_aging.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/tests/test_defer_loading.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/tests/test_demo_gif_asset.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/tests/test_describe.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/tests/test_detail_flag_hints.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/tests/test_deterministic_output.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/tests/test_dev_profile.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/tests/test_difficulty_scoring.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/tests/test_doc_staleness.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/tests/test_docker_assets.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/tests/test_docs_coverage.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/tests/test_docs_site_quality.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/tests/test_doctor.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/tests/test_drift.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/tests/test_duplicates.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/tests/test_effects.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/tests/test_effects_propagation.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/tests/test_endpoints.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/tests/test_entry_points_cmd.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/tests/test_eval_retrieve.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/tests/test_exclude_patterns.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/tests/test_exit_codes.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/tests/test_file_roles.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/tests/test_fingerprint.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/tests/test_fixes.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/tests/test_flag_dead.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/tests/test_fleet.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/tests/test_fn_coupling.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/tests/test_forecast.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/tests/test_formatters.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/tests/test_foxpro.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/tests/test_framework_detection.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/tests/test_gate_presets.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/tests/test_git_utils.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/tests/test_guard.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/tests/test_health_gate.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/tests/test_hooks.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/tests/test_hotspots.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/tests/test_index.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/tests/test_index_bundle.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/tests/test_init_cmd.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/tests/test_install_check.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/tests/test_intent.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/tests/test_invariants.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/tests/test_json_contracts.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/tests/test_json_error_envelope.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/tests/test_kotlin_swift_extractors.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/tests/test_language_corpus.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/tests/test_languages.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/tests/test_library_api.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/tests/test_math.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/tests/test_math_tips.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/tests/test_mcp_extras.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/tests/test_mcp_setup.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/tests/test_mermaid.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/tests/test_metrics_cmd.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/tests/test_migration_safety.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/tests/test_minimap.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/tests/test_missing_index.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/tests/test_mutate.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/tests/test_n1.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/tests/test_next_steps.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/tests/test_onboard.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/tests/test_oracle.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/tests/test_orchestrate.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/tests/test_orphan_routes.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/tests/test_oss_bench_harness.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/tests/test_over_fetch.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/tests/test_pagerank_truncation.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/tests/test_partition.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/tests/test_path_coverage.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/tests/test_patterns_cmd.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/tests/test_performance.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/tests/test_personalized_pagerank.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/tests/test_plan.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/tests/test_plugin_discovery.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/tests/test_pr_comment_script.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/tests/test_pr_diff.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/tests/test_pr_risk_author.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/tests/test_progress.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/tests/test_progressive_disclosure.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/tests/test_properties.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/tests/test_python_extractor_v2.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/tests/test_refactoring_intelligence.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/tests/test_relate.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/tests/test_report.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/tests/test_reset_clean.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/tests/test_resolve.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/tests/test_retrieve_seeds.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/tests/test_risk.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/tests/test_ruby.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/tests/test_rule_profiles.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/tests/test_rules.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/tests/test_rules_ast_match.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/tests/test_rules_community_pack.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/tests/test_rules_dataflow.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/tests/test_rules_symbol_requirements.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/tests/test_runtime.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/tests/test_runtime_score.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/tests/test_salesforce.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/tests/test_sarif_flag.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/tests/test_sbom.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/tests/test_scala.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/tests/test_schema_versioning.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/tests/test_search_explain.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/tests/test_secrets.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/tests/test_secrets_v2.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/tests/test_semantic_diff.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/tests/test_semantic_onnx.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/tests/test_semantic_search.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/tests/test_simulate.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/tests/test_simulate_departure.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/tests/test_sketch.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/tests/test_smells.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/tests/test_smoke.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/tests/test_sna_metrics.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/tests/test_spectral.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/tests/test_split_cmd.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/tests/test_sql.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/tests/test_suggest_reviewers.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/tests/test_supply_chain.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/tests/test_surface_counts.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/tests/test_syntax_check.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/tests/test_taint.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/tests/test_taint_analysis.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/tests/test_taint_classifier.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/tests/test_test_conventions.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/tests/test_test_gaps.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/tests/test_test_scaffold.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/tests/test_testmap.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/tests/test_top_flag_consistency.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/tests/test_tour_cmd.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/tests/test_trends.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/tests/test_trends_cohort.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/tests/test_triage.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/tests/test_uses_cmd.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/tests/test_v6_features.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/tests/test_v71_features.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/tests/test_v7_features.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/tests/test_v82_features.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/tests/test_verify.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/tests/test_verify_imports.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/tests/test_vibe_check.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/tests/test_visualize.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/tests/test_vuln.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/tests/test_vulns_cmd.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/tests/test_watch.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/tests/test_why.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/tests/test_workspace.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/tests/test_ws_resolve_fixes.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.0}/tests/test_xlang.py +0 -0
- {roam_code-12.1.0 → roam_code-12.2.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.2.0
|
|
4
4
|
Summary: Instant codebase comprehension for AI coding agents
|
|
5
5
|
Author: CosmoHac
|
|
6
6
|
License-Expression: MIT
|
|
@@ -37,6 +37,15 @@ Provides-Extra: semantic
|
|
|
37
37
|
Requires-Dist: numpy>=1.24; extra == "semantic"
|
|
38
38
|
Requires-Dist: onnxruntime>=1.16; extra == "semantic"
|
|
39
39
|
Requires-Dist: tokenizers>=0.15; extra == "semantic"
|
|
40
|
+
Provides-Extra: leiden
|
|
41
|
+
Requires-Dist: igraph>=0.11; extra == "leiden"
|
|
42
|
+
Requires-Dist: leidenalg>=0.10; extra == "leiden"
|
|
43
|
+
Provides-Extra: graph-fast
|
|
44
|
+
Requires-Dist: rustworkx>=0.14; extra == "graph-fast"
|
|
45
|
+
Provides-Extra: sbom
|
|
46
|
+
Requires-Dist: cyclonedx-python-lib>=8.0; extra == "sbom"
|
|
47
|
+
Provides-Extra: learned
|
|
48
|
+
Requires-Dist: lightgbm>=4.0; extra == "learned"
|
|
40
49
|
Provides-Extra: dev
|
|
41
50
|
Requires-Dist: pytest>=7.0; extra == "dev"
|
|
42
51
|
Requires-Dist: pytest-xdist>=3.0; extra == "dev"
|
|
@@ -51,7 +60,7 @@ Dynamic: license-file
|
|
|
51
60
|
|
|
52
61
|
**The architectural intelligence layer for AI coding agents. Structural graph, architecture governance, multi-agent orchestration, vulnerability mapping, runtime analysis -- one CLI, zero API keys.**
|
|
53
62
|
|
|
54
|
-
*150 commands ·
|
|
63
|
+
*150 commands · 116 MCP tools · 27 languages · 100% local*
|
|
55
64
|
|
|
56
65
|
[](https://pypi.org/project/roam-code/)
|
|
57
66
|
[](https://github.com/Cranot/roam-code/stargazers)
|
|
@@ -128,7 +137,7 @@ $ roam diff # blast radius of uncommitted changes
|
|
|
128
137
|
- **`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.
|
|
129
138
|
- **`.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`.
|
|
130
139
|
- **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).
|
|
131
|
-
- **150 CLI commands,
|
|
140
|
+
- **150 CLI commands, 116 MCP tools** (`fleet`, `ask`, `taint`, `cga`, `eval-retrieve` remain CLI-only; v12 exposes `roam_retrieve`, `roam_critique`, `roam_fleet_plan`, plus 5 v12.1 boolean oracles (`roam_oracle_*`) and `roam_taint_classify` as MCP tools). 33-tool `core` preset is the default for token-budget-conscious clients.
|
|
132
141
|
|
|
133
142
|
## What's New in v11
|
|
134
143
|
|
|
@@ -159,7 +168,7 @@ $ roam diff # blast radius of uncommitted changes
|
|
|
159
168
|
- MCP token overhead for default core context dropped from ~36K to <3K tokens (about 92% reduction).
|
|
160
169
|
|
|
161
170
|
### Performance and Retrieval
|
|
162
|
-
- Symbol search moved to SQLite FTS5/BM25: typical search moved from seconds to milliseconds (
|
|
171
|
+
- Symbol search moved to SQLite FTS5/BM25: typical search moved from seconds to tens of milliseconds on the indexed cohort (mileage varies by repo size and query selectivity — see `bench/retrieve/` for the methodology).
|
|
163
172
|
- Incremental indexing shifted from O(N) full-edge rebuild behavior to O(changed) updates.
|
|
164
173
|
- DB/runtime optimizations (`mmap_size`, safer large-graph guards, batched writes) reduce first-run and reindex friction on larger repos.
|
|
165
174
|
|
|
@@ -299,7 +308,7 @@ roam health
|
|
|
299
308
|
|
|
300
309
|
## Commands
|
|
301
310
|
|
|
302
|
-
The [5 core commands](#core-commands)
|
|
311
|
+
**Lead with the 5 verbs.** The [5 core commands](#core-commands) cover ~80% of agent workflows: `understand`, `context`, `retrieve`, `preflight`, `critique`. The remaining 145 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 **150 commands organised into 7 categories**, but you don't need to know that to start.
|
|
303
312
|
|
|
304
313
|
<details>
|
|
305
314
|
<summary><strong>Full command reference</strong></summary>
|
|
@@ -937,7 +946,7 @@ ROAM_MCP_LITE=0 roam mcp
|
|
|
937
946
|
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`.
|
|
938
947
|
|
|
939
948
|
<details>
|
|
940
|
-
<summary><strong>MCP tool list (all
|
|
949
|
+
<summary><strong>MCP tool list (all 116)</strong></summary>
|
|
941
950
|
|
|
942
951
|
| Tool | Description |
|
|
943
952
|
|------|-------------|
|
|
@@ -957,6 +966,10 @@ Core preset tools: `roam_affected_tests`, `roam_batch_get`, `roam_batch_search`,
|
|
|
957
966
|
| `roam_oracle_is_reachable_from_entry` | Boolean oracle: can BFS reach this symbol from any entry-point? |
|
|
958
967
|
| `roam_oracle_is_clone_of` | Boolean oracle: does this symbol participate in a persisted clone cluster? |
|
|
959
968
|
| `roam_taint_classify` | LLM-augmented taint classification (IDOR/AUTHZ/SQLI/...) via MCP sampling |
|
|
969
|
+
| `roam_taint` | Static taint analysis — graph-reach BFS with OpenVEX-correct findings |
|
|
970
|
+
| `roam_sbom` | CycloneDX 1.7 / SPDX 2.3 SBOM emit with optional AIBOM extension (EU AI Act) |
|
|
971
|
+
| `roam_cga_emit` | Emit a Code Graph Attestation (in-toto v1) with optional cosign signing |
|
|
972
|
+
| `roam_cga_verify` | Verify a CGA statement — re-derives Merkle + edge digest, checks cosign signature |
|
|
960
973
|
| `roam_trace` | Dependency path between two symbols |
|
|
961
974
|
| `roam_impact` | Blast radius of changing a symbol |
|
|
962
975
|
| `roam_file_info` | File skeleton with all definitions |
|
|
@@ -1568,7 +1581,7 @@ roam-code/
|
|
|
1568
1581
|
├── src/roam/
|
|
1569
1582
|
│ ├── __init__.py # Version (from pyproject.toml)
|
|
1570
1583
|
│ ├── cli.py # Click CLI (150 commands)
|
|
1571
|
-
│ ├── mcp_server.py # MCP server (
|
|
1584
|
+
│ ├── mcp_server.py # MCP server (116 tools, 10 resources, 5 prompts)
|
|
1572
1585
|
│ ├── db/
|
|
1573
1586
|
│ │ ├── connection.py # SQLite (WAL, pragmas, batched IN)
|
|
1574
1587
|
│ │ ├── schema.py # Tables, indexes, migrations
|
|
@@ -1662,7 +1675,7 @@ Optional: Local semantic ONNX stack (`numpy`, `onnxruntime`, `tokenizers`) via `
|
|
|
1662
1675
|
### Shipped
|
|
1663
1676
|
|
|
1664
1677
|
- [x] MCP v2 agent surface: in-process execution, compound operations, presets, schemas, annotations, and compatibility profiles.
|
|
1665
|
-
- [x] Full command and MCP inventory parity in docs: 150 CLI commands and
|
|
1678
|
+
- [x] Full command and MCP inventory parity in docs: 150 CLI commands and 116 MCP tools.
|
|
1666
1679
|
- [x] CI hardening: composite action, changed-only mode, trend-aware gates, sticky PR updater, and SARIF guardrails.
|
|
1667
1680
|
- [x] Performance foundation: FTS5/BM25 search, O(changed) incremental indexing, DB/index optimizations.
|
|
1668
1681
|
- [x] Agent governance suite: `vibe-check`, `ai-readiness`, `verify`, `ai-ratio`, `duplicates`, advanced `algo` scoring/SARIF.
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
**The architectural intelligence layer for AI coding agents. Structural graph, architecture governance, multi-agent orchestration, vulnerability mapping, runtime analysis -- one CLI, zero API keys.**
|
|
6
6
|
|
|
7
|
-
*150 commands ·
|
|
7
|
+
*150 commands · 116 MCP tools · 27 languages · 100% local*
|
|
8
8
|
|
|
9
9
|
[](https://pypi.org/project/roam-code/)
|
|
10
10
|
[](https://github.com/Cranot/roam-code/stargazers)
|
|
@@ -81,7 +81,7 @@ $ roam diff # blast radius of uncommitted changes
|
|
|
81
81
|
- **`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
82
|
- **`.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
83
|
- **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
|
-
- **150 CLI commands,
|
|
84
|
+
- **150 CLI commands, 116 MCP tools** (`fleet`, `ask`, `taint`, `cga`, `eval-retrieve` remain CLI-only; v12 exposes `roam_retrieve`, `roam_critique`, `roam_fleet_plan`, plus 5 v12.1 boolean oracles (`roam_oracle_*`) and `roam_taint_classify` as MCP tools). 33-tool `core` preset is the default for token-budget-conscious clients.
|
|
85
85
|
|
|
86
86
|
## What's New in v11
|
|
87
87
|
|
|
@@ -112,7 +112,7 @@ $ roam diff # blast radius of uncommitted changes
|
|
|
112
112
|
- MCP token overhead for default core context dropped from ~36K to <3K tokens (about 92% reduction).
|
|
113
113
|
|
|
114
114
|
### Performance and Retrieval
|
|
115
|
-
- Symbol search moved to SQLite FTS5/BM25: typical search moved from seconds to milliseconds (
|
|
115
|
+
- Symbol search moved to SQLite FTS5/BM25: typical search moved from seconds to tens of milliseconds on the indexed cohort (mileage varies by repo size and query selectivity — see `bench/retrieve/` for the methodology).
|
|
116
116
|
- Incremental indexing shifted from O(N) full-edge rebuild behavior to O(changed) updates.
|
|
117
117
|
- DB/runtime optimizations (`mmap_size`, safer large-graph guards, batched writes) reduce first-run and reindex friction on larger repos.
|
|
118
118
|
|
|
@@ -252,7 +252,7 @@ roam health
|
|
|
252
252
|
|
|
253
253
|
## Commands
|
|
254
254
|
|
|
255
|
-
The [5 core commands](#core-commands)
|
|
255
|
+
**Lead with the 5 verbs.** The [5 core commands](#core-commands) cover ~80% of agent workflows: `understand`, `context`, `retrieve`, `preflight`, `critique`. The remaining 145 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 **150 commands organised into 7 categories**, but you don't need to know that to start.
|
|
256
256
|
|
|
257
257
|
<details>
|
|
258
258
|
<summary><strong>Full command reference</strong></summary>
|
|
@@ -890,7 +890,7 @@ ROAM_MCP_LITE=0 roam mcp
|
|
|
890
890
|
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`.
|
|
891
891
|
|
|
892
892
|
<details>
|
|
893
|
-
<summary><strong>MCP tool list (all
|
|
893
|
+
<summary><strong>MCP tool list (all 116)</strong></summary>
|
|
894
894
|
|
|
895
895
|
| Tool | Description |
|
|
896
896
|
|------|-------------|
|
|
@@ -910,6 +910,10 @@ Core preset tools: `roam_affected_tests`, `roam_batch_get`, `roam_batch_search`,
|
|
|
910
910
|
| `roam_oracle_is_reachable_from_entry` | Boolean oracle: can BFS reach this symbol from any entry-point? |
|
|
911
911
|
| `roam_oracle_is_clone_of` | Boolean oracle: does this symbol participate in a persisted clone cluster? |
|
|
912
912
|
| `roam_taint_classify` | LLM-augmented taint classification (IDOR/AUTHZ/SQLI/...) via MCP sampling |
|
|
913
|
+
| `roam_taint` | Static taint analysis — graph-reach BFS with OpenVEX-correct findings |
|
|
914
|
+
| `roam_sbom` | CycloneDX 1.7 / SPDX 2.3 SBOM emit with optional AIBOM extension (EU AI Act) |
|
|
915
|
+
| `roam_cga_emit` | Emit a Code Graph Attestation (in-toto v1) with optional cosign signing |
|
|
916
|
+
| `roam_cga_verify` | Verify a CGA statement — re-derives Merkle + edge digest, checks cosign signature |
|
|
913
917
|
| `roam_trace` | Dependency path between two symbols |
|
|
914
918
|
| `roam_impact` | Blast radius of changing a symbol |
|
|
915
919
|
| `roam_file_info` | File skeleton with all definitions |
|
|
@@ -1521,7 +1525,7 @@ roam-code/
|
|
|
1521
1525
|
├── src/roam/
|
|
1522
1526
|
│ ├── __init__.py # Version (from pyproject.toml)
|
|
1523
1527
|
│ ├── cli.py # Click CLI (150 commands)
|
|
1524
|
-
│ ├── mcp_server.py # MCP server (
|
|
1528
|
+
│ ├── mcp_server.py # MCP server (116 tools, 10 resources, 5 prompts)
|
|
1525
1529
|
│ ├── db/
|
|
1526
1530
|
│ │ ├── connection.py # SQLite (WAL, pragmas, batched IN)
|
|
1527
1531
|
│ │ ├── schema.py # Tables, indexes, migrations
|
|
@@ -1615,7 +1619,7 @@ Optional: Local semantic ONNX stack (`numpy`, `onnxruntime`, `tokenizers`) via `
|
|
|
1615
1619
|
### Shipped
|
|
1616
1620
|
|
|
1617
1621
|
- [x] MCP v2 agent surface: in-process execution, compound operations, presets, schemas, annotations, and compatibility profiles.
|
|
1618
|
-
- [x] Full command and MCP inventory parity in docs: 150 CLI commands and
|
|
1622
|
+
- [x] Full command and MCP inventory parity in docs: 150 CLI commands and 116 MCP tools.
|
|
1619
1623
|
- [x] CI hardening: composite action, changed-only mode, trend-aware gates, sticky PR updater, and SARIF guardrails.
|
|
1620
1624
|
- [x] Performance foundation: FTS5/BM25 search, O(changed) incremental indexing, DB/index optimizations.
|
|
1621
1625
|
- [x] Agent governance suite: `vibe-check`, `ai-readiness`, `verify`, `ai-ratio`, `duplicates`, advanced `algo` scoring/SARIF.
|
|
@@ -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.2.0"
|
|
8
8
|
description = "Instant codebase comprehension for AI coding agents"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.9"
|
|
@@ -55,6 +55,29 @@ semantic = [
|
|
|
55
55
|
"onnxruntime>=1.16",
|
|
56
56
|
"tokenizers>=0.15",
|
|
57
57
|
]
|
|
58
|
+
leiden = [
|
|
59
|
+
# Strictly better community detection than Louvain (no badly-connected
|
|
60
|
+
# communities). Auto-detected at runtime; falls back to seeded Louvain
|
|
61
|
+
# when these aren't installed.
|
|
62
|
+
"igraph>=0.11",
|
|
63
|
+
"leidenalg>=0.10",
|
|
64
|
+
]
|
|
65
|
+
graph-fast = [
|
|
66
|
+
# rustworkx is a Rust-backed drop-in for many NetworkX algorithms;
|
|
67
|
+
# 3-100× speedup at >250k nodes per the v12.2 architecture review.
|
|
68
|
+
# Auto-detected via ``ROAM_GRAPH_BACKEND=rustworkx``.
|
|
69
|
+
"rustworkx>=0.14",
|
|
70
|
+
]
|
|
71
|
+
sbom = [
|
|
72
|
+
# CycloneDX 1.7 SBOM emit (with AIBOM extension) for `roam sbom`.
|
|
73
|
+
# Falls back to in-tree minimal emitter when not installed.
|
|
74
|
+
"cyclonedx-python-lib>=8.0",
|
|
75
|
+
]
|
|
76
|
+
learned = [
|
|
77
|
+
# LightGBM LambdaMART distillation for `roam retrieve --rerank learned`.
|
|
78
|
+
# Optional — current `--rerank fast` blend stays the default.
|
|
79
|
+
"lightgbm>=4.0",
|
|
80
|
+
]
|
|
58
81
|
dev = [
|
|
59
82
|
"pytest>=7.0",
|
|
60
83
|
"pytest-xdist>=3.0",
|
|
@@ -31,6 +31,11 @@ from typing import Any
|
|
|
31
31
|
from roam.security.taint_engine import OPENVEX_JUSTIFICATIONS, OPENVEX_STATUSES
|
|
32
32
|
|
|
33
33
|
PREDICATE_TYPE = "https://roam-code.dev/CodeGraph/v1"
|
|
34
|
+
# v12.2: fused CodeGraph + AIBOM predicate. Owns the "structurally bound
|
|
35
|
+
# AI authorship for tamper-evident codebases" lane that SLSA + SPDX +
|
|
36
|
+
# CycloneDX 1.7 + OpenVEX leave gapped. Reference impl candidate for
|
|
37
|
+
# the in-toto attestation registry.
|
|
38
|
+
PREDICATE_TYPE_AIBOM = "https://roam-code.dev/CodeGraph-AIBOM/v1"
|
|
34
39
|
STATEMENT_TYPE = "https://in-toto.io/Statement/v1"
|
|
35
40
|
SCHEMA_VERSION = "1"
|
|
36
41
|
|
|
@@ -218,6 +223,7 @@ def build_cga_statement(
|
|
|
218
223
|
project_root: Path,
|
|
219
224
|
tool_version: str | None = None,
|
|
220
225
|
taint_findings: list | None = None,
|
|
226
|
+
include_aibom: bool = False,
|
|
221
227
|
) -> dict[str, Any]:
|
|
222
228
|
"""Build the full in-toto v1 Statement wrapping the CGA predicate.
|
|
223
229
|
|
|
@@ -225,9 +231,15 @@ def build_cga_statement(
|
|
|
225
231
|
{
|
|
226
232
|
"_type": "https://in-toto.io/Statement/v1",
|
|
227
233
|
"predicateType": "https://roam-code.dev/CodeGraph/v1",
|
|
234
|
+
# → CodeGraph-AIBOM/v1 when include_aibom=True
|
|
228
235
|
"subject": [{"name": "...", "digest": {...}}],
|
|
229
236
|
"predicate": {...}
|
|
230
237
|
}
|
|
238
|
+
|
|
239
|
+
With ``include_aibom=True``, the predicate type promotes to
|
|
240
|
+
``CodeGraph-AIBOM/v1`` and embeds an ``aibom`` block binding
|
|
241
|
+
AI-authored commits to the indexed symbols they touched. Required
|
|
242
|
+
for EU AI Act Art. 50 disclosure (effective 2026-08-02).
|
|
231
243
|
"""
|
|
232
244
|
sha = _git_commit_sha(project_root) or "unknown"
|
|
233
245
|
remote = _git_remote_url(project_root)
|
|
@@ -236,16 +248,26 @@ def build_cga_statement(
|
|
|
236
248
|
"name": subject_name,
|
|
237
249
|
"digest": {"git_commit_sha1": sha},
|
|
238
250
|
}
|
|
251
|
+
predicate = build_cga_predicate(
|
|
252
|
+
conn,
|
|
253
|
+
project_root=project_root,
|
|
254
|
+
tool_version=tool_version,
|
|
255
|
+
taint_findings=taint_findings,
|
|
256
|
+
)
|
|
257
|
+
predicate_type = PREDICATE_TYPE
|
|
258
|
+
if include_aibom:
|
|
259
|
+
try:
|
|
260
|
+
from roam.security.aibom_extension import build_aibom_block
|
|
261
|
+
|
|
262
|
+
predicate["aibom"] = build_aibom_block(project_root, conn)
|
|
263
|
+
predicate_type = PREDICATE_TYPE_AIBOM
|
|
264
|
+
except Exception as exc:
|
|
265
|
+
predicate["aibom_error"] = str(exc)
|
|
239
266
|
return {
|
|
240
267
|
"_type": STATEMENT_TYPE,
|
|
241
|
-
"predicateType":
|
|
268
|
+
"predicateType": predicate_type,
|
|
242
269
|
"subject": [subject],
|
|
243
|
-
"predicate":
|
|
244
|
-
conn,
|
|
245
|
-
project_root=project_root,
|
|
246
|
-
tool_version=tool_version,
|
|
247
|
-
taint_findings=taint_findings,
|
|
248
|
-
),
|
|
270
|
+
"predicate": predicate,
|
|
249
271
|
}
|
|
250
272
|
|
|
251
273
|
|
|
@@ -270,8 +292,11 @@ def verify_cga_statement(
|
|
|
270
292
|
errors: list[str] = []
|
|
271
293
|
if statement.get("_type") != STATEMENT_TYPE:
|
|
272
294
|
errors.append(f"_type mismatch: got {statement.get('_type')!r}, expected {STATEMENT_TYPE!r}")
|
|
273
|
-
|
|
274
|
-
|
|
295
|
+
accepted_types = (PREDICATE_TYPE, PREDICATE_TYPE_AIBOM)
|
|
296
|
+
if statement.get("predicateType") not in accepted_types:
|
|
297
|
+
errors.append(
|
|
298
|
+
f"predicateType mismatch: got {statement.get('predicateType')!r}, expected one of {accepted_types!r}"
|
|
299
|
+
)
|
|
275
300
|
predicate = statement.get("predicate") or {}
|
|
276
301
|
if not isinstance(predicate, dict):
|
|
277
302
|
return False, errors + ["predicate is not a JSON object"]
|
|
@@ -110,9 +110,25 @@ def _default_output_path(project_root: Path, statement: dict) -> Path:
|
|
|
110
110
|
"browser flow."
|
|
111
111
|
),
|
|
112
112
|
)
|
|
113
|
+
@click.option(
|
|
114
|
+
"--aibom",
|
|
115
|
+
is_flag=True,
|
|
116
|
+
help=(
|
|
117
|
+
"Promote the predicate to ``CodeGraph-AIBOM/v1`` and embed an "
|
|
118
|
+
"AIBOM block binding AI-authored commits to the indexed symbols "
|
|
119
|
+
"they touched. Required for EU AI Act Art. 50 disclosure (effective "
|
|
120
|
+
"2026-08-02) and the GPAI Code of Practice provenance mandate. "
|
|
121
|
+
"Reference impl candidate for the in-toto attestation registry."
|
|
122
|
+
),
|
|
123
|
+
)
|
|
113
124
|
@click.pass_context
|
|
114
|
-
def cga_emit(ctx, output_path, no_write, include_taint, taint_rules_dir, sign, key_path, keyless):
|
|
115
|
-
"""Emit a Code Graph Attestation (in-toto v1, optionally cosign-signed).
|
|
125
|
+
def cga_emit(ctx, output_path, no_write, include_taint, taint_rules_dir, sign, key_path, keyless, aibom):
|
|
126
|
+
"""Emit a Code Graph Attestation (in-toto v1, optionally cosign-signed).
|
|
127
|
+
|
|
128
|
+
With ``--aibom`` the predicate type promotes to
|
|
129
|
+
``roam-code.dev/CodeGraph-AIBOM/v1`` and the predicate gains an
|
|
130
|
+
``aibom`` block binding AI-authored commits to indexed symbols.
|
|
131
|
+
"""
|
|
116
132
|
json_mode = ctx.obj.get("json") if ctx.obj else False
|
|
117
133
|
token_budget = ctx.obj.get("budget", 0) if ctx.obj else 0
|
|
118
134
|
|
|
@@ -137,6 +153,7 @@ def cga_emit(ctx, output_path, no_write, include_taint, taint_rules_dir, sign, k
|
|
|
137
153
|
conn,
|
|
138
154
|
project_root=project_root,
|
|
139
155
|
taint_findings=taint_findings,
|
|
156
|
+
include_aibom=aibom,
|
|
140
157
|
)
|
|
141
158
|
|
|
142
159
|
canonical = serialize_statement(statement)
|
|
@@ -198,7 +215,7 @@ def cga_emit(ctx, output_path, no_write, include_taint, taint_rules_dir, sign, k
|
|
|
198
215
|
"edge_bundle_digest": pred["edge_bundle_digest"],
|
|
199
216
|
"symbol_count": pred["symbol_count"],
|
|
200
217
|
"edge_count": pred["edge_count"],
|
|
201
|
-
"predicate_type": PREDICATE_TYPE,
|
|
218
|
+
"predicate_type": statement.get("predicateType", PREDICATE_TYPE),
|
|
202
219
|
"statement_type": STATEMENT_TYPE,
|
|
203
220
|
"written_to": written_to,
|
|
204
221
|
"signed": bool(sign_result and sign_result.get("signed")),
|
|
@@ -212,7 +229,7 @@ def cga_emit(ctx, output_path, no_write, include_taint, taint_rules_dir, sign, k
|
|
|
212
229
|
return
|
|
213
230
|
|
|
214
231
|
click.echo(f"VERDICT: {verdict}")
|
|
215
|
-
click.echo(f"Predicate type: {PREDICATE_TYPE}")
|
|
232
|
+
click.echo(f"Predicate type: {statement.get('predicateType', PREDICATE_TYPE)}")
|
|
216
233
|
click.echo(f"Statement type: {STATEMENT_TYPE}")
|
|
217
234
|
click.echo(f"Languages: {', '.join(pred['languages']) or '(none)'}")
|
|
218
235
|
if written_to:
|
|
@@ -60,8 +60,19 @@ _CONFIGS = {
|
|
|
60
60
|
|
|
61
61
|
@click.command("mcp-setup")
|
|
62
62
|
@click.argument("platform", type=click.Choice(sorted(_CONFIGS.keys())), required=False)
|
|
63
|
+
@click.option(
|
|
64
|
+
"--preset",
|
|
65
|
+
type=click.Choice(["core", "review", "refactor", "debug", "architecture", "compliance", "full"]),
|
|
66
|
+
default=None,
|
|
67
|
+
help=(
|
|
68
|
+
"Pre-fill the generated config with ``ROAM_MCP_PRESET=<preset>``. "
|
|
69
|
+
"Default = no env var (uses 'core'). v12.2 adds the 'compliance' "
|
|
70
|
+
"preset for the EU AI Act / NIS2 buyer wedge — 14 tools covering "
|
|
71
|
+
"preflight, taint, sbom, cga emit/verify."
|
|
72
|
+
),
|
|
73
|
+
)
|
|
63
74
|
@click.pass_context
|
|
64
|
-
def mcp_setup(ctx, platform):
|
|
75
|
+
def mcp_setup(ctx, platform, preset):
|
|
65
76
|
"""Generate MCP server config for AI coding platforms.
|
|
66
77
|
|
|
67
78
|
Prints the exact JSON config block to paste into your AI coding tool.
|
|
@@ -77,10 +88,17 @@ def mcp_setup(ctx, platform):
|
|
|
77
88
|
gemini-cli Gemini CLI
|
|
78
89
|
codex-cli OpenAI Codex CLI
|
|
79
90
|
|
|
91
|
+
\b
|
|
92
|
+
Presets (v12.2):
|
|
93
|
+
core 33 tools — default, balanced for daily agent use
|
|
94
|
+
compliance 14 tools — EU AI Act / NIS2 audit (taint, sbom, cga, …)
|
|
95
|
+
full 116 tools — every tool exposed
|
|
96
|
+
review/refactor/debug/architecture — task-specific subsets
|
|
97
|
+
|
|
80
98
|
\b
|
|
81
99
|
Examples:
|
|
82
100
|
roam mcp-setup claude-code
|
|
83
|
-
roam mcp-setup cursor
|
|
101
|
+
roam mcp-setup cursor --preset compliance
|
|
84
102
|
roam --json mcp-setup vscode
|
|
85
103
|
"""
|
|
86
104
|
json_mode = ctx.obj.get("json") if ctx.obj else False
|
|
@@ -106,6 +124,25 @@ def mcp_setup(ctx, platform):
|
|
|
106
124
|
|
|
107
125
|
cfg = _CONFIGS[platform]
|
|
108
126
|
|
|
127
|
+
# v12.2: when --preset is supplied, deep-copy the config and inject the
|
|
128
|
+
# ROAM_MCP_PRESET env var into the server block. Each platform stores
|
|
129
|
+
# its server entry under a slightly different key shape (mcpServers vs
|
|
130
|
+
# servers) — handle both. Mutates a copy, not the module-level dict.
|
|
131
|
+
if preset:
|
|
132
|
+
import copy
|
|
133
|
+
|
|
134
|
+
cfg = copy.deepcopy(cfg)
|
|
135
|
+
jc = cfg.get("json_config") or {}
|
|
136
|
+
# Walk to the first server entry and add an "env" block.
|
|
137
|
+
for outer_key in ("mcpServers", "servers"):
|
|
138
|
+
if outer_key not in jc:
|
|
139
|
+
continue
|
|
140
|
+
for server_name, server_block in jc[outer_key].items():
|
|
141
|
+
env = server_block.setdefault("env", {})
|
|
142
|
+
env["ROAM_MCP_PRESET"] = preset
|
|
143
|
+
cfg["json_config"] = jc
|
|
144
|
+
cfg["preset"] = preset
|
|
145
|
+
|
|
109
146
|
if json_mode:
|
|
110
147
|
click.echo(
|
|
111
148
|
to_json(
|
|
@@ -38,9 +38,14 @@ from roam.retrieve.pipeline import run_retrieve
|
|
|
38
38
|
)
|
|
39
39
|
@click.option(
|
|
40
40
|
"--rerank",
|
|
41
|
-
type=click.Choice(["fast", "off"], case_sensitive=False),
|
|
41
|
+
type=click.Choice(["fast", "off", "learned"], case_sensitive=False),
|
|
42
42
|
default=None,
|
|
43
|
-
help=
|
|
43
|
+
help=(
|
|
44
|
+
"'fast' = structural rerank (default). 'off' = lexical only. "
|
|
45
|
+
"'learned' = LightGBM LambdaMART trained on your bench (requires "
|
|
46
|
+
'``pip install "roam-code[learned]"`` + a trained model at '
|
|
47
|
+
"``$ROAM_LEARNED_MODEL``; falls back to 'fast' when unavailable)."
|
|
48
|
+
),
|
|
44
49
|
)
|
|
45
50
|
@click.option(
|
|
46
51
|
"--seed-files",
|
|
@@ -270,7 +270,7 @@ def _generate_cyclonedx(
|
|
|
270
270
|
|
|
271
271
|
bom: dict = {
|
|
272
272
|
"bomFormat": "CycloneDX",
|
|
273
|
-
"specVersion": "1.
|
|
273
|
+
"specVersion": "1.7",
|
|
274
274
|
"version": 1,
|
|
275
275
|
"serialNumber": f"urn:uuid:{serial}",
|
|
276
276
|
"metadata": metadata,
|
|
@@ -413,8 +413,19 @@ def _generate_spdx(
|
|
|
413
413
|
default=False,
|
|
414
414
|
help="Skip call-graph reachability analysis (faster)",
|
|
415
415
|
)
|
|
416
|
+
@click.option(
|
|
417
|
+
"--aibom",
|
|
418
|
+
is_flag=True,
|
|
419
|
+
default=False,
|
|
420
|
+
help=(
|
|
421
|
+
"Embed the AIBOM extension (CycloneDX only) — bind AI-authored "
|
|
422
|
+
"commits (mined via committer email + Co-Authored-By trailers + "
|
|
423
|
+
"AI-keyword scan) to the indexed symbols they touched. Required "
|
|
424
|
+
"for EU AI Act Art. 50 disclosure (effective 2026-08-02)."
|
|
425
|
+
),
|
|
426
|
+
)
|
|
416
427
|
@click.pass_context
|
|
417
|
-
def sbom(ctx, fmt, output_path, no_reachability):
|
|
428
|
+
def sbom(ctx, fmt, output_path, no_reachability, aibom):
|
|
418
429
|
"""Generate a Software Bill of Materials (SBOM) enriched with call-graph reachability.
|
|
419
430
|
|
|
420
431
|
Produces CycloneDX 1.5 or SPDX 2.3 JSON output. Each dependency is
|
|
@@ -468,6 +479,19 @@ def sbom(ctx, fmt, output_path, no_reachability):
|
|
|
468
479
|
else:
|
|
469
480
|
sbom_data = _generate_cyclonedx(project_name, deps, reachability)
|
|
470
481
|
|
|
482
|
+
# AIBOM extension (CycloneDX 1.7 only) — bind AI-authored commits to
|
|
483
|
+
# indexed symbols. Required for EU AI Act Art. 50 disclosure.
|
|
484
|
+
if aibom and fmt.lower() == "cyclonedx":
|
|
485
|
+
try:
|
|
486
|
+
from roam.security.aibom_extension import build_aibom_block
|
|
487
|
+
|
|
488
|
+
ensure_index()
|
|
489
|
+
with open_db(readonly=True) as conn:
|
|
490
|
+
aibom_block = build_aibom_block(project_root, conn)
|
|
491
|
+
sbom_data["aibom"] = aibom_block
|
|
492
|
+
except Exception as exc:
|
|
493
|
+
sbom_data["aibom"] = {"error": str(exc), "version": "0.1"}
|
|
494
|
+
|
|
471
495
|
# Build summary for verdict / JSON envelope
|
|
472
496
|
total_deps = len(deps)
|
|
473
497
|
reachable_count = 0
|
|
@@ -1360,7 +1360,7 @@ MAP_METADATA: dict[str, dict[str, object]] = {
|
|
|
1360
1360
|
"relationship": "self",
|
|
1361
1361
|
"peer": True,
|
|
1362
1362
|
"graph": "PageRank + Tarjan + Louvain + layers",
|
|
1363
|
-
"note": "Graph algorithms (PageRank, SCC, Louvain, Fiedler) on tree-sitter ASTs fused with git history in SQLite.
|
|
1363
|
+
"note": "Graph algorithms (PageRank, SCC, Louvain, Fiedler) on tree-sitter ASTs fused with git history in SQLite. 116 MCP tools, 150 CLI commands.",
|
|
1364
1364
|
"version_evaluated": "11.1.2",
|
|
1365
1365
|
"repo_url": "https://github.com/Cranot/roam-code",
|
|
1366
1366
|
},
|
|
@@ -46,6 +46,10 @@ DEFAULT_RETRIEVE_WEIGHTS: dict[str, float] = {
|
|
|
46
46
|
"gamma": 0.15, # inverse layer distance
|
|
47
47
|
"delta": 0.15, # runtime hotspot signal
|
|
48
48
|
"epsilon": 0.05, # clone-canonical boost
|
|
49
|
+
"zeta": 0.20, # v12.2: semantic similarity (bge-small + sqlite-vec)
|
|
50
|
+
# Activates only when [semantic] extras are installed AND the
|
|
51
|
+
# symbol-embedding table is populated; otherwise contributes 0 and
|
|
52
|
+
# the original blend is preserved exactly.
|
|
49
53
|
}
|
|
50
54
|
|
|
51
55
|
DEFAULT_RETRIEVE: dict[str, Any] = {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
"""
|
|
1
|
+
"""Community detection for the symbol graph (Leiden → Louvain → greedy)."""
|
|
2
2
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
@@ -11,10 +11,56 @@ import networkx as nx
|
|
|
11
11
|
from roam.db.connection import batched_in
|
|
12
12
|
|
|
13
13
|
|
|
14
|
+
def _try_leiden_communities(undirected: nx.Graph, out: list[set[int]]) -> bool:
|
|
15
|
+
"""Run Leiden via the optional ``leidenalg`` + ``igraph`` extras.
|
|
16
|
+
|
|
17
|
+
Returns ``True`` and fills *out* in place when Leiden succeeded, else
|
|
18
|
+
``False`` so the caller can fall back to NetworkX Louvain. Leiden is
|
|
19
|
+
strictly better than Louvain per Traag et al. 2019 (Nature Sci. Rep.) —
|
|
20
|
+
no badly-connected communities, fully deterministic with the seed.
|
|
21
|
+
|
|
22
|
+
Install with::
|
|
23
|
+
|
|
24
|
+
pip install "roam-code[leiden]"
|
|
25
|
+
|
|
26
|
+
Skip with ``ROAM_LEIDEN=0`` (e.g., for offline / ABI-pin environments
|
|
27
|
+
where the C extension fails to load).
|
|
28
|
+
"""
|
|
29
|
+
if os.environ.get("ROAM_LEIDEN", "1") == "0":
|
|
30
|
+
return False
|
|
31
|
+
try:
|
|
32
|
+
import igraph as ig # type: ignore
|
|
33
|
+
import leidenalg # type: ignore
|
|
34
|
+
except ImportError:
|
|
35
|
+
return False
|
|
36
|
+
try:
|
|
37
|
+
# Build an igraph from the undirected NetworkX graph. Map node ids
|
|
38
|
+
# so we can translate back. Edge weights default to 1.0.
|
|
39
|
+
node_list = list(undirected.nodes())
|
|
40
|
+
index_for: dict[int, int] = {n: i for i, n in enumerate(node_list)}
|
|
41
|
+
edges = [(index_for[u], index_for[v]) for u, v in undirected.edges() if u in index_for and v in index_for]
|
|
42
|
+
ig_graph = ig.Graph(n=len(node_list), edges=edges, directed=False)
|
|
43
|
+
partition = leidenalg.find_partition(
|
|
44
|
+
ig_graph,
|
|
45
|
+
leidenalg.ModularityVertexPartition,
|
|
46
|
+
seed=42,
|
|
47
|
+
)
|
|
48
|
+
for community in partition:
|
|
49
|
+
out.append({node_list[idx] for idx in community})
|
|
50
|
+
return True
|
|
51
|
+
except Exception:
|
|
52
|
+
# leidenalg / igraph ABI mismatch or memory pressure — fall back.
|
|
53
|
+
out.clear()
|
|
54
|
+
return False
|
|
55
|
+
|
|
56
|
+
|
|
14
57
|
def detect_clusters(G: nx.DiGraph) -> dict[int, int]:
|
|
15
|
-
"""Detect communities
|
|
58
|
+
"""Detect communities on the undirected projection of *G*.
|
|
59
|
+
|
|
60
|
+
Algorithm preference: **Leiden** (via optional ``[leiden]`` extra) →
|
|
61
|
+
seeded **Louvain** (NetworkX) → **greedy modularity** (NetworkX
|
|
62
|
+
classic, last-resort).
|
|
16
63
|
|
|
17
|
-
Falls back to greedy modularity if Louvain is unavailable.
|
|
18
64
|
Returns ``{node_id: cluster_id}``.
|
|
19
65
|
"""
|
|
20
66
|
if len(G) == 0:
|
|
@@ -24,12 +70,21 @@ def detect_clusters(G: nx.DiGraph) -> dict[int, int]:
|
|
|
24
70
|
|
|
25
71
|
# Remove isolates -- community detection on disconnected singletons just
|
|
26
72
|
# assigns each its own community, which is not useful.
|
|
73
|
+
#
|
|
74
|
+
# Algorithm preference (best → fallback):
|
|
75
|
+
# 1. Leiden (via leidenalg + igraph extras): no badly-connected
|
|
76
|
+
# communities, fully deterministic with seed. Strictly better than
|
|
77
|
+
# Louvain per Traag et al. 2019 (Nature Sci. Rep.).
|
|
78
|
+
# 2. Louvain (NetworkX) with seed=42: deterministic but may produce
|
|
79
|
+
# badly-connected communities — the Leiden paper's main critique.
|
|
80
|
+
# 3. Greedy modularity: very old NetworkX fallback for offline envs.
|
|
27
81
|
communities: list[set[int]] = []
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
82
|
+
if not _try_leiden_communities(undirected, communities):
|
|
83
|
+
try:
|
|
84
|
+
communities = list(nx.community.louvain_communities(undirected, seed=42))
|
|
85
|
+
except (AttributeError, TypeError):
|
|
86
|
+
# NetworkX < 3.1 or missing optional dependency
|
|
87
|
+
communities = list(nx.community.greedy_modularity_communities(undirected))
|
|
33
88
|
|
|
34
89
|
mapping: dict[int, int] = {}
|
|
35
90
|
for cluster_id, members in enumerate(communities):
|