specfact-cli 0.46.0__tar.gz → 0.46.4__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.46.0 → specfact_cli-0.46.4}/PKG-INFO +23 -11
- {specfact_cli-0.46.0 → specfact_cli-0.46.4}/README.md +16 -4
- {specfact_cli-0.46.0 → specfact_cli-0.46.4}/pyproject.toml +43 -28
- specfact_cli-0.46.4/resources/bundled-module-registry/index.json +20 -0
- {specfact_cli-0.46.0 → specfact_cli-0.46.4}/src/__init__.py +1 -1
- {specfact_cli-0.46.0 → specfact_cli-0.46.4}/src/specfact_cli/__init__.py +1 -1
- {specfact_cli-0.46.0 → specfact_cli-0.46.4}/src/specfact_cli/adapters/ado.py +9 -9
- {specfact_cli-0.46.0 → specfact_cli-0.46.4}/src/specfact_cli/adapters/github.py +22 -3
- {specfact_cli-0.46.0 → specfact_cli-0.46.4}/src/specfact_cli/adapters/speckit.py +14 -11
- {specfact_cli-0.46.0 → specfact_cli-0.46.4}/src/specfact_cli/analyzers/code_analyzer.py +47 -22
- {specfact_cli-0.46.0 → specfact_cli-0.46.4}/src/specfact_cli/analyzers/graph_analyzer.py +56 -65
- {specfact_cli-0.46.0 → specfact_cli-0.46.4}/src/specfact_cli/common/logger_setup.py +2 -1
- {specfact_cli-0.46.0 → specfact_cli-0.46.4}/src/specfact_cli/generators/persona_exporter.py +2 -2
- {specfact_cli-0.46.0 → specfact_cli-0.46.4}/src/specfact_cli/generators/test_to_openapi.py +13 -7
- {specfact_cli-0.46.0 → specfact_cli-0.46.4}/src/specfact_cli/modules/init/module-package.yaml +3 -3
- {specfact_cli-0.46.0 → specfact_cli-0.46.4}/src/specfact_cli/modules/init/src/first_run_selection.py +1 -1
- {specfact_cli-0.46.0 → specfact_cli-0.46.4}/src/specfact_cli/modules/module_registry/module-package.yaml +3 -3
- {specfact_cli-0.46.0 → specfact_cli-0.46.4}/src/specfact_cli/modules/module_registry/src/commands.py +1 -1
- {specfact_cli-0.46.0 → specfact_cli-0.46.4}/src/specfact_cli/registry/module_installer.py +49 -5
- {specfact_cli-0.46.0 → specfact_cli-0.46.4}/src/specfact_cli/sync/bridge_watch.py +6 -2
- {specfact_cli-0.46.0 → specfact_cli-0.46.4}/src/specfact_cli/utils/optional_deps.py +12 -15
- {specfact_cli-0.46.0 → specfact_cli-0.46.4}/src/specfact_cli/utils/project_artifact_write.py +7 -6
- {specfact_cli-0.46.0 → specfact_cli-0.46.4}/src/specfact_cli/utils/source_scanner.py +0 -4
- {specfact_cli-0.46.0 → specfact_cli-0.46.4}/src/specfact_cli/validators/cli_first_validator.py +20 -12
- {specfact_cli-0.46.0 → specfact_cli-0.46.4}/src/specfact_cli/validators/sidecar/crosshair_runner.py +5 -1
- {specfact_cli-0.46.0 → specfact_cli-0.46.4}/src/specfact_cli/validators/sidecar/crosshair_summary.py +2 -2
- {specfact_cli-0.46.0 → specfact_cli-0.46.4}/src/specfact_cli/validators/sidecar/frameworks/fastapi.py +11 -6
- {specfact_cli-0.46.0 → specfact_cli-0.46.4}/.gitignore +0 -0
- {specfact_cli-0.46.0 → specfact_cli-0.46.4}/LICENSE +0 -0
- {specfact_cli-0.46.0 → specfact_cli-0.46.4}/resources/keys/README.md +0 -0
- {specfact_cli-0.46.0 → specfact_cli-0.46.4}/resources/keys/module-signing-public.pem +0 -0
- {specfact_cli-0.46.0 → specfact_cli-0.46.4}/resources/mappings/node-async.yaml +0 -0
- {specfact_cli-0.46.0 → specfact_cli-0.46.4}/resources/mappings/python-async.yaml +0 -0
- {specfact_cli-0.46.0 → specfact_cli-0.46.4}/resources/mappings/speckit-default.yaml +0 -0
- {specfact_cli-0.46.0 → specfact_cli-0.46.4}/resources/schemas/deviation.schema.json +0 -0
- {specfact_cli-0.46.0 → specfact_cli-0.46.4}/resources/schemas/plan.schema.json +0 -0
- {specfact_cli-0.46.0 → specfact_cli-0.46.4}/resources/schemas/protocol.schema.json +0 -0
- {specfact_cli-0.46.0 → specfact_cli-0.46.4}/resources/templates/github-action.yml.j2 +0 -0
- {specfact_cli-0.46.0 → specfact_cli-0.46.4}/resources/templates/persona/architect.md.j2 +0 -0
- {specfact_cli-0.46.0 → specfact_cli-0.46.4}/resources/templates/persona/developer.md.j2 +0 -0
- {specfact_cli-0.46.0 → specfact_cli-0.46.4}/resources/templates/persona/product-owner.md.j2 +0 -0
- {specfact_cli-0.46.0 → specfact_cli-0.46.4}/resources/templates/plan.bundle.yaml.j2 +0 -0
- {specfact_cli-0.46.0 → specfact_cli-0.46.4}/resources/templates/policies/kanban.yaml +0 -0
- {specfact_cli-0.46.0 → specfact_cli-0.46.4}/resources/templates/policies/mixed.yaml +0 -0
- {specfact_cli-0.46.0 → specfact_cli-0.46.4}/resources/templates/policies/safe.yaml +0 -0
- {specfact_cli-0.46.0 → specfact_cli-0.46.4}/resources/templates/policies/scrum.yaml +0 -0
- {specfact_cli-0.46.0 → specfact_cli-0.46.4}/resources/templates/pr-template.md.j2 +0 -0
- {specfact_cli-0.46.0 → specfact_cli-0.46.4}/resources/templates/protocol.yaml.j2 +0 -0
- {specfact_cli-0.46.0 → specfact_cli-0.46.4}/resources/templates/telemetry.yaml.example +0 -0
- {specfact_cli-0.46.0 → specfact_cli-0.46.4}/src/specfact_cli/__main__.py +0 -0
- {specfact_cli-0.46.0 → specfact_cli-0.46.4}/src/specfact_cli/adapters/__init__.py +0 -0
- {specfact_cli-0.46.0 → specfact_cli-0.46.4}/src/specfact_cli/adapters/backlog_base.py +0 -0
- {specfact_cli-0.46.0 → specfact_cli-0.46.4}/src/specfact_cli/adapters/base.py +0 -0
- {specfact_cli-0.46.0 → specfact_cli-0.46.4}/src/specfact_cli/adapters/openspec.py +0 -0
- {specfact_cli-0.46.0 → specfact_cli-0.46.4}/src/specfact_cli/adapters/openspec_parser.py +0 -0
- {specfact_cli-0.46.0 → specfact_cli-0.46.4}/src/specfact_cli/adapters/registry.py +0 -0
- {specfact_cli-0.46.0 → specfact_cli-0.46.4}/src/specfact_cli/agents/__init__.py +0 -0
- {specfact_cli-0.46.0 → specfact_cli-0.46.4}/src/specfact_cli/agents/analyze_agent.py +0 -0
- {specfact_cli-0.46.0 → specfact_cli-0.46.4}/src/specfact_cli/agents/base.py +0 -0
- {specfact_cli-0.46.0 → specfact_cli-0.46.4}/src/specfact_cli/agents/plan_agent.py +0 -0
- {specfact_cli-0.46.0 → specfact_cli-0.46.4}/src/specfact_cli/agents/registry.py +0 -0
- {specfact_cli-0.46.0 → specfact_cli-0.46.4}/src/specfact_cli/agents/sync_agent.py +0 -0
- {specfact_cli-0.46.0 → specfact_cli-0.46.4}/src/specfact_cli/analyzers/__init__.py +0 -0
- {specfact_cli-0.46.0 → specfact_cli-0.46.4}/src/specfact_cli/analyzers/ambiguity_scanner.py +0 -0
- {specfact_cli-0.46.0 → specfact_cli-0.46.4}/src/specfact_cli/analyzers/constitution_evidence_extractor.py +0 -0
- {specfact_cli-0.46.0 → specfact_cli-0.46.4}/src/specfact_cli/analyzers/contract_extractor.py +0 -0
- {specfact_cli-0.46.0 → specfact_cli-0.46.4}/src/specfact_cli/analyzers/control_flow_analyzer.py +0 -0
- {specfact_cli-0.46.0 → specfact_cli-0.46.4}/src/specfact_cli/analyzers/relationship_mapper.py +0 -0
- {specfact_cli-0.46.0 → specfact_cli-0.46.4}/src/specfact_cli/analyzers/requirement_extractor.py +0 -0
- {specfact_cli-0.46.0 → specfact_cli-0.46.4}/src/specfact_cli/analyzers/test_pattern_extractor.py +0 -0
- {specfact_cli-0.46.0 → specfact_cli-0.46.4}/src/specfact_cli/backlog/__init__.py +0 -0
- {specfact_cli-0.46.0 → specfact_cli-0.46.4}/src/specfact_cli/backlog/adapters/__init__.py +0 -0
- {specfact_cli-0.46.0 → specfact_cli-0.46.4}/src/specfact_cli/backlog/adapters/base.py +0 -0
- {specfact_cli-0.46.0 → specfact_cli-0.46.4}/src/specfact_cli/backlog/converter.py +0 -0
- {specfact_cli-0.46.0 → specfact_cli-0.46.4}/src/specfact_cli/backlog/filters.py +0 -0
- {specfact_cli-0.46.0 → specfact_cli-0.46.4}/src/specfact_cli/backlog/mappers/__init__.py +0 -0
- {specfact_cli-0.46.0 → specfact_cli-0.46.4}/src/specfact_cli/backlog/mappers/ado_mapper.py +0 -0
- {specfact_cli-0.46.0 → specfact_cli-0.46.4}/src/specfact_cli/backlog/mappers/base.py +0 -0
- {specfact_cli-0.46.0 → specfact_cli-0.46.4}/src/specfact_cli/backlog/mappers/github_mapper.py +0 -0
- {specfact_cli-0.46.0 → specfact_cli-0.46.4}/src/specfact_cli/backlog/mappers/template_config.py +0 -0
- {specfact_cli-0.46.0 → specfact_cli-0.46.4}/src/specfact_cli/cli.py +0 -0
- {specfact_cli-0.46.0 → specfact_cli-0.46.4}/src/specfact_cli/commands/__init__.py +0 -0
- {specfact_cli-0.46.0 → specfact_cli-0.46.4}/src/specfact_cli/commands/_bundle_shim.py +0 -0
- {specfact_cli-0.46.0 → specfact_cli-0.46.4}/src/specfact_cli/commands/analyze.py +0 -0
- {specfact_cli-0.46.0 → specfact_cli-0.46.4}/src/specfact_cli/commands/contract_cmd.py +0 -0
- {specfact_cli-0.46.0 → specfact_cli-0.46.4}/src/specfact_cli/commands/drift.py +0 -0
- {specfact_cli-0.46.0 → specfact_cli-0.46.4}/src/specfact_cli/commands/enforce.py +0 -0
- {specfact_cli-0.46.0 → specfact_cli-0.46.4}/src/specfact_cli/commands/generate.py +0 -0
- {specfact_cli-0.46.0 → specfact_cli-0.46.4}/src/specfact_cli/commands/import_cmd.py +0 -0
- {specfact_cli-0.46.0 → specfact_cli-0.46.4}/src/specfact_cli/commands/init.py +0 -0
- {specfact_cli-0.46.0 → specfact_cli-0.46.4}/src/specfact_cli/commands/migrate.py +0 -0
- {specfact_cli-0.46.0 → specfact_cli-0.46.4}/src/specfact_cli/commands/plan.py +0 -0
- {specfact_cli-0.46.0 → specfact_cli-0.46.4}/src/specfact_cli/commands/project_cmd.py +0 -0
- {specfact_cli-0.46.0 → specfact_cli-0.46.4}/src/specfact_cli/commands/repro.py +0 -0
- {specfact_cli-0.46.0 → specfact_cli-0.46.4}/src/specfact_cli/commands/sdd.py +0 -0
- {specfact_cli-0.46.0 → specfact_cli-0.46.4}/src/specfact_cli/commands/spec.py +0 -0
- {specfact_cli-0.46.0 → specfact_cli-0.46.4}/src/specfact_cli/commands/sync.py +0 -0
- {specfact_cli-0.46.0 → specfact_cli-0.46.4}/src/specfact_cli/commands/update.py +0 -0
- {specfact_cli-0.46.0 → specfact_cli-0.46.4}/src/specfact_cli/commands/validate.py +0 -0
- {specfact_cli-0.46.0 → specfact_cli-0.46.4}/src/specfact_cli/common/__init__.py +0 -0
- {specfact_cli-0.46.0 → specfact_cli-0.46.4}/src/specfact_cli/common/bundle_factory.py +0 -0
- {specfact_cli-0.46.0 → specfact_cli-0.46.4}/src/specfact_cli/common/logging_utils.py +0 -0
- {specfact_cli-0.46.0 → specfact_cli-0.46.4}/src/specfact_cli/common/text_utils.py +0 -0
- {specfact_cli-0.46.0 → specfact_cli-0.46.4}/src/specfact_cli/common/utils.py +0 -0
- {specfact_cli-0.46.0 → specfact_cli-0.46.4}/src/specfact_cli/comparators/__init__.py +0 -0
- {specfact_cli-0.46.0 → specfact_cli-0.46.4}/src/specfact_cli/comparators/plan_comparator.py +0 -0
- {specfact_cli-0.46.0 → specfact_cli-0.46.4}/src/specfact_cli/contracts/__init__.py +0 -0
- {specfact_cli-0.46.0 → specfact_cli-0.46.4}/src/specfact_cli/contracts/crosshair_props.py +0 -0
- {specfact_cli-0.46.0 → specfact_cli-0.46.4}/src/specfact_cli/contracts/module_interface.py +0 -0
- {specfact_cli-0.46.0 → specfact_cli-0.46.4}/src/specfact_cli/enrichers/constitution_enricher.py +0 -0
- {specfact_cli-0.46.0 → specfact_cli-0.46.4}/src/specfact_cli/enrichers/plan_enricher.py +0 -0
- {specfact_cli-0.46.0 → specfact_cli-0.46.4}/src/specfact_cli/generators/__init__.py +0 -0
- {specfact_cli-0.46.0 → specfact_cli-0.46.4}/src/specfact_cli/generators/contract_generator.py +0 -0
- {specfact_cli-0.46.0 → specfact_cli-0.46.4}/src/specfact_cli/generators/openapi_extractor.py +0 -0
- {specfact_cli-0.46.0 → specfact_cli-0.46.4}/src/specfact_cli/generators/plan_generator.py +0 -0
- {specfact_cli-0.46.0 → specfact_cli-0.46.4}/src/specfact_cli/generators/protocol_generator.py +0 -0
- {specfact_cli-0.46.0 → specfact_cli-0.46.4}/src/specfact_cli/generators/report_generator.py +0 -0
- {specfact_cli-0.46.0 → specfact_cli-0.46.4}/src/specfact_cli/generators/task_generator.py +0 -0
- {specfact_cli-0.46.0 → specfact_cli-0.46.4}/src/specfact_cli/generators/workflow_generator.py +0 -0
- {specfact_cli-0.46.0 → specfact_cli-0.46.4}/src/specfact_cli/groups/__init__.py +0 -0
- {specfact_cli-0.46.0 → specfact_cli-0.46.4}/src/specfact_cli/groups/codebase_group.py +0 -0
- {specfact_cli-0.46.0 → specfact_cli-0.46.4}/src/specfact_cli/groups/govern_group.py +0 -0
- {specfact_cli-0.46.0 → specfact_cli-0.46.4}/src/specfact_cli/groups/member_group.py +0 -0
- {specfact_cli-0.46.0 → specfact_cli-0.46.4}/src/specfact_cli/groups/project_group.py +0 -0
- {specfact_cli-0.46.0 → specfact_cli-0.46.4}/src/specfact_cli/groups/spec_group.py +0 -0
- {specfact_cli-0.46.0 → specfact_cli-0.46.4}/src/specfact_cli/importers/__init__.py +0 -0
- {specfact_cli-0.46.0 → specfact_cli-0.46.4}/src/specfact_cli/importers/speckit_converter.py +0 -0
- {specfact_cli-0.46.0 → specfact_cli-0.46.4}/src/specfact_cli/importers/speckit_scanner.py +0 -0
- {specfact_cli-0.46.0 → specfact_cli-0.46.4}/src/specfact_cli/integrations/__init__.py +0 -0
- {specfact_cli-0.46.0 → specfact_cli-0.46.4}/src/specfact_cli/integrations/specmatic.py +0 -0
- {specfact_cli-0.46.0 → specfact_cli-0.46.4}/src/specfact_cli/merge/__init__.py +0 -0
- {specfact_cli-0.46.0 → specfact_cli-0.46.4}/src/specfact_cli/merge/resolver.py +0 -0
- {specfact_cli-0.46.0 → specfact_cli-0.46.4}/src/specfact_cli/migrations/__init__.py +0 -0
- {specfact_cli-0.46.0 → specfact_cli-0.46.4}/src/specfact_cli/migrations/plan_migrator.py +0 -0
- {specfact_cli-0.46.0 → specfact_cli-0.46.4}/src/specfact_cli/models/__init__.py +0 -0
- {specfact_cli-0.46.0 → specfact_cli-0.46.4}/src/specfact_cli/models/backlog_item.py +0 -0
- {specfact_cli-0.46.0 → specfact_cli-0.46.4}/src/specfact_cli/models/bridge.py +0 -0
- {specfact_cli-0.46.0 → specfact_cli-0.46.4}/src/specfact_cli/models/capabilities.py +0 -0
- {specfact_cli-0.46.0 → specfact_cli-0.46.4}/src/specfact_cli/models/change.py +0 -0
- {specfact_cli-0.46.0 → specfact_cli-0.46.4}/src/specfact_cli/models/contract.py +0 -0
- {specfact_cli-0.46.0 → specfact_cli-0.46.4}/src/specfact_cli/models/deviation.py +0 -0
- {specfact_cli-0.46.0 → specfact_cli-0.46.4}/src/specfact_cli/models/dor_config.py +0 -0
- {specfact_cli-0.46.0 → specfact_cli-0.46.4}/src/specfact_cli/models/enforcement.py +0 -0
- {specfact_cli-0.46.0 → specfact_cli-0.46.4}/src/specfact_cli/models/module_package.py +0 -0
- {specfact_cli-0.46.0 → specfact_cli-0.46.4}/src/specfact_cli/models/persona_template.py +0 -0
- {specfact_cli-0.46.0 → specfact_cli-0.46.4}/src/specfact_cli/models/plan.py +0 -0
- {specfact_cli-0.46.0 → specfact_cli-0.46.4}/src/specfact_cli/models/project.py +0 -0
- {specfact_cli-0.46.0 → specfact_cli-0.46.4}/src/specfact_cli/models/protocol.py +0 -0
- {specfact_cli-0.46.0 → specfact_cli-0.46.4}/src/specfact_cli/models/quality.py +0 -0
- {specfact_cli-0.46.0 → specfact_cli-0.46.4}/src/specfact_cli/models/sdd.py +0 -0
- {specfact_cli-0.46.0 → specfact_cli-0.46.4}/src/specfact_cli/models/source_tracking.py +0 -0
- {specfact_cli-0.46.0 → specfact_cli-0.46.4}/src/specfact_cli/models/task.py +0 -0
- {specfact_cli-0.46.0 → specfact_cli-0.46.4}/src/specfact_cli/models/validation.py +0 -0
- {specfact_cli-0.46.0 → specfact_cli-0.46.4}/src/specfact_cli/modes/__init__.py +0 -0
- {specfact_cli-0.46.0 → specfact_cli-0.46.4}/src/specfact_cli/modes/detector.py +0 -0
- {specfact_cli-0.46.0 → specfact_cli-0.46.4}/src/specfact_cli/modes/router.py +0 -0
- {specfact_cli-0.46.0 → specfact_cli-0.46.4}/src/specfact_cli/modules/__init__.py +0 -0
- {specfact_cli-0.46.0 → specfact_cli-0.46.4}/src/specfact_cli/modules/_bundle_import.py +0 -0
- {specfact_cli-0.46.0 → specfact_cli-0.46.4}/src/specfact_cli/modules/init/src/__init__.py +0 -0
- {specfact_cli-0.46.0 → specfact_cli-0.46.4}/src/specfact_cli/modules/init/src/app.py +0 -0
- {specfact_cli-0.46.0 → specfact_cli-0.46.4}/src/specfact_cli/modules/init/src/commands.py +0 -0
- {specfact_cli-0.46.0 → specfact_cli-0.46.4}/src/specfact_cli/modules/module_io_shim.py +0 -0
- {specfact_cli-0.46.0 → specfact_cli-0.46.4}/src/specfact_cli/modules/module_registry/src/__init__.py +0 -0
- {specfact_cli-0.46.0 → specfact_cli-0.46.4}/src/specfact_cli/modules/module_registry/src/app.py +0 -0
- {specfact_cli-0.46.0 → specfact_cli-0.46.4}/src/specfact_cli/modules/upgrade/module-package.yaml +0 -0
- {specfact_cli-0.46.0 → specfact_cli-0.46.4}/src/specfact_cli/modules/upgrade/src/__init__.py +0 -0
- {specfact_cli-0.46.0 → specfact_cli-0.46.4}/src/specfact_cli/modules/upgrade/src/app.py +0 -0
- {specfact_cli-0.46.0 → specfact_cli-0.46.4}/src/specfact_cli/modules/upgrade/src/commands.py +0 -0
- {specfact_cli-0.46.0 → specfact_cli-0.46.4}/src/specfact_cli/parsers/__init__.py +0 -0
- {specfact_cli-0.46.0 → specfact_cli-0.46.4}/src/specfact_cli/parsers/persona_importer.py +0 -0
- {specfact_cli-0.46.0 → specfact_cli-0.46.4}/src/specfact_cli/registry/__init__.py +0 -0
- {specfact_cli-0.46.0 → specfact_cli-0.46.4}/src/specfact_cli/registry/alias_manager.py +0 -0
- {specfact_cli-0.46.0 → specfact_cli-0.46.4}/src/specfact_cli/registry/bootstrap.py +0 -0
- {specfact_cli-0.46.0 → specfact_cli-0.46.4}/src/specfact_cli/registry/bridge_registry.py +0 -0
- {specfact_cli-0.46.0 → specfact_cli-0.46.4}/src/specfact_cli/registry/crypto_validator.py +0 -0
- {specfact_cli-0.46.0 → specfact_cli-0.46.4}/src/specfact_cli/registry/custom_registries.py +0 -0
- {specfact_cli-0.46.0 → specfact_cli-0.46.4}/src/specfact_cli/registry/dependency_resolver.py +0 -0
- {specfact_cli-0.46.0 → specfact_cli-0.46.4}/src/specfact_cli/registry/extension_registry.py +0 -0
- {specfact_cli-0.46.0 → specfact_cli-0.46.4}/src/specfact_cli/registry/help_cache.py +0 -0
- {specfact_cli-0.46.0 → specfact_cli-0.46.4}/src/specfact_cli/registry/marketplace_client.py +0 -0
- {specfact_cli-0.46.0 → specfact_cli-0.46.4}/src/specfact_cli/registry/metadata.py +0 -0
- {specfact_cli-0.46.0 → specfact_cli-0.46.4}/src/specfact_cli/registry/module_discovery.py +0 -0
- {specfact_cli-0.46.0 → specfact_cli-0.46.4}/src/specfact_cli/registry/module_grouping.py +0 -0
- {specfact_cli-0.46.0 → specfact_cli-0.46.4}/src/specfact_cli/registry/module_lifecycle.py +0 -0
- {specfact_cli-0.46.0 → specfact_cli-0.46.4}/src/specfact_cli/registry/module_packages.py +0 -0
- {specfact_cli-0.46.0 → specfact_cli-0.46.4}/src/specfact_cli/registry/module_security.py +0 -0
- {specfact_cli-0.46.0 → specfact_cli-0.46.4}/src/specfact_cli/registry/module_state.py +0 -0
- {specfact_cli-0.46.0 → specfact_cli-0.46.4}/src/specfact_cli/registry/registry.py +0 -0
- {specfact_cli-0.46.0 → specfact_cli-0.46.4}/src/specfact_cli/resources/semgrep/async.yml +0 -0
- {specfact_cli-0.46.0 → specfact_cli-0.46.4}/src/specfact_cli/resources/semgrep/code-quality.yml +0 -0
- {specfact_cli-0.46.0 → specfact_cli-0.46.4}/src/specfact_cli/resources/semgrep/feature-detection.yml +0 -0
- {specfact_cli-0.46.0 → specfact_cli-0.46.4}/src/specfact_cli/runtime.py +0 -0
- {specfact_cli-0.46.0 → specfact_cli-0.46.4}/src/specfact_cli/sync/__init__.py +0 -0
- {specfact_cli-0.46.0 → specfact_cli-0.46.4}/src/specfact_cli/sync/bridge_probe.py +0 -0
- {specfact_cli-0.46.0 → specfact_cli-0.46.4}/src/specfact_cli/sync/bridge_sync.py +0 -0
- {specfact_cli-0.46.0 → specfact_cli-0.46.4}/src/specfact_cli/sync/bridge_sync_openspec_md_parse.py +0 -0
- {specfact_cli-0.46.0 → specfact_cli-0.46.4}/src/specfact_cli/sync/bridge_sync_requirement_from_proposal.py +0 -0
- {specfact_cli-0.46.0 → specfact_cli-0.46.4}/src/specfact_cli/sync/bridge_sync_requirement_helpers.py +0 -0
- {specfact_cli-0.46.0 → specfact_cli-0.46.4}/src/specfact_cli/sync/bridge_sync_tasks_from_proposal.py +0 -0
- {specfact_cli-0.46.0 → specfact_cli-0.46.4}/src/specfact_cli/sync/bridge_sync_what_changes_format.py +0 -0
- {specfact_cli-0.46.0 → specfact_cli-0.46.4}/src/specfact_cli/sync/bridge_sync_write_openspec_from_proposal.py +0 -0
- {specfact_cli-0.46.0 → specfact_cli-0.46.4}/src/specfact_cli/sync/change_detector.py +0 -0
- {specfact_cli-0.46.0 → specfact_cli-0.46.4}/src/specfact_cli/sync/code_to_spec.py +0 -0
- {specfact_cli-0.46.0 → specfact_cli-0.46.4}/src/specfact_cli/sync/drift_detector.py +0 -0
- {specfact_cli-0.46.0 → specfact_cli-0.46.4}/src/specfact_cli/sync/repository_sync.py +0 -0
- {specfact_cli-0.46.0 → specfact_cli-0.46.4}/src/specfact_cli/sync/spec_to_code.py +0 -0
- {specfact_cli-0.46.0 → specfact_cli-0.46.4}/src/specfact_cli/sync/spec_to_tests.py +0 -0
- {specfact_cli-0.46.0 → specfact_cli-0.46.4}/src/specfact_cli/sync/watcher.py +0 -0
- {specfact_cli-0.46.0 → specfact_cli-0.46.4}/src/specfact_cli/sync/watcher_enhanced.py +0 -0
- {specfact_cli-0.46.0 → specfact_cli-0.46.4}/src/specfact_cli/telemetry.py +0 -0
- {specfact_cli-0.46.0 → specfact_cli-0.46.4}/src/specfact_cli/templates/__init__.py +0 -0
- {specfact_cli-0.46.0 → specfact_cli-0.46.4}/src/specfact_cli/templates/defaults/defect_v1.yaml +0 -0
- {specfact_cli-0.46.0 → specfact_cli-0.46.4}/src/specfact_cli/templates/defaults/enabler_v1.yaml +0 -0
- {specfact_cli-0.46.0 → specfact_cli-0.46.4}/src/specfact_cli/templates/defaults/spike_v1.yaml +0 -0
- {specfact_cli-0.46.0 → specfact_cli-0.46.4}/src/specfact_cli/templates/defaults/user_story_v1.yaml +0 -0
- {specfact_cli-0.46.0 → specfact_cli-0.46.4}/src/specfact_cli/templates/frameworks/scrum/user_story_v1.yaml +0 -0
- {specfact_cli-0.46.0 → specfact_cli-0.46.4}/src/specfact_cli/templates/personas/product-owner/user_story_v1.yaml +0 -0
- {specfact_cli-0.46.0 → specfact_cli-0.46.4}/src/specfact_cli/templates/providers/ado/work_item_v1.yaml +0 -0
- {specfact_cli-0.46.0 → specfact_cli-0.46.4}/src/specfact_cli/templates/registry.py +0 -0
- {specfact_cli-0.46.0 → specfact_cli-0.46.4}/src/specfact_cli/templates/specification_templates.py +0 -0
- {specfact_cli-0.46.0 → specfact_cli-0.46.4}/src/specfact_cli/utils/__init__.py +0 -0
- {specfact_cli-0.46.0 → specfact_cli-0.46.4}/src/specfact_cli/utils/acceptance_criteria.py +0 -0
- {specfact_cli-0.46.0 → specfact_cli-0.46.4}/src/specfact_cli/utils/auth_tokens.py +0 -0
- {specfact_cli-0.46.0 → specfact_cli-0.46.4}/src/specfact_cli/utils/bundle_converters.py +0 -0
- {specfact_cli-0.46.0 → specfact_cli-0.46.4}/src/specfact_cli/utils/bundle_loader.py +0 -0
- {specfact_cli-0.46.0 → specfact_cli-0.46.4}/src/specfact_cli/utils/code_change_detector.py +0 -0
- {specfact_cli-0.46.0 → specfact_cli-0.46.4}/src/specfact_cli/utils/console.py +0 -0
- {specfact_cli-0.46.0 → specfact_cli-0.46.4}/src/specfact_cli/utils/content_sanitizer.py +0 -0
- {specfact_cli-0.46.0 → specfact_cli-0.46.4}/src/specfact_cli/utils/context_detection.py +0 -0
- {specfact_cli-0.46.0 → specfact_cli-0.46.4}/src/specfact_cli/utils/contract_predicates.py +0 -0
- {specfact_cli-0.46.0 → specfact_cli-0.46.4}/src/specfact_cli/utils/enrichment_context.py +0 -0
- {specfact_cli-0.46.0 → specfact_cli-0.46.4}/src/specfact_cli/utils/enrichment_parser.py +0 -0
- {specfact_cli-0.46.0 → specfact_cli-0.46.4}/src/specfact_cli/utils/env_manager.py +0 -0
- {specfact_cli-0.46.0 → specfact_cli-0.46.4}/src/specfact_cli/utils/feature_keys.py +0 -0
- {specfact_cli-0.46.0 → specfact_cli-0.46.4}/src/specfact_cli/utils/git.py +0 -0
- {specfact_cli-0.46.0 → specfact_cli-0.46.4}/src/specfact_cli/utils/github_annotations.py +0 -0
- {specfact_cli-0.46.0 → specfact_cli-0.46.4}/src/specfact_cli/utils/icontract_helpers.py +0 -0
- {specfact_cli-0.46.0 → specfact_cli-0.46.4}/src/specfact_cli/utils/ide_setup.py +0 -0
- {specfact_cli-0.46.0 → specfact_cli-0.46.4}/src/specfact_cli/utils/incremental_check.py +0 -0
- {specfact_cli-0.46.0 → specfact_cli-0.46.4}/src/specfact_cli/utils/metadata.py +0 -0
- {specfact_cli-0.46.0 → specfact_cli-0.46.4}/src/specfact_cli/utils/performance.py +0 -0
- {specfact_cli-0.46.0 → specfact_cli-0.46.4}/src/specfact_cli/utils/persona_ownership.py +0 -0
- {specfact_cli-0.46.0 → specfact_cli-0.46.4}/src/specfact_cli/utils/progress.py +0 -0
- {specfact_cli-0.46.0 → specfact_cli-0.46.4}/src/specfact_cli/utils/progressive_disclosure.py +0 -0
- {specfact_cli-0.46.0 → specfact_cli-0.46.4}/src/specfact_cli/utils/prompts.py +0 -0
- {specfact_cli-0.46.0 → specfact_cli-0.46.4}/src/specfact_cli/utils/sdd_discovery.py +0 -0
- {specfact_cli-0.46.0 → specfact_cli-0.46.4}/src/specfact_cli/utils/startup_checks.py +0 -0
- {specfact_cli-0.46.0 → specfact_cli-0.46.4}/src/specfact_cli/utils/structure.py +0 -0
- {specfact_cli-0.46.0 → specfact_cli-0.46.4}/src/specfact_cli/utils/structured_io.py +0 -0
- {specfact_cli-0.46.0 → specfact_cli-0.46.4}/src/specfact_cli/utils/suggestions.py +0 -0
- {specfact_cli-0.46.0 → specfact_cli-0.46.4}/src/specfact_cli/utils/terminal.py +0 -0
- {specfact_cli-0.46.0 → specfact_cli-0.46.4}/src/specfact_cli/utils/yaml_utils.py +0 -0
- {specfact_cli-0.46.0 → specfact_cli-0.46.4}/src/specfact_cli/validation/__init__.py +0 -0
- {specfact_cli-0.46.0 → specfact_cli-0.46.4}/src/specfact_cli/validation/command_audit.py +0 -0
- {specfact_cli-0.46.0 → specfact_cli-0.46.4}/src/specfact_cli/validators/__init__.py +0 -0
- {specfact_cli-0.46.0 → specfact_cli-0.46.4}/src/specfact_cli/validators/agile_validation.py +0 -0
- {specfact_cli-0.46.0 → specfact_cli-0.46.4}/src/specfact_cli/validators/change_proposal_integration.py +0 -0
- {specfact_cli-0.46.0 → specfact_cli-0.46.4}/src/specfact_cli/validators/contract_validator.py +0 -0
- {specfact_cli-0.46.0 → specfact_cli-0.46.4}/src/specfact_cli/validators/fsm.py +0 -0
- {specfact_cli-0.46.0 → specfact_cli-0.46.4}/src/specfact_cli/validators/repro_checker.py +0 -0
- {specfact_cli-0.46.0 → specfact_cli-0.46.4}/src/specfact_cli/validators/schema.py +0 -0
- {specfact_cli-0.46.0 → specfact_cli-0.46.4}/src/specfact_cli/validators/sidecar/__init__.py +0 -0
- {specfact_cli-0.46.0 → specfact_cli-0.46.4}/src/specfact_cli/validators/sidecar/contract_populator.py +0 -0
- {specfact_cli-0.46.0 → specfact_cli-0.46.4}/src/specfact_cli/validators/sidecar/dependency_installer.py +0 -0
- {specfact_cli-0.46.0 → specfact_cli-0.46.4}/src/specfact_cli/validators/sidecar/framework_detector.py +0 -0
- {specfact_cli-0.46.0 → specfact_cli-0.46.4}/src/specfact_cli/validators/sidecar/frameworks/__init__.py +0 -0
- {specfact_cli-0.46.0 → specfact_cli-0.46.4}/src/specfact_cli/validators/sidecar/frameworks/base.py +0 -0
- {specfact_cli-0.46.0 → specfact_cli-0.46.4}/src/specfact_cli/validators/sidecar/frameworks/django.py +0 -0
- {specfact_cli-0.46.0 → specfact_cli-0.46.4}/src/specfact_cli/validators/sidecar/frameworks/drf.py +0 -0
- {specfact_cli-0.46.0 → specfact_cli-0.46.4}/src/specfact_cli/validators/sidecar/frameworks/flask.py +0 -0
- {specfact_cli-0.46.0 → specfact_cli-0.46.4}/src/specfact_cli/validators/sidecar/harness_generator.py +0 -0
- {specfact_cli-0.46.0 → specfact_cli-0.46.4}/src/specfact_cli/validators/sidecar/models.py +0 -0
- {specfact_cli-0.46.0 → specfact_cli-0.46.4}/src/specfact_cli/validators/sidecar/orchestrator.py +0 -0
- {specfact_cli-0.46.0 → specfact_cli-0.46.4}/src/specfact_cli/validators/sidecar/specmatic_runner.py +0 -0
- {specfact_cli-0.46.0 → specfact_cli-0.46.4}/src/specfact_cli/validators/sidecar/unannotated_detector.py +0 -0
- {specfact_cli-0.46.0 → specfact_cli-0.46.4}/src/specfact_cli/versioning/__init__.py +0 -0
- {specfact_cli-0.46.0 → specfact_cli-0.46.4}/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.46.
|
|
3
|
+
Version: 0.46.4
|
|
4
4
|
Summary: The swiss knife CLI for agile DevOps teams. Keep backlog, specs, tests, and code in sync with validation and contract enforcement for new projects and long-lived codebases.
|
|
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
|
|
@@ -224,12 +224,12 @@ Classifier: Topic :: Software Development :: Testing
|
|
|
224
224
|
Requires-Python: >=3.11
|
|
225
225
|
Requires-Dist: azure-identity>=1.17.1
|
|
226
226
|
Requires-Dist: beartype>=0.22.4
|
|
227
|
+
Requires-Dist: commentjson>=0.9.0
|
|
227
228
|
Requires-Dist: cryptography>=43.0.0
|
|
228
229
|
Requires-Dist: gitpython>=3.1.45
|
|
229
230
|
Requires-Dist: graphviz>=0.20.1
|
|
230
231
|
Requires-Dist: icontract>=2.7.1
|
|
231
232
|
Requires-Dist: jinja2>=3.1.6
|
|
232
|
-
Requires-Dist: json5>=0.9.28
|
|
233
233
|
Requires-Dist: jsonschema>=4.23.0
|
|
234
234
|
Requires-Dist: networkx>=3.4.2
|
|
235
235
|
Requires-Dist: opentelemetry-exporter-otlp-proto-http>=1.27.0
|
|
@@ -248,16 +248,18 @@ Provides-Extra: contracts
|
|
|
248
248
|
Requires-Dist: crosshair-tool>=0.0.97; extra == 'contracts'
|
|
249
249
|
Requires-Dist: hypothesis>=6.142.4; extra == 'contracts'
|
|
250
250
|
Provides-Extra: dev
|
|
251
|
+
Requires-Dist: bandit>=1.7.0; extra == 'dev'
|
|
251
252
|
Requires-Dist: basedpyright>=1.32.1; extra == 'dev'
|
|
252
|
-
Requires-Dist: bearer>=3.1.0; extra == 'dev'
|
|
253
253
|
Requires-Dist: beartype>=0.22.4; extra == 'dev'
|
|
254
254
|
Requires-Dist: crosshair-tool>=0.0.97; extra == 'dev'
|
|
255
255
|
Requires-Dist: graphviz>=0.20.1; extra == 'dev'
|
|
256
256
|
Requires-Dist: hypothesis>=6.142.4; extra == 'dev'
|
|
257
257
|
Requires-Dist: icontract>=2.7.1; extra == 'dev'
|
|
258
258
|
Requires-Dist: isort>=7.0.0; extra == 'dev'
|
|
259
|
+
Requires-Dist: pip-audit>=2.0.0; extra == 'dev'
|
|
260
|
+
Requires-Dist: pip-licenses>=4.0.0; extra == 'dev'
|
|
259
261
|
Requires-Dist: pip-tools>=7.5.1; extra == 'dev'
|
|
260
|
-
Requires-Dist:
|
|
262
|
+
Requires-Dist: pycg==0.0.7; extra == 'dev'
|
|
261
263
|
Requires-Dist: pylint>=4.0.2; extra == 'dev'
|
|
262
264
|
Requires-Dist: pytest-asyncio>=1.2.0; extra == 'dev'
|
|
263
265
|
Requires-Dist: pytest-cov>=7.0.0; extra == 'dev'
|
|
@@ -271,10 +273,8 @@ Requires-Dist: setuptools<82,>=69.0.0; extra == 'dev'
|
|
|
271
273
|
Requires-Dist: tomlkit>=0.13.3; extra == 'dev'
|
|
272
274
|
Requires-Dist: types-pyyaml>=6.0.12.20250516; extra == 'dev'
|
|
273
275
|
Provides-Extra: enhanced-analysis
|
|
274
|
-
Requires-Dist: bearer>=3.1.0; extra == 'enhanced-analysis'
|
|
275
276
|
Requires-Dist: graphviz>=0.20.1; extra == 'enhanced-analysis'
|
|
276
|
-
Requires-Dist:
|
|
277
|
-
Requires-Dist: syft>=0.9.5; extra == 'enhanced-analysis'
|
|
277
|
+
Requires-Dist: pycg==0.0.7; extra == 'enhanced-analysis'
|
|
278
278
|
Provides-Extra: scanning
|
|
279
279
|
Requires-Dist: semgrep>=1.144.0; extra == 'scanning'
|
|
280
280
|
Description-Content-Type: text/markdown
|
|
@@ -308,7 +308,7 @@ uvx specfact-cli code review run --path . --scope full
|
|
|
308
308
|
**Sample output:**
|
|
309
309
|
|
|
310
310
|
```text
|
|
311
|
-
SpecFact CLI - v0.46.
|
|
311
|
+
SpecFact CLI - v0.46.4
|
|
312
312
|
|
|
313
313
|
Running Ruff checks...
|
|
314
314
|
Running Radon complexity checks...
|
|
@@ -361,16 +361,28 @@ It exists because delivery drifts in predictable ways:
|
|
|
361
361
|
|
|
362
362
|
## Add SpecFact to your workflow
|
|
363
363
|
|
|
364
|
-
|
|
364
|
+
### Pre-commit hook
|
|
365
|
+
|
|
366
|
+
This repository uses a **modular** local hook layout (parity with `specfact-cli-modules`: `fail_fast`,
|
|
367
|
+
separate verify / format / YAML / Markdown / workflow / lint / Block 2 hooks). If you copy
|
|
368
|
+
[`.pre-commit-config.yaml`](.pre-commit-config.yaml) into another repo, you must also vendor the
|
|
369
|
+
referenced `scripts/*.sh` entrypoints (at minimum `scripts/pre-commit-quality-checks.sh`,
|
|
370
|
+
`scripts/pre-commit-verify-modules.sh`, `scripts/module-verify-policy.sh`, and
|
|
371
|
+
`scripts/git-branch-module-signature-flag.sh`) so hook
|
|
372
|
+
`entry:` paths resolve. Alternatively, skip vendoring the modular file and use the remote hook below.
|
|
373
|
+
|
|
374
|
+
For a **single-hook** setup in downstream repos, keep using the stable id and script shim:
|
|
365
375
|
|
|
366
376
|
```yaml
|
|
367
377
|
- repo: https://github.com/nold-ai/specfact-cli
|
|
368
|
-
rev: v0.46.
|
|
378
|
+
rev: v0.46.4
|
|
369
379
|
hooks:
|
|
370
380
|
- id: specfact-smart-checks
|
|
371
381
|
```
|
|
372
382
|
|
|
373
|
-
|
|
383
|
+
The shim runs `scripts/pre-commit-quality-checks.sh all` (full pipeline including module verify).
|
|
384
|
+
|
|
385
|
+
### GitHub Actions
|
|
374
386
|
|
|
375
387
|
```yaml
|
|
376
388
|
- name: SpecFact Gate
|
|
@@ -27,7 +27,7 @@ uvx specfact-cli code review run --path . --scope full
|
|
|
27
27
|
**Sample output:**
|
|
28
28
|
|
|
29
29
|
```text
|
|
30
|
-
SpecFact CLI - v0.46.
|
|
30
|
+
SpecFact CLI - v0.46.4
|
|
31
31
|
|
|
32
32
|
Running Ruff checks...
|
|
33
33
|
Running Radon complexity checks...
|
|
@@ -80,16 +80,28 @@ It exists because delivery drifts in predictable ways:
|
|
|
80
80
|
|
|
81
81
|
## Add SpecFact to your workflow
|
|
82
82
|
|
|
83
|
-
|
|
83
|
+
### Pre-commit hook
|
|
84
|
+
|
|
85
|
+
This repository uses a **modular** local hook layout (parity with `specfact-cli-modules`: `fail_fast`,
|
|
86
|
+
separate verify / format / YAML / Markdown / workflow / lint / Block 2 hooks). If you copy
|
|
87
|
+
[`.pre-commit-config.yaml`](.pre-commit-config.yaml) into another repo, you must also vendor the
|
|
88
|
+
referenced `scripts/*.sh` entrypoints (at minimum `scripts/pre-commit-quality-checks.sh`,
|
|
89
|
+
`scripts/pre-commit-verify-modules.sh`, `scripts/module-verify-policy.sh`, and
|
|
90
|
+
`scripts/git-branch-module-signature-flag.sh`) so hook
|
|
91
|
+
`entry:` paths resolve. Alternatively, skip vendoring the modular file and use the remote hook below.
|
|
92
|
+
|
|
93
|
+
For a **single-hook** setup in downstream repos, keep using the stable id and script shim:
|
|
84
94
|
|
|
85
95
|
```yaml
|
|
86
96
|
- repo: https://github.com/nold-ai/specfact-cli
|
|
87
|
-
rev: v0.46.
|
|
97
|
+
rev: v0.46.4
|
|
88
98
|
hooks:
|
|
89
99
|
- id: specfact-smart-checks
|
|
90
100
|
```
|
|
91
101
|
|
|
92
|
-
|
|
102
|
+
The shim runs `scripts/pre-commit-quality-checks.sh all` (full pipeline including module verify).
|
|
103
|
+
|
|
104
|
+
### GitHub Actions
|
|
93
105
|
|
|
94
106
|
```yaml
|
|
95
107
|
- name: SpecFact Gate
|
|
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "specfact-cli"
|
|
7
|
-
version = "0.46.
|
|
7
|
+
version = "0.46.4"
|
|
8
8
|
description = "The swiss knife CLI for agile DevOps teams. Keep backlog, specs, tests, and code in sync with validation and contract enforcement for new projects and long-lived codebases."
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.11"
|
|
@@ -77,6 +77,7 @@ dependencies = [
|
|
|
77
77
|
"graphviz>=0.20.1", # Graph visualization (requires system Graphviz: apt-get install graphviz)
|
|
78
78
|
|
|
79
79
|
# Git operations
|
|
80
|
+
# gitpython: CVE history (CVE-2022-24439, CVE-2023-41040, CVE-2023-40590). Phase 2: replace with dulwich.
|
|
80
81
|
"gitpython>=3.1.45",
|
|
81
82
|
|
|
82
83
|
# YAML utilities
|
|
@@ -84,8 +85,8 @@ dependencies = [
|
|
|
84
85
|
|
|
85
86
|
# Schema validation
|
|
86
87
|
"jsonschema>=4.23.0",
|
|
87
|
-
# VS Code settings.json is
|
|
88
|
-
"
|
|
88
|
+
# VS Code settings.json is JSONC (comments + trailing commas); commentjson (MIT) strips them, delegates to stdlib json
|
|
89
|
+
"commentjson>=0.9.0",
|
|
89
90
|
|
|
90
91
|
# Contract-First (runtime decorators; exploration tools are optional extra `contracts`)
|
|
91
92
|
"icontract>=2.7.1", # Design-by-contract decorators
|
|
@@ -114,13 +115,13 @@ dev = [
|
|
|
114
115
|
"pytest-xdist>=3.8.0",
|
|
115
116
|
"basedpyright>=1.32.1",
|
|
116
117
|
"isort>=7.0.0",
|
|
117
|
-
"pylint>=4.0.2",
|
|
118
|
+
"pylint>=4.0.2", # GPL-2.0-or-later — dev-only exception (not in module manifests). Phase 2: replace with ruff --select ALL.
|
|
118
119
|
"ruff>=0.14.2",
|
|
119
120
|
"radon>=6.0.1",
|
|
120
121
|
"tomlkit>=0.13.3", # Style-preserving TOML library (recommended successor to pytoml)
|
|
121
122
|
"types-PyYAML>=6.0.12.20250516",
|
|
122
123
|
"pip-tools>=7.5.1",
|
|
123
|
-
"semgrep>=1.144.0", #
|
|
124
|
+
"semgrep>=1.144.0", # LGPL-2.1 — required for code analysis; invoked as subprocess (accepted exception)
|
|
124
125
|
|
|
125
126
|
# Same contract exploration stack as [contracts] (extras cannot self-reference)
|
|
126
127
|
"crosshair-tool>=0.0.97",
|
|
@@ -131,11 +132,14 @@ dev = [
|
|
|
131
132
|
"beartype>=0.22.4",
|
|
132
133
|
|
|
133
134
|
# Enhanced Analysis Tools (for local development)
|
|
134
|
-
# Note: syft excluded from dev/test due to rich version conflict with semgrep
|
|
135
|
-
# Install separately: pip install specfact-cli[enhanced-analysis] if needed
|
|
136
135
|
"graphviz>=0.20.1", # Graph visualization (requires system Graphviz: apt-get install graphviz)
|
|
137
|
-
"
|
|
138
|
-
|
|
136
|
+
"pycg==0.0.7", # Python call graph analysis (Apache-2.0; replaces removed GPL-2.0 pyan3; pin avoids 0.0.8)
|
|
137
|
+
# Removed from dev: pyan3 (GPL-2.0) and bearer (wrong PyPI package; SaaS auth, not scanner)
|
|
138
|
+
# syft removed — wrong PyPI package (OpenMined ML framework, not Anchore SBOM)
|
|
139
|
+
"bandit>=1.7.0", # SAST scanner (MIT)
|
|
140
|
+
"pip-licenses>=4.0.0", # License enumeration for compliance gate (MIT)
|
|
141
|
+
"pip-audit>=2.0.0", # CVE audit via OSV database (Apache-2.0)
|
|
142
|
+
# pylint: GPL-2.0-or-later — dev-only exception (not in module manifests). Phase 2: replace with ruff --select ALL.
|
|
139
143
|
]
|
|
140
144
|
|
|
141
145
|
scanning = [
|
|
@@ -144,9 +148,8 @@ scanning = [
|
|
|
144
148
|
|
|
145
149
|
enhanced-analysis = [
|
|
146
150
|
"graphviz>=0.20.1", # Graph visualization (requires system Graphviz: apt-get install graphviz)
|
|
147
|
-
"
|
|
148
|
-
|
|
149
|
-
"bearer>=3.1.0", # Data flow analysis for security
|
|
151
|
+
"pycg==0.0.7", # Python call graph analysis (Apache-2.0; replaces removed GPL-2.0 pyan3; pin avoids 0.0.8)
|
|
152
|
+
# Removed from enhanced-analysis: pyan3 (GPL-2.0), bearer, and syft (wrong PyPI packages)
|
|
150
153
|
]
|
|
151
154
|
|
|
152
155
|
# Note: Specmatic integration (specfact spec commands) requires the Specmatic CLI tool
|
|
@@ -188,28 +191,36 @@ dependencies = [
|
|
|
188
191
|
"pylint>=4.0.2",
|
|
189
192
|
"ruff>=0.14.2",
|
|
190
193
|
"radon>=6.0.1",
|
|
191
|
-
"yamllint>=1.37.1",
|
|
192
|
-
"semgrep>=1.144.0", #
|
|
194
|
+
"yamllint>=1.37.1", # GPL-3.0-or-later — dev/test-only exception. Phase 2: replace with non-GPL YAML lint path.
|
|
195
|
+
"semgrep>=1.144.0", # LGPL-2.1 — required for code analysis; invoked as subprocess (accepted exception)
|
|
193
196
|
# Contract-First Development Dependencies
|
|
194
197
|
"icontract>=2.7.1",
|
|
195
|
-
"beartype>=0.22.4",
|
|
198
|
+
"beartype>=0.22.4",
|
|
196
199
|
"crosshair-tool>=0.0.97",
|
|
197
200
|
"hypothesis>=6.142.4",
|
|
198
201
|
# Enhanced Analysis Tools (for local development)
|
|
199
|
-
# Note: syft excluded from dev/test due to rich version conflict with semgrep
|
|
200
|
-
# Install separately: pip install specfact-cli[enhanced-analysis] if needed
|
|
201
202
|
"graphviz>=0.20.1", # Graph visualization (requires system Graphviz: apt-get install graphviz)
|
|
202
|
-
"
|
|
203
|
-
|
|
203
|
+
"pycg==0.0.7", # Python call graph analysis (Apache-2.0; replaces removed GPL-2.0 pyan3; pin avoids 0.0.8)
|
|
204
|
+
# Removed from Hatch default: pyan3 (GPL-2.0), bearer, and syft (wrong PyPI packages)
|
|
205
|
+
"bandit>=1.7.0", # SAST scanner (MIT)
|
|
206
|
+
"pip-licenses>=4.0.0", # License enumeration for compliance gate (MIT)
|
|
207
|
+
"pip-audit>=2.0.0", # CVE audit via OSV database (Apache-2.0)
|
|
208
|
+
# pylint: GPL-2.0-or-later — dev-only exception (not in module manifests). Phase 2: replace with ruff --select ALL.
|
|
204
209
|
]
|
|
205
210
|
|
|
206
211
|
[tool.hatch.envs.default.scripts]
|
|
207
212
|
validate-prompts = "python tools/validate_prompts.py"
|
|
213
|
+
# Security and license compliance gates
|
|
214
|
+
bandit-scan = "bandit -r src/ -ll"
|
|
215
|
+
license-check = "python scripts/check_license_compliance.py"
|
|
216
|
+
# Wrap pip-audit so JSON parsing and CVSS threshold checks fail closed.
|
|
217
|
+
security-audit = "python scripts/security_audit_gate.py"
|
|
208
218
|
# Development scripts
|
|
209
219
|
test = "pytest {args}"
|
|
210
220
|
test-cov = "pytest --cov=src --cov-report=term-missing {args}"
|
|
211
221
|
type-check = "basedpyright --pythonpath $(python -c 'import sys; print(sys.executable)') {args}"
|
|
212
|
-
|
|
222
|
+
# basedpyright --level error: suppress warning noise in pre-commit (Block 1 runs `hatch run lint`).
|
|
223
|
+
lint = "ruff format . --check && basedpyright --level error --pythonpath $(python -c 'import sys; print(sys.executable)') && ruff check . && pylint src tests tools && python scripts/verify_safe_project_writes.py"
|
|
213
224
|
governance = "pylint src tests tools --reports=y --output-format=parseable"
|
|
214
225
|
format = "ruff check . --fix && ruff format ."
|
|
215
226
|
|
|
@@ -236,7 +247,8 @@ check-cross-site-links = "python scripts/check-cross-site-links.py"
|
|
|
236
247
|
doc-frontmatter-check = "python scripts/check_doc_frontmatter.py"
|
|
237
248
|
validate-agent-rule-signals = "python scripts/validate_agent_rule_applies_when.py"
|
|
238
249
|
check-version-sources = "python scripts/check_version_sources.py"
|
|
239
|
-
|
|
250
|
+
check-pypi-ahead = "python scripts/check_local_version_ahead_of_pypi.py"
|
|
251
|
+
release = "python scripts/check_local_version_ahead_of_pypi.py && python scripts/check_version_sources.py"
|
|
240
252
|
docs-validate = "python scripts/check-docs-commands.py && python scripts/check-cross-site-links.py --warn-only && python scripts/check_doc_frontmatter.py && python scripts/validate_agent_rule_applies_when.py"
|
|
241
253
|
|
|
242
254
|
# Legacy entry (kept for compatibility); prefer `workflows-lint` above
|
|
@@ -259,10 +271,15 @@ smart-test-e2e = "python tools/smart_test_coverage.py run --level e2e {args}"
|
|
|
259
271
|
smart-test-full = "python tools/smart_test_coverage.py run --level full {args}"
|
|
260
272
|
smart-test-auto = "python tools/smart_test_coverage.py run --level auto {args}"
|
|
261
273
|
|
|
274
|
+
# Bundled module verify (flags from scripts/module-verify-policy.sh via run_verify_modules_policy.sh)
|
|
275
|
+
verify-modules-signature = "bash scripts/run_verify_modules_policy.sh strict {args}"
|
|
276
|
+
verify-modules-signature-pr = "bash scripts/run_verify_modules_policy.sh pr {args}"
|
|
277
|
+
verify-modules-signature-push = "bash scripts/run_verify_modules_policy.sh push-orchestrator {args}"
|
|
278
|
+
|
|
262
279
|
# Module migration pre-deletion gate
|
|
263
280
|
verify-removal-gate = [
|
|
264
281
|
"python scripts/verify-bundle-published.py --modules project,plan,import_cmd,sync,migrate,backlog,policy_engine,analyze,drift,validate,repro,contract,spec,sdd,generate,enforce,patch_mode",
|
|
265
|
-
"
|
|
282
|
+
"hatch run verify-modules-signature",
|
|
266
283
|
]
|
|
267
284
|
export-change-github = "python scripts/export-change-to-github.py {args}"
|
|
268
285
|
|
|
@@ -286,7 +303,6 @@ contract-prune = "python tools/migrate_tests_to_contracts.py --prune --dry-run"
|
|
|
286
303
|
# Pre-commit hooks
|
|
287
304
|
pre-commit-install = "bash scripts/setup-git-hooks.sh"
|
|
288
305
|
pre-commit-checks = "bash scripts/pre-commit-smart-checks.sh"
|
|
289
|
-
pre-commit-test = "bash scripts/pre-commit-smart-test.sh"
|
|
290
306
|
|
|
291
307
|
[tool.hatch.envs.py311]
|
|
292
308
|
python = "3.11"
|
|
@@ -313,13 +329,12 @@ dependencies = [
|
|
|
313
329
|
"beartype>=0.22.4",
|
|
314
330
|
"crosshair-tool>=0.0.97",
|
|
315
331
|
"hypothesis>=6.142.4",
|
|
316
|
-
"yamllint>=1.37.1",
|
|
332
|
+
"yamllint>=1.37.1", # GPL-3.0-or-later — dev/test-only exception. Phase 2: replace with non-GPL YAML lint path.
|
|
317
333
|
# Enhanced Analysis Tools (for testing)
|
|
318
|
-
# Note: syft excluded from test due to rich version conflict with semgrep
|
|
319
|
-
# Install separately: pip install specfact-cli[enhanced-analysis] if needed
|
|
320
334
|
"graphviz>=0.20.1", # Graph visualization (requires system Graphviz: apt-get install graphviz)
|
|
321
|
-
"
|
|
322
|
-
|
|
335
|
+
"pycg==0.0.7", # Python call graph analysis (Apache-2.0; replaces removed GPL-2.0 pyan3; pin avoids 0.0.8)
|
|
336
|
+
# Removed from Hatch test: pyan3 (GPL-2.0), bearer, and syft (wrong PyPI packages)
|
|
337
|
+
"commentjson>=0.9.0", # JSONC parser (MIT; replaces json5 for VS Code settings JSONC support)
|
|
323
338
|
]
|
|
324
339
|
dev-mode = true
|
|
325
340
|
parallel = true
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
{
|
|
2
|
+
"modules": [
|
|
3
|
+
{
|
|
4
|
+
"id": "init",
|
|
5
|
+
"latest_version": "0.1.30"
|
|
6
|
+
},
|
|
7
|
+
{
|
|
8
|
+
"id": "upgrade",
|
|
9
|
+
"latest_version": "0.1.4"
|
|
10
|
+
},
|
|
11
|
+
{
|
|
12
|
+
"id": "module-registry",
|
|
13
|
+
"latest_version": "0.1.19"
|
|
14
|
+
},
|
|
15
|
+
{
|
|
16
|
+
"id": "bundle-mapper",
|
|
17
|
+
"latest_version": "0.1.9"
|
|
18
|
+
}
|
|
19
|
+
]
|
|
20
|
+
}
|
|
@@ -54,7 +54,7 @@ console = Console()
|
|
|
54
54
|
|
|
55
55
|
@dataclass(frozen=True, slots=True)
|
|
56
56
|
class _AdoCreatedWorkItemRef:
|
|
57
|
-
work_item_id:
|
|
57
|
+
work_item_id: int | str
|
|
58
58
|
work_item_url: str
|
|
59
59
|
org: str
|
|
60
60
|
project: str
|
|
@@ -744,7 +744,10 @@ class AdoAdapter(BridgeAdapter, BacklogAdapterMixin, BacklogAdapter):
|
|
|
744
744
|
},
|
|
745
745
|
}
|
|
746
746
|
source_tracking = proposal_data.get("source_tracking")
|
|
747
|
-
if source_tracking
|
|
747
|
+
if isinstance(source_tracking, list):
|
|
748
|
+
cast(list[dict[str, Any]], source_tracking).append(tracking_update)
|
|
749
|
+
return
|
|
750
|
+
if source_tracking is None or (isinstance(source_tracking, dict) and len(source_tracking) == 0):
|
|
748
751
|
proposal_data["source_tracking"] = tracking_update
|
|
749
752
|
return
|
|
750
753
|
if isinstance(source_tracking, dict):
|
|
@@ -752,11 +755,6 @@ class AdoAdapter(BridgeAdapter, BacklogAdapterMixin, BacklogAdapter):
|
|
|
752
755
|
st.update(tracking_update)
|
|
753
756
|
proposal_data["source_tracking"] = st
|
|
754
757
|
return
|
|
755
|
-
if isinstance(source_tracking, list):
|
|
756
|
-
if not source_tracking:
|
|
757
|
-
proposal_data["source_tracking"] = [tracking_update]
|
|
758
|
-
return
|
|
759
|
-
cast(list[dict[str, Any]], source_tracking).append(tracking_update)
|
|
760
758
|
|
|
761
759
|
def _is_on_premise(self) -> bool:
|
|
762
760
|
"""
|
|
@@ -2270,8 +2268,10 @@ class AdoAdapter(BridgeAdapter, BacklogAdapterMixin, BacklogAdapter):
|
|
|
2270
2268
|
Returns:
|
|
2271
2269
|
Dict with updated work item data: {"work_item_id": int, "work_item_url": str, "state": str}
|
|
2272
2270
|
"""
|
|
2273
|
-
|
|
2274
|
-
|
|
2271
|
+
work_item_id = self._get_source_tracking_work_item_id(
|
|
2272
|
+
proposal_data.get("source_tracking", {}),
|
|
2273
|
+
f"{org}/{project}",
|
|
2274
|
+
)
|
|
2275
2275
|
ado_state = self._resolve_proposal_ado_state(proposal_data)
|
|
2276
2276
|
work_item_data = self._patch_work_item(
|
|
2277
2277
|
org,
|
|
@@ -159,7 +159,11 @@ def _get_github_token_from_gh_cli() -> str | None:
|
|
|
159
159
|
return None
|
|
160
160
|
|
|
161
161
|
|
|
162
|
-
|
|
162
|
+
# Line-anchored so ``pushurl =`` / ``insteadOf`` lines do not match the ``url`` token inside another key.
|
|
163
|
+
# Matches: https://, http://, ssh://, git://, and git@host:path remotes.
|
|
164
|
+
_GITHUB_GIT_CONFIG_URL_RE = re.compile(
|
|
165
|
+
r"(?m)^\s*url\s*=\s*(https?://[^\s]+|ssh://[^\s]+|git://[^\s]+|git@[^:]+:[^\s]+)"
|
|
166
|
+
)
|
|
163
167
|
|
|
164
168
|
|
|
165
169
|
def _git_config_content_indicates_github(config_content: str) -> bool:
|
|
@@ -1471,7 +1475,14 @@ class GitHubAdapter(BridgeAdapter, BacklogAdapterMixin, BacklogAdapter):
|
|
|
1471
1475
|
title = raw_title
|
|
1472
1476
|
|
|
1473
1477
|
body = self._render_issue_body(
|
|
1474
|
-
_IssueBodyRenderInput(
|
|
1478
|
+
_IssueBodyRenderInput(
|
|
1479
|
+
title=title,
|
|
1480
|
+
description=description,
|
|
1481
|
+
rationale=rationale,
|
|
1482
|
+
impact=impact,
|
|
1483
|
+
change_id=change_id,
|
|
1484
|
+
raw_body=raw_body,
|
|
1485
|
+
)
|
|
1475
1486
|
)
|
|
1476
1487
|
|
|
1477
1488
|
# Check for API token before making request
|
|
@@ -1735,7 +1746,15 @@ class GitHubAdapter(BridgeAdapter, BacklogAdapterMixin, BacklogAdapter):
|
|
|
1735
1746
|
current_body, current_title, current_state = self._fetch_issue_snapshot(repo_owner, repo_name, issue_number)
|
|
1736
1747
|
preserved_sections = self._preserved_issue_sections(current_body, change_id)
|
|
1737
1748
|
body = self._render_issue_body(
|
|
1738
|
-
_IssueBodyRenderInput(
|
|
1749
|
+
_IssueBodyRenderInput(
|
|
1750
|
+
title=title,
|
|
1751
|
+
description=description,
|
|
1752
|
+
rationale=rationale,
|
|
1753
|
+
impact=impact,
|
|
1754
|
+
change_id=change_id,
|
|
1755
|
+
raw_body=raw_body,
|
|
1756
|
+
preserved_sections=preserved_sections,
|
|
1757
|
+
)
|
|
1739
1758
|
)
|
|
1740
1759
|
|
|
1741
1760
|
# Update issue body via GitHub API PATCH
|
|
@@ -462,15 +462,19 @@ class SpecKitAdapter(BridgeAdapter):
|
|
|
462
462
|
story_title = sd.get("title", "Unknown Story")
|
|
463
463
|
priority = sd.get("priority", "P3")
|
|
464
464
|
acceptance_raw = sd.get("acceptance", [])
|
|
465
|
-
default_acceptance = [f"{story_title} is implemented"]
|
|
466
465
|
if isinstance(acceptance_raw, list) and acceptance_raw:
|
|
467
466
|
if all(isinstance(x, str) for x in acceptance_raw):
|
|
468
|
-
acceptance =
|
|
467
|
+
acceptance = [s.strip() for s in acceptance_raw if isinstance(s, str) and s.strip()]
|
|
469
468
|
else:
|
|
470
|
-
|
|
471
|
-
|
|
469
|
+
acceptance = [
|
|
470
|
+
s.strip() for s in self._extract_text_list(cast(list[Any], acceptance_raw)) if s.strip()
|
|
471
|
+
]
|
|
472
|
+
if not acceptance:
|
|
473
|
+
acceptance = [f"{story_title} is implemented"]
|
|
474
|
+
if not acceptance:
|
|
475
|
+
acceptance = [f"{story_title} is implemented"]
|
|
472
476
|
else:
|
|
473
|
-
acceptance =
|
|
477
|
+
acceptance = [f"{story_title} is implemented"]
|
|
474
478
|
story_points = priority_map.get(str(priority), 3)
|
|
475
479
|
stories.append(
|
|
476
480
|
Story(
|
|
@@ -548,14 +552,13 @@ class SpecKitAdapter(BridgeAdapter):
|
|
|
548
552
|
|
|
549
553
|
if existing_feature:
|
|
550
554
|
existing_feature.title = payload.feature_title
|
|
551
|
-
existing_feature.outcomes = (
|
|
552
|
-
|
|
553
|
-
)
|
|
554
|
-
existing_feature.acceptance = (
|
|
555
|
-
list(payload.acceptance) if payload.acceptance else list(existing_feature.acceptance or [])
|
|
556
|
-
)
|
|
555
|
+
existing_feature.outcomes = list(payload.outcomes)
|
|
556
|
+
existing_feature.acceptance = list(payload.acceptance)
|
|
557
557
|
existing_feature.stories = [s.model_copy(deep=True) for s in payload.stories]
|
|
558
558
|
existing_feature.constraints = list(constraints)
|
|
559
|
+
existing_feature.source_tracking = self._build_speckit_source_tracking(
|
|
560
|
+
payload.spec_path, payload.bridge_config
|
|
561
|
+
)
|
|
559
562
|
return
|
|
560
563
|
|
|
561
564
|
feature = Feature(
|
|
@@ -507,16 +507,16 @@ class CodeAnalyzer:
|
|
|
507
507
|
}
|
|
508
508
|
)
|
|
509
509
|
|
|
510
|
-
# Dependency Graph Analysis (requires
|
|
511
|
-
|
|
510
|
+
# Dependency Graph Analysis (requires pycg and networkx)
|
|
511
|
+
pycg_available, _ = check_cli_tool_available("pycg")
|
|
512
512
|
networkx_available = check_python_package_available("networkx")
|
|
513
|
-
graph_enabled =
|
|
513
|
+
graph_enabled = pycg_available and networkx_available
|
|
514
514
|
graph_used = graph_enabled # Used if both dependencies are available
|
|
515
515
|
|
|
516
|
-
if not
|
|
517
|
-
reason = "
|
|
518
|
-
elif not
|
|
519
|
-
reason = "
|
|
516
|
+
if not pycg_available and not networkx_available:
|
|
517
|
+
reason = "pycg and networkx not installed (install: pip install pycg networkx)"
|
|
518
|
+
elif not pycg_available:
|
|
519
|
+
reason = "pycg not installed (install: pip install pycg)"
|
|
520
520
|
elif not networkx_available:
|
|
521
521
|
reason = "networkx not installed (install: pip install networkx)"
|
|
522
522
|
else:
|
|
@@ -738,19 +738,8 @@ class CodeAnalyzer:
|
|
|
738
738
|
@staticmethod
|
|
739
739
|
def _themes_for_import_module(module_name: str, theme_keywords: dict[str, str]) -> set[str]:
|
|
740
740
|
lowered = module_name.lower()
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
found: set[str] = set()
|
|
744
|
-
for keyword, theme in theme_keywords.items():
|
|
745
|
-
if keyword == lowered or lowered.startswith(f"{keyword}."):
|
|
746
|
-
found.add(theme)
|
|
747
|
-
continue
|
|
748
|
-
if keyword == top_level or top_level.startswith(f"{keyword}."):
|
|
749
|
-
found.add(theme)
|
|
750
|
-
continue
|
|
751
|
-
if keyword in tokens:
|
|
752
|
-
found.add(theme)
|
|
753
|
-
return found
|
|
741
|
+
segments = {p for p in lowered.replace("-", ".").split(".") if p}
|
|
742
|
+
return {theme for keyword, theme in theme_keywords.items() if keyword in segments}
|
|
754
743
|
|
|
755
744
|
def _themes_for_import_node(self, node: ast.Import | ast.ImportFrom, theme_keywords: dict[str, str]) -> set[str]:
|
|
756
745
|
if isinstance(node, ast.Import):
|
|
@@ -1745,14 +1734,50 @@ class CodeAnalyzer:
|
|
|
1745
1734
|
self.async_patterns[module_name].extend(async_methods)
|
|
1746
1735
|
return async_methods
|
|
1747
1736
|
|
|
1748
|
-
|
|
1737
|
+
@staticmethod
|
|
1738
|
+
def _build_ast_parent_map(tree: ast.AST) -> dict[ast.AST, ast.AST | None]:
|
|
1739
|
+
parents: dict[ast.AST, ast.AST | None] = {}
|
|
1740
|
+
|
|
1741
|
+
def visit(node: ast.AST, parent: ast.AST | None) -> None:
|
|
1742
|
+
parents[node] = parent
|
|
1743
|
+
for child in ast.iter_child_nodes(node):
|
|
1744
|
+
visit(child, node)
|
|
1745
|
+
|
|
1746
|
+
visit(tree, None)
|
|
1747
|
+
return parents
|
|
1748
|
+
|
|
1749
|
+
@staticmethod
|
|
1750
|
+
def _function_name_holding_await(parents: dict[ast.AST, ast.AST | None], await_node: ast.Await) -> str | None:
|
|
1751
|
+
current: ast.AST | None = await_node
|
|
1752
|
+
while current is not None:
|
|
1753
|
+
par = parents.get(current)
|
|
1754
|
+
if par is None:
|
|
1755
|
+
return None
|
|
1756
|
+
if isinstance(par, (ast.FunctionDef, ast.AsyncFunctionDef)):
|
|
1757
|
+
return par.name
|
|
1758
|
+
current = par
|
|
1759
|
+
return None
|
|
1760
|
+
|
|
1761
|
+
def _detect_async_patterns_parallel(self, tree: ast.AST, _file_path: Path) -> list[str]:
|
|
1749
1762
|
"""
|
|
1750
1763
|
Detect async/await patterns in code (thread-safe version).
|
|
1751
1764
|
|
|
1752
1765
|
Returns:
|
|
1753
1766
|
List of async method/function names
|
|
1754
1767
|
"""
|
|
1755
|
-
|
|
1768
|
+
async_methods: list[str] = []
|
|
1769
|
+
parents = self._build_ast_parent_map(tree)
|
|
1770
|
+
|
|
1771
|
+
for node in ast.walk(tree):
|
|
1772
|
+
if isinstance(node, ast.AsyncFunctionDef):
|
|
1773
|
+
async_methods.append(node.name)
|
|
1774
|
+
if not isinstance(node, ast.Await):
|
|
1775
|
+
continue
|
|
1776
|
+
host = self._function_name_holding_await(parents, node)
|
|
1777
|
+
if host and host not in async_methods:
|
|
1778
|
+
async_methods.append(host)
|
|
1779
|
+
|
|
1780
|
+
return async_methods
|
|
1756
1781
|
|
|
1757
1782
|
def _apply_commit_hash_to_matching_features(self, feature_num: str, commit_hash: str) -> None:
|
|
1758
1783
|
for feature in self.features:
|