specfact-cli 0.35.0__tar.gz → 0.36.0__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.35.0 → specfact_cli-0.36.0}/.gitignore +6 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/PKG-INFO +5 -45
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/README.md +4 -44
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/pyproject.toml +1 -1
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/__init__.py +1 -1
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/__init__.py +1 -1
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/adapters/ado.py +147 -19
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/adapters/backlog_base.py +71 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/adapters/github.py +263 -12
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/backlog/filters.py +2 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/backlog/mappers/github_mapper.py +47 -20
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/modules/analyze/module-package.yaml +1 -1
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/modules/auth/module-package.yaml +1 -1
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/modules/auth/src/commands.py +2 -2
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/modules/backlog/module-package.yaml +1 -1
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/modules/backlog/src/commands.py +708 -8
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/modules/contract/module-package.yaml +1 -1
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/modules/drift/module-package.yaml +1 -1
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/modules/enforce/module-package.yaml +1 -1
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/modules/generate/module-package.yaml +1 -1
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/modules/import_cmd/module-package.yaml +1 -1
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/modules/init/module-package.yaml +1 -1
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/modules/migrate/module-package.yaml +1 -1
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/modules/module_registry/module-package.yaml +1 -1
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/modules/patch_mode/module-package.yaml +1 -1
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/modules/plan/module-package.yaml +1 -1
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/modules/policy_engine/module-package.yaml +1 -1
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/modules/project/module-package.yaml +1 -1
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/modules/repro/module-package.yaml +1 -1
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/modules/sdd/module-package.yaml +1 -1
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/modules/spec/module-package.yaml +1 -1
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/modules/sync/module-package.yaml +1 -1
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/modules/upgrade/module-package.yaml +1 -1
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/modules/validate/module-package.yaml +1 -1
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/registry/module_packages.py +2 -1
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/LICENSE +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/resources/mappings/node-async.yaml +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/resources/mappings/python-async.yaml +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/resources/mappings/speckit-default.yaml +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/resources/prompts/shared/cli-enforcement.md +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/resources/prompts/specfact.01-import.md +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/resources/prompts/specfact.02-plan.md +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/resources/prompts/specfact.03-review.md +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/resources/prompts/specfact.04-sdd.md +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/resources/prompts/specfact.05-enforce.md +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/resources/prompts/specfact.06-sync.md +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/resources/prompts/specfact.07-contracts.md +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/resources/prompts/specfact.backlog-daily.md +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/resources/prompts/specfact.backlog-refine.md +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/resources/prompts/specfact.compare.md +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/resources/prompts/specfact.sync-backlog.md +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/resources/prompts/specfact.validate.md +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/resources/schemas/deviation.schema.json +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/resources/schemas/plan.schema.json +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/resources/schemas/protocol.schema.json +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/resources/templates/backlog/defaults/defect_v1.yaml +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/resources/templates/backlog/defaults/enabler_v1.yaml +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/resources/templates/backlog/defaults/spike_v1.yaml +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/resources/templates/backlog/defaults/user_story_v1.yaml +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/resources/templates/backlog/field_mappings/ado_agile.yaml +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/resources/templates/backlog/field_mappings/ado_default.yaml +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/resources/templates/backlog/field_mappings/ado_kanban.yaml +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/resources/templates/backlog/field_mappings/ado_safe.yaml +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/resources/templates/backlog/field_mappings/ado_scrum.yaml +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/resources/templates/backlog/frameworks/safe/safe_feature_v1.yaml +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/resources/templates/backlog/frameworks/scrum/user_story_v1.yaml +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/resources/templates/backlog/personas/developer/developer_task_v1.yaml +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/resources/templates/backlog/personas/product-owner/user_story_v1.yaml +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/resources/templates/backlog/providers/ado/work_item_v1.yaml +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/resources/templates/github-action.yml.j2 +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/resources/templates/persona/architect.md.j2 +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/resources/templates/persona/developer.md.j2 +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/resources/templates/persona/product-owner.md.j2 +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/resources/templates/plan.bundle.yaml.j2 +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/resources/templates/policies/kanban.yaml +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/resources/templates/policies/mixed.yaml +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/resources/templates/policies/safe.yaml +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/resources/templates/policies/scrum.yaml +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/resources/templates/pr-template.md.j2 +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/resources/templates/protocol.yaml.j2 +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/resources/templates/telemetry.yaml.example +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/__main__.py +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/adapters/__init__.py +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/adapters/base.py +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/adapters/openspec.py +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/adapters/openspec_parser.py +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/adapters/registry.py +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/adapters/speckit.py +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/agents/__init__.py +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/agents/analyze_agent.py +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/agents/base.py +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/agents/plan_agent.py +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/agents/registry.py +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/agents/sync_agent.py +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/analyzers/__init__.py +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/analyzers/ambiguity_scanner.py +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/analyzers/code_analyzer.py +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/analyzers/constitution_evidence_extractor.py +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/analyzers/contract_extractor.py +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/analyzers/control_flow_analyzer.py +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/analyzers/graph_analyzer.py +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/analyzers/relationship_mapper.py +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/analyzers/requirement_extractor.py +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/analyzers/test_pattern_extractor.py +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/backlog/__init__.py +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/backlog/adapters/__init__.py +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/backlog/adapters/base.py +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/backlog/adapters/local_yaml_adapter.py +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/backlog/ai_refiner.py +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/backlog/converter.py +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/backlog/format_detector.py +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/backlog/formats/__init__.py +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/backlog/formats/base.py +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/backlog/formats/markdown_format.py +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/backlog/formats/structured_format.py +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/backlog/mappers/__init__.py +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/backlog/mappers/ado_mapper.py +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/backlog/mappers/base.py +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/backlog/mappers/template_config.py +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/backlog/template_detector.py +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/cli.py +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/commands/__init__.py +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/commands/analyze.py +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/commands/auth.py +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/commands/backlog_commands.py +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/commands/contract_cmd.py +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/commands/drift.py +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/commands/enforce.py +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/commands/generate.py +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/commands/import_cmd.py +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/commands/init.py +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/commands/migrate.py +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/commands/plan.py +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/commands/project_cmd.py +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/commands/repro.py +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/commands/sdd.py +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/commands/spec.py +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/commands/sync.py +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/commands/update.py +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/commands/validate.py +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/common/__init__.py +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/common/logger_setup.py +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/common/logging_utils.py +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/common/text_utils.py +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/common/utils.py +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/comparators/__init__.py +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/comparators/plan_comparator.py +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/contracts/__init__.py +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/contracts/crosshair_props.py +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/contracts/module_interface.py +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/enrichers/constitution_enricher.py +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/enrichers/plan_enricher.py +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/generators/__init__.py +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/generators/contract_generator.py +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/generators/openapi_extractor.py +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/generators/persona_exporter.py +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/generators/plan_generator.py +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/generators/protocol_generator.py +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/generators/report_generator.py +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/generators/task_generator.py +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/generators/test_to_openapi.py +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/generators/workflow_generator.py +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/importers/__init__.py +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/importers/speckit_converter.py +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/importers/speckit_scanner.py +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/integrations/__init__.py +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/integrations/specmatic.py +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/merge/__init__.py +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/merge/resolver.py +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/migrations/__init__.py +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/migrations/plan_migrator.py +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/models/__init__.py +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/models/backlog_item.py +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/models/bridge.py +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/models/capabilities.py +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/models/change.py +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/models/contract.py +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/models/deviation.py +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/models/dor_config.py +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/models/enforcement.py +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/models/module_package.py +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/models/persona_template.py +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/models/plan.py +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/models/project.py +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/models/protocol.py +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/models/quality.py +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/models/sdd.py +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/models/source_tracking.py +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/models/task.py +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/models/validation.py +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/modes/__init__.py +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/modes/detector.py +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/modes/router.py +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/modules/__init__.py +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/modules/analyze/src/__init__.py +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/modules/analyze/src/app.py +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/modules/analyze/src/commands.py +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/modules/auth/src/__init__.py +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/modules/auth/src/app.py +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/modules/backlog/src/__init__.py +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/modules/backlog/src/adapters/__init__.py +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/modules/backlog/src/adapters/ado.py +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/modules/backlog/src/adapters/base.py +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/modules/backlog/src/adapters/github.py +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/modules/backlog/src/adapters/jira.py +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/modules/backlog/src/adapters/linear.py +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/modules/backlog/src/app.py +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/modules/contract/src/__init__.py +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/modules/contract/src/app.py +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/modules/contract/src/commands.py +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/modules/drift/src/__init__.py +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/modules/drift/src/app.py +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/modules/drift/src/commands.py +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/modules/enforce/src/__init__.py +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/modules/enforce/src/app.py +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/modules/enforce/src/commands.py +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/modules/generate/src/__init__.py +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/modules/generate/src/app.py +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/modules/generate/src/commands.py +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/modules/import_cmd/src/__init__.py +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/modules/import_cmd/src/app.py +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/modules/import_cmd/src/commands.py +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/modules/init/src/__init__.py +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/modules/init/src/app.py +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/modules/init/src/commands.py +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/modules/migrate/src/__init__.py +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/modules/migrate/src/app.py +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/modules/migrate/src/commands.py +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/modules/module_io_shim.py +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/modules/module_registry/src/__init__.py +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/modules/module_registry/src/app.py +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/modules/module_registry/src/commands.py +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/modules/patch_mode/src/app.py +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/modules/patch_mode/src/commands.py +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/modules/patch_mode/src/patch_mode/__init__.py +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/modules/patch_mode/src/patch_mode/commands/__init__.py +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/modules/patch_mode/src/patch_mode/commands/apply.py +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/modules/patch_mode/src/patch_mode/pipeline/__init__.py +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/modules/patch_mode/src/patch_mode/pipeline/applier.py +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/modules/patch_mode/src/patch_mode/pipeline/generator.py +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/modules/patch_mode/src/patch_mode/pipeline/idempotency.py +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/modules/plan/src/__init__.py +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/modules/plan/src/app.py +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/modules/plan/src/commands.py +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/modules/policy_engine/src/app.py +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/modules/policy_engine/src/commands.py +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/modules/policy_engine/src/policy_engine/__init__.py +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/modules/policy_engine/src/policy_engine/config/__init__.py +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/modules/policy_engine/src/policy_engine/config/policy_config.py +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/modules/policy_engine/src/policy_engine/config/templates.py +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/modules/policy_engine/src/policy_engine/engine/__init__.py +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/modules/policy_engine/src/policy_engine/engine/suggester.py +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/modules/policy_engine/src/policy_engine/engine/validator.py +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/modules/policy_engine/src/policy_engine/main.py +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/modules/policy_engine/src/policy_engine/models/__init__.py +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/modules/policy_engine/src/policy_engine/models/policy_result.py +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/modules/policy_engine/src/policy_engine/policies/__init__.py +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/modules/policy_engine/src/policy_engine/policies/kanban.py +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/modules/policy_engine/src/policy_engine/policies/safe.py +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/modules/policy_engine/src/policy_engine/policies/scrum.py +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/modules/policy_engine/src/policy_engine/registry/__init__.py +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/modules/policy_engine/src/policy_engine/registry/policy_registry.py +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/modules/project/src/__init__.py +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/modules/project/src/app.py +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/modules/project/src/commands.py +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/modules/repro/src/__init__.py +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/modules/repro/src/app.py +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/modules/repro/src/commands.py +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/modules/sdd/src/__init__.py +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/modules/sdd/src/app.py +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/modules/sdd/src/commands.py +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/modules/spec/src/__init__.py +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/modules/spec/src/app.py +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/modules/spec/src/commands.py +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/modules/sync/src/__init__.py +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/modules/sync/src/app.py +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/modules/sync/src/commands.py +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/modules/upgrade/src/__init__.py +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/modules/upgrade/src/app.py +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/modules/upgrade/src/commands.py +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/modules/validate/src/__init__.py +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/modules/validate/src/app.py +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/modules/validate/src/commands.py +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/parsers/__init__.py +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/parsers/persona_importer.py +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/registry/__init__.py +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/registry/bootstrap.py +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/registry/bridge_registry.py +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/registry/crypto_validator.py +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/registry/extension_registry.py +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/registry/help_cache.py +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/registry/marketplace_client.py +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/registry/metadata.py +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/registry/module_discovery.py +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/registry/module_installer.py +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/registry/module_lifecycle.py +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/registry/module_state.py +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/registry/registry.py +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/resources/semgrep/async.yml +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/resources/semgrep/code-quality.yml +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/resources/semgrep/feature-detection.yml +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/runtime.py +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/sync/__init__.py +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/sync/bridge_probe.py +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/sync/bridge_sync.py +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/sync/bridge_watch.py +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/sync/change_detector.py +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/sync/code_to_spec.py +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/sync/drift_detector.py +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/sync/repository_sync.py +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/sync/spec_to_code.py +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/sync/spec_to_tests.py +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/sync/watcher.py +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/sync/watcher_enhanced.py +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/telemetry.py +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/templates/__init__.py +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/templates/bridge_templates.py +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/templates/defaults/defect_v1.yaml +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/templates/defaults/enabler_v1.yaml +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/templates/defaults/spike_v1.yaml +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/templates/defaults/user_story_v1.yaml +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/templates/frameworks/scrum/user_story_v1.yaml +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/templates/personas/product-owner/user_story_v1.yaml +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/templates/providers/ado/work_item_v1.yaml +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/templates/registry.py +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/templates/specification_templates.py +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/utils/__init__.py +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/utils/acceptance_criteria.py +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/utils/auth_tokens.py +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/utils/bundle_converters.py +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/utils/bundle_loader.py +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/utils/code_change_detector.py +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/utils/console.py +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/utils/content_sanitizer.py +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/utils/context_detection.py +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/utils/enrichment_context.py +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/utils/enrichment_parser.py +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/utils/env_manager.py +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/utils/feature_keys.py +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/utils/git.py +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/utils/github_annotations.py +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/utils/ide_setup.py +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/utils/incremental_check.py +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/utils/metadata.py +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/utils/optional_deps.py +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/utils/performance.py +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/utils/persona_ownership.py +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/utils/progress.py +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/utils/progressive_disclosure.py +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/utils/prompts.py +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/utils/sdd_discovery.py +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/utils/source_scanner.py +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/utils/startup_checks.py +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/utils/structure.py +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/utils/structured_io.py +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/utils/suggestions.py +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/utils/terminal.py +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/utils/yaml_utils.py +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/validators/__init__.py +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/validators/agile_validation.py +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/validators/change_proposal_integration.py +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/validators/cli_first_validator.py +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/validators/contract_validator.py +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/validators/fsm.py +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/validators/repro_checker.py +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/validators/schema.py +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/validators/sidecar/__init__.py +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/validators/sidecar/contract_populator.py +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/validators/sidecar/crosshair_runner.py +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/validators/sidecar/crosshair_summary.py +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/validators/sidecar/dependency_installer.py +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/validators/sidecar/framework_detector.py +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/validators/sidecar/frameworks/__init__.py +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/validators/sidecar/frameworks/base.py +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/validators/sidecar/frameworks/django.py +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/validators/sidecar/frameworks/drf.py +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/validators/sidecar/frameworks/fastapi.py +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/validators/sidecar/frameworks/flask.py +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/validators/sidecar/harness_generator.py +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/validators/sidecar/models.py +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/validators/sidecar/orchestrator.py +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/validators/sidecar/specmatic_runner.py +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/validators/sidecar/unannotated_detector.py +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/versioning/__init__.py +0 -0
- {specfact_cli-0.35.0 → specfact_cli-0.36.0}/src/specfact_cli/versioning/analyzer.py +0 -0
|
@@ -111,6 +111,12 @@ docs/internal/
|
|
|
111
111
|
.claude/skills/openspec-*/
|
|
112
112
|
!.claude/skills/openspec-workflows/
|
|
113
113
|
|
|
114
|
+
.codex/skills/openspec-*/
|
|
115
|
+
!.codex/skills/openspec-workflows/
|
|
116
|
+
|
|
117
|
+
.vibe/skills/openspec-*/
|
|
118
|
+
!.vibe/skills/openspec-workflows/
|
|
119
|
+
|
|
114
120
|
# Semgrep rules (generated from tools/semgrep/ - source rules are versioned)
|
|
115
121
|
.semgrep/
|
|
116
122
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: specfact-cli
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.36.0
|
|
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
|
|
@@ -431,51 +431,11 @@ Start with:
|
|
|
431
431
|
- **Backlogs**: GitHub Issues, Azure DevOps, Jira, Linear
|
|
432
432
|
- **Contracts**: Specmatic, OpenAPI
|
|
433
433
|
|
|
434
|
-
|
|
434
|
+
For technical architecture details (module lifecycle, registry internals, adapters, and implementation status), use:
|
|
435
435
|
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
-
|
|
439
|
-
- `specfact init ide` handles IDE prompt/template sync and IDE settings updates.
|
|
440
|
-
- `specfact module` is the canonical lifecycle surface:
|
|
441
|
-
- `specfact module install <namespace/name>` installs marketplace modules into `~/.specfact/marketplace-modules/`.
|
|
442
|
-
- `specfact module list [--source builtin|marketplace|custom]` shows multi-source discovery state.
|
|
443
|
-
- `specfact module enable <id>` / `specfact module disable <id> [--force]` manage enabled state.
|
|
444
|
-
- `specfact module uninstall <name>` and `specfact module upgrade <name>` manage marketplace lifecycle.
|
|
445
|
-
- `specfact init --list-modules`, `--enable-module`, and `--disable-module` remain supported as compatibility aliases during migration.
|
|
446
|
-
- Module lifecycle operations keep dependency-aware safety checks with `--force` cascading behavior.
|
|
447
|
-
- Module manifests support dependency and core-version compatibility enforcement at registration time.
|
|
448
|
-
|
|
449
|
-
This lifecycle model is the baseline for future granular module updates and enhancements. Module installation from third-party or open-source community providers is planned, but not implemented yet.
|
|
450
|
-
|
|
451
|
-
Contract-first module architecture highlights:
|
|
452
|
-
|
|
453
|
-
- `ModuleIOContract` formalizes module IO operations (`import`, `export`, `sync`, `validate`) on `ProjectBundle`.
|
|
454
|
-
- Core-module isolation is enforced by static analysis (`core` never imports `specfact_cli.modules.*` directly).
|
|
455
|
-
- Registration tracks protocol operation coverage and schema compatibility metadata.
|
|
456
|
-
- Bridge registry support allows module manifests to declare `service_bridges` converters (for example ADO/Jira/Linear/GitHub) loaded at lifecycle startup without direct core-to-module imports.
|
|
457
|
-
- Protocol reporting classifies modules from effective runtime interfaces with a single aggregate summary (`Full/Partial/Legacy`).
|
|
458
|
-
- Module manifests support publisher and integrity metadata (arch-06) with optional checksum and signature verification at registration time.
|
|
459
|
-
|
|
460
|
-
Why this matters:
|
|
461
|
-
|
|
462
|
-
- Feature areas can evolve independently without repeatedly modifying core CLI wiring.
|
|
463
|
-
- Module teams can ship at different speeds while preserving stable core behavior.
|
|
464
|
-
- Clear IO contracts reduce coupling and make future migrations (e.g., new adapters/modules) lower risk.
|
|
465
|
-
- Core remains focused on lifecycle, registry, and validation orchestration rather than tool-specific command logic.
|
|
466
|
-
|
|
467
|
-
---
|
|
468
|
-
|
|
469
|
-
## Developer Note: Command Layout
|
|
470
|
-
|
|
471
|
-
- Primary command implementations live in `src/specfact_cli/modules/<module>/src/commands.py`.
|
|
472
|
-
- Legacy imports from `src/specfact_cli/commands/*.py` are compatibility shims and only guarantee `app` re-exports.
|
|
473
|
-
- Preferred imports for module code:
|
|
474
|
-
- `from specfact_cli.modules.<module>.src.commands import app`
|
|
475
|
-
- `from specfact_cli.modules.<module>.src.commands import <symbol>`
|
|
476
|
-
- Shim deprecation timeline:
|
|
477
|
-
- Legacy shim usage is deprecated for non-`app` symbols now.
|
|
478
|
-
- Shim removal is planned no earlier than `v0.30` (or the next major migration window).
|
|
436
|
+
- [Architecture Reference](docs/reference/architecture.md)
|
|
437
|
+
- [Architecture Docs Index](docs/architecture/README.md)
|
|
438
|
+
- [Architecture Implementation Status](docs/architecture/implementation-status.md)
|
|
479
439
|
|
|
480
440
|
---
|
|
481
441
|
|
|
@@ -154,51 +154,11 @@ Start with:
|
|
|
154
154
|
- **Backlogs**: GitHub Issues, Azure DevOps, Jira, Linear
|
|
155
155
|
- **Contracts**: Specmatic, OpenAPI
|
|
156
156
|
|
|
157
|
-
|
|
157
|
+
For technical architecture details (module lifecycle, registry internals, adapters, and implementation status), use:
|
|
158
158
|
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
-
|
|
162
|
-
- `specfact init ide` handles IDE prompt/template sync and IDE settings updates.
|
|
163
|
-
- `specfact module` is the canonical lifecycle surface:
|
|
164
|
-
- `specfact module install <namespace/name>` installs marketplace modules into `~/.specfact/marketplace-modules/`.
|
|
165
|
-
- `specfact module list [--source builtin|marketplace|custom]` shows multi-source discovery state.
|
|
166
|
-
- `specfact module enable <id>` / `specfact module disable <id> [--force]` manage enabled state.
|
|
167
|
-
- `specfact module uninstall <name>` and `specfact module upgrade <name>` manage marketplace lifecycle.
|
|
168
|
-
- `specfact init --list-modules`, `--enable-module`, and `--disable-module` remain supported as compatibility aliases during migration.
|
|
169
|
-
- Module lifecycle operations keep dependency-aware safety checks with `--force` cascading behavior.
|
|
170
|
-
- Module manifests support dependency and core-version compatibility enforcement at registration time.
|
|
171
|
-
|
|
172
|
-
This lifecycle model is the baseline for future granular module updates and enhancements. Module installation from third-party or open-source community providers is planned, but not implemented yet.
|
|
173
|
-
|
|
174
|
-
Contract-first module architecture highlights:
|
|
175
|
-
|
|
176
|
-
- `ModuleIOContract` formalizes module IO operations (`import`, `export`, `sync`, `validate`) on `ProjectBundle`.
|
|
177
|
-
- Core-module isolation is enforced by static analysis (`core` never imports `specfact_cli.modules.*` directly).
|
|
178
|
-
- Registration tracks protocol operation coverage and schema compatibility metadata.
|
|
179
|
-
- Bridge registry support allows module manifests to declare `service_bridges` converters (for example ADO/Jira/Linear/GitHub) loaded at lifecycle startup without direct core-to-module imports.
|
|
180
|
-
- Protocol reporting classifies modules from effective runtime interfaces with a single aggregate summary (`Full/Partial/Legacy`).
|
|
181
|
-
- Module manifests support publisher and integrity metadata (arch-06) with optional checksum and signature verification at registration time.
|
|
182
|
-
|
|
183
|
-
Why this matters:
|
|
184
|
-
|
|
185
|
-
- Feature areas can evolve independently without repeatedly modifying core CLI wiring.
|
|
186
|
-
- Module teams can ship at different speeds while preserving stable core behavior.
|
|
187
|
-
- Clear IO contracts reduce coupling and make future migrations (e.g., new adapters/modules) lower risk.
|
|
188
|
-
- Core remains focused on lifecycle, registry, and validation orchestration rather than tool-specific command logic.
|
|
189
|
-
|
|
190
|
-
---
|
|
191
|
-
|
|
192
|
-
## Developer Note: Command Layout
|
|
193
|
-
|
|
194
|
-
- Primary command implementations live in `src/specfact_cli/modules/<module>/src/commands.py`.
|
|
195
|
-
- Legacy imports from `src/specfact_cli/commands/*.py` are compatibility shims and only guarantee `app` re-exports.
|
|
196
|
-
- Preferred imports for module code:
|
|
197
|
-
- `from specfact_cli.modules.<module>.src.commands import app`
|
|
198
|
-
- `from specfact_cli.modules.<module>.src.commands import <symbol>`
|
|
199
|
-
- Shim deprecation timeline:
|
|
200
|
-
- Legacy shim usage is deprecated for non-`app` symbols now.
|
|
201
|
-
- Shim removal is planned no earlier than `v0.30` (or the next major migration window).
|
|
159
|
+
- [Architecture Reference](docs/reference/architecture.md)
|
|
160
|
+
- [Architecture Docs Index](docs/architecture/README.md)
|
|
161
|
+
- [Architecture Implementation Status](docs/architecture/implementation-status.md)
|
|
202
162
|
|
|
203
163
|
---
|
|
204
164
|
|
|
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "specfact-cli"
|
|
7
|
-
version = "0.
|
|
7
|
+
version = "0.36.0"
|
|
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"
|
|
@@ -1657,7 +1657,10 @@ class AdoAdapter(BridgeAdapter, BacklogAdapterMixin, BacklogAdapter):
|
|
|
1657
1657
|
]
|
|
1658
1658
|
|
|
1659
1659
|
try:
|
|
1660
|
-
response =
|
|
1660
|
+
response = self._request_with_retry(
|
|
1661
|
+
lambda: requests.patch(url, json=patch_document, headers=headers, timeout=30),
|
|
1662
|
+
retry_on_ambiguous_transport=False,
|
|
1663
|
+
)
|
|
1661
1664
|
if is_debug_mode():
|
|
1662
1665
|
debug_log_operation(
|
|
1663
1666
|
"ado_patch",
|
|
@@ -1800,8 +1803,9 @@ class AdoAdapter(BridgeAdapter, BacklogAdapterMixin, BacklogAdapter):
|
|
|
1800
1803
|
patch_document = [{"op": "replace", "path": "/fields/System.State", "value": ado_state}]
|
|
1801
1804
|
|
|
1802
1805
|
try:
|
|
1803
|
-
response =
|
|
1804
|
-
|
|
1806
|
+
response = self._request_with_retry(
|
|
1807
|
+
lambda: requests.patch(url, json=patch_document, headers=headers, timeout=30)
|
|
1808
|
+
)
|
|
1805
1809
|
work_item_data = response.json()
|
|
1806
1810
|
|
|
1807
1811
|
work_item_url = work_item_data.get("_links", {}).get("html", {}).get("href", "")
|
|
@@ -1933,8 +1937,9 @@ class AdoAdapter(BridgeAdapter, BacklogAdapterMixin, BacklogAdapter):
|
|
|
1933
1937
|
]
|
|
1934
1938
|
|
|
1935
1939
|
try:
|
|
1936
|
-
response =
|
|
1937
|
-
|
|
1940
|
+
response = self._request_with_retry(
|
|
1941
|
+
lambda: requests.patch(url, json=patch_document, headers=headers, timeout=30)
|
|
1942
|
+
)
|
|
1938
1943
|
work_item_data = response.json()
|
|
1939
1944
|
|
|
1940
1945
|
work_item_url = work_item_data.get("_links", {}).get("html", {}).get("href", "")
|
|
@@ -2040,8 +2045,9 @@ class AdoAdapter(BridgeAdapter, BacklogAdapterMixin, BacklogAdapter):
|
|
|
2040
2045
|
patch_document = [{"op": "replace", "path": "/fields/System.State", "value": ado_state}]
|
|
2041
2046
|
|
|
2042
2047
|
try:
|
|
2043
|
-
response =
|
|
2044
|
-
|
|
2048
|
+
response = self._request_with_retry(
|
|
2049
|
+
lambda: requests.patch(url, json=patch_document, headers=headers, timeout=30)
|
|
2050
|
+
)
|
|
2045
2051
|
work_item_data = response.json()
|
|
2046
2052
|
|
|
2047
2053
|
work_item_url = work_item_data.get("_links", {}).get("html", {}).get("href", "")
|
|
@@ -2442,8 +2448,10 @@ class AdoAdapter(BridgeAdapter, BacklogAdapterMixin, BacklogAdapter):
|
|
|
2442
2448
|
comment_body = {"text": comment_text}
|
|
2443
2449
|
|
|
2444
2450
|
try:
|
|
2445
|
-
response =
|
|
2446
|
-
|
|
2451
|
+
response = self._request_with_retry(
|
|
2452
|
+
lambda: requests.post(url, json=comment_body, headers=headers, timeout=30),
|
|
2453
|
+
retry_on_ambiguous_transport=False,
|
|
2454
|
+
)
|
|
2447
2455
|
comment_data = response.json()
|
|
2448
2456
|
|
|
2449
2457
|
comment_id = comment_data.get("id")
|
|
@@ -2690,6 +2698,7 @@ class AdoAdapter(BridgeAdapter, BacklogAdapterMixin, BacklogAdapter):
|
|
|
2690
2698
|
self,
|
|
2691
2699
|
sprint_filter: str | None,
|
|
2692
2700
|
items: list[BacklogItem],
|
|
2701
|
+
apply_current_when_missing: bool = True,
|
|
2693
2702
|
) -> tuple[str | None, list[BacklogItem]]:
|
|
2694
2703
|
"""
|
|
2695
2704
|
Resolve sprint filter with path matching and ambiguity detection.
|
|
@@ -2705,6 +2714,8 @@ class AdoAdapter(BridgeAdapter, BacklogAdapterMixin, BacklogAdapter):
|
|
|
2705
2714
|
ValueError: If ambiguous sprint name match is detected
|
|
2706
2715
|
"""
|
|
2707
2716
|
if not sprint_filter:
|
|
2717
|
+
if not apply_current_when_missing:
|
|
2718
|
+
return None, items
|
|
2708
2719
|
# No sprint filter - try to get current iteration
|
|
2709
2720
|
current_iteration = self._get_current_iteration()
|
|
2710
2721
|
if current_iteration:
|
|
@@ -2859,13 +2870,16 @@ class AdoAdapter(BridgeAdapter, BacklogAdapterMixin, BacklogAdapter):
|
|
|
2859
2870
|
# Sprint will be resolved post-fetch to handle ambiguity
|
|
2860
2871
|
pass
|
|
2861
2872
|
else:
|
|
2862
|
-
# No sprint/iteration -
|
|
2863
|
-
|
|
2864
|
-
|
|
2865
|
-
|
|
2866
|
-
|
|
2867
|
-
|
|
2868
|
-
|
|
2873
|
+
# No sprint/iteration - optionally use current iteration default
|
|
2874
|
+
if getattr(filters, "use_current_iteration_default", True):
|
|
2875
|
+
current_iteration = self._get_current_iteration()
|
|
2876
|
+
if current_iteration:
|
|
2877
|
+
resolved_iteration = current_iteration
|
|
2878
|
+
conditions.append(f"[System.IterationPath] = '{resolved_iteration}'")
|
|
2879
|
+
else:
|
|
2880
|
+
console.print(
|
|
2881
|
+
"[yellow]⚠ No current iteration found and no sprint/iteration filter provided[/yellow]"
|
|
2882
|
+
)
|
|
2869
2883
|
|
|
2870
2884
|
if conditions:
|
|
2871
2885
|
wiql_parts.append("AND " + " AND ".join(conditions))
|
|
@@ -3113,7 +3127,11 @@ class AdoAdapter(BridgeAdapter, BacklogAdapterMixin, BacklogAdapter):
|
|
|
3113
3127
|
# Sprint filtering with path matching and ambiguity detection
|
|
3114
3128
|
if filters.sprint:
|
|
3115
3129
|
try:
|
|
3116
|
-
_, filtered_items = self._resolve_sprint_filter(
|
|
3130
|
+
_, filtered_items = self._resolve_sprint_filter(
|
|
3131
|
+
filters.sprint,
|
|
3132
|
+
filtered_items,
|
|
3133
|
+
apply_current_when_missing=getattr(filters, "use_current_iteration_default", True),
|
|
3134
|
+
)
|
|
3117
3135
|
except ValueError as e:
|
|
3118
3136
|
# Ambiguous sprint match - raise with clear error message
|
|
3119
3137
|
console.print(f"[red]Error:[/red] {e}")
|
|
@@ -3137,6 +3155,115 @@ class AdoAdapter(BridgeAdapter, BacklogAdapterMixin, BacklogAdapter):
|
|
|
3137
3155
|
|
|
3138
3156
|
return filtered_items
|
|
3139
3157
|
|
|
3158
|
+
@beartype
|
|
3159
|
+
@require(
|
|
3160
|
+
lambda project_id: isinstance(project_id, str) and len(project_id.strip()) > 0, "project_id must be non-empty"
|
|
3161
|
+
)
|
|
3162
|
+
@require(lambda payload: isinstance(payload, dict), "payload must be dict")
|
|
3163
|
+
@ensure(lambda result: isinstance(result, dict), "Must return dict")
|
|
3164
|
+
def create_issue(self, project_id: str, payload: dict[str, Any]) -> dict[str, Any]:
|
|
3165
|
+
"""Create an Azure DevOps work item from provider-agnostic backlog payload."""
|
|
3166
|
+
org, project = self._resolve_graph_project_context(project_id)
|
|
3167
|
+
if not self.api_token:
|
|
3168
|
+
raise ValueError("Azure DevOps API token is required")
|
|
3169
|
+
|
|
3170
|
+
title = str(payload.get("title") or "").strip()
|
|
3171
|
+
if not title:
|
|
3172
|
+
raise ValueError("payload.title is required")
|
|
3173
|
+
|
|
3174
|
+
raw_type = str(payload.get("type") or "task").strip().lower()
|
|
3175
|
+
type_mapping = {
|
|
3176
|
+
"epic": "Epic",
|
|
3177
|
+
"feature": "Feature",
|
|
3178
|
+
"story": "User Story",
|
|
3179
|
+
"user story": "User Story",
|
|
3180
|
+
"task": "Task",
|
|
3181
|
+
"bug": "Bug",
|
|
3182
|
+
"spike": "Task",
|
|
3183
|
+
}
|
|
3184
|
+
work_item_type = type_mapping.get(raw_type, "Task")
|
|
3185
|
+
|
|
3186
|
+
description = str(payload.get("description") or payload.get("body") or "").strip()
|
|
3187
|
+
description_format = str(payload.get("description_format") or "markdown").strip().lower()
|
|
3188
|
+
field_rendering_format = "Markdown" if description_format != "classic" else "Html"
|
|
3189
|
+
patch_document: list[dict[str, Any]] = [
|
|
3190
|
+
{"op": "add", "path": "/fields/System.Title", "value": title},
|
|
3191
|
+
{"op": "add", "path": "/fields/System.Description", "value": description},
|
|
3192
|
+
{"op": "add", "path": "/multilineFieldsFormat/System.Description", "value": field_rendering_format},
|
|
3193
|
+
]
|
|
3194
|
+
|
|
3195
|
+
acceptance_criteria = str(payload.get("acceptance_criteria") or "").strip()
|
|
3196
|
+
if acceptance_criteria:
|
|
3197
|
+
patch_document.append(
|
|
3198
|
+
{
|
|
3199
|
+
"op": "add",
|
|
3200
|
+
"path": "/fields/Microsoft.VSTS.Common.AcceptanceCriteria",
|
|
3201
|
+
"value": acceptance_criteria,
|
|
3202
|
+
}
|
|
3203
|
+
)
|
|
3204
|
+
|
|
3205
|
+
priority = payload.get("priority")
|
|
3206
|
+
if priority not in (None, ""):
|
|
3207
|
+
patch_document.append(
|
|
3208
|
+
{
|
|
3209
|
+
"op": "add",
|
|
3210
|
+
"path": "/fields/Microsoft.VSTS.Common.Priority",
|
|
3211
|
+
"value": priority,
|
|
3212
|
+
}
|
|
3213
|
+
)
|
|
3214
|
+
|
|
3215
|
+
story_points = payload.get("story_points")
|
|
3216
|
+
if story_points is not None:
|
|
3217
|
+
patch_document.append(
|
|
3218
|
+
{
|
|
3219
|
+
"op": "add",
|
|
3220
|
+
"path": "/fields/Microsoft.VSTS.Scheduling.StoryPoints",
|
|
3221
|
+
"value": story_points,
|
|
3222
|
+
}
|
|
3223
|
+
)
|
|
3224
|
+
|
|
3225
|
+
sprint = str(payload.get("sprint") or "").strip()
|
|
3226
|
+
if sprint:
|
|
3227
|
+
patch_document.append(
|
|
3228
|
+
{
|
|
3229
|
+
"op": "add",
|
|
3230
|
+
"path": "/fields/System.IterationPath",
|
|
3231
|
+
"value": sprint,
|
|
3232
|
+
}
|
|
3233
|
+
)
|
|
3234
|
+
|
|
3235
|
+
parent_id = str(payload.get("parent_id") or "").strip()
|
|
3236
|
+
if parent_id:
|
|
3237
|
+
parent_url = f"{self.base_url}/{org}/{project}/_apis/wit/workItems/{parent_id}"
|
|
3238
|
+
patch_document.append(
|
|
3239
|
+
{
|
|
3240
|
+
"op": "add",
|
|
3241
|
+
"path": "/relations/-",
|
|
3242
|
+
"value": {"rel": "System.LinkTypes.Hierarchy-Reverse", "url": parent_url},
|
|
3243
|
+
}
|
|
3244
|
+
)
|
|
3245
|
+
|
|
3246
|
+
url = f"{self.base_url}/{org}/{project}/_apis/wit/workitems/${work_item_type}?api-version=7.1"
|
|
3247
|
+
headers = {
|
|
3248
|
+
"Content-Type": "application/json-patch+json",
|
|
3249
|
+
**self._auth_headers(),
|
|
3250
|
+
}
|
|
3251
|
+
response = self._request_with_retry(
|
|
3252
|
+
lambda: requests.patch(url, json=patch_document, headers=headers, timeout=30),
|
|
3253
|
+
retry_on_ambiguous_transport=False,
|
|
3254
|
+
)
|
|
3255
|
+
created = response.json()
|
|
3256
|
+
|
|
3257
|
+
created_id = str(created.get("id") or "")
|
|
3258
|
+
html_url = str(created.get("_links", {}).get("html", {}).get("href") or "")
|
|
3259
|
+
fallback_url = str(created.get("url") or "")
|
|
3260
|
+
|
|
3261
|
+
return {
|
|
3262
|
+
"id": created_id,
|
|
3263
|
+
"key": created_id,
|
|
3264
|
+
"url": html_url or fallback_url,
|
|
3265
|
+
}
|
|
3266
|
+
|
|
3140
3267
|
@beartype
|
|
3141
3268
|
@require(lambda project_id: isinstance(project_id, str) and len(project_id) > 0, "project_id must be non-empty")
|
|
3142
3269
|
@ensure(lambda result: isinstance(result, list), "Must return list")
|
|
@@ -3429,8 +3556,9 @@ class AdoAdapter(BridgeAdapter, BacklogAdapterMixin, BacklogAdapter):
|
|
|
3429
3556
|
|
|
3430
3557
|
# Update work item
|
|
3431
3558
|
try:
|
|
3432
|
-
response =
|
|
3433
|
-
|
|
3559
|
+
response = self._request_with_retry(
|
|
3560
|
+
lambda: requests.patch(url, headers=headers, json=operations, timeout=30)
|
|
3561
|
+
)
|
|
3434
3562
|
except requests.HTTPError as e:
|
|
3435
3563
|
user_msg = _log_ado_patch_failure(e.response, operations, url)
|
|
3436
3564
|
e.ado_user_message = user_msg
|
|
@@ -11,10 +11,12 @@ functionality for status mapping, metadata extraction, and conflict resolution.
|
|
|
11
11
|
|
|
12
12
|
from __future__ import annotations
|
|
13
13
|
|
|
14
|
+
import time
|
|
14
15
|
from abc import ABC, abstractmethod
|
|
15
16
|
from datetime import UTC, datetime
|
|
16
17
|
from typing import Any
|
|
17
18
|
|
|
19
|
+
import requests
|
|
18
20
|
from beartype import beartype
|
|
19
21
|
from icontract import ensure, require
|
|
20
22
|
|
|
@@ -35,6 +37,10 @@ class BacklogAdapterMixin(ABC):
|
|
|
35
37
|
and implement the abstract methods to provide tool-specific implementations.
|
|
36
38
|
"""
|
|
37
39
|
|
|
40
|
+
RETRYABLE_HTTP_STATUSES: tuple[int, ...] = (429, 500, 502, 503, 504)
|
|
41
|
+
RETRY_DEFAULT_ATTEMPTS: int = 3
|
|
42
|
+
RETRY_BACKOFF_SECONDS: float = 0.5
|
|
43
|
+
|
|
38
44
|
@abstractmethod
|
|
39
45
|
@beartype
|
|
40
46
|
@require(lambda status: isinstance(status, str) and len(status) > 0, "Status must be non-empty string")
|
|
@@ -140,6 +146,71 @@ class BacklogAdapterMixin(ABC):
|
|
|
140
146
|
|
|
141
147
|
return target_state
|
|
142
148
|
|
|
149
|
+
@beartype
|
|
150
|
+
@require(lambda attempts: attempts is None or attempts > 0, "attempts must be > 0 when provided")
|
|
151
|
+
@require(
|
|
152
|
+
lambda backoff_seconds: backoff_seconds is None or backoff_seconds >= 0,
|
|
153
|
+
"backoff_seconds must be >= 0 when provided",
|
|
154
|
+
)
|
|
155
|
+
@require(
|
|
156
|
+
lambda retry_on_ambiguous_transport: isinstance(retry_on_ambiguous_transport, bool), "retry flag must be bool"
|
|
157
|
+
)
|
|
158
|
+
@ensure(lambda result: hasattr(result, "raise_for_status"), "Result must support raise_for_status")
|
|
159
|
+
def _request_with_retry(
|
|
160
|
+
self,
|
|
161
|
+
request_callable: Any,
|
|
162
|
+
*,
|
|
163
|
+
attempts: int | None = None,
|
|
164
|
+
backoff_seconds: float | None = None,
|
|
165
|
+
retry_on_ambiguous_transport: bool = True,
|
|
166
|
+
) -> Any:
|
|
167
|
+
"""Execute HTTP request with central retry policy for transient failures.
|
|
168
|
+
|
|
169
|
+
For non-idempotent writes, callers can disable transport-error replay by passing
|
|
170
|
+
retry_on_ambiguous_transport=False to avoid accidental duplicate side effects.
|
|
171
|
+
"""
|
|
172
|
+
max_attempts = attempts or self.RETRY_DEFAULT_ATTEMPTS
|
|
173
|
+
delay = backoff_seconds if backoff_seconds is not None else self.RETRY_BACKOFF_SECONDS
|
|
174
|
+
|
|
175
|
+
last_error: Exception | None = None
|
|
176
|
+
for attempt in range(1, max_attempts + 1):
|
|
177
|
+
try:
|
|
178
|
+
response = request_callable()
|
|
179
|
+
status_code = int(getattr(response, "status_code", 0) or 0)
|
|
180
|
+
if status_code in self.RETRYABLE_HTTP_STATUSES and attempt < max_attempts:
|
|
181
|
+
time.sleep(delay * (2 ** (attempt - 1)))
|
|
182
|
+
continue
|
|
183
|
+
response.raise_for_status()
|
|
184
|
+
return response
|
|
185
|
+
except requests.HTTPError as error:
|
|
186
|
+
status_code = int(getattr(error.response, "status_code", 0) or 0)
|
|
187
|
+
is_transient = status_code in self.RETRYABLE_HTTP_STATUSES
|
|
188
|
+
last_error = error
|
|
189
|
+
if is_transient and attempt < max_attempts:
|
|
190
|
+
time.sleep(delay * (2 ** (attempt - 1)))
|
|
191
|
+
continue
|
|
192
|
+
raise
|
|
193
|
+
except (requests.Timeout, requests.ConnectionError) as error:
|
|
194
|
+
last_error = error
|
|
195
|
+
if retry_on_ambiguous_transport and attempt < max_attempts:
|
|
196
|
+
time.sleep(delay * (2 ** (attempt - 1)))
|
|
197
|
+
continue
|
|
198
|
+
raise
|
|
199
|
+
|
|
200
|
+
if last_error is not None:
|
|
201
|
+
raise last_error
|
|
202
|
+
raise RuntimeError("Retry logic failed without response or error")
|
|
203
|
+
|
|
204
|
+
@abstractmethod
|
|
205
|
+
@beartype
|
|
206
|
+
@require(
|
|
207
|
+
lambda project_id: isinstance(project_id, str) and len(project_id.strip()) > 0, "Project ID must be non-empty"
|
|
208
|
+
)
|
|
209
|
+
@require(lambda payload: isinstance(payload, dict), "Payload must be dict")
|
|
210
|
+
@ensure(lambda result: isinstance(result, dict), "Must return created issue metadata dict")
|
|
211
|
+
def create_issue(self, project_id: str, payload: dict[str, Any]) -> dict[str, Any]:
|
|
212
|
+
"""Create backlog issue/work item from provider-agnostic payload."""
|
|
213
|
+
|
|
143
214
|
@abstractmethod
|
|
144
215
|
@beartype
|
|
145
216
|
@require(lambda item_data: isinstance(item_data, dict), "Item data must be dict")
|