specfact-cli 0.40.1__tar.gz → 0.40.3__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.40.1 → specfact_cli-0.40.3}/PKG-INFO +3 -1
- {specfact_cli-0.40.1 → specfact_cli-0.40.3}/README.md +2 -0
- {specfact_cli-0.40.1 → specfact_cli-0.40.3}/pyproject.toml +1 -2
- {specfact_cli-0.40.1 → specfact_cli-0.40.3}/src/__init__.py +1 -1
- {specfact_cli-0.40.1 → specfact_cli-0.40.3}/src/specfact_cli/__init__.py +1 -1
- {specfact_cli-0.40.1 → specfact_cli-0.40.3}/src/specfact_cli/adapters/ado.py +15 -0
- specfact_cli-0.40.3/src/specfact_cli/backlog/__init__.py +14 -0
- specfact_cli-0.40.3/src/specfact_cli/backlog/adapters/__init__.py +8 -0
- {specfact_cli-0.40.1 → specfact_cli-0.40.3}/src/specfact_cli/common/logger_setup.py +11 -5
- {specfact_cli-0.40.1 → specfact_cli-0.40.3}/src/specfact_cli/common/logging_utils.py +7 -1
- {specfact_cli-0.40.1 → specfact_cli-0.40.3}/src/specfact_cli/groups/__init__.py +1 -3
- specfact_cli-0.40.3/src/specfact_cli/groups/member_group.py +60 -0
- {specfact_cli-0.40.1 → specfact_cli-0.40.3}/src/specfact_cli/modules/module_registry/module-package.yaml +3 -3
- {specfact_cli-0.40.1 → specfact_cli-0.40.3}/src/specfact_cli/modules/module_registry/src/commands.py +22 -4
- {specfact_cli-0.40.1 → specfact_cli-0.40.3}/src/specfact_cli/registry/module_discovery.py +7 -0
- {specfact_cli-0.40.1 → specfact_cli-0.40.3}/src/specfact_cli/registry/module_installer.py +1 -1
- {specfact_cli-0.40.1 → specfact_cli-0.40.3}/src/specfact_cli/registry/module_packages.py +22 -18
- {specfact_cli-0.40.1 → specfact_cli-0.40.3}/src/specfact_cli/utils/ide_setup.py +0 -4
- specfact_cli-0.40.3/src/specfact_cli/validation/__init__.py +14 -0
- specfact_cli-0.40.3/src/specfact_cli/validation/command_audit.py +165 -0
- specfact_cli-0.40.1/resources/prompts/specfact.backlog-add.md +0 -90
- specfact_cli-0.40.1/resources/prompts/specfact.backlog-daily.md +0 -125
- specfact_cli-0.40.1/resources/prompts/specfact.backlog-refine.md +0 -557
- specfact_cli-0.40.1/resources/prompts/specfact.sync-backlog.md +0 -557
- specfact_cli-0.40.1/resources/templates/backlog/field_mappings/ado_agile.yaml +0 -24
- specfact_cli-0.40.1/resources/templates/backlog/field_mappings/ado_default.yaml +0 -24
- specfact_cli-0.40.1/resources/templates/backlog/field_mappings/ado_kanban.yaml +0 -26
- specfact_cli-0.40.1/resources/templates/backlog/field_mappings/ado_safe.yaml +0 -29
- specfact_cli-0.40.1/resources/templates/backlog/field_mappings/ado_scrum.yaml +0 -24
- specfact_cli-0.40.1/resources/templates/backlog/frameworks/safe/safe_feature_v1.yaml +0 -26
- specfact_cli-0.40.1/resources/templates/backlog/personas/developer/developer_task_v1.yaml +0 -28
- specfact_cli-0.40.1/src/specfact_cli/backlog/__init__.py +0 -24
- specfact_cli-0.40.1/src/specfact_cli/backlog/adapters/__init__.py +0 -13
- specfact_cli-0.40.1/src/specfact_cli/backlog/adapters/local_yaml_adapter.py +0 -177
- specfact_cli-0.40.1/src/specfact_cli/backlog/ai_refiner.py +0 -523
- specfact_cli-0.40.1/src/specfact_cli/backlog/format_detector.py +0 -50
- specfact_cli-0.40.1/src/specfact_cli/backlog/formats/__init__.py +0 -14
- specfact_cli-0.40.1/src/specfact_cli/backlog/formats/base.py +0 -98
- specfact_cli-0.40.1/src/specfact_cli/backlog/formats/markdown_format.py +0 -130
- specfact_cli-0.40.1/src/specfact_cli/backlog/formats/structured_format.py +0 -97
- specfact_cli-0.40.1/src/specfact_cli/backlog/template_detector.py +0 -288
- specfact_cli-0.40.1/src/specfact_cli/commands/backlog_commands.py +0 -18
- specfact_cli-0.40.1/src/specfact_cli/groups/backlog_group.py +0 -63
- specfact_cli-0.40.1/src/specfact_cli/templates/defaults/defect_v1.yaml +0 -22
- specfact_cli-0.40.1/src/specfact_cli/templates/defaults/enabler_v1.yaml +0 -21
- specfact_cli-0.40.1/src/specfact_cli/templates/defaults/spike_v1.yaml +0 -20
- specfact_cli-0.40.1/src/specfact_cli/templates/defaults/user_story_v1.yaml +0 -19
- specfact_cli-0.40.1/src/specfact_cli/templates/frameworks/scrum/user_story_v1.yaml +0 -23
- specfact_cli-0.40.1/src/specfact_cli/templates/personas/product-owner/user_story_v1.yaml +0 -24
- specfact_cli-0.40.1/src/specfact_cli/templates/providers/ado/work_item_v1.yaml +0 -21
- {specfact_cli-0.40.1 → specfact_cli-0.40.3}/.gitignore +0 -0
- {specfact_cli-0.40.1 → specfact_cli-0.40.3}/LICENSE +0 -0
- {specfact_cli-0.40.1 → specfact_cli-0.40.3}/resources/keys/README.md +0 -0
- {specfact_cli-0.40.1 → specfact_cli-0.40.3}/resources/keys/module-signing-public.pem +0 -0
- {specfact_cli-0.40.1 → specfact_cli-0.40.3}/resources/mappings/node-async.yaml +0 -0
- {specfact_cli-0.40.1 → specfact_cli-0.40.3}/resources/mappings/python-async.yaml +0 -0
- {specfact_cli-0.40.1 → specfact_cli-0.40.3}/resources/mappings/speckit-default.yaml +0 -0
- {specfact_cli-0.40.1 → specfact_cli-0.40.3}/resources/prompts/shared/cli-enforcement.md +0 -0
- {specfact_cli-0.40.1 → specfact_cli-0.40.3}/resources/prompts/specfact.01-import.md +0 -0
- {specfact_cli-0.40.1 → specfact_cli-0.40.3}/resources/prompts/specfact.02-plan.md +0 -0
- {specfact_cli-0.40.1 → specfact_cli-0.40.3}/resources/prompts/specfact.03-review.md +0 -0
- {specfact_cli-0.40.1 → specfact_cli-0.40.3}/resources/prompts/specfact.04-sdd.md +0 -0
- {specfact_cli-0.40.1 → specfact_cli-0.40.3}/resources/prompts/specfact.05-enforce.md +0 -0
- {specfact_cli-0.40.1 → specfact_cli-0.40.3}/resources/prompts/specfact.06-sync.md +0 -0
- {specfact_cli-0.40.1 → specfact_cli-0.40.3}/resources/prompts/specfact.07-contracts.md +0 -0
- {specfact_cli-0.40.1 → specfact_cli-0.40.3}/resources/prompts/specfact.compare.md +0 -0
- {specfact_cli-0.40.1 → specfact_cli-0.40.3}/resources/prompts/specfact.validate.md +0 -0
- {specfact_cli-0.40.1 → specfact_cli-0.40.3}/resources/schemas/deviation.schema.json +0 -0
- {specfact_cli-0.40.1 → specfact_cli-0.40.3}/resources/schemas/plan.schema.json +0 -0
- {specfact_cli-0.40.1 → specfact_cli-0.40.3}/resources/schemas/protocol.schema.json +0 -0
- {specfact_cli-0.40.1 → specfact_cli-0.40.3}/resources/templates/github-action.yml.j2 +0 -0
- {specfact_cli-0.40.1 → specfact_cli-0.40.3}/resources/templates/persona/architect.md.j2 +0 -0
- {specfact_cli-0.40.1 → specfact_cli-0.40.3}/resources/templates/persona/developer.md.j2 +0 -0
- {specfact_cli-0.40.1 → specfact_cli-0.40.3}/resources/templates/persona/product-owner.md.j2 +0 -0
- {specfact_cli-0.40.1 → specfact_cli-0.40.3}/resources/templates/plan.bundle.yaml.j2 +0 -0
- {specfact_cli-0.40.1 → specfact_cli-0.40.3}/resources/templates/policies/kanban.yaml +0 -0
- {specfact_cli-0.40.1 → specfact_cli-0.40.3}/resources/templates/policies/mixed.yaml +0 -0
- {specfact_cli-0.40.1 → specfact_cli-0.40.3}/resources/templates/policies/safe.yaml +0 -0
- {specfact_cli-0.40.1 → specfact_cli-0.40.3}/resources/templates/policies/scrum.yaml +0 -0
- {specfact_cli-0.40.1 → specfact_cli-0.40.3}/resources/templates/pr-template.md.j2 +0 -0
- {specfact_cli-0.40.1 → specfact_cli-0.40.3}/resources/templates/protocol.yaml.j2 +0 -0
- {specfact_cli-0.40.1 → specfact_cli-0.40.3}/resources/templates/telemetry.yaml.example +0 -0
- {specfact_cli-0.40.1 → specfact_cli-0.40.3}/src/specfact_cli/__main__.py +0 -0
- {specfact_cli-0.40.1 → specfact_cli-0.40.3}/src/specfact_cli/adapters/__init__.py +0 -0
- {specfact_cli-0.40.1 → specfact_cli-0.40.3}/src/specfact_cli/adapters/backlog_base.py +0 -0
- {specfact_cli-0.40.1 → specfact_cli-0.40.3}/src/specfact_cli/adapters/base.py +0 -0
- {specfact_cli-0.40.1 → specfact_cli-0.40.3}/src/specfact_cli/adapters/github.py +0 -0
- {specfact_cli-0.40.1 → specfact_cli-0.40.3}/src/specfact_cli/adapters/openspec.py +0 -0
- {specfact_cli-0.40.1 → specfact_cli-0.40.3}/src/specfact_cli/adapters/openspec_parser.py +0 -0
- {specfact_cli-0.40.1 → specfact_cli-0.40.3}/src/specfact_cli/adapters/registry.py +0 -0
- {specfact_cli-0.40.1 → specfact_cli-0.40.3}/src/specfact_cli/adapters/speckit.py +0 -0
- {specfact_cli-0.40.1 → specfact_cli-0.40.3}/src/specfact_cli/agents/__init__.py +0 -0
- {specfact_cli-0.40.1 → specfact_cli-0.40.3}/src/specfact_cli/agents/analyze_agent.py +0 -0
- {specfact_cli-0.40.1 → specfact_cli-0.40.3}/src/specfact_cli/agents/base.py +0 -0
- {specfact_cli-0.40.1 → specfact_cli-0.40.3}/src/specfact_cli/agents/plan_agent.py +0 -0
- {specfact_cli-0.40.1 → specfact_cli-0.40.3}/src/specfact_cli/agents/registry.py +0 -0
- {specfact_cli-0.40.1 → specfact_cli-0.40.3}/src/specfact_cli/agents/sync_agent.py +0 -0
- {specfact_cli-0.40.1 → specfact_cli-0.40.3}/src/specfact_cli/analyzers/__init__.py +0 -0
- {specfact_cli-0.40.1 → specfact_cli-0.40.3}/src/specfact_cli/analyzers/ambiguity_scanner.py +0 -0
- {specfact_cli-0.40.1 → specfact_cli-0.40.3}/src/specfact_cli/analyzers/code_analyzer.py +0 -0
- {specfact_cli-0.40.1 → specfact_cli-0.40.3}/src/specfact_cli/analyzers/constitution_evidence_extractor.py +0 -0
- {specfact_cli-0.40.1 → specfact_cli-0.40.3}/src/specfact_cli/analyzers/contract_extractor.py +0 -0
- {specfact_cli-0.40.1 → specfact_cli-0.40.3}/src/specfact_cli/analyzers/control_flow_analyzer.py +0 -0
- {specfact_cli-0.40.1 → specfact_cli-0.40.3}/src/specfact_cli/analyzers/graph_analyzer.py +0 -0
- {specfact_cli-0.40.1 → specfact_cli-0.40.3}/src/specfact_cli/analyzers/relationship_mapper.py +0 -0
- {specfact_cli-0.40.1 → specfact_cli-0.40.3}/src/specfact_cli/analyzers/requirement_extractor.py +0 -0
- {specfact_cli-0.40.1 → specfact_cli-0.40.3}/src/specfact_cli/analyzers/test_pattern_extractor.py +0 -0
- {specfact_cli-0.40.1 → specfact_cli-0.40.3}/src/specfact_cli/backlog/adapters/base.py +0 -0
- {specfact_cli-0.40.1 → specfact_cli-0.40.3}/src/specfact_cli/backlog/converter.py +0 -0
- {specfact_cli-0.40.1 → specfact_cli-0.40.3}/src/specfact_cli/backlog/filters.py +0 -0
- {specfact_cli-0.40.1 → specfact_cli-0.40.3}/src/specfact_cli/backlog/mappers/__init__.py +0 -0
- {specfact_cli-0.40.1 → specfact_cli-0.40.3}/src/specfact_cli/backlog/mappers/ado_mapper.py +0 -0
- {specfact_cli-0.40.1 → specfact_cli-0.40.3}/src/specfact_cli/backlog/mappers/base.py +0 -0
- {specfact_cli-0.40.1 → specfact_cli-0.40.3}/src/specfact_cli/backlog/mappers/github_mapper.py +0 -0
- {specfact_cli-0.40.1 → specfact_cli-0.40.3}/src/specfact_cli/backlog/mappers/template_config.py +0 -0
- {specfact_cli-0.40.1 → specfact_cli-0.40.3}/src/specfact_cli/cli.py +0 -0
- {specfact_cli-0.40.1 → specfact_cli-0.40.3}/src/specfact_cli/commands/__init__.py +0 -0
- {specfact_cli-0.40.1 → specfact_cli-0.40.3}/src/specfact_cli/commands/_bundle_shim.py +0 -0
- {specfact_cli-0.40.1 → specfact_cli-0.40.3}/src/specfact_cli/commands/analyze.py +0 -0
- {specfact_cli-0.40.1 → specfact_cli-0.40.3}/src/specfact_cli/commands/contract_cmd.py +0 -0
- {specfact_cli-0.40.1 → specfact_cli-0.40.3}/src/specfact_cli/commands/drift.py +0 -0
- {specfact_cli-0.40.1 → specfact_cli-0.40.3}/src/specfact_cli/commands/enforce.py +0 -0
- {specfact_cli-0.40.1 → specfact_cli-0.40.3}/src/specfact_cli/commands/generate.py +0 -0
- {specfact_cli-0.40.1 → specfact_cli-0.40.3}/src/specfact_cli/commands/import_cmd.py +0 -0
- {specfact_cli-0.40.1 → specfact_cli-0.40.3}/src/specfact_cli/commands/init.py +0 -0
- {specfact_cli-0.40.1 → specfact_cli-0.40.3}/src/specfact_cli/commands/migrate.py +0 -0
- {specfact_cli-0.40.1 → specfact_cli-0.40.3}/src/specfact_cli/commands/plan.py +0 -0
- {specfact_cli-0.40.1 → specfact_cli-0.40.3}/src/specfact_cli/commands/project_cmd.py +0 -0
- {specfact_cli-0.40.1 → specfact_cli-0.40.3}/src/specfact_cli/commands/repro.py +0 -0
- {specfact_cli-0.40.1 → specfact_cli-0.40.3}/src/specfact_cli/commands/sdd.py +0 -0
- {specfact_cli-0.40.1 → specfact_cli-0.40.3}/src/specfact_cli/commands/spec.py +0 -0
- {specfact_cli-0.40.1 → specfact_cli-0.40.3}/src/specfact_cli/commands/sync.py +0 -0
- {specfact_cli-0.40.1 → specfact_cli-0.40.3}/src/specfact_cli/commands/update.py +0 -0
- {specfact_cli-0.40.1 → specfact_cli-0.40.3}/src/specfact_cli/commands/validate.py +0 -0
- {specfact_cli-0.40.1 → specfact_cli-0.40.3}/src/specfact_cli/common/__init__.py +0 -0
- {specfact_cli-0.40.1 → specfact_cli-0.40.3}/src/specfact_cli/common/bundle_factory.py +0 -0
- {specfact_cli-0.40.1 → specfact_cli-0.40.3}/src/specfact_cli/common/text_utils.py +0 -0
- {specfact_cli-0.40.1 → specfact_cli-0.40.3}/src/specfact_cli/common/utils.py +0 -0
- {specfact_cli-0.40.1 → specfact_cli-0.40.3}/src/specfact_cli/comparators/__init__.py +0 -0
- {specfact_cli-0.40.1 → specfact_cli-0.40.3}/src/specfact_cli/comparators/plan_comparator.py +0 -0
- {specfact_cli-0.40.1 → specfact_cli-0.40.3}/src/specfact_cli/contracts/__init__.py +0 -0
- {specfact_cli-0.40.1 → specfact_cli-0.40.3}/src/specfact_cli/contracts/crosshair_props.py +0 -0
- {specfact_cli-0.40.1 → specfact_cli-0.40.3}/src/specfact_cli/contracts/module_interface.py +0 -0
- {specfact_cli-0.40.1 → specfact_cli-0.40.3}/src/specfact_cli/enrichers/constitution_enricher.py +0 -0
- {specfact_cli-0.40.1 → specfact_cli-0.40.3}/src/specfact_cli/enrichers/plan_enricher.py +0 -0
- {specfact_cli-0.40.1 → specfact_cli-0.40.3}/src/specfact_cli/generators/__init__.py +0 -0
- {specfact_cli-0.40.1 → specfact_cli-0.40.3}/src/specfact_cli/generators/contract_generator.py +0 -0
- {specfact_cli-0.40.1 → specfact_cli-0.40.3}/src/specfact_cli/generators/openapi_extractor.py +0 -0
- {specfact_cli-0.40.1 → specfact_cli-0.40.3}/src/specfact_cli/generators/persona_exporter.py +0 -0
- {specfact_cli-0.40.1 → specfact_cli-0.40.3}/src/specfact_cli/generators/plan_generator.py +0 -0
- {specfact_cli-0.40.1 → specfact_cli-0.40.3}/src/specfact_cli/generators/protocol_generator.py +0 -0
- {specfact_cli-0.40.1 → specfact_cli-0.40.3}/src/specfact_cli/generators/report_generator.py +0 -0
- {specfact_cli-0.40.1 → specfact_cli-0.40.3}/src/specfact_cli/generators/task_generator.py +0 -0
- {specfact_cli-0.40.1 → specfact_cli-0.40.3}/src/specfact_cli/generators/test_to_openapi.py +0 -0
- {specfact_cli-0.40.1 → specfact_cli-0.40.3}/src/specfact_cli/generators/workflow_generator.py +0 -0
- {specfact_cli-0.40.1 → specfact_cli-0.40.3}/src/specfact_cli/groups/codebase_group.py +0 -0
- {specfact_cli-0.40.1 → specfact_cli-0.40.3}/src/specfact_cli/groups/govern_group.py +0 -0
- {specfact_cli-0.40.1 → specfact_cli-0.40.3}/src/specfact_cli/groups/project_group.py +0 -0
- {specfact_cli-0.40.1 → specfact_cli-0.40.3}/src/specfact_cli/groups/spec_group.py +0 -0
- {specfact_cli-0.40.1 → specfact_cli-0.40.3}/src/specfact_cli/importers/__init__.py +0 -0
- {specfact_cli-0.40.1 → specfact_cli-0.40.3}/src/specfact_cli/importers/speckit_converter.py +0 -0
- {specfact_cli-0.40.1 → specfact_cli-0.40.3}/src/specfact_cli/importers/speckit_scanner.py +0 -0
- {specfact_cli-0.40.1 → specfact_cli-0.40.3}/src/specfact_cli/integrations/__init__.py +0 -0
- {specfact_cli-0.40.1 → specfact_cli-0.40.3}/src/specfact_cli/integrations/specmatic.py +0 -0
- {specfact_cli-0.40.1 → specfact_cli-0.40.3}/src/specfact_cli/merge/__init__.py +0 -0
- {specfact_cli-0.40.1 → specfact_cli-0.40.3}/src/specfact_cli/merge/resolver.py +0 -0
- {specfact_cli-0.40.1 → specfact_cli-0.40.3}/src/specfact_cli/migrations/__init__.py +0 -0
- {specfact_cli-0.40.1 → specfact_cli-0.40.3}/src/specfact_cli/migrations/plan_migrator.py +0 -0
- {specfact_cli-0.40.1 → specfact_cli-0.40.3}/src/specfact_cli/models/__init__.py +0 -0
- {specfact_cli-0.40.1 → specfact_cli-0.40.3}/src/specfact_cli/models/backlog_item.py +0 -0
- {specfact_cli-0.40.1 → specfact_cli-0.40.3}/src/specfact_cli/models/bridge.py +0 -0
- {specfact_cli-0.40.1 → specfact_cli-0.40.3}/src/specfact_cli/models/capabilities.py +0 -0
- {specfact_cli-0.40.1 → specfact_cli-0.40.3}/src/specfact_cli/models/change.py +0 -0
- {specfact_cli-0.40.1 → specfact_cli-0.40.3}/src/specfact_cli/models/contract.py +0 -0
- {specfact_cli-0.40.1 → specfact_cli-0.40.3}/src/specfact_cli/models/deviation.py +0 -0
- {specfact_cli-0.40.1 → specfact_cli-0.40.3}/src/specfact_cli/models/dor_config.py +0 -0
- {specfact_cli-0.40.1 → specfact_cli-0.40.3}/src/specfact_cli/models/enforcement.py +0 -0
- {specfact_cli-0.40.1 → specfact_cli-0.40.3}/src/specfact_cli/models/module_package.py +0 -0
- {specfact_cli-0.40.1 → specfact_cli-0.40.3}/src/specfact_cli/models/persona_template.py +0 -0
- {specfact_cli-0.40.1 → specfact_cli-0.40.3}/src/specfact_cli/models/plan.py +0 -0
- {specfact_cli-0.40.1 → specfact_cli-0.40.3}/src/specfact_cli/models/project.py +0 -0
- {specfact_cli-0.40.1 → specfact_cli-0.40.3}/src/specfact_cli/models/protocol.py +0 -0
- {specfact_cli-0.40.1 → specfact_cli-0.40.3}/src/specfact_cli/models/quality.py +0 -0
- {specfact_cli-0.40.1 → specfact_cli-0.40.3}/src/specfact_cli/models/sdd.py +0 -0
- {specfact_cli-0.40.1 → specfact_cli-0.40.3}/src/specfact_cli/models/source_tracking.py +0 -0
- {specfact_cli-0.40.1 → specfact_cli-0.40.3}/src/specfact_cli/models/task.py +0 -0
- {specfact_cli-0.40.1 → specfact_cli-0.40.3}/src/specfact_cli/models/validation.py +0 -0
- {specfact_cli-0.40.1 → specfact_cli-0.40.3}/src/specfact_cli/modes/__init__.py +0 -0
- {specfact_cli-0.40.1 → specfact_cli-0.40.3}/src/specfact_cli/modes/detector.py +0 -0
- {specfact_cli-0.40.1 → specfact_cli-0.40.3}/src/specfact_cli/modes/router.py +0 -0
- {specfact_cli-0.40.1 → specfact_cli-0.40.3}/src/specfact_cli/modules/__init__.py +0 -0
- {specfact_cli-0.40.1 → specfact_cli-0.40.3}/src/specfact_cli/modules/_bundle_import.py +0 -0
- {specfact_cli-0.40.1 → specfact_cli-0.40.3}/src/specfact_cli/modules/init/module-package.yaml +0 -0
- {specfact_cli-0.40.1 → specfact_cli-0.40.3}/src/specfact_cli/modules/init/src/__init__.py +0 -0
- {specfact_cli-0.40.1 → specfact_cli-0.40.3}/src/specfact_cli/modules/init/src/app.py +0 -0
- {specfact_cli-0.40.1 → specfact_cli-0.40.3}/src/specfact_cli/modules/init/src/commands.py +0 -0
- {specfact_cli-0.40.1 → specfact_cli-0.40.3}/src/specfact_cli/modules/init/src/first_run_selection.py +0 -0
- {specfact_cli-0.40.1 → specfact_cli-0.40.3}/src/specfact_cli/modules/module_io_shim.py +0 -0
- {specfact_cli-0.40.1 → specfact_cli-0.40.3}/src/specfact_cli/modules/module_registry/src/__init__.py +0 -0
- {specfact_cli-0.40.1 → specfact_cli-0.40.3}/src/specfact_cli/modules/module_registry/src/app.py +0 -0
- {specfact_cli-0.40.1 → specfact_cli-0.40.3}/src/specfact_cli/modules/upgrade/module-package.yaml +0 -0
- {specfact_cli-0.40.1 → specfact_cli-0.40.3}/src/specfact_cli/modules/upgrade/src/__init__.py +0 -0
- {specfact_cli-0.40.1 → specfact_cli-0.40.3}/src/specfact_cli/modules/upgrade/src/app.py +0 -0
- {specfact_cli-0.40.1 → specfact_cli-0.40.3}/src/specfact_cli/modules/upgrade/src/commands.py +0 -0
- {specfact_cli-0.40.1 → specfact_cli-0.40.3}/src/specfact_cli/parsers/__init__.py +0 -0
- {specfact_cli-0.40.1 → specfact_cli-0.40.3}/src/specfact_cli/parsers/persona_importer.py +0 -0
- {specfact_cli-0.40.1 → specfact_cli-0.40.3}/src/specfact_cli/registry/__init__.py +0 -0
- {specfact_cli-0.40.1 → specfact_cli-0.40.3}/src/specfact_cli/registry/alias_manager.py +0 -0
- {specfact_cli-0.40.1 → specfact_cli-0.40.3}/src/specfact_cli/registry/bootstrap.py +0 -0
- {specfact_cli-0.40.1 → specfact_cli-0.40.3}/src/specfact_cli/registry/bridge_registry.py +0 -0
- {specfact_cli-0.40.1 → specfact_cli-0.40.3}/src/specfact_cli/registry/crypto_validator.py +0 -0
- {specfact_cli-0.40.1 → specfact_cli-0.40.3}/src/specfact_cli/registry/custom_registries.py +0 -0
- {specfact_cli-0.40.1 → specfact_cli-0.40.3}/src/specfact_cli/registry/dependency_resolver.py +0 -0
- {specfact_cli-0.40.1 → specfact_cli-0.40.3}/src/specfact_cli/registry/extension_registry.py +0 -0
- {specfact_cli-0.40.1 → specfact_cli-0.40.3}/src/specfact_cli/registry/help_cache.py +0 -0
- {specfact_cli-0.40.1 → specfact_cli-0.40.3}/src/specfact_cli/registry/marketplace_client.py +0 -0
- {specfact_cli-0.40.1 → specfact_cli-0.40.3}/src/specfact_cli/registry/metadata.py +0 -0
- {specfact_cli-0.40.1 → specfact_cli-0.40.3}/src/specfact_cli/registry/module_grouping.py +0 -0
- {specfact_cli-0.40.1 → specfact_cli-0.40.3}/src/specfact_cli/registry/module_lifecycle.py +0 -0
- {specfact_cli-0.40.1 → specfact_cli-0.40.3}/src/specfact_cli/registry/module_security.py +0 -0
- {specfact_cli-0.40.1 → specfact_cli-0.40.3}/src/specfact_cli/registry/module_state.py +0 -0
- {specfact_cli-0.40.1 → specfact_cli-0.40.3}/src/specfact_cli/registry/registry.py +0 -0
- {specfact_cli-0.40.1 → specfact_cli-0.40.3}/src/specfact_cli/resources/semgrep/async.yml +0 -0
- {specfact_cli-0.40.1 → specfact_cli-0.40.3}/src/specfact_cli/resources/semgrep/code-quality.yml +0 -0
- {specfact_cli-0.40.1 → specfact_cli-0.40.3}/src/specfact_cli/resources/semgrep/feature-detection.yml +0 -0
- {specfact_cli-0.40.1 → specfact_cli-0.40.3}/src/specfact_cli/runtime.py +0 -0
- {specfact_cli-0.40.1 → specfact_cli-0.40.3}/src/specfact_cli/sync/__init__.py +0 -0
- {specfact_cli-0.40.1 → specfact_cli-0.40.3}/src/specfact_cli/sync/bridge_probe.py +0 -0
- {specfact_cli-0.40.1 → specfact_cli-0.40.3}/src/specfact_cli/sync/bridge_sync.py +0 -0
- {specfact_cli-0.40.1 → specfact_cli-0.40.3}/src/specfact_cli/sync/bridge_watch.py +0 -0
- {specfact_cli-0.40.1 → specfact_cli-0.40.3}/src/specfact_cli/sync/change_detector.py +0 -0
- {specfact_cli-0.40.1 → specfact_cli-0.40.3}/src/specfact_cli/sync/code_to_spec.py +0 -0
- {specfact_cli-0.40.1 → specfact_cli-0.40.3}/src/specfact_cli/sync/drift_detector.py +0 -0
- {specfact_cli-0.40.1 → specfact_cli-0.40.3}/src/specfact_cli/sync/repository_sync.py +0 -0
- {specfact_cli-0.40.1 → specfact_cli-0.40.3}/src/specfact_cli/sync/spec_to_code.py +0 -0
- {specfact_cli-0.40.1 → specfact_cli-0.40.3}/src/specfact_cli/sync/spec_to_tests.py +0 -0
- {specfact_cli-0.40.1 → specfact_cli-0.40.3}/src/specfact_cli/sync/watcher.py +0 -0
- {specfact_cli-0.40.1 → specfact_cli-0.40.3}/src/specfact_cli/sync/watcher_enhanced.py +0 -0
- {specfact_cli-0.40.1 → specfact_cli-0.40.3}/src/specfact_cli/telemetry.py +0 -0
- {specfact_cli-0.40.1 → specfact_cli-0.40.3}/src/specfact_cli/templates/__init__.py +0 -0
- {specfact_cli-0.40.1/resources/templates/backlog → specfact_cli-0.40.3/src/specfact_cli/templates}/defaults/defect_v1.yaml +0 -0
- {specfact_cli-0.40.1/resources/templates/backlog → specfact_cli-0.40.3/src/specfact_cli/templates}/defaults/enabler_v1.yaml +0 -0
- {specfact_cli-0.40.1/resources/templates/backlog → specfact_cli-0.40.3/src/specfact_cli/templates}/defaults/spike_v1.yaml +0 -0
- {specfact_cli-0.40.1/resources/templates/backlog → specfact_cli-0.40.3/src/specfact_cli/templates}/defaults/user_story_v1.yaml +0 -0
- {specfact_cli-0.40.1/resources/templates/backlog → specfact_cli-0.40.3/src/specfact_cli/templates}/frameworks/scrum/user_story_v1.yaml +0 -0
- {specfact_cli-0.40.1/resources/templates/backlog → specfact_cli-0.40.3/src/specfact_cli/templates}/personas/product-owner/user_story_v1.yaml +0 -0
- {specfact_cli-0.40.1/resources/templates/backlog → specfact_cli-0.40.3/src/specfact_cli/templates}/providers/ado/work_item_v1.yaml +0 -0
- {specfact_cli-0.40.1 → specfact_cli-0.40.3}/src/specfact_cli/templates/registry.py +0 -0
- {specfact_cli-0.40.1 → specfact_cli-0.40.3}/src/specfact_cli/templates/specification_templates.py +0 -0
- {specfact_cli-0.40.1 → specfact_cli-0.40.3}/src/specfact_cli/utils/__init__.py +0 -0
- {specfact_cli-0.40.1 → specfact_cli-0.40.3}/src/specfact_cli/utils/acceptance_criteria.py +0 -0
- {specfact_cli-0.40.1 → specfact_cli-0.40.3}/src/specfact_cli/utils/auth_tokens.py +0 -0
- {specfact_cli-0.40.1 → specfact_cli-0.40.3}/src/specfact_cli/utils/bundle_converters.py +0 -0
- {specfact_cli-0.40.1 → specfact_cli-0.40.3}/src/specfact_cli/utils/bundle_loader.py +0 -0
- {specfact_cli-0.40.1 → specfact_cli-0.40.3}/src/specfact_cli/utils/code_change_detector.py +0 -0
- {specfact_cli-0.40.1 → specfact_cli-0.40.3}/src/specfact_cli/utils/console.py +0 -0
- {specfact_cli-0.40.1 → specfact_cli-0.40.3}/src/specfact_cli/utils/content_sanitizer.py +0 -0
- {specfact_cli-0.40.1 → specfact_cli-0.40.3}/src/specfact_cli/utils/context_detection.py +0 -0
- {specfact_cli-0.40.1 → specfact_cli-0.40.3}/src/specfact_cli/utils/enrichment_context.py +0 -0
- {specfact_cli-0.40.1 → specfact_cli-0.40.3}/src/specfact_cli/utils/enrichment_parser.py +0 -0
- {specfact_cli-0.40.1 → specfact_cli-0.40.3}/src/specfact_cli/utils/env_manager.py +0 -0
- {specfact_cli-0.40.1 → specfact_cli-0.40.3}/src/specfact_cli/utils/feature_keys.py +0 -0
- {specfact_cli-0.40.1 → specfact_cli-0.40.3}/src/specfact_cli/utils/git.py +0 -0
- {specfact_cli-0.40.1 → specfact_cli-0.40.3}/src/specfact_cli/utils/github_annotations.py +0 -0
- {specfact_cli-0.40.1 → specfact_cli-0.40.3}/src/specfact_cli/utils/incremental_check.py +0 -0
- {specfact_cli-0.40.1 → specfact_cli-0.40.3}/src/specfact_cli/utils/metadata.py +0 -0
- {specfact_cli-0.40.1 → specfact_cli-0.40.3}/src/specfact_cli/utils/optional_deps.py +0 -0
- {specfact_cli-0.40.1 → specfact_cli-0.40.3}/src/specfact_cli/utils/performance.py +0 -0
- {specfact_cli-0.40.1 → specfact_cli-0.40.3}/src/specfact_cli/utils/persona_ownership.py +0 -0
- {specfact_cli-0.40.1 → specfact_cli-0.40.3}/src/specfact_cli/utils/progress.py +0 -0
- {specfact_cli-0.40.1 → specfact_cli-0.40.3}/src/specfact_cli/utils/progressive_disclosure.py +0 -0
- {specfact_cli-0.40.1 → specfact_cli-0.40.3}/src/specfact_cli/utils/prompts.py +0 -0
- {specfact_cli-0.40.1 → specfact_cli-0.40.3}/src/specfact_cli/utils/sdd_discovery.py +0 -0
- {specfact_cli-0.40.1 → specfact_cli-0.40.3}/src/specfact_cli/utils/source_scanner.py +0 -0
- {specfact_cli-0.40.1 → specfact_cli-0.40.3}/src/specfact_cli/utils/startup_checks.py +0 -0
- {specfact_cli-0.40.1 → specfact_cli-0.40.3}/src/specfact_cli/utils/structure.py +0 -0
- {specfact_cli-0.40.1 → specfact_cli-0.40.3}/src/specfact_cli/utils/structured_io.py +0 -0
- {specfact_cli-0.40.1 → specfact_cli-0.40.3}/src/specfact_cli/utils/suggestions.py +0 -0
- {specfact_cli-0.40.1 → specfact_cli-0.40.3}/src/specfact_cli/utils/terminal.py +0 -0
- {specfact_cli-0.40.1 → specfact_cli-0.40.3}/src/specfact_cli/utils/yaml_utils.py +0 -0
- {specfact_cli-0.40.1 → specfact_cli-0.40.3}/src/specfact_cli/validators/__init__.py +0 -0
- {specfact_cli-0.40.1 → specfact_cli-0.40.3}/src/specfact_cli/validators/agile_validation.py +0 -0
- {specfact_cli-0.40.1 → specfact_cli-0.40.3}/src/specfact_cli/validators/change_proposal_integration.py +0 -0
- {specfact_cli-0.40.1 → specfact_cli-0.40.3}/src/specfact_cli/validators/cli_first_validator.py +0 -0
- {specfact_cli-0.40.1 → specfact_cli-0.40.3}/src/specfact_cli/validators/contract_validator.py +0 -0
- {specfact_cli-0.40.1 → specfact_cli-0.40.3}/src/specfact_cli/validators/fsm.py +0 -0
- {specfact_cli-0.40.1 → specfact_cli-0.40.3}/src/specfact_cli/validators/repro_checker.py +0 -0
- {specfact_cli-0.40.1 → specfact_cli-0.40.3}/src/specfact_cli/validators/schema.py +0 -0
- {specfact_cli-0.40.1 → specfact_cli-0.40.3}/src/specfact_cli/validators/sidecar/__init__.py +0 -0
- {specfact_cli-0.40.1 → specfact_cli-0.40.3}/src/specfact_cli/validators/sidecar/contract_populator.py +0 -0
- {specfact_cli-0.40.1 → specfact_cli-0.40.3}/src/specfact_cli/validators/sidecar/crosshair_runner.py +0 -0
- {specfact_cli-0.40.1 → specfact_cli-0.40.3}/src/specfact_cli/validators/sidecar/crosshair_summary.py +0 -0
- {specfact_cli-0.40.1 → specfact_cli-0.40.3}/src/specfact_cli/validators/sidecar/dependency_installer.py +0 -0
- {specfact_cli-0.40.1 → specfact_cli-0.40.3}/src/specfact_cli/validators/sidecar/framework_detector.py +0 -0
- {specfact_cli-0.40.1 → specfact_cli-0.40.3}/src/specfact_cli/validators/sidecar/frameworks/__init__.py +0 -0
- {specfact_cli-0.40.1 → specfact_cli-0.40.3}/src/specfact_cli/validators/sidecar/frameworks/base.py +0 -0
- {specfact_cli-0.40.1 → specfact_cli-0.40.3}/src/specfact_cli/validators/sidecar/frameworks/django.py +0 -0
- {specfact_cli-0.40.1 → specfact_cli-0.40.3}/src/specfact_cli/validators/sidecar/frameworks/drf.py +0 -0
- {specfact_cli-0.40.1 → specfact_cli-0.40.3}/src/specfact_cli/validators/sidecar/frameworks/fastapi.py +0 -0
- {specfact_cli-0.40.1 → specfact_cli-0.40.3}/src/specfact_cli/validators/sidecar/frameworks/flask.py +0 -0
- {specfact_cli-0.40.1 → specfact_cli-0.40.3}/src/specfact_cli/validators/sidecar/harness_generator.py +0 -0
- {specfact_cli-0.40.1 → specfact_cli-0.40.3}/src/specfact_cli/validators/sidecar/models.py +0 -0
- {specfact_cli-0.40.1 → specfact_cli-0.40.3}/src/specfact_cli/validators/sidecar/orchestrator.py +0 -0
- {specfact_cli-0.40.1 → specfact_cli-0.40.3}/src/specfact_cli/validators/sidecar/specmatic_runner.py +0 -0
- {specfact_cli-0.40.1 → specfact_cli-0.40.3}/src/specfact_cli/validators/sidecar/unannotated_detector.py +0 -0
- {specfact_cli-0.40.1 → specfact_cli-0.40.3}/src/specfact_cli/versioning/__init__.py +0 -0
- {specfact_cli-0.40.1 → specfact_cli-0.40.3}/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.40.
|
|
3
|
+
Version: 0.40.3
|
|
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
|
|
@@ -353,6 +353,8 @@ As of `0.40.0`, flat root commands are removed. Use grouped commands:
|
|
|
353
353
|
### Backlog Bridge (60 seconds)
|
|
354
354
|
|
|
355
355
|
SpecFact's USP is closing the drift gap between **backlog -> specs -> code**.
|
|
356
|
+
These commands require the backlog bundle to be installed first, for example via
|
|
357
|
+
`specfact init --profile backlog-team` or `specfact init --install backlog`.
|
|
356
358
|
|
|
357
359
|
```bash
|
|
358
360
|
# 1) Initialize backlog config + field mapping
|
|
@@ -74,6 +74,8 @@ As of `0.40.0`, flat root commands are removed. Use grouped commands:
|
|
|
74
74
|
### Backlog Bridge (60 seconds)
|
|
75
75
|
|
|
76
76
|
SpecFact's USP is closing the drift gap between **backlog -> specs -> code**.
|
|
77
|
+
These commands require the backlog bundle to be installed first, for example via
|
|
78
|
+
`specfact init --profile backlog-team` or `specfact init --install backlog`.
|
|
77
79
|
|
|
78
80
|
```bash
|
|
79
81
|
# 1) Initialize backlog config + field mapping
|
|
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "specfact-cli"
|
|
7
|
-
version = "0.40.
|
|
7
|
+
version = "0.40.3"
|
|
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"
|
|
@@ -388,7 +388,6 @@ sources = ["src"]
|
|
|
388
388
|
"resources/schemas" = "specfact_cli/resources/schemas"
|
|
389
389
|
"resources/mappings" = "specfact_cli/resources/mappings"
|
|
390
390
|
"resources/keys" = "specfact_cli/resources/keys"
|
|
391
|
-
"modules/backlog-core" = "specfact_cli/modules/backlog-core"
|
|
392
391
|
"modules/bundle-mapper" = "specfact_cli/modules/bundle-mapper"
|
|
393
392
|
# Note: resources/semgrep files are in src/specfact_cli/resources/semgrep/ and are automatically included
|
|
394
393
|
|
|
@@ -3414,6 +3414,21 @@ class AdoAdapter(BridgeAdapter, BacklogAdapterMixin, BacklogAdapter):
|
|
|
3414
3414
|
}
|
|
3415
3415
|
)
|
|
3416
3416
|
|
|
3417
|
+
provider_fields = payload.get("provider_fields")
|
|
3418
|
+
provider_field_values = provider_fields.get("fields") if isinstance(provider_fields, dict) else None
|
|
3419
|
+
if isinstance(provider_field_values, dict):
|
|
3420
|
+
for field_name, field_value in provider_field_values.items():
|
|
3421
|
+
normalized_field = str(field_name).strip()
|
|
3422
|
+
if not normalized_field:
|
|
3423
|
+
continue
|
|
3424
|
+
patch_document.append(
|
|
3425
|
+
{
|
|
3426
|
+
"op": "add",
|
|
3427
|
+
"path": f"/fields/{normalized_field}",
|
|
3428
|
+
"value": field_value,
|
|
3429
|
+
}
|
|
3430
|
+
)
|
|
3431
|
+
|
|
3417
3432
|
sprint = str(payload.get("sprint") or "").strip()
|
|
3418
3433
|
if sprint:
|
|
3419
3434
|
patch_document.append(
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
"""Shared backlog conversion helpers retained by core adapters."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from specfact_cli.backlog.converter import (
|
|
6
|
+
convert_ado_work_item_to_backlog_item,
|
|
7
|
+
convert_github_issue_to_backlog_item,
|
|
8
|
+
)
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
__all__ = [
|
|
12
|
+
"convert_ado_work_item_to_backlog_item",
|
|
13
|
+
"convert_github_issue_to_backlog_item",
|
|
14
|
+
]
|
|
@@ -396,6 +396,7 @@ class LoggerSetup:
|
|
|
396
396
|
use_rotating_file: bool = True,
|
|
397
397
|
append_mode: bool = True,
|
|
398
398
|
preserve_test_format: bool = False,
|
|
399
|
+
emit_to_console: bool = True,
|
|
399
400
|
) -> logging.Logger:
|
|
400
401
|
"""
|
|
401
402
|
Creates a new logger or returns an existing one with the specified configuration.
|
|
@@ -513,15 +514,20 @@ class LoggerSetup:
|
|
|
513
514
|
with contextlib.suppress(Exception):
|
|
514
515
|
logger.info("[LoggerSetup] File logger initialized: %s", log_file_path)
|
|
515
516
|
else:
|
|
516
|
-
# If no log file is specified,
|
|
517
|
+
# If no log file is specified, stream to console only when explicitly requested.
|
|
517
518
|
log_queue = Queue(-1)
|
|
518
519
|
cls._log_queues[logger_name] = log_queue
|
|
519
520
|
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
521
|
+
sink_handler: logging.Handler
|
|
522
|
+
if emit_to_console:
|
|
523
|
+
sink_handler = logging.StreamHandler(_safe_console_stream())
|
|
524
|
+
sink_handler.setFormatter(log_format)
|
|
525
|
+
sink_handler.setLevel(level)
|
|
526
|
+
else:
|
|
527
|
+
sink_handler = logging.NullHandler()
|
|
528
|
+
sink_handler.setLevel(level)
|
|
523
529
|
|
|
524
|
-
listener = QueueListener(log_queue,
|
|
530
|
+
listener = QueueListener(log_queue, sink_handler, respect_handler_level=True)
|
|
525
531
|
listener.start()
|
|
526
532
|
cls._log_listeners[logger_name] = listener
|
|
527
533
|
|
|
@@ -38,4 +38,10 @@ def _try_common_logger(name: str, level: str) -> logging.Logger | None:
|
|
|
38
38
|
from specfact_cli.common.logger_setup import LoggerSetup # type: ignore[import]
|
|
39
39
|
except ImportError:
|
|
40
40
|
return None
|
|
41
|
-
|
|
41
|
+
try:
|
|
42
|
+
from specfact_cli.runtime import is_debug_mode # type: ignore[import]
|
|
43
|
+
|
|
44
|
+
emit_to_console = is_debug_mode()
|
|
45
|
+
except ImportError:
|
|
46
|
+
emit_to_console = False
|
|
47
|
+
return LoggerSetup.create_logger(name, log_level=level, emit_to_console=emit_to_console)
|
|
@@ -1,8 +1,7 @@
|
|
|
1
|
-
"""Category group commands: project,
|
|
1
|
+
"""Category group commands: project, code, spec, govern."""
|
|
2
2
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
|
-
from specfact_cli.groups.backlog_group import app as backlog_app
|
|
6
5
|
from specfact_cli.groups.codebase_group import app as codebase_app
|
|
7
6
|
from specfact_cli.groups.govern_group import app as govern_app
|
|
8
7
|
from specfact_cli.groups.project_group import app as project_app
|
|
@@ -10,7 +9,6 @@ from specfact_cli.groups.spec_group import app as spec_app
|
|
|
10
9
|
|
|
11
10
|
|
|
12
11
|
__all__ = [
|
|
13
|
-
"backlog_app",
|
|
14
12
|
"codebase_app",
|
|
15
13
|
"govern_app",
|
|
16
14
|
"project_app",
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
"""Generic category group builder for bundle-owned command surfaces."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from collections.abc import Sequence
|
|
6
|
+
|
|
7
|
+
import typer
|
|
8
|
+
from beartype import beartype
|
|
9
|
+
from icontract import ensure, require
|
|
10
|
+
|
|
11
|
+
from specfact_cli.registry.registry import CommandRegistry
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
@require(lambda app: app is not None)
|
|
15
|
+
@beartype
|
|
16
|
+
def _register_members(app: typer.Typer, members: Sequence[tuple[str, str]]) -> int:
|
|
17
|
+
"""Register member module sub-apps and return how many were added."""
|
|
18
|
+
added = 0
|
|
19
|
+
for display_name, cmd_name in members:
|
|
20
|
+
try:
|
|
21
|
+
member_app = CommandRegistry.get_module_typer(cmd_name)
|
|
22
|
+
if member_app is not None:
|
|
23
|
+
app.add_typer(member_app, name=display_name)
|
|
24
|
+
added += 1
|
|
25
|
+
except ValueError:
|
|
26
|
+
continue
|
|
27
|
+
return added
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
@require(lambda name: isinstance(name, str) and len(name) > 0)
|
|
31
|
+
@require(lambda help_text: isinstance(help_text, str) and len(help_text) > 0)
|
|
32
|
+
@ensure(lambda result: isinstance(result, typer.Typer))
|
|
33
|
+
@beartype
|
|
34
|
+
def build_member_group(
|
|
35
|
+
*,
|
|
36
|
+
name: str,
|
|
37
|
+
help_text: str,
|
|
38
|
+
members: Sequence[tuple[str, str]],
|
|
39
|
+
flatten_same_name: str | None = None,
|
|
40
|
+
install_hint_module: str | None = None,
|
|
41
|
+
) -> typer.Typer:
|
|
42
|
+
"""Build a lazy category group from registered member modules."""
|
|
43
|
+
app = typer.Typer(name=name, help=help_text, no_args_is_help=True)
|
|
44
|
+
added = _register_members(app, members)
|
|
45
|
+
|
|
46
|
+
if added == 0 and install_hint_module:
|
|
47
|
+
placeholder = typer.Typer(help=f"{help_text} (module not loaded).")
|
|
48
|
+
|
|
49
|
+
@placeholder.command("install")
|
|
50
|
+
def _install_hint() -> None:
|
|
51
|
+
from specfact_cli.utils.prompts import print_warning
|
|
52
|
+
|
|
53
|
+
print_warning(f"No {name} module loaded. Install with: specfact module install {install_hint_module}")
|
|
54
|
+
|
|
55
|
+
app.add_typer(placeholder, name=name)
|
|
56
|
+
|
|
57
|
+
if flatten_same_name:
|
|
58
|
+
app._specfact_flatten_same_name = flatten_same_name
|
|
59
|
+
|
|
60
|
+
return app
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
name: module-registry
|
|
2
|
-
version: 0.1.
|
|
2
|
+
version: 0.1.10
|
|
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:85e40c4c083982f0bab2bde2a27a08a4e6832fe6d93ae3d62c4659c138fd6295
|
|
21
|
+
signature: INFP+nx7iPCqZPnGIwB39L6GpckB16+EqPwaPW/kwwBtlLBX9RaZ1DcMbhu5f6tPqjA9sETRMBtrQ5GiBbVfCQ==
|
{specfact_cli-0.40.1 → specfact_cli-0.40.3}/src/specfact_cli/modules/module_registry/src/commands.py
RENAMED
|
@@ -7,6 +7,7 @@ import shutil
|
|
|
7
7
|
from pathlib import Path
|
|
8
8
|
|
|
9
9
|
import typer
|
|
10
|
+
import yaml
|
|
10
11
|
from beartype import beartype
|
|
11
12
|
from rich.console import Console
|
|
12
13
|
from rich.table import Table
|
|
@@ -39,6 +40,20 @@ app = typer.Typer(help="Manage marketplace modules")
|
|
|
39
40
|
console = Console()
|
|
40
41
|
|
|
41
42
|
|
|
43
|
+
def _read_installed_module_version(module_dir: Path) -> str:
|
|
44
|
+
"""Read installed module version from its manifest, if available."""
|
|
45
|
+
manifest_path = module_dir / "module-package.yaml"
|
|
46
|
+
if not manifest_path.exists():
|
|
47
|
+
return "unknown"
|
|
48
|
+
try:
|
|
49
|
+
loaded = yaml.safe_load(manifest_path.read_text(encoding="utf-8"))
|
|
50
|
+
except Exception:
|
|
51
|
+
return "unknown"
|
|
52
|
+
if not isinstance(loaded, dict):
|
|
53
|
+
return "unknown"
|
|
54
|
+
return str(loaded.get("version", "unknown"))
|
|
55
|
+
|
|
56
|
+
|
|
42
57
|
def _publisher_from_module_id(module_id: str) -> str:
|
|
43
58
|
"""Extract normalized publisher namespace from module id."""
|
|
44
59
|
return module_id.split("/", 1)[0].strip().lower() if "/" in module_id else ""
|
|
@@ -803,19 +818,22 @@ def upgrade(
|
|
|
803
818
|
# If module isn't discovered locally, still attempt marketplace install/upgrade by ID.
|
|
804
819
|
target_ids = [prefixed]
|
|
805
820
|
|
|
806
|
-
upgraded: list[str] = []
|
|
821
|
+
upgraded: list[tuple[str, str, str]] = []
|
|
807
822
|
failed: list[str] = []
|
|
808
823
|
for target in target_ids:
|
|
809
824
|
try:
|
|
810
825
|
module_id = target if "/" in target else f"specfact/{target}"
|
|
811
|
-
|
|
812
|
-
|
|
826
|
+
previous_version = str(by_id.get(target, {}).get("version", "unknown"))
|
|
827
|
+
installed_path = install_module(module_id, reinstall=True)
|
|
828
|
+
upgraded.append((module_id, previous_version, _read_installed_module_version(installed_path)))
|
|
813
829
|
except Exception as exc:
|
|
814
830
|
console.print(f"[red]Failed upgrading {target}: {exc}[/red]")
|
|
815
831
|
failed.append(target)
|
|
816
832
|
|
|
817
833
|
if upgraded:
|
|
818
|
-
console.print(
|
|
834
|
+
console.print("[green]Upgraded:[/green]")
|
|
835
|
+
for module_id, previous_version, new_version in upgraded:
|
|
836
|
+
console.print(f" {module_id}: {previous_version} -> {new_version}")
|
|
819
837
|
if failed:
|
|
820
838
|
raise typer.Exit(1)
|
|
821
839
|
|
|
@@ -56,7 +56,14 @@ def discover_all_modules(
|
|
|
56
56
|
effective_custom_root = custom_root or CUSTOM_MODULES_ROOT
|
|
57
57
|
|
|
58
58
|
roots: list[tuple[str, Path]] = [("builtin", effective_builtin_root)]
|
|
59
|
+
project_matches_user_root = False
|
|
59
60
|
if effective_project_root is not None:
|
|
61
|
+
try:
|
|
62
|
+
project_matches_user_root = effective_project_root.resolve() == effective_user_root.resolve()
|
|
63
|
+
except OSError:
|
|
64
|
+
project_matches_user_root = effective_project_root == effective_user_root
|
|
65
|
+
|
|
66
|
+
if effective_project_root is not None and not project_matches_user_root:
|
|
60
67
|
roots.append(("project", effective_project_root))
|
|
61
68
|
roots.extend(
|
|
62
69
|
[
|
|
@@ -730,7 +730,7 @@ def install_module(
|
|
|
730
730
|
dependency_manifest = target_root / dependency_name / "module-package.yaml"
|
|
731
731
|
if dependency_manifest.exists():
|
|
732
732
|
dependency_version = _installed_dependency_version(dependency_manifest)
|
|
733
|
-
logger.
|
|
733
|
+
logger.info(
|
|
734
734
|
"Dependency %s already satisfied (version %s)", dependency_module_id, dependency_version
|
|
735
735
|
)
|
|
736
736
|
continue
|
|
@@ -879,14 +879,24 @@ def get_installed_bundles(
|
|
|
879
879
|
|
|
880
880
|
# Bundle name -> (group_name, help_str, build_app_fn) for conditional category mounting.
|
|
881
881
|
def _build_bundle_to_group() -> dict[str, tuple[str, str, Any]]:
|
|
882
|
-
from specfact_cli.groups.backlog_group import build_app as build_backlog_app
|
|
883
882
|
from specfact_cli.groups.codebase_group import build_app as build_codebase_app
|
|
884
883
|
from specfact_cli.groups.govern_group import build_app as build_govern_app
|
|
884
|
+
from specfact_cli.groups.member_group import build_member_group
|
|
885
885
|
from specfact_cli.groups.project_group import build_app as build_project_app
|
|
886
886
|
from specfact_cli.groups.spec_group import build_app as build_spec_app
|
|
887
887
|
|
|
888
888
|
return {
|
|
889
|
-
"specfact-backlog": (
|
|
889
|
+
"specfact-backlog": (
|
|
890
|
+
"backlog",
|
|
891
|
+
"Backlog and policy commands.",
|
|
892
|
+
lambda: build_member_group(
|
|
893
|
+
name="backlog",
|
|
894
|
+
help_text="Backlog and policy commands.",
|
|
895
|
+
members=(("backlog", "backlog"), ("policy", "policy")),
|
|
896
|
+
flatten_same_name="backlog",
|
|
897
|
+
install_hint_module="nold-ai/specfact-backlog",
|
|
898
|
+
),
|
|
899
|
+
),
|
|
890
900
|
"specfact-codebase": (
|
|
891
901
|
"code",
|
|
892
902
|
"Codebase quality commands: analyze, drift, validate, repro.",
|
|
@@ -1171,26 +1181,20 @@ def register_module_package_commands(
|
|
|
1171
1181
|
if category_grouping_enabled:
|
|
1172
1182
|
_mount_installed_category_groups(packages, enabled_map)
|
|
1173
1183
|
discovered_count = protocol_full + protocol_partial + protocol_legacy
|
|
1174
|
-
if discovered_count and (protocol_partial > 0 or protocol_legacy > 0):
|
|
1175
|
-
|
|
1176
|
-
"Module compatibility check: "
|
|
1177
|
-
|
|
1178
|
-
|
|
1184
|
+
if discovered_count and (protocol_partial > 0 or protocol_legacy > 0) and is_debug_mode():
|
|
1185
|
+
logger.info(
|
|
1186
|
+
"Module compatibility check: %s/%s compliant (full=%s, partial=%s, legacy=%s)",
|
|
1187
|
+
protocol_full + protocol_partial,
|
|
1188
|
+
discovered_count,
|
|
1189
|
+
protocol_full,
|
|
1190
|
+
protocol_partial,
|
|
1191
|
+
protocol_legacy,
|
|
1179
1192
|
)
|
|
1180
1193
|
if partial_modules:
|
|
1181
1194
|
partial_desc = ", ".join(f"{name} ({'/'.join(ops)})" for name, ops in sorted(partial_modules))
|
|
1182
|
-
|
|
1195
|
+
logger.info("Partially compliant modules: %s", partial_desc)
|
|
1183
1196
|
if legacy_modules:
|
|
1184
|
-
|
|
1185
|
-
if is_debug_mode():
|
|
1186
|
-
logger.info(
|
|
1187
|
-
"Protocol-compliant: %s/%s modules (Full=%s, Partial=%s, Legacy=%s)",
|
|
1188
|
-
protocol_full + protocol_partial,
|
|
1189
|
-
discovered_count,
|
|
1190
|
-
protocol_full,
|
|
1191
|
-
protocol_partial,
|
|
1192
|
-
protocol_legacy,
|
|
1193
|
-
)
|
|
1197
|
+
logger.info("Legacy modules: %s", ", ".join(sorted(set(legacy_modules))))
|
|
1194
1198
|
for module_id, reason in skipped:
|
|
1195
1199
|
logger.debug("Skipped module '%s': %s", module_id, reason)
|
|
1196
1200
|
|
|
@@ -121,10 +121,6 @@ SPECFACT_COMMANDS = [
|
|
|
121
121
|
"specfact.06-sync",
|
|
122
122
|
"specfact.07-contracts",
|
|
123
123
|
"specfact.compare",
|
|
124
|
-
"specfact.sync-backlog",
|
|
125
|
-
"specfact.backlog-daily",
|
|
126
|
-
"specfact.backlog-refine",
|
|
127
|
-
"specfact.backlog-add",
|
|
128
124
|
"specfact.validate",
|
|
129
125
|
]
|
|
130
126
|
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
"""Validation helpers for release and command-surface audits."""
|
|
2
|
+
|
|
3
|
+
from specfact_cli.validation.command_audit import (
|
|
4
|
+
CommandAuditCase,
|
|
5
|
+
build_command_audit_cases,
|
|
6
|
+
official_marketplace_module_ids,
|
|
7
|
+
)
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
__all__ = [
|
|
11
|
+
"CommandAuditCase",
|
|
12
|
+
"build_command_audit_cases",
|
|
13
|
+
"official_marketplace_module_ids",
|
|
14
|
+
]
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
"""Helpers for command-package runtime validation."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import importlib
|
|
6
|
+
import os
|
|
7
|
+
import sys
|
|
8
|
+
from dataclasses import dataclass
|
|
9
|
+
from pathlib import Path
|
|
10
|
+
from typing import Literal
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
AuditMode = Literal["help-only", "fixture-backed", "dry-run"]
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
@dataclass(frozen=True)
|
|
17
|
+
class CommandAuditCase:
|
|
18
|
+
"""One command-path audit case."""
|
|
19
|
+
|
|
20
|
+
command_path: str
|
|
21
|
+
argv: tuple[str, ...]
|
|
22
|
+
phase: str
|
|
23
|
+
mode: AuditMode
|
|
24
|
+
owner: str
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def _resolve_modules_repo_root() -> Path | None:
|
|
28
|
+
configured = os.environ.get("SPECFACT_MODULES_REPO", "").strip()
|
|
29
|
+
if configured:
|
|
30
|
+
candidate = Path(configured).expanduser().resolve()
|
|
31
|
+
if candidate.exists():
|
|
32
|
+
return candidate
|
|
33
|
+
|
|
34
|
+
current = Path(__file__).resolve()
|
|
35
|
+
for parent in current.parents:
|
|
36
|
+
sibling = parent.parent / "specfact-cli-modules"
|
|
37
|
+
if sibling.exists():
|
|
38
|
+
return sibling.resolve()
|
|
39
|
+
direct = parent / "specfact-cli-modules"
|
|
40
|
+
if direct.exists():
|
|
41
|
+
return direct.resolve()
|
|
42
|
+
return None
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def _ensure_bundle_sources_on_sys_path() -> None:
|
|
46
|
+
modules_repo = _resolve_modules_repo_root()
|
|
47
|
+
if modules_repo is None:
|
|
48
|
+
return
|
|
49
|
+
packages_root = modules_repo / "packages"
|
|
50
|
+
if not packages_root.exists():
|
|
51
|
+
return
|
|
52
|
+
for bundle_src in packages_root.glob("*/src"):
|
|
53
|
+
bundle_src_str = str(bundle_src.resolve())
|
|
54
|
+
if bundle_src_str not in sys.path:
|
|
55
|
+
sys.path.insert(0, bundle_src_str)
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def _command_info_name(command_info: object) -> str:
|
|
59
|
+
explicit_name = getattr(command_info, "name", None)
|
|
60
|
+
if isinstance(explicit_name, str) and explicit_name:
|
|
61
|
+
return explicit_name
|
|
62
|
+
callback = getattr(command_info, "callback", None)
|
|
63
|
+
callback_name = getattr(callback, "__name__", "")
|
|
64
|
+
return callback_name.replace("_", "-") if callback_name else ""
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def _collect_typer_paths(app: object, prefix: str) -> set[str]:
|
|
68
|
+
paths: set[str] = set()
|
|
69
|
+
|
|
70
|
+
for command_info in list(getattr(app, "registered_commands", [])):
|
|
71
|
+
command_name = _command_info_name(command_info)
|
|
72
|
+
if command_name:
|
|
73
|
+
paths.add(f"{prefix} {command_name}")
|
|
74
|
+
|
|
75
|
+
for group_info in list(getattr(app, "registered_groups", [])):
|
|
76
|
+
group_name = getattr(group_info, "name", "") or ""
|
|
77
|
+
if not group_name:
|
|
78
|
+
nested_app = getattr(group_info, "typer_instance", None)
|
|
79
|
+
nested_info = getattr(nested_app, "info", None) if nested_app is not None else None
|
|
80
|
+
group_name = getattr(nested_info, "name", "") or ""
|
|
81
|
+
if not group_name:
|
|
82
|
+
continue
|
|
83
|
+
group_prefix = f"{prefix} {group_name}"
|
|
84
|
+
paths.add(group_prefix)
|
|
85
|
+
nested_app = getattr(group_info, "typer_instance", None)
|
|
86
|
+
if nested_app is not None:
|
|
87
|
+
paths.update(_collect_typer_paths(nested_app, group_prefix))
|
|
88
|
+
|
|
89
|
+
return paths
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
def _import_typer(module_path: str, attr_name: str = "app") -> object:
|
|
93
|
+
module = importlib.import_module(module_path)
|
|
94
|
+
return getattr(module, attr_name)
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
def official_marketplace_module_ids() -> tuple[str, ...]:
|
|
98
|
+
"""Return the official marketplace module ids that make up the full CLI surface."""
|
|
99
|
+
return (
|
|
100
|
+
"nold-ai/specfact-project",
|
|
101
|
+
"nold-ai/specfact-spec",
|
|
102
|
+
"nold-ai/specfact-codebase",
|
|
103
|
+
"nold-ai/specfact-backlog",
|
|
104
|
+
"nold-ai/specfact-govern",
|
|
105
|
+
)
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
def _explicit_cases() -> list[CommandAuditCase]:
|
|
109
|
+
return [
|
|
110
|
+
CommandAuditCase("specfact", ("--help",), "root", "help-only", "specfact-core"),
|
|
111
|
+
CommandAuditCase("project", ("project", "--help"), "project", "help-only", "nold-ai/specfact-project"),
|
|
112
|
+
CommandAuditCase("spec", ("spec", "--help"), "spec", "help-only", "nold-ai/specfact-spec"),
|
|
113
|
+
CommandAuditCase("code", ("code", "--help"), "code", "help-only", "nold-ai/specfact-codebase"),
|
|
114
|
+
CommandAuditCase("backlog", ("backlog", "--help"), "backlog", "help-only", "nold-ai/specfact-backlog"),
|
|
115
|
+
CommandAuditCase("govern", ("govern", "--help"), "govern", "help-only", "nold-ai/specfact-govern"),
|
|
116
|
+
CommandAuditCase(
|
|
117
|
+
"module init", ("module", "init", "--scope", "user"), "core", "fixture-backed", "specfact-core"
|
|
118
|
+
),
|
|
119
|
+
CommandAuditCase("module search", ("module", "search", "specfact"), "core", "fixture-backed", "specfact-core"),
|
|
120
|
+
CommandAuditCase("module list", ("module", "list", "--show-origin"), "core", "fixture-backed", "specfact-core"),
|
|
121
|
+
CommandAuditCase(
|
|
122
|
+
"module show",
|
|
123
|
+
("module", "show", "nold-ai/specfact-backlog"),
|
|
124
|
+
"core",
|
|
125
|
+
"fixture-backed",
|
|
126
|
+
"specfact-core",
|
|
127
|
+
),
|
|
128
|
+
]
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
def build_command_audit_cases() -> list[CommandAuditCase]:
|
|
132
|
+
"""Build the full command audit matrix for core and official bundle command paths."""
|
|
133
|
+
_ensure_bundle_sources_on_sys_path()
|
|
134
|
+
app_specs = [
|
|
135
|
+
("specfact_cli.modules.init.src.commands", "init", "core", "specfact-core", "import"),
|
|
136
|
+
("specfact_cli.modules.module_registry.src.commands", "module", "core", "specfact-core", "import"),
|
|
137
|
+
("specfact_cli.modules.upgrade.src.commands", "upgrade", "core", "specfact-core", "import"),
|
|
138
|
+
("specfact_project.project.commands", "project", "project", "nold-ai/specfact-project", "import"),
|
|
139
|
+
("specfact_spec.spec.commands", "spec", "spec", "nold-ai/specfact-spec", "import"),
|
|
140
|
+
("specfact_codebase.code.commands", "code", "code", "nold-ai/specfact-codebase", "import"),
|
|
141
|
+
("specfact_backlog.backlog.commands", "backlog", "backlog", "nold-ai/specfact-backlog", "import"),
|
|
142
|
+
("specfact_govern.govern.commands", "govern", "govern", "nold-ai/specfact-govern", "import"),
|
|
143
|
+
]
|
|
144
|
+
|
|
145
|
+
cases: dict[str, CommandAuditCase] = {case.command_path: case for case in _explicit_cases()}
|
|
146
|
+
for module_path, prefix, phase, owner, _load_mode in app_specs:
|
|
147
|
+
app = _import_typer(module_path)
|
|
148
|
+
cases.setdefault(
|
|
149
|
+
prefix,
|
|
150
|
+
CommandAuditCase(prefix, (*tuple(prefix.split()), "--help"), phase, "help-only", owner),
|
|
151
|
+
)
|
|
152
|
+
for command_path in sorted(_collect_typer_paths(app, prefix)):
|
|
153
|
+
cases.setdefault(
|
|
154
|
+
command_path,
|
|
155
|
+
CommandAuditCase(
|
|
156
|
+
command_path,
|
|
157
|
+
(*tuple(command_path.split()), "--help"),
|
|
158
|
+
phase,
|
|
159
|
+
"help-only",
|
|
160
|
+
owner,
|
|
161
|
+
),
|
|
162
|
+
)
|
|
163
|
+
|
|
164
|
+
phase_order = {"root": 0, "core": 1, "project": 2, "spec": 3, "code": 4, "backlog": 5, "govern": 6}
|
|
165
|
+
return sorted(cases.values(), key=lambda case: (phase_order.get(case.phase, 99), case.command_path))
|