specfact-cli 0.30.4__tar.gz → 0.31.1__tar.gz

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