specfact-cli 0.47.6__tar.gz → 0.48.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {specfact_cli-0.47.6 → specfact_cli-0.48.2}/.gitignore +1 -0
- {specfact_cli-0.47.6 → specfact_cli-0.48.2}/PKG-INFO +1 -1
- {specfact_cli-0.47.6 → specfact_cli-0.48.2}/pyproject.toml +3 -1
- {specfact_cli-0.47.6 → specfact_cli-0.48.2}/resources/bundled-module-registry/index.json +6 -6
- {specfact_cli-0.47.6 → specfact_cli-0.48.2}/src/__init__.py +1 -1
- {specfact_cli-0.47.6 → specfact_cli-0.48.2}/src/specfact_cli/__init__.py +1 -1
- {specfact_cli-0.47.6 → specfact_cli-0.48.2}/src/specfact_cli/adapters/ado.py +2 -2
- {specfact_cli-0.47.6 → specfact_cli-0.48.2}/src/specfact_cli/generators/persona_exporter.py +1 -1
- {specfact_cli-0.47.6 → specfact_cli-0.48.2}/src/specfact_cli/generators/plan_generator.py +1 -1
- {specfact_cli-0.47.6 → specfact_cli-0.48.2}/src/specfact_cli/generators/protocol_generator.py +1 -1
- {specfact_cli-0.47.6 → specfact_cli-0.48.2}/src/specfact_cli/generators/report_generator.py +1 -1
- {specfact_cli-0.47.6 → specfact_cli-0.48.2}/src/specfact_cli/generators/workflow_generator.py +1 -1
- {specfact_cli-0.47.6 → specfact_cli-0.48.2}/src/specfact_cli/modules/init/module-package.yaml +3 -3
- {specfact_cli-0.47.6 → specfact_cli-0.48.2}/src/specfact_cli/modules/init/src/commands.py +21 -10
- {specfact_cli-0.47.6 → specfact_cli-0.48.2}/src/specfact_cli/modules/init/src/first_run_selection.py +280 -2
- {specfact_cli-0.47.6 → specfact_cli-0.48.2}/src/specfact_cli/modules/module_registry/module-package.yaml +3 -3
- {specfact_cli-0.47.6 → specfact_cli-0.48.2}/src/specfact_cli/modules/module_registry/src/commands.py +12 -4
- {specfact_cli-0.47.6 → specfact_cli-0.48.2}/src/specfact_cli/registry/dependency_resolver.py +11 -8
- {specfact_cli-0.47.6 → specfact_cli-0.48.2}/src/specfact_cli/registry/module_installer.py +1 -1
- {specfact_cli-0.47.6 → specfact_cli-0.48.2}/src/specfact_cli/telemetry.py +10 -4
- {specfact_cli-0.47.6 → specfact_cli-0.48.2}/src/specfact_cli/utils/suggestions.py +4 -4
- {specfact_cli-0.47.6 → specfact_cli-0.48.2}/LICENSE +0 -0
- {specfact_cli-0.47.6 → specfact_cli-0.48.2}/README.md +0 -0
- {specfact_cli-0.47.6 → specfact_cli-0.48.2}/resources/keys/README.md +0 -0
- {specfact_cli-0.47.6 → specfact_cli-0.48.2}/resources/keys/module-signing-public.pem +0 -0
- {specfact_cli-0.47.6 → specfact_cli-0.48.2}/resources/mappings/node-async.yaml +0 -0
- {specfact_cli-0.47.6 → specfact_cli-0.48.2}/resources/mappings/python-async.yaml +0 -0
- {specfact_cli-0.47.6 → specfact_cli-0.48.2}/resources/mappings/speckit-default.yaml +0 -0
- {specfact_cli-0.47.6 → specfact_cli-0.48.2}/resources/schemas/deviation.schema.json +0 -0
- {specfact_cli-0.47.6 → specfact_cli-0.48.2}/resources/schemas/plan.schema.json +0 -0
- {specfact_cli-0.47.6 → specfact_cli-0.48.2}/resources/schemas/protocol.schema.json +0 -0
- {specfact_cli-0.47.6 → specfact_cli-0.48.2}/resources/templates/github-action.yml.j2 +0 -0
- {specfact_cli-0.47.6 → specfact_cli-0.48.2}/resources/templates/persona/architect.md.j2 +0 -0
- {specfact_cli-0.47.6 → specfact_cli-0.48.2}/resources/templates/persona/developer.md.j2 +0 -0
- {specfact_cli-0.47.6 → specfact_cli-0.48.2}/resources/templates/persona/product-owner.md.j2 +0 -0
- {specfact_cli-0.47.6 → specfact_cli-0.48.2}/resources/templates/plan.bundle.yaml.j2 +0 -0
- {specfact_cli-0.47.6 → specfact_cli-0.48.2}/resources/templates/policies/kanban.yaml +0 -0
- {specfact_cli-0.47.6 → specfact_cli-0.48.2}/resources/templates/policies/mixed.yaml +0 -0
- {specfact_cli-0.47.6 → specfact_cli-0.48.2}/resources/templates/policies/safe.yaml +0 -0
- {specfact_cli-0.47.6 → specfact_cli-0.48.2}/resources/templates/policies/scrum.yaml +0 -0
- {specfact_cli-0.47.6 → specfact_cli-0.48.2}/resources/templates/pr-template.md.j2 +0 -0
- {specfact_cli-0.47.6 → specfact_cli-0.48.2}/resources/templates/protocol.yaml.j2 +0 -0
- {specfact_cli-0.47.6 → specfact_cli-0.48.2}/resources/templates/telemetry.yaml.example +0 -0
- {specfact_cli-0.47.6 → specfact_cli-0.48.2}/src/specfact_cli/__main__.py +0 -0
- {specfact_cli-0.47.6 → specfact_cli-0.48.2}/src/specfact_cli/adapters/__init__.py +0 -0
- {specfact_cli-0.47.6 → specfact_cli-0.48.2}/src/specfact_cli/adapters/backlog_base.py +0 -0
- {specfact_cli-0.47.6 → specfact_cli-0.48.2}/src/specfact_cli/adapters/base.py +0 -0
- {specfact_cli-0.47.6 → specfact_cli-0.48.2}/src/specfact_cli/adapters/github.py +0 -0
- {specfact_cli-0.47.6 → specfact_cli-0.48.2}/src/specfact_cli/adapters/openspec.py +0 -0
- {specfact_cli-0.47.6 → specfact_cli-0.48.2}/src/specfact_cli/adapters/openspec_parser.py +0 -0
- {specfact_cli-0.47.6 → specfact_cli-0.48.2}/src/specfact_cli/adapters/registry.py +0 -0
- {specfact_cli-0.47.6 → specfact_cli-0.48.2}/src/specfact_cli/adapters/speckit.py +0 -0
- {specfact_cli-0.47.6 → specfact_cli-0.48.2}/src/specfact_cli/agents/__init__.py +0 -0
- {specfact_cli-0.47.6 → specfact_cli-0.48.2}/src/specfact_cli/agents/analyze_agent.py +0 -0
- {specfact_cli-0.47.6 → specfact_cli-0.48.2}/src/specfact_cli/agents/base.py +0 -0
- {specfact_cli-0.47.6 → specfact_cli-0.48.2}/src/specfact_cli/agents/plan_agent.py +0 -0
- {specfact_cli-0.47.6 → specfact_cli-0.48.2}/src/specfact_cli/agents/registry.py +0 -0
- {specfact_cli-0.47.6 → specfact_cli-0.48.2}/src/specfact_cli/agents/sync_agent.py +0 -0
- {specfact_cli-0.47.6 → specfact_cli-0.48.2}/src/specfact_cli/analyzers/__init__.py +0 -0
- {specfact_cli-0.47.6 → specfact_cli-0.48.2}/src/specfact_cli/analyzers/ambiguity_scanner.py +0 -0
- {specfact_cli-0.47.6 → specfact_cli-0.48.2}/src/specfact_cli/analyzers/code_analyzer.py +0 -0
- {specfact_cli-0.47.6 → specfact_cli-0.48.2}/src/specfact_cli/analyzers/constitution_evidence_extractor.py +0 -0
- {specfact_cli-0.47.6 → specfact_cli-0.48.2}/src/specfact_cli/analyzers/contract_extractor.py +0 -0
- {specfact_cli-0.47.6 → specfact_cli-0.48.2}/src/specfact_cli/analyzers/control_flow_analyzer.py +0 -0
- {specfact_cli-0.47.6 → specfact_cli-0.48.2}/src/specfact_cli/analyzers/graph_analyzer.py +0 -0
- {specfact_cli-0.47.6 → specfact_cli-0.48.2}/src/specfact_cli/analyzers/relationship_mapper.py +0 -0
- {specfact_cli-0.47.6 → specfact_cli-0.48.2}/src/specfact_cli/analyzers/requirement_extractor.py +0 -0
- {specfact_cli-0.47.6 → specfact_cli-0.48.2}/src/specfact_cli/analyzers/test_pattern_extractor.py +0 -0
- {specfact_cli-0.47.6 → specfact_cli-0.48.2}/src/specfact_cli/backlog/__init__.py +0 -0
- {specfact_cli-0.47.6 → specfact_cli-0.48.2}/src/specfact_cli/backlog/adapters/__init__.py +0 -0
- {specfact_cli-0.47.6 → specfact_cli-0.48.2}/src/specfact_cli/backlog/adapters/base.py +0 -0
- {specfact_cli-0.47.6 → specfact_cli-0.48.2}/src/specfact_cli/backlog/converter.py +0 -0
- {specfact_cli-0.47.6 → specfact_cli-0.48.2}/src/specfact_cli/backlog/filters.py +0 -0
- {specfact_cli-0.47.6 → specfact_cli-0.48.2}/src/specfact_cli/backlog/mappers/__init__.py +0 -0
- {specfact_cli-0.47.6 → specfact_cli-0.48.2}/src/specfact_cli/backlog/mappers/ado_mapper.py +0 -0
- {specfact_cli-0.47.6 → specfact_cli-0.48.2}/src/specfact_cli/backlog/mappers/base.py +0 -0
- {specfact_cli-0.47.6 → specfact_cli-0.48.2}/src/specfact_cli/backlog/mappers/github_mapper.py +0 -0
- {specfact_cli-0.47.6 → specfact_cli-0.48.2}/src/specfact_cli/backlog/mappers/template_config.py +0 -0
- {specfact_cli-0.47.6 → specfact_cli-0.48.2}/src/specfact_cli/cli.py +0 -0
- {specfact_cli-0.47.6 → specfact_cli-0.48.2}/src/specfact_cli/commands/__init__.py +0 -0
- {specfact_cli-0.47.6 → specfact_cli-0.48.2}/src/specfact_cli/commands/_bundle_shim.py +0 -0
- {specfact_cli-0.47.6 → specfact_cli-0.48.2}/src/specfact_cli/commands/analyze.py +0 -0
- {specfact_cli-0.47.6 → specfact_cli-0.48.2}/src/specfact_cli/commands/contract_cmd.py +0 -0
- {specfact_cli-0.47.6 → specfact_cli-0.48.2}/src/specfact_cli/commands/drift.py +0 -0
- {specfact_cli-0.47.6 → specfact_cli-0.48.2}/src/specfact_cli/commands/enforce.py +0 -0
- {specfact_cli-0.47.6 → specfact_cli-0.48.2}/src/specfact_cli/commands/generate.py +0 -0
- {specfact_cli-0.47.6 → specfact_cli-0.48.2}/src/specfact_cli/commands/import_cmd.py +0 -0
- {specfact_cli-0.47.6 → specfact_cli-0.48.2}/src/specfact_cli/commands/init.py +0 -0
- {specfact_cli-0.47.6 → specfact_cli-0.48.2}/src/specfact_cli/commands/migrate.py +0 -0
- {specfact_cli-0.47.6 → specfact_cli-0.48.2}/src/specfact_cli/commands/plan.py +0 -0
- {specfact_cli-0.47.6 → specfact_cli-0.48.2}/src/specfact_cli/commands/project_cmd.py +0 -0
- {specfact_cli-0.47.6 → specfact_cli-0.48.2}/src/specfact_cli/commands/repro.py +0 -0
- {specfact_cli-0.47.6 → specfact_cli-0.48.2}/src/specfact_cli/commands/sdd.py +0 -0
- {specfact_cli-0.47.6 → specfact_cli-0.48.2}/src/specfact_cli/commands/spec.py +0 -0
- {specfact_cli-0.47.6 → specfact_cli-0.48.2}/src/specfact_cli/commands/sync.py +0 -0
- {specfact_cli-0.47.6 → specfact_cli-0.48.2}/src/specfact_cli/commands/update.py +0 -0
- {specfact_cli-0.47.6 → specfact_cli-0.48.2}/src/specfact_cli/commands/validate.py +0 -0
- {specfact_cli-0.47.6 → specfact_cli-0.48.2}/src/specfact_cli/common/__init__.py +0 -0
- {specfact_cli-0.47.6 → specfact_cli-0.48.2}/src/specfact_cli/common/bundle_factory.py +0 -0
- {specfact_cli-0.47.6 → specfact_cli-0.48.2}/src/specfact_cli/common/logger_setup.py +0 -0
- {specfact_cli-0.47.6 → specfact_cli-0.48.2}/src/specfact_cli/common/logging_utils.py +0 -0
- {specfact_cli-0.47.6 → specfact_cli-0.48.2}/src/specfact_cli/common/text_utils.py +0 -0
- {specfact_cli-0.47.6 → specfact_cli-0.48.2}/src/specfact_cli/common/utils.py +0 -0
- {specfact_cli-0.47.6 → specfact_cli-0.48.2}/src/specfact_cli/comparators/__init__.py +0 -0
- {specfact_cli-0.47.6 → specfact_cli-0.48.2}/src/specfact_cli/comparators/plan_comparator.py +0 -0
- {specfact_cli-0.47.6 → specfact_cli-0.48.2}/src/specfact_cli/contracts/__init__.py +0 -0
- {specfact_cli-0.47.6 → specfact_cli-0.48.2}/src/specfact_cli/contracts/crosshair_props.py +0 -0
- {specfact_cli-0.47.6 → specfact_cli-0.48.2}/src/specfact_cli/contracts/module_interface.py +0 -0
- {specfact_cli-0.47.6 → specfact_cli-0.48.2}/src/specfact_cli/enrichers/constitution_enricher.py +0 -0
- {specfact_cli-0.47.6 → specfact_cli-0.48.2}/src/specfact_cli/enrichers/plan_enricher.py +0 -0
- {specfact_cli-0.47.6 → specfact_cli-0.48.2}/src/specfact_cli/generators/__init__.py +0 -0
- {specfact_cli-0.47.6 → specfact_cli-0.48.2}/src/specfact_cli/generators/contract_generator.py +0 -0
- {specfact_cli-0.47.6 → specfact_cli-0.48.2}/src/specfact_cli/generators/openapi_extractor.py +0 -0
- {specfact_cli-0.47.6 → specfact_cli-0.48.2}/src/specfact_cli/generators/task_generator.py +0 -0
- {specfact_cli-0.47.6 → specfact_cli-0.48.2}/src/specfact_cli/generators/test_to_openapi.py +0 -0
- {specfact_cli-0.47.6 → specfact_cli-0.48.2}/src/specfact_cli/groups/__init__.py +0 -0
- {specfact_cli-0.47.6 → specfact_cli-0.48.2}/src/specfact_cli/groups/codebase_group.py +0 -0
- {specfact_cli-0.47.6 → specfact_cli-0.48.2}/src/specfact_cli/groups/govern_group.py +0 -0
- {specfact_cli-0.47.6 → specfact_cli-0.48.2}/src/specfact_cli/groups/member_group.py +0 -0
- {specfact_cli-0.47.6 → specfact_cli-0.48.2}/src/specfact_cli/groups/project_group.py +0 -0
- {specfact_cli-0.47.6 → specfact_cli-0.48.2}/src/specfact_cli/groups/spec_group.py +0 -0
- {specfact_cli-0.47.6 → specfact_cli-0.48.2}/src/specfact_cli/importers/__init__.py +0 -0
- {specfact_cli-0.47.6 → specfact_cli-0.48.2}/src/specfact_cli/importers/speckit_converter.py +0 -0
- {specfact_cli-0.47.6 → specfact_cli-0.48.2}/src/specfact_cli/importers/speckit_scanner.py +0 -0
- {specfact_cli-0.47.6 → specfact_cli-0.48.2}/src/specfact_cli/integrations/__init__.py +0 -0
- {specfact_cli-0.47.6 → specfact_cli-0.48.2}/src/specfact_cli/integrations/specmatic.py +0 -0
- {specfact_cli-0.47.6 → specfact_cli-0.48.2}/src/specfact_cli/merge/__init__.py +0 -0
- {specfact_cli-0.47.6 → specfact_cli-0.48.2}/src/specfact_cli/merge/resolver.py +0 -0
- {specfact_cli-0.47.6 → specfact_cli-0.48.2}/src/specfact_cli/migrations/__init__.py +0 -0
- {specfact_cli-0.47.6 → specfact_cli-0.48.2}/src/specfact_cli/migrations/plan_migrator.py +0 -0
- {specfact_cli-0.47.6 → specfact_cli-0.48.2}/src/specfact_cli/models/__init__.py +0 -0
- {specfact_cli-0.47.6 → specfact_cli-0.48.2}/src/specfact_cli/models/backlog_item.py +0 -0
- {specfact_cli-0.47.6 → specfact_cli-0.48.2}/src/specfact_cli/models/bridge.py +0 -0
- {specfact_cli-0.47.6 → specfact_cli-0.48.2}/src/specfact_cli/models/capabilities.py +0 -0
- {specfact_cli-0.47.6 → specfact_cli-0.48.2}/src/specfact_cli/models/change.py +0 -0
- {specfact_cli-0.47.6 → specfact_cli-0.48.2}/src/specfact_cli/models/contract.py +0 -0
- {specfact_cli-0.47.6 → specfact_cli-0.48.2}/src/specfact_cli/models/deviation.py +0 -0
- {specfact_cli-0.47.6 → specfact_cli-0.48.2}/src/specfact_cli/models/dor_config.py +0 -0
- {specfact_cli-0.47.6 → specfact_cli-0.48.2}/src/specfact_cli/models/enforcement.py +0 -0
- {specfact_cli-0.47.6 → specfact_cli-0.48.2}/src/specfact_cli/models/module_package.py +0 -0
- {specfact_cli-0.47.6 → specfact_cli-0.48.2}/src/specfact_cli/models/persona_template.py +0 -0
- {specfact_cli-0.47.6 → specfact_cli-0.48.2}/src/specfact_cli/models/plan.py +0 -0
- {specfact_cli-0.47.6 → specfact_cli-0.48.2}/src/specfact_cli/models/project.py +0 -0
- {specfact_cli-0.47.6 → specfact_cli-0.48.2}/src/specfact_cli/models/protocol.py +0 -0
- {specfact_cli-0.47.6 → specfact_cli-0.48.2}/src/specfact_cli/models/quality.py +0 -0
- {specfact_cli-0.47.6 → specfact_cli-0.48.2}/src/specfact_cli/models/sdd.py +0 -0
- {specfact_cli-0.47.6 → specfact_cli-0.48.2}/src/specfact_cli/models/source_tracking.py +0 -0
- {specfact_cli-0.47.6 → specfact_cli-0.48.2}/src/specfact_cli/models/task.py +0 -0
- {specfact_cli-0.47.6 → specfact_cli-0.48.2}/src/specfact_cli/models/validation.py +0 -0
- {specfact_cli-0.47.6 → specfact_cli-0.48.2}/src/specfact_cli/modes/__init__.py +0 -0
- {specfact_cli-0.47.6 → specfact_cli-0.48.2}/src/specfact_cli/modes/detector.py +0 -0
- {specfact_cli-0.47.6 → specfact_cli-0.48.2}/src/specfact_cli/modes/router.py +0 -0
- {specfact_cli-0.47.6 → specfact_cli-0.48.2}/src/specfact_cli/modules/__init__.py +0 -0
- {specfact_cli-0.47.6 → specfact_cli-0.48.2}/src/specfact_cli/modules/_bundle_import.py +0 -0
- {specfact_cli-0.47.6 → specfact_cli-0.48.2}/src/specfact_cli/modules/init/src/__init__.py +0 -0
- {specfact_cli-0.47.6 → specfact_cli-0.48.2}/src/specfact_cli/modules/init/src/app.py +0 -0
- {specfact_cli-0.47.6 → specfact_cli-0.48.2}/src/specfact_cli/modules/module_io_shim.py +0 -0
- {specfact_cli-0.47.6 → specfact_cli-0.48.2}/src/specfact_cli/modules/module_registry/src/__init__.py +0 -0
- {specfact_cli-0.47.6 → specfact_cli-0.48.2}/src/specfact_cli/modules/module_registry/src/app.py +0 -0
- {specfact_cli-0.47.6 → specfact_cli-0.48.2}/src/specfact_cli/modules/upgrade/module-package.yaml +0 -0
- {specfact_cli-0.47.6 → specfact_cli-0.48.2}/src/specfact_cli/modules/upgrade/src/__init__.py +0 -0
- {specfact_cli-0.47.6 → specfact_cli-0.48.2}/src/specfact_cli/modules/upgrade/src/app.py +0 -0
- {specfact_cli-0.47.6 → specfact_cli-0.48.2}/src/specfact_cli/modules/upgrade/src/commands.py +0 -0
- {specfact_cli-0.47.6 → specfact_cli-0.48.2}/src/specfact_cli/parsers/__init__.py +0 -0
- {specfact_cli-0.47.6 → specfact_cli-0.48.2}/src/specfact_cli/parsers/persona_importer.py +0 -0
- {specfact_cli-0.47.6 → specfact_cli-0.48.2}/src/specfact_cli/registry/__init__.py +0 -0
- {specfact_cli-0.47.6 → specfact_cli-0.48.2}/src/specfact_cli/registry/alias_manager.py +0 -0
- {specfact_cli-0.47.6 → specfact_cli-0.48.2}/src/specfact_cli/registry/bootstrap.py +0 -0
- {specfact_cli-0.47.6 → specfact_cli-0.48.2}/src/specfact_cli/registry/bridge_registry.py +0 -0
- {specfact_cli-0.47.6 → specfact_cli-0.48.2}/src/specfact_cli/registry/crypto_validator.py +0 -0
- {specfact_cli-0.47.6 → specfact_cli-0.48.2}/src/specfact_cli/registry/custom_registries.py +0 -0
- {specfact_cli-0.47.6 → specfact_cli-0.48.2}/src/specfact_cli/registry/extension_registry.py +0 -0
- {specfact_cli-0.47.6 → specfact_cli-0.48.2}/src/specfact_cli/registry/help_cache.py +0 -0
- {specfact_cli-0.47.6 → specfact_cli-0.48.2}/src/specfact_cli/registry/marketplace_client.py +0 -0
- {specfact_cli-0.47.6 → specfact_cli-0.48.2}/src/specfact_cli/registry/metadata.py +0 -0
- {specfact_cli-0.47.6 → specfact_cli-0.48.2}/src/specfact_cli/registry/module_availability.py +0 -0
- {specfact_cli-0.47.6 → specfact_cli-0.48.2}/src/specfact_cli/registry/module_discovery.py +0 -0
- {specfact_cli-0.47.6 → specfact_cli-0.48.2}/src/specfact_cli/registry/module_grouping.py +0 -0
- {specfact_cli-0.47.6 → specfact_cli-0.48.2}/src/specfact_cli/registry/module_lifecycle.py +0 -0
- {specfact_cli-0.47.6 → specfact_cli-0.48.2}/src/specfact_cli/registry/module_packages.py +0 -0
- {specfact_cli-0.47.6 → specfact_cli-0.48.2}/src/specfact_cli/registry/module_security.py +0 -0
- {specfact_cli-0.47.6 → specfact_cli-0.48.2}/src/specfact_cli/registry/module_state.py +0 -0
- {specfact_cli-0.47.6 → specfact_cli-0.48.2}/src/specfact_cli/registry/registry.py +0 -0
- {specfact_cli-0.47.6 → specfact_cli-0.48.2}/src/specfact_cli/resources/semgrep/async.yml +0 -0
- {specfact_cli-0.47.6 → specfact_cli-0.48.2}/src/specfact_cli/resources/semgrep/code-quality.yml +0 -0
- {specfact_cli-0.47.6 → specfact_cli-0.48.2}/src/specfact_cli/resources/semgrep/feature-detection.yml +0 -0
- {specfact_cli-0.47.6 → specfact_cli-0.48.2}/src/specfact_cli/runtime.py +0 -0
- {specfact_cli-0.47.6 → specfact_cli-0.48.2}/src/specfact_cli/sync/__init__.py +0 -0
- {specfact_cli-0.47.6 → specfact_cli-0.48.2}/src/specfact_cli/sync/bridge_probe.py +0 -0
- {specfact_cli-0.47.6 → specfact_cli-0.48.2}/src/specfact_cli/sync/bridge_sync.py +0 -0
- {specfact_cli-0.47.6 → specfact_cli-0.48.2}/src/specfact_cli/sync/bridge_sync_openspec_md_parse.py +0 -0
- {specfact_cli-0.47.6 → specfact_cli-0.48.2}/src/specfact_cli/sync/bridge_sync_requirement_from_proposal.py +0 -0
- {specfact_cli-0.47.6 → specfact_cli-0.48.2}/src/specfact_cli/sync/bridge_sync_requirement_helpers.py +0 -0
- {specfact_cli-0.47.6 → specfact_cli-0.48.2}/src/specfact_cli/sync/bridge_sync_tasks_from_proposal.py +0 -0
- {specfact_cli-0.47.6 → specfact_cli-0.48.2}/src/specfact_cli/sync/bridge_sync_what_changes_format.py +0 -0
- {specfact_cli-0.47.6 → specfact_cli-0.48.2}/src/specfact_cli/sync/bridge_sync_write_openspec_from_proposal.py +0 -0
- {specfact_cli-0.47.6 → specfact_cli-0.48.2}/src/specfact_cli/sync/bridge_watch.py +0 -0
- {specfact_cli-0.47.6 → specfact_cli-0.48.2}/src/specfact_cli/sync/change_detector.py +0 -0
- {specfact_cli-0.47.6 → specfact_cli-0.48.2}/src/specfact_cli/sync/code_to_spec.py +0 -0
- {specfact_cli-0.47.6 → specfact_cli-0.48.2}/src/specfact_cli/sync/drift_detector.py +0 -0
- {specfact_cli-0.47.6 → specfact_cli-0.48.2}/src/specfact_cli/sync/repository_sync.py +0 -0
- {specfact_cli-0.47.6 → specfact_cli-0.48.2}/src/specfact_cli/sync/spec_to_code.py +0 -0
- {specfact_cli-0.47.6 → specfact_cli-0.48.2}/src/specfact_cli/sync/spec_to_tests.py +0 -0
- {specfact_cli-0.47.6 → specfact_cli-0.48.2}/src/specfact_cli/sync/watcher.py +0 -0
- {specfact_cli-0.47.6 → specfact_cli-0.48.2}/src/specfact_cli/sync/watcher_enhanced.py +0 -0
- {specfact_cli-0.47.6 → specfact_cli-0.48.2}/src/specfact_cli/templates/__init__.py +0 -0
- {specfact_cli-0.47.6 → specfact_cli-0.48.2}/src/specfact_cli/templates/defaults/defect_v1.yaml +0 -0
- {specfact_cli-0.47.6 → specfact_cli-0.48.2}/src/specfact_cli/templates/defaults/enabler_v1.yaml +0 -0
- {specfact_cli-0.47.6 → specfact_cli-0.48.2}/src/specfact_cli/templates/defaults/spike_v1.yaml +0 -0
- {specfact_cli-0.47.6 → specfact_cli-0.48.2}/src/specfact_cli/templates/defaults/user_story_v1.yaml +0 -0
- {specfact_cli-0.47.6 → specfact_cli-0.48.2}/src/specfact_cli/templates/frameworks/scrum/user_story_v1.yaml +0 -0
- {specfact_cli-0.47.6 → specfact_cli-0.48.2}/src/specfact_cli/templates/personas/product-owner/user_story_v1.yaml +0 -0
- {specfact_cli-0.47.6 → specfact_cli-0.48.2}/src/specfact_cli/templates/providers/ado/work_item_v1.yaml +0 -0
- {specfact_cli-0.47.6 → specfact_cli-0.48.2}/src/specfact_cli/templates/registry.py +0 -0
- {specfact_cli-0.47.6 → specfact_cli-0.48.2}/src/specfact_cli/templates/specification_templates.py +0 -0
- {specfact_cli-0.47.6 → specfact_cli-0.48.2}/src/specfact_cli/utils/__init__.py +0 -0
- {specfact_cli-0.47.6 → specfact_cli-0.48.2}/src/specfact_cli/utils/acceptance_criteria.py +0 -0
- {specfact_cli-0.47.6 → specfact_cli-0.48.2}/src/specfact_cli/utils/auth_tokens.py +0 -0
- {specfact_cli-0.47.6 → specfact_cli-0.48.2}/src/specfact_cli/utils/bundle_converters.py +0 -0
- {specfact_cli-0.47.6 → specfact_cli-0.48.2}/src/specfact_cli/utils/bundle_loader.py +0 -0
- {specfact_cli-0.47.6 → specfact_cli-0.48.2}/src/specfact_cli/utils/code_change_detector.py +0 -0
- {specfact_cli-0.47.6 → specfact_cli-0.48.2}/src/specfact_cli/utils/console.py +0 -0
- {specfact_cli-0.47.6 → specfact_cli-0.48.2}/src/specfact_cli/utils/content_sanitizer.py +0 -0
- {specfact_cli-0.47.6 → specfact_cli-0.48.2}/src/specfact_cli/utils/context_detection.py +0 -0
- {specfact_cli-0.47.6 → specfact_cli-0.48.2}/src/specfact_cli/utils/contract_predicates.py +0 -0
- {specfact_cli-0.47.6 → specfact_cli-0.48.2}/src/specfact_cli/utils/enrichment_context.py +0 -0
- {specfact_cli-0.47.6 → specfact_cli-0.48.2}/src/specfact_cli/utils/enrichment_parser.py +0 -0
- {specfact_cli-0.47.6 → specfact_cli-0.48.2}/src/specfact_cli/utils/env_manager.py +0 -0
- {specfact_cli-0.47.6 → specfact_cli-0.48.2}/src/specfact_cli/utils/feature_keys.py +0 -0
- {specfact_cli-0.47.6 → specfact_cli-0.48.2}/src/specfact_cli/utils/git.py +0 -0
- {specfact_cli-0.47.6 → specfact_cli-0.48.2}/src/specfact_cli/utils/github_annotations.py +0 -0
- {specfact_cli-0.47.6 → specfact_cli-0.48.2}/src/specfact_cli/utils/icontract_helpers.py +0 -0
- {specfact_cli-0.47.6 → specfact_cli-0.48.2}/src/specfact_cli/utils/ide_setup.py +0 -0
- {specfact_cli-0.47.6 → specfact_cli-0.48.2}/src/specfact_cli/utils/incremental_check.py +0 -0
- {specfact_cli-0.47.6 → specfact_cli-0.48.2}/src/specfact_cli/utils/metadata.py +0 -0
- {specfact_cli-0.47.6 → specfact_cli-0.48.2}/src/specfact_cli/utils/optional_deps.py +0 -0
- {specfact_cli-0.47.6 → specfact_cli-0.48.2}/src/specfact_cli/utils/performance.py +0 -0
- {specfact_cli-0.47.6 → specfact_cli-0.48.2}/src/specfact_cli/utils/persona_ownership.py +0 -0
- {specfact_cli-0.47.6 → specfact_cli-0.48.2}/src/specfact_cli/utils/progress.py +0 -0
- {specfact_cli-0.47.6 → specfact_cli-0.48.2}/src/specfact_cli/utils/progressive_disclosure.py +0 -0
- {specfact_cli-0.47.6 → specfact_cli-0.48.2}/src/specfact_cli/utils/project_artifact_write.py +0 -0
- {specfact_cli-0.47.6 → specfact_cli-0.48.2}/src/specfact_cli/utils/prompts.py +0 -0
- {specfact_cli-0.47.6 → specfact_cli-0.48.2}/src/specfact_cli/utils/sdd_discovery.py +0 -0
- {specfact_cli-0.47.6 → specfact_cli-0.48.2}/src/specfact_cli/utils/source_scanner.py +0 -0
- {specfact_cli-0.47.6 → specfact_cli-0.48.2}/src/specfact_cli/utils/startup_checks.py +0 -0
- {specfact_cli-0.47.6 → specfact_cli-0.48.2}/src/specfact_cli/utils/structure.py +0 -0
- {specfact_cli-0.47.6 → specfact_cli-0.48.2}/src/specfact_cli/utils/structured_io.py +0 -0
- {specfact_cli-0.47.6 → specfact_cli-0.48.2}/src/specfact_cli/utils/terminal.py +0 -0
- {specfact_cli-0.47.6 → specfact_cli-0.48.2}/src/specfact_cli/utils/yaml_utils.py +0 -0
- {specfact_cli-0.47.6 → specfact_cli-0.48.2}/src/specfact_cli/validation/__init__.py +0 -0
- {specfact_cli-0.47.6 → specfact_cli-0.48.2}/src/specfact_cli/validation/command_audit.py +0 -0
- {specfact_cli-0.47.6 → specfact_cli-0.48.2}/src/specfact_cli/validators/__init__.py +0 -0
- {specfact_cli-0.47.6 → specfact_cli-0.48.2}/src/specfact_cli/validators/agile_validation.py +0 -0
- {specfact_cli-0.47.6 → specfact_cli-0.48.2}/src/specfact_cli/validators/change_proposal_integration.py +0 -0
- {specfact_cli-0.47.6 → specfact_cli-0.48.2}/src/specfact_cli/validators/cli_first_validator.py +0 -0
- {specfact_cli-0.47.6 → specfact_cli-0.48.2}/src/specfact_cli/validators/contract_validator.py +0 -0
- {specfact_cli-0.47.6 → specfact_cli-0.48.2}/src/specfact_cli/validators/fsm.py +0 -0
- {specfact_cli-0.47.6 → specfact_cli-0.48.2}/src/specfact_cli/validators/repro_checker.py +0 -0
- {specfact_cli-0.47.6 → specfact_cli-0.48.2}/src/specfact_cli/validators/schema.py +0 -0
- {specfact_cli-0.47.6 → specfact_cli-0.48.2}/src/specfact_cli/validators/sidecar/__init__.py +0 -0
- {specfact_cli-0.47.6 → specfact_cli-0.48.2}/src/specfact_cli/validators/sidecar/contract_populator.py +0 -0
- {specfact_cli-0.47.6 → specfact_cli-0.48.2}/src/specfact_cli/validators/sidecar/crosshair_runner.py +0 -0
- {specfact_cli-0.47.6 → specfact_cli-0.48.2}/src/specfact_cli/validators/sidecar/crosshair_summary.py +0 -0
- {specfact_cli-0.47.6 → specfact_cli-0.48.2}/src/specfact_cli/validators/sidecar/dependency_installer.py +0 -0
- {specfact_cli-0.47.6 → specfact_cli-0.48.2}/src/specfact_cli/validators/sidecar/framework_detector.py +0 -0
- {specfact_cli-0.47.6 → specfact_cli-0.48.2}/src/specfact_cli/validators/sidecar/frameworks/__init__.py +0 -0
- {specfact_cli-0.47.6 → specfact_cli-0.48.2}/src/specfact_cli/validators/sidecar/frameworks/base.py +0 -0
- {specfact_cli-0.47.6 → specfact_cli-0.48.2}/src/specfact_cli/validators/sidecar/frameworks/django.py +0 -0
- {specfact_cli-0.47.6 → specfact_cli-0.48.2}/src/specfact_cli/validators/sidecar/frameworks/drf.py +0 -0
- {specfact_cli-0.47.6 → specfact_cli-0.48.2}/src/specfact_cli/validators/sidecar/frameworks/fastapi.py +0 -0
- {specfact_cli-0.47.6 → specfact_cli-0.48.2}/src/specfact_cli/validators/sidecar/frameworks/flask.py +0 -0
- {specfact_cli-0.47.6 → specfact_cli-0.48.2}/src/specfact_cli/validators/sidecar/harness_generator.py +0 -0
- {specfact_cli-0.47.6 → specfact_cli-0.48.2}/src/specfact_cli/validators/sidecar/models.py +0 -0
- {specfact_cli-0.47.6 → specfact_cli-0.48.2}/src/specfact_cli/validators/sidecar/orchestrator.py +0 -0
- {specfact_cli-0.47.6 → specfact_cli-0.48.2}/src/specfact_cli/validators/sidecar/specmatic_runner.py +0 -0
- {specfact_cli-0.47.6 → specfact_cli-0.48.2}/src/specfact_cli/validators/sidecar/unannotated_detector.py +0 -0
- {specfact_cli-0.47.6 → specfact_cli-0.48.2}/src/specfact_cli/versioning/__init__.py +0 -0
- {specfact_cli-0.47.6 → specfact_cli-0.48.2}/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.48.2
|
|
4
4
|
Summary: AI-bloat defense CLI for Python teams. Run deterministic code review, cleanup forecasts, and spec/contract evidence for AI-assisted and brownfield delivery.
|
|
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
|
|
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "specfact-cli"
|
|
7
|
-
version = "0.
|
|
7
|
+
version = "0.48.2"
|
|
8
8
|
description = "AI-bloat defense CLI for Python teams. Run deterministic code review, cleanup forecasts, and spec/contract evidence for AI-assisted and brownfield delivery."
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.11"
|
|
@@ -234,6 +234,8 @@ format = "ruff check . --fix && ruff format ."
|
|
|
234
234
|
# Code scanning (Semgrep)
|
|
235
235
|
# Semgrep 1.38+ deprecated implicit .semgrep.yml discovery; always pass --config explicitly.
|
|
236
236
|
# Script must not be named `semgrep` (Hatch treats that as circular expansion with the semgrep CLI).
|
|
237
|
+
semgrep-sast = "semgrep scan --config tools/semgrep/sast.yml {args}"
|
|
238
|
+
semgrep-sast-gate = "python scripts/semgrep_sast_gate.py {args}"
|
|
237
239
|
semgrep-full = "semgrep --config tools/semgrep {args}"
|
|
238
240
|
scan = "semgrep --config tools/semgrep/async.yml {args}"
|
|
239
241
|
scan-all = "semgrep --config tools/semgrep/async.yml ."
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"modules": [
|
|
3
3
|
{
|
|
4
|
-
"checksum_sha256": "
|
|
5
|
-
"download_url": "https://github.com/nold-ai/specfact-cli/releases/download/init-0.1.
|
|
4
|
+
"checksum_sha256": "2ffe401e068883086f2df2beed8e9d328db9f0802deb3c69212b0594bfa1be75",
|
|
5
|
+
"download_url": "https://github.com/nold-ai/specfact-cli/releases/download/init-0.1.41.tar.gz",
|
|
6
6
|
"id": "init",
|
|
7
|
-
"latest_version": "0.1.
|
|
7
|
+
"latest_version": "0.1.41"
|
|
8
8
|
},
|
|
9
9
|
{
|
|
10
10
|
"checksum_sha256": "35c592374b209224af9a826c21efa66947cc16bf680b20ee0463f44bee4f9b59",
|
|
@@ -13,10 +13,10 @@
|
|
|
13
13
|
"latest_version": "0.1.21"
|
|
14
14
|
},
|
|
15
15
|
{
|
|
16
|
-
"checksum_sha256": "
|
|
17
|
-
"download_url": "https://github.com/nold-ai/specfact-cli/releases/download/module-registry-0.1.
|
|
16
|
+
"checksum_sha256": "2552567a090d15826d8439ffb9242e0bf002bbe92296bc894e17ca0ba6a773f8",
|
|
17
|
+
"download_url": "https://github.com/nold-ai/specfact-cli/releases/download/module-registry-0.1.32.tar.gz",
|
|
18
18
|
"id": "module-registry",
|
|
19
|
-
"latest_version": "0.1.
|
|
19
|
+
"latest_version": "0.1.32"
|
|
20
20
|
},
|
|
21
21
|
{
|
|
22
22
|
"id": "bundle-mapper",
|
|
@@ -1986,7 +1986,7 @@ class AdoAdapter(BridgeAdapter, BacklogAdapterMixin, BacklogAdapter):
|
|
|
1986
1986
|
return cast(
|
|
1987
1987
|
Any,
|
|
1988
1988
|
self._request_with_retry(
|
|
1989
|
-
lambda: requests.post(url, **request_kwargs),
|
|
1989
|
+
lambda: requests.post(url, **request_kwargs), # nosec B113 - request_kwargs always includes timeout.
|
|
1990
1990
|
retry_on_ambiguous_transport=retry_on_ambiguous_transport,
|
|
1991
1991
|
),
|
|
1992
1992
|
)
|
|
@@ -2129,7 +2129,7 @@ class AdoAdapter(BridgeAdapter, BacklogAdapterMixin, BacklogAdapter):
|
|
|
2129
2129
|
change_id_escaped = change_id.replace("'", "''")
|
|
2130
2130
|
wiql = {
|
|
2131
2131
|
"query": (
|
|
2132
|
-
"Select [System.Id] From WorkItems "
|
|
2132
|
+
"Select [System.Id] From WorkItems " # nosec B608 - WIQL uses escaped literals, not SQL.
|
|
2133
2133
|
f"Where [System.TeamProject] = '{project_escaped}' "
|
|
2134
2134
|
f"And [System.Description] Contains 'OpenSpec Change Proposal: `{change_id_escaped}`'"
|
|
2135
2135
|
)
|
|
@@ -90,7 +90,7 @@ class PersonaExporter:
|
|
|
90
90
|
self.project_templates_dir = project_templates_dir
|
|
91
91
|
|
|
92
92
|
# Create Jinja2 environment with fallback support
|
|
93
|
-
self.env = Environment(
|
|
93
|
+
self.env = Environment( # nosec B701 - exports trusted Markdown templates, not HTML.
|
|
94
94
|
loader=FileSystemLoader(
|
|
95
95
|
[str(self.templates_dir)] + ([str(self.project_templates_dir)] if project_templates_dir else [])
|
|
96
96
|
),
|
|
@@ -33,7 +33,7 @@ class PlanGenerator:
|
|
|
33
33
|
templates_dir = Path(__file__).parent.parent.parent.parent / "resources" / "templates"
|
|
34
34
|
|
|
35
35
|
self.templates_dir = Path(templates_dir)
|
|
36
|
-
self.env = Environment(
|
|
36
|
+
self.env = Environment( # nosec B701 - generates trusted text/YAML workflow templates, not HTML.
|
|
37
37
|
loader=FileSystemLoader(self.templates_dir),
|
|
38
38
|
# Must be False: trim_blocks removes the newline after {% endraw %}, merging `if:` with `run:`.
|
|
39
39
|
trim_blocks=False,
|
{specfact_cli-0.47.6 → specfact_cli-0.48.2}/src/specfact_cli/generators/protocol_generator.py
RENAMED
|
@@ -32,7 +32,7 @@ class ProtocolGenerator:
|
|
|
32
32
|
templates_dir = Path(__file__).parent.parent.parent.parent / "resources" / "templates"
|
|
33
33
|
|
|
34
34
|
self.templates_dir = Path(templates_dir)
|
|
35
|
-
self.env = Environment(
|
|
35
|
+
self.env = Environment( # nosec B701 - generates trusted protocol Markdown, not HTML.
|
|
36
36
|
loader=FileSystemLoader(self.templates_dir),
|
|
37
37
|
trim_blocks=True,
|
|
38
38
|
lstrip_blocks=True,
|
|
@@ -43,7 +43,7 @@ class ReportGenerator:
|
|
|
43
43
|
templates_dir = Path(__file__).parent.parent.parent.parent / "resources" / "templates"
|
|
44
44
|
|
|
45
45
|
self.templates_dir = Path(templates_dir)
|
|
46
|
-
self.env = Environment(
|
|
46
|
+
self.env = Environment( # nosec B701 - generates trusted validation reports, not HTML.
|
|
47
47
|
loader=FileSystemLoader(self.templates_dir),
|
|
48
48
|
trim_blocks=True,
|
|
49
49
|
lstrip_blocks=True,
|
{specfact_cli-0.47.6 → specfact_cli-0.48.2}/src/specfact_cli/generators/workflow_generator.py
RENAMED
|
@@ -38,7 +38,7 @@ class WorkflowGenerator:
|
|
|
38
38
|
templates_dir = Path(__file__).parent.parent.parent.parent / "resources" / "templates"
|
|
39
39
|
|
|
40
40
|
self.templates_dir = Path(templates_dir)
|
|
41
|
-
self.env = Environment(
|
|
41
|
+
self.env = Environment( # nosec B701 - generates trusted text/YAML workflow templates, not HTML.
|
|
42
42
|
loader=FileSystemLoader(self.templates_dir),
|
|
43
43
|
# Must be False: trim_blocks removes the newline after {% endraw %}, merging `if:` with `run:`.
|
|
44
44
|
trim_blocks=False,
|
{specfact_cli-0.47.6 → specfact_cli-0.48.2}/src/specfact_cli/modules/init/module-package.yaml
RENAMED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
name: init
|
|
2
|
-
version: 0.1.
|
|
2
|
+
version: 0.1.41
|
|
3
3
|
commands:
|
|
4
4
|
- init
|
|
5
5
|
category: core
|
|
@@ -17,5 +17,5 @@ publisher:
|
|
|
17
17
|
description: Initialize SpecFact workspace and bootstrap local configuration.
|
|
18
18
|
license: Apache-2.0
|
|
19
19
|
integrity:
|
|
20
|
-
checksum: sha256:
|
|
21
|
-
signature:
|
|
20
|
+
checksum: sha256:7ad9c0b623f52a55b45f35adf9576bea1fd47edcd3005ed2354188a6a6ea20f5
|
|
21
|
+
signature: 7ED3ulYnu2VsJvPN/rEVLFHdVdBoiqJg1a8kR1FpWTmz9ZILrT/+NYq3CtnwGIvZscD/8CEBLBqKt1+0fCqZCQ==
|
|
@@ -48,14 +48,7 @@ from specfact_cli.utils.ide_setup import (
|
|
|
48
48
|
)
|
|
49
49
|
|
|
50
50
|
|
|
51
|
-
VALID_PROFILES: frozenset[str] = frozenset(
|
|
52
|
-
{
|
|
53
|
-
"solo-developer",
|
|
54
|
-
"backlog-team",
|
|
55
|
-
"api-first-team",
|
|
56
|
-
"enterprise-full-stack",
|
|
57
|
-
}
|
|
58
|
-
)
|
|
51
|
+
VALID_PROFILES: frozenset[str] = frozenset(first_run_selection.get_valid_profile_names())
|
|
59
52
|
PROFILE_BUNDLES: dict[str, list[str]] = first_run_selection.PROFILE_PRESETS
|
|
60
53
|
|
|
61
54
|
install_bundles_for_init = first_run_selection.install_bundles_for_init
|
|
@@ -709,6 +702,21 @@ def init_ide(
|
|
|
709
702
|
console.print(f"[green]Updated VS Code settings:[/green] {settings_path}")
|
|
710
703
|
|
|
711
704
|
|
|
705
|
+
def _write_profile_config_or_exit(repo_path: Path, profile: str) -> None:
|
|
706
|
+
try:
|
|
707
|
+
first_run_selection.write_profile_config(repo_path, profile)
|
|
708
|
+
except ValueError as e:
|
|
709
|
+
console.print(f"[red]Error:[/red] {e}")
|
|
710
|
+
raise typer.Exit(1) from e
|
|
711
|
+
|
|
712
|
+
|
|
713
|
+
def _apply_explicit_profile_or_install(repo_path: Path, profile: str | None, install: str | None) -> list[str]:
|
|
714
|
+
enabled_module_ids = _apply_profile_or_install_bundles(profile, install)
|
|
715
|
+
if profile is not None:
|
|
716
|
+
_write_profile_config_or_exit(repo_path, profile)
|
|
717
|
+
return enabled_module_ids
|
|
718
|
+
|
|
719
|
+
|
|
712
720
|
@app.callback(invoke_without_command=True)
|
|
713
721
|
@require(lambda repo: _is_valid_repo_path(repo), "Repo path must exist and be directory")
|
|
714
722
|
@ensure(lambda result: result is None, "Command should return None")
|
|
@@ -725,7 +733,10 @@ def init(
|
|
|
725
733
|
profile: str | None = typer.Option(
|
|
726
734
|
None,
|
|
727
735
|
"--profile",
|
|
728
|
-
help=
|
|
736
|
+
help=(
|
|
737
|
+
"Validation tier or legacy workflow preset: solo, startup, mid_size, enterprise, "
|
|
738
|
+
"solo-developer, backlog-team, api-first-team, enterprise-full-stack"
|
|
739
|
+
),
|
|
729
740
|
),
|
|
730
741
|
install: str | None = typer.Option(
|
|
731
742
|
None,
|
|
@@ -750,7 +761,7 @@ def init(
|
|
|
750
761
|
|
|
751
762
|
enabled_module_ids: list[str] = []
|
|
752
763
|
if profile is not None or install is not None:
|
|
753
|
-
enabled_module_ids =
|
|
764
|
+
enabled_module_ids = _apply_explicit_profile_or_install(repo_path, profile, install)
|
|
754
765
|
elif is_first_run(user_root=INIT_USER_MODULES_ROOT) and is_non_interactive():
|
|
755
766
|
console.print(
|
|
756
767
|
"[red]Error:[/red] In CI/CD (non-interactive) mode, first-run init requires "
|
{specfact_cli-0.47.6 → specfact_cli-0.48.2}/src/specfact_cli/modules/init/src/first_run_selection.py
RENAMED
|
@@ -3,10 +3,13 @@
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
5
|
import os
|
|
6
|
+
from collections.abc import Mapping
|
|
7
|
+
from copy import deepcopy
|
|
6
8
|
from dataclasses import dataclass
|
|
7
9
|
from pathlib import Path
|
|
8
|
-
from typing import Any
|
|
10
|
+
from typing import Any, cast
|
|
9
11
|
|
|
12
|
+
import yaml
|
|
10
13
|
from beartype import beartype
|
|
11
14
|
from icontract import ensure, require
|
|
12
15
|
|
|
@@ -25,8 +28,118 @@ PROFILE_PRESETS: dict[str, list[str]] = {
|
|
|
25
28
|
"specfact-spec",
|
|
26
29
|
"specfact-govern",
|
|
27
30
|
],
|
|
31
|
+
"solo": ["specfact-codebase", "specfact-code-review"],
|
|
32
|
+
"startup": ["specfact-project", "specfact-backlog", "specfact-codebase", "specfact-code-review"],
|
|
33
|
+
"mid_size": ["specfact-project", "specfact-backlog", "specfact-codebase", "specfact-spec", "specfact-code-review"],
|
|
34
|
+
"enterprise": [
|
|
35
|
+
"specfact-project",
|
|
36
|
+
"specfact-backlog",
|
|
37
|
+
"specfact-codebase",
|
|
38
|
+
"specfact-spec",
|
|
39
|
+
"specfact-govern",
|
|
40
|
+
"specfact-code-review",
|
|
41
|
+
],
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
VALIDATION_TIER_PROFILES: tuple[str, ...] = ("solo", "startup", "mid_size", "enterprise")
|
|
45
|
+
|
|
46
|
+
LEGACY_PROFILE_TO_VALIDATION_TIER: dict[str, str] = {
|
|
47
|
+
"solo-developer": "solo",
|
|
48
|
+
"backlog-team": "startup",
|
|
49
|
+
"api-first-team": "mid_size",
|
|
50
|
+
"enterprise-full-stack": "enterprise",
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def _canonical_module_ids(bundle_ids: list[str]) -> list[str]:
|
|
55
|
+
return [f"nold-ai/{bundle_id}" for bundle_id in bundle_ids]
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
_PROFILE_DEFAULTS: dict[str, dict[str, Any]] = {
|
|
59
|
+
"solo": {
|
|
60
|
+
"profile": "solo",
|
|
61
|
+
"validation": {
|
|
62
|
+
"severity": "advisory",
|
|
63
|
+
"policy_mode": "advisory",
|
|
64
|
+
"evidence_persistence": "local",
|
|
65
|
+
},
|
|
66
|
+
"clean_code": {
|
|
67
|
+
"mode": "advisory",
|
|
68
|
+
},
|
|
69
|
+
"modules": {
|
|
70
|
+
"enabled": _canonical_module_ids(PROFILE_PRESETS["solo"]),
|
|
71
|
+
},
|
|
72
|
+
"requirements_schema": {
|
|
73
|
+
"required_fields": ["id", "title", "acceptance"],
|
|
74
|
+
},
|
|
75
|
+
},
|
|
76
|
+
"startup": {
|
|
77
|
+
"profile": "startup",
|
|
78
|
+
"validation": {
|
|
79
|
+
"severity": "mixed",
|
|
80
|
+
"policy_mode": "mixed",
|
|
81
|
+
"evidence_persistence": "repo",
|
|
82
|
+
},
|
|
83
|
+
"clean_code": {
|
|
84
|
+
"mode": "advisory_then_mixed",
|
|
85
|
+
},
|
|
86
|
+
"modules": {
|
|
87
|
+
"enabled": _canonical_module_ids(PROFILE_PRESETS["startup"]),
|
|
88
|
+
},
|
|
89
|
+
"requirements_schema": {
|
|
90
|
+
"required_fields": ["id", "title", "owner", "acceptance"],
|
|
91
|
+
},
|
|
92
|
+
},
|
|
93
|
+
"mid_size": {
|
|
94
|
+
"profile": "mid_size",
|
|
95
|
+
"validation": {
|
|
96
|
+
"severity": "mixed",
|
|
97
|
+
"policy_mode": "mixed",
|
|
98
|
+
"evidence_persistence": "repo",
|
|
99
|
+
},
|
|
100
|
+
"clean_code": {
|
|
101
|
+
"mode": "mixed",
|
|
102
|
+
},
|
|
103
|
+
"modules": {
|
|
104
|
+
"enabled": _canonical_module_ids(PROFILE_PRESETS["mid_size"]),
|
|
105
|
+
},
|
|
106
|
+
"requirements_schema": {
|
|
107
|
+
"required_fields": ["id", "title", "owner", "acceptance", "trace_links"],
|
|
108
|
+
},
|
|
109
|
+
},
|
|
110
|
+
"enterprise": {
|
|
111
|
+
"profile": "enterprise",
|
|
112
|
+
"validation": {
|
|
113
|
+
"severity": "hard",
|
|
114
|
+
"policy_mode": "hard",
|
|
115
|
+
"evidence_persistence": "required",
|
|
116
|
+
},
|
|
117
|
+
"clean_code": {
|
|
118
|
+
"mode": "hard",
|
|
119
|
+
},
|
|
120
|
+
"modules": {
|
|
121
|
+
"enabled": _canonical_module_ids(PROFILE_PRESETS["enterprise"]),
|
|
122
|
+
},
|
|
123
|
+
"requirements_schema": {
|
|
124
|
+
"required_fields": [
|
|
125
|
+
"id",
|
|
126
|
+
"title",
|
|
127
|
+
"owner",
|
|
128
|
+
"acceptance",
|
|
129
|
+
"trace_links",
|
|
130
|
+
"risk_classification",
|
|
131
|
+
"exception_evidence",
|
|
132
|
+
],
|
|
133
|
+
},
|
|
134
|
+
},
|
|
28
135
|
}
|
|
29
136
|
|
|
137
|
+
_RESERVED_CONFIG_KEYS: frozenset[str] = frozenset({"source_annotations", "profile_warnings"})
|
|
138
|
+
_PROFILE_GENERATED_CONFIG_KEYS: frozenset[str] = frozenset(
|
|
139
|
+
{"validation", "clean_code", "modules", "requirements_schema"}
|
|
140
|
+
)
|
|
141
|
+
_POLICY_STRENGTH: dict[str, int] = {"advisory": 1, "advisory_then_mixed": 2, "mixed": 3, "hard": 4}
|
|
142
|
+
|
|
30
143
|
_INSTALL_ALL_BUNDLES: tuple[str, ...] = (
|
|
31
144
|
"specfact-project",
|
|
32
145
|
"specfact-backlog",
|
|
@@ -53,6 +166,8 @@ BUNDLE_ALIAS_TO_CANONICAL: dict[str, str] = {
|
|
|
53
166
|
"backlog": "specfact-backlog",
|
|
54
167
|
"codebase": "specfact-codebase",
|
|
55
168
|
"code": "specfact-codebase",
|
|
169
|
+
"code-review": "specfact-code-review",
|
|
170
|
+
"code_review": "specfact-code-review",
|
|
56
171
|
"spec": "specfact-spec",
|
|
57
172
|
"govern": "specfact-govern",
|
|
58
173
|
}
|
|
@@ -83,6 +198,15 @@ BUNDLE_DISPLAY: dict[str, str] = {
|
|
|
83
198
|
}
|
|
84
199
|
|
|
85
200
|
|
|
201
|
+
@dataclass(frozen=True)
|
|
202
|
+
class ResolvedProfileConfig:
|
|
203
|
+
"""Resolved profile config with source annotations for each winning value."""
|
|
204
|
+
|
|
205
|
+
values: dict[str, Any]
|
|
206
|
+
sources: dict[str, Any]
|
|
207
|
+
warnings: list[str]
|
|
208
|
+
|
|
209
|
+
|
|
86
210
|
def _emit_init_bundle_progress() -> bool:
|
|
87
211
|
"""Return True when init should print progress (suppressed during pytest)."""
|
|
88
212
|
return os.environ.get("PYTEST_CURRENT_TEST") is None
|
|
@@ -107,6 +231,160 @@ def _expand_bundle_install_order(bundle_ids: list[str]) -> list[str]:
|
|
|
107
231
|
return to_install
|
|
108
232
|
|
|
109
233
|
|
|
234
|
+
def _normalize_profile_key(profile: str) -> str:
|
|
235
|
+
key = profile.strip().lower()
|
|
236
|
+
return "mid_size" if key in {"mid-size", "mid_size"} else key
|
|
237
|
+
|
|
238
|
+
|
|
239
|
+
@require(lambda profile: isinstance(profile, str) and profile.strip() != "", "profile must be non-empty string")
|
|
240
|
+
@ensure(lambda result: result in VALIDATION_TIER_PROFILES, "result must be a validation tier")
|
|
241
|
+
@beartype
|
|
242
|
+
def resolve_validation_tier(profile: str) -> str:
|
|
243
|
+
"""Map a validation tier or legacy workflow preset to a validation config tier."""
|
|
244
|
+
key = profile.strip().lower()
|
|
245
|
+
normalized = _normalize_profile_key(profile)
|
|
246
|
+
if normalized in VALIDATION_TIER_PROFILES:
|
|
247
|
+
return normalized
|
|
248
|
+
if key in LEGACY_PROFILE_TO_VALIDATION_TIER:
|
|
249
|
+
return LEGACY_PROFILE_TO_VALIDATION_TIER[key]
|
|
250
|
+
valid = ", ".join(get_valid_profile_names())
|
|
251
|
+
raise ValueError(f"Unknown profile {profile!r}. Valid profiles: {valid}")
|
|
252
|
+
|
|
253
|
+
|
|
254
|
+
def _source_tree(value: Any, source: str) -> Any:
|
|
255
|
+
if isinstance(value, Mapping):
|
|
256
|
+
return {str(key): _source_tree(child, source) for key, child in value.items()}
|
|
257
|
+
return source
|
|
258
|
+
|
|
259
|
+
|
|
260
|
+
def _merge_config_layer(target: dict[str, Any], sources: dict[str, Any], layer: Mapping[str, Any], source: str) -> None:
|
|
261
|
+
for raw_key, raw_value in layer.items():
|
|
262
|
+
key = str(raw_key)
|
|
263
|
+
if key in _RESERVED_CONFIG_KEYS:
|
|
264
|
+
continue
|
|
265
|
+
value = deepcopy(raw_value)
|
|
266
|
+
if isinstance(value, Mapping) and isinstance(target.get(key), dict):
|
|
267
|
+
existing_source = sources.get(key)
|
|
268
|
+
if not isinstance(existing_source, dict):
|
|
269
|
+
sources[key] = {}
|
|
270
|
+
_merge_config_layer(target[key], sources[key], value, source)
|
|
271
|
+
continue
|
|
272
|
+
target[key] = value
|
|
273
|
+
sources[key] = _source_tree(value, source)
|
|
274
|
+
|
|
275
|
+
|
|
276
|
+
def _extract_policy_value(layer: Mapping[str, Any] | None, key: str) -> str | None:
|
|
277
|
+
if not layer:
|
|
278
|
+
return None
|
|
279
|
+
validation = layer.get("validation")
|
|
280
|
+
if not isinstance(validation, Mapping):
|
|
281
|
+
return None
|
|
282
|
+
validation_values: dict[str, Any] = {str(raw_key): raw_value for raw_key, raw_value in validation.items()}
|
|
283
|
+
value = validation_values.get(key)
|
|
284
|
+
return value if isinstance(value, str) else None
|
|
285
|
+
|
|
286
|
+
|
|
287
|
+
def _local_policy_weakened(org_baseline: Mapping[str, Any] | None, developer_local: Mapping[str, Any] | None) -> bool:
|
|
288
|
+
for key in ("severity", "policy_mode"):
|
|
289
|
+
org_value = _extract_policy_value(org_baseline, key)
|
|
290
|
+
local_value = _extract_policy_value(developer_local, key)
|
|
291
|
+
if org_value is None or local_value is None:
|
|
292
|
+
continue
|
|
293
|
+
if _POLICY_STRENGTH.get(local_value, 0) < _POLICY_STRENGTH.get(org_value, 0):
|
|
294
|
+
return True
|
|
295
|
+
return False
|
|
296
|
+
|
|
297
|
+
|
|
298
|
+
@require(lambda profile: isinstance(profile, str) and profile.strip() != "", "profile must be non-empty string")
|
|
299
|
+
@ensure(lambda result: isinstance(result, ResolvedProfileConfig), "result must be a resolved profile config")
|
|
300
|
+
@beartype
|
|
301
|
+
def resolve_profile_config(
|
|
302
|
+
profile: str,
|
|
303
|
+
*,
|
|
304
|
+
org_baseline: Mapping[str, Any] | None = None,
|
|
305
|
+
repo_overlay: Mapping[str, Any] | None = None,
|
|
306
|
+
developer_local: Mapping[str, Any] | None = None,
|
|
307
|
+
) -> ResolvedProfileConfig:
|
|
308
|
+
"""Resolve profile defaults, org baseline, repo overlay, and developer-local config layers."""
|
|
309
|
+
tier = resolve_validation_tier(profile)
|
|
310
|
+
values: dict[str, Any] = {}
|
|
311
|
+
sources: dict[str, Any] = {}
|
|
312
|
+
_merge_config_layer(values, sources, _PROFILE_DEFAULTS[tier], f"profile:{tier}")
|
|
313
|
+
for layer, source in (
|
|
314
|
+
(org_baseline, "org_baseline"),
|
|
315
|
+
(repo_overlay, "repo_overlay"),
|
|
316
|
+
(developer_local, "developer_local"),
|
|
317
|
+
):
|
|
318
|
+
if layer:
|
|
319
|
+
_merge_config_layer(values, sources, layer, source)
|
|
320
|
+
|
|
321
|
+
warnings: list[str] = []
|
|
322
|
+
if _local_policy_weakened(org_baseline, developer_local):
|
|
323
|
+
warnings.append("developer_local weakens org validation policy")
|
|
324
|
+
return ResolvedProfileConfig(values=values, sources=sources, warnings=warnings)
|
|
325
|
+
|
|
326
|
+
|
|
327
|
+
def _read_yaml_mapping(path: Path) -> dict[str, Any]:
|
|
328
|
+
if not path.exists():
|
|
329
|
+
return {}
|
|
330
|
+
raw = yaml.safe_load(path.read_text(encoding="utf-8")) or {}
|
|
331
|
+
if not isinstance(raw, dict):
|
|
332
|
+
raise ValueError(f"Config file must contain a mapping: {path}")
|
|
333
|
+
return raw
|
|
334
|
+
|
|
335
|
+
|
|
336
|
+
def _source_tree_contains_profile(value: Any) -> bool:
|
|
337
|
+
if isinstance(value, str):
|
|
338
|
+
return value.startswith("profile:")
|
|
339
|
+
if isinstance(value, Mapping):
|
|
340
|
+
return any(_source_tree_contains_profile(child) for child in value.values())
|
|
341
|
+
return False
|
|
342
|
+
|
|
343
|
+
|
|
344
|
+
def _repo_overlay_without_generated_profile_values(raw_overlay: Mapping[str, Any]) -> dict[str, Any]:
|
|
345
|
+
overlay = {str(key): deepcopy(value) for key, value in raw_overlay.items()}
|
|
346
|
+
overlay.pop("profile", None)
|
|
347
|
+
source_annotations = overlay.pop("source_annotations", {})
|
|
348
|
+
overlay.pop("profile_warnings", None)
|
|
349
|
+
if not isinstance(source_annotations, Mapping):
|
|
350
|
+
return overlay
|
|
351
|
+
sources_by_key = {str(key): cast(Any, value) for key, value in source_annotations.items()}
|
|
352
|
+
for key in _PROFILE_GENERATED_CONFIG_KEYS:
|
|
353
|
+
if _source_tree_contains_profile(sources_by_key.get(key)):
|
|
354
|
+
overlay.pop(key, None)
|
|
355
|
+
return overlay
|
|
356
|
+
|
|
357
|
+
|
|
358
|
+
@require(lambda repo_path: isinstance(repo_path, Path), "repo_path must be Path")
|
|
359
|
+
@require(lambda profile: isinstance(profile, str) and profile.strip() != "", "profile must be non-empty string")
|
|
360
|
+
@ensure(lambda result: isinstance(result, ResolvedProfileConfig), "result must be a resolved profile config")
|
|
361
|
+
@beartype
|
|
362
|
+
def write_profile_config(repo_path: Path, profile: str) -> ResolvedProfileConfig:
|
|
363
|
+
"""Write `.specfact/config.yaml` for the selected profile with source annotations."""
|
|
364
|
+
specfact_dir = repo_path / ".specfact"
|
|
365
|
+
config_path = specfact_dir / "config.yaml"
|
|
366
|
+
local_path = specfact_dir / "config.local.yaml"
|
|
367
|
+
org_path = Path.home() / ".specfact" / "config.yaml"
|
|
368
|
+
|
|
369
|
+
repo_overlay = _repo_overlay_without_generated_profile_values(_read_yaml_mapping(config_path))
|
|
370
|
+
resolved = resolve_profile_config(
|
|
371
|
+
profile,
|
|
372
|
+
org_baseline=_read_yaml_mapping(org_path),
|
|
373
|
+
repo_overlay=repo_overlay,
|
|
374
|
+
developer_local=_read_yaml_mapping(local_path),
|
|
375
|
+
)
|
|
376
|
+
|
|
377
|
+
payload = deepcopy(resolved.values)
|
|
378
|
+
payload["profile"] = resolve_validation_tier(profile)
|
|
379
|
+
payload["source_annotations"] = resolved.sources
|
|
380
|
+
if resolved.warnings:
|
|
381
|
+
payload["profile_warnings"] = resolved.warnings
|
|
382
|
+
|
|
383
|
+
specfact_dir.mkdir(parents=True, exist_ok=True)
|
|
384
|
+
config_path.write_text(yaml.safe_dump(payload, sort_keys=False, allow_unicode=False), encoding="utf-8")
|
|
385
|
+
return resolved
|
|
386
|
+
|
|
387
|
+
|
|
110
388
|
@dataclass
|
|
111
389
|
class _InitBundleInstallDeps:
|
|
112
390
|
root: Path
|
|
@@ -235,7 +513,7 @@ def _process_one_bundle_install_row(bid: str, deps: _InitBundleInstallDeps) -> N
|
|
|
235
513
|
@beartype
|
|
236
514
|
def resolve_profile_bundles(profile: str) -> list[str]:
|
|
237
515
|
"""Resolve a profile name to the list of canonical bundle ids to install."""
|
|
238
|
-
key = profile
|
|
516
|
+
key = _normalize_profile_key(profile)
|
|
239
517
|
if key not in PROFILE_PRESETS:
|
|
240
518
|
valid = ", ".join(sorted(PROFILE_PRESETS))
|
|
241
519
|
raise ValueError(f"Unknown profile {profile!r}. Valid profiles: {valid}")
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
name: module-registry
|
|
2
|
-
version: 0.1.
|
|
2
|
+
version: 0.1.32
|
|
3
3
|
commands:
|
|
4
4
|
- module
|
|
5
5
|
category: core
|
|
@@ -17,5 +17,5 @@ publisher:
|
|
|
17
17
|
description: 'Manage modules: search, list, show, install, and upgrade.'
|
|
18
18
|
license: Apache-2.0
|
|
19
19
|
integrity:
|
|
20
|
-
checksum: sha256:
|
|
21
|
-
signature:
|
|
20
|
+
checksum: sha256:a707de59cb2a706bad96a30415b693df0905cc67edf9238c0e9d601cc5b6af70
|
|
21
|
+
signature: GREOnCQAXmZvwN0s+2T6wwhPFTYiDchy/V7uut9uMHZjevK7ZDBxbTPZJhGjb1ndX7pkaL+/TfyNOuh/9AUDDw==
|
{specfact_cli-0.47.6 → specfact_cli-0.48.2}/src/specfact_cli/modules/module_registry/src/commands.py
RENAMED
|
@@ -16,7 +16,7 @@ import typer
|
|
|
16
16
|
import yaml
|
|
17
17
|
from beartype import beartype
|
|
18
18
|
from click.exceptions import Exit as ClickExit
|
|
19
|
-
from icontract import require
|
|
19
|
+
from icontract import ensure, require
|
|
20
20
|
from packaging.version import InvalidVersion, Version
|
|
21
21
|
from rich.console import Console
|
|
22
22
|
from rich.table import Table
|
|
@@ -54,13 +54,21 @@ from specfact_cli.registry.module_packages import get_discovered_modules_for_sta
|
|
|
54
54
|
from specfact_cli.registry.module_security import ensure_publisher_trusted, is_official_publisher
|
|
55
55
|
from specfact_cli.registry.module_state import read_modules_state, write_modules_state
|
|
56
56
|
from specfact_cli.registry.registry import CommandRegistry
|
|
57
|
-
from specfact_cli.runtime import is_non_interactive
|
|
57
|
+
from specfact_cli.runtime import is_non_interactive, refresh_loaded_module_consoles
|
|
58
58
|
|
|
59
59
|
|
|
60
60
|
app = typer.Typer(help="Manage marketplace modules")
|
|
61
61
|
console = Console()
|
|
62
62
|
|
|
63
63
|
|
|
64
|
+
@app.callback()
|
|
65
|
+
@beartype
|
|
66
|
+
@ensure(lambda result: result is None, "module registry callback returns None")
|
|
67
|
+
def module_registry_callback() -> None:
|
|
68
|
+
"""Prepare invocation-local output streams for direct module command tests."""
|
|
69
|
+
refresh_loaded_module_consoles()
|
|
70
|
+
|
|
71
|
+
|
|
64
72
|
def _module_upgrade_show_spinner() -> bool:
|
|
65
73
|
"""Rich Live/spinner breaks some tests; mirror ``utils.progress`` test-mode detection."""
|
|
66
74
|
return os.environ.get("TEST_MODE") != "true" and os.environ.get("PYTEST_CURRENT_TEST") is None
|
|
@@ -386,10 +394,10 @@ def _install_one(module_id: str, params: _InstallOneParams) -> bool:
|
|
|
386
394
|
except Exception as exc:
|
|
387
395
|
console.print(f"[red]Failed installing {normalized}: {exc}[/red]")
|
|
388
396
|
return False
|
|
389
|
-
|
|
397
|
+
typer.echo(f"Installed {normalized} -> {installed_path}")
|
|
390
398
|
publisher = _publisher_from_module_id(normalized)
|
|
391
399
|
if is_official_publisher(publisher):
|
|
392
|
-
|
|
400
|
+
typer.echo(f"Verified: official ({publisher})")
|
|
393
401
|
return True
|
|
394
402
|
|
|
395
403
|
|
{specfact_cli-0.47.6 → specfact_cli-0.48.2}/src/specfact_cli/registry/dependency_resolver.py
RENAMED
|
@@ -127,15 +127,18 @@ def _collect_constraints(modules: list[ModulePackageMetadata]) -> list[str]:
|
|
|
127
127
|
seen: set[str] = set()
|
|
128
128
|
for meta in modules:
|
|
129
129
|
for d in meta.pip_dependencies or []:
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
130
|
+
normalized = d.strip()
|
|
131
|
+
if normalized and normalized not in seen:
|
|
132
|
+
constraints.append(normalized)
|
|
133
|
+
seen.add(normalized)
|
|
133
134
|
for vd in meta.pip_dependencies_versioned or []:
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
if
|
|
137
|
-
|
|
138
|
-
|
|
135
|
+
name = vd.name.strip()
|
|
136
|
+
spec = (vd.version_specifier or "").strip()
|
|
137
|
+
s = f"{name}{spec}" if spec else name
|
|
138
|
+
normalized = s.strip()
|
|
139
|
+
if normalized and normalized not in seen:
|
|
140
|
+
constraints.append(normalized)
|
|
141
|
+
seen.add(normalized)
|
|
139
142
|
return constraints
|
|
140
143
|
|
|
141
144
|
|
|
@@ -825,7 +825,7 @@ def _extract_marketplace_archive(archive_path: Path, extract_root: Path) -> None
|
|
|
825
825
|
try:
|
|
826
826
|
archive.extractall(path=extract_root, members=members, filter="data")
|
|
827
827
|
except TypeError:
|
|
828
|
-
archive.extractall(path=extract_root, members=members)
|
|
828
|
+
archive.extractall(path=extract_root, members=members) # nosec B202 - members were validated above.
|
|
829
829
|
|
|
830
830
|
|
|
831
831
|
def _load_first_extracted_module_manifest(extract_root: Path) -> tuple[Path, dict[str, Any]]:
|