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.
Files changed (342) hide show
  1. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/PKG-INFO +1 -1
  2. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/pyproject.toml +1 -1
  3. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/__init__.py +1 -1
  4. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/__init__.py +1 -1
  5. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/adapters/github.py +8 -5
  6. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/modules/backlog/src/commands.py +285 -154
  7. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/.gitignore +0 -0
  8. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/LICENSE.md +0 -0
  9. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/README.md +0 -0
  10. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/resources/mappings/node-async.yaml +0 -0
  11. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/resources/mappings/python-async.yaml +0 -0
  12. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/resources/mappings/speckit-default.yaml +0 -0
  13. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/resources/prompts/shared/cli-enforcement.md +0 -0
  14. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/resources/prompts/specfact.01-import.md +0 -0
  15. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/resources/prompts/specfact.02-plan.md +0 -0
  16. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/resources/prompts/specfact.03-review.md +0 -0
  17. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/resources/prompts/specfact.04-sdd.md +0 -0
  18. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/resources/prompts/specfact.05-enforce.md +0 -0
  19. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/resources/prompts/specfact.06-sync.md +0 -0
  20. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/resources/prompts/specfact.07-contracts.md +0 -0
  21. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/resources/prompts/specfact.backlog-daily.md +0 -0
  22. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/resources/prompts/specfact.backlog-refine.md +0 -0
  23. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/resources/prompts/specfact.compare.md +0 -0
  24. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/resources/prompts/specfact.sync-backlog.md +0 -0
  25. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/resources/prompts/specfact.validate.md +0 -0
  26. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/resources/schemas/deviation.schema.json +0 -0
  27. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/resources/schemas/plan.schema.json +0 -0
  28. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/resources/schemas/protocol.schema.json +0 -0
  29. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/resources/templates/backlog/defaults/defect_v1.yaml +0 -0
  30. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/resources/templates/backlog/defaults/enabler_v1.yaml +0 -0
  31. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/resources/templates/backlog/defaults/spike_v1.yaml +0 -0
  32. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/resources/templates/backlog/defaults/user_story_v1.yaml +0 -0
  33. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/resources/templates/backlog/field_mappings/ado_agile.yaml +0 -0
  34. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/resources/templates/backlog/field_mappings/ado_default.yaml +0 -0
  35. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/resources/templates/backlog/field_mappings/ado_kanban.yaml +0 -0
  36. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/resources/templates/backlog/field_mappings/ado_safe.yaml +0 -0
  37. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/resources/templates/backlog/field_mappings/ado_scrum.yaml +0 -0
  38. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/resources/templates/backlog/frameworks/safe/safe_feature_v1.yaml +0 -0
  39. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/resources/templates/backlog/frameworks/scrum/user_story_v1.yaml +0 -0
  40. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/resources/templates/backlog/personas/developer/developer_task_v1.yaml +0 -0
  41. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/resources/templates/backlog/personas/product-owner/user_story_v1.yaml +0 -0
  42. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/resources/templates/backlog/providers/ado/work_item_v1.yaml +0 -0
  43. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/resources/templates/github-action.yml.j2 +0 -0
  44. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/resources/templates/persona/architect.md.j2 +0 -0
  45. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/resources/templates/persona/developer.md.j2 +0 -0
  46. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/resources/templates/persona/product-owner.md.j2 +0 -0
  47. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/resources/templates/plan.bundle.yaml.j2 +0 -0
  48. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/resources/templates/pr-template.md.j2 +0 -0
  49. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/resources/templates/protocol.yaml.j2 +0 -0
  50. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/resources/templates/telemetry.yaml.example +0 -0
  51. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/__main__.py +0 -0
  52. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/adapters/__init__.py +0 -0
  53. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/adapters/ado.py +0 -0
  54. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/adapters/backlog_base.py +0 -0
  55. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/adapters/base.py +0 -0
  56. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/adapters/openspec.py +0 -0
  57. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/adapters/openspec_parser.py +0 -0
  58. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/adapters/registry.py +0 -0
  59. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/adapters/speckit.py +0 -0
  60. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/agents/__init__.py +0 -0
  61. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/agents/analyze_agent.py +0 -0
  62. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/agents/base.py +0 -0
  63. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/agents/plan_agent.py +0 -0
  64. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/agents/registry.py +0 -0
  65. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/agents/sync_agent.py +0 -0
  66. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/analyzers/__init__.py +0 -0
  67. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/analyzers/ambiguity_scanner.py +0 -0
  68. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/analyzers/code_analyzer.py +0 -0
  69. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/analyzers/constitution_evidence_extractor.py +0 -0
  70. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/analyzers/contract_extractor.py +0 -0
  71. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/analyzers/control_flow_analyzer.py +0 -0
  72. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/analyzers/graph_analyzer.py +0 -0
  73. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/analyzers/relationship_mapper.py +0 -0
  74. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/analyzers/requirement_extractor.py +0 -0
  75. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/analyzers/test_pattern_extractor.py +0 -0
  76. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/backlog/__init__.py +0 -0
  77. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/backlog/adapters/__init__.py +0 -0
  78. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/backlog/adapters/base.py +0 -0
  79. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/backlog/adapters/local_yaml_adapter.py +0 -0
  80. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/backlog/ai_refiner.py +0 -0
  81. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/backlog/converter.py +0 -0
  82. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/backlog/filters.py +0 -0
  83. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/backlog/format_detector.py +0 -0
  84. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/backlog/formats/__init__.py +0 -0
  85. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/backlog/formats/base.py +0 -0
  86. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/backlog/formats/markdown_format.py +0 -0
  87. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/backlog/formats/structured_format.py +0 -0
  88. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/backlog/mappers/__init__.py +0 -0
  89. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/backlog/mappers/ado_mapper.py +0 -0
  90. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/backlog/mappers/base.py +0 -0
  91. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/backlog/mappers/github_mapper.py +0 -0
  92. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/backlog/mappers/template_config.py +0 -0
  93. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/backlog/template_detector.py +0 -0
  94. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/cli.py +0 -0
  95. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/commands/__init__.py +0 -0
  96. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/commands/analyze.py +0 -0
  97. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/commands/auth.py +0 -0
  98. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/commands/backlog_commands.py +0 -0
  99. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/commands/contract_cmd.py +0 -0
  100. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/commands/drift.py +0 -0
  101. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/commands/enforce.py +0 -0
  102. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/commands/generate.py +0 -0
  103. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/commands/import_cmd.py +0 -0
  104. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/commands/init.py +0 -0
  105. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/commands/migrate.py +0 -0
  106. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/commands/plan.py +0 -0
  107. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/commands/project_cmd.py +0 -0
  108. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/commands/repro.py +0 -0
  109. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/commands/sdd.py +0 -0
  110. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/commands/spec.py +0 -0
  111. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/commands/sync.py +0 -0
  112. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/commands/update.py +0 -0
  113. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/commands/validate.py +0 -0
  114. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/common/__init__.py +0 -0
  115. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/common/logger_setup.py +0 -0
  116. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/common/logging_utils.py +0 -0
  117. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/common/text_utils.py +0 -0
  118. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/common/utils.py +0 -0
  119. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/comparators/__init__.py +0 -0
  120. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/comparators/plan_comparator.py +0 -0
  121. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/contracts/__init__.py +0 -0
  122. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/contracts/crosshair_props.py +0 -0
  123. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/contracts/module_interface.py +0 -0
  124. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/enrichers/constitution_enricher.py +0 -0
  125. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/enrichers/plan_enricher.py +0 -0
  126. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/generators/__init__.py +0 -0
  127. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/generators/contract_generator.py +0 -0
  128. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/generators/openapi_extractor.py +0 -0
  129. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/generators/persona_exporter.py +0 -0
  130. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/generators/plan_generator.py +0 -0
  131. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/generators/protocol_generator.py +0 -0
  132. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/generators/report_generator.py +0 -0
  133. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/generators/task_generator.py +0 -0
  134. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/generators/test_to_openapi.py +0 -0
  135. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/generators/workflow_generator.py +0 -0
  136. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/importers/__init__.py +0 -0
  137. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/importers/speckit_converter.py +0 -0
  138. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/importers/speckit_scanner.py +0 -0
  139. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/integrations/__init__.py +0 -0
  140. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/integrations/specmatic.py +0 -0
  141. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/merge/__init__.py +0 -0
  142. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/merge/resolver.py +0 -0
  143. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/migrations/__init__.py +0 -0
  144. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/migrations/plan_migrator.py +0 -0
  145. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/models/__init__.py +0 -0
  146. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/models/backlog_item.py +0 -0
  147. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/models/bridge.py +0 -0
  148. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/models/capabilities.py +0 -0
  149. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/models/change.py +0 -0
  150. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/models/contract.py +0 -0
  151. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/models/deviation.py +0 -0
  152. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/models/dor_config.py +0 -0
  153. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/models/enforcement.py +0 -0
  154. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/models/module_package.py +0 -0
  155. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/models/persona_template.py +0 -0
  156. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/models/plan.py +0 -0
  157. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/models/project.py +0 -0
  158. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/models/protocol.py +0 -0
  159. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/models/quality.py +0 -0
  160. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/models/sdd.py +0 -0
  161. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/models/source_tracking.py +0 -0
  162. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/models/task.py +0 -0
  163. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/models/validation.py +0 -0
  164. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/modes/__init__.py +0 -0
  165. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/modes/detector.py +0 -0
  166. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/modes/router.py +0 -0
  167. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/modules/__init__.py +0 -0
  168. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/modules/analyze/module-package.yaml +0 -0
  169. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/modules/analyze/src/__init__.py +0 -0
  170. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/modules/analyze/src/app.py +0 -0
  171. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/modules/analyze/src/commands.py +0 -0
  172. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/modules/auth/module-package.yaml +0 -0
  173. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/modules/auth/src/__init__.py +0 -0
  174. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/modules/auth/src/app.py +0 -0
  175. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/modules/auth/src/commands.py +0 -0
  176. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/modules/backlog/module-package.yaml +0 -0
  177. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/modules/backlog/src/__init__.py +0 -0
  178. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/modules/backlog/src/adapters/__init__.py +0 -0
  179. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/modules/backlog/src/adapters/ado.py +0 -0
  180. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/modules/backlog/src/adapters/base.py +0 -0
  181. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/modules/backlog/src/adapters/github.py +0 -0
  182. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/modules/backlog/src/adapters/jira.py +0 -0
  183. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/modules/backlog/src/adapters/linear.py +0 -0
  184. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/modules/backlog/src/app.py +0 -0
  185. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/modules/contract/module-package.yaml +0 -0
  186. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/modules/contract/src/__init__.py +0 -0
  187. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/modules/contract/src/app.py +0 -0
  188. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/modules/contract/src/commands.py +0 -0
  189. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/modules/drift/module-package.yaml +0 -0
  190. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/modules/drift/src/__init__.py +0 -0
  191. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/modules/drift/src/app.py +0 -0
  192. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/modules/drift/src/commands.py +0 -0
  193. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/modules/enforce/module-package.yaml +0 -0
  194. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/modules/enforce/src/__init__.py +0 -0
  195. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/modules/enforce/src/app.py +0 -0
  196. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/modules/enforce/src/commands.py +0 -0
  197. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/modules/generate/module-package.yaml +0 -0
  198. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/modules/generate/src/__init__.py +0 -0
  199. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/modules/generate/src/app.py +0 -0
  200. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/modules/generate/src/commands.py +0 -0
  201. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/modules/import_cmd/module-package.yaml +0 -0
  202. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/modules/import_cmd/src/__init__.py +0 -0
  203. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/modules/import_cmd/src/app.py +0 -0
  204. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/modules/import_cmd/src/commands.py +0 -0
  205. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/modules/init/module-package.yaml +0 -0
  206. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/modules/init/src/__init__.py +0 -0
  207. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/modules/init/src/app.py +0 -0
  208. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/modules/init/src/commands.py +0 -0
  209. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/modules/migrate/module-package.yaml +0 -0
  210. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/modules/migrate/src/__init__.py +0 -0
  211. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/modules/migrate/src/app.py +0 -0
  212. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/modules/migrate/src/commands.py +0 -0
  213. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/modules/module_io_shim.py +0 -0
  214. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/modules/plan/module-package.yaml +0 -0
  215. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/modules/plan/src/__init__.py +0 -0
  216. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/modules/plan/src/app.py +0 -0
  217. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/modules/plan/src/commands.py +0 -0
  218. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/modules/project/module-package.yaml +0 -0
  219. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/modules/project/src/__init__.py +0 -0
  220. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/modules/project/src/app.py +0 -0
  221. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/modules/project/src/commands.py +0 -0
  222. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/modules/repro/module-package.yaml +0 -0
  223. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/modules/repro/src/__init__.py +0 -0
  224. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/modules/repro/src/app.py +0 -0
  225. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/modules/repro/src/commands.py +0 -0
  226. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/modules/sdd/module-package.yaml +0 -0
  227. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/modules/sdd/src/__init__.py +0 -0
  228. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/modules/sdd/src/app.py +0 -0
  229. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/modules/sdd/src/commands.py +0 -0
  230. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/modules/spec/module-package.yaml +0 -0
  231. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/modules/spec/src/__init__.py +0 -0
  232. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/modules/spec/src/app.py +0 -0
  233. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/modules/spec/src/commands.py +0 -0
  234. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/modules/sync/module-package.yaml +0 -0
  235. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/modules/sync/src/__init__.py +0 -0
  236. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/modules/sync/src/app.py +0 -0
  237. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/modules/sync/src/commands.py +0 -0
  238. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/modules/upgrade/module-package.yaml +0 -0
  239. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/modules/upgrade/src/__init__.py +0 -0
  240. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/modules/upgrade/src/app.py +0 -0
  241. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/modules/upgrade/src/commands.py +0 -0
  242. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/modules/validate/module-package.yaml +0 -0
  243. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/modules/validate/src/__init__.py +0 -0
  244. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/modules/validate/src/app.py +0 -0
  245. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/modules/validate/src/commands.py +0 -0
  246. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/parsers/__init__.py +0 -0
  247. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/parsers/persona_importer.py +0 -0
  248. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/registry/__init__.py +0 -0
  249. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/registry/bootstrap.py +0 -0
  250. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/registry/bridge_registry.py +0 -0
  251. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/registry/help_cache.py +0 -0
  252. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/registry/metadata.py +0 -0
  253. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/registry/module_packages.py +0 -0
  254. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/registry/module_state.py +0 -0
  255. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/registry/registry.py +0 -0
  256. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/resources/semgrep/async.yml +0 -0
  257. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/resources/semgrep/code-quality.yml +0 -0
  258. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/resources/semgrep/feature-detection.yml +0 -0
  259. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/runtime.py +0 -0
  260. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/sync/__init__.py +0 -0
  261. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/sync/bridge_probe.py +0 -0
  262. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/sync/bridge_sync.py +0 -0
  263. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/sync/bridge_watch.py +0 -0
  264. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/sync/change_detector.py +0 -0
  265. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/sync/code_to_spec.py +0 -0
  266. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/sync/drift_detector.py +0 -0
  267. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/sync/repository_sync.py +0 -0
  268. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/sync/spec_to_code.py +0 -0
  269. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/sync/spec_to_tests.py +0 -0
  270. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/sync/watcher.py +0 -0
  271. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/sync/watcher_enhanced.py +0 -0
  272. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/telemetry.py +0 -0
  273. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/templates/__init__.py +0 -0
  274. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/templates/bridge_templates.py +0 -0
  275. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/templates/defaults/defect_v1.yaml +0 -0
  276. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/templates/defaults/enabler_v1.yaml +0 -0
  277. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/templates/defaults/spike_v1.yaml +0 -0
  278. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/templates/defaults/user_story_v1.yaml +0 -0
  279. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/templates/frameworks/scrum/user_story_v1.yaml +0 -0
  280. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/templates/personas/product-owner/user_story_v1.yaml +0 -0
  281. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/templates/providers/ado/work_item_v1.yaml +0 -0
  282. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/templates/registry.py +0 -0
  283. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/templates/specification_templates.py +0 -0
  284. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/utils/__init__.py +0 -0
  285. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/utils/acceptance_criteria.py +0 -0
  286. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/utils/auth_tokens.py +0 -0
  287. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/utils/bundle_converters.py +0 -0
  288. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/utils/bundle_loader.py +0 -0
  289. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/utils/code_change_detector.py +0 -0
  290. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/utils/console.py +0 -0
  291. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/utils/content_sanitizer.py +0 -0
  292. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/utils/context_detection.py +0 -0
  293. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/utils/enrichment_context.py +0 -0
  294. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/utils/enrichment_parser.py +0 -0
  295. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/utils/env_manager.py +0 -0
  296. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/utils/feature_keys.py +0 -0
  297. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/utils/git.py +0 -0
  298. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/utils/github_annotations.py +0 -0
  299. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/utils/ide_setup.py +0 -0
  300. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/utils/incremental_check.py +0 -0
  301. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/utils/metadata.py +0 -0
  302. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/utils/optional_deps.py +0 -0
  303. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/utils/performance.py +0 -0
  304. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/utils/persona_ownership.py +0 -0
  305. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/utils/progress.py +0 -0
  306. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/utils/progressive_disclosure.py +0 -0
  307. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/utils/prompts.py +0 -0
  308. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/utils/sdd_discovery.py +0 -0
  309. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/utils/source_scanner.py +0 -0
  310. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/utils/startup_checks.py +0 -0
  311. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/utils/structure.py +0 -0
  312. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/utils/structured_io.py +0 -0
  313. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/utils/suggestions.py +0 -0
  314. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/utils/terminal.py +0 -0
  315. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/utils/yaml_utils.py +0 -0
  316. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/validators/__init__.py +0 -0
  317. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/validators/agile_validation.py +0 -0
  318. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/validators/change_proposal_integration.py +0 -0
  319. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/validators/cli_first_validator.py +0 -0
  320. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/validators/contract_validator.py +0 -0
  321. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/validators/fsm.py +0 -0
  322. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/validators/repro_checker.py +0 -0
  323. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/validators/schema.py +0 -0
  324. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/validators/sidecar/__init__.py +0 -0
  325. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/validators/sidecar/contract_populator.py +0 -0
  326. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/validators/sidecar/crosshair_runner.py +0 -0
  327. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/validators/sidecar/crosshair_summary.py +0 -0
  328. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/validators/sidecar/dependency_installer.py +0 -0
  329. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/validators/sidecar/framework_detector.py +0 -0
  330. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/validators/sidecar/frameworks/__init__.py +0 -0
  331. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/validators/sidecar/frameworks/base.py +0 -0
  332. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/validators/sidecar/frameworks/django.py +0 -0
  333. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/validators/sidecar/frameworks/drf.py +0 -0
  334. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/validators/sidecar/frameworks/fastapi.py +0 -0
  335. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/validators/sidecar/frameworks/flask.py +0 -0
  336. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/validators/sidecar/harness_generator.py +0 -0
  337. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/validators/sidecar/models.py +0 -0
  338. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/validators/sidecar/orchestrator.py +0 -0
  339. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/validators/sidecar/specmatic_runner.py +0 -0
  340. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/validators/sidecar/unannotated_detector.py +0 -0
  341. {specfact_cli-0.30.2 → specfact_cli-0.30.3}/src/specfact_cli/versioning/__init__.py +0 -0
  342. {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.2
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.2"
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"
@@ -3,4 +3,4 @@ SpecFact CLI - Spec→Contract→Sentinel tool for contract-driven development.
3
3
  """
4
4
 
5
5
  # Package version: keep in sync with pyproject.toml, setup.py, src/specfact_cli/__init__.py
6
- __version__ = "0.30.1"
6
+ __version__ = "0.30.3"
@@ -8,6 +8,6 @@ This package provides command-line tools for:
8
8
  - Supporting agile ceremonies and team workflows
9
9
  """
10
10
 
11
- __version__ = "0.30.2"
11
+ __version__ = "0.30.3"
12
12
 
13
13
  __all__ = ["__version__"]
@@ -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
- # Use extracted sections from refined body (these are the refined values)
2767
- "acceptance_criteria": existing_acceptance_criteria,
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 None
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 None
2777
+ else item.business_value
2777
2778
  ),
2778
2779
  "priority": (
2779
- int(existing_priority) if existing_priority and existing_priority.strip().isdigit() else None
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,
@@ -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
- while True:
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
- # Check if session was cancelled
2950
- if cancelled:
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
- # Store refined body for preview/write
3027
- item.refined_body = refinement_result.refined_body
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
- # Auto-accept high confidence
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
- item.apply_refinement()
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
- # Writeback to remote backlog using adapter
3044
- # Build adapter kwargs for writeback
3045
- writeback_kwargs = _build_adapter_kwargs(
3046
- adapter,
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
- # Interactive prompt with clear separation
3099
- console.print()
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