specfact-cli 0.46.4__tar.gz → 0.46.16__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.4 → specfact_cli-0.46.16}/PKG-INFO +1 -1
- {specfact_cli-0.46.4 → specfact_cli-0.46.16}/pyproject.toml +2 -1
- {specfact_cli-0.46.4 → specfact_cli-0.46.16}/src/__init__.py +1 -1
- {specfact_cli-0.46.4 → specfact_cli-0.46.16}/src/specfact_cli/__init__.py +1 -1
- {specfact_cli-0.46.4 → specfact_cli-0.46.16}/src/specfact_cli/analyzers/code_analyzer.py +20 -0
- {specfact_cli-0.46.4 → specfact_cli-0.46.16}/src/specfact_cli/cli.py +127 -26
- {specfact_cli-0.46.4 → specfact_cli-0.46.16}/src/specfact_cli/modules/init/module-package.yaml +3 -3
- {specfact_cli-0.46.4 → specfact_cli-0.46.16}/src/specfact_cli/modules/init/src/commands.py +33 -13
- {specfact_cli-0.46.4 → specfact_cli-0.46.16}/src/specfact_cli/modules/module_registry/module-package.yaml +3 -3
- {specfact_cli-0.46.4 → specfact_cli-0.46.16}/src/specfact_cli/modules/module_registry/src/commands.py +72 -7
- {specfact_cli-0.46.4 → specfact_cli-0.46.16}/src/specfact_cli/modules/upgrade/module-package.yaml +2 -3
- {specfact_cli-0.46.4 → specfact_cli-0.46.16}/src/specfact_cli/modules/upgrade/src/commands.py +152 -87
- specfact_cli-0.46.16/src/specfact_cli/registry/module_availability.py +214 -0
- {specfact_cli-0.46.4 → specfact_cli-0.46.16}/src/specfact_cli/registry/module_discovery.py +123 -41
- {specfact_cli-0.46.4 → specfact_cli-0.46.16}/src/specfact_cli/registry/module_packages.py +22 -5
- {specfact_cli-0.46.4 → specfact_cli-0.46.16}/src/specfact_cli/utils/optional_deps.py +7 -1
- {specfact_cli-0.46.4 → specfact_cli-0.46.16}/.gitignore +0 -0
- {specfact_cli-0.46.4 → specfact_cli-0.46.16}/LICENSE +0 -0
- {specfact_cli-0.46.4 → specfact_cli-0.46.16}/README.md +0 -0
- {specfact_cli-0.46.4 → specfact_cli-0.46.16}/resources/bundled-module-registry/index.json +0 -0
- {specfact_cli-0.46.4 → specfact_cli-0.46.16}/resources/keys/README.md +0 -0
- {specfact_cli-0.46.4 → specfact_cli-0.46.16}/resources/keys/module-signing-public.pem +0 -0
- {specfact_cli-0.46.4 → specfact_cli-0.46.16}/resources/mappings/node-async.yaml +0 -0
- {specfact_cli-0.46.4 → specfact_cli-0.46.16}/resources/mappings/python-async.yaml +0 -0
- {specfact_cli-0.46.4 → specfact_cli-0.46.16}/resources/mappings/speckit-default.yaml +0 -0
- {specfact_cli-0.46.4 → specfact_cli-0.46.16}/resources/schemas/deviation.schema.json +0 -0
- {specfact_cli-0.46.4 → specfact_cli-0.46.16}/resources/schemas/plan.schema.json +0 -0
- {specfact_cli-0.46.4 → specfact_cli-0.46.16}/resources/schemas/protocol.schema.json +0 -0
- {specfact_cli-0.46.4 → specfact_cli-0.46.16}/resources/templates/github-action.yml.j2 +0 -0
- {specfact_cli-0.46.4 → specfact_cli-0.46.16}/resources/templates/persona/architect.md.j2 +0 -0
- {specfact_cli-0.46.4 → specfact_cli-0.46.16}/resources/templates/persona/developer.md.j2 +0 -0
- {specfact_cli-0.46.4 → specfact_cli-0.46.16}/resources/templates/persona/product-owner.md.j2 +0 -0
- {specfact_cli-0.46.4 → specfact_cli-0.46.16}/resources/templates/plan.bundle.yaml.j2 +0 -0
- {specfact_cli-0.46.4 → specfact_cli-0.46.16}/resources/templates/policies/kanban.yaml +0 -0
- {specfact_cli-0.46.4 → specfact_cli-0.46.16}/resources/templates/policies/mixed.yaml +0 -0
- {specfact_cli-0.46.4 → specfact_cli-0.46.16}/resources/templates/policies/safe.yaml +0 -0
- {specfact_cli-0.46.4 → specfact_cli-0.46.16}/resources/templates/policies/scrum.yaml +0 -0
- {specfact_cli-0.46.4 → specfact_cli-0.46.16}/resources/templates/pr-template.md.j2 +0 -0
- {specfact_cli-0.46.4 → specfact_cli-0.46.16}/resources/templates/protocol.yaml.j2 +0 -0
- {specfact_cli-0.46.4 → specfact_cli-0.46.16}/resources/templates/telemetry.yaml.example +0 -0
- {specfact_cli-0.46.4 → specfact_cli-0.46.16}/src/specfact_cli/__main__.py +0 -0
- {specfact_cli-0.46.4 → specfact_cli-0.46.16}/src/specfact_cli/adapters/__init__.py +0 -0
- {specfact_cli-0.46.4 → specfact_cli-0.46.16}/src/specfact_cli/adapters/ado.py +0 -0
- {specfact_cli-0.46.4 → specfact_cli-0.46.16}/src/specfact_cli/adapters/backlog_base.py +0 -0
- {specfact_cli-0.46.4 → specfact_cli-0.46.16}/src/specfact_cli/adapters/base.py +0 -0
- {specfact_cli-0.46.4 → specfact_cli-0.46.16}/src/specfact_cli/adapters/github.py +0 -0
- {specfact_cli-0.46.4 → specfact_cli-0.46.16}/src/specfact_cli/adapters/openspec.py +0 -0
- {specfact_cli-0.46.4 → specfact_cli-0.46.16}/src/specfact_cli/adapters/openspec_parser.py +0 -0
- {specfact_cli-0.46.4 → specfact_cli-0.46.16}/src/specfact_cli/adapters/registry.py +0 -0
- {specfact_cli-0.46.4 → specfact_cli-0.46.16}/src/specfact_cli/adapters/speckit.py +0 -0
- {specfact_cli-0.46.4 → specfact_cli-0.46.16}/src/specfact_cli/agents/__init__.py +0 -0
- {specfact_cli-0.46.4 → specfact_cli-0.46.16}/src/specfact_cli/agents/analyze_agent.py +0 -0
- {specfact_cli-0.46.4 → specfact_cli-0.46.16}/src/specfact_cli/agents/base.py +0 -0
- {specfact_cli-0.46.4 → specfact_cli-0.46.16}/src/specfact_cli/agents/plan_agent.py +0 -0
- {specfact_cli-0.46.4 → specfact_cli-0.46.16}/src/specfact_cli/agents/registry.py +0 -0
- {specfact_cli-0.46.4 → specfact_cli-0.46.16}/src/specfact_cli/agents/sync_agent.py +0 -0
- {specfact_cli-0.46.4 → specfact_cli-0.46.16}/src/specfact_cli/analyzers/__init__.py +0 -0
- {specfact_cli-0.46.4 → specfact_cli-0.46.16}/src/specfact_cli/analyzers/ambiguity_scanner.py +0 -0
- {specfact_cli-0.46.4 → specfact_cli-0.46.16}/src/specfact_cli/analyzers/constitution_evidence_extractor.py +0 -0
- {specfact_cli-0.46.4 → specfact_cli-0.46.16}/src/specfact_cli/analyzers/contract_extractor.py +0 -0
- {specfact_cli-0.46.4 → specfact_cli-0.46.16}/src/specfact_cli/analyzers/control_flow_analyzer.py +0 -0
- {specfact_cli-0.46.4 → specfact_cli-0.46.16}/src/specfact_cli/analyzers/graph_analyzer.py +0 -0
- {specfact_cli-0.46.4 → specfact_cli-0.46.16}/src/specfact_cli/analyzers/relationship_mapper.py +0 -0
- {specfact_cli-0.46.4 → specfact_cli-0.46.16}/src/specfact_cli/analyzers/requirement_extractor.py +0 -0
- {specfact_cli-0.46.4 → specfact_cli-0.46.16}/src/specfact_cli/analyzers/test_pattern_extractor.py +0 -0
- {specfact_cli-0.46.4 → specfact_cli-0.46.16}/src/specfact_cli/backlog/__init__.py +0 -0
- {specfact_cli-0.46.4 → specfact_cli-0.46.16}/src/specfact_cli/backlog/adapters/__init__.py +0 -0
- {specfact_cli-0.46.4 → specfact_cli-0.46.16}/src/specfact_cli/backlog/adapters/base.py +0 -0
- {specfact_cli-0.46.4 → specfact_cli-0.46.16}/src/specfact_cli/backlog/converter.py +0 -0
- {specfact_cli-0.46.4 → specfact_cli-0.46.16}/src/specfact_cli/backlog/filters.py +0 -0
- {specfact_cli-0.46.4 → specfact_cli-0.46.16}/src/specfact_cli/backlog/mappers/__init__.py +0 -0
- {specfact_cli-0.46.4 → specfact_cli-0.46.16}/src/specfact_cli/backlog/mappers/ado_mapper.py +0 -0
- {specfact_cli-0.46.4 → specfact_cli-0.46.16}/src/specfact_cli/backlog/mappers/base.py +0 -0
- {specfact_cli-0.46.4 → specfact_cli-0.46.16}/src/specfact_cli/backlog/mappers/github_mapper.py +0 -0
- {specfact_cli-0.46.4 → specfact_cli-0.46.16}/src/specfact_cli/backlog/mappers/template_config.py +0 -0
- {specfact_cli-0.46.4 → specfact_cli-0.46.16}/src/specfact_cli/commands/__init__.py +0 -0
- {specfact_cli-0.46.4 → specfact_cli-0.46.16}/src/specfact_cli/commands/_bundle_shim.py +0 -0
- {specfact_cli-0.46.4 → specfact_cli-0.46.16}/src/specfact_cli/commands/analyze.py +0 -0
- {specfact_cli-0.46.4 → specfact_cli-0.46.16}/src/specfact_cli/commands/contract_cmd.py +0 -0
- {specfact_cli-0.46.4 → specfact_cli-0.46.16}/src/specfact_cli/commands/drift.py +0 -0
- {specfact_cli-0.46.4 → specfact_cli-0.46.16}/src/specfact_cli/commands/enforce.py +0 -0
- {specfact_cli-0.46.4 → specfact_cli-0.46.16}/src/specfact_cli/commands/generate.py +0 -0
- {specfact_cli-0.46.4 → specfact_cli-0.46.16}/src/specfact_cli/commands/import_cmd.py +0 -0
- {specfact_cli-0.46.4 → specfact_cli-0.46.16}/src/specfact_cli/commands/init.py +0 -0
- {specfact_cli-0.46.4 → specfact_cli-0.46.16}/src/specfact_cli/commands/migrate.py +0 -0
- {specfact_cli-0.46.4 → specfact_cli-0.46.16}/src/specfact_cli/commands/plan.py +0 -0
- {specfact_cli-0.46.4 → specfact_cli-0.46.16}/src/specfact_cli/commands/project_cmd.py +0 -0
- {specfact_cli-0.46.4 → specfact_cli-0.46.16}/src/specfact_cli/commands/repro.py +0 -0
- {specfact_cli-0.46.4 → specfact_cli-0.46.16}/src/specfact_cli/commands/sdd.py +0 -0
- {specfact_cli-0.46.4 → specfact_cli-0.46.16}/src/specfact_cli/commands/spec.py +0 -0
- {specfact_cli-0.46.4 → specfact_cli-0.46.16}/src/specfact_cli/commands/sync.py +0 -0
- {specfact_cli-0.46.4 → specfact_cli-0.46.16}/src/specfact_cli/commands/update.py +0 -0
- {specfact_cli-0.46.4 → specfact_cli-0.46.16}/src/specfact_cli/commands/validate.py +0 -0
- {specfact_cli-0.46.4 → specfact_cli-0.46.16}/src/specfact_cli/common/__init__.py +0 -0
- {specfact_cli-0.46.4 → specfact_cli-0.46.16}/src/specfact_cli/common/bundle_factory.py +0 -0
- {specfact_cli-0.46.4 → specfact_cli-0.46.16}/src/specfact_cli/common/logger_setup.py +0 -0
- {specfact_cli-0.46.4 → specfact_cli-0.46.16}/src/specfact_cli/common/logging_utils.py +0 -0
- {specfact_cli-0.46.4 → specfact_cli-0.46.16}/src/specfact_cli/common/text_utils.py +0 -0
- {specfact_cli-0.46.4 → specfact_cli-0.46.16}/src/specfact_cli/common/utils.py +0 -0
- {specfact_cli-0.46.4 → specfact_cli-0.46.16}/src/specfact_cli/comparators/__init__.py +0 -0
- {specfact_cli-0.46.4 → specfact_cli-0.46.16}/src/specfact_cli/comparators/plan_comparator.py +0 -0
- {specfact_cli-0.46.4 → specfact_cli-0.46.16}/src/specfact_cli/contracts/__init__.py +0 -0
- {specfact_cli-0.46.4 → specfact_cli-0.46.16}/src/specfact_cli/contracts/crosshair_props.py +0 -0
- {specfact_cli-0.46.4 → specfact_cli-0.46.16}/src/specfact_cli/contracts/module_interface.py +0 -0
- {specfact_cli-0.46.4 → specfact_cli-0.46.16}/src/specfact_cli/enrichers/constitution_enricher.py +0 -0
- {specfact_cli-0.46.4 → specfact_cli-0.46.16}/src/specfact_cli/enrichers/plan_enricher.py +0 -0
- {specfact_cli-0.46.4 → specfact_cli-0.46.16}/src/specfact_cli/generators/__init__.py +0 -0
- {specfact_cli-0.46.4 → specfact_cli-0.46.16}/src/specfact_cli/generators/contract_generator.py +0 -0
- {specfact_cli-0.46.4 → specfact_cli-0.46.16}/src/specfact_cli/generators/openapi_extractor.py +0 -0
- {specfact_cli-0.46.4 → specfact_cli-0.46.16}/src/specfact_cli/generators/persona_exporter.py +0 -0
- {specfact_cli-0.46.4 → specfact_cli-0.46.16}/src/specfact_cli/generators/plan_generator.py +0 -0
- {specfact_cli-0.46.4 → specfact_cli-0.46.16}/src/specfact_cli/generators/protocol_generator.py +0 -0
- {specfact_cli-0.46.4 → specfact_cli-0.46.16}/src/specfact_cli/generators/report_generator.py +0 -0
- {specfact_cli-0.46.4 → specfact_cli-0.46.16}/src/specfact_cli/generators/task_generator.py +0 -0
- {specfact_cli-0.46.4 → specfact_cli-0.46.16}/src/specfact_cli/generators/test_to_openapi.py +0 -0
- {specfact_cli-0.46.4 → specfact_cli-0.46.16}/src/specfact_cli/generators/workflow_generator.py +0 -0
- {specfact_cli-0.46.4 → specfact_cli-0.46.16}/src/specfact_cli/groups/__init__.py +0 -0
- {specfact_cli-0.46.4 → specfact_cli-0.46.16}/src/specfact_cli/groups/codebase_group.py +0 -0
- {specfact_cli-0.46.4 → specfact_cli-0.46.16}/src/specfact_cli/groups/govern_group.py +0 -0
- {specfact_cli-0.46.4 → specfact_cli-0.46.16}/src/specfact_cli/groups/member_group.py +0 -0
- {specfact_cli-0.46.4 → specfact_cli-0.46.16}/src/specfact_cli/groups/project_group.py +0 -0
- {specfact_cli-0.46.4 → specfact_cli-0.46.16}/src/specfact_cli/groups/spec_group.py +0 -0
- {specfact_cli-0.46.4 → specfact_cli-0.46.16}/src/specfact_cli/importers/__init__.py +0 -0
- {specfact_cli-0.46.4 → specfact_cli-0.46.16}/src/specfact_cli/importers/speckit_converter.py +0 -0
- {specfact_cli-0.46.4 → specfact_cli-0.46.16}/src/specfact_cli/importers/speckit_scanner.py +0 -0
- {specfact_cli-0.46.4 → specfact_cli-0.46.16}/src/specfact_cli/integrations/__init__.py +0 -0
- {specfact_cli-0.46.4 → specfact_cli-0.46.16}/src/specfact_cli/integrations/specmatic.py +0 -0
- {specfact_cli-0.46.4 → specfact_cli-0.46.16}/src/specfact_cli/merge/__init__.py +0 -0
- {specfact_cli-0.46.4 → specfact_cli-0.46.16}/src/specfact_cli/merge/resolver.py +0 -0
- {specfact_cli-0.46.4 → specfact_cli-0.46.16}/src/specfact_cli/migrations/__init__.py +0 -0
- {specfact_cli-0.46.4 → specfact_cli-0.46.16}/src/specfact_cli/migrations/plan_migrator.py +0 -0
- {specfact_cli-0.46.4 → specfact_cli-0.46.16}/src/specfact_cli/models/__init__.py +0 -0
- {specfact_cli-0.46.4 → specfact_cli-0.46.16}/src/specfact_cli/models/backlog_item.py +0 -0
- {specfact_cli-0.46.4 → specfact_cli-0.46.16}/src/specfact_cli/models/bridge.py +0 -0
- {specfact_cli-0.46.4 → specfact_cli-0.46.16}/src/specfact_cli/models/capabilities.py +0 -0
- {specfact_cli-0.46.4 → specfact_cli-0.46.16}/src/specfact_cli/models/change.py +0 -0
- {specfact_cli-0.46.4 → specfact_cli-0.46.16}/src/specfact_cli/models/contract.py +0 -0
- {specfact_cli-0.46.4 → specfact_cli-0.46.16}/src/specfact_cli/models/deviation.py +0 -0
- {specfact_cli-0.46.4 → specfact_cli-0.46.16}/src/specfact_cli/models/dor_config.py +0 -0
- {specfact_cli-0.46.4 → specfact_cli-0.46.16}/src/specfact_cli/models/enforcement.py +0 -0
- {specfact_cli-0.46.4 → specfact_cli-0.46.16}/src/specfact_cli/models/module_package.py +0 -0
- {specfact_cli-0.46.4 → specfact_cli-0.46.16}/src/specfact_cli/models/persona_template.py +0 -0
- {specfact_cli-0.46.4 → specfact_cli-0.46.16}/src/specfact_cli/models/plan.py +0 -0
- {specfact_cli-0.46.4 → specfact_cli-0.46.16}/src/specfact_cli/models/project.py +0 -0
- {specfact_cli-0.46.4 → specfact_cli-0.46.16}/src/specfact_cli/models/protocol.py +0 -0
- {specfact_cli-0.46.4 → specfact_cli-0.46.16}/src/specfact_cli/models/quality.py +0 -0
- {specfact_cli-0.46.4 → specfact_cli-0.46.16}/src/specfact_cli/models/sdd.py +0 -0
- {specfact_cli-0.46.4 → specfact_cli-0.46.16}/src/specfact_cli/models/source_tracking.py +0 -0
- {specfact_cli-0.46.4 → specfact_cli-0.46.16}/src/specfact_cli/models/task.py +0 -0
- {specfact_cli-0.46.4 → specfact_cli-0.46.16}/src/specfact_cli/models/validation.py +0 -0
- {specfact_cli-0.46.4 → specfact_cli-0.46.16}/src/specfact_cli/modes/__init__.py +0 -0
- {specfact_cli-0.46.4 → specfact_cli-0.46.16}/src/specfact_cli/modes/detector.py +0 -0
- {specfact_cli-0.46.4 → specfact_cli-0.46.16}/src/specfact_cli/modes/router.py +0 -0
- {specfact_cli-0.46.4 → specfact_cli-0.46.16}/src/specfact_cli/modules/__init__.py +0 -0
- {specfact_cli-0.46.4 → specfact_cli-0.46.16}/src/specfact_cli/modules/_bundle_import.py +0 -0
- {specfact_cli-0.46.4 → specfact_cli-0.46.16}/src/specfact_cli/modules/init/src/__init__.py +0 -0
- {specfact_cli-0.46.4 → specfact_cli-0.46.16}/src/specfact_cli/modules/init/src/app.py +0 -0
- {specfact_cli-0.46.4 → specfact_cli-0.46.16}/src/specfact_cli/modules/init/src/first_run_selection.py +0 -0
- {specfact_cli-0.46.4 → specfact_cli-0.46.16}/src/specfact_cli/modules/module_io_shim.py +0 -0
- {specfact_cli-0.46.4 → specfact_cli-0.46.16}/src/specfact_cli/modules/module_registry/src/__init__.py +0 -0
- {specfact_cli-0.46.4 → specfact_cli-0.46.16}/src/specfact_cli/modules/module_registry/src/app.py +0 -0
- {specfact_cli-0.46.4 → specfact_cli-0.46.16}/src/specfact_cli/modules/upgrade/src/__init__.py +0 -0
- {specfact_cli-0.46.4 → specfact_cli-0.46.16}/src/specfact_cli/modules/upgrade/src/app.py +0 -0
- {specfact_cli-0.46.4 → specfact_cli-0.46.16}/src/specfact_cli/parsers/__init__.py +0 -0
- {specfact_cli-0.46.4 → specfact_cli-0.46.16}/src/specfact_cli/parsers/persona_importer.py +0 -0
- {specfact_cli-0.46.4 → specfact_cli-0.46.16}/src/specfact_cli/registry/__init__.py +0 -0
- {specfact_cli-0.46.4 → specfact_cli-0.46.16}/src/specfact_cli/registry/alias_manager.py +0 -0
- {specfact_cli-0.46.4 → specfact_cli-0.46.16}/src/specfact_cli/registry/bootstrap.py +0 -0
- {specfact_cli-0.46.4 → specfact_cli-0.46.16}/src/specfact_cli/registry/bridge_registry.py +0 -0
- {specfact_cli-0.46.4 → specfact_cli-0.46.16}/src/specfact_cli/registry/crypto_validator.py +0 -0
- {specfact_cli-0.46.4 → specfact_cli-0.46.16}/src/specfact_cli/registry/custom_registries.py +0 -0
- {specfact_cli-0.46.4 → specfact_cli-0.46.16}/src/specfact_cli/registry/dependency_resolver.py +0 -0
- {specfact_cli-0.46.4 → specfact_cli-0.46.16}/src/specfact_cli/registry/extension_registry.py +0 -0
- {specfact_cli-0.46.4 → specfact_cli-0.46.16}/src/specfact_cli/registry/help_cache.py +0 -0
- {specfact_cli-0.46.4 → specfact_cli-0.46.16}/src/specfact_cli/registry/marketplace_client.py +0 -0
- {specfact_cli-0.46.4 → specfact_cli-0.46.16}/src/specfact_cli/registry/metadata.py +0 -0
- {specfact_cli-0.46.4 → specfact_cli-0.46.16}/src/specfact_cli/registry/module_grouping.py +0 -0
- {specfact_cli-0.46.4 → specfact_cli-0.46.16}/src/specfact_cli/registry/module_installer.py +0 -0
- {specfact_cli-0.46.4 → specfact_cli-0.46.16}/src/specfact_cli/registry/module_lifecycle.py +0 -0
- {specfact_cli-0.46.4 → specfact_cli-0.46.16}/src/specfact_cli/registry/module_security.py +0 -0
- {specfact_cli-0.46.4 → specfact_cli-0.46.16}/src/specfact_cli/registry/module_state.py +0 -0
- {specfact_cli-0.46.4 → specfact_cli-0.46.16}/src/specfact_cli/registry/registry.py +0 -0
- {specfact_cli-0.46.4 → specfact_cli-0.46.16}/src/specfact_cli/resources/semgrep/async.yml +0 -0
- {specfact_cli-0.46.4 → specfact_cli-0.46.16}/src/specfact_cli/resources/semgrep/code-quality.yml +0 -0
- {specfact_cli-0.46.4 → specfact_cli-0.46.16}/src/specfact_cli/resources/semgrep/feature-detection.yml +0 -0
- {specfact_cli-0.46.4 → specfact_cli-0.46.16}/src/specfact_cli/runtime.py +0 -0
- {specfact_cli-0.46.4 → specfact_cli-0.46.16}/src/specfact_cli/sync/__init__.py +0 -0
- {specfact_cli-0.46.4 → specfact_cli-0.46.16}/src/specfact_cli/sync/bridge_probe.py +0 -0
- {specfact_cli-0.46.4 → specfact_cli-0.46.16}/src/specfact_cli/sync/bridge_sync.py +0 -0
- {specfact_cli-0.46.4 → specfact_cli-0.46.16}/src/specfact_cli/sync/bridge_sync_openspec_md_parse.py +0 -0
- {specfact_cli-0.46.4 → specfact_cli-0.46.16}/src/specfact_cli/sync/bridge_sync_requirement_from_proposal.py +0 -0
- {specfact_cli-0.46.4 → specfact_cli-0.46.16}/src/specfact_cli/sync/bridge_sync_requirement_helpers.py +0 -0
- {specfact_cli-0.46.4 → specfact_cli-0.46.16}/src/specfact_cli/sync/bridge_sync_tasks_from_proposal.py +0 -0
- {specfact_cli-0.46.4 → specfact_cli-0.46.16}/src/specfact_cli/sync/bridge_sync_what_changes_format.py +0 -0
- {specfact_cli-0.46.4 → specfact_cli-0.46.16}/src/specfact_cli/sync/bridge_sync_write_openspec_from_proposal.py +0 -0
- {specfact_cli-0.46.4 → specfact_cli-0.46.16}/src/specfact_cli/sync/bridge_watch.py +0 -0
- {specfact_cli-0.46.4 → specfact_cli-0.46.16}/src/specfact_cli/sync/change_detector.py +0 -0
- {specfact_cli-0.46.4 → specfact_cli-0.46.16}/src/specfact_cli/sync/code_to_spec.py +0 -0
- {specfact_cli-0.46.4 → specfact_cli-0.46.16}/src/specfact_cli/sync/drift_detector.py +0 -0
- {specfact_cli-0.46.4 → specfact_cli-0.46.16}/src/specfact_cli/sync/repository_sync.py +0 -0
- {specfact_cli-0.46.4 → specfact_cli-0.46.16}/src/specfact_cli/sync/spec_to_code.py +0 -0
- {specfact_cli-0.46.4 → specfact_cli-0.46.16}/src/specfact_cli/sync/spec_to_tests.py +0 -0
- {specfact_cli-0.46.4 → specfact_cli-0.46.16}/src/specfact_cli/sync/watcher.py +0 -0
- {specfact_cli-0.46.4 → specfact_cli-0.46.16}/src/specfact_cli/sync/watcher_enhanced.py +0 -0
- {specfact_cli-0.46.4 → specfact_cli-0.46.16}/src/specfact_cli/telemetry.py +0 -0
- {specfact_cli-0.46.4 → specfact_cli-0.46.16}/src/specfact_cli/templates/__init__.py +0 -0
- {specfact_cli-0.46.4 → specfact_cli-0.46.16}/src/specfact_cli/templates/defaults/defect_v1.yaml +0 -0
- {specfact_cli-0.46.4 → specfact_cli-0.46.16}/src/specfact_cli/templates/defaults/enabler_v1.yaml +0 -0
- {specfact_cli-0.46.4 → specfact_cli-0.46.16}/src/specfact_cli/templates/defaults/spike_v1.yaml +0 -0
- {specfact_cli-0.46.4 → specfact_cli-0.46.16}/src/specfact_cli/templates/defaults/user_story_v1.yaml +0 -0
- {specfact_cli-0.46.4 → specfact_cli-0.46.16}/src/specfact_cli/templates/frameworks/scrum/user_story_v1.yaml +0 -0
- {specfact_cli-0.46.4 → specfact_cli-0.46.16}/src/specfact_cli/templates/personas/product-owner/user_story_v1.yaml +0 -0
- {specfact_cli-0.46.4 → specfact_cli-0.46.16}/src/specfact_cli/templates/providers/ado/work_item_v1.yaml +0 -0
- {specfact_cli-0.46.4 → specfact_cli-0.46.16}/src/specfact_cli/templates/registry.py +0 -0
- {specfact_cli-0.46.4 → specfact_cli-0.46.16}/src/specfact_cli/templates/specification_templates.py +0 -0
- {specfact_cli-0.46.4 → specfact_cli-0.46.16}/src/specfact_cli/utils/__init__.py +0 -0
- {specfact_cli-0.46.4 → specfact_cli-0.46.16}/src/specfact_cli/utils/acceptance_criteria.py +0 -0
- {specfact_cli-0.46.4 → specfact_cli-0.46.16}/src/specfact_cli/utils/auth_tokens.py +0 -0
- {specfact_cli-0.46.4 → specfact_cli-0.46.16}/src/specfact_cli/utils/bundle_converters.py +0 -0
- {specfact_cli-0.46.4 → specfact_cli-0.46.16}/src/specfact_cli/utils/bundle_loader.py +0 -0
- {specfact_cli-0.46.4 → specfact_cli-0.46.16}/src/specfact_cli/utils/code_change_detector.py +0 -0
- {specfact_cli-0.46.4 → specfact_cli-0.46.16}/src/specfact_cli/utils/console.py +0 -0
- {specfact_cli-0.46.4 → specfact_cli-0.46.16}/src/specfact_cli/utils/content_sanitizer.py +0 -0
- {specfact_cli-0.46.4 → specfact_cli-0.46.16}/src/specfact_cli/utils/context_detection.py +0 -0
- {specfact_cli-0.46.4 → specfact_cli-0.46.16}/src/specfact_cli/utils/contract_predicates.py +0 -0
- {specfact_cli-0.46.4 → specfact_cli-0.46.16}/src/specfact_cli/utils/enrichment_context.py +0 -0
- {specfact_cli-0.46.4 → specfact_cli-0.46.16}/src/specfact_cli/utils/enrichment_parser.py +0 -0
- {specfact_cli-0.46.4 → specfact_cli-0.46.16}/src/specfact_cli/utils/env_manager.py +0 -0
- {specfact_cli-0.46.4 → specfact_cli-0.46.16}/src/specfact_cli/utils/feature_keys.py +0 -0
- {specfact_cli-0.46.4 → specfact_cli-0.46.16}/src/specfact_cli/utils/git.py +0 -0
- {specfact_cli-0.46.4 → specfact_cli-0.46.16}/src/specfact_cli/utils/github_annotations.py +0 -0
- {specfact_cli-0.46.4 → specfact_cli-0.46.16}/src/specfact_cli/utils/icontract_helpers.py +0 -0
- {specfact_cli-0.46.4 → specfact_cli-0.46.16}/src/specfact_cli/utils/ide_setup.py +0 -0
- {specfact_cli-0.46.4 → specfact_cli-0.46.16}/src/specfact_cli/utils/incremental_check.py +0 -0
- {specfact_cli-0.46.4 → specfact_cli-0.46.16}/src/specfact_cli/utils/metadata.py +0 -0
- {specfact_cli-0.46.4 → specfact_cli-0.46.16}/src/specfact_cli/utils/performance.py +0 -0
- {specfact_cli-0.46.4 → specfact_cli-0.46.16}/src/specfact_cli/utils/persona_ownership.py +0 -0
- {specfact_cli-0.46.4 → specfact_cli-0.46.16}/src/specfact_cli/utils/progress.py +0 -0
- {specfact_cli-0.46.4 → specfact_cli-0.46.16}/src/specfact_cli/utils/progressive_disclosure.py +0 -0
- {specfact_cli-0.46.4 → specfact_cli-0.46.16}/src/specfact_cli/utils/project_artifact_write.py +0 -0
- {specfact_cli-0.46.4 → specfact_cli-0.46.16}/src/specfact_cli/utils/prompts.py +0 -0
- {specfact_cli-0.46.4 → specfact_cli-0.46.16}/src/specfact_cli/utils/sdd_discovery.py +0 -0
- {specfact_cli-0.46.4 → specfact_cli-0.46.16}/src/specfact_cli/utils/source_scanner.py +0 -0
- {specfact_cli-0.46.4 → specfact_cli-0.46.16}/src/specfact_cli/utils/startup_checks.py +0 -0
- {specfact_cli-0.46.4 → specfact_cli-0.46.16}/src/specfact_cli/utils/structure.py +0 -0
- {specfact_cli-0.46.4 → specfact_cli-0.46.16}/src/specfact_cli/utils/structured_io.py +0 -0
- {specfact_cli-0.46.4 → specfact_cli-0.46.16}/src/specfact_cli/utils/suggestions.py +0 -0
- {specfact_cli-0.46.4 → specfact_cli-0.46.16}/src/specfact_cli/utils/terminal.py +0 -0
- {specfact_cli-0.46.4 → specfact_cli-0.46.16}/src/specfact_cli/utils/yaml_utils.py +0 -0
- {specfact_cli-0.46.4 → specfact_cli-0.46.16}/src/specfact_cli/validation/__init__.py +0 -0
- {specfact_cli-0.46.4 → specfact_cli-0.46.16}/src/specfact_cli/validation/command_audit.py +0 -0
- {specfact_cli-0.46.4 → specfact_cli-0.46.16}/src/specfact_cli/validators/__init__.py +0 -0
- {specfact_cli-0.46.4 → specfact_cli-0.46.16}/src/specfact_cli/validators/agile_validation.py +0 -0
- {specfact_cli-0.46.4 → specfact_cli-0.46.16}/src/specfact_cli/validators/change_proposal_integration.py +0 -0
- {specfact_cli-0.46.4 → specfact_cli-0.46.16}/src/specfact_cli/validators/cli_first_validator.py +0 -0
- {specfact_cli-0.46.4 → specfact_cli-0.46.16}/src/specfact_cli/validators/contract_validator.py +0 -0
- {specfact_cli-0.46.4 → specfact_cli-0.46.16}/src/specfact_cli/validators/fsm.py +0 -0
- {specfact_cli-0.46.4 → specfact_cli-0.46.16}/src/specfact_cli/validators/repro_checker.py +0 -0
- {specfact_cli-0.46.4 → specfact_cli-0.46.16}/src/specfact_cli/validators/schema.py +0 -0
- {specfact_cli-0.46.4 → specfact_cli-0.46.16}/src/specfact_cli/validators/sidecar/__init__.py +0 -0
- {specfact_cli-0.46.4 → specfact_cli-0.46.16}/src/specfact_cli/validators/sidecar/contract_populator.py +0 -0
- {specfact_cli-0.46.4 → specfact_cli-0.46.16}/src/specfact_cli/validators/sidecar/crosshair_runner.py +0 -0
- {specfact_cli-0.46.4 → specfact_cli-0.46.16}/src/specfact_cli/validators/sidecar/crosshair_summary.py +0 -0
- {specfact_cli-0.46.4 → specfact_cli-0.46.16}/src/specfact_cli/validators/sidecar/dependency_installer.py +0 -0
- {specfact_cli-0.46.4 → specfact_cli-0.46.16}/src/specfact_cli/validators/sidecar/framework_detector.py +0 -0
- {specfact_cli-0.46.4 → specfact_cli-0.46.16}/src/specfact_cli/validators/sidecar/frameworks/__init__.py +0 -0
- {specfact_cli-0.46.4 → specfact_cli-0.46.16}/src/specfact_cli/validators/sidecar/frameworks/base.py +0 -0
- {specfact_cli-0.46.4 → specfact_cli-0.46.16}/src/specfact_cli/validators/sidecar/frameworks/django.py +0 -0
- {specfact_cli-0.46.4 → specfact_cli-0.46.16}/src/specfact_cli/validators/sidecar/frameworks/drf.py +0 -0
- {specfact_cli-0.46.4 → specfact_cli-0.46.16}/src/specfact_cli/validators/sidecar/frameworks/fastapi.py +0 -0
- {specfact_cli-0.46.4 → specfact_cli-0.46.16}/src/specfact_cli/validators/sidecar/frameworks/flask.py +0 -0
- {specfact_cli-0.46.4 → specfact_cli-0.46.16}/src/specfact_cli/validators/sidecar/harness_generator.py +0 -0
- {specfact_cli-0.46.4 → specfact_cli-0.46.16}/src/specfact_cli/validators/sidecar/models.py +0 -0
- {specfact_cli-0.46.4 → specfact_cli-0.46.16}/src/specfact_cli/validators/sidecar/orchestrator.py +0 -0
- {specfact_cli-0.46.4 → specfact_cli-0.46.16}/src/specfact_cli/validators/sidecar/specmatic_runner.py +0 -0
- {specfact_cli-0.46.4 → specfact_cli-0.46.16}/src/specfact_cli/validators/sidecar/unannotated_detector.py +0 -0
- {specfact_cli-0.46.4 → specfact_cli-0.46.16}/src/specfact_cli/versioning/__init__.py +0 -0
- {specfact_cli-0.46.4 → specfact_cli-0.46.16}/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.16
|
|
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
|
|
@@ -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.16"
|
|
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"
|
|
@@ -221,6 +221,7 @@ test-cov = "pytest --cov=src --cov-report=term-missing {args}"
|
|
|
221
221
|
type-check = "basedpyright --pythonpath $(python -c 'import sys; print(sys.executable)') {args}"
|
|
222
222
|
# basedpyright --level error: suppress warning noise in pre-commit (Block 1 runs `hatch run lint`).
|
|
223
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"
|
|
224
|
+
lint-changed = "python scripts/run_changed_lint.py {args}"
|
|
224
225
|
governance = "pylint src tests tools --reports=y --output-format=parseable"
|
|
225
226
|
format = "ruff check . --fix && ruff format ."
|
|
226
227
|
|
|
@@ -32,6 +32,24 @@ from specfact_cli.utils.feature_keys import to_classname_key, to_sequential_key
|
|
|
32
32
|
console = Console()
|
|
33
33
|
|
|
34
34
|
|
|
35
|
+
def _ensure_semgrep_runtime_dir(repo_path: Path, relative: str) -> str:
|
|
36
|
+
"""Create and return a stable repo-local runtime directory for Semgrep."""
|
|
37
|
+
path = (repo_path / relative).resolve()
|
|
38
|
+
path.mkdir(parents=True, exist_ok=True)
|
|
39
|
+
return str(path)
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def _build_semgrep_env(repo_path: Path) -> dict[str, str]:
|
|
43
|
+
"""Build a repo-local Semgrep runtime env so CLI startup stays deterministic."""
|
|
44
|
+
env = dict(os.environ)
|
|
45
|
+
env["XDG_CONFIG_HOME"] = _ensure_semgrep_runtime_dir(repo_path, ".specfact/config")
|
|
46
|
+
env["XDG_CACHE_HOME"] = _ensure_semgrep_runtime_dir(repo_path, ".specfact/cache")
|
|
47
|
+
env["SEMGREP_VERSION_CACHE_PATH"] = _ensure_semgrep_runtime_dir(repo_path, ".specfact/cache/semgrep")
|
|
48
|
+
semgrep_log_dir = _ensure_semgrep_runtime_dir(repo_path, ".specfact/logs")
|
|
49
|
+
env["SEMGREP_LOG_FILE"] = str((Path(semgrep_log_dir) / "semgrep.log").resolve())
|
|
50
|
+
return env
|
|
51
|
+
|
|
52
|
+
|
|
35
53
|
@dataclass
|
|
36
54
|
class _SemgrepFeatureBuckets:
|
|
37
55
|
api_endpoints: list[str] = field(default_factory=list)
|
|
@@ -457,6 +475,7 @@ class CodeAnalyzer:
|
|
|
457
475
|
capture_output=True,
|
|
458
476
|
text=True,
|
|
459
477
|
timeout=5, # Increased timeout to 5s (Semgrep may need time to initialize)
|
|
478
|
+
env=_build_semgrep_env(self.repo_path),
|
|
460
479
|
)
|
|
461
480
|
return result.returncode == 0
|
|
462
481
|
except (FileNotFoundError, subprocess.TimeoutExpired, OSError):
|
|
@@ -566,6 +585,7 @@ class CodeAnalyzer:
|
|
|
566
585
|
capture_output=True,
|
|
567
586
|
text=True,
|
|
568
587
|
timeout=timeout,
|
|
588
|
+
env=_build_semgrep_env(self.repo_path),
|
|
569
589
|
)
|
|
570
590
|
|
|
571
591
|
# Semgrep may return non-zero for valid findings
|
|
@@ -14,7 +14,7 @@ from collections.abc import Callable, Mapping
|
|
|
14
14
|
from dataclasses import dataclass
|
|
15
15
|
from datetime import datetime
|
|
16
16
|
from pathlib import Path
|
|
17
|
-
from typing import Annotated, Any, cast
|
|
17
|
+
from typing import Annotated, Any, NoReturn, cast
|
|
18
18
|
|
|
19
19
|
|
|
20
20
|
_DetectShellFn = Callable[..., tuple[str | None, str | None]]
|
|
@@ -66,6 +66,7 @@ from specfact_cli.registry import CommandRegistry
|
|
|
66
66
|
from specfact_cli.registry.alias_manager import resolve_command
|
|
67
67
|
from specfact_cli.registry.bootstrap import register_builtin_commands
|
|
68
68
|
from specfact_cli.registry.metadata import CommandMetadata
|
|
69
|
+
from specfact_cli.registry.module_availability import ModuleAvailabilityStatus, classify_module_availability
|
|
69
70
|
from specfact_cli.runtime import get_configured_console, init_debug_log_file, set_debug_mode
|
|
70
71
|
from specfact_cli.utils.progressive_disclosure import ProgressiveDisclosureGroup
|
|
71
72
|
from specfact_cli.utils.structured_io import StructuredFormat
|
|
@@ -124,6 +125,28 @@ def _print_missing_bundle_command_help(invoked: str) -> None:
|
|
|
124
125
|
module_id = _INVOKED_TO_MARKETPLACE_MODULE.get(invoked)
|
|
125
126
|
console = get_configured_console()
|
|
126
127
|
if module_id is not None:
|
|
128
|
+
availability = classify_module_availability(module_id=module_id, command_name=invoked)
|
|
129
|
+
if availability.status is ModuleAvailabilityStatus.DISABLED:
|
|
130
|
+
console.print(
|
|
131
|
+
f"[bold red]Module '{availability.module_id or module_id}' is installed but disabled.[/bold red]\n"
|
|
132
|
+
f"The [bold]{invoked}[/bold] command group is provided by that module. "
|
|
133
|
+
f"Enable with [bold]{availability.recovery_command}[/bold]."
|
|
134
|
+
)
|
|
135
|
+
return
|
|
136
|
+
if availability.status is ModuleAvailabilityStatus.SKIPPED:
|
|
137
|
+
console.print(
|
|
138
|
+
f"[bold red]Module '{availability.module_id or module_id}' is installed but skipped.[/bold red]\n"
|
|
139
|
+
f"Reason: {availability.reason}. "
|
|
140
|
+
"Inspect with [bold]specfact module list --show-origin[/bold]."
|
|
141
|
+
)
|
|
142
|
+
return
|
|
143
|
+
if availability.status is ModuleAvailabilityStatus.SHADOWED:
|
|
144
|
+
console.print(
|
|
145
|
+
f"[bold red]Module '{availability.module_id or module_id}' is shadowed in this workspace.[/bold red]\n"
|
|
146
|
+
f"Shadowed by: {availability.shadowed_by}. "
|
|
147
|
+
"Inspect with [bold]specfact module list --show-origin[/bold]."
|
|
148
|
+
)
|
|
149
|
+
return
|
|
127
150
|
console.print(
|
|
128
151
|
f"[bold red]Module '{module_id}' is not installed.[/bold red]\n"
|
|
129
152
|
f"The [bold]{invoked}[/bold] command group is provided by that module. "
|
|
@@ -509,6 +532,84 @@ def _lazy_delegate_cmd_name_ready(self: _LazyDelegateGroup) -> bool:
|
|
|
509
532
|
return len(self._lazy_cmd_name) > 0
|
|
510
533
|
|
|
511
534
|
|
|
535
|
+
def _args_request_help(args: tuple[str, ...] | list[str]) -> bool:
|
|
536
|
+
"""Return True when delegated args are asking only for command help."""
|
|
537
|
+
return any(arg in ("--help", "-h", "--help-advanced", "-ha") for arg in args)
|
|
538
|
+
|
|
539
|
+
|
|
540
|
+
def _delegated_help_path(cmd_name: str, args: tuple[str, ...] | list[str]) -> str:
|
|
541
|
+
"""Build a stable command path for fallback help output."""
|
|
542
|
+
path_parts = [cmd_name]
|
|
543
|
+
for arg in args:
|
|
544
|
+
if arg.startswith("-"):
|
|
545
|
+
continue
|
|
546
|
+
path_parts.append(arg)
|
|
547
|
+
return " ".join(path_parts)
|
|
548
|
+
|
|
549
|
+
|
|
550
|
+
def _print_lazy_help_fallback(cmd_name: str, args: tuple[str, ...] | list[str]) -> None:
|
|
551
|
+
"""Print minimal help when Typer cannot materialize a command for a loaded bundle."""
|
|
552
|
+
command_path = _delegated_help_path(cmd_name, args)
|
|
553
|
+
get_configured_console().print(
|
|
554
|
+
f"[bold]{command_path}[/bold]\n\n"
|
|
555
|
+
"Help is available for this installed command path, but the command metadata could not be "
|
|
556
|
+
"materialized in this runtime. Reinstall the providing module or run the command without "
|
|
557
|
+
"`--help` to execute it."
|
|
558
|
+
)
|
|
559
|
+
|
|
560
|
+
|
|
561
|
+
def _raise_lazy_delegate_click_exception(exc: Exception) -> NoReturn:
|
|
562
|
+
raise click.ClickException(str(exc)) from exc
|
|
563
|
+
|
|
564
|
+
|
|
565
|
+
def _load_lazy_delegate_typer(cmd_name: str) -> typer.Typer:
|
|
566
|
+
resolved_name = resolve_command(cmd_name)
|
|
567
|
+
try:
|
|
568
|
+
return CommandRegistry.get_typer(resolved_name)
|
|
569
|
+
except ValueError as exc:
|
|
570
|
+
if cmd_name in KNOWN_BUNDLE_GROUP_OR_SHIM_NAMES:
|
|
571
|
+
_print_missing_bundle_command_help(cmd_name)
|
|
572
|
+
raise SystemExit(1) from None
|
|
573
|
+
_raise_lazy_delegate_click_exception(exc)
|
|
574
|
+
raise AssertionError("unreachable") from None
|
|
575
|
+
|
|
576
|
+
|
|
577
|
+
def _build_lazy_delegate_click_command(cmd_name: str, args: tuple[str, ...], real_typer: typer.Typer) -> click.Command:
|
|
578
|
+
from typer.main import get_command
|
|
579
|
+
|
|
580
|
+
try:
|
|
581
|
+
return get_command(real_typer)
|
|
582
|
+
except (RuntimeError, ValueError) as exc:
|
|
583
|
+
if _args_request_help(args):
|
|
584
|
+
_print_lazy_help_fallback(cmd_name, args)
|
|
585
|
+
raise SystemExit(0) from None
|
|
586
|
+
_raise_lazy_delegate_click_exception(exc)
|
|
587
|
+
raise AssertionError("unreachable") from None
|
|
588
|
+
|
|
589
|
+
|
|
590
|
+
def _lazy_delegate_prog_name(ctx: click.Context, cmd_name: str) -> str:
|
|
591
|
+
parts: list[str] = []
|
|
592
|
+
parent = ctx.parent
|
|
593
|
+
while parent and getattr(parent, "command", None):
|
|
594
|
+
name = getattr(parent.command, "name", None)
|
|
595
|
+
if name and name != "__delegate__":
|
|
596
|
+
parts.append(name)
|
|
597
|
+
parent = getattr(parent, "parent", None)
|
|
598
|
+
if parts:
|
|
599
|
+
return " ".join(reversed(parts))
|
|
600
|
+
original_prog_name = ctx.meta.get("original_prog_name")
|
|
601
|
+
if isinstance(original_prog_name, str) and original_prog_name:
|
|
602
|
+
return original_prog_name
|
|
603
|
+
return cmd_name
|
|
604
|
+
|
|
605
|
+
|
|
606
|
+
def _strip_redundant_single_command_arg(click_cmd: click.Command, args: tuple[str, ...]) -> list[str]:
|
|
607
|
+
args_list = list(args)
|
|
608
|
+
if not isinstance(click_cmd, click.Group) and args_list and args_list[0] == getattr(click_cmd, "name", None):
|
|
609
|
+
return args_list[1:]
|
|
610
|
+
return args_list
|
|
611
|
+
|
|
612
|
+
|
|
512
613
|
class _LazyDelegateGroup(click.Group):
|
|
513
614
|
"""Click Group that delegates all args to the real command (lazy-loaded)."""
|
|
514
615
|
|
|
@@ -521,6 +622,8 @@ class _LazyDelegateGroup(click.Group):
|
|
|
521
622
|
name=name or cmd_name,
|
|
522
623
|
help=help or help_str,
|
|
523
624
|
context_settings={"ignore_unknown_options": True},
|
|
625
|
+
invoke_without_command=True,
|
|
626
|
+
no_args_is_help=False,
|
|
524
627
|
)
|
|
525
628
|
self._lazy_cmd_name = cmd_name
|
|
526
629
|
self._lazy_help_str = help_str
|
|
@@ -530,31 +633,15 @@ class _LazyDelegateGroup(click.Group):
|
|
|
530
633
|
cmd_name = self._lazy_cmd_name
|
|
531
634
|
|
|
532
635
|
def _invoke(args: tuple[str, ...]) -> None:
|
|
533
|
-
from typer.main import get_command
|
|
534
|
-
|
|
535
636
|
ctx = click.get_current_context()
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
click_cmd = get_command(real_typer)
|
|
637
|
+
real_typer = _load_lazy_delegate_typer(cmd_name)
|
|
638
|
+
click_cmd = _build_lazy_delegate_click_command(cmd_name, args, real_typer)
|
|
539
639
|
# Build full prog name from root (e.g. "specfact sync") so usage shows "specfact sync bridge", not "sync sync bridge"
|
|
540
|
-
|
|
541
|
-
p = ctx.parent
|
|
542
|
-
while p and getattr(p, "command", None):
|
|
543
|
-
name = getattr(p.command, "name", None)
|
|
544
|
-
if name and name != "__delegate__":
|
|
545
|
-
parts.append(name)
|
|
546
|
-
p = getattr(p, "parent", None)
|
|
547
|
-
prog_name = " ".join(reversed(parts)) if parts else cmd_name
|
|
548
|
-
args_list = list(args)
|
|
640
|
+
prog_name = _lazy_delegate_prog_name(ctx, cmd_name)
|
|
549
641
|
# When the real app is a single command (e.g. drift has only "detect"), Typer
|
|
550
642
|
# builds a TyperCommand, not a Group. Then args are ["detect", "bundle", "--repo", ...]
|
|
551
643
|
# and the command expects ["bundle", "--repo", ...] (no leading "detect").
|
|
552
|
-
|
|
553
|
-
not isinstance(click_cmd, click.Group)
|
|
554
|
-
and args_list
|
|
555
|
-
and args_list[0] == getattr(click_cmd, "name", None)
|
|
556
|
-
):
|
|
557
|
-
args_list = args_list[1:]
|
|
644
|
+
args_list = _strip_redundant_single_command_arg(click_cmd, args)
|
|
558
645
|
exit_code = click_cmd.main(args=args_list, prog_name=prog_name, standalone_mode=False)
|
|
559
646
|
if exit_code and exit_code != 0:
|
|
560
647
|
raise SystemExit(exit_code)
|
|
@@ -567,6 +654,14 @@ class _LazyDelegateGroup(click.Group):
|
|
|
567
654
|
add_help_option=False, # Pass --help through to real Typer so "specfact backlog daily ado --help" shows correct usage
|
|
568
655
|
)
|
|
569
656
|
|
|
657
|
+
@require(lambda ctx: ctx is not None, "ctx must not be None")
|
|
658
|
+
@ensure(lambda result: result is None or isinstance(result, int), "result must be None or an exit code")
|
|
659
|
+
def invoke(self, ctx: click.Context) -> Any:
|
|
660
|
+
if ctx.invoked_subcommand is None and not ctx.args:
|
|
661
|
+
ctx.meta["original_prog_name"] = ctx.command_path
|
|
662
|
+
return self._delegate_cmd.main(args=[], prog_name=ctx.command_path, standalone_mode=False)
|
|
663
|
+
return super().invoke(ctx)
|
|
664
|
+
|
|
570
665
|
@require(_lazy_delegate_cmd_name_ready, "lazy command name must be set")
|
|
571
666
|
@ensure(lambda result: isinstance(result, tuple) and len(result) == 3, "result must be a 3-tuple")
|
|
572
667
|
def resolve_command(
|
|
@@ -574,7 +669,7 @@ class _LazyDelegateGroup(click.Group):
|
|
|
574
669
|
) -> tuple[str | None, click.Command | None, list[str]]:
|
|
575
670
|
# Pass through all args to the delegate so "plan init bundle" becomes args for the real plan Typer.
|
|
576
671
|
if not args:
|
|
577
|
-
return
|
|
672
|
+
return self._delegate_cmd.name, self._delegate_cmd, []
|
|
578
673
|
return self._delegate_cmd.name, self._delegate_cmd, list(args)
|
|
579
674
|
|
|
580
675
|
@ensure(lambda result: isinstance(result, list), "result must be a list of command names")
|
|
@@ -598,8 +693,11 @@ class _LazyDelegateGroup(click.Group):
|
|
|
598
693
|
from typer.main import get_command
|
|
599
694
|
|
|
600
695
|
resolved_name = resolve_command(self._lazy_cmd_name)
|
|
601
|
-
|
|
602
|
-
|
|
696
|
+
try:
|
|
697
|
+
real_typer = CommandRegistry.get_typer(resolved_name)
|
|
698
|
+
click_cmd = get_command(real_typer)
|
|
699
|
+
except (RuntimeError, ValueError):
|
|
700
|
+
return None
|
|
603
701
|
if isinstance(click_cmd, click.Group):
|
|
604
702
|
return click_cmd
|
|
605
703
|
return None
|
|
@@ -610,8 +708,11 @@ class _LazyDelegateGroup(click.Group):
|
|
|
610
708
|
from typer.main import get_command
|
|
611
709
|
|
|
612
710
|
resolved_name = resolve_command(self._lazy_cmd_name)
|
|
613
|
-
|
|
614
|
-
|
|
711
|
+
try:
|
|
712
|
+
real_typer = CommandRegistry.get_typer(resolved_name)
|
|
713
|
+
click_cmd = get_command(real_typer)
|
|
714
|
+
except (RuntimeError, ValueError):
|
|
715
|
+
return
|
|
615
716
|
prog_name = (
|
|
616
717
|
f"{ctx.parent.command.name} {self._lazy_cmd_name}"
|
|
617
718
|
if ctx.parent and ctx.parent.command
|
{specfact_cli-0.46.4 → specfact_cli-0.46.16}/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.31
|
|
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:0f7bc54a823bea14033fcb143ecb6c83d2bca2b5da661f03a0b545100acebe5b
|
|
21
|
+
signature: dTatkqgBUtti4tL/pmcFBZY9bsJ61gY/V0lP9gZU8Y5W3YWK+wpgRx1oewlAmfKzkxca2NhalKcjLACQjTNvAA==
|
|
@@ -106,7 +106,7 @@ def _resolve_field_mapping_templates_dir(repo_path: Path) -> Path | None:
|
|
|
106
106
|
package_templates_dir = Path(str(templates_ref)).resolve()
|
|
107
107
|
if package_templates_dir.exists():
|
|
108
108
|
return package_templates_dir
|
|
109
|
-
except
|
|
109
|
+
except (ImportError, OSError, ValueError):
|
|
110
110
|
try:
|
|
111
111
|
import importlib.util
|
|
112
112
|
|
|
@@ -118,8 +118,8 @@ def _resolve_field_mapping_templates_dir(repo_path: Path) -> Path | None:
|
|
|
118
118
|
).resolve()
|
|
119
119
|
if package_templates_dir.exists():
|
|
120
120
|
return package_templates_dir
|
|
121
|
-
except
|
|
122
|
-
|
|
121
|
+
except (ImportError, OSError, ValueError):
|
|
122
|
+
return None
|
|
123
123
|
return None
|
|
124
124
|
|
|
125
125
|
|
|
@@ -434,7 +434,15 @@ def _is_valid_repo_path(repo: Path) -> bool:
|
|
|
434
434
|
|
|
435
435
|
|
|
436
436
|
@beartype
|
|
437
|
-
def
|
|
437
|
+
def _marketplace_ids_for_bundles(bundle_ids: list[str]) -> list[str]:
|
|
438
|
+
return [
|
|
439
|
+
first_run_selection.MARKETPLACE_ONLY_BUNDLES[bid]
|
|
440
|
+
for bid in bundle_ids
|
|
441
|
+
if bid in first_run_selection.MARKETPLACE_ONLY_BUNDLES
|
|
442
|
+
]
|
|
443
|
+
|
|
444
|
+
|
|
445
|
+
def _install_profile_bundles(profile: str, install_root: Path, non_interactive: bool) -> list[str]:
|
|
438
446
|
"""Resolve profile to bundle list and install via module installer."""
|
|
439
447
|
bundle_ids = first_run_selection.resolve_profile_bundles(profile)
|
|
440
448
|
if bundle_ids:
|
|
@@ -444,10 +452,11 @@ def _install_profile_bundles(profile: str, install_root: Path, non_interactive:
|
|
|
444
452
|
install_root,
|
|
445
453
|
non_interactive=non_interactive,
|
|
446
454
|
)
|
|
455
|
+
return _marketplace_ids_for_bundles(bundle_ids)
|
|
447
456
|
|
|
448
457
|
|
|
449
458
|
@beartype
|
|
450
|
-
def _install_bundle_list(install_arg: str, install_root: Path, non_interactive: bool) ->
|
|
459
|
+
def _install_bundle_list(install_arg: str, install_root: Path, non_interactive: bool) -> list[str]:
|
|
451
460
|
"""Parse comma-separated or 'all' and install bundles via module installer."""
|
|
452
461
|
bundle_ids = first_run_selection.resolve_install_bundles(install_arg)
|
|
453
462
|
if bundle_ids:
|
|
@@ -457,20 +466,32 @@ def _install_bundle_list(install_arg: str, install_root: Path, non_interactive:
|
|
|
457
466
|
install_root,
|
|
458
467
|
non_interactive=non_interactive,
|
|
459
468
|
)
|
|
469
|
+
return _marketplace_ids_for_bundles(bundle_ids)
|
|
460
470
|
|
|
461
471
|
|
|
462
|
-
def _apply_profile_or_install_bundles(profile: str | None, install: str | None) ->
|
|
472
|
+
def _apply_profile_or_install_bundles(profile: str | None, install: str | None) -> list[str]:
|
|
463
473
|
try:
|
|
464
474
|
non_interactive = is_non_interactive()
|
|
465
475
|
if profile is not None:
|
|
466
|
-
_install_profile_bundles(profile, INIT_USER_MODULES_ROOT, non_interactive=non_interactive)
|
|
467
|
-
|
|
468
|
-
_install_bundle_list(install or "", INIT_USER_MODULES_ROOT, non_interactive=non_interactive)
|
|
476
|
+
return _install_profile_bundles(profile, INIT_USER_MODULES_ROOT, non_interactive=non_interactive)
|
|
477
|
+
return _install_bundle_list(install or "", INIT_USER_MODULES_ROOT, non_interactive=non_interactive)
|
|
469
478
|
except ValueError as e:
|
|
470
479
|
console.print(f"[red]Error:[/red] {e}")
|
|
471
480
|
raise typer.Exit(1) from e
|
|
472
481
|
|
|
473
482
|
|
|
483
|
+
def _refresh_init_module_state(repo_path: Path, enabled_module_ids: list[str]) -> list[dict[str, Any]]:
|
|
484
|
+
modules_list = get_discovered_modules_for_state(
|
|
485
|
+
enable_ids=enabled_module_ids,
|
|
486
|
+
disable_ids=[],
|
|
487
|
+
base_path=repo_path,
|
|
488
|
+
preserve_existing=True,
|
|
489
|
+
)
|
|
490
|
+
if modules_list:
|
|
491
|
+
write_modules_state(modules_list)
|
|
492
|
+
return modules_list
|
|
493
|
+
|
|
494
|
+
|
|
474
495
|
def _run_interactive_first_run_install() -> None:
|
|
475
496
|
try:
|
|
476
497
|
bundle_ids = _interactive_first_run_bundle_selection()
|
|
@@ -702,8 +723,9 @@ def init(
|
|
|
702
723
|
|
|
703
724
|
repo_path = repo.resolve()
|
|
704
725
|
|
|
726
|
+
enabled_module_ids: list[str] = []
|
|
705
727
|
if profile is not None or install is not None:
|
|
706
|
-
_apply_profile_or_install_bundles(profile, install)
|
|
728
|
+
enabled_module_ids = _apply_profile_or_install_bundles(profile, install)
|
|
707
729
|
elif is_first_run(user_root=INIT_USER_MODULES_ROOT) and is_non_interactive():
|
|
708
730
|
console.print(
|
|
709
731
|
"[red]Error:[/red] In CI/CD (non-interactive) mode, first-run init requires "
|
|
@@ -718,9 +740,7 @@ def init(
|
|
|
718
740
|
_run_interactive_first_run_install()
|
|
719
741
|
|
|
720
742
|
_init_user_visible_step("[cyan]→[/cyan] Discovering installed modules and writing registry state…")
|
|
721
|
-
modules_list =
|
|
722
|
-
if modules_list:
|
|
723
|
-
write_modules_state(modules_list)
|
|
743
|
+
modules_list = _refresh_init_module_state(repo_path, enabled_module_ids)
|
|
724
744
|
|
|
725
745
|
_init_user_visible_step("[cyan]→[/cyan] Indexing CLI commands for help cache…")
|
|
726
746
|
run_discovery_and_write_cache(__version__)
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
name: module-registry
|
|
2
|
-
version: 0.1.
|
|
2
|
+
version: 0.1.23
|
|
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:f500281d2249d712be23a1b25b5660374694dfa47634f60ae4378eb2cdb753ca
|
|
21
|
+
signature: cunuat95bD44IcNUBs35NTlHYOR8atydVz4ZyZTjIzwJL6Wz5YOfBYGq/WtgDnOFv3KplfvhRZbTo3CwmS/KBQ==
|
|
@@ -20,12 +20,14 @@ from packaging.version import InvalidVersion, Version
|
|
|
20
20
|
from rich.console import Console
|
|
21
21
|
from rich.table import Table
|
|
22
22
|
|
|
23
|
+
from specfact_cli import __version__
|
|
23
24
|
from specfact_cli.models.module_package import ModulePackageMetadata
|
|
24
25
|
from specfact_cli.modules import module_io_shim
|
|
25
26
|
from specfact_cli.registry.alias_manager import create_alias, list_aliases, remove_alias
|
|
26
27
|
from specfact_cli.registry.custom_registries import add_registry, fetch_all_indexes, list_registries, remove_registry
|
|
28
|
+
from specfact_cli.registry.help_cache import run_discovery_and_write_cache
|
|
27
29
|
from specfact_cli.registry.marketplace_client import fetch_registry_index
|
|
28
|
-
from specfact_cli.registry.module_discovery import discover_all_modules
|
|
30
|
+
from specfact_cli.registry.module_discovery import discover_all_modules, discover_all_modules_for_project
|
|
29
31
|
from specfact_cli.registry.module_installer import (
|
|
30
32
|
REGISTRY_ID_FILE,
|
|
31
33
|
USER_MODULES_ROOT,
|
|
@@ -42,7 +44,9 @@ from specfact_cli.registry.module_lifecycle import (
|
|
|
42
44
|
render_modules_table,
|
|
43
45
|
select_module_ids_interactive,
|
|
44
46
|
)
|
|
47
|
+
from specfact_cli.registry.module_packages import get_discovered_modules_for_state
|
|
45
48
|
from specfact_cli.registry.module_security import ensure_publisher_trusted, is_official_publisher
|
|
49
|
+
from specfact_cli.registry.module_state import read_modules_state, write_modules_state
|
|
46
50
|
from specfact_cli.registry.registry import CommandRegistry
|
|
47
51
|
from specfact_cli.runtime import is_non_interactive
|
|
48
52
|
|
|
@@ -178,15 +182,64 @@ def _resolve_install_target_root(scope_normalized: str, repo: Path | None) -> Pa
|
|
|
178
182
|
return USER_MODULES_ROOT if scope_normalized == "user" else repo_path / ".specfact" / "modules"
|
|
179
183
|
|
|
180
184
|
|
|
185
|
+
def _normalize_project_repo(repo: Path | None) -> Path | None:
|
|
186
|
+
"""Resolve a project-scoped repo argument to the nearest workspace root."""
|
|
187
|
+
if repo is None:
|
|
188
|
+
return None
|
|
189
|
+
repo_path = repo.resolve()
|
|
190
|
+
for candidate in [repo_path, *repo_path.parents]:
|
|
191
|
+
if (candidate / ".git").exists():
|
|
192
|
+
return candidate
|
|
193
|
+
return repo_path
|
|
194
|
+
|
|
195
|
+
|
|
196
|
+
def _read_installed_manifest_id(module_dir: Path, fallback_name: str) -> str:
|
|
197
|
+
manifest_path = module_dir / "module-package.yaml"
|
|
198
|
+
try:
|
|
199
|
+
raw = yaml.safe_load(manifest_path.read_text(encoding="utf-8"))
|
|
200
|
+
except (OSError, yaml.YAMLError):
|
|
201
|
+
return fallback_name
|
|
202
|
+
if isinstance(raw, dict):
|
|
203
|
+
manifest = cast(dict[str, Any], raw)
|
|
204
|
+
if manifest.get("name"):
|
|
205
|
+
return str(manifest["name"])
|
|
206
|
+
return fallback_name
|
|
207
|
+
|
|
208
|
+
|
|
209
|
+
def _enable_if_disabled(module_id: str, base_path: Path | None = None) -> bool:
|
|
210
|
+
state = read_modules_state()
|
|
211
|
+
if state.get(module_id, {}).get("enabled", True) is not False:
|
|
212
|
+
return False
|
|
213
|
+
modules = get_discovered_modules_for_state(
|
|
214
|
+
enable_ids=[module_id],
|
|
215
|
+
disable_ids=[],
|
|
216
|
+
base_path=base_path,
|
|
217
|
+
preserve_existing=True,
|
|
218
|
+
)
|
|
219
|
+
write_modules_state(modules)
|
|
220
|
+
run_discovery_and_write_cache(__version__)
|
|
221
|
+
return any(str(row.get("id", "")) == module_id and bool(row.get("enabled", True)) for row in modules)
|
|
222
|
+
|
|
223
|
+
|
|
181
224
|
def _install_skip_if_already_satisfied(
|
|
182
225
|
scope_normalized: str,
|
|
183
226
|
requested_name: str,
|
|
184
227
|
target_root: Path,
|
|
228
|
+
repo: Path | None,
|
|
185
229
|
reinstall: bool,
|
|
186
230
|
discovered_by_name: dict[str, Any],
|
|
187
231
|
) -> bool:
|
|
188
|
-
|
|
189
|
-
|
|
232
|
+
installed_dir = target_root / requested_name
|
|
233
|
+
if (installed_dir / "module-package.yaml").exists() and not reinstall:
|
|
234
|
+
module_id = _read_installed_manifest_id(installed_dir, requested_name)
|
|
235
|
+
enabled = _enable_if_disabled(module_id, base_path=repo if scope_normalized == "project" else None)
|
|
236
|
+
if enabled:
|
|
237
|
+
console.print(
|
|
238
|
+
f"[yellow]Module '{module_id}' is already installed in {target_root}; "
|
|
239
|
+
"enabled it in module state.[/yellow]"
|
|
240
|
+
)
|
|
241
|
+
else:
|
|
242
|
+
console.print(f"[yellow]Module '{module_id}' is already installed in {target_root}.[/yellow]")
|
|
190
243
|
return True
|
|
191
244
|
skip_sources = {"builtin", "project", "user", "custom"}
|
|
192
245
|
if scope_normalized == "project":
|
|
@@ -195,9 +248,14 @@ def _install_skip_if_already_satisfied(
|
|
|
195
248
|
skip_sources.discard("project")
|
|
196
249
|
existing = discovered_by_name.get(requested_name)
|
|
197
250
|
if existing is not None and existing.source in skip_sources:
|
|
251
|
+
enabled = _enable_if_disabled(
|
|
252
|
+
existing.metadata.name,
|
|
253
|
+
base_path=repo if scope_normalized == "project" else None,
|
|
254
|
+
)
|
|
255
|
+
state_hint = " Enabled it in module state." if enabled else ""
|
|
198
256
|
console.print(
|
|
199
|
-
f"[yellow]Module '{
|
|
200
|
-
"No marketplace install needed.[/yellow]"
|
|
257
|
+
f"[yellow]Module '{existing.metadata.name}' is already available from source '{existing.source}'. "
|
|
258
|
+
f"No marketplace install needed.{state_hint}[/yellow]"
|
|
201
259
|
)
|
|
202
260
|
return True
|
|
203
261
|
return False
|
|
@@ -274,6 +332,7 @@ class _InstallOneParams:
|
|
|
274
332
|
scope_normalized: str
|
|
275
333
|
source_normalized: str
|
|
276
334
|
target_root: Path
|
|
335
|
+
repo: Path | None
|
|
277
336
|
version: str | None
|
|
278
337
|
reinstall: bool
|
|
279
338
|
trust_non_official: bool
|
|
@@ -289,6 +348,7 @@ def _install_one(module_id: str, params: _InstallOneParams) -> bool:
|
|
|
289
348
|
params.scope_normalized,
|
|
290
349
|
requested_name,
|
|
291
350
|
params.target_root,
|
|
351
|
+
params.repo,
|
|
292
352
|
params.reinstall,
|
|
293
353
|
params.discovered_by_name,
|
|
294
354
|
):
|
|
@@ -392,12 +452,17 @@ def _install_impl(module_ids: list[str], **kwargs: Any) -> None:
|
|
|
392
452
|
)
|
|
393
453
|
raise typer.Exit(1)
|
|
394
454
|
scope_normalized, source_normalized = _parse_install_scope_and_source(scope, source)
|
|
395
|
-
|
|
396
|
-
|
|
455
|
+
normalized_repo = _normalize_project_repo(repo) if scope_normalized == "project" else None
|
|
456
|
+
target_root = _resolve_install_target_root(scope_normalized, normalized_repo)
|
|
457
|
+
discovered = (
|
|
458
|
+
discover_all_modules_for_project(normalized_repo) if normalized_repo is not None else discover_all_modules()
|
|
459
|
+
)
|
|
460
|
+
discovered_by_name = {entry.metadata.name: entry for entry in discovered}
|
|
397
461
|
params = _InstallOneParams(
|
|
398
462
|
scope_normalized=scope_normalized,
|
|
399
463
|
source_normalized=source_normalized,
|
|
400
464
|
target_root=target_root,
|
|
465
|
+
repo=normalized_repo,
|
|
401
466
|
version=version,
|
|
402
467
|
reinstall=reinstall,
|
|
403
468
|
trust_non_official=trust_non_official,
|
{specfact_cli-0.46.4 → specfact_cli-0.46.16}/src/specfact_cli/modules/upgrade/module-package.yaml
RENAMED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
name: upgrade
|
|
2
|
-
version: 0.1.
|
|
2
|
+
version: 0.1.12
|
|
3
3
|
commands:
|
|
4
4
|
- upgrade
|
|
5
5
|
category: core
|
|
@@ -17,5 +17,4 @@ publisher:
|
|
|
17
17
|
description: Check and apply SpecFact CLI version upgrades.
|
|
18
18
|
license: Apache-2.0
|
|
19
19
|
integrity:
|
|
20
|
-
checksum: sha256:
|
|
21
|
-
signature: ZMw8ljS+0f4TYg2WVAqQCpgaae1d8z7wT/1r2yxuM6ZeZjMejhgeBuOyXopda5LOXjioxTxOlWZmGN94cCC3Ag==
|
|
20
|
+
checksum: sha256:5bc01c11370e696da4fa365188c3d223a7bd3a99ffc36e878b841a4081d6bb62
|