sourcecode 1.31.11__tar.gz → 1.31.13__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.
- {sourcecode-1.31.11 → sourcecode-1.31.13}/PKG-INFO +3 -3
- {sourcecode-1.31.11 → sourcecode-1.31.13}/README.md +2 -2
- {sourcecode-1.31.11 → sourcecode-1.31.13}/pyproject.toml +1 -1
- {sourcecode-1.31.11 → sourcecode-1.31.13}/src/sourcecode/__init__.py +1 -1
- {sourcecode-1.31.11 → sourcecode-1.31.13}/src/sourcecode/cli.py +289 -21
- {sourcecode-1.31.11 → sourcecode-1.31.13}/src/sourcecode/mcp/server.py +67 -11
- sourcecode-1.31.13/src/sourcecode/output_budget.py +157 -0
- {sourcecode-1.31.11 → sourcecode-1.31.13}/src/sourcecode/prepare_context.py +16 -23
- {sourcecode-1.31.11 → sourcecode-1.31.13}/src/sourcecode/repository_ir.py +604 -20
- {sourcecode-1.31.11 → sourcecode-1.31.13}/src/sourcecode/serializer.py +41 -4
- sourcecode-1.31.13/tests/test_audit_fixes.py +833 -0
- sourcecode-1.31.13/tests/test_audit_sas_v2.py +506 -0
- sourcecode-1.31.13/tests/test_enterprise_benchmarks.py +967 -0
- {sourcecode-1.31.11 → sourcecode-1.31.13}/tests/test_repository_ir.py +327 -0
- {sourcecode-1.31.11 → sourcecode-1.31.13}/tests/test_v1_10_regressions.py +119 -7
- sourcecode-1.31.11/.sourcecode-cache/snapshot-0778d0a-0911b79e.json +0 -3825
- sourcecode-1.31.11/.sourcecode-cache/snapshot-0778d0a-37df4554.json +0 -266
- sourcecode-1.31.11/.sourcecode-cache/snapshot-0778d0a-624321f3.json +0 -12478
- sourcecode-1.31.11/.sourcecode-cache/snapshot-0778d0a-776b4676.json +0 -386
- sourcecode-1.31.11/.sourcecode-cache/snapshot-0778d0a-9770fba7.json +0 -377
- sourcecode-1.31.11/.sourcecode-cache/snapshot-0778d0a-c4e3c102.json +0 -266
- sourcecode-1.31.11/.sourcecode-cache/snapshot-0778d0a-e8bc5fb4.json +0 -122
- sourcecode-1.31.11/.sourcecode-cache/snapshot-0778d0a-e9801942.json +0 -223
- sourcecode-1.31.11/.sourcecode-cache/snapshot-0778d0a-ee60e0cd.json +0 -322
- sourcecode-1.31.11/.sourcecode-cache/snapshot-0778d0a-fdd9d3f7.json +0 -552
- sourcecode-1.31.11/.sourcecode-cache/snapshot-3b5997a-27b713ac.json +0 -386
- sourcecode-1.31.11/.sourcecode-cache/snapshot-3b5997a-28e558ac.json +0 -325
- sourcecode-1.31.11/.sourcecode-cache/snapshot-3b5997a-2d7eca5f.json +0 -12577
- sourcecode-1.31.11/.sourcecode-cache/snapshot-3b5997a-7cd91058.json +0 -226
- sourcecode-1.31.11/.sourcecode-cache/snapshot-3b5997a-8ea5b5bb.json +0 -380
- sourcecode-1.31.11/.sourcecode-cache/snapshot-3b5997a-bef1a526.json +0 -261
- sourcecode-1.31.11/.sourcecode-cache/snapshot-3b5997a-c69bcdc8.json +0 -122
- sourcecode-1.31.11/.sourcecode-cache/snapshot-3b5997a-d2dfc690.json +0 -3825
- sourcecode-1.31.11/.sourcecode-cache/snapshot-3b5997a-dc30daab.json +0 -552
- {sourcecode-1.31.11 → sourcecode-1.31.13}/.agents/skills/source-command-gsd-join-discord/SKILL.md +0 -0
- {sourcecode-1.31.11 → sourcecode-1.31.13}/.agents/skills/source-command-gsd-review-backlog/SKILL.md +0 -0
- {sourcecode-1.31.11 → sourcecode-1.31.13}/.agents/skills/source-command-gsd-workstreams/SKILL.md +0 -0
- {sourcecode-1.31.11 → sourcecode-1.31.13}/.continue-here.md +0 -0
- {sourcecode-1.31.11 → sourcecode-1.31.13}/.github/workflows/build-windows.yml +0 -0
- {sourcecode-1.31.11 → sourcecode-1.31.13}/.gitignore +0 -0
- {sourcecode-1.31.11 → sourcecode-1.31.13}/.ruff.toml +0 -0
- {sourcecode-1.31.11 → sourcecode-1.31.13}/.sourcecode-cache/snapshot-3b5997a-fa5c742c.json +0 -0
- {sourcecode-1.31.11 → sourcecode-1.31.13}/CHANGELOG.md +0 -0
- {sourcecode-1.31.11 → sourcecode-1.31.13}/CONTRIBUTING.md +0 -0
- {sourcecode-1.31.11 → sourcecode-1.31.13}/LICENSE +0 -0
- {sourcecode-1.31.11 → sourcecode-1.31.13}/SECURITY.md +0 -0
- {sourcecode-1.31.11 → sourcecode-1.31.13}/docs/privacy.md +0 -0
- {sourcecode-1.31.11 → sourcecode-1.31.13}/docs/schema.md +0 -0
- {sourcecode-1.31.11 → sourcecode-1.31.13}/raw +0 -0
- {sourcecode-1.31.11 → sourcecode-1.31.13}/run_cli.py +0 -0
- {sourcecode-1.31.11 → sourcecode-1.31.13}/src/sourcecode/adaptive_scanner.py +0 -0
- {sourcecode-1.31.11 → sourcecode-1.31.13}/src/sourcecode/architecture_analyzer.py +0 -0
- {sourcecode-1.31.11 → sourcecode-1.31.13}/src/sourcecode/architecture_summary.py +0 -0
- {sourcecode-1.31.11 → sourcecode-1.31.13}/src/sourcecode/ast_extractor.py +0 -0
- {sourcecode-1.31.11 → sourcecode-1.31.13}/src/sourcecode/classifier.py +0 -0
- {sourcecode-1.31.11 → sourcecode-1.31.13}/src/sourcecode/code_notes_analyzer.py +0 -0
- {sourcecode-1.31.11 → sourcecode-1.31.13}/src/sourcecode/confidence_analyzer.py +0 -0
- {sourcecode-1.31.11 → sourcecode-1.31.13}/src/sourcecode/context_scorer.py +0 -0
- {sourcecode-1.31.11 → sourcecode-1.31.13}/src/sourcecode/context_summarizer.py +0 -0
- {sourcecode-1.31.11 → sourcecode-1.31.13}/src/sourcecode/contract_model.py +0 -0
- {sourcecode-1.31.11 → sourcecode-1.31.13}/src/sourcecode/contract_pipeline.py +0 -0
- {sourcecode-1.31.11 → sourcecode-1.31.13}/src/sourcecode/coverage_parser.py +0 -0
- {sourcecode-1.31.11 → sourcecode-1.31.13}/src/sourcecode/dependency_analyzer.py +0 -0
- {sourcecode-1.31.11 → sourcecode-1.31.13}/src/sourcecode/detectors/__init__.py +0 -0
- {sourcecode-1.31.11 → sourcecode-1.31.13}/src/sourcecode/detectors/base.py +0 -0
- {sourcecode-1.31.11 → sourcecode-1.31.13}/src/sourcecode/detectors/csproj_parser.py +0 -0
- {sourcecode-1.31.11 → sourcecode-1.31.13}/src/sourcecode/detectors/dart.py +0 -0
- {sourcecode-1.31.11 → sourcecode-1.31.13}/src/sourcecode/detectors/dotnet.py +0 -0
- {sourcecode-1.31.11 → sourcecode-1.31.13}/src/sourcecode/detectors/elixir.py +0 -0
- {sourcecode-1.31.11 → sourcecode-1.31.13}/src/sourcecode/detectors/go.py +0 -0
- {sourcecode-1.31.11 → sourcecode-1.31.13}/src/sourcecode/detectors/heuristic.py +0 -0
- {sourcecode-1.31.11 → sourcecode-1.31.13}/src/sourcecode/detectors/hybrid.py +0 -0
- {sourcecode-1.31.11 → sourcecode-1.31.13}/src/sourcecode/detectors/java.py +0 -0
- {sourcecode-1.31.11 → sourcecode-1.31.13}/src/sourcecode/detectors/jvm_ext.py +0 -0
- {sourcecode-1.31.11 → sourcecode-1.31.13}/src/sourcecode/detectors/nodejs.py +0 -0
- {sourcecode-1.31.11 → sourcecode-1.31.13}/src/sourcecode/detectors/parsers.py +0 -0
- {sourcecode-1.31.11 → sourcecode-1.31.13}/src/sourcecode/detectors/php.py +0 -0
- {sourcecode-1.31.11 → sourcecode-1.31.13}/src/sourcecode/detectors/project.py +0 -0
- {sourcecode-1.31.11 → sourcecode-1.31.13}/src/sourcecode/detectors/python.py +0 -0
- {sourcecode-1.31.11 → sourcecode-1.31.13}/src/sourcecode/detectors/ruby.py +0 -0
- {sourcecode-1.31.11 → sourcecode-1.31.13}/src/sourcecode/detectors/rust.py +0 -0
- {sourcecode-1.31.11 → sourcecode-1.31.13}/src/sourcecode/detectors/systems.py +0 -0
- {sourcecode-1.31.11 → sourcecode-1.31.13}/src/sourcecode/detectors/terraform.py +0 -0
- {sourcecode-1.31.11 → sourcecode-1.31.13}/src/sourcecode/detectors/tooling.py +0 -0
- {sourcecode-1.31.11 → sourcecode-1.31.13}/src/sourcecode/doc_analyzer.py +0 -0
- {sourcecode-1.31.11 → sourcecode-1.31.13}/src/sourcecode/entrypoint_classifier.py +0 -0
- {sourcecode-1.31.11 → sourcecode-1.31.13}/src/sourcecode/env_analyzer.py +0 -0
- {sourcecode-1.31.11 → sourcecode-1.31.13}/src/sourcecode/file_classifier.py +0 -0
- {sourcecode-1.31.11 → sourcecode-1.31.13}/src/sourcecode/flow_analyzer.py +0 -0
- {sourcecode-1.31.11 → sourcecode-1.31.13}/src/sourcecode/git_analyzer.py +0 -0
- {sourcecode-1.31.11 → sourcecode-1.31.13}/src/sourcecode/graph_analyzer.py +0 -0
- {sourcecode-1.31.11 → sourcecode-1.31.13}/src/sourcecode/mcp/__init__.py +0 -0
- {sourcecode-1.31.11 → sourcecode-1.31.13}/src/sourcecode/mcp/onboarding/__init__.py +0 -0
- {sourcecode-1.31.11 → sourcecode-1.31.13}/src/sourcecode/mcp/onboarding/applier.py +0 -0
- {sourcecode-1.31.11 → sourcecode-1.31.13}/src/sourcecode/mcp/onboarding/backup.py +0 -0
- {sourcecode-1.31.11 → sourcecode-1.31.13}/src/sourcecode/mcp/onboarding/detector.py +0 -0
- {sourcecode-1.31.11 → sourcecode-1.31.13}/src/sourcecode/mcp/onboarding/planner.py +0 -0
- {sourcecode-1.31.11 → sourcecode-1.31.13}/src/sourcecode/mcp/runner.py +0 -0
- {sourcecode-1.31.11 → sourcecode-1.31.13}/src/sourcecode/metrics_analyzer.py +0 -0
- {sourcecode-1.31.11 → sourcecode-1.31.13}/src/sourcecode/path_filters.py +0 -0
- {sourcecode-1.31.11 → sourcecode-1.31.13}/src/sourcecode/pr_comment_renderer.py +0 -0
- {sourcecode-1.31.11 → sourcecode-1.31.13}/src/sourcecode/progress.py +0 -0
- {sourcecode-1.31.11 → sourcecode-1.31.13}/src/sourcecode/ranking_engine.py +0 -0
- {sourcecode-1.31.11 → sourcecode-1.31.13}/src/sourcecode/redactor.py +0 -0
- {sourcecode-1.31.11 → sourcecode-1.31.13}/src/sourcecode/relevance_scorer.py +0 -0
- {sourcecode-1.31.11 → sourcecode-1.31.13}/src/sourcecode/repo_classifier.py +0 -0
- {sourcecode-1.31.11 → sourcecode-1.31.13}/src/sourcecode/runtime_classifier.py +0 -0
- {sourcecode-1.31.11 → sourcecode-1.31.13}/src/sourcecode/scanner.py +0 -0
- {sourcecode-1.31.11 → sourcecode-1.31.13}/src/sourcecode/schema.py +0 -0
- {sourcecode-1.31.11 → sourcecode-1.31.13}/src/sourcecode/semantic_analyzer.py +0 -0
- {sourcecode-1.31.11 → sourcecode-1.31.13}/src/sourcecode/summarizer.py +0 -0
- {sourcecode-1.31.11 → sourcecode-1.31.13}/src/sourcecode/telemetry/__init__.py +0 -0
- {sourcecode-1.31.11 → sourcecode-1.31.13}/src/sourcecode/telemetry/config.py +0 -0
- {sourcecode-1.31.11 → sourcecode-1.31.13}/src/sourcecode/telemetry/consent.py +0 -0
- {sourcecode-1.31.11 → sourcecode-1.31.13}/src/sourcecode/telemetry/events.py +0 -0
- {sourcecode-1.31.11 → sourcecode-1.31.13}/src/sourcecode/telemetry/filters.py +0 -0
- {sourcecode-1.31.11 → sourcecode-1.31.13}/src/sourcecode/telemetry/transport.py +0 -0
- {sourcecode-1.31.11 → sourcecode-1.31.13}/src/sourcecode/tree_utils.py +0 -0
- {sourcecode-1.31.11 → sourcecode-1.31.13}/src/sourcecode/workspace.py +0 -0
- {sourcecode-1.31.11 → sourcecode-1.31.13}/tests/__init__.py +0 -0
- {sourcecode-1.31.11 → sourcecode-1.31.13}/tests/conftest.py +0 -0
- {sourcecode-1.31.11 → sourcecode-1.31.13}/tests/fixtures/coverage.xml +0 -0
- {sourcecode-1.31.11 → sourcecode-1.31.13}/tests/fixtures/fastapi_app/pyproject.toml +0 -0
- {sourcecode-1.31.11 → sourcecode-1.31.13}/tests/fixtures/fastapi_app/src/main.py +0 -0
- {sourcecode-1.31.11 → sourcecode-1.31.13}/tests/fixtures/go_service/cmd/api/main.go +0 -0
- {sourcecode-1.31.11 → sourcecode-1.31.13}/tests/fixtures/go_service/go.mod +0 -0
- {sourcecode-1.31.11 → sourcecode-1.31.13}/tests/fixtures/jacoco.xml +0 -0
- {sourcecode-1.31.11 → sourcecode-1.31.13}/tests/fixtures/latin1_sample.java +0 -0
- {sourcecode-1.31.11 → sourcecode-1.31.13}/tests/fixtures/latin1_sample_iso.java +0 -0
- {sourcecode-1.31.11 → sourcecode-1.31.13}/tests/fixtures/lcov.info +0 -0
- {sourcecode-1.31.11 → sourcecode-1.31.13}/tests/fixtures/nextjs_app/app/page.tsx +0 -0
- {sourcecode-1.31.11 → sourcecode-1.31.13}/tests/fixtures/nextjs_app/package.json +0 -0
- {sourcecode-1.31.11 → sourcecode-1.31.13}/tests/fixtures/nextjs_app/pnpm-lock.yaml +0 -0
- {sourcecode-1.31.11 → sourcecode-1.31.13}/tests/fixtures/pnpm_monorepo/apps/web/app/page.tsx +0 -0
- {sourcecode-1.31.11 → sourcecode-1.31.13}/tests/fixtures/pnpm_monorepo/apps/web/package.json +0 -0
- {sourcecode-1.31.11 → sourcecode-1.31.13}/tests/fixtures/pnpm_monorepo/packages/api/main.py +0 -0
- {sourcecode-1.31.11 → sourcecode-1.31.13}/tests/fixtures/pnpm_monorepo/packages/api/pyproject.toml +0 -0
- {sourcecode-1.31.11 → sourcecode-1.31.13}/tests/fixtures/pnpm_monorepo/pnpm-workspace.yaml +0 -0
- {sourcecode-1.31.11 → sourcecode-1.31.13}/tests/fixtures/spring_boot_minimal/pom.xml +0 -0
- {sourcecode-1.31.11 → sourcecode-1.31.13}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/ddd/ausente/application/service/FindAusenteService.java +0 -0
- {sourcecode-1.31.11 → sourcecode-1.31.13}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/ddd/ausente/domain/entities/Ausente.java +0 -0
- {sourcecode-1.31.11 → sourcecode-1.31.13}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/ddd/ausente/infrastructure/rest/AusenteRestController.java +0 -0
- {sourcecode-1.31.11 → sourcecode-1.31.13}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/ddd/autocoberturas/application/service/FindAutocoberturasService.java +0 -0
- {sourcecode-1.31.11 → sourcecode-1.31.13}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/ddd/autocoberturas/domain/entities/Autocoberturas.java +0 -0
- {sourcecode-1.31.11 → sourcecode-1.31.13}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/ddd/autocoberturas/infrastructure/rest/AutocoberturasRestController.java +0 -0
- {sourcecode-1.31.11 → sourcecode-1.31.13}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/ddd/calendarioTrabajador/application/service/FindCalendarioTrabajadorService.java +0 -0
- {sourcecode-1.31.11 → sourcecode-1.31.13}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/ddd/calendarioTrabajador/domain/entities/CalendarioTrabajador.java +0 -0
- {sourcecode-1.31.11 → sourcecode-1.31.13}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/ddd/calendarioTrabajador/infrastructure/rest/CalendarioTrabajadorRestController.java +0 -0
- {sourcecode-1.31.11 → sourcecode-1.31.13}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/ddd/departamento/application/service/FindDepartamentoService.java +0 -0
- {sourcecode-1.31.11 → sourcecode-1.31.13}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/ddd/departamento/domain/entities/Departamento.java +0 -0
- {sourcecode-1.31.11 → sourcecode-1.31.13}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/ddd/departamento/infrastructure/rest/DepartamentoRestController.java +0 -0
- {sourcecode-1.31.11 → sourcecode-1.31.13}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/ddd/empleado/application/service/FindEmpleadoService.java +0 -0
- {sourcecode-1.31.11 → sourcecode-1.31.13}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/ddd/empleado/domain/entities/Empleado.java +0 -0
- {sourcecode-1.31.11 → sourcecode-1.31.13}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/ddd/empleado/infrastructure/rest/EmpleadoRestController.java +0 -0
- {sourcecode-1.31.11 → sourcecode-1.31.13}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/demo/DemoApplication.java +0 -0
- {sourcecode-1.31.11 → sourcecode-1.31.13}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/demo/config/FilterConfig.java +0 -0
- {sourcecode-1.31.11 → sourcecode-1.31.13}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/demo/domain/Health.java +0 -0
- {sourcecode-1.31.11 → sourcecode-1.31.13}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/demo/mapper/HealthMapper.java +0 -0
- {sourcecode-1.31.11 → sourcecode-1.31.13}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/demo/repository/HealthRepository.java +0 -0
- {sourcecode-1.31.11 → sourcecode-1.31.13}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/demo/service/HealthService.java +0 -0
- {sourcecode-1.31.11 → sourcecode-1.31.13}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/demo/web/HealthRestController.java +0 -0
- {sourcecode-1.31.11 → sourcecode-1.31.13}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/demo/web/NominaRestController.java +0 -0
- {sourcecode-1.31.11 → sourcecode-1.31.13}/tests/fixtures/spring_boot_minimal/src/main/resources/application-dev.yml +0 -0
- {sourcecode-1.31.11 → sourcecode-1.31.13}/tests/fixtures/spring_boot_minimal/src/main/resources/application.yml +0 -0
- {sourcecode-1.31.11 → sourcecode-1.31.13}/tests/fixtures/spring_boot_minimal/src/main/resources/mapper/HealthMapper.xml +0 -0
- {sourcecode-1.31.11 → sourcecode-1.31.13}/tests/test_architecture_analyzer.py +0 -0
- {sourcecode-1.31.11 → sourcecode-1.31.13}/tests/test_architecture_summary.py +0 -0
- {sourcecode-1.31.11 → sourcecode-1.31.13}/tests/test_ast_extractor.py +0 -0
- {sourcecode-1.31.11 → sourcecode-1.31.13}/tests/test_block1_reliability.py +0 -0
- {sourcecode-1.31.11 → sourcecode-1.31.13}/tests/test_block2_coverage.py +0 -0
- {sourcecode-1.31.11 → sourcecode-1.31.13}/tests/test_block5_quality.py +0 -0
- {sourcecode-1.31.11 → sourcecode-1.31.13}/tests/test_broadleaf_fixes.py +0 -0
- {sourcecode-1.31.11 → sourcecode-1.31.13}/tests/test_bug_fixes_v1302.py +0 -0
- {sourcecode-1.31.11 → sourcecode-1.31.13}/tests/test_bug_fixes_v1312.py +0 -0
- {sourcecode-1.31.11 → sourcecode-1.31.13}/tests/test_bug_fixes_v1313.py +0 -0
- {sourcecode-1.31.11 → sourcecode-1.31.13}/tests/test_bug_fixes_v16.py +0 -0
- {sourcecode-1.31.11 → sourcecode-1.31.13}/tests/test_bug_fixes_v2.py +0 -0
- {sourcecode-1.31.11 → sourcecode-1.31.13}/tests/test_classifier.py +0 -0
- {sourcecode-1.31.11 → sourcecode-1.31.13}/tests/test_cli.py +0 -0
- {sourcecode-1.31.11 → sourcecode-1.31.13}/tests/test_code_notes_analyzer.py +0 -0
- {sourcecode-1.31.11 → sourcecode-1.31.13}/tests/test_context_scorer.py +0 -0
- {sourcecode-1.31.11 → sourcecode-1.31.13}/tests/test_contract_pipeline.py +0 -0
- {sourcecode-1.31.11 → sourcecode-1.31.13}/tests/test_coverage_parser.py +0 -0
- {sourcecode-1.31.11 → sourcecode-1.31.13}/tests/test_cross_consistency.py +0 -0
- {sourcecode-1.31.11 → sourcecode-1.31.13}/tests/test_dependency_analyzer_node_python.py +0 -0
- {sourcecode-1.31.11 → sourcecode-1.31.13}/tests/test_dependency_analyzer_polyglot.py +0 -0
- {sourcecode-1.31.11 → sourcecode-1.31.13}/tests/test_dependency_schema.py +0 -0
- {sourcecode-1.31.11 → sourcecode-1.31.13}/tests/test_detector_dotnet.py +0 -0
- {sourcecode-1.31.11 → sourcecode-1.31.13}/tests/test_detector_go_rust_java.py +0 -0
- {sourcecode-1.31.11 → sourcecode-1.31.13}/tests/test_detector_nodejs.py +0 -0
- {sourcecode-1.31.11 → sourcecode-1.31.13}/tests/test_detector_php_ruby_dart.py +0 -0
- {sourcecode-1.31.11 → sourcecode-1.31.13}/tests/test_detector_python.py +0 -0
- {sourcecode-1.31.11 → sourcecode-1.31.13}/tests/test_detector_universal_managed.py +0 -0
- {sourcecode-1.31.11 → sourcecode-1.31.13}/tests/test_detector_universal_systems.py +0 -0
- {sourcecode-1.31.11 → sourcecode-1.31.13}/tests/test_detectors_base.py +0 -0
- {sourcecode-1.31.11 → sourcecode-1.31.13}/tests/test_doc_analyzer_jsdom.py +0 -0
- {sourcecode-1.31.11 → sourcecode-1.31.13}/tests/test_doc_analyzer_python.py +0 -0
- {sourcecode-1.31.11 → sourcecode-1.31.13}/tests/test_encoding_regression.py +0 -0
- {sourcecode-1.31.11 → sourcecode-1.31.13}/tests/test_graph_analyzer_polyglot.py +0 -0
- {sourcecode-1.31.11 → sourcecode-1.31.13}/tests/test_graph_analyzer_python_node.py +0 -0
- {sourcecode-1.31.11 → sourcecode-1.31.13}/tests/test_graph_schema.py +0 -0
- {sourcecode-1.31.11 → sourcecode-1.31.13}/tests/test_hybrid_inference.py +0 -0
- {sourcecode-1.31.11 → sourcecode-1.31.13}/tests/test_integration.py +0 -0
- {sourcecode-1.31.11 → sourcecode-1.31.13}/tests/test_integration_dependencies.py +0 -0
- {sourcecode-1.31.11 → sourcecode-1.31.13}/tests/test_integration_detection.py +0 -0
- {sourcecode-1.31.11 → sourcecode-1.31.13}/tests/test_integration_docs.py +0 -0
- {sourcecode-1.31.11 → sourcecode-1.31.13}/tests/test_integration_graph_modules.py +0 -0
- {sourcecode-1.31.11 → sourcecode-1.31.13}/tests/test_integration_lqn.py +0 -0
- {sourcecode-1.31.11 → sourcecode-1.31.13}/tests/test_integration_metrics.py +0 -0
- {sourcecode-1.31.11 → sourcecode-1.31.13}/tests/test_integration_multistack.py +0 -0
- {sourcecode-1.31.11 → sourcecode-1.31.13}/tests/test_integration_semantics.py +0 -0
- {sourcecode-1.31.11 → sourcecode-1.31.13}/tests/test_integration_universal.py +0 -0
- {sourcecode-1.31.11 → sourcecode-1.31.13}/tests/test_java_spring_integration.py +0 -0
- {sourcecode-1.31.11 → sourcecode-1.31.13}/tests/test_mcp_runner.py +0 -0
- {sourcecode-1.31.11 → sourcecode-1.31.13}/tests/test_mcp_serve.py +0 -0
- {sourcecode-1.31.11 → sourcecode-1.31.13}/tests/test_mcp_tools.py +0 -0
- {sourcecode-1.31.11 → sourcecode-1.31.13}/tests/test_metrics_analyzer.py +0 -0
- {sourcecode-1.31.11 → sourcecode-1.31.13}/tests/test_output_ux.py +0 -0
- {sourcecode-1.31.11 → sourcecode-1.31.13}/tests/test_packaging.py +0 -0
- {sourcecode-1.31.11 → sourcecode-1.31.13}/tests/test_phase1_improvements.py +0 -0
- {sourcecode-1.31.11 → sourcecode-1.31.13}/tests/test_pipeline_integrity.py +0 -0
- {sourcecode-1.31.11 → sourcecode-1.31.13}/tests/test_real_projects.py +0 -0
- {sourcecode-1.31.11 → sourcecode-1.31.13}/tests/test_redactor.py +0 -0
- {sourcecode-1.31.11 → sourcecode-1.31.13}/tests/test_scanner.py +0 -0
- {sourcecode-1.31.11 → sourcecode-1.31.13}/tests/test_schema.py +0 -0
- {sourcecode-1.31.11 → sourcecode-1.31.13}/tests/test_schema_normalization.py +0 -0
- {sourcecode-1.31.11 → sourcecode-1.31.13}/tests/test_scoring_calibration.py +0 -0
- {sourcecode-1.31.11 → sourcecode-1.31.13}/tests/test_semantic_analyzer_node.py +0 -0
- {sourcecode-1.31.11 → sourcecode-1.31.13}/tests/test_semantic_analyzer_python.py +0 -0
- {sourcecode-1.31.11 → sourcecode-1.31.13}/tests/test_semantic_import_resolution.py +0 -0
- {sourcecode-1.31.11 → sourcecode-1.31.13}/tests/test_semantic_schema.py +0 -0
- {sourcecode-1.31.11 → sourcecode-1.31.13}/tests/test_signal_hierarchy.py +0 -0
- {sourcecode-1.31.11 → sourcecode-1.31.13}/tests/test_summarizer.py +0 -0
- {sourcecode-1.31.11 → sourcecode-1.31.13}/tests/test_surface_honesty.py +0 -0
- {sourcecode-1.31.11 → sourcecode-1.31.13}/tests/test_task_differentiation.py +0 -0
- {sourcecode-1.31.11 → sourcecode-1.31.13}/tests/test_telemetry.py +0 -0
- {sourcecode-1.31.11 → sourcecode-1.31.13}/tests/test_v131_improvements.py +0 -0
- {sourcecode-1.31.11 → sourcecode-1.31.13}/tests/test_workspace_analyzer.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: sourcecode
|
|
3
|
-
Version: 1.31.
|
|
3
|
+
Version: 1.31.13
|
|
4
4
|
Summary: Deterministic codebase context for AI coding agents
|
|
5
5
|
License: Apache License
|
|
6
6
|
Version 2.0, January 2004
|
|
@@ -225,7 +225,7 @@ Description-Content-Type: text/markdown
|
|
|
225
225
|
|
|
226
226
|
**Deterministic, behavior-aware codebase context for AI agents and PR review.**
|
|
227
227
|
|
|
228
|
-

|
|
229
229
|

|
|
230
230
|
|
|
231
231
|
---
|
|
@@ -261,7 +261,7 @@ pipx install sourcecode
|
|
|
261
261
|
|
|
262
262
|
```bash
|
|
263
263
|
sourcecode version
|
|
264
|
-
# sourcecode 1.31.
|
|
264
|
+
# sourcecode 1.31.13
|
|
265
265
|
```
|
|
266
266
|
|
|
267
267
|
---
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
**Deterministic, behavior-aware codebase context for AI agents and PR review.**
|
|
4
4
|
|
|
5
|
-

|
|
6
6
|

|
|
7
7
|
|
|
8
8
|
---
|
|
@@ -38,7 +38,7 @@ pipx install sourcecode
|
|
|
38
38
|
|
|
39
39
|
```bash
|
|
40
40
|
sourcecode version
|
|
41
|
-
# sourcecode 1.31.
|
|
41
|
+
# sourcecode 1.31.13
|
|
42
42
|
```
|
|
43
43
|
|
|
44
44
|
---
|
|
@@ -166,7 +166,7 @@ Compressed AI-ready context for Java/Spring enterprise codebases.
|
|
|
166
166
|
# Known subcommand names — tokens matching these are routed as subcommands,
|
|
167
167
|
# not consumed as a repository path.
|
|
168
168
|
_SUBCOMMANDS: frozenset[str] = frozenset(
|
|
169
|
-
{"telemetry", "prepare-context", "version", "config", "analyze", "repo-ir", "mcp", "endpoints"}
|
|
169
|
+
{"telemetry", "prepare-context", "version", "config", "analyze", "repo-ir", "mcp", "endpoints", "impact"}
|
|
170
170
|
)
|
|
171
171
|
|
|
172
172
|
# Mutable container holding the path extracted by _preprocess_argv().
|
|
@@ -671,14 +671,14 @@ def main(
|
|
|
671
671
|
f"Error: invalid value '{mode}' for --mode. Valid options: {', '.join(_MODE_CHOICES)}",
|
|
672
672
|
err=True,
|
|
673
673
|
)
|
|
674
|
-
raise typer.Exit(code=
|
|
674
|
+
raise typer.Exit(code=2) # FIX-P2-7: arg validation → exit 2
|
|
675
675
|
_RANK_CHOICES = ("relevance", "centrality", "git-churn")
|
|
676
676
|
if rank_by not in _RANK_CHOICES:
|
|
677
677
|
typer.echo(
|
|
678
678
|
f"Error: invalid value '{rank_by}' for --rank-by. Valid options: {', '.join(_RANK_CHOICES)}",
|
|
679
679
|
err=True,
|
|
680
680
|
)
|
|
681
|
-
raise typer.Exit(code=
|
|
681
|
+
raise typer.Exit(code=2) # FIX-P2-7: arg validation → exit 2
|
|
682
682
|
|
|
683
683
|
if symbol is not None and not symbol.strip():
|
|
684
684
|
typer.echo("symbol query cannot be empty", err=True)
|
|
@@ -690,14 +690,14 @@ def main(
|
|
|
690
690
|
"Symbol search uses the contract pipeline which does not run in raw mode.",
|
|
691
691
|
err=True,
|
|
692
692
|
)
|
|
693
|
-
raise typer.Exit(code=
|
|
693
|
+
raise typer.Exit(code=2) # FIX-P2-7: arg validation → exit 2
|
|
694
694
|
|
|
695
695
|
if entrypoints_only and mode not in ("contract", "standard"):
|
|
696
696
|
typer.echo(
|
|
697
697
|
f"Error: --entrypoints-only requires --mode contract or standard (got '{mode}').",
|
|
698
698
|
err=True,
|
|
699
699
|
)
|
|
700
|
-
raise typer.Exit(code=
|
|
700
|
+
raise typer.Exit(code=2) # FIX-P2-7: arg validation → exit 2
|
|
701
701
|
|
|
702
702
|
if dependency_depth > 0:
|
|
703
703
|
typer.echo(
|
|
@@ -715,33 +715,65 @@ def main(
|
|
|
715
715
|
err=True,
|
|
716
716
|
)
|
|
717
717
|
|
|
718
|
+
# P0-2 FIX: --compact and --full are mutually exclusive.
|
|
719
|
+
# compact is designed to be a bounded summary; --full removes truncation limits,
|
|
720
|
+
# which contradicts compact's purpose. Use --agent --full for expanded output.
|
|
721
|
+
if compact and full:
|
|
722
|
+
typer.echo(
|
|
723
|
+
"Error: --compact and --full are mutually exclusive. "
|
|
724
|
+
"--compact produces a bounded summary; --full removes truncation limits and "
|
|
725
|
+
"is meant for --agent mode. Use --agent --full for expanded output.",
|
|
726
|
+
err=True,
|
|
727
|
+
)
|
|
728
|
+
raise typer.Exit(code=2)
|
|
729
|
+
|
|
730
|
+
# P0-2 FIX: --full without --compact or --agent has no effect in contract/raw mode.
|
|
731
|
+
# Warn so the user knows the flag is not doing anything.
|
|
732
|
+
if full and not compact and not agent:
|
|
733
|
+
typer.echo(
|
|
734
|
+
"[warning] --full has no effect in contract/raw mode. "
|
|
735
|
+
"It only expands mybatis.dto_mappers and transactional_boundaries in "
|
|
736
|
+
"--compact or --agent mode. Add --agent to get expanded output.",
|
|
737
|
+
err=True,
|
|
738
|
+
)
|
|
739
|
+
|
|
740
|
+
# P0-2 FIX: --changed-only silently implies --compact; inform the user.
|
|
741
|
+
if changed_only and not compact and not agent:
|
|
742
|
+
typer.echo(
|
|
743
|
+
"[info] --changed-only implies --compact (bounding output to changed files).",
|
|
744
|
+
err=True,
|
|
745
|
+
)
|
|
746
|
+
|
|
718
747
|
# Validate format choices
|
|
719
748
|
if format not in FORMAT_CHOICES:
|
|
720
749
|
typer.echo(
|
|
721
750
|
f"Error: invalid value '{format}' for --format. Valid options: {', '.join(FORMAT_CHOICES)}",
|
|
722
751
|
err=True,
|
|
723
752
|
)
|
|
724
|
-
raise typer.Exit(code=
|
|
753
|
+
raise typer.Exit(code=2) # FIX-P2-7: arg validation → exit 2
|
|
725
754
|
if graph_detail not in GRAPH_DETAIL_CHOICES:
|
|
726
755
|
typer.echo(
|
|
727
756
|
f"Error: invalid value '{graph_detail}' for --graph-detail. Valid options: {', '.join(GRAPH_DETAIL_CHOICES)}",
|
|
728
757
|
err=True,
|
|
729
758
|
)
|
|
730
|
-
raise typer.Exit(code=
|
|
759
|
+
raise typer.Exit(code=2) # FIX-P2-7: arg validation → exit 2
|
|
731
760
|
if docs_depth not in DOCS_DEPTH_CHOICES:
|
|
732
761
|
typer.echo(
|
|
733
762
|
f"Error: invalid value '{docs_depth}' for --docs-depth. Valid options: {', '.join(DOCS_DEPTH_CHOICES)}",
|
|
734
763
|
err=True,
|
|
735
764
|
)
|
|
736
|
-
raise typer.Exit(code=
|
|
765
|
+
raise typer.Exit(code=2) # FIX-P2-7: arg validation → exit 2
|
|
737
766
|
|
|
738
767
|
# Path was extracted from argv by _preprocess_argv() before Click ran.
|
|
739
|
-
|
|
768
|
+
# FIX-P2-8: preserve original user input in error messages (Windows Git Bash
|
|
769
|
+
# rewrites "/nonexistent" → "C:\Program Files\Git\nonexistent" via Path.resolve()).
|
|
770
|
+
_raw_path_input = _detected_path[0]
|
|
771
|
+
target = Path(_raw_path_input).resolve()
|
|
740
772
|
if not target.exists():
|
|
741
|
-
typer.echo(f"Error: directory '{
|
|
773
|
+
typer.echo(f"Error: directory '{_raw_path_input}' does not exist.", err=True)
|
|
742
774
|
raise typer.Exit(code=1)
|
|
743
775
|
if not target.is_dir():
|
|
744
|
-
typer.echo(f"Error: '{
|
|
776
|
+
typer.echo(f"Error: '{_raw_path_input}' is not a directory.", err=True)
|
|
745
777
|
raise typer.Exit(code=1)
|
|
746
778
|
|
|
747
779
|
# Normalize mode aliases
|
|
@@ -860,13 +892,27 @@ def main(
|
|
|
860
892
|
# Include every output-affecting flag so different flag combos never collide
|
|
861
893
|
# Include version so cache is invalidated on sourcecode upgrades
|
|
862
894
|
from sourcecode import __version__ as _sc_version
|
|
895
|
+
# FIX-P0-1: cache key must include ALL analysis-affecting flags.
|
|
896
|
+
# Previously missing: exclude, depth, rank_by, symbol, entrypoints_only,
|
|
897
|
+
# no_redact, graph_detail, docs_depth, max_nodes, graph_edges,
|
|
898
|
+
# max_importers, emit_graph.
|
|
899
|
+
# Use effective_depth (not raw depth) so Java auto-adjustment is captured.
|
|
900
|
+
_excl_key = (
|
|
901
|
+
",".join(sorted(e.strip() for e in exclude.split(",") if e.strip()))
|
|
902
|
+
if exclude else ""
|
|
903
|
+
)
|
|
863
904
|
_flags_str = (
|
|
864
905
|
f"v={_sc_version},"
|
|
865
906
|
f"c={compact},ag={agent},fmt={format},full={full},"
|
|
866
907
|
f"co={changed_only},dep={dependencies},gm={graph_modules},"
|
|
867
908
|
f"docs={docs},fm={full_metrics},sem={semantics},"
|
|
868
909
|
f"arch={architecture},gc={git_context},em={env_map},"
|
|
869
|
-
f"cn={code_notes},tree={tree},mode={mode}"
|
|
910
|
+
f"cn={code_notes},tree={tree},mode={mode},"
|
|
911
|
+
f"ex={_excl_key},depth={effective_depth},"
|
|
912
|
+
f"rb={rank_by},sym={symbol},ep={entrypoints_only},"
|
|
913
|
+
f"nr={no_redact},gd={graph_detail},dd={docs_depth},"
|
|
914
|
+
f"mn={max_nodes},ge={graph_edges},mi={max_importers},"
|
|
915
|
+
f"eg={emit_graph}"
|
|
870
916
|
)
|
|
871
917
|
_flags_h = _hashlib.md5(_flags_str.encode()).hexdigest()[:8]
|
|
872
918
|
_cache_key = f"{_git_sha}-{_flags_h}"
|
|
@@ -1612,7 +1658,26 @@ def main(
|
|
|
1612
1658
|
data = agent_view(sm, full=full)
|
|
1613
1659
|
if not no_redact:
|
|
1614
1660
|
data = redact_dict(data)
|
|
1615
|
-
|
|
1661
|
+
# P0-1: Apply output budget — safety net for large repos.
|
|
1662
|
+
from sourcecode.output_budget import trim_to_budget as _trim, BUDGET_AGENT
|
|
1663
|
+
data = _trim(data, BUDGET_AGENT, label="agent")
|
|
1664
|
+
# FIX-P0-2: agent mode must honour --format yaml (previously always emitted JSON).
|
|
1665
|
+
if format == "yaml":
|
|
1666
|
+
from io import StringIO
|
|
1667
|
+
from ruamel.yaml import YAML as _YAML
|
|
1668
|
+
_yaml_ag = _YAML()
|
|
1669
|
+
_yaml_ag.default_flow_style = False
|
|
1670
|
+
_yaml_ag.representer.add_representer(
|
|
1671
|
+
type(None),
|
|
1672
|
+
lambda dumper, data_val: dumper.represent_scalar(
|
|
1673
|
+
"tag:yaml.org,2002:null", "null"
|
|
1674
|
+
),
|
|
1675
|
+
)
|
|
1676
|
+
_stream_ag = StringIO()
|
|
1677
|
+
_yaml_ag.dump(data, _stream_ag)
|
|
1678
|
+
content = _stream_ag.getvalue()
|
|
1679
|
+
else:
|
|
1680
|
+
content = json.dumps(data, indent=2, ensure_ascii=False)
|
|
1616
1681
|
elif compact:
|
|
1617
1682
|
if changed_only and _allowed_changed_files:
|
|
1618
1683
|
# GAP-5: preserve full entry_points for architecture context even in
|
|
@@ -1624,6 +1689,9 @@ def main(
|
|
|
1624
1689
|
data = compact_view(sm, no_tree=no_tree, full=full)
|
|
1625
1690
|
if not no_redact:
|
|
1626
1691
|
data = redact_dict(data)
|
|
1692
|
+
# P0-1: Apply output budget — safety net for large repos.
|
|
1693
|
+
from sourcecode.output_budget import trim_to_budget as _trim_c, BUDGET_COMPACT
|
|
1694
|
+
data = _trim_c(data, BUDGET_COMPACT, label="compact")
|
|
1627
1695
|
if format == "yaml":
|
|
1628
1696
|
from io import StringIO
|
|
1629
1697
|
from ruamel.yaml import YAML as _YAML
|
|
@@ -2158,7 +2226,10 @@ def prepare_context_cmd(
|
|
|
2158
2226
|
_sys.stdout.buffer.write(_err_json.encode("utf-8"))
|
|
2159
2227
|
_sys.stdout.buffer.write(b"\n")
|
|
2160
2228
|
_sys.stdout.buffer.flush()
|
|
2161
|
-
|
|
2229
|
+
# FIX: no_diff (no PR changes) is not an error — exit 0, consistent
|
|
2230
|
+
# with delta's no_changes handling. Only true errors exit non-zero.
|
|
2231
|
+
_review_pr_exit = 0 if output.error_code == "no_diff" else 1
|
|
2232
|
+
raise typer.Exit(code=_review_pr_exit)
|
|
2162
2233
|
out["review_type"] = "pull_request"
|
|
2163
2234
|
if output.ci_decision:
|
|
2164
2235
|
out["ci_decision"] = output.ci_decision
|
|
@@ -2240,6 +2311,24 @@ def prepare_context_cmd(
|
|
|
2240
2311
|
if llm_prompt:
|
|
2241
2312
|
out["llm_prompt"] = builder.render_prompt(output)
|
|
2242
2313
|
|
|
2314
|
+
# P0-1: Apply output budget per task — safety net for large repos.
|
|
2315
|
+
from sourcecode.output_budget import (
|
|
2316
|
+
trim_to_budget as _pc_trim,
|
|
2317
|
+
BUDGET_FIX_BUG, BUDGET_REVIEW_PR, BUDGET_ONBOARD,
|
|
2318
|
+
BUDGET_EXPLAIN, BUDGET_REFACTOR, BUDGET_DELTA,
|
|
2319
|
+
)
|
|
2320
|
+
_pc_budgets: dict[str, int] = {
|
|
2321
|
+
"fix-bug": BUDGET_FIX_BUG,
|
|
2322
|
+
"review-pr": BUDGET_REVIEW_PR,
|
|
2323
|
+
"onboard": BUDGET_ONBOARD,
|
|
2324
|
+
"explain": BUDGET_EXPLAIN,
|
|
2325
|
+
"refactor": BUDGET_REFACTOR,
|
|
2326
|
+
"delta": BUDGET_DELTA,
|
|
2327
|
+
"generate-tests": BUDGET_EXPLAIN,
|
|
2328
|
+
}
|
|
2329
|
+
_pc_budget = _pc_budgets.get(task, BUDGET_EXPLAIN)
|
|
2330
|
+
out = _pc_trim(out, _pc_budget, label=task)
|
|
2331
|
+
|
|
2243
2332
|
if format == "github-comment" and task == "review-pr":
|
|
2244
2333
|
from sourcecode.pr_comment_renderer import render_github_comment
|
|
2245
2334
|
_pc_content = render_github_comment(out)
|
|
@@ -2457,6 +2546,115 @@ def repo_ir_cmd(
|
|
|
2457
2546
|
_sys.stdout.write("\n")
|
|
2458
2547
|
|
|
2459
2548
|
|
|
2549
|
+
# ── impact (blast-radius / change-impact analysis) ────────────────────────────
|
|
2550
|
+
|
|
2551
|
+
@app.command("impact")
|
|
2552
|
+
def impact_cmd(
|
|
2553
|
+
target: str = typer.Argument(
|
|
2554
|
+
...,
|
|
2555
|
+
help=(
|
|
2556
|
+
"Class name (simple or FQN) or file path to analyze impact for. "
|
|
2557
|
+
"Examples: UserService, org.example.UserService, UserService.java"
|
|
2558
|
+
),
|
|
2559
|
+
),
|
|
2560
|
+
path: Path = typer.Argument(
|
|
2561
|
+
Path("."),
|
|
2562
|
+
help="Repository root to analyze (default: current directory)",
|
|
2563
|
+
),
|
|
2564
|
+
depth: int = typer.Option(
|
|
2565
|
+
4,
|
|
2566
|
+
"--depth",
|
|
2567
|
+
help="BFS depth for indirect caller traversal (default: 4).",
|
|
2568
|
+
min=1,
|
|
2569
|
+
max=8,
|
|
2570
|
+
),
|
|
2571
|
+
output_path: Optional[Path] = typer.Option(
|
|
2572
|
+
None,
|
|
2573
|
+
"--output",
|
|
2574
|
+
"-o",
|
|
2575
|
+
help="Write output to a file instead of stdout.",
|
|
2576
|
+
),
|
|
2577
|
+
include_tests: bool = typer.Option(
|
|
2578
|
+
False,
|
|
2579
|
+
"--include-tests",
|
|
2580
|
+
help="Include test files in analysis (excluded by default).",
|
|
2581
|
+
),
|
|
2582
|
+
) -> None:
|
|
2583
|
+
"""Blast-radius analysis: who calls this class and what breaks if it changes?
|
|
2584
|
+
|
|
2585
|
+
\b
|
|
2586
|
+
Builds the repository IR and propagates impact from the target symbol
|
|
2587
|
+
through the reverse dependency graph. Returns:
|
|
2588
|
+
- direct_callers — classes that directly call or depend on the target
|
|
2589
|
+
- indirect_callers — transitive callers (BFS, bounded by --depth)
|
|
2590
|
+
- endpoints_affected — HTTP endpoints that transitively depend on the target
|
|
2591
|
+
- transactional_boundaries_touched — @Transactional classes in the call chain
|
|
2592
|
+
- risk_score / risk_level — quantified change risk
|
|
2593
|
+
|
|
2594
|
+
\b
|
|
2595
|
+
Examples:
|
|
2596
|
+
sourcecode impact UserService
|
|
2597
|
+
sourcecode impact org.keycloak.services.DefaultKeycloakSession /path/to/keycloak
|
|
2598
|
+
sourcecode impact UserService --depth 6 --output impact.json
|
|
2599
|
+
"""
|
|
2600
|
+
import json as _json
|
|
2601
|
+
import sys as _sys
|
|
2602
|
+
|
|
2603
|
+
from sourcecode.repository_ir import (
|
|
2604
|
+
build_repo_ir, find_java_files, compute_blast_radius,
|
|
2605
|
+
)
|
|
2606
|
+
from sourcecode.output_budget import trim_to_budget as _trim, BUDGET_IMPACT
|
|
2607
|
+
|
|
2608
|
+
root = path.resolve()
|
|
2609
|
+
if not root.is_dir():
|
|
2610
|
+
typer.echo(f"Error: {root} is not a directory", err=True)
|
|
2611
|
+
raise typer.Exit(1)
|
|
2612
|
+
|
|
2613
|
+
file_list = find_java_files(root)
|
|
2614
|
+
if not include_tests:
|
|
2615
|
+
file_list = [f for f in file_list if "/test/" not in f and "/tests/" not in f]
|
|
2616
|
+
|
|
2617
|
+
if not file_list:
|
|
2618
|
+
typer.echo(
|
|
2619
|
+
_json.dumps(
|
|
2620
|
+
{
|
|
2621
|
+
"target": target,
|
|
2622
|
+
"resolution": "not_found",
|
|
2623
|
+
"message": "No Java files found in repository.",
|
|
2624
|
+
"risk_level": "unknown",
|
|
2625
|
+
},
|
|
2626
|
+
indent=2,
|
|
2627
|
+
)
|
|
2628
|
+
)
|
|
2629
|
+
return
|
|
2630
|
+
|
|
2631
|
+
_prog = Progress()
|
|
2632
|
+
_prog.start(f"building IR ({len(file_list)} files) for impact analysis")
|
|
2633
|
+
try:
|
|
2634
|
+
ir = build_repo_ir(file_list, root)
|
|
2635
|
+
finally:
|
|
2636
|
+
_prog.finish()
|
|
2637
|
+
|
|
2638
|
+
result = compute_blast_radius(ir, target, max_depth=depth)
|
|
2639
|
+
result = _trim(result, BUDGET_IMPACT, label="impact")
|
|
2640
|
+
|
|
2641
|
+
output = _json.dumps(result, indent=2, ensure_ascii=False)
|
|
2642
|
+
if output_path:
|
|
2643
|
+
output_path.write_text(output, encoding="utf-8")
|
|
2644
|
+
typer.echo(f"Impact analysis written to {output_path}", err=True)
|
|
2645
|
+
else:
|
|
2646
|
+
try:
|
|
2647
|
+
_sys.stdout.buffer.write(output.encode("utf-8"))
|
|
2648
|
+
_sys.stdout.buffer.write(b"\n")
|
|
2649
|
+
_sys.stdout.buffer.flush()
|
|
2650
|
+
except AttributeError:
|
|
2651
|
+
_sys.stdout.write(output + "\n")
|
|
2652
|
+
|
|
2653
|
+
# Non-zero exit when target not found
|
|
2654
|
+
if result.get("resolution") == "not_found":
|
|
2655
|
+
raise typer.Exit(code=1)
|
|
2656
|
+
|
|
2657
|
+
|
|
2460
2658
|
# ── endpoints ─────────────────────────────────────────────────────────────────
|
|
2461
2659
|
|
|
2462
2660
|
# _extract_java_endpoints is imported from sourcecode.repository_ir as the
|
|
@@ -2723,6 +2921,9 @@ def mcp_init(
|
|
|
2723
2921
|
@mcp_app.command("status")
|
|
2724
2922
|
def mcp_status() -> None:
|
|
2725
2923
|
"""Show MCP integration status: dependencies, config files, and connectivity."""
|
|
2924
|
+
import subprocess as _sp
|
|
2925
|
+
import sys as _sys
|
|
2926
|
+
from sourcecode import __version__ as _cli_version
|
|
2726
2927
|
from sourcecode.mcp.onboarding.detector import detect_clients, is_client_running
|
|
2727
2928
|
from sourcecode.mcp.onboarding import applier
|
|
2728
2929
|
|
|
@@ -2731,6 +2932,10 @@ def mcp_status() -> None:
|
|
|
2731
2932
|
typer.echo("MCP Status")
|
|
2732
2933
|
typer.echo(sep)
|
|
2733
2934
|
|
|
2935
|
+
# FIX-P0-5/P0-6: Show CLI version explicitly so drift is immediately visible.
|
|
2936
|
+
typer.echo(f"CLI version {_cli_version} ({_sys.executable})")
|
|
2937
|
+
typer.echo("")
|
|
2938
|
+
|
|
2734
2939
|
# Stage 1: Dependencies
|
|
2735
2940
|
try:
|
|
2736
2941
|
import mcp as _mcp_pkg # noqa: F401
|
|
@@ -2747,27 +2952,88 @@ def mcp_status() -> None:
|
|
|
2747
2952
|
typer.echo(" Setup: sourcecode mcp init")
|
|
2748
2953
|
raise typer.Exit(code=0)
|
|
2749
2954
|
|
|
2750
|
-
# Stage 2: Config files
|
|
2751
|
-
|
|
2955
|
+
# Stage 2: Config files — is sourcecode registered in the client's config?
|
|
2956
|
+
# FIX-P0-6: "configured" and "running" are distinct, independent checks.
|
|
2957
|
+
# Also detect external server installs (different Python/executable than CLI).
|
|
2958
|
+
typer.echo("Config (sourcecode registered in client config?)")
|
|
2752
2959
|
for client in clients:
|
|
2753
2960
|
if not client.app_installed:
|
|
2754
|
-
typer.echo(f" {client.name:<20} ✗ not found")
|
|
2961
|
+
typer.echo(f" {client.name:<20} ✗ app not found at expected path")
|
|
2755
2962
|
typer.echo(f" Expected: {client.config_path}")
|
|
2756
2963
|
typer.echo(f" Fix: sourcecode mcp init --target {client.slug}")
|
|
2757
2964
|
continue
|
|
2758
2965
|
config = applier.read_config(client.config_path)
|
|
2759
2966
|
if applier.is_installed(config):
|
|
2760
2967
|
typer.echo(f" {client.name:<20} ✓ configured {client.config_path}")
|
|
2968
|
+
# FIX-P0-5: inspect registered command for external-server drift.
|
|
2969
|
+
_registered = config.get("mcpServers", {}).get("sourcecode", {})
|
|
2970
|
+
_reg_cmd = _registered.get("command", "")
|
|
2971
|
+
_reg_args = _registered.get("args", [])
|
|
2972
|
+
# Built-in form: command=sourcecode args=[mcp, serve] (or just the binary)
|
|
2973
|
+
_is_builtin = (
|
|
2974
|
+
_reg_cmd == "sourcecode"
|
|
2975
|
+
or (not _reg_args and _reg_cmd.endswith("/sourcecode"))
|
|
2976
|
+
or (_reg_args and _reg_args[:2] == ["mcp", "serve"])
|
|
2977
|
+
)
|
|
2978
|
+
if _is_builtin:
|
|
2979
|
+
typer.echo(f" Server: built-in (sourcecode mcp serve) version={_cli_version}")
|
|
2980
|
+
else:
|
|
2981
|
+
# External server — different Python or custom server.py
|
|
2982
|
+
typer.echo(f" Server: ⚠ EXTERNAL — {_reg_cmd} {' '.join(_reg_args)}")
|
|
2983
|
+
# Try to get the external server's sourcecode version by finding the
|
|
2984
|
+
# sourcecode binary relative to the registered Python executable,
|
|
2985
|
+
# or falling back to probing the Python for the installed package.
|
|
2986
|
+
_ext_ver: str = "unknown"
|
|
2987
|
+
try:
|
|
2988
|
+
import os as _os
|
|
2989
|
+
# Strategy 1: look for sourcecode binary next to registered Python
|
|
2990
|
+
_reg_bin_dir = _os.path.dirname(_reg_cmd)
|
|
2991
|
+
_sc_sibling = _os.path.join(_reg_bin_dir, "sourcecode")
|
|
2992
|
+
if _os.path.isfile(_sc_sibling):
|
|
2993
|
+
_ver_r = _sp.run(
|
|
2994
|
+
[_sc_sibling, "--version"],
|
|
2995
|
+
capture_output=True, text=True, timeout=5,
|
|
2996
|
+
)
|
|
2997
|
+
if _ver_r.returncode == 0 and _ver_r.stdout.strip():
|
|
2998
|
+
# "sourcecode X.Y.Z" → extract version
|
|
2999
|
+
_ext_ver = _ver_r.stdout.strip().split()[-1]
|
|
3000
|
+
# Strategy 2: import via registered Python
|
|
3001
|
+
if _ext_ver == "unknown":
|
|
3002
|
+
_ver_r2 = _sp.run(
|
|
3003
|
+
[_reg_cmd, "-c",
|
|
3004
|
+
"import sourcecode; print(sourcecode.__version__)"],
|
|
3005
|
+
capture_output=True, text=True, timeout=5,
|
|
3006
|
+
)
|
|
3007
|
+
if _ver_r2.returncode == 0 and _ver_r2.stdout.strip():
|
|
3008
|
+
_ext_ver = _ver_r2.stdout.strip()
|
|
3009
|
+
except Exception:
|
|
3010
|
+
pass
|
|
3011
|
+
if _ext_ver != "unknown" and _ext_ver != _cli_version:
|
|
3012
|
+
typer.echo(
|
|
3013
|
+
f" ⚠ VERSION DRIFT: external server version={_ext_ver}, "
|
|
3014
|
+
f"CLI version={_cli_version}"
|
|
3015
|
+
)
|
|
3016
|
+
typer.echo(
|
|
3017
|
+
" To fix: sourcecode mcp init (re-register using CLI built-in server)"
|
|
3018
|
+
)
|
|
3019
|
+
elif _ext_ver != "unknown":
|
|
3020
|
+
typer.echo(f" External server version={_ext_ver} (matches CLI ✓)")
|
|
3021
|
+
else:
|
|
3022
|
+
typer.echo(
|
|
3023
|
+
f" ⚠ Cannot verify external server version (CLI={_cli_version}). "
|
|
3024
|
+
"Re-run: sourcecode mcp init to switch to built-in server."
|
|
3025
|
+
)
|
|
2761
3026
|
else:
|
|
2762
|
-
typer.echo(f" {client.name:<20} ✗ not configured")
|
|
3027
|
+
typer.echo(f" {client.name:<20} ✗ not configured (app found, but sourcecode entry missing)")
|
|
2763
3028
|
typer.echo(f" Fix: sourcecode mcp init --target {client.slug}")
|
|
2764
3029
|
typer.echo("")
|
|
2765
3030
|
|
|
2766
|
-
# Stage 3:
|
|
2767
|
-
|
|
3031
|
+
# Stage 3: Process liveness — is the client app currently running?
|
|
3032
|
+
# This is independent from config: a running app may still need restart to pick up config.
|
|
3033
|
+
typer.echo("Runtime (client app process running?)")
|
|
2768
3034
|
any_installed = any(c.app_installed for c in clients)
|
|
2769
3035
|
if not any_installed:
|
|
2770
|
-
typer.echo(" (no
|
|
3036
|
+
typer.echo(" (no client apps found — nothing to check)")
|
|
2771
3037
|
else:
|
|
2772
3038
|
for client in clients:
|
|
2773
3039
|
if not client.app_installed:
|
|
@@ -2779,6 +3045,8 @@ def mcp_status() -> None:
|
|
|
2779
3045
|
typer.echo(f" Fix: open {client.name}, then run sourcecode mcp status")
|
|
2780
3046
|
|
|
2781
3047
|
typer.echo(sep)
|
|
3048
|
+
typer.echo(" Note: 'configured' and 'running' are checked independently.")
|
|
3049
|
+
typer.echo(" A running app still needs restart after first-time config.")
|
|
2782
3050
|
typer.echo(" Setup: sourcecode mcp init")
|
|
2783
3051
|
typer.echo(" Remove: sourcecode mcp remove")
|
|
2784
3052
|
|
|
@@ -15,9 +15,15 @@ from typing import Any
|
|
|
15
15
|
|
|
16
16
|
from mcp.server.fastmcp import FastMCP
|
|
17
17
|
|
|
18
|
+
from sourcecode import __version__ as _sourcecode_version
|
|
18
19
|
from sourcecode.mcp.runner import run_command
|
|
19
20
|
|
|
21
|
+
# FIX-P0-5: MCP server version must match CLI version exactly.
|
|
22
|
+
# FastMCP does not accept version= in __init__; inject it on the underlying
|
|
23
|
+
# low-level Server so the MCP initialize handshake reports the correct version.
|
|
20
24
|
mcp = FastMCP("sourcecode")
|
|
25
|
+
if hasattr(mcp, "_mcp_server"):
|
|
26
|
+
mcp._mcp_server.version = _sourcecode_version # type: ignore[attr-defined]
|
|
21
27
|
|
|
22
28
|
|
|
23
29
|
def _ok(data: Any) -> dict:
|
|
@@ -37,10 +43,14 @@ def _execute(args: list[str]) -> dict:
|
|
|
37
43
|
|
|
38
44
|
@mcp.tool()
|
|
39
45
|
def get_compact_context(repo_path: str = ".", git_context: bool = False) -> dict:
|
|
40
|
-
"""
|
|
46
|
+
"""Compact human/LLM summary of a repository (~1000-3000 tokens). USE THIS FIRST.
|
|
47
|
+
|
|
48
|
+
Best for: quick project orientation, first-time context, token-budget constrained tasks.
|
|
49
|
+
Returns: stacks, entry points, dependency summary, architecture summary, confidence, gaps.
|
|
50
|
+
Includes security_surface, mybatis, and transactional_boundaries for Java/Spring projects.
|
|
51
|
+
For richer machine-oriented detail (deeper signals, more sections), use get_agent_context.
|
|
41
52
|
|
|
42
53
|
Maps to: sourcecode <repo_path> --compact [--git-context]
|
|
43
|
-
Returns: stacks, entry points, dependency summary, confidence, gaps.
|
|
44
54
|
repo_path: absolute path to the repository (default: current working directory).
|
|
45
55
|
git_context: include git log and branch context in the analysis.
|
|
46
56
|
"""
|
|
@@ -56,10 +66,14 @@ def get_compact_context(repo_path: str = ".", git_context: bool = False) -> dict
|
|
|
56
66
|
|
|
57
67
|
@mcp.tool()
|
|
58
68
|
def get_agent_context(repo_path: str = ".", git_context: bool = False) -> dict:
|
|
59
|
-
"""
|
|
69
|
+
"""Full structured agent context with extended machine-oriented signals (~5000-15000 tokens).
|
|
70
|
+
|
|
71
|
+
Best for: deep analysis, bug investigation, code review, or when get_compact_context
|
|
72
|
+
lacks sufficient detail. Includes all compact fields plus: env_map, code_notes,
|
|
73
|
+
architecture layers, security surface, transactional boundaries, module graph summary.
|
|
74
|
+
Prefer get_compact_context for quick orientation or token-constrained workflows.
|
|
60
75
|
|
|
61
76
|
Maps to: sourcecode <repo_path> --agent [--git-context]
|
|
62
|
-
Returns: structured noise-free JSON for AI agents.
|
|
63
77
|
repo_path: absolute path to the repository (default: current working directory).
|
|
64
78
|
git_context: include git log and branch context in the analysis.
|
|
65
79
|
"""
|
|
@@ -79,8 +93,14 @@ def get_endpoints(repo_path: str = ".") -> dict:
|
|
|
79
93
|
|
|
80
94
|
Maps to: sourcecode endpoints <repo_path>
|
|
81
95
|
Returns: endpoints list with method, path, controller, handler fields;
|
|
82
|
-
|
|
96
|
+
security dict when authorization annotations are present
|
|
97
|
+
(policy: roles_allowed|permit_all|deny_all|authenticated|...);
|
|
98
|
+
total (int) and no_security_signal (int) counts.
|
|
99
|
+
no_security_signal counts endpoints with no recognized auth annotation —
|
|
100
|
+
repos using framework-level auth (e.g. Keycloak) may show high counts.
|
|
83
101
|
Supports Spring MVC (@GetMapping etc.) and JAX-RS (@GET/@POST etc.).
|
|
102
|
+
Security annotations detected: @RolesAllowed, @PermitAll, @DenyAll,
|
|
103
|
+
@Authenticated, @PreAuthorize, @Secured, @SecurityRequirement, @M3FiltroSeguridad.
|
|
84
104
|
repo_path: absolute path to the repository (default: current working directory).
|
|
85
105
|
"""
|
|
86
106
|
if not isinstance(repo_path, str):
|
|
@@ -121,11 +141,17 @@ def get_delta(repo_path: str = ".", since: str = "HEAD~1") -> dict:
|
|
|
121
141
|
|
|
122
142
|
@mcp.tool()
|
|
123
143
|
def get_ir_summary(repo_path: str = ".") -> dict:
|
|
124
|
-
"""Deterministic symbol-level IR summary for Java repositories.
|
|
144
|
+
"""Deterministic symbol-level IR summary for Java repositories. Java only.
|
|
125
145
|
|
|
126
146
|
Maps to: sourcecode repo-ir <repo_path> --summary-only
|
|
127
|
-
Returns:
|
|
128
|
-
|
|
147
|
+
Returns: reverse_graph (top 10 hubs), route_surface (top 50 endpoints),
|
|
148
|
+
subsystems (top 15), impact, analysis. Full graph nodes/edges omitted.
|
|
149
|
+
|
|
150
|
+
Output is bounded to ~100 KB for LLM safety. For full IR (can exceed 10 MB
|
|
151
|
+
on large repos), use the CLI: sourcecode repo-ir <path> --output ir.json
|
|
152
|
+
Use get_compact_context or get_agent_context for non-Java repos.
|
|
153
|
+
|
|
154
|
+
repo_path: absolute path to the Java repository (default: current working directory).
|
|
129
155
|
"""
|
|
130
156
|
if not isinstance(repo_path, str):
|
|
131
157
|
return _err("repo_path must be a string", "INVALID_ARGUMENT")
|
|
@@ -223,6 +249,34 @@ def generate_tests_context(repo_path: str = ".", include_all: bool = False) -> d
|
|
|
223
249
|
return _execute(args)
|
|
224
250
|
|
|
225
251
|
|
|
252
|
+
@mcp.tool()
|
|
253
|
+
def get_impact_context(repo_path: str = ".", target: str = "", depth: int = 4) -> dict:
|
|
254
|
+
"""Blast-radius analysis: who calls a class and what breaks if it changes? Java only.
|
|
255
|
+
|
|
256
|
+
Maps to: sourcecode impact <target> <repo_path> [--depth <depth>]
|
|
257
|
+
Returns: direct_callers, indirect_callers, endpoints_affected,
|
|
258
|
+
transactional_boundaries_touched, risk_score, risk_level, stats.
|
|
259
|
+
|
|
260
|
+
Use this when:
|
|
261
|
+
- Planning a refactor: understand the full call chain before changing a class
|
|
262
|
+
- PR review: assess blast radius of a changed service or utility class
|
|
263
|
+
- Incident triage: find all paths that reach a faulty component
|
|
264
|
+
|
|
265
|
+
target: class name (simple or FQN) or Java file path. Examples:
|
|
266
|
+
"UserService", "org.example.UserService", "UserService.java"
|
|
267
|
+
repo_path: absolute path to the Java repository (default: current working directory).
|
|
268
|
+
depth: BFS depth for indirect caller traversal (1–8, default: 4).
|
|
269
|
+
"""
|
|
270
|
+
if not isinstance(repo_path, str):
|
|
271
|
+
return _err("repo_path must be a string", "INVALID_ARGUMENT")
|
|
272
|
+
if not isinstance(target, str) or not target.strip():
|
|
273
|
+
return _err("target must be a non-empty class name or FQN", "INVALID_ARGUMENT")
|
|
274
|
+
if not isinstance(depth, int) or depth < 1 or depth > 8:
|
|
275
|
+
return _err("depth must be an integer between 1 and 8", "INVALID_ARGUMENT")
|
|
276
|
+
args = ["impact", target.strip(), repo_path, "--depth", str(depth)]
|
|
277
|
+
return _execute(args)
|
|
278
|
+
|
|
279
|
+
|
|
226
280
|
_TELEMETRY_ACTIONS = frozenset({"status", "enable", "disable"})
|
|
227
281
|
|
|
228
282
|
|
|
@@ -248,12 +302,14 @@ def config() -> dict:
|
|
|
248
302
|
def telemetry(action: str) -> dict:
|
|
249
303
|
"""Manage telemetry settings.
|
|
250
304
|
|
|
251
|
-
Maps to: sourcecode telemetry <
|
|
252
|
-
action
|
|
305
|
+
Maps to: sourcecode telemetry <action>
|
|
306
|
+
action: one of "status" (show current state), "enable" (opt in), "disable" (opt out).
|
|
307
|
+
Valid values: "status" | "enable" | "disable"
|
|
253
308
|
"""
|
|
309
|
+
# FIX-P2-10: enumerate valid actions in docstring so agents don't guess.
|
|
254
310
|
if action not in _TELEMETRY_ACTIONS:
|
|
255
311
|
return _err(
|
|
256
|
-
f"action
|
|
312
|
+
f"Invalid action '{action}'. Must be one of: {', '.join(sorted(_TELEMETRY_ACTIONS))}",
|
|
257
313
|
"INVALID_ARGUMENT",
|
|
258
314
|
)
|
|
259
315
|
return _execute(["telemetry", action])
|