specfact-cli 0.30.2__tar.gz → 0.30.3__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/PKG-INFO +1 -1
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/pyproject.toml +1 -1
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/__init__.py +1 -1
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/__init__.py +1 -1
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/adapters/github.py +8 -5
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/modules/backlog/src/commands.py +285 -154
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/.gitignore +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/LICENSE.md +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/README.md +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/resources/mappings/node-async.yaml +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/resources/mappings/python-async.yaml +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/resources/mappings/speckit-default.yaml +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/resources/prompts/shared/cli-enforcement.md +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/resources/prompts/specfact.01-import.md +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/resources/prompts/specfact.02-plan.md +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/resources/prompts/specfact.03-review.md +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/resources/prompts/specfact.04-sdd.md +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/resources/prompts/specfact.05-enforce.md +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/resources/prompts/specfact.06-sync.md +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/resources/prompts/specfact.07-contracts.md +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/resources/prompts/specfact.backlog-daily.md +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/resources/prompts/specfact.backlog-refine.md +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/resources/prompts/specfact.compare.md +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/resources/prompts/specfact.sync-backlog.md +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/resources/prompts/specfact.validate.md +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/resources/schemas/deviation.schema.json +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/resources/schemas/plan.schema.json +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/resources/schemas/protocol.schema.json +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/resources/templates/backlog/defaults/defect_v1.yaml +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/resources/templates/backlog/defaults/enabler_v1.yaml +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/resources/templates/backlog/defaults/spike_v1.yaml +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/resources/templates/backlog/defaults/user_story_v1.yaml +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/resources/templates/backlog/field_mappings/ado_agile.yaml +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/resources/templates/backlog/field_mappings/ado_default.yaml +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/resources/templates/backlog/field_mappings/ado_kanban.yaml +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/resources/templates/backlog/field_mappings/ado_safe.yaml +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/resources/templates/backlog/field_mappings/ado_scrum.yaml +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/resources/templates/backlog/frameworks/safe/safe_feature_v1.yaml +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/resources/templates/backlog/frameworks/scrum/user_story_v1.yaml +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/resources/templates/backlog/personas/developer/developer_task_v1.yaml +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/resources/templates/backlog/personas/product-owner/user_story_v1.yaml +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/resources/templates/backlog/providers/ado/work_item_v1.yaml +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/resources/templates/github-action.yml.j2 +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/resources/templates/persona/architect.md.j2 +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/resources/templates/persona/developer.md.j2 +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/resources/templates/persona/product-owner.md.j2 +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/resources/templates/plan.bundle.yaml.j2 +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/resources/templates/pr-template.md.j2 +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/resources/templates/protocol.yaml.j2 +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/resources/templates/telemetry.yaml.example +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/__main__.py +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/adapters/__init__.py +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/adapters/ado.py +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/adapters/backlog_base.py +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/adapters/base.py +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/adapters/openspec.py +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/adapters/openspec_parser.py +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/adapters/registry.py +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/adapters/speckit.py +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/agents/__init__.py +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/agents/analyze_agent.py +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/agents/base.py +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/agents/plan_agent.py +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/agents/registry.py +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/agents/sync_agent.py +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/analyzers/__init__.py +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/analyzers/ambiguity_scanner.py +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/analyzers/code_analyzer.py +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/analyzers/constitution_evidence_extractor.py +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/analyzers/contract_extractor.py +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/analyzers/control_flow_analyzer.py +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/analyzers/graph_analyzer.py +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/analyzers/relationship_mapper.py +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/analyzers/requirement_extractor.py +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/analyzers/test_pattern_extractor.py +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/backlog/__init__.py +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/backlog/adapters/__init__.py +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/backlog/adapters/base.py +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/backlog/adapters/local_yaml_adapter.py +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/backlog/ai_refiner.py +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/backlog/converter.py +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/backlog/filters.py +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/backlog/format_detector.py +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/backlog/formats/__init__.py +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/backlog/formats/base.py +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/backlog/formats/markdown_format.py +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/backlog/formats/structured_format.py +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/backlog/mappers/__init__.py +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/backlog/mappers/ado_mapper.py +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/backlog/mappers/base.py +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/backlog/mappers/github_mapper.py +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/backlog/mappers/template_config.py +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/backlog/template_detector.py +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/cli.py +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/commands/__init__.py +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/commands/analyze.py +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/commands/auth.py +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/commands/backlog_commands.py +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/commands/contract_cmd.py +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/commands/drift.py +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/commands/enforce.py +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/commands/generate.py +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/commands/import_cmd.py +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/commands/init.py +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/commands/migrate.py +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/commands/plan.py +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/commands/project_cmd.py +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/commands/repro.py +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/commands/sdd.py +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/commands/spec.py +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/commands/sync.py +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/commands/update.py +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/commands/validate.py +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/common/__init__.py +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/common/logger_setup.py +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/common/logging_utils.py +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/common/text_utils.py +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/common/utils.py +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/comparators/__init__.py +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/comparators/plan_comparator.py +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/contracts/__init__.py +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/contracts/crosshair_props.py +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/contracts/module_interface.py +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/enrichers/constitution_enricher.py +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/enrichers/plan_enricher.py +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/generators/__init__.py +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/generators/contract_generator.py +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/generators/openapi_extractor.py +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/generators/persona_exporter.py +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/generators/plan_generator.py +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/generators/protocol_generator.py +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/generators/report_generator.py +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/generators/task_generator.py +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/generators/test_to_openapi.py +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/generators/workflow_generator.py +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/importers/__init__.py +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/importers/speckit_converter.py +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/importers/speckit_scanner.py +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/integrations/__init__.py +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/integrations/specmatic.py +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/merge/__init__.py +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/merge/resolver.py +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/migrations/__init__.py +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/migrations/plan_migrator.py +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/models/__init__.py +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/models/backlog_item.py +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/models/bridge.py +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/models/capabilities.py +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/models/change.py +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/models/contract.py +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/models/deviation.py +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/models/dor_config.py +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/models/enforcement.py +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/models/module_package.py +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/models/persona_template.py +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/models/plan.py +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/models/project.py +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/models/protocol.py +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/models/quality.py +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/models/sdd.py +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/models/source_tracking.py +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/models/task.py +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/models/validation.py +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/modes/__init__.py +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/modes/detector.py +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/modes/router.py +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/modules/__init__.py +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/modules/analyze/module-package.yaml +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/modules/analyze/src/__init__.py +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/modules/analyze/src/app.py +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/modules/analyze/src/commands.py +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/modules/auth/module-package.yaml +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/modules/auth/src/__init__.py +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/modules/auth/src/app.py +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/modules/auth/src/commands.py +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/modules/backlog/module-package.yaml +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/modules/backlog/src/__init__.py +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/modules/backlog/src/adapters/__init__.py +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/modules/backlog/src/adapters/ado.py +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/modules/backlog/src/adapters/base.py +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/modules/backlog/src/adapters/github.py +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/modules/backlog/src/adapters/jira.py +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/modules/backlog/src/adapters/linear.py +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/modules/backlog/src/app.py +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/modules/contract/module-package.yaml +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/modules/contract/src/__init__.py +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/modules/contract/src/app.py +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/modules/contract/src/commands.py +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/modules/drift/module-package.yaml +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/modules/drift/src/__init__.py +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/modules/drift/src/app.py +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/modules/drift/src/commands.py +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/modules/enforce/module-package.yaml +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/modules/enforce/src/__init__.py +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/modules/enforce/src/app.py +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/modules/enforce/src/commands.py +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/modules/generate/module-package.yaml +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/modules/generate/src/__init__.py +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/modules/generate/src/app.py +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/modules/generate/src/commands.py +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/modules/import_cmd/module-package.yaml +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/modules/import_cmd/src/__init__.py +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/modules/import_cmd/src/app.py +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/modules/import_cmd/src/commands.py +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/modules/init/module-package.yaml +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/modules/init/src/__init__.py +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/modules/init/src/app.py +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/modules/init/src/commands.py +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/modules/migrate/module-package.yaml +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/modules/migrate/src/__init__.py +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/modules/migrate/src/app.py +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/modules/migrate/src/commands.py +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/modules/module_io_shim.py +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/modules/plan/module-package.yaml +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/modules/plan/src/__init__.py +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/modules/plan/src/app.py +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/modules/plan/src/commands.py +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/modules/project/module-package.yaml +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/modules/project/src/__init__.py +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/modules/project/src/app.py +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/modules/project/src/commands.py +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/modules/repro/module-package.yaml +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/modules/repro/src/__init__.py +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/modules/repro/src/app.py +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/modules/repro/src/commands.py +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/modules/sdd/module-package.yaml +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/modules/sdd/src/__init__.py +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/modules/sdd/src/app.py +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/modules/sdd/src/commands.py +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/modules/spec/module-package.yaml +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/modules/spec/src/__init__.py +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/modules/spec/src/app.py +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/modules/spec/src/commands.py +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/modules/sync/module-package.yaml +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/modules/sync/src/__init__.py +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/modules/sync/src/app.py +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/modules/sync/src/commands.py +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/modules/upgrade/module-package.yaml +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/modules/upgrade/src/__init__.py +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/modules/upgrade/src/app.py +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/modules/upgrade/src/commands.py +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/modules/validate/module-package.yaml +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/modules/validate/src/__init__.py +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/modules/validate/src/app.py +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/modules/validate/src/commands.py +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/parsers/__init__.py +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/parsers/persona_importer.py +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/registry/__init__.py +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/registry/bootstrap.py +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/registry/bridge_registry.py +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/registry/help_cache.py +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/registry/metadata.py +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/registry/module_packages.py +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/registry/module_state.py +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/registry/registry.py +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/resources/semgrep/async.yml +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/resources/semgrep/code-quality.yml +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/resources/semgrep/feature-detection.yml +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/runtime.py +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/sync/__init__.py +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/sync/bridge_probe.py +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/sync/bridge_sync.py +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/sync/bridge_watch.py +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/sync/change_detector.py +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/sync/code_to_spec.py +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/sync/drift_detector.py +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/sync/repository_sync.py +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/sync/spec_to_code.py +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/sync/spec_to_tests.py +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/sync/watcher.py +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/sync/watcher_enhanced.py +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/telemetry.py +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/templates/__init__.py +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/templates/bridge_templates.py +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/templates/defaults/defect_v1.yaml +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/templates/defaults/enabler_v1.yaml +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/templates/defaults/spike_v1.yaml +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/templates/defaults/user_story_v1.yaml +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/templates/frameworks/scrum/user_story_v1.yaml +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/templates/personas/product-owner/user_story_v1.yaml +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/templates/providers/ado/work_item_v1.yaml +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/templates/registry.py +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/templates/specification_templates.py +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/utils/__init__.py +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/utils/acceptance_criteria.py +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/utils/auth_tokens.py +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/utils/bundle_converters.py +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/utils/bundle_loader.py +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/utils/code_change_detector.py +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/utils/console.py +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/utils/content_sanitizer.py +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/utils/context_detection.py +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/utils/enrichment_context.py +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/utils/enrichment_parser.py +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/utils/env_manager.py +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/utils/feature_keys.py +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/utils/git.py +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/utils/github_annotations.py +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/utils/ide_setup.py +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/utils/incremental_check.py +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/utils/metadata.py +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/utils/optional_deps.py +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/utils/performance.py +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/utils/persona_ownership.py +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/utils/progress.py +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/utils/progressive_disclosure.py +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/utils/prompts.py +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/utils/sdd_discovery.py +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/utils/source_scanner.py +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/utils/startup_checks.py +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/utils/structure.py +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/utils/structured_io.py +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/utils/suggestions.py +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/utils/terminal.py +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/utils/yaml_utils.py +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/validators/__init__.py +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/validators/agile_validation.py +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/validators/change_proposal_integration.py +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/validators/cli_first_validator.py +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/validators/contract_validator.py +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/validators/fsm.py +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/validators/repro_checker.py +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/validators/schema.py +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/validators/sidecar/__init__.py +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/validators/sidecar/contract_populator.py +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/validators/sidecar/crosshair_runner.py +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/validators/sidecar/crosshair_summary.py +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/validators/sidecar/dependency_installer.py +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/validators/sidecar/framework_detector.py +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/validators/sidecar/frameworks/__init__.py +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/validators/sidecar/frameworks/base.py +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/validators/sidecar/frameworks/django.py +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/validators/sidecar/frameworks/drf.py +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/validators/sidecar/frameworks/fastapi.py +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/validators/sidecar/frameworks/flask.py +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/validators/sidecar/harness_generator.py +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/validators/sidecar/models.py +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/validators/sidecar/orchestrator.py +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/validators/sidecar/specmatic_runner.py +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/validators/sidecar/unannotated_detector.py +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/versioning/__init__.py +0 -0
- {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/versioning/analyzer.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: specfact-cli
|
|
3
|
-
Version: 0.30.
|
|
3
|
+
Version: 0.30.3
|
|
4
4
|
Summary: The swiss knife CLI for agile DevOps teams. Keep backlog, specs, tests, and code in sync with validation and contract enforcement for new projects and long-lived codebases.
|
|
5
5
|
Project-URL: Homepage, https://github.com/nold-ai/specfact-cli
|
|
6
6
|
Project-URL: Repository, https://github.com/nold-ai/specfact-cli.git
|
|
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "specfact-cli"
|
|
7
|
-
version = "0.30.
|
|
7
|
+
version = "0.30.3"
|
|
8
8
|
description = "The swiss knife CLI for agile DevOps teams. Keep backlog, specs, tests, and code in sync with validation and contract enforcement for new projects and long-lived codebases."
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.11"
|
|
@@ -2763,20 +2763,23 @@ class GitHubAdapter(BridgeAdapter, BacklogAdapterMixin, BacklogAdapter):
|
|
|
2763
2763
|
# Build canonical fields from parsed refined body (use refined values)
|
|
2764
2764
|
canonical_fields = {
|
|
2765
2765
|
"description": description,
|
|
2766
|
-
#
|
|
2767
|
-
|
|
2766
|
+
# Prefer extracted section values, but fall back to canonical item fields
|
|
2767
|
+
# so label-style refinement parsing still writes dedicated fields.
|
|
2768
|
+
"acceptance_criteria": existing_acceptance_criteria or item.acceptance_criteria,
|
|
2768
2769
|
"story_points": (
|
|
2769
2770
|
int(existing_story_points)
|
|
2770
2771
|
if existing_story_points and existing_story_points.strip().isdigit()
|
|
2771
|
-
else
|
|
2772
|
+
else item.story_points
|
|
2772
2773
|
),
|
|
2773
2774
|
"business_value": (
|
|
2774
2775
|
int(existing_business_value)
|
|
2775
2776
|
if existing_business_value and existing_business_value.strip().isdigit()
|
|
2776
|
-
else
|
|
2777
|
+
else item.business_value
|
|
2777
2778
|
),
|
|
2778
2779
|
"priority": (
|
|
2779
|
-
int(existing_priority)
|
|
2780
|
+
int(existing_priority)
|
|
2781
|
+
if existing_priority and existing_priority.strip().isdigit()
|
|
2782
|
+
else item.priority
|
|
2780
2783
|
),
|
|
2781
2784
|
"value_points": item.value_points,
|
|
2782
2785
|
"work_item_type": item.work_item_type,
|
{specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/modules/backlog/src/commands.py
RENAMED
|
@@ -1490,6 +1490,148 @@ def _parse_refined_export_markdown(content: str) -> dict[str, dict[str, Any]]:
|
|
|
1490
1490
|
return result
|
|
1491
1491
|
|
|
1492
1492
|
|
|
1493
|
+
@beartype
|
|
1494
|
+
@require(lambda content: isinstance(content, str), "Refinement output must be a string")
|
|
1495
|
+
@ensure(lambda result: isinstance(result, dict), "Must return a dict")
|
|
1496
|
+
def _parse_refinement_output_fields(content: str) -> dict[str, Any]:
|
|
1497
|
+
"""
|
|
1498
|
+
Parse refinement output into canonical fields for provider-safe writeback.
|
|
1499
|
+
|
|
1500
|
+
Supports both:
|
|
1501
|
+
- Markdown heading style (`## Acceptance Criteria`, `## Story Points`, ...)
|
|
1502
|
+
- Label style (`Acceptance Criteria:`, `Story Points:`, ...)
|
|
1503
|
+
"""
|
|
1504
|
+
normalized = content.replace("\r\n", "\n").strip()
|
|
1505
|
+
if not normalized:
|
|
1506
|
+
return {}
|
|
1507
|
+
|
|
1508
|
+
parsed: dict[str, Any] = {}
|
|
1509
|
+
|
|
1510
|
+
# First parse markdown-heading style using existing GitHub field semantics.
|
|
1511
|
+
from specfact_cli.backlog.mappers.github_mapper import GitHubFieldMapper
|
|
1512
|
+
|
|
1513
|
+
heading_mapper = GitHubFieldMapper()
|
|
1514
|
+
heading_fields = heading_mapper.extract_fields({"body": normalized, "labels": []})
|
|
1515
|
+
|
|
1516
|
+
description = (heading_fields.get("description") or "").strip()
|
|
1517
|
+
if description:
|
|
1518
|
+
parsed["description"] = description
|
|
1519
|
+
|
|
1520
|
+
acceptance = heading_fields.get("acceptance_criteria")
|
|
1521
|
+
if isinstance(acceptance, str) and acceptance.strip():
|
|
1522
|
+
parsed["acceptance_criteria"] = acceptance.strip()
|
|
1523
|
+
|
|
1524
|
+
for key in ("story_points", "business_value", "priority"):
|
|
1525
|
+
value = heading_fields.get(key)
|
|
1526
|
+
if isinstance(value, int):
|
|
1527
|
+
parsed[key] = value
|
|
1528
|
+
|
|
1529
|
+
def _extract_heading_section(section_name: str) -> str:
|
|
1530
|
+
pattern = rf"^##+\s+{re.escape(section_name)}\s*$\n(.*?)(?=^##|\Z)"
|
|
1531
|
+
match = re.search(pattern, normalized, re.MULTILINE | re.DOTALL | re.IGNORECASE)
|
|
1532
|
+
if not match:
|
|
1533
|
+
return ""
|
|
1534
|
+
return match.group(1).strip()
|
|
1535
|
+
|
|
1536
|
+
# Then parse label-style blocks; explicit labels override heading heuristics.
|
|
1537
|
+
label_aliases = {
|
|
1538
|
+
"description": "description",
|
|
1539
|
+
"acceptance criteria": "acceptance_criteria",
|
|
1540
|
+
"story points": "story_points",
|
|
1541
|
+
"business value": "business_value",
|
|
1542
|
+
"priority": "priority",
|
|
1543
|
+
"work item type": "work_item_type",
|
|
1544
|
+
"notes": "notes",
|
|
1545
|
+
"dependencies": "dependencies",
|
|
1546
|
+
"area path": "area_path",
|
|
1547
|
+
"iteration path": "iteration_path",
|
|
1548
|
+
"provider": "provider",
|
|
1549
|
+
}
|
|
1550
|
+
label_pattern = re.compile(r"^\s*(?:[-*]\s*)?(?:\*\*)?([A-Za-z][A-Za-z0-9 ()/_-]*?)(?:\*\*)?\s*:\s*(.*)\s*$")
|
|
1551
|
+
blocks: dict[str, str] = {}
|
|
1552
|
+
current_key: str | None = None
|
|
1553
|
+
current_lines: list[str] = []
|
|
1554
|
+
|
|
1555
|
+
def _flush_current() -> None:
|
|
1556
|
+
nonlocal current_key, current_lines
|
|
1557
|
+
if current_key is None:
|
|
1558
|
+
return
|
|
1559
|
+
value = "\n".join(current_lines).strip()
|
|
1560
|
+
blocks[current_key] = value
|
|
1561
|
+
current_key = None
|
|
1562
|
+
current_lines = []
|
|
1563
|
+
|
|
1564
|
+
for line in normalized.splitlines():
|
|
1565
|
+
match = label_pattern.match(line)
|
|
1566
|
+
if match:
|
|
1567
|
+
candidate = re.sub(r"\s+", " ", match.group(1).strip().lower())
|
|
1568
|
+
canonical = label_aliases.get(candidate)
|
|
1569
|
+
if canonical:
|
|
1570
|
+
_flush_current()
|
|
1571
|
+
current_key = canonical
|
|
1572
|
+
first_value = (match.group(2) or "").strip()
|
|
1573
|
+
current_lines = [first_value] if first_value else []
|
|
1574
|
+
continue
|
|
1575
|
+
if current_key is not None:
|
|
1576
|
+
current_lines.append(line.rstrip())
|
|
1577
|
+
_flush_current()
|
|
1578
|
+
|
|
1579
|
+
if blocks and not blocks.get("description"):
|
|
1580
|
+
# If label-style blocks are present but no explicit Description block exists,
|
|
1581
|
+
# do not keep the heading parser fallback description (it may contain raw labels).
|
|
1582
|
+
parsed.pop("description", None)
|
|
1583
|
+
|
|
1584
|
+
if blocks.get("description"):
|
|
1585
|
+
parsed["description"] = blocks["description"]
|
|
1586
|
+
if blocks.get("acceptance_criteria"):
|
|
1587
|
+
parsed["acceptance_criteria"] = blocks["acceptance_criteria"]
|
|
1588
|
+
if blocks.get("work_item_type"):
|
|
1589
|
+
parsed["work_item_type"] = blocks["work_item_type"]
|
|
1590
|
+
|
|
1591
|
+
def _parse_int(key: str) -> int | None:
|
|
1592
|
+
raw = blocks.get(key)
|
|
1593
|
+
if not raw:
|
|
1594
|
+
return None
|
|
1595
|
+
match = re.search(r"\d+", raw)
|
|
1596
|
+
if not match:
|
|
1597
|
+
return None
|
|
1598
|
+
return int(match.group(0))
|
|
1599
|
+
|
|
1600
|
+
story_points = _parse_int("story_points")
|
|
1601
|
+
if story_points is not None:
|
|
1602
|
+
parsed["story_points"] = story_points
|
|
1603
|
+
business_value = _parse_int("business_value")
|
|
1604
|
+
if business_value is not None:
|
|
1605
|
+
parsed["business_value"] = business_value
|
|
1606
|
+
priority = _parse_int("priority")
|
|
1607
|
+
if priority is not None:
|
|
1608
|
+
parsed["priority"] = priority
|
|
1609
|
+
|
|
1610
|
+
# Build a clean writeback body (description + narrative sections only).
|
|
1611
|
+
body_parts: list[str] = []
|
|
1612
|
+
cleaned_description = (parsed.get("description") or "").strip()
|
|
1613
|
+
if cleaned_description:
|
|
1614
|
+
body_parts.append(cleaned_description)
|
|
1615
|
+
for section_key, title in (("notes", "Notes"), ("dependencies", "Dependencies")):
|
|
1616
|
+
section_value = (blocks.get(section_key) or "").strip()
|
|
1617
|
+
if not section_value:
|
|
1618
|
+
section_value = _extract_heading_section(title)
|
|
1619
|
+
if section_value:
|
|
1620
|
+
body_parts.append(f"## {title}\n\n{section_value}")
|
|
1621
|
+
|
|
1622
|
+
cleaned_body = "\n\n".join(part for part in body_parts if part.strip()).strip()
|
|
1623
|
+
if cleaned_body:
|
|
1624
|
+
parsed["body_markdown"] = cleaned_body
|
|
1625
|
+
elif cleaned_description:
|
|
1626
|
+
parsed["body_markdown"] = cleaned_description
|
|
1627
|
+
elif blocks:
|
|
1628
|
+
parsed["body_markdown"] = ""
|
|
1629
|
+
else:
|
|
1630
|
+
parsed["body_markdown"] = normalized
|
|
1631
|
+
|
|
1632
|
+
return parsed
|
|
1633
|
+
|
|
1634
|
+
|
|
1493
1635
|
@beartype
|
|
1494
1636
|
def _item_needs_refinement(
|
|
1495
1637
|
item: BacklogItem,
|
|
@@ -1629,6 +1771,111 @@ def _fetch_backlog_items(
|
|
|
1629
1771
|
return items
|
|
1630
1772
|
|
|
1631
1773
|
|
|
1774
|
+
@beartype
|
|
1775
|
+
@require(lambda item: isinstance(item, BacklogItem), "Item must be BacklogItem")
|
|
1776
|
+
@ensure(lambda result: isinstance(result, list), "Must return list")
|
|
1777
|
+
def _build_refine_update_fields(item: BacklogItem) -> list[str]:
|
|
1778
|
+
"""Build update field list for refine writeback based on populated canonical fields."""
|
|
1779
|
+
update_fields_list = ["title", "body_markdown"]
|
|
1780
|
+
if item.acceptance_criteria:
|
|
1781
|
+
update_fields_list.append("acceptance_criteria")
|
|
1782
|
+
if item.story_points is not None:
|
|
1783
|
+
update_fields_list.append("story_points")
|
|
1784
|
+
if item.business_value is not None:
|
|
1785
|
+
update_fields_list.append("business_value")
|
|
1786
|
+
if item.priority is not None:
|
|
1787
|
+
update_fields_list.append("priority")
|
|
1788
|
+
return update_fields_list
|
|
1789
|
+
|
|
1790
|
+
|
|
1791
|
+
@beartype
|
|
1792
|
+
def _maybe_add_refine_openspec_comment(
|
|
1793
|
+
adapter_instance: BacklogAdapter,
|
|
1794
|
+
updated_item: BacklogItem,
|
|
1795
|
+
item: BacklogItem,
|
|
1796
|
+
openspec_comment: bool,
|
|
1797
|
+
) -> None:
|
|
1798
|
+
"""Optionally add OpenSpec reference comment after successful writeback."""
|
|
1799
|
+
if not openspec_comment:
|
|
1800
|
+
return
|
|
1801
|
+
|
|
1802
|
+
original_body = item.body_markdown or ""
|
|
1803
|
+
openspec_change_id = _extract_openspec_change_id(original_body)
|
|
1804
|
+
change_id = openspec_change_id or f"backlog-refine-{item.id}"
|
|
1805
|
+
comment_text = (
|
|
1806
|
+
f"## OpenSpec Change Proposal Reference\n\n"
|
|
1807
|
+
f"This backlog item was refined using SpecFact CLI template-driven refinement.\n\n"
|
|
1808
|
+
f"- **Change ID**: `{change_id}`\n"
|
|
1809
|
+
f"- **Template**: `{item.detected_template or 'auto-detected'}`\n"
|
|
1810
|
+
f"- **Confidence**: `{item.template_confidence or 0.0:.2f}`\n"
|
|
1811
|
+
f"- **Refined**: {item.refinement_timestamp or 'N/A'}\n\n"
|
|
1812
|
+
f"*Note: Original body preserved. "
|
|
1813
|
+
f"This comment provides OpenSpec reference for cross-sync.*"
|
|
1814
|
+
)
|
|
1815
|
+
if adapter_instance.add_comment(updated_item, comment_text):
|
|
1816
|
+
console.print("[green]✓ Added OpenSpec reference comment[/green]")
|
|
1817
|
+
else:
|
|
1818
|
+
console.print("[yellow]⚠ Failed to add comment (adapter may not support comments)[/yellow]")
|
|
1819
|
+
|
|
1820
|
+
|
|
1821
|
+
@beartype
|
|
1822
|
+
def _write_refined_backlog_item(
|
|
1823
|
+
adapter_registry: AdapterRegistry,
|
|
1824
|
+
adapter: str,
|
|
1825
|
+
item: BacklogItem,
|
|
1826
|
+
repo_owner: str | None,
|
|
1827
|
+
repo_name: str | None,
|
|
1828
|
+
github_token: str | None,
|
|
1829
|
+
ado_org: str | None,
|
|
1830
|
+
ado_project: str | None,
|
|
1831
|
+
ado_token: str | None,
|
|
1832
|
+
openspec_comment: bool,
|
|
1833
|
+
) -> bool:
|
|
1834
|
+
"""Write a refined item back to adapter and optionally add OpenSpec comment."""
|
|
1835
|
+
writeback_kwargs = _build_adapter_kwargs(
|
|
1836
|
+
adapter,
|
|
1837
|
+
repo_owner=repo_owner,
|
|
1838
|
+
repo_name=repo_name,
|
|
1839
|
+
github_token=github_token,
|
|
1840
|
+
ado_org=ado_org,
|
|
1841
|
+
ado_project=ado_project,
|
|
1842
|
+
ado_token=ado_token,
|
|
1843
|
+
)
|
|
1844
|
+
|
|
1845
|
+
adapter_instance = adapter_registry.get_adapter(adapter, **writeback_kwargs)
|
|
1846
|
+
if not isinstance(adapter_instance, BacklogAdapter):
|
|
1847
|
+
console.print("[yellow]⚠ Adapter does not support backlog updates[/yellow]")
|
|
1848
|
+
return False
|
|
1849
|
+
|
|
1850
|
+
update_fields_list = _build_refine_update_fields(item)
|
|
1851
|
+
updated_item = adapter_instance.update_backlog_item(item, update_fields=update_fields_list)
|
|
1852
|
+
console.print(f"[green]✓ Updated backlog item: {updated_item.url}[/green]")
|
|
1853
|
+
_maybe_add_refine_openspec_comment(adapter_instance, updated_item, item, openspec_comment)
|
|
1854
|
+
return True
|
|
1855
|
+
|
|
1856
|
+
|
|
1857
|
+
@beartype
|
|
1858
|
+
@ensure(lambda result: isinstance(result, str), "Must return string")
|
|
1859
|
+
def _read_refined_content_from_stdin() -> str:
|
|
1860
|
+
"""Read multiline refined content with sentinel commands from stdin."""
|
|
1861
|
+
refined_content_lines: list[str] = []
|
|
1862
|
+
console.print("[bold]Paste refined content below (type 'END' on a new line when done):[/bold]")
|
|
1863
|
+
console.print("[dim]Commands: :skip (skip this item), :quit or :abort (cancel session)[/dim]")
|
|
1864
|
+
|
|
1865
|
+
while True:
|
|
1866
|
+
try:
|
|
1867
|
+
line = input()
|
|
1868
|
+
line_upper = line.strip().upper()
|
|
1869
|
+
if line_upper == "END":
|
|
1870
|
+
break
|
|
1871
|
+
if line_upper in (":SKIP", ":QUIT", ":ABORT"):
|
|
1872
|
+
return line_upper
|
|
1873
|
+
refined_content_lines.append(line)
|
|
1874
|
+
except EOFError:
|
|
1875
|
+
break
|
|
1876
|
+
return "\n".join(refined_content_lines).strip()
|
|
1877
|
+
|
|
1878
|
+
|
|
1632
1879
|
@beartype
|
|
1633
1880
|
@app.command()
|
|
1634
1881
|
@require(
|
|
@@ -2909,50 +3156,22 @@ def refine(
|
|
|
2909
3156
|
console.print("3. Copy the refined content from the AI copilot response")
|
|
2910
3157
|
console.print("4. Paste the refined content below, then type 'END' on a new line when done\n")
|
|
2911
3158
|
|
|
2912
|
-
# Read multiline input from stdin
|
|
2913
|
-
# Support both interactive (paste + Ctrl+D) and non-interactive (EOF) modes
|
|
2914
|
-
# Note: When pasting multiline content, each line is read sequentially
|
|
2915
|
-
refined_content_lines: list[str] = []
|
|
2916
|
-
console.print("[bold]Paste refined content below (type 'END' on a new line when done):[/bold]")
|
|
2917
|
-
console.print("[dim]Commands: :skip (skip this item), :quit or :abort (cancel session)[/dim]")
|
|
2918
|
-
|
|
2919
3159
|
try:
|
|
2920
|
-
|
|
2921
|
-
try:
|
|
2922
|
-
line = input()
|
|
2923
|
-
line_stripped = line.strip()
|
|
2924
|
-
line_upper = line_stripped.upper()
|
|
2925
|
-
|
|
2926
|
-
# Check for sentinel values (case-insensitive)
|
|
2927
|
-
if line_upper == "END":
|
|
2928
|
-
break
|
|
2929
|
-
if line_upper == ":SKIP":
|
|
2930
|
-
console.print("[yellow]Skipping current item[/yellow]")
|
|
2931
|
-
skipped_count += 1
|
|
2932
|
-
refined_content_lines = [] # Clear content
|
|
2933
|
-
break
|
|
2934
|
-
if line_upper in (":QUIT", ":ABORT"):
|
|
2935
|
-
console.print("[yellow]Cancelling refinement session[/yellow]")
|
|
2936
|
-
cancelled = True
|
|
2937
|
-
refined_content_lines = [] # Clear content
|
|
2938
|
-
break
|
|
2939
|
-
|
|
2940
|
-
refined_content_lines.append(line)
|
|
2941
|
-
except EOFError:
|
|
2942
|
-
# Ctrl+D pressed or EOF reached (common when pasting multiline content)
|
|
2943
|
-
break
|
|
3160
|
+
refined_content = _read_refined_content_from_stdin()
|
|
2944
3161
|
except KeyboardInterrupt:
|
|
2945
3162
|
console.print("\n[yellow]Input cancelled - skipping[/yellow]")
|
|
2946
3163
|
skipped_count += 1
|
|
2947
3164
|
continue
|
|
2948
3165
|
|
|
2949
|
-
|
|
2950
|
-
|
|
3166
|
+
if refined_content == ":SKIP":
|
|
3167
|
+
console.print("[yellow]Skipping current item[/yellow]")
|
|
3168
|
+
skipped_count += 1
|
|
3169
|
+
continue
|
|
3170
|
+
if refined_content in (":QUIT", ":ABORT"):
|
|
3171
|
+
console.print("[yellow]Cancelling refinement session[/yellow]")
|
|
3172
|
+
cancelled = True
|
|
2951
3173
|
break
|
|
2952
|
-
|
|
2953
|
-
refined_content = "\n".join(refined_content_lines).strip()
|
|
2954
|
-
|
|
2955
|
-
if not refined_content:
|
|
3174
|
+
if not refined_content.strip():
|
|
2956
3175
|
console.print("[yellow]No refined content provided - skipping[/yellow]")
|
|
2957
3176
|
skipped_count += 1
|
|
2958
3177
|
continue
|
|
@@ -3023,8 +3242,21 @@ def refine(
|
|
|
3023
3242
|
)
|
|
3024
3243
|
)
|
|
3025
3244
|
|
|
3026
|
-
#
|
|
3027
|
-
|
|
3245
|
+
# Parse structured refinement output before writeback so provider fields
|
|
3246
|
+
# are updated from canonical values instead of writing prompt labels verbatim.
|
|
3247
|
+
parsed_refined_fields = _parse_refinement_output_fields(refinement_result.refined_body)
|
|
3248
|
+
item.refined_body = parsed_refined_fields.get("body_markdown", refinement_result.refined_body)
|
|
3249
|
+
|
|
3250
|
+
if parsed_refined_fields.get("acceptance_criteria"):
|
|
3251
|
+
item.acceptance_criteria = parsed_refined_fields["acceptance_criteria"]
|
|
3252
|
+
if parsed_refined_fields.get("story_points") is not None:
|
|
3253
|
+
item.story_points = parsed_refined_fields["story_points"]
|
|
3254
|
+
if parsed_refined_fields.get("business_value") is not None:
|
|
3255
|
+
item.business_value = parsed_refined_fields["business_value"]
|
|
3256
|
+
if parsed_refined_fields.get("priority") is not None:
|
|
3257
|
+
item.priority = parsed_refined_fields["priority"]
|
|
3258
|
+
if parsed_refined_fields.get("work_item_type"):
|
|
3259
|
+
item.work_item_type = parsed_refined_fields["work_item_type"]
|
|
3028
3260
|
|
|
3029
3261
|
# Preview mode (default) - don't write, just show preview
|
|
3030
3262
|
if preview and not write:
|
|
@@ -3033,134 +3265,33 @@ def refine(
|
|
|
3033
3265
|
refined_count += 1 # Count as refined for preview purposes
|
|
3034
3266
|
continue
|
|
3035
3267
|
|
|
3036
|
-
# Write mode - requires explicit --write flag
|
|
3037
3268
|
if write:
|
|
3038
|
-
|
|
3269
|
+
should_write = False
|
|
3039
3270
|
if auto_accept_high_confidence and refinement_result.confidence >= 0.85:
|
|
3040
3271
|
console.print("[green]Auto-accepting high-confidence refinement and writing to backlog[/green]")
|
|
3041
|
-
|
|
3272
|
+
should_write = True
|
|
3273
|
+
else:
|
|
3274
|
+
console.print()
|
|
3275
|
+
should_write = Confirm.ask("Accept refinement and write to backlog?", default=False)
|
|
3042
3276
|
|
|
3043
|
-
|
|
3044
|
-
|
|
3045
|
-
|
|
3046
|
-
|
|
3277
|
+
if should_write:
|
|
3278
|
+
item.apply_refinement()
|
|
3279
|
+
_write_refined_backlog_item(
|
|
3280
|
+
adapter_registry=adapter_registry,
|
|
3281
|
+
adapter=adapter,
|
|
3282
|
+
item=item,
|
|
3047
3283
|
repo_owner=repo_owner,
|
|
3048
3284
|
repo_name=repo_name,
|
|
3049
3285
|
github_token=github_token,
|
|
3050
3286
|
ado_org=ado_org,
|
|
3051
3287
|
ado_project=ado_project,
|
|
3052
3288
|
ado_token=ado_token,
|
|
3289
|
+
openspec_comment=openspec_comment,
|
|
3053
3290
|
)
|
|
3054
|
-
|
|
3055
|
-
adapter_instance = adapter_registry.get_adapter(adapter, **writeback_kwargs)
|
|
3056
|
-
if isinstance(adapter_instance, BacklogAdapter):
|
|
3057
|
-
# Update all fields including new agile framework fields
|
|
3058
|
-
update_fields_list = ["title", "body_markdown"]
|
|
3059
|
-
if item.acceptance_criteria:
|
|
3060
|
-
update_fields_list.append("acceptance_criteria")
|
|
3061
|
-
if item.story_points is not None:
|
|
3062
|
-
update_fields_list.append("story_points")
|
|
3063
|
-
if item.business_value is not None:
|
|
3064
|
-
update_fields_list.append("business_value")
|
|
3065
|
-
if item.priority is not None:
|
|
3066
|
-
update_fields_list.append("priority")
|
|
3067
|
-
updated_item = adapter_instance.update_backlog_item(item, update_fields=update_fields_list)
|
|
3068
|
-
console.print(f"[green]✓ Updated backlog item: {updated_item.url}[/green]")
|
|
3069
|
-
|
|
3070
|
-
# Add OpenSpec comment if requested
|
|
3071
|
-
if openspec_comment:
|
|
3072
|
-
# Extract OpenSpec change proposal ID from original body if present
|
|
3073
|
-
original_body = item.body_markdown or ""
|
|
3074
|
-
openspec_change_id = _extract_openspec_change_id(original_body)
|
|
3075
|
-
|
|
3076
|
-
# Generate OpenSpec change proposal reference
|
|
3077
|
-
change_id = openspec_change_id or f"backlog-refine-{item.id}"
|
|
3078
|
-
comment_text = (
|
|
3079
|
-
f"## OpenSpec Change Proposal Reference\n\n"
|
|
3080
|
-
f"This backlog item was refined using SpecFact CLI template-driven refinement.\n\n"
|
|
3081
|
-
f"- **Change ID**: `{change_id}`\n"
|
|
3082
|
-
f"- **Template**: `{item.detected_template or 'auto-detected'}`\n"
|
|
3083
|
-
f"- **Confidence**: `{item.template_confidence or 0.0:.2f}`\n"
|
|
3084
|
-
f"- **Refined**: {item.refinement_timestamp or 'N/A'}\n\n"
|
|
3085
|
-
f"*Note: Original body preserved. "
|
|
3086
|
-
f"This comment provides OpenSpec reference for cross-sync.*"
|
|
3087
|
-
)
|
|
3088
|
-
if adapter_instance.add_comment(updated_item, comment_text):
|
|
3089
|
-
console.print("[green]✓ Added OpenSpec reference comment[/green]")
|
|
3090
|
-
else:
|
|
3091
|
-
console.print(
|
|
3092
|
-
"[yellow]⚠ Failed to add comment (adapter may not support comments)[/yellow]"
|
|
3093
|
-
)
|
|
3094
|
-
else:
|
|
3095
|
-
console.print("[yellow]⚠ Adapter does not support backlog updates[/yellow]")
|
|
3096
3291
|
refined_count += 1
|
|
3097
3292
|
else:
|
|
3098
|
-
|
|
3099
|
-
|
|
3100
|
-
accept = Confirm.ask("Accept refinement and write to backlog?", default=False)
|
|
3101
|
-
if accept:
|
|
3102
|
-
item.apply_refinement()
|
|
3103
|
-
|
|
3104
|
-
# Writeback to remote backlog using adapter
|
|
3105
|
-
# Build adapter kwargs for writeback
|
|
3106
|
-
writeback_kwargs = _build_adapter_kwargs(
|
|
3107
|
-
adapter,
|
|
3108
|
-
repo_owner=repo_owner,
|
|
3109
|
-
repo_name=repo_name,
|
|
3110
|
-
github_token=github_token,
|
|
3111
|
-
ado_org=ado_org,
|
|
3112
|
-
ado_project=ado_project,
|
|
3113
|
-
ado_token=ado_token,
|
|
3114
|
-
)
|
|
3115
|
-
|
|
3116
|
-
adapter_instance = adapter_registry.get_adapter(adapter, **writeback_kwargs)
|
|
3117
|
-
if isinstance(adapter_instance, BacklogAdapter):
|
|
3118
|
-
# Update all fields including new agile framework fields
|
|
3119
|
-
update_fields_list = ["title", "body_markdown"]
|
|
3120
|
-
if item.acceptance_criteria:
|
|
3121
|
-
update_fields_list.append("acceptance_criteria")
|
|
3122
|
-
if item.story_points is not None:
|
|
3123
|
-
update_fields_list.append("story_points")
|
|
3124
|
-
if item.business_value is not None:
|
|
3125
|
-
update_fields_list.append("business_value")
|
|
3126
|
-
if item.priority is not None:
|
|
3127
|
-
update_fields_list.append("priority")
|
|
3128
|
-
updated_item = adapter_instance.update_backlog_item(
|
|
3129
|
-
item, update_fields=update_fields_list
|
|
3130
|
-
)
|
|
3131
|
-
console.print(f"[green]✓ Updated backlog item: {updated_item.url}[/green]")
|
|
3132
|
-
|
|
3133
|
-
# Add OpenSpec comment if requested
|
|
3134
|
-
if openspec_comment:
|
|
3135
|
-
# Extract OpenSpec change proposal ID from original body if present
|
|
3136
|
-
original_body = item.body_markdown or ""
|
|
3137
|
-
openspec_change_id = _extract_openspec_change_id(original_body)
|
|
3138
|
-
|
|
3139
|
-
# Generate OpenSpec change proposal reference
|
|
3140
|
-
change_id = openspec_change_id or f"backlog-refine-{item.id}"
|
|
3141
|
-
comment_text = (
|
|
3142
|
-
f"## OpenSpec Change Proposal Reference\n\n"
|
|
3143
|
-
f"This backlog item was refined using SpecFact CLI template-driven refinement.\n\n"
|
|
3144
|
-
f"- **Change ID**: `{change_id}`\n"
|
|
3145
|
-
f"- **Template**: `{item.detected_template or 'auto-detected'}`\n"
|
|
3146
|
-
f"- **Confidence**: `{item.template_confidence or 0.0:.2f}`\n"
|
|
3147
|
-
f"- **Refined**: {item.refinement_timestamp or 'N/A'}\n\n"
|
|
3148
|
-
f"*Note: Original body preserved. "
|
|
3149
|
-
f"This comment provides OpenSpec reference for cross-sync.*"
|
|
3150
|
-
)
|
|
3151
|
-
if adapter_instance.add_comment(updated_item, comment_text):
|
|
3152
|
-
console.print("[green]✓ Added OpenSpec reference comment[/green]")
|
|
3153
|
-
else:
|
|
3154
|
-
console.print(
|
|
3155
|
-
"[yellow]⚠ Failed to add comment "
|
|
3156
|
-
"(adapter may not support comments)[/yellow]"
|
|
3157
|
-
)
|
|
3158
|
-
else:
|
|
3159
|
-
console.print("[yellow]⚠ Adapter does not support backlog updates[/yellow]")
|
|
3160
|
-
refined_count += 1
|
|
3161
|
-
else:
|
|
3162
|
-
console.print("[yellow]Refinement rejected - not writing to backlog[/yellow]")
|
|
3163
|
-
skipped_count += 1
|
|
3293
|
+
console.print("[yellow]Refinement rejected - not writing to backlog[/yellow]")
|
|
3294
|
+
skipped_count += 1
|
|
3164
3295
|
else:
|
|
3165
3296
|
# Preview mode but user didn't explicitly set --write
|
|
3166
3297
|
console.print("[yellow]Preview mode: Use --write to update backlog[/yellow]")
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{specfact_cli-0.30.2 → specfact_cli-0.30.3}/resources/templates/backlog/defaults/defect_v1.yaml
RENAMED
|
File without changes
|
{specfact_cli-0.30.2 → specfact_cli-0.30.3}/resources/templates/backlog/defaults/enabler_v1.yaml
RENAMED
|
File without changes
|
{specfact_cli-0.30.2 → specfact_cli-0.30.3}/resources/templates/backlog/defaults/spike_v1.yaml
RENAMED
|
File without changes
|
{specfact_cli-0.30.2 → specfact_cli-0.30.3}/resources/templates/backlog/defaults/user_story_v1.yaml
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{specfact_cli-0.30.2 → specfact_cli-0.30.3}/resources/templates/backlog/field_mappings/ado_safe.yaml
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|