specfact-cli 0.47.3__tar.gz → 0.47.11__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.3 → specfact_cli-0.47.11}/.gitignore +3 -0
- {specfact_cli-0.47.3 → specfact_cli-0.47.11}/PKG-INFO +25 -17
- {specfact_cli-0.47.3 → specfact_cli-0.47.11}/README.md +22 -14
- {specfact_cli-0.47.3 → specfact_cli-0.47.11}/pyproject.toml +10 -4
- {specfact_cli-0.47.3 → specfact_cli-0.47.11}/resources/bundled-module-registry/index.json +3 -3
- specfact_cli-0.47.11/src/__init__.py +6 -0
- {specfact_cli-0.47.3 → specfact_cli-0.47.11}/src/specfact_cli/__init__.py +3 -2
- {specfact_cli-0.47.3 → specfact_cli-0.47.11}/src/specfact_cli/adapters/ado.py +2 -2
- {specfact_cli-0.47.3 → specfact_cli-0.47.11}/src/specfact_cli/cli.py +87 -39
- {specfact_cli-0.47.3 → specfact_cli-0.47.11}/src/specfact_cli/generators/persona_exporter.py +1 -1
- {specfact_cli-0.47.3 → specfact_cli-0.47.11}/src/specfact_cli/generators/plan_generator.py +1 -1
- {specfact_cli-0.47.3 → specfact_cli-0.47.11}/src/specfact_cli/generators/protocol_generator.py +1 -1
- {specfact_cli-0.47.3 → specfact_cli-0.47.11}/src/specfact_cli/generators/report_generator.py +1 -1
- {specfact_cli-0.47.3 → specfact_cli-0.47.11}/src/specfact_cli/generators/workflow_generator.py +1 -1
- {specfact_cli-0.47.3 → specfact_cli-0.47.11}/src/specfact_cli/modules/module_registry/module-package.yaml +3 -3
- {specfact_cli-0.47.3 → specfact_cli-0.47.11}/src/specfact_cli/modules/module_registry/src/commands.py +12 -4
- {specfact_cli-0.47.3 → specfact_cli-0.47.11}/src/specfact_cli/registry/dependency_resolver.py +11 -8
- {specfact_cli-0.47.3 → specfact_cli-0.47.11}/src/specfact_cli/registry/module_installer.py +1 -1
- {specfact_cli-0.47.3 → specfact_cli-0.47.11}/src/specfact_cli/telemetry.py +10 -4
- {specfact_cli-0.47.3 → specfact_cli-0.47.11}/src/specfact_cli/utils/suggestions.py +4 -4
- specfact_cli-0.47.3/src/__init__.py +0 -6
- {specfact_cli-0.47.3 → specfact_cli-0.47.11}/LICENSE +0 -0
- {specfact_cli-0.47.3 → specfact_cli-0.47.11}/resources/keys/README.md +0 -0
- {specfact_cli-0.47.3 → specfact_cli-0.47.11}/resources/keys/module-signing-public.pem +0 -0
- {specfact_cli-0.47.3 → specfact_cli-0.47.11}/resources/mappings/node-async.yaml +0 -0
- {specfact_cli-0.47.3 → specfact_cli-0.47.11}/resources/mappings/python-async.yaml +0 -0
- {specfact_cli-0.47.3 → specfact_cli-0.47.11}/resources/mappings/speckit-default.yaml +0 -0
- {specfact_cli-0.47.3 → specfact_cli-0.47.11}/resources/schemas/deviation.schema.json +0 -0
- {specfact_cli-0.47.3 → specfact_cli-0.47.11}/resources/schemas/plan.schema.json +0 -0
- {specfact_cli-0.47.3 → specfact_cli-0.47.11}/resources/schemas/protocol.schema.json +0 -0
- {specfact_cli-0.47.3 → specfact_cli-0.47.11}/resources/templates/github-action.yml.j2 +0 -0
- {specfact_cli-0.47.3 → specfact_cli-0.47.11}/resources/templates/persona/architect.md.j2 +0 -0
- {specfact_cli-0.47.3 → specfact_cli-0.47.11}/resources/templates/persona/developer.md.j2 +0 -0
- {specfact_cli-0.47.3 → specfact_cli-0.47.11}/resources/templates/persona/product-owner.md.j2 +0 -0
- {specfact_cli-0.47.3 → specfact_cli-0.47.11}/resources/templates/plan.bundle.yaml.j2 +0 -0
- {specfact_cli-0.47.3 → specfact_cli-0.47.11}/resources/templates/policies/kanban.yaml +0 -0
- {specfact_cli-0.47.3 → specfact_cli-0.47.11}/resources/templates/policies/mixed.yaml +0 -0
- {specfact_cli-0.47.3 → specfact_cli-0.47.11}/resources/templates/policies/safe.yaml +0 -0
- {specfact_cli-0.47.3 → specfact_cli-0.47.11}/resources/templates/policies/scrum.yaml +0 -0
- {specfact_cli-0.47.3 → specfact_cli-0.47.11}/resources/templates/pr-template.md.j2 +0 -0
- {specfact_cli-0.47.3 → specfact_cli-0.47.11}/resources/templates/protocol.yaml.j2 +0 -0
- {specfact_cli-0.47.3 → specfact_cli-0.47.11}/resources/templates/telemetry.yaml.example +0 -0
- {specfact_cli-0.47.3 → specfact_cli-0.47.11}/src/specfact_cli/__main__.py +0 -0
- {specfact_cli-0.47.3 → specfact_cli-0.47.11}/src/specfact_cli/adapters/__init__.py +0 -0
- {specfact_cli-0.47.3 → specfact_cli-0.47.11}/src/specfact_cli/adapters/backlog_base.py +0 -0
- {specfact_cli-0.47.3 → specfact_cli-0.47.11}/src/specfact_cli/adapters/base.py +0 -0
- {specfact_cli-0.47.3 → specfact_cli-0.47.11}/src/specfact_cli/adapters/github.py +0 -0
- {specfact_cli-0.47.3 → specfact_cli-0.47.11}/src/specfact_cli/adapters/openspec.py +0 -0
- {specfact_cli-0.47.3 → specfact_cli-0.47.11}/src/specfact_cli/adapters/openspec_parser.py +0 -0
- {specfact_cli-0.47.3 → specfact_cli-0.47.11}/src/specfact_cli/adapters/registry.py +0 -0
- {specfact_cli-0.47.3 → specfact_cli-0.47.11}/src/specfact_cli/adapters/speckit.py +0 -0
- {specfact_cli-0.47.3 → specfact_cli-0.47.11}/src/specfact_cli/agents/__init__.py +0 -0
- {specfact_cli-0.47.3 → specfact_cli-0.47.11}/src/specfact_cli/agents/analyze_agent.py +0 -0
- {specfact_cli-0.47.3 → specfact_cli-0.47.11}/src/specfact_cli/agents/base.py +0 -0
- {specfact_cli-0.47.3 → specfact_cli-0.47.11}/src/specfact_cli/agents/plan_agent.py +0 -0
- {specfact_cli-0.47.3 → specfact_cli-0.47.11}/src/specfact_cli/agents/registry.py +0 -0
- {specfact_cli-0.47.3 → specfact_cli-0.47.11}/src/specfact_cli/agents/sync_agent.py +0 -0
- {specfact_cli-0.47.3 → specfact_cli-0.47.11}/src/specfact_cli/analyzers/__init__.py +0 -0
- {specfact_cli-0.47.3 → specfact_cli-0.47.11}/src/specfact_cli/analyzers/ambiguity_scanner.py +0 -0
- {specfact_cli-0.47.3 → specfact_cli-0.47.11}/src/specfact_cli/analyzers/code_analyzer.py +0 -0
- {specfact_cli-0.47.3 → specfact_cli-0.47.11}/src/specfact_cli/analyzers/constitution_evidence_extractor.py +0 -0
- {specfact_cli-0.47.3 → specfact_cli-0.47.11}/src/specfact_cli/analyzers/contract_extractor.py +0 -0
- {specfact_cli-0.47.3 → specfact_cli-0.47.11}/src/specfact_cli/analyzers/control_flow_analyzer.py +0 -0
- {specfact_cli-0.47.3 → specfact_cli-0.47.11}/src/specfact_cli/analyzers/graph_analyzer.py +0 -0
- {specfact_cli-0.47.3 → specfact_cli-0.47.11}/src/specfact_cli/analyzers/relationship_mapper.py +0 -0
- {specfact_cli-0.47.3 → specfact_cli-0.47.11}/src/specfact_cli/analyzers/requirement_extractor.py +0 -0
- {specfact_cli-0.47.3 → specfact_cli-0.47.11}/src/specfact_cli/analyzers/test_pattern_extractor.py +0 -0
- {specfact_cli-0.47.3 → specfact_cli-0.47.11}/src/specfact_cli/backlog/__init__.py +0 -0
- {specfact_cli-0.47.3 → specfact_cli-0.47.11}/src/specfact_cli/backlog/adapters/__init__.py +0 -0
- {specfact_cli-0.47.3 → specfact_cli-0.47.11}/src/specfact_cli/backlog/adapters/base.py +0 -0
- {specfact_cli-0.47.3 → specfact_cli-0.47.11}/src/specfact_cli/backlog/converter.py +0 -0
- {specfact_cli-0.47.3 → specfact_cli-0.47.11}/src/specfact_cli/backlog/filters.py +0 -0
- {specfact_cli-0.47.3 → specfact_cli-0.47.11}/src/specfact_cli/backlog/mappers/__init__.py +0 -0
- {specfact_cli-0.47.3 → specfact_cli-0.47.11}/src/specfact_cli/backlog/mappers/ado_mapper.py +0 -0
- {specfact_cli-0.47.3 → specfact_cli-0.47.11}/src/specfact_cli/backlog/mappers/base.py +0 -0
- {specfact_cli-0.47.3 → specfact_cli-0.47.11}/src/specfact_cli/backlog/mappers/github_mapper.py +0 -0
- {specfact_cli-0.47.3 → specfact_cli-0.47.11}/src/specfact_cli/backlog/mappers/template_config.py +0 -0
- {specfact_cli-0.47.3 → specfact_cli-0.47.11}/src/specfact_cli/commands/__init__.py +0 -0
- {specfact_cli-0.47.3 → specfact_cli-0.47.11}/src/specfact_cli/commands/_bundle_shim.py +0 -0
- {specfact_cli-0.47.3 → specfact_cli-0.47.11}/src/specfact_cli/commands/analyze.py +0 -0
- {specfact_cli-0.47.3 → specfact_cli-0.47.11}/src/specfact_cli/commands/contract_cmd.py +0 -0
- {specfact_cli-0.47.3 → specfact_cli-0.47.11}/src/specfact_cli/commands/drift.py +0 -0
- {specfact_cli-0.47.3 → specfact_cli-0.47.11}/src/specfact_cli/commands/enforce.py +0 -0
- {specfact_cli-0.47.3 → specfact_cli-0.47.11}/src/specfact_cli/commands/generate.py +0 -0
- {specfact_cli-0.47.3 → specfact_cli-0.47.11}/src/specfact_cli/commands/import_cmd.py +0 -0
- {specfact_cli-0.47.3 → specfact_cli-0.47.11}/src/specfact_cli/commands/init.py +0 -0
- {specfact_cli-0.47.3 → specfact_cli-0.47.11}/src/specfact_cli/commands/migrate.py +0 -0
- {specfact_cli-0.47.3 → specfact_cli-0.47.11}/src/specfact_cli/commands/plan.py +0 -0
- {specfact_cli-0.47.3 → specfact_cli-0.47.11}/src/specfact_cli/commands/project_cmd.py +0 -0
- {specfact_cli-0.47.3 → specfact_cli-0.47.11}/src/specfact_cli/commands/repro.py +0 -0
- {specfact_cli-0.47.3 → specfact_cli-0.47.11}/src/specfact_cli/commands/sdd.py +0 -0
- {specfact_cli-0.47.3 → specfact_cli-0.47.11}/src/specfact_cli/commands/spec.py +0 -0
- {specfact_cli-0.47.3 → specfact_cli-0.47.11}/src/specfact_cli/commands/sync.py +0 -0
- {specfact_cli-0.47.3 → specfact_cli-0.47.11}/src/specfact_cli/commands/update.py +0 -0
- {specfact_cli-0.47.3 → specfact_cli-0.47.11}/src/specfact_cli/commands/validate.py +0 -0
- {specfact_cli-0.47.3 → specfact_cli-0.47.11}/src/specfact_cli/common/__init__.py +0 -0
- {specfact_cli-0.47.3 → specfact_cli-0.47.11}/src/specfact_cli/common/bundle_factory.py +0 -0
- {specfact_cli-0.47.3 → specfact_cli-0.47.11}/src/specfact_cli/common/logger_setup.py +0 -0
- {specfact_cli-0.47.3 → specfact_cli-0.47.11}/src/specfact_cli/common/logging_utils.py +0 -0
- {specfact_cli-0.47.3 → specfact_cli-0.47.11}/src/specfact_cli/common/text_utils.py +0 -0
- {specfact_cli-0.47.3 → specfact_cli-0.47.11}/src/specfact_cli/common/utils.py +0 -0
- {specfact_cli-0.47.3 → specfact_cli-0.47.11}/src/specfact_cli/comparators/__init__.py +0 -0
- {specfact_cli-0.47.3 → specfact_cli-0.47.11}/src/specfact_cli/comparators/plan_comparator.py +0 -0
- {specfact_cli-0.47.3 → specfact_cli-0.47.11}/src/specfact_cli/contracts/__init__.py +0 -0
- {specfact_cli-0.47.3 → specfact_cli-0.47.11}/src/specfact_cli/contracts/crosshair_props.py +0 -0
- {specfact_cli-0.47.3 → specfact_cli-0.47.11}/src/specfact_cli/contracts/module_interface.py +0 -0
- {specfact_cli-0.47.3 → specfact_cli-0.47.11}/src/specfact_cli/enrichers/constitution_enricher.py +0 -0
- {specfact_cli-0.47.3 → specfact_cli-0.47.11}/src/specfact_cli/enrichers/plan_enricher.py +0 -0
- {specfact_cli-0.47.3 → specfact_cli-0.47.11}/src/specfact_cli/generators/__init__.py +0 -0
- {specfact_cli-0.47.3 → specfact_cli-0.47.11}/src/specfact_cli/generators/contract_generator.py +0 -0
- {specfact_cli-0.47.3 → specfact_cli-0.47.11}/src/specfact_cli/generators/openapi_extractor.py +0 -0
- {specfact_cli-0.47.3 → specfact_cli-0.47.11}/src/specfact_cli/generators/task_generator.py +0 -0
- {specfact_cli-0.47.3 → specfact_cli-0.47.11}/src/specfact_cli/generators/test_to_openapi.py +0 -0
- {specfact_cli-0.47.3 → specfact_cli-0.47.11}/src/specfact_cli/groups/__init__.py +0 -0
- {specfact_cli-0.47.3 → specfact_cli-0.47.11}/src/specfact_cli/groups/codebase_group.py +0 -0
- {specfact_cli-0.47.3 → specfact_cli-0.47.11}/src/specfact_cli/groups/govern_group.py +0 -0
- {specfact_cli-0.47.3 → specfact_cli-0.47.11}/src/specfact_cli/groups/member_group.py +0 -0
- {specfact_cli-0.47.3 → specfact_cli-0.47.11}/src/specfact_cli/groups/project_group.py +0 -0
- {specfact_cli-0.47.3 → specfact_cli-0.47.11}/src/specfact_cli/groups/spec_group.py +0 -0
- {specfact_cli-0.47.3 → specfact_cli-0.47.11}/src/specfact_cli/importers/__init__.py +0 -0
- {specfact_cli-0.47.3 → specfact_cli-0.47.11}/src/specfact_cli/importers/speckit_converter.py +0 -0
- {specfact_cli-0.47.3 → specfact_cli-0.47.11}/src/specfact_cli/importers/speckit_scanner.py +0 -0
- {specfact_cli-0.47.3 → specfact_cli-0.47.11}/src/specfact_cli/integrations/__init__.py +0 -0
- {specfact_cli-0.47.3 → specfact_cli-0.47.11}/src/specfact_cli/integrations/specmatic.py +0 -0
- {specfact_cli-0.47.3 → specfact_cli-0.47.11}/src/specfact_cli/merge/__init__.py +0 -0
- {specfact_cli-0.47.3 → specfact_cli-0.47.11}/src/specfact_cli/merge/resolver.py +0 -0
- {specfact_cli-0.47.3 → specfact_cli-0.47.11}/src/specfact_cli/migrations/__init__.py +0 -0
- {specfact_cli-0.47.3 → specfact_cli-0.47.11}/src/specfact_cli/migrations/plan_migrator.py +0 -0
- {specfact_cli-0.47.3 → specfact_cli-0.47.11}/src/specfact_cli/models/__init__.py +0 -0
- {specfact_cli-0.47.3 → specfact_cli-0.47.11}/src/specfact_cli/models/backlog_item.py +0 -0
- {specfact_cli-0.47.3 → specfact_cli-0.47.11}/src/specfact_cli/models/bridge.py +0 -0
- {specfact_cli-0.47.3 → specfact_cli-0.47.11}/src/specfact_cli/models/capabilities.py +0 -0
- {specfact_cli-0.47.3 → specfact_cli-0.47.11}/src/specfact_cli/models/change.py +0 -0
- {specfact_cli-0.47.3 → specfact_cli-0.47.11}/src/specfact_cli/models/contract.py +0 -0
- {specfact_cli-0.47.3 → specfact_cli-0.47.11}/src/specfact_cli/models/deviation.py +0 -0
- {specfact_cli-0.47.3 → specfact_cli-0.47.11}/src/specfact_cli/models/dor_config.py +0 -0
- {specfact_cli-0.47.3 → specfact_cli-0.47.11}/src/specfact_cli/models/enforcement.py +0 -0
- {specfact_cli-0.47.3 → specfact_cli-0.47.11}/src/specfact_cli/models/module_package.py +0 -0
- {specfact_cli-0.47.3 → specfact_cli-0.47.11}/src/specfact_cli/models/persona_template.py +0 -0
- {specfact_cli-0.47.3 → specfact_cli-0.47.11}/src/specfact_cli/models/plan.py +0 -0
- {specfact_cli-0.47.3 → specfact_cli-0.47.11}/src/specfact_cli/models/project.py +0 -0
- {specfact_cli-0.47.3 → specfact_cli-0.47.11}/src/specfact_cli/models/protocol.py +0 -0
- {specfact_cli-0.47.3 → specfact_cli-0.47.11}/src/specfact_cli/models/quality.py +0 -0
- {specfact_cli-0.47.3 → specfact_cli-0.47.11}/src/specfact_cli/models/sdd.py +0 -0
- {specfact_cli-0.47.3 → specfact_cli-0.47.11}/src/specfact_cli/models/source_tracking.py +0 -0
- {specfact_cli-0.47.3 → specfact_cli-0.47.11}/src/specfact_cli/models/task.py +0 -0
- {specfact_cli-0.47.3 → specfact_cli-0.47.11}/src/specfact_cli/models/validation.py +0 -0
- {specfact_cli-0.47.3 → specfact_cli-0.47.11}/src/specfact_cli/modes/__init__.py +0 -0
- {specfact_cli-0.47.3 → specfact_cli-0.47.11}/src/specfact_cli/modes/detector.py +0 -0
- {specfact_cli-0.47.3 → specfact_cli-0.47.11}/src/specfact_cli/modes/router.py +0 -0
- {specfact_cli-0.47.3 → specfact_cli-0.47.11}/src/specfact_cli/modules/__init__.py +0 -0
- {specfact_cli-0.47.3 → specfact_cli-0.47.11}/src/specfact_cli/modules/_bundle_import.py +0 -0
- {specfact_cli-0.47.3 → specfact_cli-0.47.11}/src/specfact_cli/modules/init/module-package.yaml +0 -0
- {specfact_cli-0.47.3 → specfact_cli-0.47.11}/src/specfact_cli/modules/init/src/__init__.py +0 -0
- {specfact_cli-0.47.3 → specfact_cli-0.47.11}/src/specfact_cli/modules/init/src/app.py +0 -0
- {specfact_cli-0.47.3 → specfact_cli-0.47.11}/src/specfact_cli/modules/init/src/commands.py +0 -0
- {specfact_cli-0.47.3 → specfact_cli-0.47.11}/src/specfact_cli/modules/init/src/first_run_selection.py +0 -0
- {specfact_cli-0.47.3 → specfact_cli-0.47.11}/src/specfact_cli/modules/module_io_shim.py +0 -0
- {specfact_cli-0.47.3 → specfact_cli-0.47.11}/src/specfact_cli/modules/module_registry/src/__init__.py +0 -0
- {specfact_cli-0.47.3 → specfact_cli-0.47.11}/src/specfact_cli/modules/module_registry/src/app.py +0 -0
- {specfact_cli-0.47.3 → specfact_cli-0.47.11}/src/specfact_cli/modules/upgrade/module-package.yaml +0 -0
- {specfact_cli-0.47.3 → specfact_cli-0.47.11}/src/specfact_cli/modules/upgrade/src/__init__.py +0 -0
- {specfact_cli-0.47.3 → specfact_cli-0.47.11}/src/specfact_cli/modules/upgrade/src/app.py +0 -0
- {specfact_cli-0.47.3 → specfact_cli-0.47.11}/src/specfact_cli/modules/upgrade/src/commands.py +0 -0
- {specfact_cli-0.47.3 → specfact_cli-0.47.11}/src/specfact_cli/parsers/__init__.py +0 -0
- {specfact_cli-0.47.3 → specfact_cli-0.47.11}/src/specfact_cli/parsers/persona_importer.py +0 -0
- {specfact_cli-0.47.3 → specfact_cli-0.47.11}/src/specfact_cli/registry/__init__.py +0 -0
- {specfact_cli-0.47.3 → specfact_cli-0.47.11}/src/specfact_cli/registry/alias_manager.py +0 -0
- {specfact_cli-0.47.3 → specfact_cli-0.47.11}/src/specfact_cli/registry/bootstrap.py +0 -0
- {specfact_cli-0.47.3 → specfact_cli-0.47.11}/src/specfact_cli/registry/bridge_registry.py +0 -0
- {specfact_cli-0.47.3 → specfact_cli-0.47.11}/src/specfact_cli/registry/crypto_validator.py +0 -0
- {specfact_cli-0.47.3 → specfact_cli-0.47.11}/src/specfact_cli/registry/custom_registries.py +0 -0
- {specfact_cli-0.47.3 → specfact_cli-0.47.11}/src/specfact_cli/registry/extension_registry.py +0 -0
- {specfact_cli-0.47.3 → specfact_cli-0.47.11}/src/specfact_cli/registry/help_cache.py +0 -0
- {specfact_cli-0.47.3 → specfact_cli-0.47.11}/src/specfact_cli/registry/marketplace_client.py +0 -0
- {specfact_cli-0.47.3 → specfact_cli-0.47.11}/src/specfact_cli/registry/metadata.py +0 -0
- {specfact_cli-0.47.3 → specfact_cli-0.47.11}/src/specfact_cli/registry/module_availability.py +0 -0
- {specfact_cli-0.47.3 → specfact_cli-0.47.11}/src/specfact_cli/registry/module_discovery.py +0 -0
- {specfact_cli-0.47.3 → specfact_cli-0.47.11}/src/specfact_cli/registry/module_grouping.py +0 -0
- {specfact_cli-0.47.3 → specfact_cli-0.47.11}/src/specfact_cli/registry/module_lifecycle.py +0 -0
- {specfact_cli-0.47.3 → specfact_cli-0.47.11}/src/specfact_cli/registry/module_packages.py +0 -0
- {specfact_cli-0.47.3 → specfact_cli-0.47.11}/src/specfact_cli/registry/module_security.py +0 -0
- {specfact_cli-0.47.3 → specfact_cli-0.47.11}/src/specfact_cli/registry/module_state.py +0 -0
- {specfact_cli-0.47.3 → specfact_cli-0.47.11}/src/specfact_cli/registry/registry.py +0 -0
- {specfact_cli-0.47.3 → specfact_cli-0.47.11}/src/specfact_cli/resources/semgrep/async.yml +0 -0
- {specfact_cli-0.47.3 → specfact_cli-0.47.11}/src/specfact_cli/resources/semgrep/code-quality.yml +0 -0
- {specfact_cli-0.47.3 → specfact_cli-0.47.11}/src/specfact_cli/resources/semgrep/feature-detection.yml +0 -0
- {specfact_cli-0.47.3 → specfact_cli-0.47.11}/src/specfact_cli/runtime.py +0 -0
- {specfact_cli-0.47.3 → specfact_cli-0.47.11}/src/specfact_cli/sync/__init__.py +0 -0
- {specfact_cli-0.47.3 → specfact_cli-0.47.11}/src/specfact_cli/sync/bridge_probe.py +0 -0
- {specfact_cli-0.47.3 → specfact_cli-0.47.11}/src/specfact_cli/sync/bridge_sync.py +0 -0
- {specfact_cli-0.47.3 → specfact_cli-0.47.11}/src/specfact_cli/sync/bridge_sync_openspec_md_parse.py +0 -0
- {specfact_cli-0.47.3 → specfact_cli-0.47.11}/src/specfact_cli/sync/bridge_sync_requirement_from_proposal.py +0 -0
- {specfact_cli-0.47.3 → specfact_cli-0.47.11}/src/specfact_cli/sync/bridge_sync_requirement_helpers.py +0 -0
- {specfact_cli-0.47.3 → specfact_cli-0.47.11}/src/specfact_cli/sync/bridge_sync_tasks_from_proposal.py +0 -0
- {specfact_cli-0.47.3 → specfact_cli-0.47.11}/src/specfact_cli/sync/bridge_sync_what_changes_format.py +0 -0
- {specfact_cli-0.47.3 → specfact_cli-0.47.11}/src/specfact_cli/sync/bridge_sync_write_openspec_from_proposal.py +0 -0
- {specfact_cli-0.47.3 → specfact_cli-0.47.11}/src/specfact_cli/sync/bridge_watch.py +0 -0
- {specfact_cli-0.47.3 → specfact_cli-0.47.11}/src/specfact_cli/sync/change_detector.py +0 -0
- {specfact_cli-0.47.3 → specfact_cli-0.47.11}/src/specfact_cli/sync/code_to_spec.py +0 -0
- {specfact_cli-0.47.3 → specfact_cli-0.47.11}/src/specfact_cli/sync/drift_detector.py +0 -0
- {specfact_cli-0.47.3 → specfact_cli-0.47.11}/src/specfact_cli/sync/repository_sync.py +0 -0
- {specfact_cli-0.47.3 → specfact_cli-0.47.11}/src/specfact_cli/sync/spec_to_code.py +0 -0
- {specfact_cli-0.47.3 → specfact_cli-0.47.11}/src/specfact_cli/sync/spec_to_tests.py +0 -0
- {specfact_cli-0.47.3 → specfact_cli-0.47.11}/src/specfact_cli/sync/watcher.py +0 -0
- {specfact_cli-0.47.3 → specfact_cli-0.47.11}/src/specfact_cli/sync/watcher_enhanced.py +0 -0
- {specfact_cli-0.47.3 → specfact_cli-0.47.11}/src/specfact_cli/templates/__init__.py +0 -0
- {specfact_cli-0.47.3 → specfact_cli-0.47.11}/src/specfact_cli/templates/defaults/defect_v1.yaml +0 -0
- {specfact_cli-0.47.3 → specfact_cli-0.47.11}/src/specfact_cli/templates/defaults/enabler_v1.yaml +0 -0
- {specfact_cli-0.47.3 → specfact_cli-0.47.11}/src/specfact_cli/templates/defaults/spike_v1.yaml +0 -0
- {specfact_cli-0.47.3 → specfact_cli-0.47.11}/src/specfact_cli/templates/defaults/user_story_v1.yaml +0 -0
- {specfact_cli-0.47.3 → specfact_cli-0.47.11}/src/specfact_cli/templates/frameworks/scrum/user_story_v1.yaml +0 -0
- {specfact_cli-0.47.3 → specfact_cli-0.47.11}/src/specfact_cli/templates/personas/product-owner/user_story_v1.yaml +0 -0
- {specfact_cli-0.47.3 → specfact_cli-0.47.11}/src/specfact_cli/templates/providers/ado/work_item_v1.yaml +0 -0
- {specfact_cli-0.47.3 → specfact_cli-0.47.11}/src/specfact_cli/templates/registry.py +0 -0
- {specfact_cli-0.47.3 → specfact_cli-0.47.11}/src/specfact_cli/templates/specification_templates.py +0 -0
- {specfact_cli-0.47.3 → specfact_cli-0.47.11}/src/specfact_cli/utils/__init__.py +0 -0
- {specfact_cli-0.47.3 → specfact_cli-0.47.11}/src/specfact_cli/utils/acceptance_criteria.py +0 -0
- {specfact_cli-0.47.3 → specfact_cli-0.47.11}/src/specfact_cli/utils/auth_tokens.py +0 -0
- {specfact_cli-0.47.3 → specfact_cli-0.47.11}/src/specfact_cli/utils/bundle_converters.py +0 -0
- {specfact_cli-0.47.3 → specfact_cli-0.47.11}/src/specfact_cli/utils/bundle_loader.py +0 -0
- {specfact_cli-0.47.3 → specfact_cli-0.47.11}/src/specfact_cli/utils/code_change_detector.py +0 -0
- {specfact_cli-0.47.3 → specfact_cli-0.47.11}/src/specfact_cli/utils/console.py +0 -0
- {specfact_cli-0.47.3 → specfact_cli-0.47.11}/src/specfact_cli/utils/content_sanitizer.py +0 -0
- {specfact_cli-0.47.3 → specfact_cli-0.47.11}/src/specfact_cli/utils/context_detection.py +0 -0
- {specfact_cli-0.47.3 → specfact_cli-0.47.11}/src/specfact_cli/utils/contract_predicates.py +0 -0
- {specfact_cli-0.47.3 → specfact_cli-0.47.11}/src/specfact_cli/utils/enrichment_context.py +0 -0
- {specfact_cli-0.47.3 → specfact_cli-0.47.11}/src/specfact_cli/utils/enrichment_parser.py +0 -0
- {specfact_cli-0.47.3 → specfact_cli-0.47.11}/src/specfact_cli/utils/env_manager.py +0 -0
- {specfact_cli-0.47.3 → specfact_cli-0.47.11}/src/specfact_cli/utils/feature_keys.py +0 -0
- {specfact_cli-0.47.3 → specfact_cli-0.47.11}/src/specfact_cli/utils/git.py +0 -0
- {specfact_cli-0.47.3 → specfact_cli-0.47.11}/src/specfact_cli/utils/github_annotations.py +0 -0
- {specfact_cli-0.47.3 → specfact_cli-0.47.11}/src/specfact_cli/utils/icontract_helpers.py +0 -0
- {specfact_cli-0.47.3 → specfact_cli-0.47.11}/src/specfact_cli/utils/ide_setup.py +0 -0
- {specfact_cli-0.47.3 → specfact_cli-0.47.11}/src/specfact_cli/utils/incremental_check.py +0 -0
- {specfact_cli-0.47.3 → specfact_cli-0.47.11}/src/specfact_cli/utils/metadata.py +0 -0
- {specfact_cli-0.47.3 → specfact_cli-0.47.11}/src/specfact_cli/utils/optional_deps.py +0 -0
- {specfact_cli-0.47.3 → specfact_cli-0.47.11}/src/specfact_cli/utils/performance.py +0 -0
- {specfact_cli-0.47.3 → specfact_cli-0.47.11}/src/specfact_cli/utils/persona_ownership.py +0 -0
- {specfact_cli-0.47.3 → specfact_cli-0.47.11}/src/specfact_cli/utils/progress.py +0 -0
- {specfact_cli-0.47.3 → specfact_cli-0.47.11}/src/specfact_cli/utils/progressive_disclosure.py +0 -0
- {specfact_cli-0.47.3 → specfact_cli-0.47.11}/src/specfact_cli/utils/project_artifact_write.py +0 -0
- {specfact_cli-0.47.3 → specfact_cli-0.47.11}/src/specfact_cli/utils/prompts.py +0 -0
- {specfact_cli-0.47.3 → specfact_cli-0.47.11}/src/specfact_cli/utils/sdd_discovery.py +0 -0
- {specfact_cli-0.47.3 → specfact_cli-0.47.11}/src/specfact_cli/utils/source_scanner.py +0 -0
- {specfact_cli-0.47.3 → specfact_cli-0.47.11}/src/specfact_cli/utils/startup_checks.py +0 -0
- {specfact_cli-0.47.3 → specfact_cli-0.47.11}/src/specfact_cli/utils/structure.py +0 -0
- {specfact_cli-0.47.3 → specfact_cli-0.47.11}/src/specfact_cli/utils/structured_io.py +0 -0
- {specfact_cli-0.47.3 → specfact_cli-0.47.11}/src/specfact_cli/utils/terminal.py +0 -0
- {specfact_cli-0.47.3 → specfact_cli-0.47.11}/src/specfact_cli/utils/yaml_utils.py +0 -0
- {specfact_cli-0.47.3 → specfact_cli-0.47.11}/src/specfact_cli/validation/__init__.py +0 -0
- {specfact_cli-0.47.3 → specfact_cli-0.47.11}/src/specfact_cli/validation/command_audit.py +0 -0
- {specfact_cli-0.47.3 → specfact_cli-0.47.11}/src/specfact_cli/validators/__init__.py +0 -0
- {specfact_cli-0.47.3 → specfact_cli-0.47.11}/src/specfact_cli/validators/agile_validation.py +0 -0
- {specfact_cli-0.47.3 → specfact_cli-0.47.11}/src/specfact_cli/validators/change_proposal_integration.py +0 -0
- {specfact_cli-0.47.3 → specfact_cli-0.47.11}/src/specfact_cli/validators/cli_first_validator.py +0 -0
- {specfact_cli-0.47.3 → specfact_cli-0.47.11}/src/specfact_cli/validators/contract_validator.py +0 -0
- {specfact_cli-0.47.3 → specfact_cli-0.47.11}/src/specfact_cli/validators/fsm.py +0 -0
- {specfact_cli-0.47.3 → specfact_cli-0.47.11}/src/specfact_cli/validators/repro_checker.py +0 -0
- {specfact_cli-0.47.3 → specfact_cli-0.47.11}/src/specfact_cli/validators/schema.py +0 -0
- {specfact_cli-0.47.3 → specfact_cli-0.47.11}/src/specfact_cli/validators/sidecar/__init__.py +0 -0
- {specfact_cli-0.47.3 → specfact_cli-0.47.11}/src/specfact_cli/validators/sidecar/contract_populator.py +0 -0
- {specfact_cli-0.47.3 → specfact_cli-0.47.11}/src/specfact_cli/validators/sidecar/crosshair_runner.py +0 -0
- {specfact_cli-0.47.3 → specfact_cli-0.47.11}/src/specfact_cli/validators/sidecar/crosshair_summary.py +0 -0
- {specfact_cli-0.47.3 → specfact_cli-0.47.11}/src/specfact_cli/validators/sidecar/dependency_installer.py +0 -0
- {specfact_cli-0.47.3 → specfact_cli-0.47.11}/src/specfact_cli/validators/sidecar/framework_detector.py +0 -0
- {specfact_cli-0.47.3 → specfact_cli-0.47.11}/src/specfact_cli/validators/sidecar/frameworks/__init__.py +0 -0
- {specfact_cli-0.47.3 → specfact_cli-0.47.11}/src/specfact_cli/validators/sidecar/frameworks/base.py +0 -0
- {specfact_cli-0.47.3 → specfact_cli-0.47.11}/src/specfact_cli/validators/sidecar/frameworks/django.py +0 -0
- {specfact_cli-0.47.3 → specfact_cli-0.47.11}/src/specfact_cli/validators/sidecar/frameworks/drf.py +0 -0
- {specfact_cli-0.47.3 → specfact_cli-0.47.11}/src/specfact_cli/validators/sidecar/frameworks/fastapi.py +0 -0
- {specfact_cli-0.47.3 → specfact_cli-0.47.11}/src/specfact_cli/validators/sidecar/frameworks/flask.py +0 -0
- {specfact_cli-0.47.3 → specfact_cli-0.47.11}/src/specfact_cli/validators/sidecar/harness_generator.py +0 -0
- {specfact_cli-0.47.3 → specfact_cli-0.47.11}/src/specfact_cli/validators/sidecar/models.py +0 -0
- {specfact_cli-0.47.3 → specfact_cli-0.47.11}/src/specfact_cli/validators/sidecar/orchestrator.py +0 -0
- {specfact_cli-0.47.3 → specfact_cli-0.47.11}/src/specfact_cli/validators/sidecar/specmatic_runner.py +0 -0
- {specfact_cli-0.47.3 → specfact_cli-0.47.11}/src/specfact_cli/validators/sidecar/unannotated_detector.py +0 -0
- {specfact_cli-0.47.3 → specfact_cli-0.47.11}/src/specfact_cli/versioning/__init__.py +0 -0
- {specfact_cli-0.47.3 → specfact_cli-0.47.11}/src/specfact_cli/versioning/analyzer.py +0 -0
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: specfact-cli
|
|
3
|
-
Version: 0.47.
|
|
4
|
-
Summary:
|
|
3
|
+
Version: 0.47.11
|
|
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
|
|
7
7
|
Project-URL: Documentation, https://github.com/nold-ai/specfact-cli#readme
|
|
@@ -210,7 +210,7 @@ License: Apache License
|
|
|
210
210
|
See the License for the specific language governing permissions and
|
|
211
211
|
limitations under the License.
|
|
212
212
|
License-File: LICENSE
|
|
213
|
-
Keywords: agile,backlog,beartype,ceremonies,cli,contract-driven-development,contracts,crosshair,devops,existing-code,icontract,kanban,legacy,modernization,policy-as-code,property-based-testing,safe,scrum,specfact,specs,tdd,validation
|
|
213
|
+
Keywords: agile,ai-bloat,backlog,beartype,ceremonies,clean-code,cli,code-review,contract-driven-development,contracts,crosshair,devops,existing-code,icontract,kanban,legacy,modernization,policy-as-code,property-based-testing,safe,scrum,specfact,specs,tdd,technical-debt,validation
|
|
214
214
|
Classifier: Development Status :: 4 - Beta
|
|
215
215
|
Classifier: Intended Audience :: Developers
|
|
216
216
|
Classifier: License :: OSI Approved :: Apache Software License
|
|
@@ -283,8 +283,8 @@ Description-Content-Type: text/markdown
|
|
|
283
283
|
|
|
284
284
|
# SpecFact CLI
|
|
285
285
|
|
|
286
|
-
>
|
|
287
|
-
>
|
|
286
|
+
> Defend AI-assisted Python code from bloat before it reaches PR.
|
|
287
|
+
> Run deterministic review, cleanup forecasts, and spec/contract evidence locally.
|
|
288
288
|
|
|
289
289
|
[](https://pypi.org/project/specfact-cli/)
|
|
290
290
|
[](https://pypi.org/project/specfact-cli/)
|
|
@@ -315,7 +315,7 @@ uvx specfact-cli code review run --path . --scope full
|
|
|
315
315
|
**Sample output:**
|
|
316
316
|
|
|
317
317
|
```text
|
|
318
|
-
SpecFact CLI - v0.
|
|
318
|
+
SpecFact CLI - v0.47.4
|
|
319
319
|
|
|
320
320
|
Running Ruff checks...
|
|
321
321
|
Running Radon complexity checks...
|
|
@@ -347,23 +347,30 @@ specfact code review run --path . --scope full
|
|
|
347
347
|
|
|
348
348
|
The sample output comes from a pinned capture against `nold-ai/specfact-demo-repo`. Reproduce it with `docs/_support/readme-first-contact/capture-readme-output.sh`; capture metadata lives alongside the raw logs in `docs/_support/readme-first-contact/sample-output/`.
|
|
349
349
|
|
|
350
|
-
|
|
350
|
+
## AI-bloat defense loop
|
|
351
|
+
|
|
352
|
+
SpecFact is the local AI-bloat defense CLI for Python-first teams using AI IDEs. The Code Review bundle reports `ai_bloat` advisories for code shapes that AI-assisted coding often amplifies: redundant wrappers, passthrough lambdas, identity `try`/`except` blocks, avoidable intermediate lists, and long low-branch functions.
|
|
353
|
+
|
|
354
|
+
For cleanup work, run a JSON review, inspect the cleanup forecast and AI-bloat index, hand remediation packets to your AI IDE, accept only safe changes, then re-run review for proof. The JSON report is the portable handoff artifact for Claude, Codex, Cursor, Copilot, or a headless agent.
|
|
355
|
+
|
|
356
|
+
These findings are bloat-shape detection and cleanup guidance, not AI-authorship detection. Exact simplify flags and report fields live in the [AI bloat quickstart](https://modules.specfact.io/quickstart-ai-bloat/) and [Code Review run guide](https://modules.specfact.io/bundles/code-review/run/) on the modules docs site.
|
|
351
357
|
|
|
352
358
|
## What SpecFact does
|
|
353
359
|
|
|
354
|
-
- **
|
|
360
|
+
- **Defends against AI bloat deterministically** — forecast cleanup impact and route remediation packets to your AI IDE
|
|
361
|
+
- **Reviews AI-assisted changes against evidence** — compare code against contracts, clean-code rules, and policy gates
|
|
355
362
|
- **Extracts structure from existing code** — reverse-engineer brownfield repos before you change them
|
|
356
363
|
- **Blocks drift before merge** — use the same checks locally, in pre-commit, and in CI
|
|
357
|
-
- **
|
|
364
|
+
- **Consumes upstream planning inputs** — treat Spec Kit, OpenSpec, backlog, specs, tests, and contracts as validation evidence
|
|
358
365
|
- **Stays local-first** — no cloud account, no vendor lock-in, no built-in model dependency
|
|
359
366
|
|
|
360
367
|
## What is SpecFact?
|
|
361
368
|
|
|
362
|
-
SpecFact is a local CLI for
|
|
369
|
+
SpecFact is a local CLI for AI-bloat defense, deterministic code review, and backlog/spec/code drift control. It gives solo developers, legacy maintainers, and teams a validation layer around AI-assisted delivery, brownfield reverse engineering, and contract-first reviews.
|
|
363
370
|
|
|
364
371
|
It exists because delivery drifts in predictable ways:
|
|
365
372
|
|
|
366
|
-
- AI-assisted code lands faster than validation
|
|
373
|
+
- AI-assisted code lands faster than cleanup and validation catch up
|
|
367
374
|
- brownfield systems rarely have trustworthy up-to-date specs
|
|
368
375
|
- backlog intent gets reinterpreted before it reaches code
|
|
369
376
|
- teams need the same review rules across IDEs, CI, and pull requests
|
|
@@ -384,7 +391,7 @@ For a **single-hook** setup in downstream repos, keep using the stable id and sc
|
|
|
384
391
|
|
|
385
392
|
```yaml
|
|
386
393
|
- repo: https://github.com/nold-ai/specfact-cli
|
|
387
|
-
rev: v0.
|
|
394
|
+
rev: v0.47.4
|
|
388
395
|
hooks:
|
|
389
396
|
- id: specfact-smart-checks
|
|
390
397
|
```
|
|
@@ -412,17 +419,18 @@ SpecFact uses the same discipline it asks you to trust:
|
|
|
412
419
|
|
|
413
420
|
## For teams and organizations
|
|
414
421
|
|
|
415
|
-
SpecFact still scales beyond the solo-developer entry path
|
|
422
|
+
SpecFact still scales beyond the solo-developer entry path, but validation stays
|
|
423
|
+
the center:
|
|
416
424
|
|
|
417
|
-
- **
|
|
425
|
+
- **Upstream context adapters** for GitHub, Azure DevOps, Jira, Linear, OpenSpec, and Spec Kit inputs
|
|
418
426
|
- **DoR/DoD and policy enforcement** for teams that need repeatable gates
|
|
419
427
|
- **Evidence-backed PR review** with the same checks used locally
|
|
420
428
|
- **CI/CD adoption path** that keeps validation deterministic instead of model-driven
|
|
421
429
|
|
|
422
|
-
|
|
430
|
+
Optional module-deep paths include:
|
|
423
431
|
|
|
424
|
-
- `specfact
|
|
425
|
-
- `specfact
|
|
432
|
+
- `specfact project import ...`
|
|
433
|
+
- `specfact spec validate ...`
|
|
426
434
|
- `specfact backlog verify-readiness --bundle <bundle-name>`
|
|
427
435
|
- `specfact govern ...`
|
|
428
436
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# SpecFact CLI
|
|
2
2
|
|
|
3
|
-
>
|
|
4
|
-
>
|
|
3
|
+
> Defend AI-assisted Python code from bloat before it reaches PR.
|
|
4
|
+
> Run deterministic review, cleanup forecasts, and spec/contract evidence locally.
|
|
5
5
|
|
|
6
6
|
[](https://pypi.org/project/specfact-cli/)
|
|
7
7
|
[](https://pypi.org/project/specfact-cli/)
|
|
@@ -32,7 +32,7 @@ uvx specfact-cli code review run --path . --scope full
|
|
|
32
32
|
**Sample output:**
|
|
33
33
|
|
|
34
34
|
```text
|
|
35
|
-
SpecFact CLI - v0.
|
|
35
|
+
SpecFact CLI - v0.47.4
|
|
36
36
|
|
|
37
37
|
Running Ruff checks...
|
|
38
38
|
Running Radon complexity checks...
|
|
@@ -64,23 +64,30 @@ specfact code review run --path . --scope full
|
|
|
64
64
|
|
|
65
65
|
The sample output comes from a pinned capture against `nold-ai/specfact-demo-repo`. Reproduce it with `docs/_support/readme-first-contact/capture-readme-output.sh`; capture metadata lives alongside the raw logs in `docs/_support/readme-first-contact/sample-output/`.
|
|
66
66
|
|
|
67
|
-
|
|
67
|
+
## AI-bloat defense loop
|
|
68
|
+
|
|
69
|
+
SpecFact is the local AI-bloat defense CLI for Python-first teams using AI IDEs. The Code Review bundle reports `ai_bloat` advisories for code shapes that AI-assisted coding often amplifies: redundant wrappers, passthrough lambdas, identity `try`/`except` blocks, avoidable intermediate lists, and long low-branch functions.
|
|
70
|
+
|
|
71
|
+
For cleanup work, run a JSON review, inspect the cleanup forecast and AI-bloat index, hand remediation packets to your AI IDE, accept only safe changes, then re-run review for proof. The JSON report is the portable handoff artifact for Claude, Codex, Cursor, Copilot, or a headless agent.
|
|
72
|
+
|
|
73
|
+
These findings are bloat-shape detection and cleanup guidance, not AI-authorship detection. Exact simplify flags and report fields live in the [AI bloat quickstart](https://modules.specfact.io/quickstart-ai-bloat/) and [Code Review run guide](https://modules.specfact.io/bundles/code-review/run/) on the modules docs site.
|
|
68
74
|
|
|
69
75
|
## What SpecFact does
|
|
70
76
|
|
|
71
|
-
- **
|
|
77
|
+
- **Defends against AI bloat deterministically** — forecast cleanup impact and route remediation packets to your AI IDE
|
|
78
|
+
- **Reviews AI-assisted changes against evidence** — compare code against contracts, clean-code rules, and policy gates
|
|
72
79
|
- **Extracts structure from existing code** — reverse-engineer brownfield repos before you change them
|
|
73
80
|
- **Blocks drift before merge** — use the same checks locally, in pre-commit, and in CI
|
|
74
|
-
- **
|
|
81
|
+
- **Consumes upstream planning inputs** — treat Spec Kit, OpenSpec, backlog, specs, tests, and contracts as validation evidence
|
|
75
82
|
- **Stays local-first** — no cloud account, no vendor lock-in, no built-in model dependency
|
|
76
83
|
|
|
77
84
|
## What is SpecFact?
|
|
78
85
|
|
|
79
|
-
SpecFact is a local CLI for
|
|
86
|
+
SpecFact is a local CLI for AI-bloat defense, deterministic code review, and backlog/spec/code drift control. It gives solo developers, legacy maintainers, and teams a validation layer around AI-assisted delivery, brownfield reverse engineering, and contract-first reviews.
|
|
80
87
|
|
|
81
88
|
It exists because delivery drifts in predictable ways:
|
|
82
89
|
|
|
83
|
-
- AI-assisted code lands faster than validation
|
|
90
|
+
- AI-assisted code lands faster than cleanup and validation catch up
|
|
84
91
|
- brownfield systems rarely have trustworthy up-to-date specs
|
|
85
92
|
- backlog intent gets reinterpreted before it reaches code
|
|
86
93
|
- teams need the same review rules across IDEs, CI, and pull requests
|
|
@@ -101,7 +108,7 @@ For a **single-hook** setup in downstream repos, keep using the stable id and sc
|
|
|
101
108
|
|
|
102
109
|
```yaml
|
|
103
110
|
- repo: https://github.com/nold-ai/specfact-cli
|
|
104
|
-
rev: v0.
|
|
111
|
+
rev: v0.47.4
|
|
105
112
|
hooks:
|
|
106
113
|
- id: specfact-smart-checks
|
|
107
114
|
```
|
|
@@ -129,17 +136,18 @@ SpecFact uses the same discipline it asks you to trust:
|
|
|
129
136
|
|
|
130
137
|
## For teams and organizations
|
|
131
138
|
|
|
132
|
-
SpecFact still scales beyond the solo-developer entry path
|
|
139
|
+
SpecFact still scales beyond the solo-developer entry path, but validation stays
|
|
140
|
+
the center:
|
|
133
141
|
|
|
134
|
-
- **
|
|
142
|
+
- **Upstream context adapters** for GitHub, Azure DevOps, Jira, Linear, OpenSpec, and Spec Kit inputs
|
|
135
143
|
- **DoR/DoD and policy enforcement** for teams that need repeatable gates
|
|
136
144
|
- **Evidence-backed PR review** with the same checks used locally
|
|
137
145
|
- **CI/CD adoption path** that keeps validation deterministic instead of model-driven
|
|
138
146
|
|
|
139
|
-
|
|
147
|
+
Optional module-deep paths include:
|
|
140
148
|
|
|
141
|
-
- `specfact
|
|
142
|
-
- `specfact
|
|
149
|
+
- `specfact project import ...`
|
|
150
|
+
- `specfact spec validate ...`
|
|
143
151
|
- `specfact backlog verify-readiness --bundle <bundle-name>`
|
|
144
152
|
- `specfact govern ...`
|
|
145
153
|
|
|
@@ -4,8 +4,8 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "specfact-cli"
|
|
7
|
-
version = "0.47.
|
|
8
|
-
description = "
|
|
7
|
+
version = "0.47.11"
|
|
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"
|
|
11
11
|
license = { file = "LICENSE" } # Apache License 2.0
|
|
@@ -14,8 +14,12 @@ authors = [
|
|
|
14
14
|
]
|
|
15
15
|
keywords = [
|
|
16
16
|
"agile",
|
|
17
|
+
"ai-bloat",
|
|
17
18
|
"devops",
|
|
18
19
|
"backlog",
|
|
20
|
+
"code-review",
|
|
21
|
+
"clean-code",
|
|
22
|
+
"technical-debt",
|
|
19
23
|
"scrum",
|
|
20
24
|
"kanban",
|
|
21
25
|
"safe",
|
|
@@ -220,9 +224,9 @@ security-audit = "python scripts/security_audit_gate.py"
|
|
|
220
224
|
# Development scripts
|
|
221
225
|
test = "pytest {args}"
|
|
222
226
|
test-cov = "pytest --cov=src --cov-report=term-missing {args}"
|
|
223
|
-
type-check = "basedpyright --pythonpath $(python -c 'import sys; print(sys.executable)') {args}"
|
|
227
|
+
type-check = "basedpyright --pythonpath \"$(python -c 'import sys; print(sys.executable)')\" {args}"
|
|
224
228
|
# basedpyright --level error: suppress warning noise in pre-commit (Block 1 runs `hatch run lint`).
|
|
225
|
-
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"
|
|
229
|
+
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"
|
|
226
230
|
lint-changed = "python scripts/run_changed_lint.py {args}"
|
|
227
231
|
governance = "pylint src tests tools --reports=y --output-format=parseable"
|
|
228
232
|
format = "ruff check . --fix && ruff format ."
|
|
@@ -230,6 +234,8 @@ format = "ruff check . --fix && ruff format ."
|
|
|
230
234
|
# Code scanning (Semgrep)
|
|
231
235
|
# Semgrep 1.38+ deprecated implicit .semgrep.yml discovery; always pass --config explicitly.
|
|
232
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}"
|
|
233
239
|
semgrep-full = "semgrep --config tools/semgrep {args}"
|
|
234
240
|
scan = "semgrep --config tools/semgrep/async.yml {args}"
|
|
235
241
|
scan-all = "semgrep --config tools/semgrep/async.yml ."
|
|
@@ -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",
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
"""
|
|
2
|
-
SpecFact CLI -
|
|
2
|
+
SpecFact CLI - AI-bloat defense CLI for Python teams.
|
|
3
3
|
|
|
4
4
|
This package provides command-line tools for:
|
|
5
|
+
- Defending AI-assisted code against cleanup bloat
|
|
5
6
|
- Turning code into clear specs and plans
|
|
6
7
|
- Keeping backlog, specs, tests, and code in sync
|
|
7
8
|
- Enforcing validation and contract checks before production
|
|
@@ -75,6 +76,6 @@ def _install_progressive_disclosure() -> None:
|
|
|
75
76
|
# keeps missing-command and missing-parameter UX consistent outside the root CLI too.
|
|
76
77
|
_install_progressive_disclosure()
|
|
77
78
|
|
|
78
|
-
__version__ = "0.47.
|
|
79
|
+
__version__ = "0.47.11"
|
|
79
80
|
|
|
80
81
|
__all__ = ["__version__"]
|
|
@@ -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
|
)
|
|
@@ -87,19 +87,12 @@ KNOWN_BUNDLE_GROUP_OR_SHIM_NAMES: frozenset[str] = frozenset(
|
|
|
87
87
|
"project",
|
|
88
88
|
"spec",
|
|
89
89
|
"govern",
|
|
90
|
-
"plan",
|
|
91
|
-
"validate",
|
|
92
90
|
"contract",
|
|
93
91
|
"sdd",
|
|
94
92
|
"generate",
|
|
95
93
|
"enforce",
|
|
96
94
|
"patch",
|
|
97
|
-
"migrate",
|
|
98
|
-
"repro",
|
|
99
|
-
"drift",
|
|
100
|
-
"analyze",
|
|
101
95
|
"policy",
|
|
102
|
-
"sync",
|
|
103
96
|
}
|
|
104
97
|
)
|
|
105
98
|
|
|
@@ -109,14 +102,7 @@ _INVOKED_TO_MARKETPLACE_MODULE: dict[str, str] = {
|
|
|
109
102
|
"backlog": "nold-ai/specfact-backlog",
|
|
110
103
|
"policy": "nold-ai/specfact-backlog",
|
|
111
104
|
"code": "nold-ai/specfact-codebase",
|
|
112
|
-
"analyze": "nold-ai/specfact-codebase",
|
|
113
|
-
"drift": "nold-ai/specfact-codebase",
|
|
114
|
-
"validate": "nold-ai/specfact-codebase",
|
|
115
|
-
"repro": "nold-ai/specfact-codebase",
|
|
116
105
|
"project": "nold-ai/specfact-project",
|
|
117
|
-
"plan": "nold-ai/specfact-project",
|
|
118
|
-
"sync": "nold-ai/specfact-project",
|
|
119
|
-
"migrate": "nold-ai/specfact-project",
|
|
120
106
|
"spec": "nold-ai/specfact-spec",
|
|
121
107
|
"contract": "nold-ai/specfact-spec",
|
|
122
108
|
"sdd": "nold-ai/specfact-spec",
|
|
@@ -126,6 +112,18 @@ _INVOKED_TO_MARKETPLACE_MODULE: dict[str, str] = {
|
|
|
126
112
|
"patch": "nold-ai/specfact-govern",
|
|
127
113
|
}
|
|
128
114
|
|
|
115
|
+
# Removed flat root aliases -> canonical grouped replacement (see docs/migration/migration-guide.md).
|
|
116
|
+
# These must never route through marketplace module availability diagnostics.
|
|
117
|
+
_REMOVED_FLAT_ALIAS_TO_CANONICAL: dict[str, str] = {
|
|
118
|
+
"plan": "specfact project",
|
|
119
|
+
"validate": "specfact code validate",
|
|
120
|
+
"analyze": "specfact code analyze",
|
|
121
|
+
"drift": "specfact code drift",
|
|
122
|
+
"repro": "specfact code repro",
|
|
123
|
+
"sync": "specfact project sync",
|
|
124
|
+
"migrate": "specfact project migrate",
|
|
125
|
+
}
|
|
126
|
+
|
|
129
127
|
|
|
130
128
|
def _print_missing_bundle_command_help(invoked: str) -> None:
|
|
131
129
|
"""Print install guidance when a bundle group or shim is not registered."""
|
|
@@ -168,34 +166,81 @@ def _print_missing_bundle_command_help(invoked: str) -> None:
|
|
|
168
166
|
)
|
|
169
167
|
|
|
170
168
|
|
|
169
|
+
def _print_removed_flat_alias_help(invoked: str) -> None:
|
|
170
|
+
"""Print canonical grouped-command guidance for a removed flat root alias."""
|
|
171
|
+
canonical = _REMOVED_FLAT_ALIAS_TO_CANONICAL[invoked]
|
|
172
|
+
console = get_configured_console()
|
|
173
|
+
console.print(
|
|
174
|
+
f"[bold red]No such command '{invoked}'.[/bold red]\n"
|
|
175
|
+
f"The flat [bold]specfact {invoked}[/bold] command was removed. "
|
|
176
|
+
f"Use [bold]{canonical} ...[/bold] instead."
|
|
177
|
+
)
|
|
178
|
+
|
|
179
|
+
|
|
180
|
+
def _print_unresolved_root_token_help(invoked: str) -> bool:
|
|
181
|
+
"""Render guidance for an unresolved root token; return True when guidance was printed."""
|
|
182
|
+
if invoked in _REMOVED_FLAT_ALIAS_TO_CANONICAL:
|
|
183
|
+
_print_removed_flat_alias_help(invoked)
|
|
184
|
+
return True
|
|
185
|
+
if invoked in KNOWN_BUNDLE_GROUP_OR_SHIM_NAMES:
|
|
186
|
+
_print_missing_bundle_command_help(invoked)
|
|
187
|
+
return True
|
|
188
|
+
return False
|
|
189
|
+
|
|
190
|
+
|
|
191
|
+
def _resolve_root_command_with_guidance(resolve: Callable[[Any, list[str]], Any], ctx: Any, args: list[str]) -> Any:
|
|
192
|
+
"""Resolve a root command, replacing unresolved known tokens with actionable guidance."""
|
|
193
|
+
if not args:
|
|
194
|
+
return resolve(ctx, args)
|
|
195
|
+
invoked = args[0]
|
|
196
|
+
try:
|
|
197
|
+
result = resolve(ctx, args)
|
|
198
|
+
except click.UsageError:
|
|
199
|
+
if _print_unresolved_root_token_help(invoked):
|
|
200
|
+
raise SystemExit(1) from None
|
|
201
|
+
raise
|
|
202
|
+
except ValueError as exc:
|
|
203
|
+
if _print_unresolved_root_token_help(invoked):
|
|
204
|
+
raise SystemExit(1) from exc
|
|
205
|
+
raise
|
|
206
|
+
_name, cmd, remaining = result
|
|
207
|
+
if cmd is not None or not remaining:
|
|
208
|
+
return result
|
|
209
|
+
unresolved = remaining[0]
|
|
210
|
+
if _print_unresolved_root_token_help(unresolved):
|
|
211
|
+
raise SystemExit(1)
|
|
212
|
+
return result
|
|
213
|
+
|
|
214
|
+
|
|
171
215
|
class _RootCLIGroup(ProgressiveDisclosureGroup):
|
|
172
216
|
"""Root group that shows actionable error when an unknown command is a known bundle group/shim."""
|
|
173
217
|
|
|
174
218
|
@ensure(lambda result: isinstance(result, tuple) and len(result) == 3, "result must be a 3-tuple")
|
|
175
219
|
def resolve_command(self, ctx: Any, args: list[str]) -> Any:
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
raise
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
220
|
+
return _resolve_root_command_with_guidance(super().resolve_command, ctx, args)
|
|
221
|
+
|
|
222
|
+
|
|
223
|
+
def _patch_missing_bundle_resolution(group: click.Group) -> click.Group:
|
|
224
|
+
"""Patch a generated Typer root group to preserve missing bundle diagnostics."""
|
|
225
|
+
if getattr(group, "_specfact_missing_bundle_resolution", False):
|
|
226
|
+
return group
|
|
227
|
+
|
|
228
|
+
original_get_command = group.get_command
|
|
229
|
+
original_resolve_command = group.resolve_command
|
|
230
|
+
|
|
231
|
+
def _get_command(ctx: click.Context, cmd_name: str) -> click.Command | None:
|
|
232
|
+
command = original_get_command(ctx, cmd_name)
|
|
233
|
+
if command is None and _print_unresolved_root_token_help(cmd_name):
|
|
234
|
+
raise SystemExit(1)
|
|
235
|
+
return command
|
|
236
|
+
|
|
237
|
+
def _resolve_command(ctx: click.Context, args: list[str]) -> Any:
|
|
238
|
+
return _resolve_root_command_with_guidance(original_resolve_command, ctx, args)
|
|
239
|
+
|
|
240
|
+
group.get_command = _get_command # type: ignore[method-assign]
|
|
241
|
+
group.resolve_command = _resolve_command # type: ignore[method-assign]
|
|
242
|
+
group._specfact_missing_bundle_resolution = True # type: ignore[attr-defined]
|
|
243
|
+
return group
|
|
199
244
|
|
|
200
245
|
|
|
201
246
|
# Map shell names for completion support
|
|
@@ -581,8 +626,7 @@ def _load_lazy_delegate_typer(cmd_name: str) -> typer.Typer:
|
|
|
581
626
|
try:
|
|
582
627
|
return CommandRegistry.get_typer(resolved_name)
|
|
583
628
|
except ValueError as exc:
|
|
584
|
-
if cmd_name
|
|
585
|
-
_print_missing_bundle_command_help(cmd_name)
|
|
629
|
+
if _print_unresolved_root_token_help(cmd_name):
|
|
586
630
|
raise SystemExit(1) from None
|
|
587
631
|
_raise_lazy_delegate_click_exception(exc)
|
|
588
632
|
raise AssertionError("unreachable") from None
|
|
@@ -866,6 +910,8 @@ def _get_command(typer_instance: typer.Typer) -> click.Command:
|
|
|
866
910
|
return _build_lazy_delegate_group(cmd_name, help_str)
|
|
867
911
|
assert _typer_get_command_original is not None
|
|
868
912
|
result = _typer_get_command_original(typer_instance)
|
|
913
|
+
if typer_instance is app and isinstance(result, click.Group):
|
|
914
|
+
result = _patch_missing_bundle_resolution(result)
|
|
869
915
|
flatten_name = getattr(typer_instance, "_specfact_flatten_same_name", None)
|
|
870
916
|
if isinstance(flatten_name, str) and isinstance(result, click.Group) and flatten_name in result.commands:
|
|
871
917
|
_flatten_specfact_nested_subgroup(result, flatten_name)
|
|
@@ -894,6 +940,8 @@ def _get_group_from_info_wrapper(
|
|
|
894
940
|
suggest_commands=suggest_commands,
|
|
895
941
|
rich_markup_mode=rich_markup_mode,
|
|
896
942
|
)
|
|
943
|
+
if typer_instance is app:
|
|
944
|
+
result = _patch_missing_bundle_resolution(result)
|
|
897
945
|
flatten_name = getattr(typer_instance, "_specfact_flatten_same_name", None) if typer_instance else None
|
|
898
946
|
if isinstance(flatten_name, str) and flatten_name in result.commands:
|
|
899
947
|
_flatten_specfact_nested_subgroup(result, flatten_name)
|
{specfact_cli-0.47.3 → specfact_cli-0.47.11}/src/specfact_cli/generators/persona_exporter.py
RENAMED
|
@@ -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.3 → specfact_cli-0.47.11}/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,
|
{specfact_cli-0.47.3 → specfact_cli-0.47.11}/src/specfact_cli/generators/report_generator.py
RENAMED
|
@@ -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.3 → specfact_cli-0.47.11}/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,
|
|
@@ -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==
|
|
@@ -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.3 → specfact_cli-0.47.11}/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
|
|