specfact-cli 0.23.0__tar.gz → 0.24.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {specfact_cli-0.23.0 → specfact_cli-0.24.0}/PKG-INFO +15 -2
- {specfact_cli-0.23.0 → specfact_cli-0.24.0}/README.md +14 -1
- {specfact_cli-0.23.0 → specfact_cli-0.24.0}/pyproject.toml +1 -1
- {specfact_cli-0.23.0 → specfact_cli-0.24.0}/resources/templates/sidecar/common/run_sidecar.sh +124 -35
- {specfact_cli-0.23.0 → specfact_cli-0.24.0}/src/__init__.py +1 -1
- {specfact_cli-0.23.0 → specfact_cli-0.24.0}/src/specfact_cli/__init__.py +1 -1
- {specfact_cli-0.23.0 → specfact_cli-0.24.0}/src/specfact_cli/analyzers/contract_extractor.py +9 -2
- {specfact_cli-0.23.0 → specfact_cli-0.24.0}/src/specfact_cli/cli.py +2 -0
- {specfact_cli-0.23.0 → specfact_cli-0.24.0}/src/specfact_cli/commands/__init__.py +2 -0
- {specfact_cli-0.23.0 → specfact_cli-0.24.0}/src/specfact_cli/commands/import_cmd.py +479 -91
- {specfact_cli-0.23.0 → specfact_cli-0.24.0}/src/specfact_cli/commands/init.py +3 -0
- {specfact_cli-0.23.0 → specfact_cli-0.24.0}/src/specfact_cli/commands/repro.py +45 -0
- specfact_cli-0.24.0/src/specfact_cli/commands/validate.py +158 -0
- {specfact_cli-0.23.0 → specfact_cli-0.24.0}/src/specfact_cli/generators/openapi_extractor.py +268 -75
- {specfact_cli-0.23.0 → specfact_cli-0.24.0}/src/specfact_cli/sync/bridge_sync.py +7 -1
- {specfact_cli-0.23.0 → specfact_cli-0.24.0}/src/specfact_cli/utils/code_change_detector.py +7 -1
- {specfact_cli-0.23.0 → specfact_cli-0.24.0}/src/specfact_cli/utils/incremental_check.py +68 -1
- {specfact_cli-0.23.0 → specfact_cli-0.24.0}/src/specfact_cli/utils/progress.py +42 -12
- specfact_cli-0.24.0/src/specfact_cli/validators/sidecar/__init__.py +11 -0
- specfact_cli-0.24.0/src/specfact_cli/validators/sidecar/contract_populator.py +145 -0
- specfact_cli-0.24.0/src/specfact_cli/validators/sidecar/crosshair_runner.py +98 -0
- specfact_cli-0.24.0/src/specfact_cli/validators/sidecar/crosshair_summary.py +164 -0
- specfact_cli-0.24.0/src/specfact_cli/validators/sidecar/framework_detector.py +184 -0
- specfact_cli-0.24.0/src/specfact_cli/validators/sidecar/frameworks/__init__.py +12 -0
- specfact_cli-0.24.0/src/specfact_cli/validators/sidecar/frameworks/base.py +84 -0
- specfact_cli-0.24.0/src/specfact_cli/validators/sidecar/frameworks/django.py +322 -0
- specfact_cli-0.24.0/src/specfact_cli/validators/sidecar/frameworks/drf.py +95 -0
- specfact_cli-0.24.0/src/specfact_cli/validators/sidecar/frameworks/fastapi.py +225 -0
- specfact_cli-0.24.0/src/specfact_cli/validators/sidecar/harness_generator.py +148 -0
- specfact_cli-0.24.0/src/specfact_cli/validators/sidecar/models.py +184 -0
- specfact_cli-0.24.0/src/specfact_cli/validators/sidecar/orchestrator.py +357 -0
- specfact_cli-0.24.0/src/specfact_cli/validators/sidecar/specmatic_runner.py +106 -0
- specfact_cli-0.24.0/src/specfact_cli/validators/sidecar/unannotated_detector.py +140 -0
- {specfact_cli-0.23.0 → specfact_cli-0.24.0}/.gitignore +0 -0
- {specfact_cli-0.23.0 → specfact_cli-0.24.0}/LICENSE.md +0 -0
- {specfact_cli-0.23.0 → specfact_cli-0.24.0}/resources/mappings/node-async.yaml +0 -0
- {specfact_cli-0.23.0 → specfact_cli-0.24.0}/resources/mappings/python-async.yaml +0 -0
- {specfact_cli-0.23.0 → specfact_cli-0.24.0}/resources/mappings/speckit-default.yaml +0 -0
- {specfact_cli-0.23.0 → specfact_cli-0.24.0}/resources/prompts/shared/cli-enforcement.md +0 -0
- {specfact_cli-0.23.0 → specfact_cli-0.24.0}/resources/prompts/specfact.01-import.md +0 -0
- {specfact_cli-0.23.0 → specfact_cli-0.24.0}/resources/prompts/specfact.02-plan.md +0 -0
- {specfact_cli-0.23.0 → specfact_cli-0.24.0}/resources/prompts/specfact.03-review.md +0 -0
- {specfact_cli-0.23.0 → specfact_cli-0.24.0}/resources/prompts/specfact.04-sdd.md +0 -0
- {specfact_cli-0.23.0 → specfact_cli-0.24.0}/resources/prompts/specfact.05-enforce.md +0 -0
- {specfact_cli-0.23.0 → specfact_cli-0.24.0}/resources/prompts/specfact.06-sync.md +0 -0
- {specfact_cli-0.23.0 → specfact_cli-0.24.0}/resources/prompts/specfact.07-contracts.md +0 -0
- {specfact_cli-0.23.0 → specfact_cli-0.24.0}/resources/prompts/specfact.compare.md +0 -0
- {specfact_cli-0.23.0 → specfact_cli-0.24.0}/resources/prompts/specfact.sync-backlog.md +0 -0
- {specfact_cli-0.23.0 → specfact_cli-0.24.0}/resources/prompts/specfact.validate.md +0 -0
- {specfact_cli-0.23.0 → specfact_cli-0.24.0}/resources/schemas/deviation.schema.json +0 -0
- {specfact_cli-0.23.0 → specfact_cli-0.24.0}/resources/schemas/plan.schema.json +0 -0
- {specfact_cli-0.23.0 → specfact_cli-0.24.0}/resources/schemas/protocol.schema.json +0 -0
- {specfact_cli-0.23.0 → specfact_cli-0.24.0}/resources/templates/github-action.yml.j2 +0 -0
- {specfact_cli-0.23.0 → specfact_cli-0.24.0}/resources/templates/persona/architect.md.j2 +0 -0
- {specfact_cli-0.23.0 → specfact_cli-0.24.0}/resources/templates/persona/developer.md.j2 +0 -0
- {specfact_cli-0.23.0 → specfact_cli-0.24.0}/resources/templates/persona/product-owner.md.j2 +0 -0
- {specfact_cli-0.23.0 → specfact_cli-0.24.0}/resources/templates/plan.bundle.yaml.j2 +0 -0
- {specfact_cli-0.23.0 → specfact_cli-0.24.0}/resources/templates/pr-template.md.j2 +0 -0
- {specfact_cli-0.23.0 → specfact_cli-0.24.0}/resources/templates/protocol.yaml.j2 +0 -0
- {specfact_cli-0.23.0 → specfact_cli-0.24.0}/resources/templates/sidecar/STRUCTURE.md +0 -0
- {specfact_cli-0.23.0 → specfact_cli-0.24.0}/resources/templates/sidecar/__init__.py +0 -0
- {specfact_cli-0.23.0 → specfact_cli-0.24.0}/resources/templates/sidecar/common/README.md +0 -0
- {specfact_cli-0.23.0 → specfact_cli-0.24.0}/resources/templates/sidecar/common/adapters.py +0 -0
- {specfact_cli-0.23.0 → specfact_cli-0.24.0}/resources/templates/sidecar/common/bindings.yaml.example +0 -0
- {specfact_cli-0.23.0 → specfact_cli-0.24.0}/resources/templates/sidecar/common/crosshair_plugin.py +0 -0
- {specfact_cli-0.23.0 → specfact_cli-0.24.0}/resources/templates/sidecar/common/generate_harness.py +0 -0
- {specfact_cli-0.23.0 → specfact_cli-0.24.0}/resources/templates/sidecar/common/harness_contracts.py.example +0 -0
- {specfact_cli-0.23.0 → specfact_cli-0.24.0}/resources/templates/sidecar/common/populate_contracts.py +0 -0
- {specfact_cli-0.23.0 → specfact_cli-0.24.0}/resources/templates/sidecar/common/sidecar-init.sh +0 -0
- {specfact_cli-0.23.0 → specfact_cli-0.24.0}/resources/templates/sidecar/frameworks/django/__init__.py +0 -0
- {specfact_cli-0.23.0 → specfact_cli-0.24.0}/resources/templates/sidecar/frameworks/django/crosshair_django_wrapper.py +0 -0
- {specfact_cli-0.23.0 → specfact_cli-0.24.0}/resources/templates/sidecar/frameworks/django/django_form_extractor.py +0 -0
- {specfact_cli-0.23.0 → specfact_cli-0.24.0}/resources/templates/sidecar/frameworks/django/django_url_extractor.py +0 -0
- {specfact_cli-0.23.0 → specfact_cli-0.24.0}/resources/templates/sidecar/frameworks/drf/__init__.py +0 -0
- {specfact_cli-0.23.0 → specfact_cli-0.24.0}/resources/templates/sidecar/frameworks/drf/drf_serializer_extractor.py +0 -0
- {specfact_cli-0.23.0 → specfact_cli-0.24.0}/resources/templates/sidecar/frameworks/fastapi/__init__.py +0 -0
- {specfact_cli-0.23.0 → specfact_cli-0.24.0}/resources/templates/sidecar/frameworks/fastapi/fastapi_route_extractor.py +0 -0
- {specfact_cli-0.23.0 → specfact_cli-0.24.0}/resources/templates/telemetry.yaml.example +0 -0
- {specfact_cli-0.23.0 → specfact_cli-0.24.0}/src/specfact_cli/adapters/__init__.py +0 -0
- {specfact_cli-0.23.0 → specfact_cli-0.24.0}/src/specfact_cli/adapters/base.py +0 -0
- {specfact_cli-0.23.0 → specfact_cli-0.24.0}/src/specfact_cli/adapters/github.py +0 -0
- {specfact_cli-0.23.0 → specfact_cli-0.24.0}/src/specfact_cli/adapters/openspec.py +0 -0
- {specfact_cli-0.23.0 → specfact_cli-0.24.0}/src/specfact_cli/adapters/openspec_parser.py +0 -0
- {specfact_cli-0.23.0 → specfact_cli-0.24.0}/src/specfact_cli/adapters/registry.py +0 -0
- {specfact_cli-0.23.0 → specfact_cli-0.24.0}/src/specfact_cli/adapters/speckit.py +0 -0
- {specfact_cli-0.23.0 → specfact_cli-0.24.0}/src/specfact_cli/agents/__init__.py +0 -0
- {specfact_cli-0.23.0 → specfact_cli-0.24.0}/src/specfact_cli/agents/analyze_agent.py +0 -0
- {specfact_cli-0.23.0 → specfact_cli-0.24.0}/src/specfact_cli/agents/base.py +0 -0
- {specfact_cli-0.23.0 → specfact_cli-0.24.0}/src/specfact_cli/agents/plan_agent.py +0 -0
- {specfact_cli-0.23.0 → specfact_cli-0.24.0}/src/specfact_cli/agents/registry.py +0 -0
- {specfact_cli-0.23.0 → specfact_cli-0.24.0}/src/specfact_cli/agents/sync_agent.py +0 -0
- {specfact_cli-0.23.0 → specfact_cli-0.24.0}/src/specfact_cli/analyzers/__init__.py +0 -0
- {specfact_cli-0.23.0 → specfact_cli-0.24.0}/src/specfact_cli/analyzers/ambiguity_scanner.py +0 -0
- {specfact_cli-0.23.0 → specfact_cli-0.24.0}/src/specfact_cli/analyzers/code_analyzer.py +0 -0
- {specfact_cli-0.23.0 → specfact_cli-0.24.0}/src/specfact_cli/analyzers/constitution_evidence_extractor.py +0 -0
- {specfact_cli-0.23.0 → specfact_cli-0.24.0}/src/specfact_cli/analyzers/control_flow_analyzer.py +0 -0
- {specfact_cli-0.23.0 → specfact_cli-0.24.0}/src/specfact_cli/analyzers/graph_analyzer.py +0 -0
- {specfact_cli-0.23.0 → specfact_cli-0.24.0}/src/specfact_cli/analyzers/relationship_mapper.py +0 -0
- {specfact_cli-0.23.0 → specfact_cli-0.24.0}/src/specfact_cli/analyzers/requirement_extractor.py +0 -0
- {specfact_cli-0.23.0 → specfact_cli-0.24.0}/src/specfact_cli/analyzers/test_pattern_extractor.py +0 -0
- {specfact_cli-0.23.0 → specfact_cli-0.24.0}/src/specfact_cli/commands/analyze.py +0 -0
- {specfact_cli-0.23.0 → specfact_cli-0.24.0}/src/specfact_cli/commands/contract_cmd.py +0 -0
- {specfact_cli-0.23.0 → specfact_cli-0.24.0}/src/specfact_cli/commands/drift.py +0 -0
- {specfact_cli-0.23.0 → specfact_cli-0.24.0}/src/specfact_cli/commands/enforce.py +0 -0
- {specfact_cli-0.23.0 → specfact_cli-0.24.0}/src/specfact_cli/commands/generate.py +0 -0
- {specfact_cli-0.23.0 → specfact_cli-0.24.0}/src/specfact_cli/commands/migrate.py +0 -0
- {specfact_cli-0.23.0 → specfact_cli-0.24.0}/src/specfact_cli/commands/plan.py +0 -0
- {specfact_cli-0.23.0 → specfact_cli-0.24.0}/src/specfact_cli/commands/project_cmd.py +0 -0
- {specfact_cli-0.23.0 → specfact_cli-0.24.0}/src/specfact_cli/commands/sdd.py +0 -0
- {specfact_cli-0.23.0 → specfact_cli-0.24.0}/src/specfact_cli/commands/spec.py +0 -0
- {specfact_cli-0.23.0 → specfact_cli-0.24.0}/src/specfact_cli/commands/sync.py +0 -0
- {specfact_cli-0.23.0 → specfact_cli-0.24.0}/src/specfact_cli/common/__init__.py +0 -0
- {specfact_cli-0.23.0 → specfact_cli-0.24.0}/src/specfact_cli/common/logger_setup.py +0 -0
- {specfact_cli-0.23.0 → specfact_cli-0.24.0}/src/specfact_cli/common/logging_utils.py +0 -0
- {specfact_cli-0.23.0 → specfact_cli-0.24.0}/src/specfact_cli/common/text_utils.py +0 -0
- {specfact_cli-0.23.0 → specfact_cli-0.24.0}/src/specfact_cli/common/utils.py +0 -0
- {specfact_cli-0.23.0 → specfact_cli-0.24.0}/src/specfact_cli/comparators/__init__.py +0 -0
- {specfact_cli-0.23.0 → specfact_cli-0.24.0}/src/specfact_cli/comparators/plan_comparator.py +0 -0
- {specfact_cli-0.23.0 → specfact_cli-0.24.0}/src/specfact_cli/enrichers/constitution_enricher.py +0 -0
- {specfact_cli-0.23.0 → specfact_cli-0.24.0}/src/specfact_cli/enrichers/plan_enricher.py +0 -0
- {specfact_cli-0.23.0 → specfact_cli-0.24.0}/src/specfact_cli/generators/__init__.py +0 -0
- {specfact_cli-0.23.0 → specfact_cli-0.24.0}/src/specfact_cli/generators/contract_generator.py +0 -0
- {specfact_cli-0.23.0 → specfact_cli-0.24.0}/src/specfact_cli/generators/persona_exporter.py +0 -0
- {specfact_cli-0.23.0 → specfact_cli-0.24.0}/src/specfact_cli/generators/plan_generator.py +0 -0
- {specfact_cli-0.23.0 → specfact_cli-0.24.0}/src/specfact_cli/generators/protocol_generator.py +0 -0
- {specfact_cli-0.23.0 → specfact_cli-0.24.0}/src/specfact_cli/generators/report_generator.py +0 -0
- {specfact_cli-0.23.0 → specfact_cli-0.24.0}/src/specfact_cli/generators/task_generator.py +0 -0
- {specfact_cli-0.23.0 → specfact_cli-0.24.0}/src/specfact_cli/generators/test_to_openapi.py +0 -0
- {specfact_cli-0.23.0 → specfact_cli-0.24.0}/src/specfact_cli/generators/workflow_generator.py +0 -0
- {specfact_cli-0.23.0 → specfact_cli-0.24.0}/src/specfact_cli/importers/__init__.py +0 -0
- {specfact_cli-0.23.0 → specfact_cli-0.24.0}/src/specfact_cli/importers/speckit_converter.py +0 -0
- {specfact_cli-0.23.0 → specfact_cli-0.24.0}/src/specfact_cli/importers/speckit_scanner.py +0 -0
- {specfact_cli-0.23.0 → specfact_cli-0.24.0}/src/specfact_cli/integrations/__init__.py +0 -0
- {specfact_cli-0.23.0 → specfact_cli-0.24.0}/src/specfact_cli/integrations/specmatic.py +0 -0
- {specfact_cli-0.23.0 → specfact_cli-0.24.0}/src/specfact_cli/merge/__init__.py +0 -0
- {specfact_cli-0.23.0 → specfact_cli-0.24.0}/src/specfact_cli/merge/resolver.py +0 -0
- {specfact_cli-0.23.0 → specfact_cli-0.24.0}/src/specfact_cli/migrations/__init__.py +0 -0
- {specfact_cli-0.23.0 → specfact_cli-0.24.0}/src/specfact_cli/migrations/plan_migrator.py +0 -0
- {specfact_cli-0.23.0 → specfact_cli-0.24.0}/src/specfact_cli/models/__init__.py +0 -0
- {specfact_cli-0.23.0 → specfact_cli-0.24.0}/src/specfact_cli/models/bridge.py +0 -0
- {specfact_cli-0.23.0 → specfact_cli-0.24.0}/src/specfact_cli/models/capabilities.py +0 -0
- {specfact_cli-0.23.0 → specfact_cli-0.24.0}/src/specfact_cli/models/change.py +0 -0
- {specfact_cli-0.23.0 → specfact_cli-0.24.0}/src/specfact_cli/models/contract.py +0 -0
- {specfact_cli-0.23.0 → specfact_cli-0.24.0}/src/specfact_cli/models/deviation.py +0 -0
- {specfact_cli-0.23.0 → specfact_cli-0.24.0}/src/specfact_cli/models/enforcement.py +0 -0
- {specfact_cli-0.23.0 → specfact_cli-0.24.0}/src/specfact_cli/models/persona_template.py +0 -0
- {specfact_cli-0.23.0 → specfact_cli-0.24.0}/src/specfact_cli/models/plan.py +0 -0
- {specfact_cli-0.23.0 → specfact_cli-0.24.0}/src/specfact_cli/models/project.py +0 -0
- {specfact_cli-0.23.0 → specfact_cli-0.24.0}/src/specfact_cli/models/protocol.py +0 -0
- {specfact_cli-0.23.0 → specfact_cli-0.24.0}/src/specfact_cli/models/quality.py +0 -0
- {specfact_cli-0.23.0 → specfact_cli-0.24.0}/src/specfact_cli/models/sdd.py +0 -0
- {specfact_cli-0.23.0 → specfact_cli-0.24.0}/src/specfact_cli/models/source_tracking.py +0 -0
- {specfact_cli-0.23.0 → specfact_cli-0.24.0}/src/specfact_cli/models/task.py +0 -0
- {specfact_cli-0.23.0 → specfact_cli-0.24.0}/src/specfact_cli/modes/__init__.py +0 -0
- {specfact_cli-0.23.0 → specfact_cli-0.24.0}/src/specfact_cli/modes/detector.py +0 -0
- {specfact_cli-0.23.0 → specfact_cli-0.24.0}/src/specfact_cli/modes/router.py +0 -0
- {specfact_cli-0.23.0 → specfact_cli-0.24.0}/src/specfact_cli/parsers/__init__.py +0 -0
- {specfact_cli-0.23.0 → specfact_cli-0.24.0}/src/specfact_cli/parsers/persona_importer.py +0 -0
- {specfact_cli-0.23.0 → specfact_cli-0.24.0}/src/specfact_cli/resources/semgrep/async.yml +0 -0
- {specfact_cli-0.23.0 → specfact_cli-0.24.0}/src/specfact_cli/resources/semgrep/code-quality.yml +0 -0
- {specfact_cli-0.23.0 → specfact_cli-0.24.0}/src/specfact_cli/resources/semgrep/feature-detection.yml +0 -0
- {specfact_cli-0.23.0 → specfact_cli-0.24.0}/src/specfact_cli/runtime.py +0 -0
- {specfact_cli-0.23.0 → specfact_cli-0.24.0}/src/specfact_cli/sync/__init__.py +0 -0
- {specfact_cli-0.23.0 → specfact_cli-0.24.0}/src/specfact_cli/sync/bridge_probe.py +0 -0
- {specfact_cli-0.23.0 → specfact_cli-0.24.0}/src/specfact_cli/sync/bridge_watch.py +0 -0
- {specfact_cli-0.23.0 → specfact_cli-0.24.0}/src/specfact_cli/sync/change_detector.py +0 -0
- {specfact_cli-0.23.0 → specfact_cli-0.24.0}/src/specfact_cli/sync/code_to_spec.py +0 -0
- {specfact_cli-0.23.0 → specfact_cli-0.24.0}/src/specfact_cli/sync/drift_detector.py +0 -0
- {specfact_cli-0.23.0 → specfact_cli-0.24.0}/src/specfact_cli/sync/repository_sync.py +0 -0
- {specfact_cli-0.23.0 → specfact_cli-0.24.0}/src/specfact_cli/sync/spec_to_code.py +0 -0
- {specfact_cli-0.23.0 → specfact_cli-0.24.0}/src/specfact_cli/sync/spec_to_tests.py +0 -0
- {specfact_cli-0.23.0 → specfact_cli-0.24.0}/src/specfact_cli/sync/watcher.py +0 -0
- {specfact_cli-0.23.0 → specfact_cli-0.24.0}/src/specfact_cli/sync/watcher_enhanced.py +0 -0
- {specfact_cli-0.23.0 → specfact_cli-0.24.0}/src/specfact_cli/telemetry.py +0 -0
- {specfact_cli-0.23.0 → specfact_cli-0.24.0}/src/specfact_cli/templates/__init__.py +0 -0
- {specfact_cli-0.23.0 → specfact_cli-0.24.0}/src/specfact_cli/templates/bridge_templates.py +0 -0
- {specfact_cli-0.23.0 → specfact_cli-0.24.0}/src/specfact_cli/templates/specification_templates.py +0 -0
- {specfact_cli-0.23.0 → specfact_cli-0.24.0}/src/specfact_cli/utils/__init__.py +0 -0
- {specfact_cli-0.23.0 → specfact_cli-0.24.0}/src/specfact_cli/utils/acceptance_criteria.py +0 -0
- {specfact_cli-0.23.0 → specfact_cli-0.24.0}/src/specfact_cli/utils/bundle_loader.py +0 -0
- {specfact_cli-0.23.0 → specfact_cli-0.24.0}/src/specfact_cli/utils/console.py +0 -0
- {specfact_cli-0.23.0 → specfact_cli-0.24.0}/src/specfact_cli/utils/content_sanitizer.py +0 -0
- {specfact_cli-0.23.0 → specfact_cli-0.24.0}/src/specfact_cli/utils/context_detection.py +0 -0
- {specfact_cli-0.23.0 → specfact_cli-0.24.0}/src/specfact_cli/utils/enrichment_context.py +0 -0
- {specfact_cli-0.23.0 → specfact_cli-0.24.0}/src/specfact_cli/utils/enrichment_parser.py +0 -0
- {specfact_cli-0.23.0 → specfact_cli-0.24.0}/src/specfact_cli/utils/env_manager.py +0 -0
- {specfact_cli-0.23.0 → specfact_cli-0.24.0}/src/specfact_cli/utils/feature_keys.py +0 -0
- {specfact_cli-0.23.0 → specfact_cli-0.24.0}/src/specfact_cli/utils/git.py +0 -0
- {specfact_cli-0.23.0 → specfact_cli-0.24.0}/src/specfact_cli/utils/github_annotations.py +0 -0
- {specfact_cli-0.23.0 → specfact_cli-0.24.0}/src/specfact_cli/utils/ide_setup.py +0 -0
- {specfact_cli-0.23.0 → specfact_cli-0.24.0}/src/specfact_cli/utils/optional_deps.py +0 -0
- {specfact_cli-0.23.0 → specfact_cli-0.24.0}/src/specfact_cli/utils/performance.py +0 -0
- {specfact_cli-0.23.0 → specfact_cli-0.24.0}/src/specfact_cli/utils/progressive_disclosure.py +0 -0
- {specfact_cli-0.23.0 → specfact_cli-0.24.0}/src/specfact_cli/utils/prompts.py +0 -0
- {specfact_cli-0.23.0 → specfact_cli-0.24.0}/src/specfact_cli/utils/sdd_discovery.py +0 -0
- {specfact_cli-0.23.0 → specfact_cli-0.24.0}/src/specfact_cli/utils/source_scanner.py +0 -0
- {specfact_cli-0.23.0 → specfact_cli-0.24.0}/src/specfact_cli/utils/structure.py +0 -0
- {specfact_cli-0.23.0 → specfact_cli-0.24.0}/src/specfact_cli/utils/structured_io.py +0 -0
- {specfact_cli-0.23.0 → specfact_cli-0.24.0}/src/specfact_cli/utils/suggestions.py +0 -0
- {specfact_cli-0.23.0 → specfact_cli-0.24.0}/src/specfact_cli/utils/terminal.py +0 -0
- {specfact_cli-0.23.0 → specfact_cli-0.24.0}/src/specfact_cli/utils/yaml_utils.py +0 -0
- {specfact_cli-0.23.0 → specfact_cli-0.24.0}/src/specfact_cli/validators/__init__.py +0 -0
- {specfact_cli-0.23.0 → specfact_cli-0.24.0}/src/specfact_cli/validators/agile_validation.py +0 -0
- {specfact_cli-0.23.0 → specfact_cli-0.24.0}/src/specfact_cli/validators/cli_first_validator.py +0 -0
- {specfact_cli-0.23.0 → specfact_cli-0.24.0}/src/specfact_cli/validators/contract_validator.py +0 -0
- {specfact_cli-0.23.0 → specfact_cli-0.24.0}/src/specfact_cli/validators/fsm.py +0 -0
- {specfact_cli-0.23.0 → specfact_cli-0.24.0}/src/specfact_cli/validators/repro_checker.py +0 -0
- {specfact_cli-0.23.0 → specfact_cli-0.24.0}/src/specfact_cli/validators/schema.py +0 -0
- {specfact_cli-0.23.0 → specfact_cli-0.24.0}/src/specfact_cli/versioning/__init__.py +0 -0
- {specfact_cli-0.23.0 → specfact_cli-0.24.0}/src/specfact_cli/versioning/analyzer.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: specfact-cli
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.24.0
|
|
4
4
|
Summary: Brownfield-first CLI: Reverse engineer legacy Python → specs → enforced contracts. Automate legacy code documentation and prevent modernization regressions.
|
|
5
5
|
Project-URL: Homepage, https://github.com/nold-ai/specfact-cli
|
|
6
6
|
Project-URL: Repository, https://github.com/nold-ai/specfact-cli.git
|
|
@@ -368,6 +368,10 @@ specfact init --ide cursor --install-deps
|
|
|
368
368
|
```bash
|
|
369
369
|
# Analyze legacy codebase (most common use case)
|
|
370
370
|
specfact import from-code my-project --repo .
|
|
371
|
+
|
|
372
|
+
# Or validate external codebase without modifying source (sidecar validation)
|
|
373
|
+
specfact validate sidecar init my-project /path/to/repo
|
|
374
|
+
specfact validate sidecar run my-project /path/to/repo
|
|
371
375
|
```
|
|
372
376
|
|
|
373
377
|
**⏱️ Timing:** Analysis typically takes **10-15 minutes** for typical repositories (e.g., `specfact-cli` itself with several hundred features & contracts). Smaller codebases may complete in 2-5 minutes. Large codebases (3000+ features) may take 15-30 minutes, but progress reporting shows real-time status. The analysis performs AST parsing, Semgrep pattern detection, and Specmatic integration.
|
|
@@ -432,8 +436,10 @@ specfact import from-code my-project --repo .
|
|
|
432
436
|
- **Prevent regressions** with runtime contract validation
|
|
433
437
|
- **CI/CD integration** - Block bad code from merging
|
|
434
438
|
- **Works offline** - No cloud required
|
|
439
|
+
- **Sidecar validation** - Validate external codebases without modifying source code
|
|
435
440
|
|
|
436
|
-
👉 **[Command Reference](docs/reference/commands.md)** - All enforcement commands
|
|
441
|
+
👉 **[Command Reference](docs/reference/commands.md)** - All enforcement commands
|
|
442
|
+
👉 **[Sidecar Validation Guide](docs/guides/sidecar-validation.md)** - Validate external codebases
|
|
437
443
|
|
|
438
444
|
### 👥 Team Collaboration
|
|
439
445
|
|
|
@@ -463,6 +469,12 @@ specfact import from-code my-project --repo .
|
|
|
463
469
|
|
|
464
470
|
👉 **[Brownfield Modernization Guide](docs/guides/brownfield-engineer.md)** - Complete walkthrough
|
|
465
471
|
|
|
472
|
+
### 1.5. Validating External Codebases (Sidecar Validation) 🆕
|
|
473
|
+
|
|
474
|
+
**Problem:** Need to validate third-party libraries or legacy codebases without modifying source code
|
|
475
|
+
|
|
476
|
+
👉 **[Sidecar Validation Guide](docs/guides/sidecar-validation.md)** - Validate external codebases with contract testing
|
|
477
|
+
|
|
466
478
|
### 2. Working with a Team
|
|
467
479
|
|
|
468
480
|
**Problem:** Need team collaboration with role-based workflows
|
|
@@ -502,6 +514,7 @@ specfact import from-code my-project --repo .
|
|
|
502
514
|
- **[AI IDE Workflow](docs/guides/ai-ide-workflow.md)** ⭐ **NEW** - AI-assisted development
|
|
503
515
|
- **[Agile/Scrum Workflows](docs/guides/agile-scrum-workflows.md)** ⭐ - Team collaboration
|
|
504
516
|
- **[Integrations Overview](docs/guides/integrations-overview.md)** ⭐ **NEW** - All integrations
|
|
517
|
+
- **[Sidecar Validation](docs/guides/sidecar-validation.md)** 🆕 - Validate external codebases without modifying source
|
|
505
518
|
- **[Use Cases](docs/guides/use-cases.md)** - Common scenarios
|
|
506
519
|
|
|
507
520
|
### Integration Guides
|
|
@@ -93,6 +93,10 @@ specfact init --ide cursor --install-deps
|
|
|
93
93
|
```bash
|
|
94
94
|
# Analyze legacy codebase (most common use case)
|
|
95
95
|
specfact import from-code my-project --repo .
|
|
96
|
+
|
|
97
|
+
# Or validate external codebase without modifying source (sidecar validation)
|
|
98
|
+
specfact validate sidecar init my-project /path/to/repo
|
|
99
|
+
specfact validate sidecar run my-project /path/to/repo
|
|
96
100
|
```
|
|
97
101
|
|
|
98
102
|
**⏱️ Timing:** Analysis typically takes **10-15 minutes** for typical repositories (e.g., `specfact-cli` itself with several hundred features & contracts). Smaller codebases may complete in 2-5 minutes. Large codebases (3000+ features) may take 15-30 minutes, but progress reporting shows real-time status. The analysis performs AST parsing, Semgrep pattern detection, and Specmatic integration.
|
|
@@ -157,8 +161,10 @@ specfact import from-code my-project --repo .
|
|
|
157
161
|
- **Prevent regressions** with runtime contract validation
|
|
158
162
|
- **CI/CD integration** - Block bad code from merging
|
|
159
163
|
- **Works offline** - No cloud required
|
|
164
|
+
- **Sidecar validation** - Validate external codebases without modifying source code
|
|
160
165
|
|
|
161
|
-
👉 **[Command Reference](docs/reference/commands.md)** - All enforcement commands
|
|
166
|
+
👉 **[Command Reference](docs/reference/commands.md)** - All enforcement commands
|
|
167
|
+
👉 **[Sidecar Validation Guide](docs/guides/sidecar-validation.md)** - Validate external codebases
|
|
162
168
|
|
|
163
169
|
### 👥 Team Collaboration
|
|
164
170
|
|
|
@@ -188,6 +194,12 @@ specfact import from-code my-project --repo .
|
|
|
188
194
|
|
|
189
195
|
👉 **[Brownfield Modernization Guide](docs/guides/brownfield-engineer.md)** - Complete walkthrough
|
|
190
196
|
|
|
197
|
+
### 1.5. Validating External Codebases (Sidecar Validation) 🆕
|
|
198
|
+
|
|
199
|
+
**Problem:** Need to validate third-party libraries or legacy codebases without modifying source code
|
|
200
|
+
|
|
201
|
+
👉 **[Sidecar Validation Guide](docs/guides/sidecar-validation.md)** - Validate external codebases with contract testing
|
|
202
|
+
|
|
191
203
|
### 2. Working with a Team
|
|
192
204
|
|
|
193
205
|
**Problem:** Need team collaboration with role-based workflows
|
|
@@ -227,6 +239,7 @@ specfact import from-code my-project --repo .
|
|
|
227
239
|
- **[AI IDE Workflow](docs/guides/ai-ide-workflow.md)** ⭐ **NEW** - AI-assisted development
|
|
228
240
|
- **[Agile/Scrum Workflows](docs/guides/agile-scrum-workflows.md)** ⭐ - Team collaboration
|
|
229
241
|
- **[Integrations Overview](docs/guides/integrations-overview.md)** ⭐ **NEW** - All integrations
|
|
242
|
+
- **[Sidecar Validation](docs/guides/sidecar-validation.md)** 🆕 - Validate external codebases without modifying source
|
|
230
243
|
- **[Use Cases](docs/guides/use-cases.md)** - Common scenarios
|
|
231
244
|
|
|
232
245
|
### Integration Guides
|
|
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "specfact-cli"
|
|
7
|
-
version = "0.
|
|
7
|
+
version = "0.24.0"
|
|
8
8
|
description = "Brownfield-first CLI: Reverse engineer legacy Python → specs → enforced contracts. Automate legacy code documentation and prevent modernization regressions."
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.11"
|
{specfact_cli-0.23.0 → specfact_cli-0.24.0}/resources/templates/sidecar/common/run_sidecar.sh
RENAMED
|
@@ -148,6 +148,54 @@ _filter_crosshair_dirs() {
|
|
|
148
148
|
echo "${filtered[@]}"
|
|
149
149
|
}
|
|
150
150
|
|
|
151
|
+
# Convert source directory paths to Python module names for CrossHair
|
|
152
|
+
# CrossHair expects module names (e.g., "sqlalchemy") not paths (e.g., "lib/sqlalchemy")
|
|
153
|
+
# This function extracts the module name and ensures PYTHONPATH includes the parent directory
|
|
154
|
+
_path_to_module() {
|
|
155
|
+
local source_path="$1"
|
|
156
|
+
local repo_path="${2:-${REPO_PATH}}"
|
|
157
|
+
|
|
158
|
+
# Remove trailing slash
|
|
159
|
+
source_path="${source_path%/}"
|
|
160
|
+
|
|
161
|
+
# If it's an absolute path, make it relative to repo
|
|
162
|
+
if [[ "$source_path" == /* ]]; then
|
|
163
|
+
source_path="${source_path#${repo_path}/}"
|
|
164
|
+
fi
|
|
165
|
+
|
|
166
|
+
# Handle common patterns: lib/pkg, src/pkg, backend/app, pkg
|
|
167
|
+
# Extract the module name (last component that's a valid Python package)
|
|
168
|
+
local module_name=""
|
|
169
|
+
local parent_dir=""
|
|
170
|
+
|
|
171
|
+
if [[ "$source_path" == *"/"* ]]; then
|
|
172
|
+
# Path has directory structure (e.g., lib/sqlalchemy, src/mypackage)
|
|
173
|
+
parent_dir="${source_path%/*}" # Everything before last /
|
|
174
|
+
module_name="${source_path##*/}" # Last component
|
|
175
|
+
|
|
176
|
+
# Check if the module directory has __init__.py (is a package)
|
|
177
|
+
local full_module_path="${repo_path}/${source_path}"
|
|
178
|
+
if [[ -f "${full_module_path}/__init__.py" ]]; then
|
|
179
|
+
# It's a package - return module name and parent dir
|
|
180
|
+
echo "${module_name}|${repo_path}/${parent_dir}"
|
|
181
|
+
return 0
|
|
182
|
+
fi
|
|
183
|
+
|
|
184
|
+
# Check subdirectories for packages (e.g., lib/sqlalchemy where sqlalchemy is the package)
|
|
185
|
+
for subdir in "${full_module_path}"/*; do
|
|
186
|
+
if [[ -d "$subdir" ]] && [[ -f "${subdir}/__init__.py" ]]; then
|
|
187
|
+
# Found a package subdirectory
|
|
188
|
+
echo "$(basename "$subdir")|${full_module_path}"
|
|
189
|
+
return 0
|
|
190
|
+
fi
|
|
191
|
+
done
|
|
192
|
+
fi
|
|
193
|
+
|
|
194
|
+
# No directory structure or no package found - use the path as-is
|
|
195
|
+
# This handles cases like "mypackage" where PYTHONPATH is already set correctly
|
|
196
|
+
echo "${source_path}|"
|
|
197
|
+
}
|
|
198
|
+
|
|
151
199
|
run_with_timeout() {
|
|
152
200
|
local timeout_secs="$1"
|
|
153
201
|
shift
|
|
@@ -427,36 +475,68 @@ if [[ "${RUN_CROSSHAIR}" == "1" ]] && command -v crosshair >/dev/null 2>&1; then
|
|
|
427
475
|
if [[ -z "${CROSSHAIR_FILTERED_DIRS}" ]]; then
|
|
428
476
|
echo "[sidecar] warning: all source directories filtered out (contain tests), skipping source code analysis"
|
|
429
477
|
else
|
|
430
|
-
|
|
431
|
-
#
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
478
|
+
# Convert source paths to module names for CrossHair
|
|
479
|
+
# CrossHair expects module names (e.g., "sqlalchemy") not paths (e.g., "lib/sqlalchemy")
|
|
480
|
+
CROSSHAIR_MODULES=""
|
|
481
|
+
CROSSHAIR_EXTRA_PYTHONPATH=""
|
|
482
|
+
for src_dir in ${CROSSHAIR_FILTERED_DIRS}; do
|
|
483
|
+
MODULE_INFO=$(_path_to_module "$src_dir" "${REPO_PATH}")
|
|
484
|
+
MODULE_NAME="${MODULE_INFO%%|*}"
|
|
485
|
+
MODULE_PARENT="${MODULE_INFO##*|}"
|
|
486
|
+
|
|
487
|
+
if [[ -n "$MODULE_NAME" ]]; then
|
|
488
|
+
CROSSHAIR_MODULES="${CROSSHAIR_MODULES} ${MODULE_NAME}"
|
|
489
|
+
if [[ -n "$MODULE_PARENT" ]] && [[ ":${CROSSHAIR_EXTRA_PYTHONPATH}:" != *":${MODULE_PARENT}:"* ]]; then
|
|
490
|
+
CROSSHAIR_EXTRA_PYTHONPATH="${CROSSHAIR_EXTRA_PYTHONPATH}:${MODULE_PARENT}"
|
|
491
|
+
fi
|
|
439
492
|
fi
|
|
440
|
-
|
|
441
|
-
|
|
493
|
+
done
|
|
494
|
+
CROSSHAIR_MODULES="${CROSSHAIR_MODULES# }" # Trim leading space
|
|
495
|
+
CROSSHAIR_EXTRA_PYTHONPATH="${CROSSHAIR_EXTRA_PYTHONPATH#:}" # Trim leading colon
|
|
496
|
+
|
|
497
|
+
if [[ -z "${CROSSHAIR_MODULES}" ]]; then
|
|
498
|
+
echo "[sidecar] warning: could not convert source directories to modules, skipping source code analysis"
|
|
499
|
+
else
|
|
500
|
+
echo "[sidecar] analyzing modules: ${CROSSHAIR_MODULES}"
|
|
501
|
+
if [[ -n "${CROSSHAIR_EXTRA_PYTHONPATH}" ]]; then
|
|
502
|
+
echo "[sidecar] extra PYTHONPATH: ${CROSSHAIR_EXTRA_PYTHONPATH}"
|
|
442
503
|
fi
|
|
443
|
-
|
|
444
|
-
|
|
504
|
+
|
|
505
|
+
# Build PYTHONPATH for CrossHair (include extra paths for module resolution)
|
|
506
|
+
CROSSHAIR_PYTHONPATH="${PYTHONPATH:-}"
|
|
507
|
+
if [[ -n "${CROSSHAIR_EXTRA_PYTHONPATH}" ]]; then
|
|
508
|
+
CROSSHAIR_PYTHONPATH="${CROSSHAIR_EXTRA_PYTHONPATH}:${CROSSHAIR_PYTHONPATH}"
|
|
509
|
+
fi
|
|
510
|
+
|
|
511
|
+
if [[ "${FRAMEWORK_TYPE}" == "django" ]]; then
|
|
512
|
+
# Use Django-aware wrapper for source code analysis
|
|
513
|
+
CROSSHAIR_WRAPPER="${SIDECAR_DIR}/../frameworks/django/crosshair_django_wrapper.py"
|
|
514
|
+
if [[ -f "${CROSSHAIR_WRAPPER}" ]]; then
|
|
515
|
+
echo "[sidecar] using Django-aware CrossHair wrapper for source analysis"
|
|
516
|
+
# Export environment variables for Django initialization
|
|
517
|
+
CROSSHAIR_ENV=""
|
|
518
|
+
if [[ -n "${DJANGO_SETTINGS_MODULE:-}" ]]; then
|
|
519
|
+
CROSSHAIR_ENV="DJANGO_SETTINGS_MODULE=${DJANGO_SETTINGS_MODULE} "
|
|
520
|
+
fi
|
|
521
|
+
if [[ -n "${REPO_PATH:-}" ]]; then
|
|
522
|
+
CROSSHAIR_ENV="${CROSSHAIR_ENV}REPO_PATH=${REPO_PATH} "
|
|
523
|
+
fi
|
|
524
|
+
CROSSHAIR_ENV="${CROSSHAIR_ENV}PYTHONPATH=${CROSSHAIR_PYTHONPATH} "
|
|
525
|
+
run_and_log "${TIMEOUT_CROSSHAIR}" \
|
|
526
|
+
"${SIDECAR_REPORTS_DIR}/${TIMESTAMP}-crosshair-source.log" \
|
|
527
|
+
env ${CROSSHAIR_ENV}"${PYTHON_CMD}" "${CROSSHAIR_WRAPPER}" check "${CROSSHAIR_ARGS[@]}" ${CROSSHAIR_MODULES}
|
|
528
|
+
else
|
|
529
|
+
echo "[sidecar] warning: Django wrapper not found, using standard CrossHair (may fail)"
|
|
530
|
+
run_and_log "${TIMEOUT_CROSSHAIR}" \
|
|
531
|
+
"${SIDECAR_REPORTS_DIR}/${TIMESTAMP}-crosshair-source.log" \
|
|
532
|
+
env PYTHONPATH="${CROSSHAIR_PYTHONPATH}" "${PYTHON_CMD}" -m crosshair check "${CROSSHAIR_ARGS[@]}" ${CROSSHAIR_MODULES}
|
|
533
|
+
fi
|
|
534
|
+
else
|
|
535
|
+
# Standard CrossHair for non-Django projects
|
|
536
|
+
run_and_log "${TIMEOUT_CROSSHAIR}" \
|
|
537
|
+
"${SIDECAR_REPORTS_DIR}/${TIMESTAMP}-crosshair-source.log" \
|
|
538
|
+
env PYTHONPATH="${CROSSHAIR_PYTHONPATH}" "${PYTHON_CMD}" -m crosshair check "${CROSSHAIR_ARGS[@]}" ${CROSSHAIR_MODULES}
|
|
445
539
|
fi
|
|
446
|
-
run_and_log "${TIMEOUT_CROSSHAIR}" \
|
|
447
|
-
"${SIDECAR_REPORTS_DIR}/${TIMESTAMP}-crosshair-source.log" \
|
|
448
|
-
env ${CROSSHAIR_ENV}"${PYTHON_CMD}" "${CROSSHAIR_WRAPPER}" check "${CROSSHAIR_ARGS[@]}" ${CROSSHAIR_FILTERED_DIRS}
|
|
449
|
-
else
|
|
450
|
-
echo "[sidecar] warning: Django wrapper not found, using standard CrossHair (may fail)"
|
|
451
|
-
run_and_log "${TIMEOUT_CROSSHAIR}" \
|
|
452
|
-
"${SIDECAR_REPORTS_DIR}/${TIMESTAMP}-crosshair-source.log" \
|
|
453
|
-
"${PYTHON_CMD}" -m crosshair check "${CROSSHAIR_ARGS[@]}" ${CROSSHAIR_FILTERED_DIRS}
|
|
454
|
-
fi
|
|
455
|
-
else
|
|
456
|
-
# Standard CrossHair for non-Django projects
|
|
457
|
-
run_and_log "${TIMEOUT_CROSSHAIR}" \
|
|
458
|
-
"${SIDECAR_REPORTS_DIR}/${TIMESTAMP}-crosshair-source.log" \
|
|
459
|
-
"${PYTHON_CMD}" -m crosshair check "${CROSSHAIR_ARGS[@]}" ${CROSSHAIR_FILTERED_DIRS}
|
|
460
540
|
fi
|
|
461
541
|
fi
|
|
462
542
|
fi
|
|
@@ -466,21 +546,30 @@ if [[ "${RUN_CROSSHAIR}" == "1" ]] && command -v crosshair >/dev/null 2>&1; then
|
|
|
466
546
|
# This is the primary analysis method for frameworks without decorators (Django, etc.)
|
|
467
547
|
if [[ -f "${HARNESS_PATH}" ]]; then
|
|
468
548
|
echo "[sidecar] crosshair (harness - external contracts)..."
|
|
549
|
+
|
|
550
|
+
# Build PYTHONPATH for harness analysis:
|
|
551
|
+
# 1. Sidecar directory (for harness imports like 'common.adapters')
|
|
552
|
+
# 2. Original PYTHONPATH (for repo modules)
|
|
553
|
+
HARNESS_DIR="$(cd "$(dirname "${HARNESS_PATH}")" && pwd)"
|
|
554
|
+
HARNESS_FILE="$(basename "${HARNESS_PATH}")"
|
|
555
|
+
HARNESS_MODULE="${HARNESS_FILE%.py}" # Remove .py extension
|
|
556
|
+
|
|
557
|
+
# Build PYTHONPATH: sidecar dir + original PYTHONPATH
|
|
558
|
+
HARNESS_PYTHONPATH="${HARNESS_DIR}"
|
|
559
|
+
if [[ -n "${PYTHONPATH:-}" ]]; then
|
|
560
|
+
HARNESS_PYTHONPATH="${HARNESS_PYTHONPATH}:${PYTHONPATH}"
|
|
561
|
+
fi
|
|
562
|
+
|
|
469
563
|
# Export environment variables for CrossHair subprocess
|
|
470
|
-
CROSSHAIR_ENV=""
|
|
564
|
+
CROSSHAIR_ENV="PYTHONPATH=${HARNESS_PYTHONPATH} "
|
|
471
565
|
if [[ -n "${DJANGO_SETTINGS_MODULE:-}" ]]; then
|
|
472
|
-
CROSSHAIR_ENV="DJANGO_SETTINGS_MODULE=${DJANGO_SETTINGS_MODULE} "
|
|
566
|
+
CROSSHAIR_ENV="${CROSSHAIR_ENV}DJANGO_SETTINGS_MODULE=${DJANGO_SETTINGS_MODULE} "
|
|
473
567
|
fi
|
|
474
568
|
if [[ -n "${REPO_PATH:-}" ]]; then
|
|
475
569
|
CROSSHAIR_ENV="${CROSSHAIR_ENV}REPO_PATH=${REPO_PATH} "
|
|
476
570
|
fi
|
|
477
|
-
|
|
478
|
-
CROSSHAIR_ENV="${CROSSHAIR_ENV}PYTHONPATH=${PYTHONPATH} "
|
|
479
|
-
fi
|
|
571
|
+
|
|
480
572
|
# Change to harness directory to ensure valid module name (avoids hyphenated directory names in module path)
|
|
481
|
-
HARNESS_DIR="$(dirname "${HARNESS_PATH}")"
|
|
482
|
-
HARNESS_FILE="$(basename "${HARNESS_PATH}")"
|
|
483
|
-
HARNESS_MODULE="${HARNESS_FILE%.py}" # Remove .py extension
|
|
484
573
|
run_and_log "${TIMEOUT_CROSSHAIR}" \
|
|
485
574
|
"${SIDECAR_REPORTS_DIR}/${TIMESTAMP}-crosshair-harness.log" \
|
|
486
575
|
bash -c "cd '${HARNESS_DIR}' && env ${CROSSHAIR_ENV}${PYTHON_CMD} -m crosshair check ${CROSSHAIR_ARGS[*]} ${HARNESS_MODULE}"
|
{specfact_cli-0.23.0 → specfact_cli-0.24.0}/src/specfact_cli/analyzers/contract_extractor.py
RENAMED
|
@@ -260,8 +260,15 @@ class ContractExtractor:
|
|
|
260
260
|
return repr(node.value)
|
|
261
261
|
if isinstance(node, ast.Name):
|
|
262
262
|
return node.id
|
|
263
|
-
|
|
264
|
-
|
|
263
|
+
# Python < 3.8 compatibility - suppress deprecation warning
|
|
264
|
+
import warnings
|
|
265
|
+
|
|
266
|
+
with warnings.catch_warnings():
|
|
267
|
+
warnings.simplefilter("ignore", DeprecationWarning)
|
|
268
|
+
# ast.NameConstant is deprecated in Python 3.8+, removed in 3.14
|
|
269
|
+
# Keep for backward compatibility with older Python versions
|
|
270
|
+
if hasattr(ast, "NameConstant") and isinstance(node, ast.NameConstant):
|
|
271
|
+
return str(node.value)
|
|
265
272
|
|
|
266
273
|
# Use ast.unparse if available
|
|
267
274
|
if hasattr(ast, "unparse"):
|
|
@@ -68,6 +68,7 @@ from specfact_cli.commands import (
|
|
|
68
68
|
sdd,
|
|
69
69
|
spec,
|
|
70
70
|
sync,
|
|
71
|
+
validate,
|
|
71
72
|
)
|
|
72
73
|
from specfact_cli.modes import OperationalMode, detect_mode
|
|
73
74
|
from specfact_cli.runtime import get_configured_console
|
|
@@ -350,6 +351,7 @@ app.add_typer(drift.app, name="drift", help="Detect drift between code and speci
|
|
|
350
351
|
|
|
351
352
|
# 11.6. Analysis
|
|
352
353
|
app.add_typer(analyze.app, name="analyze", help="Analyze codebase for contract coverage and quality")
|
|
354
|
+
app.add_typer(validate.app, name="validate", help="Validation commands including sidecar validation")
|
|
353
355
|
|
|
354
356
|
|
|
355
357
|
def cli_main() -> None:
|