sourcecode 1.31.0__tar.gz → 1.31.2__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.0 → sourcecode-1.31.2}/PKG-INFO +4 -3
- {sourcecode-1.31.0 → sourcecode-1.31.2}/README.md +2 -2
- {sourcecode-1.31.0 → sourcecode-1.31.2}/pyproject.toml +2 -1
- {sourcecode-1.31.0 → sourcecode-1.31.2}/src/sourcecode/__init__.py +1 -1
- {sourcecode-1.31.0 → sourcecode-1.31.2}/src/sourcecode/cli.py +287 -26
- {sourcecode-1.31.0 → sourcecode-1.31.2}/src/sourcecode/confidence_analyzer.py +1 -1
- sourcecode-1.31.2/src/sourcecode/mcp/onboarding/detector.py +104 -0
- {sourcecode-1.31.0 → sourcecode-1.31.2}/src/sourcecode/mcp/runner.py +10 -3
- sourcecode-1.31.2/src/sourcecode/mcp/server.py +156 -0
- {sourcecode-1.31.0 → sourcecode-1.31.2}/src/sourcecode/prepare_context.py +165 -35
- {sourcecode-1.31.0 → sourcecode-1.31.2}/src/sourcecode/serializer.py +1 -1
- {sourcecode-1.31.0 → sourcecode-1.31.2}/tests/test_mcp_runner.py +3 -3
- {sourcecode-1.31.0 → sourcecode-1.31.2}/tests/test_mcp_serve.py +0 -8
- sourcecode-1.31.2/tests/test_mcp_tools.py +232 -0
- sourcecode-1.31.0/.sourcecode-cache/snapshot-0e430c1-37df4554.json +0 -262
- sourcecode-1.31.0/.sourcecode-cache/snapshot-0e430c1-e9801942.json +0 -219
- sourcecode-1.31.0/.sourcecode-cache/snapshot-0e430c1-ee60e0cd.json +0 -326
- sourcecode-1.31.0/src/sourcecode/mcp/onboarding/detector.py +0 -59
- sourcecode-1.31.0/src/sourcecode/mcp/server.py +0 -147
- sourcecode-1.31.0/tests/test_mcp_tools.py +0 -242
- {sourcecode-1.31.0 → sourcecode-1.31.2}/.agents/skills/source-command-gsd-join-discord/SKILL.md +0 -0
- {sourcecode-1.31.0 → sourcecode-1.31.2}/.agents/skills/source-command-gsd-review-backlog/SKILL.md +0 -0
- {sourcecode-1.31.0 → sourcecode-1.31.2}/.agents/skills/source-command-gsd-workstreams/SKILL.md +0 -0
- {sourcecode-1.31.0 → sourcecode-1.31.2}/.continue-here.md +0 -0
- {sourcecode-1.31.0 → sourcecode-1.31.2}/.github/workflows/build-windows.yml +0 -0
- {sourcecode-1.31.0 → sourcecode-1.31.2}/.gitignore +0 -0
- {sourcecode-1.31.0 → sourcecode-1.31.2}/.ruff.toml +0 -0
- {sourcecode-1.31.0 → sourcecode-1.31.2}/CHANGELOG.md +0 -0
- {sourcecode-1.31.0 → sourcecode-1.31.2}/CONTRIBUTING.md +0 -0
- {sourcecode-1.31.0 → sourcecode-1.31.2}/LICENSE +0 -0
- {sourcecode-1.31.0 → sourcecode-1.31.2}/SECURITY.md +0 -0
- {sourcecode-1.31.0 → sourcecode-1.31.2}/docs/privacy.md +0 -0
- {sourcecode-1.31.0 → sourcecode-1.31.2}/docs/schema.md +0 -0
- {sourcecode-1.31.0 → sourcecode-1.31.2}/raw +0 -0
- {sourcecode-1.31.0 → sourcecode-1.31.2}/run_cli.py +0 -0
- {sourcecode-1.31.0 → sourcecode-1.31.2}/src/sourcecode/adaptive_scanner.py +0 -0
- {sourcecode-1.31.0 → sourcecode-1.31.2}/src/sourcecode/architecture_analyzer.py +0 -0
- {sourcecode-1.31.0 → sourcecode-1.31.2}/src/sourcecode/architecture_summary.py +0 -0
- {sourcecode-1.31.0 → sourcecode-1.31.2}/src/sourcecode/ast_extractor.py +0 -0
- {sourcecode-1.31.0 → sourcecode-1.31.2}/src/sourcecode/classifier.py +0 -0
- {sourcecode-1.31.0 → sourcecode-1.31.2}/src/sourcecode/code_notes_analyzer.py +0 -0
- {sourcecode-1.31.0 → sourcecode-1.31.2}/src/sourcecode/context_scorer.py +0 -0
- {sourcecode-1.31.0 → sourcecode-1.31.2}/src/sourcecode/context_summarizer.py +0 -0
- {sourcecode-1.31.0 → sourcecode-1.31.2}/src/sourcecode/contract_model.py +0 -0
- {sourcecode-1.31.0 → sourcecode-1.31.2}/src/sourcecode/contract_pipeline.py +0 -0
- {sourcecode-1.31.0 → sourcecode-1.31.2}/src/sourcecode/coverage_parser.py +0 -0
- {sourcecode-1.31.0 → sourcecode-1.31.2}/src/sourcecode/dependency_analyzer.py +0 -0
- {sourcecode-1.31.0 → sourcecode-1.31.2}/src/sourcecode/detectors/__init__.py +0 -0
- {sourcecode-1.31.0 → sourcecode-1.31.2}/src/sourcecode/detectors/base.py +0 -0
- {sourcecode-1.31.0 → sourcecode-1.31.2}/src/sourcecode/detectors/csproj_parser.py +0 -0
- {sourcecode-1.31.0 → sourcecode-1.31.2}/src/sourcecode/detectors/dart.py +0 -0
- {sourcecode-1.31.0 → sourcecode-1.31.2}/src/sourcecode/detectors/dotnet.py +0 -0
- {sourcecode-1.31.0 → sourcecode-1.31.2}/src/sourcecode/detectors/elixir.py +0 -0
- {sourcecode-1.31.0 → sourcecode-1.31.2}/src/sourcecode/detectors/go.py +0 -0
- {sourcecode-1.31.0 → sourcecode-1.31.2}/src/sourcecode/detectors/heuristic.py +0 -0
- {sourcecode-1.31.0 → sourcecode-1.31.2}/src/sourcecode/detectors/hybrid.py +0 -0
- {sourcecode-1.31.0 → sourcecode-1.31.2}/src/sourcecode/detectors/java.py +0 -0
- {sourcecode-1.31.0 → sourcecode-1.31.2}/src/sourcecode/detectors/jvm_ext.py +0 -0
- {sourcecode-1.31.0 → sourcecode-1.31.2}/src/sourcecode/detectors/nodejs.py +0 -0
- {sourcecode-1.31.0 → sourcecode-1.31.2}/src/sourcecode/detectors/parsers.py +0 -0
- {sourcecode-1.31.0 → sourcecode-1.31.2}/src/sourcecode/detectors/php.py +0 -0
- {sourcecode-1.31.0 → sourcecode-1.31.2}/src/sourcecode/detectors/project.py +0 -0
- {sourcecode-1.31.0 → sourcecode-1.31.2}/src/sourcecode/detectors/python.py +0 -0
- {sourcecode-1.31.0 → sourcecode-1.31.2}/src/sourcecode/detectors/ruby.py +0 -0
- {sourcecode-1.31.0 → sourcecode-1.31.2}/src/sourcecode/detectors/rust.py +0 -0
- {sourcecode-1.31.0 → sourcecode-1.31.2}/src/sourcecode/detectors/systems.py +0 -0
- {sourcecode-1.31.0 → sourcecode-1.31.2}/src/sourcecode/detectors/terraform.py +0 -0
- {sourcecode-1.31.0 → sourcecode-1.31.2}/src/sourcecode/detectors/tooling.py +0 -0
- {sourcecode-1.31.0 → sourcecode-1.31.2}/src/sourcecode/doc_analyzer.py +0 -0
- {sourcecode-1.31.0 → sourcecode-1.31.2}/src/sourcecode/entrypoint_classifier.py +0 -0
- {sourcecode-1.31.0 → sourcecode-1.31.2}/src/sourcecode/env_analyzer.py +0 -0
- {sourcecode-1.31.0 → sourcecode-1.31.2}/src/sourcecode/file_classifier.py +0 -0
- {sourcecode-1.31.0 → sourcecode-1.31.2}/src/sourcecode/flow_analyzer.py +0 -0
- {sourcecode-1.31.0 → sourcecode-1.31.2}/src/sourcecode/git_analyzer.py +0 -0
- {sourcecode-1.31.0 → sourcecode-1.31.2}/src/sourcecode/graph_analyzer.py +0 -0
- {sourcecode-1.31.0 → sourcecode-1.31.2}/src/sourcecode/mcp/__init__.py +0 -0
- {sourcecode-1.31.0 → sourcecode-1.31.2}/src/sourcecode/mcp/onboarding/__init__.py +0 -0
- {sourcecode-1.31.0 → sourcecode-1.31.2}/src/sourcecode/mcp/onboarding/applier.py +0 -0
- {sourcecode-1.31.0 → sourcecode-1.31.2}/src/sourcecode/mcp/onboarding/backup.py +0 -0
- {sourcecode-1.31.0 → sourcecode-1.31.2}/src/sourcecode/mcp/onboarding/planner.py +0 -0
- {sourcecode-1.31.0 → sourcecode-1.31.2}/src/sourcecode/metrics_analyzer.py +0 -0
- {sourcecode-1.31.0 → sourcecode-1.31.2}/src/sourcecode/pr_comment_renderer.py +0 -0
- {sourcecode-1.31.0 → sourcecode-1.31.2}/src/sourcecode/progress.py +0 -0
- {sourcecode-1.31.0 → sourcecode-1.31.2}/src/sourcecode/ranking_engine.py +0 -0
- {sourcecode-1.31.0 → sourcecode-1.31.2}/src/sourcecode/redactor.py +0 -0
- {sourcecode-1.31.0 → sourcecode-1.31.2}/src/sourcecode/relevance_scorer.py +0 -0
- {sourcecode-1.31.0 → sourcecode-1.31.2}/src/sourcecode/repo_classifier.py +0 -0
- {sourcecode-1.31.0 → sourcecode-1.31.2}/src/sourcecode/repository_ir.py +0 -0
- {sourcecode-1.31.0 → sourcecode-1.31.2}/src/sourcecode/runtime_classifier.py +0 -0
- {sourcecode-1.31.0 → sourcecode-1.31.2}/src/sourcecode/scanner.py +0 -0
- {sourcecode-1.31.0 → sourcecode-1.31.2}/src/sourcecode/schema.py +0 -0
- {sourcecode-1.31.0 → sourcecode-1.31.2}/src/sourcecode/semantic_analyzer.py +0 -0
- {sourcecode-1.31.0 → sourcecode-1.31.2}/src/sourcecode/summarizer.py +0 -0
- {sourcecode-1.31.0 → sourcecode-1.31.2}/src/sourcecode/telemetry/__init__.py +0 -0
- {sourcecode-1.31.0 → sourcecode-1.31.2}/src/sourcecode/telemetry/config.py +0 -0
- {sourcecode-1.31.0 → sourcecode-1.31.2}/src/sourcecode/telemetry/consent.py +0 -0
- {sourcecode-1.31.0 → sourcecode-1.31.2}/src/sourcecode/telemetry/events.py +0 -0
- {sourcecode-1.31.0 → sourcecode-1.31.2}/src/sourcecode/telemetry/filters.py +0 -0
- {sourcecode-1.31.0 → sourcecode-1.31.2}/src/sourcecode/telemetry/transport.py +0 -0
- {sourcecode-1.31.0 → sourcecode-1.31.2}/src/sourcecode/tree_utils.py +0 -0
- {sourcecode-1.31.0 → sourcecode-1.31.2}/src/sourcecode/workspace.py +0 -0
- {sourcecode-1.31.0 → sourcecode-1.31.2}/tests/__init__.py +0 -0
- {sourcecode-1.31.0 → sourcecode-1.31.2}/tests/conftest.py +0 -0
- {sourcecode-1.31.0 → sourcecode-1.31.2}/tests/fixtures/coverage.xml +0 -0
- {sourcecode-1.31.0 → sourcecode-1.31.2}/tests/fixtures/fastapi_app/pyproject.toml +0 -0
- {sourcecode-1.31.0 → sourcecode-1.31.2}/tests/fixtures/fastapi_app/src/main.py +0 -0
- {sourcecode-1.31.0 → sourcecode-1.31.2}/tests/fixtures/go_service/cmd/api/main.go +0 -0
- {sourcecode-1.31.0 → sourcecode-1.31.2}/tests/fixtures/go_service/go.mod +0 -0
- {sourcecode-1.31.0 → sourcecode-1.31.2}/tests/fixtures/jacoco.xml +0 -0
- {sourcecode-1.31.0 → sourcecode-1.31.2}/tests/fixtures/latin1_sample.java +0 -0
- {sourcecode-1.31.0 → sourcecode-1.31.2}/tests/fixtures/latin1_sample_iso.java +0 -0
- {sourcecode-1.31.0 → sourcecode-1.31.2}/tests/fixtures/lcov.info +0 -0
- {sourcecode-1.31.0 → sourcecode-1.31.2}/tests/fixtures/nextjs_app/app/page.tsx +0 -0
- {sourcecode-1.31.0 → sourcecode-1.31.2}/tests/fixtures/nextjs_app/package.json +0 -0
- {sourcecode-1.31.0 → sourcecode-1.31.2}/tests/fixtures/nextjs_app/pnpm-lock.yaml +0 -0
- {sourcecode-1.31.0 → sourcecode-1.31.2}/tests/fixtures/pnpm_monorepo/apps/web/app/page.tsx +0 -0
- {sourcecode-1.31.0 → sourcecode-1.31.2}/tests/fixtures/pnpm_monorepo/apps/web/package.json +0 -0
- {sourcecode-1.31.0 → sourcecode-1.31.2}/tests/fixtures/pnpm_monorepo/packages/api/main.py +0 -0
- {sourcecode-1.31.0 → sourcecode-1.31.2}/tests/fixtures/pnpm_monorepo/packages/api/pyproject.toml +0 -0
- {sourcecode-1.31.0 → sourcecode-1.31.2}/tests/fixtures/pnpm_monorepo/pnpm-workspace.yaml +0 -0
- {sourcecode-1.31.0 → sourcecode-1.31.2}/tests/fixtures/spring_boot_minimal/pom.xml +0 -0
- {sourcecode-1.31.0 → sourcecode-1.31.2}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/ddd/ausente/application/service/FindAusenteService.java +0 -0
- {sourcecode-1.31.0 → sourcecode-1.31.2}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/ddd/ausente/domain/entities/Ausente.java +0 -0
- {sourcecode-1.31.0 → sourcecode-1.31.2}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/ddd/ausente/infrastructure/rest/AusenteRestController.java +0 -0
- {sourcecode-1.31.0 → sourcecode-1.31.2}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/ddd/autocoberturas/application/service/FindAutocoberturasService.java +0 -0
- {sourcecode-1.31.0 → sourcecode-1.31.2}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/ddd/autocoberturas/domain/entities/Autocoberturas.java +0 -0
- {sourcecode-1.31.0 → sourcecode-1.31.2}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/ddd/autocoberturas/infrastructure/rest/AutocoberturasRestController.java +0 -0
- {sourcecode-1.31.0 → sourcecode-1.31.2}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/ddd/calendarioTrabajador/application/service/FindCalendarioTrabajadorService.java +0 -0
- {sourcecode-1.31.0 → sourcecode-1.31.2}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/ddd/calendarioTrabajador/domain/entities/CalendarioTrabajador.java +0 -0
- {sourcecode-1.31.0 → sourcecode-1.31.2}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/ddd/calendarioTrabajador/infrastructure/rest/CalendarioTrabajadorRestController.java +0 -0
- {sourcecode-1.31.0 → sourcecode-1.31.2}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/ddd/departamento/application/service/FindDepartamentoService.java +0 -0
- {sourcecode-1.31.0 → sourcecode-1.31.2}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/ddd/departamento/domain/entities/Departamento.java +0 -0
- {sourcecode-1.31.0 → sourcecode-1.31.2}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/ddd/departamento/infrastructure/rest/DepartamentoRestController.java +0 -0
- {sourcecode-1.31.0 → sourcecode-1.31.2}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/ddd/empleado/application/service/FindEmpleadoService.java +0 -0
- {sourcecode-1.31.0 → sourcecode-1.31.2}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/ddd/empleado/domain/entities/Empleado.java +0 -0
- {sourcecode-1.31.0 → sourcecode-1.31.2}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/ddd/empleado/infrastructure/rest/EmpleadoRestController.java +0 -0
- {sourcecode-1.31.0 → sourcecode-1.31.2}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/demo/DemoApplication.java +0 -0
- {sourcecode-1.31.0 → sourcecode-1.31.2}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/demo/config/FilterConfig.java +0 -0
- {sourcecode-1.31.0 → sourcecode-1.31.2}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/demo/domain/Health.java +0 -0
- {sourcecode-1.31.0 → sourcecode-1.31.2}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/demo/mapper/HealthMapper.java +0 -0
- {sourcecode-1.31.0 → sourcecode-1.31.2}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/demo/repository/HealthRepository.java +0 -0
- {sourcecode-1.31.0 → sourcecode-1.31.2}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/demo/service/HealthService.java +0 -0
- {sourcecode-1.31.0 → sourcecode-1.31.2}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/demo/web/HealthRestController.java +0 -0
- {sourcecode-1.31.0 → sourcecode-1.31.2}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/demo/web/NominaRestController.java +0 -0
- {sourcecode-1.31.0 → sourcecode-1.31.2}/tests/fixtures/spring_boot_minimal/src/main/resources/application-dev.yml +0 -0
- {sourcecode-1.31.0 → sourcecode-1.31.2}/tests/fixtures/spring_boot_minimal/src/main/resources/application.yml +0 -0
- {sourcecode-1.31.0 → sourcecode-1.31.2}/tests/fixtures/spring_boot_minimal/src/main/resources/mapper/HealthMapper.xml +0 -0
- {sourcecode-1.31.0 → sourcecode-1.31.2}/tests/test_architecture_analyzer.py +0 -0
- {sourcecode-1.31.0 → sourcecode-1.31.2}/tests/test_architecture_summary.py +0 -0
- {sourcecode-1.31.0 → sourcecode-1.31.2}/tests/test_ast_extractor.py +0 -0
- {sourcecode-1.31.0 → sourcecode-1.31.2}/tests/test_block1_reliability.py +0 -0
- {sourcecode-1.31.0 → sourcecode-1.31.2}/tests/test_block2_coverage.py +0 -0
- {sourcecode-1.31.0 → sourcecode-1.31.2}/tests/test_block5_quality.py +0 -0
- {sourcecode-1.31.0 → sourcecode-1.31.2}/tests/test_bug_fixes_v1302.py +0 -0
- {sourcecode-1.31.0 → sourcecode-1.31.2}/tests/test_bug_fixes_v16.py +0 -0
- {sourcecode-1.31.0 → sourcecode-1.31.2}/tests/test_bug_fixes_v2.py +0 -0
- {sourcecode-1.31.0 → sourcecode-1.31.2}/tests/test_classifier.py +0 -0
- {sourcecode-1.31.0 → sourcecode-1.31.2}/tests/test_cli.py +0 -0
- {sourcecode-1.31.0 → sourcecode-1.31.2}/tests/test_code_notes_analyzer.py +0 -0
- {sourcecode-1.31.0 → sourcecode-1.31.2}/tests/test_context_scorer.py +0 -0
- {sourcecode-1.31.0 → sourcecode-1.31.2}/tests/test_contract_pipeline.py +0 -0
- {sourcecode-1.31.0 → sourcecode-1.31.2}/tests/test_coverage_parser.py +0 -0
- {sourcecode-1.31.0 → sourcecode-1.31.2}/tests/test_cross_consistency.py +0 -0
- {sourcecode-1.31.0 → sourcecode-1.31.2}/tests/test_dependency_analyzer_node_python.py +0 -0
- {sourcecode-1.31.0 → sourcecode-1.31.2}/tests/test_dependency_analyzer_polyglot.py +0 -0
- {sourcecode-1.31.0 → sourcecode-1.31.2}/tests/test_dependency_schema.py +0 -0
- {sourcecode-1.31.0 → sourcecode-1.31.2}/tests/test_detector_dotnet.py +0 -0
- {sourcecode-1.31.0 → sourcecode-1.31.2}/tests/test_detector_go_rust_java.py +0 -0
- {sourcecode-1.31.0 → sourcecode-1.31.2}/tests/test_detector_nodejs.py +0 -0
- {sourcecode-1.31.0 → sourcecode-1.31.2}/tests/test_detector_php_ruby_dart.py +0 -0
- {sourcecode-1.31.0 → sourcecode-1.31.2}/tests/test_detector_python.py +0 -0
- {sourcecode-1.31.0 → sourcecode-1.31.2}/tests/test_detector_universal_managed.py +0 -0
- {sourcecode-1.31.0 → sourcecode-1.31.2}/tests/test_detector_universal_systems.py +0 -0
- {sourcecode-1.31.0 → sourcecode-1.31.2}/tests/test_detectors_base.py +0 -0
- {sourcecode-1.31.0 → sourcecode-1.31.2}/tests/test_doc_analyzer_jsdom.py +0 -0
- {sourcecode-1.31.0 → sourcecode-1.31.2}/tests/test_doc_analyzer_python.py +0 -0
- {sourcecode-1.31.0 → sourcecode-1.31.2}/tests/test_encoding_regression.py +0 -0
- {sourcecode-1.31.0 → sourcecode-1.31.2}/tests/test_graph_analyzer_polyglot.py +0 -0
- {sourcecode-1.31.0 → sourcecode-1.31.2}/tests/test_graph_analyzer_python_node.py +0 -0
- {sourcecode-1.31.0 → sourcecode-1.31.2}/tests/test_graph_schema.py +0 -0
- {sourcecode-1.31.0 → sourcecode-1.31.2}/tests/test_hybrid_inference.py +0 -0
- {sourcecode-1.31.0 → sourcecode-1.31.2}/tests/test_integration.py +0 -0
- {sourcecode-1.31.0 → sourcecode-1.31.2}/tests/test_integration_dependencies.py +0 -0
- {sourcecode-1.31.0 → sourcecode-1.31.2}/tests/test_integration_detection.py +0 -0
- {sourcecode-1.31.0 → sourcecode-1.31.2}/tests/test_integration_docs.py +0 -0
- {sourcecode-1.31.0 → sourcecode-1.31.2}/tests/test_integration_graph_modules.py +0 -0
- {sourcecode-1.31.0 → sourcecode-1.31.2}/tests/test_integration_lqn.py +0 -0
- {sourcecode-1.31.0 → sourcecode-1.31.2}/tests/test_integration_metrics.py +0 -0
- {sourcecode-1.31.0 → sourcecode-1.31.2}/tests/test_integration_multistack.py +0 -0
- {sourcecode-1.31.0 → sourcecode-1.31.2}/tests/test_integration_semantics.py +0 -0
- {sourcecode-1.31.0 → sourcecode-1.31.2}/tests/test_integration_universal.py +0 -0
- {sourcecode-1.31.0 → sourcecode-1.31.2}/tests/test_java_spring_integration.py +0 -0
- {sourcecode-1.31.0 → sourcecode-1.31.2}/tests/test_metrics_analyzer.py +0 -0
- {sourcecode-1.31.0 → sourcecode-1.31.2}/tests/test_output_ux.py +0 -0
- {sourcecode-1.31.0 → sourcecode-1.31.2}/tests/test_packaging.py +0 -0
- {sourcecode-1.31.0 → sourcecode-1.31.2}/tests/test_phase1_improvements.py +0 -0
- {sourcecode-1.31.0 → sourcecode-1.31.2}/tests/test_pipeline_integrity.py +0 -0
- {sourcecode-1.31.0 → sourcecode-1.31.2}/tests/test_real_projects.py +0 -0
- {sourcecode-1.31.0 → sourcecode-1.31.2}/tests/test_redactor.py +0 -0
- {sourcecode-1.31.0 → sourcecode-1.31.2}/tests/test_repository_ir.py +0 -0
- {sourcecode-1.31.0 → sourcecode-1.31.2}/tests/test_scanner.py +0 -0
- {sourcecode-1.31.0 → sourcecode-1.31.2}/tests/test_schema.py +0 -0
- {sourcecode-1.31.0 → sourcecode-1.31.2}/tests/test_schema_normalization.py +0 -0
- {sourcecode-1.31.0 → sourcecode-1.31.2}/tests/test_scoring_calibration.py +0 -0
- {sourcecode-1.31.0 → sourcecode-1.31.2}/tests/test_semantic_analyzer_node.py +0 -0
- {sourcecode-1.31.0 → sourcecode-1.31.2}/tests/test_semantic_analyzer_python.py +0 -0
- {sourcecode-1.31.0 → sourcecode-1.31.2}/tests/test_semantic_import_resolution.py +0 -0
- {sourcecode-1.31.0 → sourcecode-1.31.2}/tests/test_semantic_schema.py +0 -0
- {sourcecode-1.31.0 → sourcecode-1.31.2}/tests/test_signal_hierarchy.py +0 -0
- {sourcecode-1.31.0 → sourcecode-1.31.2}/tests/test_summarizer.py +0 -0
- {sourcecode-1.31.0 → sourcecode-1.31.2}/tests/test_surface_honesty.py +0 -0
- {sourcecode-1.31.0 → sourcecode-1.31.2}/tests/test_task_differentiation.py +0 -0
- {sourcecode-1.31.0 → sourcecode-1.31.2}/tests/test_telemetry.py +0 -0
- {sourcecode-1.31.0 → sourcecode-1.31.2}/tests/test_v131_improvements.py +0 -0
- {sourcecode-1.31.0 → sourcecode-1.31.2}/tests/test_v1_10_regressions.py +0 -0
- {sourcecode-1.31.0 → sourcecode-1.31.2}/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.2
|
|
4
4
|
Summary: Deterministic codebase context for AI coding agents
|
|
5
5
|
License: Apache License
|
|
6
6
|
Version 2.0, January 2004
|
|
@@ -203,6 +203,7 @@ Classifier: Programming Language :: Python :: 3.12
|
|
|
203
203
|
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
204
204
|
Classifier: Topic :: Utilities
|
|
205
205
|
Requires-Python: >=3.9
|
|
206
|
+
Requires-Dist: mcp>=1.0.0
|
|
206
207
|
Requires-Dist: pathspec>=1.0
|
|
207
208
|
Requires-Dist: ruamel-yaml>=0.18
|
|
208
209
|
Requires-Dist: tomli>=2.0; python_version < '3.11'
|
|
@@ -224,7 +225,7 @@ Description-Content-Type: text/markdown
|
|
|
224
225
|
|
|
225
226
|
**Deterministic, behavior-aware codebase context for AI agents and PR review.**
|
|
226
227
|
|
|
227
|
-

|
|
228
229
|

|
|
229
230
|
|
|
230
231
|
---
|
|
@@ -260,7 +261,7 @@ pipx install sourcecode
|
|
|
260
261
|
|
|
261
262
|
```bash
|
|
262
263
|
sourcecode version
|
|
263
|
-
# sourcecode 1.31.
|
|
264
|
+
# sourcecode 1.31.2
|
|
264
265
|
```
|
|
265
266
|
|
|
266
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.2
|
|
42
42
|
```
|
|
43
43
|
|
|
44
44
|
---
|
|
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "sourcecode"
|
|
7
|
-
version = "1.31.
|
|
7
|
+
version = "1.31.2"
|
|
8
8
|
description = "Deterministic codebase context for AI coding agents"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.9"
|
|
@@ -29,6 +29,7 @@ dependencies = [
|
|
|
29
29
|
"pathspec>=1.0",
|
|
30
30
|
"ruamel.yaml>=0.18",
|
|
31
31
|
"tomli>=2.0; python_version < '3.11'",
|
|
32
|
+
"mcp>=1.0.0",
|
|
32
33
|
]
|
|
33
34
|
|
|
34
35
|
[project.scripts]
|
|
@@ -165,7 +165,7 @@ Compressed AI-ready context for Java/Spring enterprise codebases.
|
|
|
165
165
|
# Known subcommand names — tokens matching these are routed as subcommands,
|
|
166
166
|
# not consumed as a repository path.
|
|
167
167
|
_SUBCOMMANDS: frozenset[str] = frozenset(
|
|
168
|
-
{"telemetry", "prepare-context", "version", "config", "analyze", "repo-ir", "mcp"}
|
|
168
|
+
{"telemetry", "prepare-context", "version", "config", "analyze", "repo-ir", "mcp", "endpoints"}
|
|
169
169
|
)
|
|
170
170
|
|
|
171
171
|
# Mutable container holding the path extracted by _preprocess_argv().
|
|
@@ -2037,6 +2037,27 @@ def prepare_context_cmd(
|
|
|
2037
2037
|
out["changed_files"] = output.changed_files
|
|
2038
2038
|
if _task_include("affected_entry_points") and output.affected_entry_points:
|
|
2039
2039
|
out["affected_entry_points"] = output.affected_entry_points
|
|
2040
|
+
# compact_base fields — included for all non-delta/review-pr tasks (Fix #1)
|
|
2041
|
+
if task not in ("delta", "review-pr"):
|
|
2042
|
+
if output.entry_points_structured:
|
|
2043
|
+
out["entry_points"] = output.entry_points_structured
|
|
2044
|
+
if output.deployment:
|
|
2045
|
+
out["deployment"] = output.deployment
|
|
2046
|
+
if output.deployment_risks:
|
|
2047
|
+
out["deployment_risks"] = output.deployment_risks
|
|
2048
|
+
if output.security_surface:
|
|
2049
|
+
out["security_surface"] = output.security_surface
|
|
2050
|
+
if output.mybatis:
|
|
2051
|
+
out["mybatis"] = output.mybatis
|
|
2052
|
+
if output.transactional_boundaries:
|
|
2053
|
+
out["transactional_boundaries"] = output.transactional_boundaries
|
|
2054
|
+
if output.spring_profiles_info:
|
|
2055
|
+
out["spring_profiles"] = output.spring_profiles_info
|
|
2056
|
+
if output.angular_analysis and (
|
|
2057
|
+
output.angular_analysis.get("component_count", 0) > 0
|
|
2058
|
+
or output.angular_analysis.get("angular_version")
|
|
2059
|
+
):
|
|
2060
|
+
out["angular_analysis"] = output.angular_analysis
|
|
2040
2061
|
# Delta-specific impact fields
|
|
2041
2062
|
if task == "delta":
|
|
2042
2063
|
if output.error_code:
|
|
@@ -2420,6 +2441,190 @@ def repo_ir_cmd(
|
|
|
2420
2441
|
_sys.stdout.write("\n")
|
|
2421
2442
|
|
|
2422
2443
|
|
|
2444
|
+
# ── endpoints ─────────────────────────────────────────────────────────────────
|
|
2445
|
+
|
|
2446
|
+
def _extract_java_endpoints(root: "Path") -> "dict[str, Any]":
|
|
2447
|
+
"""Extract REST endpoint surface from Java source files.
|
|
2448
|
+
|
|
2449
|
+
Scans all .java files for @RequestMapping/@GetMapping/@PostMapping/@PutMapping/
|
|
2450
|
+
@DeleteMapping/@PatchMapping and @M3FiltroSeguridad annotations.
|
|
2451
|
+
Returns JSON-serializable dict with endpoints list, total, and undocumented count.
|
|
2452
|
+
"""
|
|
2453
|
+
import re as _re
|
|
2454
|
+
from pathlib import Path as _Path
|
|
2455
|
+
|
|
2456
|
+
_HTTP_MAPPING_RE = _re.compile(
|
|
2457
|
+
r'@(Get|Post|Put|Delete|Patch|Request)Mapping\s*'
|
|
2458
|
+
r'(?:\(\s*(?:value\s*=\s*)?(?:"([^"]*)"|\{[^}]*\}|[^)]*)\s*\))?',
|
|
2459
|
+
)
|
|
2460
|
+
_CLASS_RE = _re.compile(r'(?:class|interface)\s+(\w+)')
|
|
2461
|
+
_METHOD_RE = _re.compile(
|
|
2462
|
+
r'(?:public|protected|private)\s+\S+\s+(\w+)\s*\(',
|
|
2463
|
+
)
|
|
2464
|
+
_FILTRO_RE = _re.compile(
|
|
2465
|
+
r'@M3FiltroSeguridad\s*\(\s*(?:nombreRecurso\s*=\s*)?["\']([^"\']+)["\']',
|
|
2466
|
+
)
|
|
2467
|
+
_CLASS_PATH_RE = _re.compile(
|
|
2468
|
+
r'@RequestMapping\s*\(\s*(?:value\s*=\s*)?["\']([^"\']+)["\']',
|
|
2469
|
+
)
|
|
2470
|
+
|
|
2471
|
+
_HTTP_METHOD_MAP = {
|
|
2472
|
+
"Get": "GET", "Post": "POST", "Put": "PUT",
|
|
2473
|
+
"Delete": "DELETE", "Patch": "PATCH", "Request": "GET",
|
|
2474
|
+
}
|
|
2475
|
+
|
|
2476
|
+
endpoints: list[dict] = []
|
|
2477
|
+
seen: set[tuple] = set()
|
|
2478
|
+
|
|
2479
|
+
java_files = [
|
|
2480
|
+
p for p in root.rglob("*.java")
|
|
2481
|
+
if "/test/" not in str(p).replace("\\", "/")
|
|
2482
|
+
and "/tests/" not in str(p).replace("\\", "/")
|
|
2483
|
+
and "target/" not in str(p).replace("\\", "/")
|
|
2484
|
+
]
|
|
2485
|
+
|
|
2486
|
+
for java_file in java_files:
|
|
2487
|
+
try:
|
|
2488
|
+
content = java_file.read_text(encoding="utf-8", errors="replace")
|
|
2489
|
+
except OSError:
|
|
2490
|
+
continue
|
|
2491
|
+
|
|
2492
|
+
# Only process files with REST controller or mapping annotations
|
|
2493
|
+
if not any(x in content for x in ("@RestController", "@Controller", "@RequestMapping")):
|
|
2494
|
+
continue
|
|
2495
|
+
|
|
2496
|
+
try:
|
|
2497
|
+
rel_path = str(java_file.relative_to(root)).replace("\\", "/")
|
|
2498
|
+
except ValueError:
|
|
2499
|
+
rel_path = str(java_file).replace("\\", "/")
|
|
2500
|
+
|
|
2501
|
+
# Extract class name
|
|
2502
|
+
cls_m = _CLASS_RE.search(content)
|
|
2503
|
+
class_name = cls_m.group(1) if cls_m else java_file.stem
|
|
2504
|
+
|
|
2505
|
+
# Extract class-level base path from @RequestMapping on the class
|
|
2506
|
+
class_base = ""
|
|
2507
|
+
lines = content.splitlines()
|
|
2508
|
+
for i, line in enumerate(lines):
|
|
2509
|
+
if "@RequestMapping" in line and i < len(lines) - 1:
|
|
2510
|
+
# Check if next non-blank line is class declaration or it's on same block
|
|
2511
|
+
block = "\n".join(lines[max(0, i - 1): i + 5])
|
|
2512
|
+
if "class " in block or "interface " in block:
|
|
2513
|
+
path_m = _CLASS_PATH_RE.search(block)
|
|
2514
|
+
if path_m:
|
|
2515
|
+
class_base = path_m.group(1).rstrip("/")
|
|
2516
|
+
break
|
|
2517
|
+
|
|
2518
|
+
# Extract method-level endpoints
|
|
2519
|
+
# Parse line-by-line to associate annotations with methods
|
|
2520
|
+
pending_annotations: list[tuple[str, str]] = [] # (http_verb, path_suffix)
|
|
2521
|
+
pending_filtro: Optional[str] = None
|
|
2522
|
+
|
|
2523
|
+
for i, line in enumerate(lines):
|
|
2524
|
+
stripped = line.strip()
|
|
2525
|
+
|
|
2526
|
+
# Check for @M3FiltroSeguridad
|
|
2527
|
+
fm = _FILTRO_RE.search(stripped)
|
|
2528
|
+
if fm:
|
|
2529
|
+
pending_filtro = fm.group(1)
|
|
2530
|
+
continue
|
|
2531
|
+
|
|
2532
|
+
# Check for HTTP mapping annotations
|
|
2533
|
+
hm = _HTTP_MAPPING_RE.search(stripped)
|
|
2534
|
+
if hm:
|
|
2535
|
+
verb_key = hm.group(1)
|
|
2536
|
+
http_verb = _HTTP_METHOD_MAP.get(verb_key, "GET")
|
|
2537
|
+
path_suffix = (hm.group(2) or "").strip()
|
|
2538
|
+
pending_annotations.append((http_verb, path_suffix))
|
|
2539
|
+
continue
|
|
2540
|
+
|
|
2541
|
+
# Check for method declaration — flush pending annotations
|
|
2542
|
+
if pending_annotations and ("public " in stripped or "protected " in stripped):
|
|
2543
|
+
mm = _METHOD_RE.search(stripped)
|
|
2544
|
+
handler = mm.group(1) if mm else ""
|
|
2545
|
+
if handler and not handler.startswith("class"):
|
|
2546
|
+
for http_verb, path_suffix in pending_annotations:
|
|
2547
|
+
full_path = (class_base + "/" + path_suffix).replace("//", "/").rstrip("/") or "/"
|
|
2548
|
+
if not full_path.startswith("/"):
|
|
2549
|
+
full_path = "/" + full_path
|
|
2550
|
+
key = (class_name, handler, http_verb)
|
|
2551
|
+
if key not in seen:
|
|
2552
|
+
seen.add(key)
|
|
2553
|
+
entry: dict[str, Any] = {
|
|
2554
|
+
"method": http_verb,
|
|
2555
|
+
"path": full_path,
|
|
2556
|
+
"controller": class_name,
|
|
2557
|
+
"handler": handler,
|
|
2558
|
+
}
|
|
2559
|
+
if pending_filtro:
|
|
2560
|
+
entry["required_permission"] = pending_filtro
|
|
2561
|
+
endpoints.append(entry)
|
|
2562
|
+
pending_annotations = []
|
|
2563
|
+
pending_filtro = None
|
|
2564
|
+
continue
|
|
2565
|
+
|
|
2566
|
+
# Non-annotation, non-method line — reset if it's a closing brace or blank
|
|
2567
|
+
if stripped in ("}", "{", "") or stripped.startswith("//") or stripped.startswith("*"):
|
|
2568
|
+
if stripped == "}":
|
|
2569
|
+
pending_annotations = []
|
|
2570
|
+
pending_filtro = None
|
|
2571
|
+
|
|
2572
|
+
endpoints.sort(key=lambda e: (e.get("controller", ""), e.get("path", "")))
|
|
2573
|
+
undocumented = sum(1 for e in endpoints if "required_permission" not in e)
|
|
2574
|
+
|
|
2575
|
+
return {
|
|
2576
|
+
"endpoints": endpoints,
|
|
2577
|
+
"total": len(endpoints),
|
|
2578
|
+
"undocumented": undocumented,
|
|
2579
|
+
}
|
|
2580
|
+
|
|
2581
|
+
|
|
2582
|
+
@app.command("endpoints")
|
|
2583
|
+
def endpoints_cmd(
|
|
2584
|
+
path: Path = typer.Argument(
|
|
2585
|
+
Path("."),
|
|
2586
|
+
help="Repository path to scan for REST endpoints (default: current directory)",
|
|
2587
|
+
),
|
|
2588
|
+
output_path: Optional[Path] = typer.Option(
|
|
2589
|
+
None, "--output", "-o",
|
|
2590
|
+
help="Write output to a file instead of stdout.",
|
|
2591
|
+
),
|
|
2592
|
+
) -> None:
|
|
2593
|
+
"""Extract REST API endpoint surface from Java source files.
|
|
2594
|
+
|
|
2595
|
+
\b
|
|
2596
|
+
Scans all @GetMapping/@PostMapping/@PutMapping/@DeleteMapping/@PatchMapping
|
|
2597
|
+
and @RequestMapping annotations. Extracts HTTP method, path, controller class,
|
|
2598
|
+
handler method, and @M3FiltroSeguridad permission resource name.
|
|
2599
|
+
|
|
2600
|
+
\b
|
|
2601
|
+
Examples:
|
|
2602
|
+
sourcecode endpoints .
|
|
2603
|
+
sourcecode endpoints /path/to/repo
|
|
2604
|
+
sourcecode endpoints . --output endpoints.json
|
|
2605
|
+
"""
|
|
2606
|
+
import sys as _sys
|
|
2607
|
+
|
|
2608
|
+
target = path.resolve()
|
|
2609
|
+
if not target.exists() or not target.is_dir():
|
|
2610
|
+
typer.echo(f"Error: '{target}' is not a valid directory.", err=True)
|
|
2611
|
+
raise typer.Exit(code=1)
|
|
2612
|
+
|
|
2613
|
+
data = _extract_java_endpoints(target)
|
|
2614
|
+
output = json.dumps(data, indent=2, ensure_ascii=False)
|
|
2615
|
+
|
|
2616
|
+
if output_path is not None:
|
|
2617
|
+
output_path.write_text(output, encoding="utf-8")
|
|
2618
|
+
typer.echo(
|
|
2619
|
+
f"Endpoints written to {output_path} ({data['total']} endpoints)",
|
|
2620
|
+
err=True,
|
|
2621
|
+
)
|
|
2622
|
+
else:
|
|
2623
|
+
_sys.stdout.buffer.write(output.encode("utf-8"))
|
|
2624
|
+
_sys.stdout.buffer.write(b"\n")
|
|
2625
|
+
_sys.stdout.buffer.flush()
|
|
2626
|
+
|
|
2627
|
+
|
|
2423
2628
|
# ── version ───────────────────────────────────────────────────────────────────
|
|
2424
2629
|
|
|
2425
2630
|
@app.command("version")
|
|
@@ -2466,10 +2671,6 @@ def analyze_cmd(
|
|
|
2466
2671
|
def mcp_serve() -> None:
|
|
2467
2672
|
"""Start the MCP server on stdio for AI agent integration.
|
|
2468
2673
|
|
|
2469
|
-
\b
|
|
2470
|
-
Requires the 'mcp' extra:
|
|
2471
|
-
pip install sourcecode[mcp]
|
|
2472
|
-
|
|
2473
2674
|
\b
|
|
2474
2675
|
Configure in your MCP client (e.g. Claude Desktop):
|
|
2475
2676
|
{
|
|
@@ -2487,15 +2688,7 @@ def mcp_serve() -> None:
|
|
|
2487
2688
|
level=logging.INFO,
|
|
2488
2689
|
format="[sourcecode-mcp] %(levelname)s %(message)s",
|
|
2489
2690
|
)
|
|
2490
|
-
|
|
2491
|
-
from sourcecode.mcp.server import mcp as _mcp
|
|
2492
|
-
except ImportError:
|
|
2493
|
-
typer.echo(
|
|
2494
|
-
"MCP support not available. Install with:\n"
|
|
2495
|
-
" pip install sourcecode[mcp]",
|
|
2496
|
-
err=True,
|
|
2497
|
-
)
|
|
2498
|
-
raise typer.Exit(code=1)
|
|
2691
|
+
from sourcecode.mcp.server import mcp as _mcp
|
|
2499
2692
|
|
|
2500
2693
|
log = logging.getLogger(__name__)
|
|
2501
2694
|
log.info("sourcecode-mcp starting (stdio transport)")
|
|
@@ -2513,21 +2706,44 @@ def mcp_serve() -> None:
|
|
|
2513
2706
|
@mcp_app.command("init")
|
|
2514
2707
|
def mcp_init(
|
|
2515
2708
|
yes: bool = typer.Option(False, "--yes", "-y", help="Skip confirmation prompt."),
|
|
2709
|
+
target: Optional[str] = typer.Option(
|
|
2710
|
+
None,
|
|
2711
|
+
"--target",
|
|
2712
|
+
"-t",
|
|
2713
|
+
help="Target client: claude-desktop | cursor. Default: auto-detect all.",
|
|
2714
|
+
),
|
|
2516
2715
|
) -> None:
|
|
2517
2716
|
"""Setup MCP integration for Claude Desktop, Cursor, and other clients.
|
|
2518
2717
|
|
|
2519
2718
|
\b
|
|
2520
2719
|
Detects installed MCP clients, backs up their config files, and safely
|
|
2521
2720
|
inserts the sourcecode server entry. Fully idempotent — safe to re-run.
|
|
2721
|
+
|
|
2722
|
+
\b
|
|
2723
|
+
Examples:
|
|
2724
|
+
sourcecode mcp init
|
|
2725
|
+
sourcecode mcp init --target claude-desktop
|
|
2726
|
+
sourcecode mcp init --target cursor --yes
|
|
2522
2727
|
"""
|
|
2523
|
-
from sourcecode.mcp.onboarding.detector import detect_clients
|
|
2728
|
+
from sourcecode.mcp.onboarding.detector import detect_clients, is_client_running
|
|
2524
2729
|
from sourcecode.mcp.onboarding.planner import build_install_plan
|
|
2525
2730
|
from sourcecode.mcp.onboarding import backup, applier
|
|
2526
2731
|
|
|
2527
2732
|
typer.echo("Detecting MCP clients...")
|
|
2528
2733
|
typer.echo("")
|
|
2529
2734
|
|
|
2530
|
-
|
|
2735
|
+
all_clients = detect_clients()
|
|
2736
|
+
|
|
2737
|
+
if target:
|
|
2738
|
+
target_slug = target.lower()
|
|
2739
|
+
clients = [c for c in all_clients if c.slug == target_slug]
|
|
2740
|
+
if not clients:
|
|
2741
|
+
valid = ", ".join(c.slug for c in all_clients)
|
|
2742
|
+
typer.echo(f"Unknown target '{target}'. Valid: {valid}", err=True)
|
|
2743
|
+
raise typer.Exit(code=1)
|
|
2744
|
+
else:
|
|
2745
|
+
clients = all_clients
|
|
2746
|
+
|
|
2531
2747
|
if not clients:
|
|
2532
2748
|
typer.echo("No MCP clients found on this system.")
|
|
2533
2749
|
typer.echo("")
|
|
@@ -2603,36 +2819,81 @@ def mcp_init(
|
|
|
2603
2819
|
typer.echo("MCP integration active.")
|
|
2604
2820
|
typer.echo("")
|
|
2605
2821
|
|
|
2606
|
-
|
|
2607
|
-
|
|
2608
|
-
|
|
2822
|
+
# Post-write: validate config and warn if client not running
|
|
2823
|
+
for a in actionable:
|
|
2824
|
+
if not is_client_running(a.client):
|
|
2825
|
+
typer.echo(
|
|
2826
|
+
f" ⚠ Config written but {a.client.name} is not running. "
|
|
2827
|
+
f"Start {a.client.name} and run sourcecode mcp status to verify.",
|
|
2828
|
+
err=False,
|
|
2829
|
+
)
|
|
2830
|
+
else:
|
|
2831
|
+
restart_msg = "" if a.will_create_file else f" Restart {a.client.name} to apply."
|
|
2832
|
+
typer.echo(f" ✓ {a.client.name} is running.{restart_msg}")
|
|
2833
|
+
|
|
2834
|
+
typer.echo("")
|
|
2609
2835
|
typer.echo(" Remove: sourcecode mcp remove")
|
|
2610
2836
|
|
|
2611
2837
|
|
|
2612
2838
|
@mcp_app.command("status")
|
|
2613
2839
|
def mcp_status() -> None:
|
|
2614
|
-
"""Show MCP integration status
|
|
2615
|
-
from sourcecode.mcp.onboarding.detector import detect_clients
|
|
2840
|
+
"""Show MCP integration status: dependencies, config files, and connectivity."""
|
|
2841
|
+
from sourcecode.mcp.onboarding.detector import detect_clients, is_client_running
|
|
2616
2842
|
from sourcecode.mcp.onboarding import applier
|
|
2617
2843
|
|
|
2618
|
-
|
|
2619
|
-
|
|
2844
|
+
sep = "─" * 46
|
|
2845
|
+
|
|
2846
|
+
typer.echo("MCP Status")
|
|
2847
|
+
typer.echo(sep)
|
|
2848
|
+
|
|
2849
|
+
# Stage 1: Dependencies
|
|
2850
|
+
try:
|
|
2851
|
+
import mcp as _mcp_pkg # noqa: F401
|
|
2852
|
+
typer.echo("Dependencies ✓ installed")
|
|
2853
|
+
except ImportError:
|
|
2854
|
+
typer.echo("Dependencies ✗ missing")
|
|
2855
|
+
typer.echo(" Fix: pip install sourcecode[mcp]")
|
|
2620
2856
|
typer.echo("")
|
|
2621
2857
|
|
|
2858
|
+
clients = detect_clients()
|
|
2622
2859
|
if not clients:
|
|
2623
2860
|
typer.echo(" No MCP clients detected on this system.")
|
|
2861
|
+
typer.echo(sep)
|
|
2862
|
+
typer.echo(" Setup: sourcecode mcp init")
|
|
2624
2863
|
raise typer.Exit(code=0)
|
|
2625
2864
|
|
|
2865
|
+
# Stage 2: Config files
|
|
2866
|
+
typer.echo("Config files")
|
|
2626
2867
|
for client in clients:
|
|
2627
2868
|
if not client.app_installed:
|
|
2628
|
-
typer.echo(f"
|
|
2869
|
+
typer.echo(f" {client.name:<20} ✗ not found")
|
|
2870
|
+
typer.echo(f" Expected: {client.config_path}")
|
|
2871
|
+
typer.echo(f" Fix: sourcecode mcp init --target {client.slug}")
|
|
2629
2872
|
continue
|
|
2630
2873
|
config = applier.read_config(client.config_path)
|
|
2631
2874
|
if applier.is_installed(config):
|
|
2632
|
-
typer.echo(f"
|
|
2875
|
+
typer.echo(f" {client.name:<20} ✓ configured {client.config_path}")
|
|
2633
2876
|
else:
|
|
2634
|
-
typer.echo(f"
|
|
2877
|
+
typer.echo(f" {client.name:<20} ✗ not configured")
|
|
2878
|
+
typer.echo(f" Fix: sourcecode mcp init --target {client.slug}")
|
|
2635
2879
|
typer.echo("")
|
|
2880
|
+
|
|
2881
|
+
# Stage 3: Connectivity
|
|
2882
|
+
typer.echo("Connectivity")
|
|
2883
|
+
any_installed = any(c.app_installed for c in clients)
|
|
2884
|
+
if not any_installed:
|
|
2885
|
+
typer.echo(" (no clients to check)")
|
|
2886
|
+
else:
|
|
2887
|
+
for client in clients:
|
|
2888
|
+
if not client.app_installed:
|
|
2889
|
+
continue
|
|
2890
|
+
if is_client_running(client):
|
|
2891
|
+
typer.echo(f" {client.name:<20} ✓ running")
|
|
2892
|
+
else:
|
|
2893
|
+
typer.echo(f" {client.name:<20} ✗ not running")
|
|
2894
|
+
typer.echo(f" Fix: open {client.name}, then run sourcecode mcp status")
|
|
2895
|
+
|
|
2896
|
+
typer.echo(sep)
|
|
2636
2897
|
typer.echo(" Setup: sourcecode mcp init")
|
|
2637
2898
|
typer.echo(" Remove: sourcecode mcp remove")
|
|
2638
2899
|
|
|
@@ -175,7 +175,7 @@ class ConfidenceAnalyzer:
|
|
|
175
175
|
if dep_summary is None or not dep_summary.requested:
|
|
176
176
|
gaps.append(AnalysisGap(
|
|
177
177
|
area="dependencies",
|
|
178
|
-
reason="Dependencies not analyzed —
|
|
178
|
+
reason="Dependencies not analyzed — use the full analyze command with dependency flags for complete context",
|
|
179
179
|
impact="medium",
|
|
180
180
|
))
|
|
181
181
|
elif dep_summary.requested and dep_summary.total_count == 0:
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
"""Detect MCP-capable clients installed on the current machine."""
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
|
|
4
|
+
import os
|
|
5
|
+
import subprocess
|
|
6
|
+
import sys
|
|
7
|
+
from dataclasses import dataclass
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
from typing import Any, Dict, List
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
_CLIENT_REGISTRY: List[Dict[str, Any]] = [
|
|
13
|
+
{
|
|
14
|
+
"name": "Claude Desktop",
|
|
15
|
+
"slug": "claude-desktop",
|
|
16
|
+
"paths": {
|
|
17
|
+
"darwin": "~/Library/Application Support/Claude/claude_desktop_config.json",
|
|
18
|
+
"linux": "~/.config/Claude/claude_desktop_config.json",
|
|
19
|
+
"win32": "{APPDATA}/Claude/claude_desktop_config.json",
|
|
20
|
+
},
|
|
21
|
+
"process": {
|
|
22
|
+
"darwin": "Claude",
|
|
23
|
+
"linux": "claude-desktop",
|
|
24
|
+
"win32": "Claude",
|
|
25
|
+
},
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
"name": "Cursor",
|
|
29
|
+
"slug": "cursor",
|
|
30
|
+
"paths": {
|
|
31
|
+
"darwin": "~/.cursor/mcp.json",
|
|
32
|
+
"linux": "~/.cursor/mcp.json",
|
|
33
|
+
"win32": "{USERPROFILE}/.cursor/mcp.json",
|
|
34
|
+
},
|
|
35
|
+
"process": {
|
|
36
|
+
"darwin": "Cursor",
|
|
37
|
+
"linux": "cursor",
|
|
38
|
+
"win32": "Cursor",
|
|
39
|
+
},
|
|
40
|
+
},
|
|
41
|
+
]
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
@dataclass(frozen=True)
|
|
45
|
+
class MCPClient:
|
|
46
|
+
name: str
|
|
47
|
+
config_path: Path
|
|
48
|
+
app_installed: bool # True if the config file (or its parent dir) exists
|
|
49
|
+
process_name: str # OS process name for connectivity check
|
|
50
|
+
slug: str # --target identifier (e.g. "claude-desktop")
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def _resolve(template: str) -> Path:
|
|
54
|
+
"""Expand env vars in Windows-style {VAR} templates, then expanduser."""
|
|
55
|
+
result = template
|
|
56
|
+
for var in ("APPDATA", "LOCALAPPDATA", "USERPROFILE"):
|
|
57
|
+
val = os.environ.get(var, "")
|
|
58
|
+
if val:
|
|
59
|
+
result = result.replace(f"{{{var}}}", val)
|
|
60
|
+
return Path(result).expanduser()
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def detect_clients() -> list[MCPClient]:
|
|
64
|
+
"""Return all known MCP clients with their resolved config paths."""
|
|
65
|
+
plat = sys.platform
|
|
66
|
+
clients: list[MCPClient] = []
|
|
67
|
+
for entry in _CLIENT_REGISTRY:
|
|
68
|
+
paths: Dict[str, str] = entry["paths"]
|
|
69
|
+
processes: Dict[str, str] = entry["process"]
|
|
70
|
+
template = paths.get(plat) or paths.get("linux", "")
|
|
71
|
+
if not template:
|
|
72
|
+
continue
|
|
73
|
+
config_path = _resolve(template)
|
|
74
|
+
app_installed = config_path.exists() or config_path.parent.exists()
|
|
75
|
+
process_name = processes.get(plat) or processes.get("linux", "")
|
|
76
|
+
clients.append(MCPClient(
|
|
77
|
+
name=entry["name"],
|
|
78
|
+
config_path=config_path,
|
|
79
|
+
app_installed=app_installed,
|
|
80
|
+
process_name=process_name,
|
|
81
|
+
slug=entry["slug"],
|
|
82
|
+
))
|
|
83
|
+
return clients
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
def is_client_running(client: MCPClient) -> bool:
|
|
87
|
+
"""True if the client process is currently running."""
|
|
88
|
+
if not client.process_name:
|
|
89
|
+
return False
|
|
90
|
+
try:
|
|
91
|
+
if sys.platform == "win32":
|
|
92
|
+
result = subprocess.run(
|
|
93
|
+
["tasklist", "/fi", f"imagename eq {client.process_name}.exe"],
|
|
94
|
+
capture_output=True, text=True, timeout=5,
|
|
95
|
+
)
|
|
96
|
+
return client.process_name.lower() in result.stdout.lower()
|
|
97
|
+
else:
|
|
98
|
+
result = subprocess.run(
|
|
99
|
+
["pgrep", "-x", client.process_name],
|
|
100
|
+
capture_output=True, timeout=5,
|
|
101
|
+
)
|
|
102
|
+
return result.returncode == 0
|
|
103
|
+
except Exception:
|
|
104
|
+
return False
|
|
@@ -6,14 +6,18 @@ lookup, no process fork, no stdout encoding issues.
|
|
|
6
6
|
"""
|
|
7
7
|
from __future__ import annotations
|
|
8
8
|
|
|
9
|
+
import json
|
|
10
|
+
from typing import Any
|
|
11
|
+
|
|
9
12
|
from typer.testing import CliRunner
|
|
10
13
|
|
|
11
14
|
_runner = CliRunner()
|
|
12
15
|
|
|
13
16
|
|
|
14
|
-
def run_command(args: list[str]) ->
|
|
15
|
-
"""Invoke a sourcecode CLI command in-process and return
|
|
17
|
+
def run_command(args: list[str]) -> Any:
|
|
18
|
+
"""Invoke a sourcecode CLI command in-process and return parsed output.
|
|
16
19
|
|
|
20
|
+
Returns parsed JSON dict when output is valid JSON, else the raw string.
|
|
17
21
|
Raises RuntimeError on non-zero exit or empty output.
|
|
18
22
|
"""
|
|
19
23
|
from sourcecode.cli import _detected_path, _preprocess_args, app
|
|
@@ -37,4 +41,7 @@ def run_command(args: list[str]) -> str:
|
|
|
37
41
|
f"Args: {args}"
|
|
38
42
|
)
|
|
39
43
|
|
|
40
|
-
|
|
44
|
+
try:
|
|
45
|
+
return json.loads(output)
|
|
46
|
+
except json.JSONDecodeError:
|
|
47
|
+
return output
|