sourcecode 1.31.12__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.12 → sourcecode-1.31.13}/PKG-INFO +3 -3
- {sourcecode-1.31.12 → sourcecode-1.31.13}/README.md +2 -2
- {sourcecode-1.31.12 → sourcecode-1.31.13}/pyproject.toml +1 -1
- {sourcecode-1.31.12 → sourcecode-1.31.13}/src/sourcecode/__init__.py +1 -1
- {sourcecode-1.31.12 → sourcecode-1.31.13}/src/sourcecode/cli.py +233 -4
- {sourcecode-1.31.12 → sourcecode-1.31.13}/src/sourcecode/mcp/server.py +44 -4
- sourcecode-1.31.13/src/sourcecode/output_budget.py +157 -0
- {sourcecode-1.31.12 → sourcecode-1.31.13}/src/sourcecode/repository_ir.py +567 -49
- {sourcecode-1.31.12 → sourcecode-1.31.13}/src/sourcecode/serializer.py +41 -4
- 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.12 → sourcecode-1.31.13}/tests/test_repository_ir.py +327 -0
- {sourcecode-1.31.12 → sourcecode-1.31.13}/tests/test_v1_10_regressions.py +119 -7
- sourcecode-1.31.12/.sourcecode-cache/snapshot-0778d0a-0911b79e.json +0 -3825
- sourcecode-1.31.12/.sourcecode-cache/snapshot-0778d0a-37df4554.json +0 -266
- sourcecode-1.31.12/.sourcecode-cache/snapshot-0778d0a-624321f3.json +0 -12478
- sourcecode-1.31.12/.sourcecode-cache/snapshot-0778d0a-776b4676.json +0 -386
- sourcecode-1.31.12/.sourcecode-cache/snapshot-0778d0a-9770fba7.json +0 -377
- sourcecode-1.31.12/.sourcecode-cache/snapshot-0778d0a-c4e3c102.json +0 -266
- sourcecode-1.31.12/.sourcecode-cache/snapshot-0778d0a-e8bc5fb4.json +0 -122
- sourcecode-1.31.12/.sourcecode-cache/snapshot-0778d0a-e9801942.json +0 -223
- sourcecode-1.31.12/.sourcecode-cache/snapshot-0778d0a-ee60e0cd.json +0 -322
- sourcecode-1.31.12/.sourcecode-cache/snapshot-0778d0a-fdd9d3f7.json +0 -552
- sourcecode-1.31.12/.sourcecode-cache/snapshot-3b5997a-27b713ac.json +0 -386
- sourcecode-1.31.12/.sourcecode-cache/snapshot-3b5997a-28e558ac.json +0 -325
- sourcecode-1.31.12/.sourcecode-cache/snapshot-3b5997a-2d7eca5f.json +0 -12577
- sourcecode-1.31.12/.sourcecode-cache/snapshot-3b5997a-7cd91058.json +0 -226
- sourcecode-1.31.12/.sourcecode-cache/snapshot-3b5997a-8ea5b5bb.json +0 -380
- sourcecode-1.31.12/.sourcecode-cache/snapshot-3b5997a-bef1a526.json +0 -261
- sourcecode-1.31.12/.sourcecode-cache/snapshot-3b5997a-c69bcdc8.json +0 -122
- sourcecode-1.31.12/.sourcecode-cache/snapshot-3b5997a-d2dfc690.json +0 -3825
- sourcecode-1.31.12/.sourcecode-cache/snapshot-3b5997a-dc30daab.json +0 -552
- sourcecode-1.31.12/.sourcecode-cache/snapshot-7571a6f-20354ec3.json +0 -261
- sourcecode-1.31.12/.sourcecode-cache/snapshot-7571a6f-2c874f8c.json +0 -122
- sourcecode-1.31.12/.sourcecode-cache/snapshot-7571a6f-6c8157db.json +0 -226
- sourcecode-1.31.12/.sourcecode-cache/snapshot-7571a6f-93090ce0.json +0 -129
- sourcecode-1.31.12/.sourcecode-cache/snapshot-7571a6f-ce79b4bf.json +0 -3846
- sourcecode-1.31.12/.sourcecode-cache/snapshot-7571a6f-cf0bfa7a.json +0 -325
- sourcecode-1.31.12/.sourcecode-cache/snapshot-7571a6f-e73c6323.json +0 -261
- sourcecode-1.31.12/.sourcecode-cache/snapshot-7571a6f-ee60e0cd.json +0 -325
- {sourcecode-1.31.12 → sourcecode-1.31.13}/.agents/skills/source-command-gsd-join-discord/SKILL.md +0 -0
- {sourcecode-1.31.12 → sourcecode-1.31.13}/.agents/skills/source-command-gsd-review-backlog/SKILL.md +0 -0
- {sourcecode-1.31.12 → sourcecode-1.31.13}/.agents/skills/source-command-gsd-workstreams/SKILL.md +0 -0
- {sourcecode-1.31.12 → sourcecode-1.31.13}/.continue-here.md +0 -0
- {sourcecode-1.31.12 → sourcecode-1.31.13}/.github/workflows/build-windows.yml +0 -0
- {sourcecode-1.31.12 → sourcecode-1.31.13}/.gitignore +0 -0
- {sourcecode-1.31.12 → sourcecode-1.31.13}/.ruff.toml +0 -0
- {sourcecode-1.31.12 → sourcecode-1.31.13}/.sourcecode-cache/snapshot-3b5997a-fa5c742c.json +0 -0
- {sourcecode-1.31.12 → sourcecode-1.31.13}/CHANGELOG.md +0 -0
- {sourcecode-1.31.12 → sourcecode-1.31.13}/CONTRIBUTING.md +0 -0
- {sourcecode-1.31.12 → sourcecode-1.31.13}/LICENSE +0 -0
- {sourcecode-1.31.12 → sourcecode-1.31.13}/SECURITY.md +0 -0
- {sourcecode-1.31.12 → sourcecode-1.31.13}/docs/privacy.md +0 -0
- {sourcecode-1.31.12 → sourcecode-1.31.13}/docs/schema.md +0 -0
- {sourcecode-1.31.12 → sourcecode-1.31.13}/raw +0 -0
- {sourcecode-1.31.12 → sourcecode-1.31.13}/run_cli.py +0 -0
- {sourcecode-1.31.12 → sourcecode-1.31.13}/src/sourcecode/adaptive_scanner.py +0 -0
- {sourcecode-1.31.12 → sourcecode-1.31.13}/src/sourcecode/architecture_analyzer.py +0 -0
- {sourcecode-1.31.12 → sourcecode-1.31.13}/src/sourcecode/architecture_summary.py +0 -0
- {sourcecode-1.31.12 → sourcecode-1.31.13}/src/sourcecode/ast_extractor.py +0 -0
- {sourcecode-1.31.12 → sourcecode-1.31.13}/src/sourcecode/classifier.py +0 -0
- {sourcecode-1.31.12 → sourcecode-1.31.13}/src/sourcecode/code_notes_analyzer.py +0 -0
- {sourcecode-1.31.12 → sourcecode-1.31.13}/src/sourcecode/confidence_analyzer.py +0 -0
- {sourcecode-1.31.12 → sourcecode-1.31.13}/src/sourcecode/context_scorer.py +0 -0
- {sourcecode-1.31.12 → sourcecode-1.31.13}/src/sourcecode/context_summarizer.py +0 -0
- {sourcecode-1.31.12 → sourcecode-1.31.13}/src/sourcecode/contract_model.py +0 -0
- {sourcecode-1.31.12 → sourcecode-1.31.13}/src/sourcecode/contract_pipeline.py +0 -0
- {sourcecode-1.31.12 → sourcecode-1.31.13}/src/sourcecode/coverage_parser.py +0 -0
- {sourcecode-1.31.12 → sourcecode-1.31.13}/src/sourcecode/dependency_analyzer.py +0 -0
- {sourcecode-1.31.12 → sourcecode-1.31.13}/src/sourcecode/detectors/__init__.py +0 -0
- {sourcecode-1.31.12 → sourcecode-1.31.13}/src/sourcecode/detectors/base.py +0 -0
- {sourcecode-1.31.12 → sourcecode-1.31.13}/src/sourcecode/detectors/csproj_parser.py +0 -0
- {sourcecode-1.31.12 → sourcecode-1.31.13}/src/sourcecode/detectors/dart.py +0 -0
- {sourcecode-1.31.12 → sourcecode-1.31.13}/src/sourcecode/detectors/dotnet.py +0 -0
- {sourcecode-1.31.12 → sourcecode-1.31.13}/src/sourcecode/detectors/elixir.py +0 -0
- {sourcecode-1.31.12 → sourcecode-1.31.13}/src/sourcecode/detectors/go.py +0 -0
- {sourcecode-1.31.12 → sourcecode-1.31.13}/src/sourcecode/detectors/heuristic.py +0 -0
- {sourcecode-1.31.12 → sourcecode-1.31.13}/src/sourcecode/detectors/hybrid.py +0 -0
- {sourcecode-1.31.12 → sourcecode-1.31.13}/src/sourcecode/detectors/java.py +0 -0
- {sourcecode-1.31.12 → sourcecode-1.31.13}/src/sourcecode/detectors/jvm_ext.py +0 -0
- {sourcecode-1.31.12 → sourcecode-1.31.13}/src/sourcecode/detectors/nodejs.py +0 -0
- {sourcecode-1.31.12 → sourcecode-1.31.13}/src/sourcecode/detectors/parsers.py +0 -0
- {sourcecode-1.31.12 → sourcecode-1.31.13}/src/sourcecode/detectors/php.py +0 -0
- {sourcecode-1.31.12 → sourcecode-1.31.13}/src/sourcecode/detectors/project.py +0 -0
- {sourcecode-1.31.12 → sourcecode-1.31.13}/src/sourcecode/detectors/python.py +0 -0
- {sourcecode-1.31.12 → sourcecode-1.31.13}/src/sourcecode/detectors/ruby.py +0 -0
- {sourcecode-1.31.12 → sourcecode-1.31.13}/src/sourcecode/detectors/rust.py +0 -0
- {sourcecode-1.31.12 → sourcecode-1.31.13}/src/sourcecode/detectors/systems.py +0 -0
- {sourcecode-1.31.12 → sourcecode-1.31.13}/src/sourcecode/detectors/terraform.py +0 -0
- {sourcecode-1.31.12 → sourcecode-1.31.13}/src/sourcecode/detectors/tooling.py +0 -0
- {sourcecode-1.31.12 → sourcecode-1.31.13}/src/sourcecode/doc_analyzer.py +0 -0
- {sourcecode-1.31.12 → sourcecode-1.31.13}/src/sourcecode/entrypoint_classifier.py +0 -0
- {sourcecode-1.31.12 → sourcecode-1.31.13}/src/sourcecode/env_analyzer.py +0 -0
- {sourcecode-1.31.12 → sourcecode-1.31.13}/src/sourcecode/file_classifier.py +0 -0
- {sourcecode-1.31.12 → sourcecode-1.31.13}/src/sourcecode/flow_analyzer.py +0 -0
- {sourcecode-1.31.12 → sourcecode-1.31.13}/src/sourcecode/git_analyzer.py +0 -0
- {sourcecode-1.31.12 → sourcecode-1.31.13}/src/sourcecode/graph_analyzer.py +0 -0
- {sourcecode-1.31.12 → sourcecode-1.31.13}/src/sourcecode/mcp/__init__.py +0 -0
- {sourcecode-1.31.12 → sourcecode-1.31.13}/src/sourcecode/mcp/onboarding/__init__.py +0 -0
- {sourcecode-1.31.12 → sourcecode-1.31.13}/src/sourcecode/mcp/onboarding/applier.py +0 -0
- {sourcecode-1.31.12 → sourcecode-1.31.13}/src/sourcecode/mcp/onboarding/backup.py +0 -0
- {sourcecode-1.31.12 → sourcecode-1.31.13}/src/sourcecode/mcp/onboarding/detector.py +0 -0
- {sourcecode-1.31.12 → sourcecode-1.31.13}/src/sourcecode/mcp/onboarding/planner.py +0 -0
- {sourcecode-1.31.12 → sourcecode-1.31.13}/src/sourcecode/mcp/runner.py +0 -0
- {sourcecode-1.31.12 → sourcecode-1.31.13}/src/sourcecode/metrics_analyzer.py +0 -0
- {sourcecode-1.31.12 → sourcecode-1.31.13}/src/sourcecode/path_filters.py +0 -0
- {sourcecode-1.31.12 → sourcecode-1.31.13}/src/sourcecode/pr_comment_renderer.py +0 -0
- {sourcecode-1.31.12 → sourcecode-1.31.13}/src/sourcecode/prepare_context.py +0 -0
- {sourcecode-1.31.12 → sourcecode-1.31.13}/src/sourcecode/progress.py +0 -0
- {sourcecode-1.31.12 → sourcecode-1.31.13}/src/sourcecode/ranking_engine.py +0 -0
- {sourcecode-1.31.12 → sourcecode-1.31.13}/src/sourcecode/redactor.py +0 -0
- {sourcecode-1.31.12 → sourcecode-1.31.13}/src/sourcecode/relevance_scorer.py +0 -0
- {sourcecode-1.31.12 → sourcecode-1.31.13}/src/sourcecode/repo_classifier.py +0 -0
- {sourcecode-1.31.12 → sourcecode-1.31.13}/src/sourcecode/runtime_classifier.py +0 -0
- {sourcecode-1.31.12 → sourcecode-1.31.13}/src/sourcecode/scanner.py +0 -0
- {sourcecode-1.31.12 → sourcecode-1.31.13}/src/sourcecode/schema.py +0 -0
- {sourcecode-1.31.12 → sourcecode-1.31.13}/src/sourcecode/semantic_analyzer.py +0 -0
- {sourcecode-1.31.12 → sourcecode-1.31.13}/src/sourcecode/summarizer.py +0 -0
- {sourcecode-1.31.12 → sourcecode-1.31.13}/src/sourcecode/telemetry/__init__.py +0 -0
- {sourcecode-1.31.12 → sourcecode-1.31.13}/src/sourcecode/telemetry/config.py +0 -0
- {sourcecode-1.31.12 → sourcecode-1.31.13}/src/sourcecode/telemetry/consent.py +0 -0
- {sourcecode-1.31.12 → sourcecode-1.31.13}/src/sourcecode/telemetry/events.py +0 -0
- {sourcecode-1.31.12 → sourcecode-1.31.13}/src/sourcecode/telemetry/filters.py +0 -0
- {sourcecode-1.31.12 → sourcecode-1.31.13}/src/sourcecode/telemetry/transport.py +0 -0
- {sourcecode-1.31.12 → sourcecode-1.31.13}/src/sourcecode/tree_utils.py +0 -0
- {sourcecode-1.31.12 → sourcecode-1.31.13}/src/sourcecode/workspace.py +0 -0
- {sourcecode-1.31.12 → sourcecode-1.31.13}/tests/__init__.py +0 -0
- {sourcecode-1.31.12 → sourcecode-1.31.13}/tests/conftest.py +0 -0
- {sourcecode-1.31.12 → sourcecode-1.31.13}/tests/fixtures/coverage.xml +0 -0
- {sourcecode-1.31.12 → sourcecode-1.31.13}/tests/fixtures/fastapi_app/pyproject.toml +0 -0
- {sourcecode-1.31.12 → sourcecode-1.31.13}/tests/fixtures/fastapi_app/src/main.py +0 -0
- {sourcecode-1.31.12 → sourcecode-1.31.13}/tests/fixtures/go_service/cmd/api/main.go +0 -0
- {sourcecode-1.31.12 → sourcecode-1.31.13}/tests/fixtures/go_service/go.mod +0 -0
- {sourcecode-1.31.12 → sourcecode-1.31.13}/tests/fixtures/jacoco.xml +0 -0
- {sourcecode-1.31.12 → sourcecode-1.31.13}/tests/fixtures/latin1_sample.java +0 -0
- {sourcecode-1.31.12 → sourcecode-1.31.13}/tests/fixtures/latin1_sample_iso.java +0 -0
- {sourcecode-1.31.12 → sourcecode-1.31.13}/tests/fixtures/lcov.info +0 -0
- {sourcecode-1.31.12 → sourcecode-1.31.13}/tests/fixtures/nextjs_app/app/page.tsx +0 -0
- {sourcecode-1.31.12 → sourcecode-1.31.13}/tests/fixtures/nextjs_app/package.json +0 -0
- {sourcecode-1.31.12 → sourcecode-1.31.13}/tests/fixtures/nextjs_app/pnpm-lock.yaml +0 -0
- {sourcecode-1.31.12 → sourcecode-1.31.13}/tests/fixtures/pnpm_monorepo/apps/web/app/page.tsx +0 -0
- {sourcecode-1.31.12 → sourcecode-1.31.13}/tests/fixtures/pnpm_monorepo/apps/web/package.json +0 -0
- {sourcecode-1.31.12 → sourcecode-1.31.13}/tests/fixtures/pnpm_monorepo/packages/api/main.py +0 -0
- {sourcecode-1.31.12 → sourcecode-1.31.13}/tests/fixtures/pnpm_monorepo/packages/api/pyproject.toml +0 -0
- {sourcecode-1.31.12 → sourcecode-1.31.13}/tests/fixtures/pnpm_monorepo/pnpm-workspace.yaml +0 -0
- {sourcecode-1.31.12 → sourcecode-1.31.13}/tests/fixtures/spring_boot_minimal/pom.xml +0 -0
- {sourcecode-1.31.12 → 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.12 → 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.12 → 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.12 → 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.12 → 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.12 → 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.12 → 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.12 → 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.12 → 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.12 → 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.12 → 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.12 → 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.12 → 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.12 → 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.12 → 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.12 → sourcecode-1.31.13}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/demo/DemoApplication.java +0 -0
- {sourcecode-1.31.12 → sourcecode-1.31.13}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/demo/config/FilterConfig.java +0 -0
- {sourcecode-1.31.12 → sourcecode-1.31.13}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/demo/domain/Health.java +0 -0
- {sourcecode-1.31.12 → sourcecode-1.31.13}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/demo/mapper/HealthMapper.java +0 -0
- {sourcecode-1.31.12 → sourcecode-1.31.13}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/demo/repository/HealthRepository.java +0 -0
- {sourcecode-1.31.12 → sourcecode-1.31.13}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/demo/service/HealthService.java +0 -0
- {sourcecode-1.31.12 → sourcecode-1.31.13}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/demo/web/HealthRestController.java +0 -0
- {sourcecode-1.31.12 → sourcecode-1.31.13}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/demo/web/NominaRestController.java +0 -0
- {sourcecode-1.31.12 → sourcecode-1.31.13}/tests/fixtures/spring_boot_minimal/src/main/resources/application-dev.yml +0 -0
- {sourcecode-1.31.12 → sourcecode-1.31.13}/tests/fixtures/spring_boot_minimal/src/main/resources/application.yml +0 -0
- {sourcecode-1.31.12 → sourcecode-1.31.13}/tests/fixtures/spring_boot_minimal/src/main/resources/mapper/HealthMapper.xml +0 -0
- {sourcecode-1.31.12 → sourcecode-1.31.13}/tests/test_architecture_analyzer.py +0 -0
- {sourcecode-1.31.12 → sourcecode-1.31.13}/tests/test_architecture_summary.py +0 -0
- {sourcecode-1.31.12 → sourcecode-1.31.13}/tests/test_ast_extractor.py +0 -0
- {sourcecode-1.31.12 → sourcecode-1.31.13}/tests/test_audit_fixes.py +0 -0
- {sourcecode-1.31.12 → sourcecode-1.31.13}/tests/test_block1_reliability.py +0 -0
- {sourcecode-1.31.12 → sourcecode-1.31.13}/tests/test_block2_coverage.py +0 -0
- {sourcecode-1.31.12 → sourcecode-1.31.13}/tests/test_block5_quality.py +0 -0
- {sourcecode-1.31.12 → sourcecode-1.31.13}/tests/test_broadleaf_fixes.py +0 -0
- {sourcecode-1.31.12 → sourcecode-1.31.13}/tests/test_bug_fixes_v1302.py +0 -0
- {sourcecode-1.31.12 → sourcecode-1.31.13}/tests/test_bug_fixes_v1312.py +0 -0
- {sourcecode-1.31.12 → sourcecode-1.31.13}/tests/test_bug_fixes_v1313.py +0 -0
- {sourcecode-1.31.12 → sourcecode-1.31.13}/tests/test_bug_fixes_v16.py +0 -0
- {sourcecode-1.31.12 → sourcecode-1.31.13}/tests/test_bug_fixes_v2.py +0 -0
- {sourcecode-1.31.12 → sourcecode-1.31.13}/tests/test_classifier.py +0 -0
- {sourcecode-1.31.12 → sourcecode-1.31.13}/tests/test_cli.py +0 -0
- {sourcecode-1.31.12 → sourcecode-1.31.13}/tests/test_code_notes_analyzer.py +0 -0
- {sourcecode-1.31.12 → sourcecode-1.31.13}/tests/test_context_scorer.py +0 -0
- {sourcecode-1.31.12 → sourcecode-1.31.13}/tests/test_contract_pipeline.py +0 -0
- {sourcecode-1.31.12 → sourcecode-1.31.13}/tests/test_coverage_parser.py +0 -0
- {sourcecode-1.31.12 → sourcecode-1.31.13}/tests/test_cross_consistency.py +0 -0
- {sourcecode-1.31.12 → sourcecode-1.31.13}/tests/test_dependency_analyzer_node_python.py +0 -0
- {sourcecode-1.31.12 → sourcecode-1.31.13}/tests/test_dependency_analyzer_polyglot.py +0 -0
- {sourcecode-1.31.12 → sourcecode-1.31.13}/tests/test_dependency_schema.py +0 -0
- {sourcecode-1.31.12 → sourcecode-1.31.13}/tests/test_detector_dotnet.py +0 -0
- {sourcecode-1.31.12 → sourcecode-1.31.13}/tests/test_detector_go_rust_java.py +0 -0
- {sourcecode-1.31.12 → sourcecode-1.31.13}/tests/test_detector_nodejs.py +0 -0
- {sourcecode-1.31.12 → sourcecode-1.31.13}/tests/test_detector_php_ruby_dart.py +0 -0
- {sourcecode-1.31.12 → sourcecode-1.31.13}/tests/test_detector_python.py +0 -0
- {sourcecode-1.31.12 → sourcecode-1.31.13}/tests/test_detector_universal_managed.py +0 -0
- {sourcecode-1.31.12 → sourcecode-1.31.13}/tests/test_detector_universal_systems.py +0 -0
- {sourcecode-1.31.12 → sourcecode-1.31.13}/tests/test_detectors_base.py +0 -0
- {sourcecode-1.31.12 → sourcecode-1.31.13}/tests/test_doc_analyzer_jsdom.py +0 -0
- {sourcecode-1.31.12 → sourcecode-1.31.13}/tests/test_doc_analyzer_python.py +0 -0
- {sourcecode-1.31.12 → sourcecode-1.31.13}/tests/test_encoding_regression.py +0 -0
- {sourcecode-1.31.12 → sourcecode-1.31.13}/tests/test_graph_analyzer_polyglot.py +0 -0
- {sourcecode-1.31.12 → sourcecode-1.31.13}/tests/test_graph_analyzer_python_node.py +0 -0
- {sourcecode-1.31.12 → sourcecode-1.31.13}/tests/test_graph_schema.py +0 -0
- {sourcecode-1.31.12 → sourcecode-1.31.13}/tests/test_hybrid_inference.py +0 -0
- {sourcecode-1.31.12 → sourcecode-1.31.13}/tests/test_integration.py +0 -0
- {sourcecode-1.31.12 → sourcecode-1.31.13}/tests/test_integration_dependencies.py +0 -0
- {sourcecode-1.31.12 → sourcecode-1.31.13}/tests/test_integration_detection.py +0 -0
- {sourcecode-1.31.12 → sourcecode-1.31.13}/tests/test_integration_docs.py +0 -0
- {sourcecode-1.31.12 → sourcecode-1.31.13}/tests/test_integration_graph_modules.py +0 -0
- {sourcecode-1.31.12 → sourcecode-1.31.13}/tests/test_integration_lqn.py +0 -0
- {sourcecode-1.31.12 → sourcecode-1.31.13}/tests/test_integration_metrics.py +0 -0
- {sourcecode-1.31.12 → sourcecode-1.31.13}/tests/test_integration_multistack.py +0 -0
- {sourcecode-1.31.12 → sourcecode-1.31.13}/tests/test_integration_semantics.py +0 -0
- {sourcecode-1.31.12 → sourcecode-1.31.13}/tests/test_integration_universal.py +0 -0
- {sourcecode-1.31.12 → sourcecode-1.31.13}/tests/test_java_spring_integration.py +0 -0
- {sourcecode-1.31.12 → sourcecode-1.31.13}/tests/test_mcp_runner.py +0 -0
- {sourcecode-1.31.12 → sourcecode-1.31.13}/tests/test_mcp_serve.py +0 -0
- {sourcecode-1.31.12 → sourcecode-1.31.13}/tests/test_mcp_tools.py +0 -0
- {sourcecode-1.31.12 → sourcecode-1.31.13}/tests/test_metrics_analyzer.py +0 -0
- {sourcecode-1.31.12 → sourcecode-1.31.13}/tests/test_output_ux.py +0 -0
- {sourcecode-1.31.12 → sourcecode-1.31.13}/tests/test_packaging.py +0 -0
- {sourcecode-1.31.12 → sourcecode-1.31.13}/tests/test_phase1_improvements.py +0 -0
- {sourcecode-1.31.12 → sourcecode-1.31.13}/tests/test_pipeline_integrity.py +0 -0
- {sourcecode-1.31.12 → sourcecode-1.31.13}/tests/test_real_projects.py +0 -0
- {sourcecode-1.31.12 → sourcecode-1.31.13}/tests/test_redactor.py +0 -0
- {sourcecode-1.31.12 → sourcecode-1.31.13}/tests/test_scanner.py +0 -0
- {sourcecode-1.31.12 → sourcecode-1.31.13}/tests/test_schema.py +0 -0
- {sourcecode-1.31.12 → sourcecode-1.31.13}/tests/test_schema_normalization.py +0 -0
- {sourcecode-1.31.12 → sourcecode-1.31.13}/tests/test_scoring_calibration.py +0 -0
- {sourcecode-1.31.12 → sourcecode-1.31.13}/tests/test_semantic_analyzer_node.py +0 -0
- {sourcecode-1.31.12 → sourcecode-1.31.13}/tests/test_semantic_analyzer_python.py +0 -0
- {sourcecode-1.31.12 → sourcecode-1.31.13}/tests/test_semantic_import_resolution.py +0 -0
- {sourcecode-1.31.12 → sourcecode-1.31.13}/tests/test_semantic_schema.py +0 -0
- {sourcecode-1.31.12 → sourcecode-1.31.13}/tests/test_signal_hierarchy.py +0 -0
- {sourcecode-1.31.12 → sourcecode-1.31.13}/tests/test_summarizer.py +0 -0
- {sourcecode-1.31.12 → sourcecode-1.31.13}/tests/test_surface_honesty.py +0 -0
- {sourcecode-1.31.12 → sourcecode-1.31.13}/tests/test_task_differentiation.py +0 -0
- {sourcecode-1.31.12 → sourcecode-1.31.13}/tests/test_telemetry.py +0 -0
- {sourcecode-1.31.12 → sourcecode-1.31.13}/tests/test_v131_improvements.py +0 -0
- {sourcecode-1.31.12 → 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().
|
|
@@ -715,6 +715,35 @@ 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(
|
|
@@ -1629,6 +1658,9 @@ def main(
|
|
|
1629
1658
|
data = agent_view(sm, full=full)
|
|
1630
1659
|
if not no_redact:
|
|
1631
1660
|
data = redact_dict(data)
|
|
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")
|
|
1632
1664
|
# FIX-P0-2: agent mode must honour --format yaml (previously always emitted JSON).
|
|
1633
1665
|
if format == "yaml":
|
|
1634
1666
|
from io import StringIO
|
|
@@ -1657,6 +1689,9 @@ def main(
|
|
|
1657
1689
|
data = compact_view(sm, no_tree=no_tree, full=full)
|
|
1658
1690
|
if not no_redact:
|
|
1659
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")
|
|
1660
1695
|
if format == "yaml":
|
|
1661
1696
|
from io import StringIO
|
|
1662
1697
|
from ruamel.yaml import YAML as _YAML
|
|
@@ -2191,7 +2226,10 @@ def prepare_context_cmd(
|
|
|
2191
2226
|
_sys.stdout.buffer.write(_err_json.encode("utf-8"))
|
|
2192
2227
|
_sys.stdout.buffer.write(b"\n")
|
|
2193
2228
|
_sys.stdout.buffer.flush()
|
|
2194
|
-
|
|
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)
|
|
2195
2233
|
out["review_type"] = "pull_request"
|
|
2196
2234
|
if output.ci_decision:
|
|
2197
2235
|
out["ci_decision"] = output.ci_decision
|
|
@@ -2273,6 +2311,24 @@ def prepare_context_cmd(
|
|
|
2273
2311
|
if llm_prompt:
|
|
2274
2312
|
out["llm_prompt"] = builder.render_prompt(output)
|
|
2275
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
|
+
|
|
2276
2332
|
if format == "github-comment" and task == "review-pr":
|
|
2277
2333
|
from sourcecode.pr_comment_renderer import render_github_comment
|
|
2278
2334
|
_pc_content = render_github_comment(out)
|
|
@@ -2490,6 +2546,115 @@ def repo_ir_cmd(
|
|
|
2490
2546
|
_sys.stdout.write("\n")
|
|
2491
2547
|
|
|
2492
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
|
+
|
|
2493
2658
|
# ── endpoints ─────────────────────────────────────────────────────────────────
|
|
2494
2659
|
|
|
2495
2660
|
# _extract_java_endpoints is imported from sourcecode.repository_ir as the
|
|
@@ -2756,6 +2921,9 @@ def mcp_init(
|
|
|
2756
2921
|
@mcp_app.command("status")
|
|
2757
2922
|
def mcp_status() -> None:
|
|
2758
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
|
|
2759
2927
|
from sourcecode.mcp.onboarding.detector import detect_clients, is_client_running
|
|
2760
2928
|
from sourcecode.mcp.onboarding import applier
|
|
2761
2929
|
|
|
@@ -2764,6 +2932,10 @@ def mcp_status() -> None:
|
|
|
2764
2932
|
typer.echo("MCP Status")
|
|
2765
2933
|
typer.echo(sep)
|
|
2766
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
|
+
|
|
2767
2939
|
# Stage 1: Dependencies
|
|
2768
2940
|
try:
|
|
2769
2941
|
import mcp as _mcp_pkg # noqa: F401
|
|
@@ -2782,8 +2954,7 @@ def mcp_status() -> None:
|
|
|
2782
2954
|
|
|
2783
2955
|
# Stage 2: Config files — is sourcecode registered in the client's config?
|
|
2784
2956
|
# FIX-P0-6: "configured" and "running" are distinct, independent checks.
|
|
2785
|
-
#
|
|
2786
|
-
# Display each state as a separate labelled field to prevent contradiction.
|
|
2957
|
+
# Also detect external server installs (different Python/executable than CLI).
|
|
2787
2958
|
typer.echo("Config (sourcecode registered in client config?)")
|
|
2788
2959
|
for client in clients:
|
|
2789
2960
|
if not client.app_installed:
|
|
@@ -2794,6 +2965,64 @@ def mcp_status() -> None:
|
|
|
2794
2965
|
config = applier.read_config(client.config_path)
|
|
2795
2966
|
if applier.is_installed(config):
|
|
2796
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
|
+
)
|
|
2797
3026
|
else:
|
|
2798
3027
|
typer.echo(f" {client.name:<20} ✗ not configured (app found, but sourcecode entry missing)")
|
|
2799
3028
|
typer.echo(f" Fix: sourcecode mcp init --target {client.slug}")
|
|
@@ -93,8 +93,14 @@ def get_endpoints(repo_path: str = ".") -> dict:
|
|
|
93
93
|
|
|
94
94
|
Maps to: sourcecode endpoints <repo_path>
|
|
95
95
|
Returns: endpoints list with method, path, controller, handler fields;
|
|
96
|
-
|
|
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.
|
|
97
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.
|
|
98
104
|
repo_path: absolute path to the repository (default: current working directory).
|
|
99
105
|
"""
|
|
100
106
|
if not isinstance(repo_path, str):
|
|
@@ -135,11 +141,17 @@ def get_delta(repo_path: str = ".", since: str = "HEAD~1") -> dict:
|
|
|
135
141
|
|
|
136
142
|
@mcp.tool()
|
|
137
143
|
def get_ir_summary(repo_path: str = ".") -> dict:
|
|
138
|
-
"""Deterministic symbol-level IR summary for Java repositories.
|
|
144
|
+
"""Deterministic symbol-level IR summary for Java repositories. Java only.
|
|
139
145
|
|
|
140
146
|
Maps to: sourcecode repo-ir <repo_path> --summary-only
|
|
141
|
-
Returns:
|
|
142
|
-
|
|
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).
|
|
143
155
|
"""
|
|
144
156
|
if not isinstance(repo_path, str):
|
|
145
157
|
return _err("repo_path must be a string", "INVALID_ARGUMENT")
|
|
@@ -237,6 +249,34 @@ def generate_tests_context(repo_path: str = ".", include_all: bool = False) -> d
|
|
|
237
249
|
return _execute(args)
|
|
238
250
|
|
|
239
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
|
+
|
|
240
280
|
_TELEMETRY_ACTIONS = frozenset({"status", "enable", "disable"})
|
|
241
281
|
|
|
242
282
|
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
"""output_budget.py — Progressive output trimming for LLM context safety.
|
|
2
|
+
|
|
3
|
+
Single entry point: trim_to_budget(data, budget_bytes, label="").
|
|
4
|
+
|
|
5
|
+
Protects against runaway output on large repos by progressively dropping
|
|
6
|
+
or truncating lower-priority sections while preserving the highest-value signals.
|
|
7
|
+
|
|
8
|
+
Trim priority (lowest → highest value, trimmed first):
|
|
9
|
+
1. Inner boost/match lists inside symptom_explain, transactional_boundaries, mybatis
|
|
10
|
+
2. Medium-value lists: code_notes, env_map, related_notes
|
|
11
|
+
3. High-cost sections: relevant_files, suspected_areas, key_dependencies
|
|
12
|
+
4. Last resort: drop non-essential sections entirely
|
|
13
|
+
|
|
14
|
+
Always preserved: project_type, project_summary, architecture_summary, task,
|
|
15
|
+
goal, confidence, analysis_gaps, error, ci_decision, summary, warnings, hint.
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
from __future__ import annotations
|
|
19
|
+
|
|
20
|
+
import json
|
|
21
|
+
from typing import Any
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
# Sections that must never be removed (structural identity + user-facing signals)
|
|
25
|
+
_ALWAYS_KEEP: frozenset[str] = frozenset({
|
|
26
|
+
"task", "goal",
|
|
27
|
+
"project_type", "project_summary", "architecture_summary",
|
|
28
|
+
"schema_version",
|
|
29
|
+
"confidence", "confidence_summary", "analysis_gaps", "gaps",
|
|
30
|
+
"error", "error_code", "message",
|
|
31
|
+
"ci_decision", "summary",
|
|
32
|
+
"warnings", "hint", "_budget_note",
|
|
33
|
+
})
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
# Each step: (top_key, inner_key_or_None, max_items)
|
|
37
|
+
# inner_key=None → trim/drop top_key itself
|
|
38
|
+
# max_items=0 → drop the section entirely
|
|
39
|
+
_TRIM_SCHEDULE: list[tuple[str, str | None, int]] = [
|
|
40
|
+
# Step 1 — trim inner lists in structured sections
|
|
41
|
+
("symptom_explain", "boosts", 10),
|
|
42
|
+
("symptom_explain", "content_matches", 5),
|
|
43
|
+
("symptom_explain", "commit_matches", 5),
|
|
44
|
+
("symptom_explain", "synonym_matches", 3),
|
|
45
|
+
("transactional_boundaries", "classes", 5),
|
|
46
|
+
("mybatis", "dto_mappers", 5),
|
|
47
|
+
# Step 2 — trim medium-value top-level lists
|
|
48
|
+
("code_notes", None, 5),
|
|
49
|
+
("env_map", None, 5),
|
|
50
|
+
("related_notes", None, 5),
|
|
51
|
+
# Step 3 — trim higher-cost sections
|
|
52
|
+
("relevant_files", None, 15),
|
|
53
|
+
("suspected_areas", None, 10),
|
|
54
|
+
("key_dependencies", None, 10),
|
|
55
|
+
# Step 4 — more aggressive
|
|
56
|
+
("relevant_files", None, 8),
|
|
57
|
+
("key_dependencies", None, 5),
|
|
58
|
+
("suspected_areas", None, 5),
|
|
59
|
+
("symptom_explain", "boosts", 0), # drop inner list
|
|
60
|
+
# Step 5 — drop non-essential sections
|
|
61
|
+
("symptom_explain", None, 0),
|
|
62
|
+
("code_notes", None, 0),
|
|
63
|
+
("env_map", None, 0),
|
|
64
|
+
("related_notes", None, 0),
|
|
65
|
+
("angular_analysis", None, 0),
|
|
66
|
+
("spring_profiles", None, 0),
|
|
67
|
+
("execution_paths", None, 0),
|
|
68
|
+
("dependency_graph_summary", None, 0),
|
|
69
|
+
# Step 6 — last resort
|
|
70
|
+
("relevant_files", None, 3),
|
|
71
|
+
("suspected_areas", None, 0),
|
|
72
|
+
("key_dependencies", None, 0),
|
|
73
|
+
]
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
def _serialized_size(data: Any) -> int:
|
|
77
|
+
"""Byte size of JSON-serialized data (UTF-8)."""
|
|
78
|
+
return len(json.dumps(data, ensure_ascii=False).encode("utf-8"))
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
def trim_to_budget(data: dict, budget_bytes: int, *, label: str = "") -> dict:
|
|
82
|
+
"""Progressively trim *data* to fit within *budget_bytes*.
|
|
83
|
+
|
|
84
|
+
Preserves the highest-value sections. Adds ``_budget_note`` when trimming
|
|
85
|
+
occurs so callers can surface it to users.
|
|
86
|
+
|
|
87
|
+
Returns data unchanged if already within budget.
|
|
88
|
+
"""
|
|
89
|
+
if _serialized_size(data) <= budget_bytes:
|
|
90
|
+
return data
|
|
91
|
+
|
|
92
|
+
result: dict = dict(data)
|
|
93
|
+
original_size = _serialized_size(result)
|
|
94
|
+
trimmed_sections: list[str] = []
|
|
95
|
+
|
|
96
|
+
for top_key, inner_key, max_items in _TRIM_SCHEDULE:
|
|
97
|
+
if _serialized_size(result) <= budget_bytes:
|
|
98
|
+
break
|
|
99
|
+
|
|
100
|
+
if top_key not in result or result[top_key] is None:
|
|
101
|
+
continue
|
|
102
|
+
|
|
103
|
+
section_val = result[top_key]
|
|
104
|
+
|
|
105
|
+
if inner_key is None:
|
|
106
|
+
# Trim or drop the top-level key
|
|
107
|
+
if max_items == 0:
|
|
108
|
+
if top_key in _ALWAYS_KEEP:
|
|
109
|
+
continue
|
|
110
|
+
del result[top_key]
|
|
111
|
+
trimmed_sections.append(f"{top_key}:dropped")
|
|
112
|
+
elif isinstance(section_val, list) and len(section_val) > max_items:
|
|
113
|
+
result[top_key] = section_val[:max_items]
|
|
114
|
+
trimmed_sections.append(f"{top_key}≤{max_items}")
|
|
115
|
+
else:
|
|
116
|
+
# Trim or drop an inner list
|
|
117
|
+
if not isinstance(section_val, dict):
|
|
118
|
+
continue
|
|
119
|
+
if inner_key not in section_val:
|
|
120
|
+
continue
|
|
121
|
+
inner_val = section_val[inner_key]
|
|
122
|
+
if max_items == 0:
|
|
123
|
+
new_sec = dict(section_val)
|
|
124
|
+
del new_sec[inner_key]
|
|
125
|
+
result[top_key] = new_sec
|
|
126
|
+
trimmed_sections.append(f"{top_key}.{inner_key}:dropped")
|
|
127
|
+
elif isinstance(inner_val, list) and len(inner_val) > max_items:
|
|
128
|
+
new_sec = dict(section_val)
|
|
129
|
+
new_sec[inner_key] = inner_val[:max_items]
|
|
130
|
+
result[top_key] = new_sec
|
|
131
|
+
trimmed_sections.append(f"{top_key}.{inner_key}≤{max_items}")
|
|
132
|
+
|
|
133
|
+
final_size = _serialized_size(result)
|
|
134
|
+
if trimmed_sections:
|
|
135
|
+
note = (
|
|
136
|
+
f"Output trimmed {original_size // 1024}KB → {final_size // 1024}KB "
|
|
137
|
+
f"(budget {budget_bytes // 1024}KB). "
|
|
138
|
+
f"Trimmed: {', '.join(trimmed_sections)}. "
|
|
139
|
+
"Use --output <file> to capture full output."
|
|
140
|
+
)
|
|
141
|
+
if label:
|
|
142
|
+
note = f"[{label}] {note}"
|
|
143
|
+
result["_budget_note"] = note
|
|
144
|
+
|
|
145
|
+
return result
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
# Budget constants (bytes) — used by CLI callers
|
|
149
|
+
BUDGET_COMPACT = 30_000 # compact/agent main cmd
|
|
150
|
+
BUDGET_AGENT = 40_000 # agent main cmd (slightly more headroom)
|
|
151
|
+
BUDGET_FIX_BUG = 100_000 # fix-bug (with or without --symptom)
|
|
152
|
+
BUDGET_REVIEW_PR = 100_000 # review-pr
|
|
153
|
+
BUDGET_ONBOARD = 30_000 # onboard
|
|
154
|
+
BUDGET_EXPLAIN = 30_000 # explain
|
|
155
|
+
BUDGET_REFACTOR = 50_000 # refactor
|
|
156
|
+
BUDGET_DELTA = 80_000 # delta (change impact context)
|
|
157
|
+
BUDGET_IMPACT = 50_000 # impact blast-radius command
|