specfact-cli 0.30.4__tar.gz → 0.31.1__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.30.4/LICENSE.md → specfact_cli-0.31.1/LICENSE +3 -4
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/PKG-INFO +14 -8
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/README.md +9 -2
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/pyproject.toml +3 -3
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/src/__init__.py +1 -1
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/src/specfact_cli/__init__.py +1 -1
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/src/specfact_cli/adapters/ado.py +107 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/src/specfact_cli/adapters/github.py +155 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/src/specfact_cli/backlog/mappers/github_mapper.py +18 -4
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/src/specfact_cli/cli.py +31 -1
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/src/specfact_cli/models/project.py +22 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/src/specfact_cli/modules/backlog/src/commands.py +207 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/src/specfact_cli/modules/project/src/commands.py +625 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/src/specfact_cli/registry/bootstrap.py +2 -2
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/src/specfact_cli/registry/bridge_registry.py +52 -1
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/src/specfact_cli/registry/module_packages.py +206 -22
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/src/specfact_cli/validators/sidecar/harness_generator.py +79 -4
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/.gitignore +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/resources/mappings/node-async.yaml +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/resources/mappings/python-async.yaml +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/resources/mappings/speckit-default.yaml +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/resources/prompts/shared/cli-enforcement.md +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/resources/prompts/specfact.01-import.md +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/resources/prompts/specfact.02-plan.md +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/resources/prompts/specfact.03-review.md +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/resources/prompts/specfact.04-sdd.md +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/resources/prompts/specfact.05-enforce.md +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/resources/prompts/specfact.06-sync.md +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/resources/prompts/specfact.07-contracts.md +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/resources/prompts/specfact.backlog-daily.md +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/resources/prompts/specfact.backlog-refine.md +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/resources/prompts/specfact.compare.md +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/resources/prompts/specfact.sync-backlog.md +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/resources/prompts/specfact.validate.md +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/resources/schemas/deviation.schema.json +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/resources/schemas/plan.schema.json +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/resources/schemas/protocol.schema.json +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/resources/templates/backlog/defaults/defect_v1.yaml +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/resources/templates/backlog/defaults/enabler_v1.yaml +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/resources/templates/backlog/defaults/spike_v1.yaml +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/resources/templates/backlog/defaults/user_story_v1.yaml +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/resources/templates/backlog/field_mappings/ado_agile.yaml +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/resources/templates/backlog/field_mappings/ado_default.yaml +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/resources/templates/backlog/field_mappings/ado_kanban.yaml +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/resources/templates/backlog/field_mappings/ado_safe.yaml +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/resources/templates/backlog/field_mappings/ado_scrum.yaml +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/resources/templates/backlog/frameworks/safe/safe_feature_v1.yaml +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/resources/templates/backlog/frameworks/scrum/user_story_v1.yaml +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/resources/templates/backlog/personas/developer/developer_task_v1.yaml +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/resources/templates/backlog/personas/product-owner/user_story_v1.yaml +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/resources/templates/backlog/providers/ado/work_item_v1.yaml +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/resources/templates/github-action.yml.j2 +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/resources/templates/persona/architect.md.j2 +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/resources/templates/persona/developer.md.j2 +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/resources/templates/persona/product-owner.md.j2 +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/resources/templates/plan.bundle.yaml.j2 +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/resources/templates/pr-template.md.j2 +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/resources/templates/protocol.yaml.j2 +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/resources/templates/telemetry.yaml.example +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/src/specfact_cli/__main__.py +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/src/specfact_cli/adapters/__init__.py +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/src/specfact_cli/adapters/backlog_base.py +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/src/specfact_cli/adapters/base.py +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/src/specfact_cli/adapters/openspec.py +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/src/specfact_cli/adapters/openspec_parser.py +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/src/specfact_cli/adapters/registry.py +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/src/specfact_cli/adapters/speckit.py +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/src/specfact_cli/agents/__init__.py +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/src/specfact_cli/agents/analyze_agent.py +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/src/specfact_cli/agents/base.py +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/src/specfact_cli/agents/plan_agent.py +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/src/specfact_cli/agents/registry.py +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/src/specfact_cli/agents/sync_agent.py +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/src/specfact_cli/analyzers/__init__.py +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/src/specfact_cli/analyzers/ambiguity_scanner.py +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/src/specfact_cli/analyzers/code_analyzer.py +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/src/specfact_cli/analyzers/constitution_evidence_extractor.py +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/src/specfact_cli/analyzers/contract_extractor.py +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/src/specfact_cli/analyzers/control_flow_analyzer.py +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/src/specfact_cli/analyzers/graph_analyzer.py +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/src/specfact_cli/analyzers/relationship_mapper.py +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/src/specfact_cli/analyzers/requirement_extractor.py +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/src/specfact_cli/analyzers/test_pattern_extractor.py +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/src/specfact_cli/backlog/__init__.py +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/src/specfact_cli/backlog/adapters/__init__.py +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/src/specfact_cli/backlog/adapters/base.py +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/src/specfact_cli/backlog/adapters/local_yaml_adapter.py +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/src/specfact_cli/backlog/ai_refiner.py +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/src/specfact_cli/backlog/converter.py +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/src/specfact_cli/backlog/filters.py +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/src/specfact_cli/backlog/format_detector.py +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/src/specfact_cli/backlog/formats/__init__.py +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/src/specfact_cli/backlog/formats/base.py +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/src/specfact_cli/backlog/formats/markdown_format.py +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/src/specfact_cli/backlog/formats/structured_format.py +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/src/specfact_cli/backlog/mappers/__init__.py +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/src/specfact_cli/backlog/mappers/ado_mapper.py +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/src/specfact_cli/backlog/mappers/base.py +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/src/specfact_cli/backlog/mappers/template_config.py +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/src/specfact_cli/backlog/template_detector.py +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/src/specfact_cli/commands/__init__.py +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/src/specfact_cli/commands/analyze.py +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/src/specfact_cli/commands/auth.py +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/src/specfact_cli/commands/backlog_commands.py +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/src/specfact_cli/commands/contract_cmd.py +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/src/specfact_cli/commands/drift.py +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/src/specfact_cli/commands/enforce.py +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/src/specfact_cli/commands/generate.py +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/src/specfact_cli/commands/import_cmd.py +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/src/specfact_cli/commands/init.py +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/src/specfact_cli/commands/migrate.py +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/src/specfact_cli/commands/plan.py +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/src/specfact_cli/commands/project_cmd.py +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/src/specfact_cli/commands/repro.py +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/src/specfact_cli/commands/sdd.py +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/src/specfact_cli/commands/spec.py +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/src/specfact_cli/commands/sync.py +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/src/specfact_cli/commands/update.py +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/src/specfact_cli/commands/validate.py +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/src/specfact_cli/common/__init__.py +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/src/specfact_cli/common/logger_setup.py +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/src/specfact_cli/common/logging_utils.py +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/src/specfact_cli/common/text_utils.py +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/src/specfact_cli/common/utils.py +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/src/specfact_cli/comparators/__init__.py +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/src/specfact_cli/comparators/plan_comparator.py +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/src/specfact_cli/contracts/__init__.py +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/src/specfact_cli/contracts/crosshair_props.py +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/src/specfact_cli/contracts/module_interface.py +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/src/specfact_cli/enrichers/constitution_enricher.py +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/src/specfact_cli/enrichers/plan_enricher.py +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/src/specfact_cli/generators/__init__.py +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/src/specfact_cli/generators/contract_generator.py +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/src/specfact_cli/generators/openapi_extractor.py +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/src/specfact_cli/generators/persona_exporter.py +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/src/specfact_cli/generators/plan_generator.py +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/src/specfact_cli/generators/protocol_generator.py +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/src/specfact_cli/generators/report_generator.py +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/src/specfact_cli/generators/task_generator.py +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/src/specfact_cli/generators/test_to_openapi.py +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/src/specfact_cli/generators/workflow_generator.py +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/src/specfact_cli/importers/__init__.py +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/src/specfact_cli/importers/speckit_converter.py +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/src/specfact_cli/importers/speckit_scanner.py +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/src/specfact_cli/integrations/__init__.py +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/src/specfact_cli/integrations/specmatic.py +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/src/specfact_cli/merge/__init__.py +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/src/specfact_cli/merge/resolver.py +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/src/specfact_cli/migrations/__init__.py +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/src/specfact_cli/migrations/plan_migrator.py +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/src/specfact_cli/models/__init__.py +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/src/specfact_cli/models/backlog_item.py +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/src/specfact_cli/models/bridge.py +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/src/specfact_cli/models/capabilities.py +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/src/specfact_cli/models/change.py +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/src/specfact_cli/models/contract.py +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/src/specfact_cli/models/deviation.py +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/src/specfact_cli/models/dor_config.py +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/src/specfact_cli/models/enforcement.py +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/src/specfact_cli/models/module_package.py +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/src/specfact_cli/models/persona_template.py +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/src/specfact_cli/models/plan.py +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/src/specfact_cli/models/protocol.py +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/src/specfact_cli/models/quality.py +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/src/specfact_cli/models/sdd.py +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/src/specfact_cli/models/source_tracking.py +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/src/specfact_cli/models/task.py +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/src/specfact_cli/models/validation.py +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/src/specfact_cli/modes/__init__.py +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/src/specfact_cli/modes/detector.py +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/src/specfact_cli/modes/router.py +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/src/specfact_cli/modules/__init__.py +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/src/specfact_cli/modules/analyze/module-package.yaml +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/src/specfact_cli/modules/analyze/src/__init__.py +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/src/specfact_cli/modules/analyze/src/app.py +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/src/specfact_cli/modules/analyze/src/commands.py +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/src/specfact_cli/modules/auth/module-package.yaml +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/src/specfact_cli/modules/auth/src/__init__.py +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/src/specfact_cli/modules/auth/src/app.py +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/src/specfact_cli/modules/auth/src/commands.py +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/src/specfact_cli/modules/backlog/module-package.yaml +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/src/specfact_cli/modules/backlog/src/__init__.py +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/src/specfact_cli/modules/backlog/src/adapters/__init__.py +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/src/specfact_cli/modules/backlog/src/adapters/ado.py +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/src/specfact_cli/modules/backlog/src/adapters/base.py +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/src/specfact_cli/modules/backlog/src/adapters/github.py +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/src/specfact_cli/modules/backlog/src/adapters/jira.py +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/src/specfact_cli/modules/backlog/src/adapters/linear.py +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/src/specfact_cli/modules/backlog/src/app.py +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/src/specfact_cli/modules/contract/module-package.yaml +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/src/specfact_cli/modules/contract/src/__init__.py +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/src/specfact_cli/modules/contract/src/app.py +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/src/specfact_cli/modules/contract/src/commands.py +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/src/specfact_cli/modules/drift/module-package.yaml +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/src/specfact_cli/modules/drift/src/__init__.py +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/src/specfact_cli/modules/drift/src/app.py +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/src/specfact_cli/modules/drift/src/commands.py +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/src/specfact_cli/modules/enforce/module-package.yaml +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/src/specfact_cli/modules/enforce/src/__init__.py +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/src/specfact_cli/modules/enforce/src/app.py +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/src/specfact_cli/modules/enforce/src/commands.py +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/src/specfact_cli/modules/generate/module-package.yaml +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/src/specfact_cli/modules/generate/src/__init__.py +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/src/specfact_cli/modules/generate/src/app.py +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/src/specfact_cli/modules/generate/src/commands.py +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/src/specfact_cli/modules/import_cmd/module-package.yaml +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/src/specfact_cli/modules/import_cmd/src/__init__.py +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/src/specfact_cli/modules/import_cmd/src/app.py +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/src/specfact_cli/modules/import_cmd/src/commands.py +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/src/specfact_cli/modules/init/module-package.yaml +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/src/specfact_cli/modules/init/src/__init__.py +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/src/specfact_cli/modules/init/src/app.py +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/src/specfact_cli/modules/init/src/commands.py +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/src/specfact_cli/modules/migrate/module-package.yaml +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/src/specfact_cli/modules/migrate/src/__init__.py +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/src/specfact_cli/modules/migrate/src/app.py +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/src/specfact_cli/modules/migrate/src/commands.py +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/src/specfact_cli/modules/module_io_shim.py +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/src/specfact_cli/modules/plan/module-package.yaml +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/src/specfact_cli/modules/plan/src/__init__.py +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/src/specfact_cli/modules/plan/src/app.py +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/src/specfact_cli/modules/plan/src/commands.py +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/src/specfact_cli/modules/project/module-package.yaml +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/src/specfact_cli/modules/project/src/__init__.py +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/src/specfact_cli/modules/project/src/app.py +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/src/specfact_cli/modules/repro/module-package.yaml +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/src/specfact_cli/modules/repro/src/__init__.py +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/src/specfact_cli/modules/repro/src/app.py +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/src/specfact_cli/modules/repro/src/commands.py +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/src/specfact_cli/modules/sdd/module-package.yaml +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/src/specfact_cli/modules/sdd/src/__init__.py +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/src/specfact_cli/modules/sdd/src/app.py +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/src/specfact_cli/modules/sdd/src/commands.py +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/src/specfact_cli/modules/spec/module-package.yaml +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/src/specfact_cli/modules/spec/src/__init__.py +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/src/specfact_cli/modules/spec/src/app.py +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/src/specfact_cli/modules/spec/src/commands.py +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/src/specfact_cli/modules/sync/module-package.yaml +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/src/specfact_cli/modules/sync/src/__init__.py +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/src/specfact_cli/modules/sync/src/app.py +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/src/specfact_cli/modules/sync/src/commands.py +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/src/specfact_cli/modules/upgrade/module-package.yaml +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/src/specfact_cli/modules/upgrade/src/__init__.py +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/src/specfact_cli/modules/upgrade/src/app.py +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/src/specfact_cli/modules/upgrade/src/commands.py +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/src/specfact_cli/modules/validate/module-package.yaml +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/src/specfact_cli/modules/validate/src/__init__.py +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/src/specfact_cli/modules/validate/src/app.py +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/src/specfact_cli/modules/validate/src/commands.py +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/src/specfact_cli/parsers/__init__.py +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/src/specfact_cli/parsers/persona_importer.py +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/src/specfact_cli/registry/__init__.py +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/src/specfact_cli/registry/help_cache.py +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/src/specfact_cli/registry/metadata.py +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/src/specfact_cli/registry/module_state.py +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/src/specfact_cli/registry/registry.py +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/src/specfact_cli/resources/semgrep/async.yml +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/src/specfact_cli/resources/semgrep/code-quality.yml +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/src/specfact_cli/resources/semgrep/feature-detection.yml +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/src/specfact_cli/runtime.py +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/src/specfact_cli/sync/__init__.py +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/src/specfact_cli/sync/bridge_probe.py +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/src/specfact_cli/sync/bridge_sync.py +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/src/specfact_cli/sync/bridge_watch.py +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/src/specfact_cli/sync/change_detector.py +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/src/specfact_cli/sync/code_to_spec.py +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/src/specfact_cli/sync/drift_detector.py +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/src/specfact_cli/sync/repository_sync.py +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/src/specfact_cli/sync/spec_to_code.py +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/src/specfact_cli/sync/spec_to_tests.py +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/src/specfact_cli/sync/watcher.py +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/src/specfact_cli/sync/watcher_enhanced.py +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/src/specfact_cli/telemetry.py +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/src/specfact_cli/templates/__init__.py +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/src/specfact_cli/templates/bridge_templates.py +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/src/specfact_cli/templates/defaults/defect_v1.yaml +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/src/specfact_cli/templates/defaults/enabler_v1.yaml +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/src/specfact_cli/templates/defaults/spike_v1.yaml +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/src/specfact_cli/templates/defaults/user_story_v1.yaml +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/src/specfact_cli/templates/frameworks/scrum/user_story_v1.yaml +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/src/specfact_cli/templates/personas/product-owner/user_story_v1.yaml +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/src/specfact_cli/templates/providers/ado/work_item_v1.yaml +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/src/specfact_cli/templates/registry.py +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/src/specfact_cli/templates/specification_templates.py +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/src/specfact_cli/utils/__init__.py +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/src/specfact_cli/utils/acceptance_criteria.py +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/src/specfact_cli/utils/auth_tokens.py +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/src/specfact_cli/utils/bundle_converters.py +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/src/specfact_cli/utils/bundle_loader.py +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/src/specfact_cli/utils/code_change_detector.py +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/src/specfact_cli/utils/console.py +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/src/specfact_cli/utils/content_sanitizer.py +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/src/specfact_cli/utils/context_detection.py +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/src/specfact_cli/utils/enrichment_context.py +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/src/specfact_cli/utils/enrichment_parser.py +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/src/specfact_cli/utils/env_manager.py +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/src/specfact_cli/utils/feature_keys.py +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/src/specfact_cli/utils/git.py +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/src/specfact_cli/utils/github_annotations.py +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/src/specfact_cli/utils/ide_setup.py +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/src/specfact_cli/utils/incremental_check.py +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/src/specfact_cli/utils/metadata.py +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/src/specfact_cli/utils/optional_deps.py +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/src/specfact_cli/utils/performance.py +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/src/specfact_cli/utils/persona_ownership.py +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/src/specfact_cli/utils/progress.py +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/src/specfact_cli/utils/progressive_disclosure.py +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/src/specfact_cli/utils/prompts.py +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/src/specfact_cli/utils/sdd_discovery.py +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/src/specfact_cli/utils/source_scanner.py +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/src/specfact_cli/utils/startup_checks.py +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/src/specfact_cli/utils/structure.py +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/src/specfact_cli/utils/structured_io.py +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/src/specfact_cli/utils/suggestions.py +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/src/specfact_cli/utils/terminal.py +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/src/specfact_cli/utils/yaml_utils.py +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/src/specfact_cli/validators/__init__.py +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/src/specfact_cli/validators/agile_validation.py +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/src/specfact_cli/validators/change_proposal_integration.py +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/src/specfact_cli/validators/cli_first_validator.py +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/src/specfact_cli/validators/contract_validator.py +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/src/specfact_cli/validators/fsm.py +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/src/specfact_cli/validators/repro_checker.py +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/src/specfact_cli/validators/schema.py +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/src/specfact_cli/validators/sidecar/__init__.py +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/src/specfact_cli/validators/sidecar/contract_populator.py +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/src/specfact_cli/validators/sidecar/crosshair_runner.py +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/src/specfact_cli/validators/sidecar/crosshair_summary.py +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/src/specfact_cli/validators/sidecar/dependency_installer.py +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/src/specfact_cli/validators/sidecar/framework_detector.py +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/src/specfact_cli/validators/sidecar/frameworks/__init__.py +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/src/specfact_cli/validators/sidecar/frameworks/base.py +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/src/specfact_cli/validators/sidecar/frameworks/django.py +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/src/specfact_cli/validators/sidecar/frameworks/drf.py +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/src/specfact_cli/validators/sidecar/frameworks/fastapi.py +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/src/specfact_cli/validators/sidecar/frameworks/flask.py +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/src/specfact_cli/validators/sidecar/models.py +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/src/specfact_cli/validators/sidecar/orchestrator.py +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/src/specfact_cli/validators/sidecar/specmatic_runner.py +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/src/specfact_cli/validators/sidecar/unannotated_detector.py +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/src/specfact_cli/versioning/__init__.py +0 -0
- {specfact_cli-0.30.4 → specfact_cli-0.31.1}/src/specfact_cli/versioning/analyzer.py +0 -0
|
@@ -35,8 +35,7 @@
|
|
|
35
35
|
"Work" shall mean the work of authorship, whether in Source or
|
|
36
36
|
Object form, made available under the License, as indicated by a
|
|
37
37
|
copyright notice that is included in or attached to the work
|
|
38
|
-
(
|
|
39
|
-
otherwise designated in writing by the copyright owner as "Not a Work").
|
|
38
|
+
(an example is provided in the Appendix below).
|
|
40
39
|
|
|
41
40
|
"Derivative Works" shall mean any work, whether in Source or Object
|
|
42
41
|
form, that is based on (or derived from) the Work and for which the
|
|
@@ -57,8 +56,8 @@
|
|
|
57
56
|
communication on electronic mailing lists, source code control systems,
|
|
58
57
|
and issue tracking systems that are managed by, or on behalf of, the
|
|
59
58
|
Licensor for the purpose of discussing and improving the Work, but
|
|
60
|
-
excluding communication that is
|
|
61
|
-
in writing by the copyright owner as "Not a Contribution"
|
|
59
|
+
excluding communication that is conspicuously marked or otherwise
|
|
60
|
+
designated in writing by the copyright owner as "Not a Contribution."
|
|
62
61
|
|
|
63
62
|
"Contributor" shall mean Licensor and any individual or Legal Entity
|
|
64
63
|
on behalf of whom a Contribution has been received by Licensor and
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: specfact-cli
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.31.1
|
|
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
|
|
@@ -45,8 +45,7 @@ License: Apache License
|
|
|
45
45
|
"Work" shall mean the work of authorship, whether in Source or
|
|
46
46
|
Object form, made available under the License, as indicated by a
|
|
47
47
|
copyright notice that is included in or attached to the work
|
|
48
|
-
(
|
|
49
|
-
otherwise designated in writing by the copyright owner as "Not a Work").
|
|
48
|
+
(an example is provided in the Appendix below).
|
|
50
49
|
|
|
51
50
|
"Derivative Works" shall mean any work, whether in Source or Object
|
|
52
51
|
form, that is based on (or derived from) the Work and for which the
|
|
@@ -67,8 +66,8 @@ License: Apache License
|
|
|
67
66
|
communication on electronic mailing lists, source code control systems,
|
|
68
67
|
and issue tracking systems that are managed by, or on behalf of, the
|
|
69
68
|
Licensor for the purpose of discussing and improving the Work, but
|
|
70
|
-
excluding communication that is
|
|
71
|
-
in writing by the copyright owner as "Not a Contribution"
|
|
69
|
+
excluding communication that is conspicuously marked or otherwise
|
|
70
|
+
designated in writing by the copyright owner as "Not a Contribution."
|
|
72
71
|
|
|
73
72
|
"Contributor" shall mean Licensor and any individual or Legal Entity
|
|
74
73
|
on behalf of whom a Contribution has been received by Licensor and
|
|
@@ -210,7 +209,7 @@ License: Apache License
|
|
|
210
209
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
211
210
|
See the License for the specific language governing permissions and
|
|
212
211
|
limitations under the License.
|
|
213
|
-
License-File: LICENSE
|
|
212
|
+
License-File: LICENSE
|
|
214
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
|
|
215
214
|
Classifier: Development Status :: 4 - Beta
|
|
216
215
|
Classifier: Intended Audience :: Developers
|
|
@@ -286,7 +285,7 @@ Description-Content-Type: text/markdown
|
|
|
286
285
|
|
|
287
286
|
[](https://pypi.org/project/specfact-cli/)
|
|
288
287
|
[](https://pypi.org/project/specfact-cli/)
|
|
289
|
-
[](LICENSE
|
|
288
|
+
[](LICENSE)
|
|
290
289
|
[](https://github.com/nold-ai/specfact-cli)
|
|
291
290
|
|
|
292
291
|
<div align="center">
|
|
@@ -388,6 +387,10 @@ Most tools help **either** coders **or** agile teams. SpecFact does both:
|
|
|
388
387
|
- **Ceremony support teams can run**: standup, refinement, sprint planning, flow metrics (Scrum/Kanban/SAFe).
|
|
389
388
|
- **Policy + validation**: DoR/DoD/flow checks plus contract enforcement for production-grade stability.
|
|
390
389
|
|
|
390
|
+
Recommended command entrypoints:
|
|
391
|
+
- `specfact backlog ceremony standup ...`
|
|
392
|
+
- `specfact backlog ceremony refinement ...`
|
|
393
|
+
|
|
391
394
|
**Try it now**
|
|
392
395
|
|
|
393
396
|
- **Coders**: [AI IDE Workflow](docs/guides/ai-ide-workflow.md)
|
|
@@ -492,6 +495,9 @@ SpecFact complements your stack rather than replacing it.
|
|
|
492
495
|
- **[Documentation Index](docs/README.md)**
|
|
493
496
|
- **[Command Reference](docs/reference/commands.md)**
|
|
494
497
|
- **[Backlog Refinement](docs/guides/backlog-refinement.md)**
|
|
498
|
+
- **[Backlog Dependency Analysis](docs/guides/backlog-dependency-analysis.md)**
|
|
499
|
+
- **[Backlog Delta Commands](docs/guides/backlog-delta-commands.md)**
|
|
500
|
+
- **[Project DevOps Flow](docs/guides/project-devops-flow.md)**
|
|
495
501
|
- **[Sidecar Validation](docs/guides/sidecar-validation.md)**
|
|
496
502
|
- **[OpenSpec Journey](docs/guides/openspec-journey.md)**
|
|
497
503
|
|
|
@@ -514,7 +520,7 @@ hatch run contract-test-full
|
|
|
514
520
|
|
|
515
521
|
**Apache License 2.0** - Open source and enterprise-friendly.
|
|
516
522
|
|
|
517
|
-
[Full license](LICENSE
|
|
523
|
+
[Full license](LICENSE)
|
|
518
524
|
|
|
519
525
|
---
|
|
520
526
|
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
|
|
9
9
|
[](https://pypi.org/project/specfact-cli/)
|
|
10
10
|
[](https://pypi.org/project/specfact-cli/)
|
|
11
|
-
[](LICENSE
|
|
11
|
+
[](LICENSE)
|
|
12
12
|
[](https://github.com/nold-ai/specfact-cli)
|
|
13
13
|
|
|
14
14
|
<div align="center">
|
|
@@ -110,6 +110,10 @@ Most tools help **either** coders **or** agile teams. SpecFact does both:
|
|
|
110
110
|
- **Ceremony support teams can run**: standup, refinement, sprint planning, flow metrics (Scrum/Kanban/SAFe).
|
|
111
111
|
- **Policy + validation**: DoR/DoD/flow checks plus contract enforcement for production-grade stability.
|
|
112
112
|
|
|
113
|
+
Recommended command entrypoints:
|
|
114
|
+
- `specfact backlog ceremony standup ...`
|
|
115
|
+
- `specfact backlog ceremony refinement ...`
|
|
116
|
+
|
|
113
117
|
**Try it now**
|
|
114
118
|
|
|
115
119
|
- **Coders**: [AI IDE Workflow](docs/guides/ai-ide-workflow.md)
|
|
@@ -214,6 +218,9 @@ SpecFact complements your stack rather than replacing it.
|
|
|
214
218
|
- **[Documentation Index](docs/README.md)**
|
|
215
219
|
- **[Command Reference](docs/reference/commands.md)**
|
|
216
220
|
- **[Backlog Refinement](docs/guides/backlog-refinement.md)**
|
|
221
|
+
- **[Backlog Dependency Analysis](docs/guides/backlog-dependency-analysis.md)**
|
|
222
|
+
- **[Backlog Delta Commands](docs/guides/backlog-delta-commands.md)**
|
|
223
|
+
- **[Project DevOps Flow](docs/guides/project-devops-flow.md)**
|
|
217
224
|
- **[Sidecar Validation](docs/guides/sidecar-validation.md)**
|
|
218
225
|
- **[OpenSpec Journey](docs/guides/openspec-journey.md)**
|
|
219
226
|
|
|
@@ -236,7 +243,7 @@ hatch run contract-test-full
|
|
|
236
243
|
|
|
237
244
|
**Apache License 2.0** - Open source and enterprise-friendly.
|
|
238
245
|
|
|
239
|
-
[Full license](LICENSE
|
|
246
|
+
[Full license](LICENSE)
|
|
240
247
|
|
|
241
248
|
---
|
|
242
249
|
|
|
@@ -4,11 +4,11 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "specfact-cli"
|
|
7
|
-
version = "0.
|
|
7
|
+
version = "0.31.1"
|
|
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"
|
|
11
|
-
license = { file = "LICENSE
|
|
11
|
+
license = { file = "LICENSE" } # Apache License 2.0
|
|
12
12
|
authors = [
|
|
13
13
|
{name = "NOLD AI (Owner: Dominikus Nold)", email = "hello@noldai.com"}
|
|
14
14
|
]
|
|
@@ -385,7 +385,7 @@ include = [
|
|
|
385
385
|
"/src",
|
|
386
386
|
"/resources",
|
|
387
387
|
"/README.md",
|
|
388
|
-
"/LICENSE
|
|
388
|
+
"/LICENSE",
|
|
389
389
|
"/pyproject.toml",
|
|
390
390
|
]
|
|
391
391
|
# Exclude development files, tests, docs, tools, etc.
|
|
@@ -32,6 +32,7 @@ from specfact_cli.models.backlog_item import BacklogItem
|
|
|
32
32
|
from specfact_cli.models.bridge import BridgeConfig
|
|
33
33
|
from specfact_cli.models.capabilities import ToolCapabilities
|
|
34
34
|
from specfact_cli.models.change import ChangeProposal, ChangeTracking
|
|
35
|
+
from specfact_cli.registry.bridge_registry import BRIDGE_PROTOCOL_REGISTRY
|
|
35
36
|
from specfact_cli.runtime import debug_log_operation, debug_print, is_debug_mode
|
|
36
37
|
from specfact_cli.utils.auth_tokens import get_token, set_token
|
|
37
38
|
|
|
@@ -3136,6 +3137,109 @@ class AdoAdapter(BridgeAdapter, BacklogAdapterMixin, BacklogAdapter):
|
|
|
3136
3137
|
|
|
3137
3138
|
return filtered_items
|
|
3138
3139
|
|
|
3140
|
+
@beartype
|
|
3141
|
+
@require(lambda project_id: isinstance(project_id, str) and len(project_id) > 0, "project_id must be non-empty")
|
|
3142
|
+
@ensure(lambda result: isinstance(result, list), "Must return list")
|
|
3143
|
+
def fetch_all_issues(self, project_id: str, filters: dict[str, Any] | None = None) -> list[dict[str, Any]]:
|
|
3144
|
+
"""Fetch all ADO work items as provider-agnostic dictionaries for graph building."""
|
|
3145
|
+
original_org = self.org
|
|
3146
|
+
original_project = self.project
|
|
3147
|
+
self.org, self.project = self._resolve_graph_project_context(project_id)
|
|
3148
|
+
try:
|
|
3149
|
+
backlog_filters = BacklogFilters(**(filters or {}))
|
|
3150
|
+
return [item.model_dump() for item in self.fetch_backlog_items(backlog_filters)]
|
|
3151
|
+
finally:
|
|
3152
|
+
self.org = original_org
|
|
3153
|
+
self.project = original_project
|
|
3154
|
+
|
|
3155
|
+
@beartype
|
|
3156
|
+
@require(lambda project_id: isinstance(project_id, str) and len(project_id) > 0, "project_id must be non-empty")
|
|
3157
|
+
@ensure(lambda result: isinstance(result, list), "Must return list")
|
|
3158
|
+
def fetch_relationships(self, project_id: str) -> list[dict[str, Any]]:
|
|
3159
|
+
"""Fetch ADO relationship edges for graph building."""
|
|
3160
|
+
relationships: list[dict[str, Any]] = []
|
|
3161
|
+
seen: set[tuple[str, str, str]] = set()
|
|
3162
|
+
|
|
3163
|
+
def _add_edge(source_id: str, target_id: str, relation_type: str) -> None:
|
|
3164
|
+
source = source_id.strip()
|
|
3165
|
+
target = target_id.strip()
|
|
3166
|
+
rel = relation_type.strip().lower()
|
|
3167
|
+
if not source or not target or source == target:
|
|
3168
|
+
return
|
|
3169
|
+
key = (source, target, rel)
|
|
3170
|
+
if key in seen:
|
|
3171
|
+
return
|
|
3172
|
+
seen.add(key)
|
|
3173
|
+
relationships.append({"source_id": source, "target_id": target, "type": rel})
|
|
3174
|
+
|
|
3175
|
+
for item in self.fetch_all_issues(project_id):
|
|
3176
|
+
item_id = str(item.get("id") or item.get("key") or "").strip()
|
|
3177
|
+
if not item_id:
|
|
3178
|
+
continue
|
|
3179
|
+
|
|
3180
|
+
provider_fields = item.get("provider_fields")
|
|
3181
|
+
relation_entries: list[Any] = []
|
|
3182
|
+
if isinstance(provider_fields, dict):
|
|
3183
|
+
relations = provider_fields.get("relations")
|
|
3184
|
+
if isinstance(relations, list):
|
|
3185
|
+
relation_entries.extend(relations)
|
|
3186
|
+
if isinstance(item.get("relations"), list):
|
|
3187
|
+
relation_entries.extend(item["relations"])
|
|
3188
|
+
|
|
3189
|
+
for relation in relation_entries:
|
|
3190
|
+
if not isinstance(relation, dict):
|
|
3191
|
+
continue
|
|
3192
|
+
rel_name = str(relation.get("rel") or relation.get("relation") or relation.get("type") or "").lower()
|
|
3193
|
+
target_ref = str(relation.get("url") or relation.get("target") or "")
|
|
3194
|
+
target_id = self._extract_work_item_id_from_reference(target_ref)
|
|
3195
|
+
if not target_id:
|
|
3196
|
+
continue
|
|
3197
|
+
|
|
3198
|
+
if "hierarchy-forward" in rel_name:
|
|
3199
|
+
_add_edge(item_id, target_id, "parent")
|
|
3200
|
+
elif "hierarchy-reverse" in rel_name:
|
|
3201
|
+
_add_edge(target_id, item_id, "parent")
|
|
3202
|
+
elif "dependency-forward" in rel_name or "predecessor-forward" in rel_name:
|
|
3203
|
+
_add_edge(item_id, target_id, "blocks")
|
|
3204
|
+
elif "dependency-reverse" in rel_name or "predecessor-reverse" in rel_name:
|
|
3205
|
+
_add_edge(target_id, item_id, "blocks")
|
|
3206
|
+
elif "related" in rel_name:
|
|
3207
|
+
_add_edge(item_id, target_id, "relates")
|
|
3208
|
+
|
|
3209
|
+
return relationships
|
|
3210
|
+
|
|
3211
|
+
@beartype
|
|
3212
|
+
@require(
|
|
3213
|
+
lambda project_id: isinstance(project_id, str) and len(project_id.strip()) > 0, "project_id must be non-empty"
|
|
3214
|
+
)
|
|
3215
|
+
@ensure(lambda result: isinstance(result, tuple) and len(result) == 2, "Must return (org, project) tuple")
|
|
3216
|
+
def _resolve_graph_project_context(self, project_id: str) -> tuple[str, str]:
|
|
3217
|
+
"""Resolve org/project context for graph APIs from linked project_id and adapter defaults."""
|
|
3218
|
+
normalized = project_id.strip()
|
|
3219
|
+
if "/" in normalized:
|
|
3220
|
+
org, project = normalized.split("/", 1)
|
|
3221
|
+
resolved_org = org.strip()
|
|
3222
|
+
resolved_project = project.strip()
|
|
3223
|
+
if resolved_org and resolved_project:
|
|
3224
|
+
return resolved_org, resolved_project
|
|
3225
|
+
raise ValueError(f"Invalid ADO project_id format: {project_id!r}. Expected '<org>/<project>'.")
|
|
3226
|
+
|
|
3227
|
+
# Backward compatibility: allow project-only identifiers when adapter org already exists.
|
|
3228
|
+
if self.org:
|
|
3229
|
+
return self.org, normalized
|
|
3230
|
+
raise ValueError(
|
|
3231
|
+
f"ADO project_id '{project_id}' missing organization. Use '<org>/<project>' or configure adapter org."
|
|
3232
|
+
)
|
|
3233
|
+
|
|
3234
|
+
@beartype
|
|
3235
|
+
@ensure(lambda result: isinstance(result, str), "Work item id extraction must return str")
|
|
3236
|
+
def _extract_work_item_id_from_reference(self, reference: str) -> str:
|
|
3237
|
+
"""Extract ADO work item id from relation reference URL/string."""
|
|
3238
|
+
if not reference:
|
|
3239
|
+
return ""
|
|
3240
|
+
match = re.search(r"/workitems/(\d+)", reference, flags=re.IGNORECASE)
|
|
3241
|
+
return match.group(1) if match else ""
|
|
3242
|
+
|
|
3139
3243
|
@beartype
|
|
3140
3244
|
def supports_add_comment(self) -> bool:
|
|
3141
3245
|
"""Whether this adapter can add comments (requires token, org, project)."""
|
|
@@ -3431,3 +3535,6 @@ class AdoAdapter(BridgeAdapter, BacklogAdapterMixin, BacklogAdapter):
|
|
|
3431
3535
|
org=self.org,
|
|
3432
3536
|
project_name=self.project,
|
|
3433
3537
|
)
|
|
3538
|
+
|
|
3539
|
+
|
|
3540
|
+
BRIDGE_PROTOCOL_REGISTRY.register_implementation("backlog_graph", "ado", AdoAdapter)
|
|
@@ -35,6 +35,7 @@ from specfact_cli.models.backlog_item import BacklogItem
|
|
|
35
35
|
from specfact_cli.models.bridge import BridgeConfig
|
|
36
36
|
from specfact_cli.models.capabilities import ToolCapabilities
|
|
37
37
|
from specfact_cli.models.change import ChangeProposal, ChangeTracking
|
|
38
|
+
from specfact_cli.registry.bridge_registry import BRIDGE_PROTOCOL_REGISTRY
|
|
38
39
|
from specfact_cli.runtime import debug_log_operation, is_debug_mode
|
|
39
40
|
from specfact_cli.utils.auth_tokens import get_token
|
|
40
41
|
|
|
@@ -2638,6 +2639,157 @@ class GitHubAdapter(BridgeAdapter, BacklogAdapterMixin, BacklogAdapter):
|
|
|
2638
2639
|
|
|
2639
2640
|
return filtered_items
|
|
2640
2641
|
|
|
2642
|
+
@beartype
|
|
2643
|
+
@require(lambda project_id: isinstance(project_id, str) and len(project_id) > 0, "project_id must be non-empty")
|
|
2644
|
+
@ensure(lambda result: isinstance(result, list), "Must return list")
|
|
2645
|
+
def fetch_all_issues(self, project_id: str, filters: dict[str, Any] | None = None) -> list[dict[str, Any]]:
|
|
2646
|
+
"""Fetch all backlog items as provider-agnostic dictionaries for graph building."""
|
|
2647
|
+
owner, repo = project_id.split("/", 1) if "/" in project_id else (self.repo_owner, self.repo_name)
|
|
2648
|
+
previous_owner = self.repo_owner
|
|
2649
|
+
previous_repo = self.repo_name
|
|
2650
|
+
try:
|
|
2651
|
+
if owner and repo:
|
|
2652
|
+
self.repo_owner = owner
|
|
2653
|
+
self.repo_name = repo
|
|
2654
|
+
backlog_filters = BacklogFilters(**(filters or {}))
|
|
2655
|
+
enriched_items: list[dict[str, Any]] = []
|
|
2656
|
+
for item in self.fetch_backlog_items(backlog_filters):
|
|
2657
|
+
issue_dict = item.model_dump()
|
|
2658
|
+
inferred_type = self._infer_graph_item_type(issue_dict)
|
|
2659
|
+
if inferred_type:
|
|
2660
|
+
issue_dict["type"] = inferred_type
|
|
2661
|
+
enriched_items.append(issue_dict)
|
|
2662
|
+
return enriched_items
|
|
2663
|
+
finally:
|
|
2664
|
+
self.repo_owner = previous_owner
|
|
2665
|
+
self.repo_name = previous_repo
|
|
2666
|
+
|
|
2667
|
+
@beartype
|
|
2668
|
+
@require(lambda project_id: isinstance(project_id, str) and len(project_id) > 0, "project_id must be non-empty")
|
|
2669
|
+
@ensure(lambda result: isinstance(result, list), "Must return list")
|
|
2670
|
+
def fetch_relationships(self, project_id: str) -> list[dict[str, Any]]:
|
|
2671
|
+
"""Fetch relationships for a GitHub backlog project."""
|
|
2672
|
+
relationships: list[dict[str, Any]] = []
|
|
2673
|
+
seen: set[tuple[str, str, str]] = set()
|
|
2674
|
+
|
|
2675
|
+
def _add_edge(source_id: str, target_id: str, relation_type: str) -> None:
|
|
2676
|
+
source = source_id.strip()
|
|
2677
|
+
target = target_id.strip()
|
|
2678
|
+
rel = relation_type.strip().lower()
|
|
2679
|
+
if not source or not target or source == target:
|
|
2680
|
+
return
|
|
2681
|
+
key = (source, target, rel)
|
|
2682
|
+
if key in seen:
|
|
2683
|
+
return
|
|
2684
|
+
seen.add(key)
|
|
2685
|
+
relationships.append({"source_id": source, "target_id": target, "type": rel})
|
|
2686
|
+
|
|
2687
|
+
issues = self.fetch_all_issues(project_id)
|
|
2688
|
+
for issue in issues:
|
|
2689
|
+
issue_id = str(issue.get("id") or issue.get("key") or "").strip()
|
|
2690
|
+
if not issue_id:
|
|
2691
|
+
continue
|
|
2692
|
+
|
|
2693
|
+
provider_fields = issue.get("provider_fields")
|
|
2694
|
+
if isinstance(provider_fields, dict):
|
|
2695
|
+
linked_issues = provider_fields.get("linked_issues", [])
|
|
2696
|
+
if isinstance(linked_issues, list):
|
|
2697
|
+
for linked in linked_issues:
|
|
2698
|
+
if not isinstance(linked, dict):
|
|
2699
|
+
continue
|
|
2700
|
+
relation = str(linked.get("relation") or linked.get("type") or "").strip().lower()
|
|
2701
|
+
linked_id = str(linked.get("id") or linked.get("number") or "").strip()
|
|
2702
|
+
if not linked_id:
|
|
2703
|
+
linked_url = str(linked.get("url") or "")
|
|
2704
|
+
linked_match = re.search(r"/issues/(\d+)", linked_url, flags=re.IGNORECASE)
|
|
2705
|
+
linked_id = linked_match.group(1) if linked_match else ""
|
|
2706
|
+
if not linked_id:
|
|
2707
|
+
continue
|
|
2708
|
+
if relation in {"blocks", "block"}:
|
|
2709
|
+
_add_edge(issue_id, linked_id, "blocks")
|
|
2710
|
+
elif relation in {"blocked_by", "blocked by"}:
|
|
2711
|
+
_add_edge(linked_id, issue_id, "blocks")
|
|
2712
|
+
elif relation in {"parent", "parent_of"}:
|
|
2713
|
+
_add_edge(linked_id, issue_id, "parent")
|
|
2714
|
+
elif relation in {"child", "child_of"}:
|
|
2715
|
+
_add_edge(issue_id, linked_id, "parent")
|
|
2716
|
+
else:
|
|
2717
|
+
_add_edge(issue_id, linked_id, "relates")
|
|
2718
|
+
|
|
2719
|
+
body = str(issue.get("body_markdown") or issue.get("description") or "")
|
|
2720
|
+
for match in re.finditer(r"(?im)\bblocks?\s+#(\d+)\b", body):
|
|
2721
|
+
_add_edge(issue_id, match.group(1), "blocks")
|
|
2722
|
+
for match in re.finditer(r"(?im)\bblocked\s+by\s+#(\d+)\b", body):
|
|
2723
|
+
_add_edge(match.group(1), issue_id, "blocks")
|
|
2724
|
+
for match in re.finditer(r"(?im)\bdepends\s+on\s+#(\d+)\b", body):
|
|
2725
|
+
_add_edge(match.group(1), issue_id, "blocks")
|
|
2726
|
+
for match in re.finditer(r"(?im)\bparent\s*[:#]?\s*#(\d+)\b", body):
|
|
2727
|
+
_add_edge(match.group(1), issue_id, "parent")
|
|
2728
|
+
for match in re.finditer(r"(?im)\bchild(?:ren)?\s*[:#]?\s*#(\d+)\b", body):
|
|
2729
|
+
_add_edge(issue_id, match.group(1), "parent")
|
|
2730
|
+
for match in re.finditer(r"(?im)\b(?:related\s+to|relates?\s+to|refs?|references?)\s+#(\d+)\b", body):
|
|
2731
|
+
_add_edge(issue_id, match.group(1), "relates")
|
|
2732
|
+
|
|
2733
|
+
return relationships
|
|
2734
|
+
|
|
2735
|
+
@beartype
|
|
2736
|
+
@ensure(lambda result: result is None or isinstance(result, str), "Type inference must return str or None")
|
|
2737
|
+
def _infer_graph_item_type(self, issue_payload: dict[str, Any]) -> str | None:
|
|
2738
|
+
"""Infer normalized graph item type from GitHub issue payload."""
|
|
2739
|
+
alias_map = {
|
|
2740
|
+
"epic": "epic",
|
|
2741
|
+
"feature": "feature",
|
|
2742
|
+
"story": "story",
|
|
2743
|
+
"user story": "story",
|
|
2744
|
+
"task": "task",
|
|
2745
|
+
"bug": "bug",
|
|
2746
|
+
"sub-task": "sub_task",
|
|
2747
|
+
"sub task": "sub_task",
|
|
2748
|
+
"subtask": "sub_task",
|
|
2749
|
+
}
|
|
2750
|
+
|
|
2751
|
+
def _normalize(raw_value: str) -> str | None:
|
|
2752
|
+
normalized = raw_value.strip().lower().replace("_", " ").replace("-", " ")
|
|
2753
|
+
if not normalized:
|
|
2754
|
+
return None
|
|
2755
|
+
if normalized in alias_map:
|
|
2756
|
+
return alias_map[normalized]
|
|
2757
|
+
for separator in (":", "/"):
|
|
2758
|
+
if separator in normalized:
|
|
2759
|
+
suffix = normalized.split(separator)[-1].strip()
|
|
2760
|
+
if suffix in alias_map:
|
|
2761
|
+
return alias_map[suffix]
|
|
2762
|
+
for token, mapped in alias_map.items():
|
|
2763
|
+
if normalized.startswith(f"{token} ") or normalized.endswith(f" {token}"):
|
|
2764
|
+
return mapped
|
|
2765
|
+
return None
|
|
2766
|
+
|
|
2767
|
+
for key in ("type", "work_item_type"):
|
|
2768
|
+
value = issue_payload.get(key)
|
|
2769
|
+
if isinstance(value, str):
|
|
2770
|
+
mapped = _normalize(value)
|
|
2771
|
+
if mapped:
|
|
2772
|
+
return mapped
|
|
2773
|
+
|
|
2774
|
+
tags = issue_payload.get("tags")
|
|
2775
|
+
if isinstance(tags, list):
|
|
2776
|
+
for tag in tags:
|
|
2777
|
+
if isinstance(tag, str):
|
|
2778
|
+
mapped = _normalize(tag)
|
|
2779
|
+
if mapped:
|
|
2780
|
+
return mapped
|
|
2781
|
+
|
|
2782
|
+
title = issue_payload.get("title")
|
|
2783
|
+
if isinstance(title, str):
|
|
2784
|
+
mapped = _normalize(title)
|
|
2785
|
+
if mapped:
|
|
2786
|
+
return mapped
|
|
2787
|
+
for token, mapped_value in alias_map.items():
|
|
2788
|
+
if title.lower().startswith(f"[{token}]"):
|
|
2789
|
+
return mapped_value
|
|
2790
|
+
|
|
2791
|
+
return None
|
|
2792
|
+
|
|
2641
2793
|
@beartype
|
|
2642
2794
|
def supports_add_comment(self) -> bool:
|
|
2643
2795
|
"""Whether this adapter can add comments (requires token and repo)."""
|
|
@@ -2818,3 +2970,6 @@ class GitHubAdapter(BridgeAdapter, BacklogAdapterMixin, BacklogAdapter):
|
|
|
2818
2970
|
from specfact_cli.backlog.converter import convert_github_issue_to_backlog_item
|
|
2819
2971
|
|
|
2820
2972
|
return convert_github_issue_to_backlog_item(updated_issue, provider="github")
|
|
2973
|
+
|
|
2974
|
+
|
|
2975
|
+
BRIDGE_PROTOCOL_REGISTRY.register_implementation("backlog_graph", "github", GitHubAdapter)
|
{specfact_cli-0.30.4 → specfact_cli-0.31.1}/src/specfact_cli/backlog/mappers/github_mapper.py
RENAMED
|
@@ -246,11 +246,25 @@ class GitHubFieldMapper(FieldMapper):
|
|
|
246
246
|
Returns:
|
|
247
247
|
Work item type or None if not found
|
|
248
248
|
"""
|
|
249
|
-
# Common work item type labels
|
|
250
|
-
|
|
249
|
+
# Common work item type labels (case-insensitive + prefixed forms)
|
|
250
|
+
normalized_to_canonical = {
|
|
251
|
+
"epic": "Epic",
|
|
252
|
+
"feature": "Feature",
|
|
253
|
+
"user story": "User Story",
|
|
254
|
+
"story": "Story",
|
|
255
|
+
"task": "Task",
|
|
256
|
+
"bug": "Bug",
|
|
257
|
+
"bugfix": "Bugfix",
|
|
258
|
+
}
|
|
251
259
|
for label in label_names:
|
|
252
|
-
|
|
253
|
-
|
|
260
|
+
normalized = str(label).strip().lower()
|
|
261
|
+
if normalized in normalized_to_canonical:
|
|
262
|
+
return normalized_to_canonical[normalized]
|
|
263
|
+
if normalized.startswith(("type:", "kind:", "work-item:")):
|
|
264
|
+
_, _, suffix = normalized.partition(":")
|
|
265
|
+
canonical = normalized_to_canonical.get(suffix.strip())
|
|
266
|
+
if canonical:
|
|
267
|
+
return canonical
|
|
254
268
|
|
|
255
269
|
# Check issue type metadata if available
|
|
256
270
|
issue_type = item_data.get("issue_type") or item_data.get("type")
|
|
@@ -59,6 +59,7 @@ from specfact_cli.modes import OperationalMode, detect_mode
|
|
|
59
59
|
# Command groups are registered via CommandRegistry (bootstrap); no top-level command imports.
|
|
60
60
|
from specfact_cli.registry import CommandRegistry
|
|
61
61
|
from specfact_cli.registry.bootstrap import register_builtin_commands
|
|
62
|
+
from specfact_cli.registry.metadata import CommandMetadata
|
|
62
63
|
from specfact_cli.runtime import get_configured_console, init_debug_log_file, set_debug_mode
|
|
63
64
|
from specfact_cli.utils.progressive_disclosure import ProgressiveDisclosureGroup
|
|
64
65
|
from specfact_cli.utils.structured_io import StructuredFormat
|
|
@@ -521,7 +522,36 @@ def _patch_typer_build() -> None:
|
|
|
521
522
|
|
|
522
523
|
register_builtin_commands()
|
|
523
524
|
_patch_typer_build()
|
|
524
|
-
|
|
525
|
+
|
|
526
|
+
|
|
527
|
+
def _grouped_command_order(
|
|
528
|
+
commands: list[tuple[str, CommandMetadata]],
|
|
529
|
+
) -> list[tuple[str, CommandMetadata]]:
|
|
530
|
+
"""Keep registration order while grouping extension commands after their base group."""
|
|
531
|
+
names = {name for name, _meta in commands}
|
|
532
|
+
base_commands: list[tuple[str, CommandMetadata]] = []
|
|
533
|
+
extension_by_base: dict[str, list[tuple[str, CommandMetadata]]] = {}
|
|
534
|
+
orphan_extensions: list[tuple[str, CommandMetadata]] = []
|
|
535
|
+
|
|
536
|
+
for name, meta in commands:
|
|
537
|
+
if "-" not in name:
|
|
538
|
+
base_commands.append((name, meta))
|
|
539
|
+
continue
|
|
540
|
+
base_name = name.split("-", 1)[0]
|
|
541
|
+
if base_name in names:
|
|
542
|
+
extension_by_base.setdefault(base_name, []).append((name, meta))
|
|
543
|
+
else:
|
|
544
|
+
orphan_extensions.append((name, meta))
|
|
545
|
+
|
|
546
|
+
ordered: list[tuple[str, CommandMetadata]] = []
|
|
547
|
+
for name, meta in base_commands:
|
|
548
|
+
ordered.append((name, meta))
|
|
549
|
+
ordered.extend(extension_by_base.get(name, []))
|
|
550
|
+
ordered.extend(orphan_extensions)
|
|
551
|
+
return ordered
|
|
552
|
+
|
|
553
|
+
|
|
554
|
+
for _name, _meta in _grouped_command_order(CommandRegistry.list_commands_for_help()):
|
|
525
555
|
app.add_typer(_make_lazy_typer(_name, _meta.help), name=_name, help=_meta.help)
|
|
526
556
|
|
|
527
557
|
|
|
@@ -85,6 +85,28 @@ class ProjectMetadata(BaseModel):
|
|
|
85
85
|
stability: str = Field("alpha", description="Stability level: alpha | beta | stable")
|
|
86
86
|
breaking_changes: list[dict[str, str]] = Field(default_factory=list, description="Breaking change history")
|
|
87
87
|
version_history: list[dict[str, str]] = Field(default_factory=list, description="Version change log")
|
|
88
|
+
extensions: dict[str, Any] = Field(default_factory=dict, description="Module-scoped metadata extensions")
|
|
89
|
+
|
|
90
|
+
@beartype
|
|
91
|
+
@require(lambda namespace: namespace.strip() != "", "Extension namespace must be non-empty")
|
|
92
|
+
@require(lambda key: key.strip() != "", "Extension key must be non-empty")
|
|
93
|
+
def set_extension(self, namespace: str, key: str, value: Any) -> None:
|
|
94
|
+
"""Set a module-scoped extension value."""
|
|
95
|
+
namespace_data = self.extensions.get(namespace)
|
|
96
|
+
if not isinstance(namespace_data, dict):
|
|
97
|
+
namespace_data = {}
|
|
98
|
+
self.extensions[namespace] = namespace_data
|
|
99
|
+
namespace_data[key] = value
|
|
100
|
+
|
|
101
|
+
@beartype
|
|
102
|
+
@require(lambda namespace: namespace.strip() != "", "Extension namespace must be non-empty")
|
|
103
|
+
@require(lambda key: key.strip() != "", "Extension key must be non-empty")
|
|
104
|
+
def get_extension(self, namespace: str, key: str, default: Any = None) -> Any:
|
|
105
|
+
"""Get a module-scoped extension value."""
|
|
106
|
+
namespace_data = self.extensions.get(namespace)
|
|
107
|
+
if not isinstance(namespace_data, dict):
|
|
108
|
+
return default
|
|
109
|
+
return namespace_data.get(key, default)
|
|
88
110
|
|
|
89
111
|
|
|
90
112
|
class BundleChecksums(BaseModel):
|