specfact-cli 0.46.18__tar.gz → 0.46.25__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 (278) hide show
  1. {specfact_cli-0.46.18 → specfact_cli-0.46.25}/.gitignore +1 -0
  2. {specfact_cli-0.46.18 → specfact_cli-0.46.25}/PKG-INFO +6 -4
  3. {specfact_cli-0.46.18 → specfact_cli-0.46.25}/pyproject.toml +9 -6
  4. {specfact_cli-0.46.18 → specfact_cli-0.46.25}/src/__init__.py +1 -1
  5. {specfact_cli-0.46.18 → specfact_cli-0.46.25}/src/specfact_cli/__init__.py +1 -1
  6. {specfact_cli-0.46.18 → specfact_cli-0.46.25}/src/specfact_cli/adapters/ado.py +13 -11
  7. {specfact_cli-0.46.18 → specfact_cli-0.46.25}/src/specfact_cli/adapters/github.py +21 -17
  8. {specfact_cli-0.46.18 → specfact_cli-0.46.25}/src/specfact_cli/cli.py +7 -0
  9. {specfact_cli-0.46.18 → specfact_cli-0.46.25}/src/specfact_cli/models/project.py +7 -1
  10. {specfact_cli-0.46.18 → specfact_cli-0.46.25}/src/specfact_cli/modules/init/module-package.yaml +3 -3
  11. {specfact_cli-0.46.18 → specfact_cli-0.46.25}/src/specfact_cli/modules/init/src/commands.py +29 -3
  12. {specfact_cli-0.46.18 → specfact_cli-0.46.25}/src/specfact_cli/modules/module_registry/module-package.yaml +3 -3
  13. {specfact_cli-0.46.18 → specfact_cli-0.46.25}/src/specfact_cli/modules/module_registry/src/commands.py +111 -1
  14. {specfact_cli-0.46.18 → specfact_cli-0.46.25}/src/specfact_cli/registry/module_availability.py +11 -1
  15. {specfact_cli-0.46.18 → specfact_cli-0.46.25}/src/specfact_cli/registry/module_installer.py +91 -17
  16. {specfact_cli-0.46.18 → specfact_cli-0.46.25}/src/specfact_cli/registry/module_packages.py +247 -17
  17. {specfact_cli-0.46.18 → specfact_cli-0.46.25}/src/specfact_cli/registry/module_state.py +11 -2
  18. {specfact_cli-0.46.18 → specfact_cli-0.46.25}/src/specfact_cli/sync/bridge_sync.py +2 -2
  19. {specfact_cli-0.46.18 → specfact_cli-0.46.25}/src/specfact_cli/utils/env_manager.py +157 -48
  20. {specfact_cli-0.46.18 → specfact_cli-0.46.25}/src/specfact_cli/utils/ide_setup.py +5 -1
  21. {specfact_cli-0.46.18 → specfact_cli-0.46.25}/src/specfact_cli/utils/yaml_utils.py +23 -9
  22. {specfact_cli-0.46.18 → specfact_cli-0.46.25}/src/specfact_cli/validators/change_proposal_integration.py +3 -3
  23. {specfact_cli-0.46.18 → specfact_cli-0.46.25}/LICENSE +0 -0
  24. {specfact_cli-0.46.18 → specfact_cli-0.46.25}/README.md +0 -0
  25. {specfact_cli-0.46.18 → specfact_cli-0.46.25}/resources/bundled-module-registry/index.json +0 -0
  26. {specfact_cli-0.46.18 → specfact_cli-0.46.25}/resources/keys/README.md +0 -0
  27. {specfact_cli-0.46.18 → specfact_cli-0.46.25}/resources/keys/module-signing-public.pem +0 -0
  28. {specfact_cli-0.46.18 → specfact_cli-0.46.25}/resources/mappings/node-async.yaml +0 -0
  29. {specfact_cli-0.46.18 → specfact_cli-0.46.25}/resources/mappings/python-async.yaml +0 -0
  30. {specfact_cli-0.46.18 → specfact_cli-0.46.25}/resources/mappings/speckit-default.yaml +0 -0
  31. {specfact_cli-0.46.18 → specfact_cli-0.46.25}/resources/schemas/deviation.schema.json +0 -0
  32. {specfact_cli-0.46.18 → specfact_cli-0.46.25}/resources/schemas/plan.schema.json +0 -0
  33. {specfact_cli-0.46.18 → specfact_cli-0.46.25}/resources/schemas/protocol.schema.json +0 -0
  34. {specfact_cli-0.46.18 → specfact_cli-0.46.25}/resources/templates/github-action.yml.j2 +0 -0
  35. {specfact_cli-0.46.18 → specfact_cli-0.46.25}/resources/templates/persona/architect.md.j2 +0 -0
  36. {specfact_cli-0.46.18 → specfact_cli-0.46.25}/resources/templates/persona/developer.md.j2 +0 -0
  37. {specfact_cli-0.46.18 → specfact_cli-0.46.25}/resources/templates/persona/product-owner.md.j2 +0 -0
  38. {specfact_cli-0.46.18 → specfact_cli-0.46.25}/resources/templates/plan.bundle.yaml.j2 +0 -0
  39. {specfact_cli-0.46.18 → specfact_cli-0.46.25}/resources/templates/policies/kanban.yaml +0 -0
  40. {specfact_cli-0.46.18 → specfact_cli-0.46.25}/resources/templates/policies/mixed.yaml +0 -0
  41. {specfact_cli-0.46.18 → specfact_cli-0.46.25}/resources/templates/policies/safe.yaml +0 -0
  42. {specfact_cli-0.46.18 → specfact_cli-0.46.25}/resources/templates/policies/scrum.yaml +0 -0
  43. {specfact_cli-0.46.18 → specfact_cli-0.46.25}/resources/templates/pr-template.md.j2 +0 -0
  44. {specfact_cli-0.46.18 → specfact_cli-0.46.25}/resources/templates/protocol.yaml.j2 +0 -0
  45. {specfact_cli-0.46.18 → specfact_cli-0.46.25}/resources/templates/telemetry.yaml.example +0 -0
  46. {specfact_cli-0.46.18 → specfact_cli-0.46.25}/src/specfact_cli/__main__.py +0 -0
  47. {specfact_cli-0.46.18 → specfact_cli-0.46.25}/src/specfact_cli/adapters/__init__.py +0 -0
  48. {specfact_cli-0.46.18 → specfact_cli-0.46.25}/src/specfact_cli/adapters/backlog_base.py +0 -0
  49. {specfact_cli-0.46.18 → specfact_cli-0.46.25}/src/specfact_cli/adapters/base.py +0 -0
  50. {specfact_cli-0.46.18 → specfact_cli-0.46.25}/src/specfact_cli/adapters/openspec.py +0 -0
  51. {specfact_cli-0.46.18 → specfact_cli-0.46.25}/src/specfact_cli/adapters/openspec_parser.py +0 -0
  52. {specfact_cli-0.46.18 → specfact_cli-0.46.25}/src/specfact_cli/adapters/registry.py +0 -0
  53. {specfact_cli-0.46.18 → specfact_cli-0.46.25}/src/specfact_cli/adapters/speckit.py +0 -0
  54. {specfact_cli-0.46.18 → specfact_cli-0.46.25}/src/specfact_cli/agents/__init__.py +0 -0
  55. {specfact_cli-0.46.18 → specfact_cli-0.46.25}/src/specfact_cli/agents/analyze_agent.py +0 -0
  56. {specfact_cli-0.46.18 → specfact_cli-0.46.25}/src/specfact_cli/agents/base.py +0 -0
  57. {specfact_cli-0.46.18 → specfact_cli-0.46.25}/src/specfact_cli/agents/plan_agent.py +0 -0
  58. {specfact_cli-0.46.18 → specfact_cli-0.46.25}/src/specfact_cli/agents/registry.py +0 -0
  59. {specfact_cli-0.46.18 → specfact_cli-0.46.25}/src/specfact_cli/agents/sync_agent.py +0 -0
  60. {specfact_cli-0.46.18 → specfact_cli-0.46.25}/src/specfact_cli/analyzers/__init__.py +0 -0
  61. {specfact_cli-0.46.18 → specfact_cli-0.46.25}/src/specfact_cli/analyzers/ambiguity_scanner.py +0 -0
  62. {specfact_cli-0.46.18 → specfact_cli-0.46.25}/src/specfact_cli/analyzers/code_analyzer.py +0 -0
  63. {specfact_cli-0.46.18 → specfact_cli-0.46.25}/src/specfact_cli/analyzers/constitution_evidence_extractor.py +0 -0
  64. {specfact_cli-0.46.18 → specfact_cli-0.46.25}/src/specfact_cli/analyzers/contract_extractor.py +0 -0
  65. {specfact_cli-0.46.18 → specfact_cli-0.46.25}/src/specfact_cli/analyzers/control_flow_analyzer.py +0 -0
  66. {specfact_cli-0.46.18 → specfact_cli-0.46.25}/src/specfact_cli/analyzers/graph_analyzer.py +0 -0
  67. {specfact_cli-0.46.18 → specfact_cli-0.46.25}/src/specfact_cli/analyzers/relationship_mapper.py +0 -0
  68. {specfact_cli-0.46.18 → specfact_cli-0.46.25}/src/specfact_cli/analyzers/requirement_extractor.py +0 -0
  69. {specfact_cli-0.46.18 → specfact_cli-0.46.25}/src/specfact_cli/analyzers/test_pattern_extractor.py +0 -0
  70. {specfact_cli-0.46.18 → specfact_cli-0.46.25}/src/specfact_cli/backlog/__init__.py +0 -0
  71. {specfact_cli-0.46.18 → specfact_cli-0.46.25}/src/specfact_cli/backlog/adapters/__init__.py +0 -0
  72. {specfact_cli-0.46.18 → specfact_cli-0.46.25}/src/specfact_cli/backlog/adapters/base.py +0 -0
  73. {specfact_cli-0.46.18 → specfact_cli-0.46.25}/src/specfact_cli/backlog/converter.py +0 -0
  74. {specfact_cli-0.46.18 → specfact_cli-0.46.25}/src/specfact_cli/backlog/filters.py +0 -0
  75. {specfact_cli-0.46.18 → specfact_cli-0.46.25}/src/specfact_cli/backlog/mappers/__init__.py +0 -0
  76. {specfact_cli-0.46.18 → specfact_cli-0.46.25}/src/specfact_cli/backlog/mappers/ado_mapper.py +0 -0
  77. {specfact_cli-0.46.18 → specfact_cli-0.46.25}/src/specfact_cli/backlog/mappers/base.py +0 -0
  78. {specfact_cli-0.46.18 → specfact_cli-0.46.25}/src/specfact_cli/backlog/mappers/github_mapper.py +0 -0
  79. {specfact_cli-0.46.18 → specfact_cli-0.46.25}/src/specfact_cli/backlog/mappers/template_config.py +0 -0
  80. {specfact_cli-0.46.18 → specfact_cli-0.46.25}/src/specfact_cli/commands/__init__.py +0 -0
  81. {specfact_cli-0.46.18 → specfact_cli-0.46.25}/src/specfact_cli/commands/_bundle_shim.py +0 -0
  82. {specfact_cli-0.46.18 → specfact_cli-0.46.25}/src/specfact_cli/commands/analyze.py +0 -0
  83. {specfact_cli-0.46.18 → specfact_cli-0.46.25}/src/specfact_cli/commands/contract_cmd.py +0 -0
  84. {specfact_cli-0.46.18 → specfact_cli-0.46.25}/src/specfact_cli/commands/drift.py +0 -0
  85. {specfact_cli-0.46.18 → specfact_cli-0.46.25}/src/specfact_cli/commands/enforce.py +0 -0
  86. {specfact_cli-0.46.18 → specfact_cli-0.46.25}/src/specfact_cli/commands/generate.py +0 -0
  87. {specfact_cli-0.46.18 → specfact_cli-0.46.25}/src/specfact_cli/commands/import_cmd.py +0 -0
  88. {specfact_cli-0.46.18 → specfact_cli-0.46.25}/src/specfact_cli/commands/init.py +0 -0
  89. {specfact_cli-0.46.18 → specfact_cli-0.46.25}/src/specfact_cli/commands/migrate.py +0 -0
  90. {specfact_cli-0.46.18 → specfact_cli-0.46.25}/src/specfact_cli/commands/plan.py +0 -0
  91. {specfact_cli-0.46.18 → specfact_cli-0.46.25}/src/specfact_cli/commands/project_cmd.py +0 -0
  92. {specfact_cli-0.46.18 → specfact_cli-0.46.25}/src/specfact_cli/commands/repro.py +0 -0
  93. {specfact_cli-0.46.18 → specfact_cli-0.46.25}/src/specfact_cli/commands/sdd.py +0 -0
  94. {specfact_cli-0.46.18 → specfact_cli-0.46.25}/src/specfact_cli/commands/spec.py +0 -0
  95. {specfact_cli-0.46.18 → specfact_cli-0.46.25}/src/specfact_cli/commands/sync.py +0 -0
  96. {specfact_cli-0.46.18 → specfact_cli-0.46.25}/src/specfact_cli/commands/update.py +0 -0
  97. {specfact_cli-0.46.18 → specfact_cli-0.46.25}/src/specfact_cli/commands/validate.py +0 -0
  98. {specfact_cli-0.46.18 → specfact_cli-0.46.25}/src/specfact_cli/common/__init__.py +0 -0
  99. {specfact_cli-0.46.18 → specfact_cli-0.46.25}/src/specfact_cli/common/bundle_factory.py +0 -0
  100. {specfact_cli-0.46.18 → specfact_cli-0.46.25}/src/specfact_cli/common/logger_setup.py +0 -0
  101. {specfact_cli-0.46.18 → specfact_cli-0.46.25}/src/specfact_cli/common/logging_utils.py +0 -0
  102. {specfact_cli-0.46.18 → specfact_cli-0.46.25}/src/specfact_cli/common/text_utils.py +0 -0
  103. {specfact_cli-0.46.18 → specfact_cli-0.46.25}/src/specfact_cli/common/utils.py +0 -0
  104. {specfact_cli-0.46.18 → specfact_cli-0.46.25}/src/specfact_cli/comparators/__init__.py +0 -0
  105. {specfact_cli-0.46.18 → specfact_cli-0.46.25}/src/specfact_cli/comparators/plan_comparator.py +0 -0
  106. {specfact_cli-0.46.18 → specfact_cli-0.46.25}/src/specfact_cli/contracts/__init__.py +0 -0
  107. {specfact_cli-0.46.18 → specfact_cli-0.46.25}/src/specfact_cli/contracts/crosshair_props.py +0 -0
  108. {specfact_cli-0.46.18 → specfact_cli-0.46.25}/src/specfact_cli/contracts/module_interface.py +0 -0
  109. {specfact_cli-0.46.18 → specfact_cli-0.46.25}/src/specfact_cli/enrichers/constitution_enricher.py +0 -0
  110. {specfact_cli-0.46.18 → specfact_cli-0.46.25}/src/specfact_cli/enrichers/plan_enricher.py +0 -0
  111. {specfact_cli-0.46.18 → specfact_cli-0.46.25}/src/specfact_cli/generators/__init__.py +0 -0
  112. {specfact_cli-0.46.18 → specfact_cli-0.46.25}/src/specfact_cli/generators/contract_generator.py +0 -0
  113. {specfact_cli-0.46.18 → specfact_cli-0.46.25}/src/specfact_cli/generators/openapi_extractor.py +0 -0
  114. {specfact_cli-0.46.18 → specfact_cli-0.46.25}/src/specfact_cli/generators/persona_exporter.py +0 -0
  115. {specfact_cli-0.46.18 → specfact_cli-0.46.25}/src/specfact_cli/generators/plan_generator.py +0 -0
  116. {specfact_cli-0.46.18 → specfact_cli-0.46.25}/src/specfact_cli/generators/protocol_generator.py +0 -0
  117. {specfact_cli-0.46.18 → specfact_cli-0.46.25}/src/specfact_cli/generators/report_generator.py +0 -0
  118. {specfact_cli-0.46.18 → specfact_cli-0.46.25}/src/specfact_cli/generators/task_generator.py +0 -0
  119. {specfact_cli-0.46.18 → specfact_cli-0.46.25}/src/specfact_cli/generators/test_to_openapi.py +0 -0
  120. {specfact_cli-0.46.18 → specfact_cli-0.46.25}/src/specfact_cli/generators/workflow_generator.py +0 -0
  121. {specfact_cli-0.46.18 → specfact_cli-0.46.25}/src/specfact_cli/groups/__init__.py +0 -0
  122. {specfact_cli-0.46.18 → specfact_cli-0.46.25}/src/specfact_cli/groups/codebase_group.py +0 -0
  123. {specfact_cli-0.46.18 → specfact_cli-0.46.25}/src/specfact_cli/groups/govern_group.py +0 -0
  124. {specfact_cli-0.46.18 → specfact_cli-0.46.25}/src/specfact_cli/groups/member_group.py +0 -0
  125. {specfact_cli-0.46.18 → specfact_cli-0.46.25}/src/specfact_cli/groups/project_group.py +0 -0
  126. {specfact_cli-0.46.18 → specfact_cli-0.46.25}/src/specfact_cli/groups/spec_group.py +0 -0
  127. {specfact_cli-0.46.18 → specfact_cli-0.46.25}/src/specfact_cli/importers/__init__.py +0 -0
  128. {specfact_cli-0.46.18 → specfact_cli-0.46.25}/src/specfact_cli/importers/speckit_converter.py +0 -0
  129. {specfact_cli-0.46.18 → specfact_cli-0.46.25}/src/specfact_cli/importers/speckit_scanner.py +0 -0
  130. {specfact_cli-0.46.18 → specfact_cli-0.46.25}/src/specfact_cli/integrations/__init__.py +0 -0
  131. {specfact_cli-0.46.18 → specfact_cli-0.46.25}/src/specfact_cli/integrations/specmatic.py +0 -0
  132. {specfact_cli-0.46.18 → specfact_cli-0.46.25}/src/specfact_cli/merge/__init__.py +0 -0
  133. {specfact_cli-0.46.18 → specfact_cli-0.46.25}/src/specfact_cli/merge/resolver.py +0 -0
  134. {specfact_cli-0.46.18 → specfact_cli-0.46.25}/src/specfact_cli/migrations/__init__.py +0 -0
  135. {specfact_cli-0.46.18 → specfact_cli-0.46.25}/src/specfact_cli/migrations/plan_migrator.py +0 -0
  136. {specfact_cli-0.46.18 → specfact_cli-0.46.25}/src/specfact_cli/models/__init__.py +0 -0
  137. {specfact_cli-0.46.18 → specfact_cli-0.46.25}/src/specfact_cli/models/backlog_item.py +0 -0
  138. {specfact_cli-0.46.18 → specfact_cli-0.46.25}/src/specfact_cli/models/bridge.py +0 -0
  139. {specfact_cli-0.46.18 → specfact_cli-0.46.25}/src/specfact_cli/models/capabilities.py +0 -0
  140. {specfact_cli-0.46.18 → specfact_cli-0.46.25}/src/specfact_cli/models/change.py +0 -0
  141. {specfact_cli-0.46.18 → specfact_cli-0.46.25}/src/specfact_cli/models/contract.py +0 -0
  142. {specfact_cli-0.46.18 → specfact_cli-0.46.25}/src/specfact_cli/models/deviation.py +0 -0
  143. {specfact_cli-0.46.18 → specfact_cli-0.46.25}/src/specfact_cli/models/dor_config.py +0 -0
  144. {specfact_cli-0.46.18 → specfact_cli-0.46.25}/src/specfact_cli/models/enforcement.py +0 -0
  145. {specfact_cli-0.46.18 → specfact_cli-0.46.25}/src/specfact_cli/models/module_package.py +0 -0
  146. {specfact_cli-0.46.18 → specfact_cli-0.46.25}/src/specfact_cli/models/persona_template.py +0 -0
  147. {specfact_cli-0.46.18 → specfact_cli-0.46.25}/src/specfact_cli/models/plan.py +0 -0
  148. {specfact_cli-0.46.18 → specfact_cli-0.46.25}/src/specfact_cli/models/protocol.py +0 -0
  149. {specfact_cli-0.46.18 → specfact_cli-0.46.25}/src/specfact_cli/models/quality.py +0 -0
  150. {specfact_cli-0.46.18 → specfact_cli-0.46.25}/src/specfact_cli/models/sdd.py +0 -0
  151. {specfact_cli-0.46.18 → specfact_cli-0.46.25}/src/specfact_cli/models/source_tracking.py +0 -0
  152. {specfact_cli-0.46.18 → specfact_cli-0.46.25}/src/specfact_cli/models/task.py +0 -0
  153. {specfact_cli-0.46.18 → specfact_cli-0.46.25}/src/specfact_cli/models/validation.py +0 -0
  154. {specfact_cli-0.46.18 → specfact_cli-0.46.25}/src/specfact_cli/modes/__init__.py +0 -0
  155. {specfact_cli-0.46.18 → specfact_cli-0.46.25}/src/specfact_cli/modes/detector.py +0 -0
  156. {specfact_cli-0.46.18 → specfact_cli-0.46.25}/src/specfact_cli/modes/router.py +0 -0
  157. {specfact_cli-0.46.18 → specfact_cli-0.46.25}/src/specfact_cli/modules/__init__.py +0 -0
  158. {specfact_cli-0.46.18 → specfact_cli-0.46.25}/src/specfact_cli/modules/_bundle_import.py +0 -0
  159. {specfact_cli-0.46.18 → specfact_cli-0.46.25}/src/specfact_cli/modules/init/src/__init__.py +0 -0
  160. {specfact_cli-0.46.18 → specfact_cli-0.46.25}/src/specfact_cli/modules/init/src/app.py +0 -0
  161. {specfact_cli-0.46.18 → specfact_cli-0.46.25}/src/specfact_cli/modules/init/src/first_run_selection.py +0 -0
  162. {specfact_cli-0.46.18 → specfact_cli-0.46.25}/src/specfact_cli/modules/module_io_shim.py +0 -0
  163. {specfact_cli-0.46.18 → specfact_cli-0.46.25}/src/specfact_cli/modules/module_registry/src/__init__.py +0 -0
  164. {specfact_cli-0.46.18 → specfact_cli-0.46.25}/src/specfact_cli/modules/module_registry/src/app.py +0 -0
  165. {specfact_cli-0.46.18 → specfact_cli-0.46.25}/src/specfact_cli/modules/upgrade/module-package.yaml +0 -0
  166. {specfact_cli-0.46.18 → specfact_cli-0.46.25}/src/specfact_cli/modules/upgrade/src/__init__.py +0 -0
  167. {specfact_cli-0.46.18 → specfact_cli-0.46.25}/src/specfact_cli/modules/upgrade/src/app.py +0 -0
  168. {specfact_cli-0.46.18 → specfact_cli-0.46.25}/src/specfact_cli/modules/upgrade/src/commands.py +0 -0
  169. {specfact_cli-0.46.18 → specfact_cli-0.46.25}/src/specfact_cli/parsers/__init__.py +0 -0
  170. {specfact_cli-0.46.18 → specfact_cli-0.46.25}/src/specfact_cli/parsers/persona_importer.py +0 -0
  171. {specfact_cli-0.46.18 → specfact_cli-0.46.25}/src/specfact_cli/registry/__init__.py +0 -0
  172. {specfact_cli-0.46.18 → specfact_cli-0.46.25}/src/specfact_cli/registry/alias_manager.py +0 -0
  173. {specfact_cli-0.46.18 → specfact_cli-0.46.25}/src/specfact_cli/registry/bootstrap.py +0 -0
  174. {specfact_cli-0.46.18 → specfact_cli-0.46.25}/src/specfact_cli/registry/bridge_registry.py +0 -0
  175. {specfact_cli-0.46.18 → specfact_cli-0.46.25}/src/specfact_cli/registry/crypto_validator.py +0 -0
  176. {specfact_cli-0.46.18 → specfact_cli-0.46.25}/src/specfact_cli/registry/custom_registries.py +0 -0
  177. {specfact_cli-0.46.18 → specfact_cli-0.46.25}/src/specfact_cli/registry/dependency_resolver.py +0 -0
  178. {specfact_cli-0.46.18 → specfact_cli-0.46.25}/src/specfact_cli/registry/extension_registry.py +0 -0
  179. {specfact_cli-0.46.18 → specfact_cli-0.46.25}/src/specfact_cli/registry/help_cache.py +0 -0
  180. {specfact_cli-0.46.18 → specfact_cli-0.46.25}/src/specfact_cli/registry/marketplace_client.py +0 -0
  181. {specfact_cli-0.46.18 → specfact_cli-0.46.25}/src/specfact_cli/registry/metadata.py +0 -0
  182. {specfact_cli-0.46.18 → specfact_cli-0.46.25}/src/specfact_cli/registry/module_discovery.py +0 -0
  183. {specfact_cli-0.46.18 → specfact_cli-0.46.25}/src/specfact_cli/registry/module_grouping.py +0 -0
  184. {specfact_cli-0.46.18 → specfact_cli-0.46.25}/src/specfact_cli/registry/module_lifecycle.py +0 -0
  185. {specfact_cli-0.46.18 → specfact_cli-0.46.25}/src/specfact_cli/registry/module_security.py +0 -0
  186. {specfact_cli-0.46.18 → specfact_cli-0.46.25}/src/specfact_cli/registry/registry.py +0 -0
  187. {specfact_cli-0.46.18 → specfact_cli-0.46.25}/src/specfact_cli/resources/semgrep/async.yml +0 -0
  188. {specfact_cli-0.46.18 → specfact_cli-0.46.25}/src/specfact_cli/resources/semgrep/code-quality.yml +0 -0
  189. {specfact_cli-0.46.18 → specfact_cli-0.46.25}/src/specfact_cli/resources/semgrep/feature-detection.yml +0 -0
  190. {specfact_cli-0.46.18 → specfact_cli-0.46.25}/src/specfact_cli/runtime.py +0 -0
  191. {specfact_cli-0.46.18 → specfact_cli-0.46.25}/src/specfact_cli/sync/__init__.py +0 -0
  192. {specfact_cli-0.46.18 → specfact_cli-0.46.25}/src/specfact_cli/sync/bridge_probe.py +0 -0
  193. {specfact_cli-0.46.18 → specfact_cli-0.46.25}/src/specfact_cli/sync/bridge_sync_openspec_md_parse.py +0 -0
  194. {specfact_cli-0.46.18 → specfact_cli-0.46.25}/src/specfact_cli/sync/bridge_sync_requirement_from_proposal.py +0 -0
  195. {specfact_cli-0.46.18 → specfact_cli-0.46.25}/src/specfact_cli/sync/bridge_sync_requirement_helpers.py +0 -0
  196. {specfact_cli-0.46.18 → specfact_cli-0.46.25}/src/specfact_cli/sync/bridge_sync_tasks_from_proposal.py +0 -0
  197. {specfact_cli-0.46.18 → specfact_cli-0.46.25}/src/specfact_cli/sync/bridge_sync_what_changes_format.py +0 -0
  198. {specfact_cli-0.46.18 → specfact_cli-0.46.25}/src/specfact_cli/sync/bridge_sync_write_openspec_from_proposal.py +0 -0
  199. {specfact_cli-0.46.18 → specfact_cli-0.46.25}/src/specfact_cli/sync/bridge_watch.py +0 -0
  200. {specfact_cli-0.46.18 → specfact_cli-0.46.25}/src/specfact_cli/sync/change_detector.py +0 -0
  201. {specfact_cli-0.46.18 → specfact_cli-0.46.25}/src/specfact_cli/sync/code_to_spec.py +0 -0
  202. {specfact_cli-0.46.18 → specfact_cli-0.46.25}/src/specfact_cli/sync/drift_detector.py +0 -0
  203. {specfact_cli-0.46.18 → specfact_cli-0.46.25}/src/specfact_cli/sync/repository_sync.py +0 -0
  204. {specfact_cli-0.46.18 → specfact_cli-0.46.25}/src/specfact_cli/sync/spec_to_code.py +0 -0
  205. {specfact_cli-0.46.18 → specfact_cli-0.46.25}/src/specfact_cli/sync/spec_to_tests.py +0 -0
  206. {specfact_cli-0.46.18 → specfact_cli-0.46.25}/src/specfact_cli/sync/watcher.py +0 -0
  207. {specfact_cli-0.46.18 → specfact_cli-0.46.25}/src/specfact_cli/sync/watcher_enhanced.py +0 -0
  208. {specfact_cli-0.46.18 → specfact_cli-0.46.25}/src/specfact_cli/telemetry.py +0 -0
  209. {specfact_cli-0.46.18 → specfact_cli-0.46.25}/src/specfact_cli/templates/__init__.py +0 -0
  210. {specfact_cli-0.46.18 → specfact_cli-0.46.25}/src/specfact_cli/templates/defaults/defect_v1.yaml +0 -0
  211. {specfact_cli-0.46.18 → specfact_cli-0.46.25}/src/specfact_cli/templates/defaults/enabler_v1.yaml +0 -0
  212. {specfact_cli-0.46.18 → specfact_cli-0.46.25}/src/specfact_cli/templates/defaults/spike_v1.yaml +0 -0
  213. {specfact_cli-0.46.18 → specfact_cli-0.46.25}/src/specfact_cli/templates/defaults/user_story_v1.yaml +0 -0
  214. {specfact_cli-0.46.18 → specfact_cli-0.46.25}/src/specfact_cli/templates/frameworks/scrum/user_story_v1.yaml +0 -0
  215. {specfact_cli-0.46.18 → specfact_cli-0.46.25}/src/specfact_cli/templates/personas/product-owner/user_story_v1.yaml +0 -0
  216. {specfact_cli-0.46.18 → specfact_cli-0.46.25}/src/specfact_cli/templates/providers/ado/work_item_v1.yaml +0 -0
  217. {specfact_cli-0.46.18 → specfact_cli-0.46.25}/src/specfact_cli/templates/registry.py +0 -0
  218. {specfact_cli-0.46.18 → specfact_cli-0.46.25}/src/specfact_cli/templates/specification_templates.py +0 -0
  219. {specfact_cli-0.46.18 → specfact_cli-0.46.25}/src/specfact_cli/utils/__init__.py +0 -0
  220. {specfact_cli-0.46.18 → specfact_cli-0.46.25}/src/specfact_cli/utils/acceptance_criteria.py +0 -0
  221. {specfact_cli-0.46.18 → specfact_cli-0.46.25}/src/specfact_cli/utils/auth_tokens.py +0 -0
  222. {specfact_cli-0.46.18 → specfact_cli-0.46.25}/src/specfact_cli/utils/bundle_converters.py +0 -0
  223. {specfact_cli-0.46.18 → specfact_cli-0.46.25}/src/specfact_cli/utils/bundle_loader.py +0 -0
  224. {specfact_cli-0.46.18 → specfact_cli-0.46.25}/src/specfact_cli/utils/code_change_detector.py +0 -0
  225. {specfact_cli-0.46.18 → specfact_cli-0.46.25}/src/specfact_cli/utils/console.py +0 -0
  226. {specfact_cli-0.46.18 → specfact_cli-0.46.25}/src/specfact_cli/utils/content_sanitizer.py +0 -0
  227. {specfact_cli-0.46.18 → specfact_cli-0.46.25}/src/specfact_cli/utils/context_detection.py +0 -0
  228. {specfact_cli-0.46.18 → specfact_cli-0.46.25}/src/specfact_cli/utils/contract_predicates.py +0 -0
  229. {specfact_cli-0.46.18 → specfact_cli-0.46.25}/src/specfact_cli/utils/enrichment_context.py +0 -0
  230. {specfact_cli-0.46.18 → specfact_cli-0.46.25}/src/specfact_cli/utils/enrichment_parser.py +0 -0
  231. {specfact_cli-0.46.18 → specfact_cli-0.46.25}/src/specfact_cli/utils/feature_keys.py +0 -0
  232. {specfact_cli-0.46.18 → specfact_cli-0.46.25}/src/specfact_cli/utils/git.py +0 -0
  233. {specfact_cli-0.46.18 → specfact_cli-0.46.25}/src/specfact_cli/utils/github_annotations.py +0 -0
  234. {specfact_cli-0.46.18 → specfact_cli-0.46.25}/src/specfact_cli/utils/icontract_helpers.py +0 -0
  235. {specfact_cli-0.46.18 → specfact_cli-0.46.25}/src/specfact_cli/utils/incremental_check.py +0 -0
  236. {specfact_cli-0.46.18 → specfact_cli-0.46.25}/src/specfact_cli/utils/metadata.py +0 -0
  237. {specfact_cli-0.46.18 → specfact_cli-0.46.25}/src/specfact_cli/utils/optional_deps.py +0 -0
  238. {specfact_cli-0.46.18 → specfact_cli-0.46.25}/src/specfact_cli/utils/performance.py +0 -0
  239. {specfact_cli-0.46.18 → specfact_cli-0.46.25}/src/specfact_cli/utils/persona_ownership.py +0 -0
  240. {specfact_cli-0.46.18 → specfact_cli-0.46.25}/src/specfact_cli/utils/progress.py +0 -0
  241. {specfact_cli-0.46.18 → specfact_cli-0.46.25}/src/specfact_cli/utils/progressive_disclosure.py +0 -0
  242. {specfact_cli-0.46.18 → specfact_cli-0.46.25}/src/specfact_cli/utils/project_artifact_write.py +0 -0
  243. {specfact_cli-0.46.18 → specfact_cli-0.46.25}/src/specfact_cli/utils/prompts.py +0 -0
  244. {specfact_cli-0.46.18 → specfact_cli-0.46.25}/src/specfact_cli/utils/sdd_discovery.py +0 -0
  245. {specfact_cli-0.46.18 → specfact_cli-0.46.25}/src/specfact_cli/utils/source_scanner.py +0 -0
  246. {specfact_cli-0.46.18 → specfact_cli-0.46.25}/src/specfact_cli/utils/startup_checks.py +0 -0
  247. {specfact_cli-0.46.18 → specfact_cli-0.46.25}/src/specfact_cli/utils/structure.py +0 -0
  248. {specfact_cli-0.46.18 → specfact_cli-0.46.25}/src/specfact_cli/utils/structured_io.py +0 -0
  249. {specfact_cli-0.46.18 → specfact_cli-0.46.25}/src/specfact_cli/utils/suggestions.py +0 -0
  250. {specfact_cli-0.46.18 → specfact_cli-0.46.25}/src/specfact_cli/utils/terminal.py +0 -0
  251. {specfact_cli-0.46.18 → specfact_cli-0.46.25}/src/specfact_cli/validation/__init__.py +0 -0
  252. {specfact_cli-0.46.18 → specfact_cli-0.46.25}/src/specfact_cli/validation/command_audit.py +0 -0
  253. {specfact_cli-0.46.18 → specfact_cli-0.46.25}/src/specfact_cli/validators/__init__.py +0 -0
  254. {specfact_cli-0.46.18 → specfact_cli-0.46.25}/src/specfact_cli/validators/agile_validation.py +0 -0
  255. {specfact_cli-0.46.18 → specfact_cli-0.46.25}/src/specfact_cli/validators/cli_first_validator.py +0 -0
  256. {specfact_cli-0.46.18 → specfact_cli-0.46.25}/src/specfact_cli/validators/contract_validator.py +0 -0
  257. {specfact_cli-0.46.18 → specfact_cli-0.46.25}/src/specfact_cli/validators/fsm.py +0 -0
  258. {specfact_cli-0.46.18 → specfact_cli-0.46.25}/src/specfact_cli/validators/repro_checker.py +0 -0
  259. {specfact_cli-0.46.18 → specfact_cli-0.46.25}/src/specfact_cli/validators/schema.py +0 -0
  260. {specfact_cli-0.46.18 → specfact_cli-0.46.25}/src/specfact_cli/validators/sidecar/__init__.py +0 -0
  261. {specfact_cli-0.46.18 → specfact_cli-0.46.25}/src/specfact_cli/validators/sidecar/contract_populator.py +0 -0
  262. {specfact_cli-0.46.18 → specfact_cli-0.46.25}/src/specfact_cli/validators/sidecar/crosshair_runner.py +0 -0
  263. {specfact_cli-0.46.18 → specfact_cli-0.46.25}/src/specfact_cli/validators/sidecar/crosshair_summary.py +0 -0
  264. {specfact_cli-0.46.18 → specfact_cli-0.46.25}/src/specfact_cli/validators/sidecar/dependency_installer.py +0 -0
  265. {specfact_cli-0.46.18 → specfact_cli-0.46.25}/src/specfact_cli/validators/sidecar/framework_detector.py +0 -0
  266. {specfact_cli-0.46.18 → specfact_cli-0.46.25}/src/specfact_cli/validators/sidecar/frameworks/__init__.py +0 -0
  267. {specfact_cli-0.46.18 → specfact_cli-0.46.25}/src/specfact_cli/validators/sidecar/frameworks/base.py +0 -0
  268. {specfact_cli-0.46.18 → specfact_cli-0.46.25}/src/specfact_cli/validators/sidecar/frameworks/django.py +0 -0
  269. {specfact_cli-0.46.18 → specfact_cli-0.46.25}/src/specfact_cli/validators/sidecar/frameworks/drf.py +0 -0
  270. {specfact_cli-0.46.18 → specfact_cli-0.46.25}/src/specfact_cli/validators/sidecar/frameworks/fastapi.py +0 -0
  271. {specfact_cli-0.46.18 → specfact_cli-0.46.25}/src/specfact_cli/validators/sidecar/frameworks/flask.py +0 -0
  272. {specfact_cli-0.46.18 → specfact_cli-0.46.25}/src/specfact_cli/validators/sidecar/harness_generator.py +0 -0
  273. {specfact_cli-0.46.18 → specfact_cli-0.46.25}/src/specfact_cli/validators/sidecar/models.py +0 -0
  274. {specfact_cli-0.46.18 → specfact_cli-0.46.25}/src/specfact_cli/validators/sidecar/orchestrator.py +0 -0
  275. {specfact_cli-0.46.18 → specfact_cli-0.46.25}/src/specfact_cli/validators/sidecar/specmatic_runner.py +0 -0
  276. {specfact_cli-0.46.18 → specfact_cli-0.46.25}/src/specfact_cli/validators/sidecar/unannotated_detector.py +0 -0
  277. {specfact_cli-0.46.18 → specfact_cli-0.46.25}/src/specfact_cli/versioning/__init__.py +0 -0
  278. {specfact_cli-0.46.18 → specfact_cli-0.46.25}/src/specfact_cli/versioning/analyzer.py +0 -0
@@ -113,6 +113,7 @@ docs/internal/
113
113
  .claude/commands/specfact.*.md
114
114
  .claude/skills/openspec-*/
115
115
  !.claude/skills/openspec-workflows/
116
+ .claude/settings.json
116
117
 
117
118
  .codex/skills/openspec-*/
118
119
  !.codex/skills/openspec-workflows/
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: specfact-cli
3
- Version: 0.46.18
3
+ Version: 0.46.25
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
@@ -224,6 +224,7 @@ Classifier: Topic :: Software Development :: Testing
224
224
  Requires-Python: >=3.11
225
225
  Requires-Dist: azure-identity>=1.17.1
226
226
  Requires-Dist: beartype>=0.22.4
227
+ Requires-Dist: click<8.2,>=8.1.8
227
228
  Requires-Dist: commentjson>=0.9.0
228
229
  Requires-Dist: cryptography>=43.0.0
229
230
  Requires-Dist: gitpython>=3.1.45
@@ -232,14 +233,12 @@ Requires-Dist: icontract>=2.7.1
232
233
  Requires-Dist: jinja2>=3.1.6
233
234
  Requires-Dist: jsonschema>=4.23.0
234
235
  Requires-Dist: networkx>=3.4.2
235
- Requires-Dist: opentelemetry-exporter-otlp-proto-http>=1.27.0
236
- Requires-Dist: opentelemetry-sdk>=1.27.0
237
236
  Requires-Dist: packaging>=24.0
238
237
  Requires-Dist: pydantic>=2.12.3
239
238
  Requires-Dist: pyyaml>=6.0.3
240
239
  Requires-Dist: questionary>=2.0.1
241
240
  Requires-Dist: requests>=2.32.3
242
- Requires-Dist: rich<13.6.0,>=13.5.2
241
+ Requires-Dist: rich<16.0.0,>=13.5.2
243
242
  Requires-Dist: ruamel-yaml>=0.18.16
244
243
  Requires-Dist: typer<0.24,>=0.20.0
245
244
  Requires-Dist: typing-extensions>=4.15.0
@@ -277,6 +276,9 @@ Requires-Dist: graphviz>=0.20.1; extra == 'enhanced-analysis'
277
276
  Requires-Dist: pycg==0.0.7; extra == 'enhanced-analysis'
278
277
  Provides-Extra: scanning
279
278
  Requires-Dist: semgrep>=1.144.0; extra == 'scanning'
279
+ Provides-Extra: telemetry
280
+ Requires-Dist: opentelemetry-exporter-otlp-proto-http>=1.27.0; extra == 'telemetry'
281
+ Requires-Dist: opentelemetry-sdk>=1.27.0; extra == 'telemetry'
280
282
  Description-Content-Type: text/markdown
281
283
 
282
284
  # SpecFact CLI
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "specfact-cli"
7
- version = "0.46.18"
7
+ version = "0.46.25"
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"
@@ -65,8 +65,9 @@ dependencies = [
65
65
 
66
66
  # CLI framework
67
67
  # Typer 0.24+ requires click>=8.2.1 (generic Choice[...]); dev semgrep pins click~=8.1.8 — cap Typer.
68
+ "click>=8.1.8,<8.2",
68
69
  "typer>=0.20.0,<0.24",
69
- "rich>=13.5.2,<13.6.0", # Compatible with semgrep (requires rich~=13.5.2)
70
+ "rich>=13.5.2,<16.0.0", # Semgrep allows rich>=13.5.2; keep below next major.
70
71
  "questionary>=2.0.1", # Interactive prompts with arrow key navigation
71
72
 
72
73
  # Template engine
@@ -94,10 +95,6 @@ dependencies = [
94
95
 
95
96
  # File system watching
96
97
  "watchdog>=6.0.0",
97
-
98
- # Telemetry (opt-in; only active when SPECFACT_TELEMETRY_OPT_IN=true)
99
- "opentelemetry-sdk>=1.27.0",
100
- "opentelemetry-exporter-otlp-proto-http>=1.27.0",
101
98
  ]
102
99
 
103
100
  [project.optional-dependencies]
@@ -106,6 +103,11 @@ contracts = [
106
103
  "hypothesis>=6.142.4",
107
104
  ]
108
105
 
106
+ telemetry = [
107
+ "opentelemetry-sdk>=1.27.0",
108
+ "opentelemetry-exporter-otlp-proto-http>=1.27.0",
109
+ ]
110
+
109
111
  dev = [
110
112
  "setuptools>=69.0.0,<82",
111
113
  "pytest>=8.4.2",
@@ -283,6 +285,7 @@ verify-removal-gate = [
283
285
  "hatch run verify-modules-signature",
284
286
  ]
285
287
  export-change-github = "python scripts/export-change-to-github.py {args}"
288
+ runtime-discovery-smoke = "python scripts/runtime_discovery_smoke.py {args}"
286
289
 
287
290
  # Contract-First Smart Test System Scripts
288
291
  contract-test = "python tools/contract_first_smart_test.py run --level auto {args}"
@@ -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.46.18"
6
+ __version__ = "0.46.25"
@@ -45,6 +45,6 @@ def _bootstrap_bundle_paths() -> None:
45
45
 
46
46
  _bootstrap_bundle_paths()
47
47
 
48
- __version__ = "0.46.18"
48
+ __version__ = "0.46.25"
49
49
 
50
50
  __all__ = ["__version__"]
@@ -1625,7 +1625,7 @@ class AdoAdapter(BridgeAdapter, BacklogAdapterMixin, BacklogAdapter):
1625
1625
  headers = {"Content-Type": "application/json-patch+json", **self._auth_headers()}
1626
1626
  try:
1627
1627
  response = self._request_with_retry(
1628
- lambda: requests.patch(url, json=patch_document, headers=headers, timeout=30)
1628
+ lambda: requests.patch(url, json=cast(Any, patch_document), headers=headers, timeout=30)
1629
1629
  )
1630
1630
  except requests.RequestException as exc:
1631
1631
  resp = getattr(exc, "response", None)
@@ -1939,7 +1939,7 @@ class AdoAdapter(BridgeAdapter, BacklogAdapterMixin, BacklogAdapter):
1939
1939
  # Refresh failed (no cached refresh token, refresh token expired, etc.)
1940
1940
  return None
1941
1941
 
1942
- def _auth_headers(self) -> dict[str, str]:
1942
+ def _auth_headers(self) -> dict[str, str | bytes]:
1943
1943
  """Return authorization headers based on token type."""
1944
1944
  if not self.api_token:
1945
1945
  return {}
@@ -1953,7 +1953,7 @@ class AdoAdapter(BridgeAdapter, BacklogAdapterMixin, BacklogAdapter):
1953
1953
  self,
1954
1954
  url: str,
1955
1955
  *,
1956
- headers: dict[str, str] | None = None,
1956
+ headers: dict[str, str | bytes] | None = None,
1957
1957
  params: dict[str, Any] | None = None,
1958
1958
  timeout: int = 30,
1959
1959
  retry_on_ambiguous_transport: bool = True,
@@ -1973,7 +1973,7 @@ class AdoAdapter(BridgeAdapter, BacklogAdapterMixin, BacklogAdapter):
1973
1973
  self,
1974
1974
  url: str,
1975
1975
  *,
1976
- headers: dict[str, str] | None = None,
1976
+ headers: dict[str, str | bytes] | None = None,
1977
1977
  params: dict[str, Any] | None = None,
1978
1978
  json: dict[str, Any] | None = None,
1979
1979
  timeout: int = 30,
@@ -2213,7 +2213,7 @@ class AdoAdapter(BridgeAdapter, BacklogAdapterMixin, BacklogAdapter):
2213
2213
 
2214
2214
  try:
2215
2215
  response = self._request_with_retry(
2216
- lambda: requests.post(url, json=patch_document, headers=headers, timeout=30),
2216
+ lambda: requests.post(url, json=cast(Any, patch_document), headers=headers, timeout=30),
2217
2217
  retry_on_ambiguous_transport=False,
2218
2218
  )
2219
2219
  if is_debug_mode():
@@ -2670,7 +2670,7 @@ class AdoAdapter(BridgeAdapter, BacklogAdapterMixin, BacklogAdapter):
2670
2670
 
2671
2671
  try:
2672
2672
  response = self._request_with_retry(
2673
- lambda: requests.post(url, json=comment_body, headers=headers, timeout=30),
2673
+ lambda: requests.post(url, json=cast(Any, comment_body), headers=headers, timeout=30),
2674
2674
  retry_on_ambiguous_transport=False,
2675
2675
  )
2676
2676
  comment_data = response.json()
@@ -3478,7 +3478,7 @@ class AdoAdapter(BridgeAdapter, BacklogAdapterMixin, BacklogAdapter):
3478
3478
  **self._auth_headers(),
3479
3479
  }
3480
3480
  response = self._request_with_retry(
3481
- lambda: requests.post(url, json=patch_document, headers=headers, timeout=30),
3481
+ lambda: requests.post(url, json=cast(Any, patch_document), headers=headers, timeout=30),
3482
3482
  retry_on_ambiguous_transport=False,
3483
3483
  )
3484
3484
  created = response.json()
@@ -3782,7 +3782,7 @@ class AdoAdapter(BridgeAdapter, BacklogAdapterMixin, BacklogAdapter):
3782
3782
  if operations_no_format == operations:
3783
3783
  return None
3784
3784
  try:
3785
- resp = requests.patch(url, headers=headers, json=operations_no_format, timeout=30)
3785
+ resp = requests.patch(url, headers=headers, json=cast(Any, operations_no_format), timeout=30)
3786
3786
  resp.raise_for_status()
3787
3787
  return resp
3788
3788
  except requests.HTTPError as retry_error:
@@ -3802,7 +3802,7 @@ class AdoAdapter(BridgeAdapter, BacklogAdapterMixin, BacklogAdapter):
3802
3802
  ) -> requests.Response | None:
3803
3803
  operations_replace = self._backlog_ops_replace_multiline_add_with_replace(operations)
3804
3804
  try:
3805
- resp = requests.patch(url, headers=headers, json=operations_replace, timeout=30)
3805
+ resp = requests.patch(url, headers=headers, json=cast(Any, operations_replace), timeout=30)
3806
3806
  resp.raise_for_status()
3807
3807
  return resp
3808
3808
  except requests.HTTPError:
@@ -3820,7 +3820,7 @@ class AdoAdapter(BridgeAdapter, BacklogAdapterMixin, BacklogAdapter):
3820
3820
  )
3821
3821
  operations_html = self._backlog_ops_convert_markdown_fields_to_html(operations)
3822
3822
  try:
3823
- resp = requests.patch(url, headers=headers, json=operations_html, timeout=30)
3823
+ resp = requests.patch(url, headers=headers, json=cast(Any, operations_html), timeout=30)
3824
3824
  resp.raise_for_status()
3825
3825
  return resp
3826
3826
  except requests.HTTPError:
@@ -3834,7 +3834,9 @@ class AdoAdapter(BridgeAdapter, BacklogAdapterMixin, BacklogAdapter):
3834
3834
  operations: list[dict[str, Any]],
3835
3835
  ) -> requests.Response:
3836
3836
  try:
3837
- return self._request_with_retry(lambda: requests.patch(url, headers=headers, json=operations, timeout=30))
3837
+ return self._request_with_retry(
3838
+ lambda: requests.patch(url, headers=headers, json=cast(Any, operations), timeout=30)
3839
+ )
3838
3840
  except requests.HTTPError as e:
3839
3841
  user_msg = _log_ado_patch_failure(e.response, operations, url)
3840
3842
  e.ado_user_message = user_msg # type: ignore[attr-defined]
@@ -561,7 +561,7 @@ class GitHubAdapter(BridgeAdapter, BacklogAdapterMixin, BacklogAdapter):
561
561
  from specfact_cli.backlog.converter import convert_github_issue_to_backlog_item
562
562
 
563
563
  url = f"{self.base_url}/search/issues"
564
- headers = {
564
+ headers: dict[str, str | bytes] = {
565
565
  "Authorization": f"token {self.api_token}",
566
566
  "Accept": "application/vnd.github.v3+json",
567
567
  }
@@ -1232,7 +1232,7 @@ class GitHubAdapter(BridgeAdapter, BacklogAdapterMixin, BacklogAdapter):
1232
1232
 
1233
1233
  repo_owner, repo_name, issue_number = self._parse_issue_reference(item_ref)
1234
1234
  url = f"{self.base_url}/repos/{repo_owner}/{repo_name}/issues/{issue_number}"
1235
- headers = {
1235
+ headers: dict[str, str | bytes] = {
1236
1236
  "Authorization": f"token {self.api_token}",
1237
1237
  "Accept": "application/vnd.github.v3+json",
1238
1238
  }
@@ -1498,7 +1498,7 @@ class GitHubAdapter(BridgeAdapter, BacklogAdapterMixin, BacklogAdapter):
1498
1498
 
1499
1499
  # Create issue via GitHub API
1500
1500
  url = f"{self.base_url}/repos/{repo_owner}/{repo_name}/issues"
1501
- headers = {
1501
+ headers: dict[str, str | bytes] = {
1502
1502
  "Authorization": f"token {self.api_token}",
1503
1503
  "Accept": "application/vnd.github.v3+json",
1504
1504
  }
@@ -1592,7 +1592,7 @@ class GitHubAdapter(BridgeAdapter, BacklogAdapterMixin, BacklogAdapter):
1592
1592
 
1593
1593
  # Update issue state
1594
1594
  url = f"{self.base_url}/repos/{repo_owner}/{repo_name}/issues/{issue_number}"
1595
- headers = {
1595
+ headers: dict[str, str | bytes] = {
1596
1596
  "Authorization": f"token {self.api_token}",
1597
1597
  "Accept": "application/vnd.github.v3+json",
1598
1598
  }
@@ -1601,7 +1601,9 @@ class GitHubAdapter(BridgeAdapter, BacklogAdapterMixin, BacklogAdapter):
1601
1601
  payload["state_reason"] = state_reason
1602
1602
 
1603
1603
  try:
1604
- response = self._request_with_retry(lambda: requests.patch(url, json=payload, headers=headers, timeout=30))
1604
+ response = self._request_with_retry(
1605
+ lambda: requests.patch(url, json=cast(Any, payload), headers=headers, timeout=30)
1606
+ )
1605
1607
  issue_data = response.json()
1606
1608
 
1607
1609
  # Add comment explaining status change
@@ -1634,7 +1636,7 @@ class GitHubAdapter(BridgeAdapter, BacklogAdapterMixin, BacklogAdapter):
1634
1636
  return []
1635
1637
 
1636
1638
  url = f"{self.base_url}/repos/{repo_owner}/{repo_name}/issues/{issue_number}/comments"
1637
- headers = {
1639
+ headers: dict[str, str | bytes] = {
1638
1640
  "Authorization": f"token {self.api_token}",
1639
1641
  "Accept": "application/vnd.github.v3+json",
1640
1642
  }
@@ -1658,7 +1660,7 @@ class GitHubAdapter(BridgeAdapter, BacklogAdapterMixin, BacklogAdapter):
1658
1660
  comment: Comment text
1659
1661
  """
1660
1662
  url = f"{self.base_url}/repos/{repo_owner}/{repo_name}/issues/{issue_number}/comments"
1661
- headers = {
1663
+ headers: dict[str, str | bytes] = {
1662
1664
  "Authorization": f"token {self.api_token}",
1663
1665
  "Accept": "application/vnd.github.v3+json",
1664
1666
  }
@@ -1666,7 +1668,7 @@ class GitHubAdapter(BridgeAdapter, BacklogAdapterMixin, BacklogAdapter):
1666
1668
 
1667
1669
  try:
1668
1670
  self._request_with_retry(
1669
- lambda: requests.post(url, json=payload, headers=headers, timeout=30),
1671
+ lambda: requests.post(url, json=cast(Any, payload), headers=headers, timeout=30),
1670
1672
  retry_on_ambiguous_transport=False,
1671
1673
  )
1672
1674
  except requests.RequestException as e:
@@ -1676,7 +1678,7 @@ class GitHubAdapter(BridgeAdapter, BacklogAdapterMixin, BacklogAdapter):
1676
1678
  def _fetch_issue_snapshot(self, repo_owner: str, repo_name: str, issue_number: int) -> tuple[str, str, str]:
1677
1679
  """Fetch current issue body, title, and state for preservation-aware updates."""
1678
1680
  url = f"{self.base_url}/repos/{repo_owner}/{repo_name}/issues/{issue_number}"
1679
- headers = {
1681
+ headers: dict[str, str | bytes] = {
1680
1682
  "Authorization": f"token {self.api_token}",
1681
1683
  "Accept": "application/vnd.github.v3+json",
1682
1684
  }
@@ -1759,7 +1761,7 @@ class GitHubAdapter(BridgeAdapter, BacklogAdapterMixin, BacklogAdapter):
1759
1761
 
1760
1762
  # Update issue body via GitHub API PATCH
1761
1763
  url = f"{self.base_url}/repos/{repo_owner}/{repo_name}/issues/{issue_number}"
1762
- headers = {
1764
+ headers: dict[str, str | bytes] = {
1763
1765
  "Authorization": f"token {self.api_token}",
1764
1766
  "Accept": "application/vnd.github.v3+json",
1765
1767
  }
@@ -1969,7 +1971,7 @@ class GitHubAdapter(BridgeAdapter, BacklogAdapterMixin, BacklogAdapter):
1969
1971
 
1970
1972
  # Get current issue to retrieve existing labels
1971
1973
  url = f"{self.base_url}/repos/{repo_owner}/{repo_name}/issues/{issue_number}"
1972
- headers = {
1974
+ headers: dict[str, str | bytes] = {
1973
1975
  "Authorization": f"token {self.api_token}",
1974
1976
  "Accept": "application/vnd.github.v3+json",
1975
1977
  }
@@ -1987,7 +1989,9 @@ class GitHubAdapter(BridgeAdapter, BacklogAdapterMixin, BacklogAdapter):
1987
1989
  patch_url = f"{self.base_url}/repos/{repo_owner}/{repo_name}/issues/{issue_number}"
1988
1990
  patch_payload = {"labels": all_labels}
1989
1991
 
1990
- self._request_with_retry(lambda: requests.patch(patch_url, json=patch_payload, headers=headers, timeout=30))
1992
+ self._request_with_retry(
1993
+ lambda: requests.patch(patch_url, json=cast(Any, patch_payload), headers=headers, timeout=30)
1994
+ )
1991
1995
 
1992
1996
  return {
1993
1997
  "issue_number": current_issue.get("number", issue_number), # Use API response number (int)
@@ -2623,7 +2627,7 @@ class GitHubAdapter(BridgeAdapter, BacklogAdapterMixin, BacklogAdapter):
2623
2627
  return None
2624
2628
 
2625
2629
  url = f"{self.base_url}/repos/{self.repo_owner}/{self.repo_name}/issues/{normalized_id}"
2626
- headers = {
2630
+ headers: dict[str, str | bytes] = {
2627
2631
  "Authorization": f"token {self.api_token}",
2628
2632
  "Accept": "application/vnd.github.v3+json",
2629
2633
  }
@@ -2761,7 +2765,7 @@ class GitHubAdapter(BridgeAdapter, BacklogAdapterMixin, BacklogAdapter):
2761
2765
  @beartype
2762
2766
  def _github_graphql(self, query: str, variables: dict[str, Any]) -> dict[str, Any]:
2763
2767
  """Execute GitHub GraphQL request and return `data` payload."""
2764
- headers = {
2768
+ headers: dict[str, str | bytes] = {
2765
2769
  "Authorization": f"token {self.api_token}",
2766
2770
  "Accept": "application/vnd.github+json",
2767
2771
  }
@@ -2976,14 +2980,14 @@ class GitHubAdapter(BridgeAdapter, BacklogAdapterMixin, BacklogAdapter):
2976
2980
  issue_type, str(payload.get("priority") or "").strip(), payload.get("story_points")
2977
2981
  )
2978
2982
  url = f"{self.base_url}/repos/{owner}/{repo}/issues"
2979
- headers = {
2983
+ headers: dict[str, str | bytes] = {
2980
2984
  "Authorization": f"token {self.api_token}",
2981
2985
  "Accept": "application/vnd.github.v3+json",
2982
2986
  }
2983
2987
  response = self._request_with_retry(
2984
2988
  lambda: requests.post(
2985
2989
  url,
2986
- json={"title": title, "body": body, "labels": labels},
2990
+ json=cast(Any, {"title": title, "body": body, "labels": labels}),
2987
2991
  headers=headers,
2988
2992
  timeout=30,
2989
2993
  ),
@@ -3245,7 +3249,7 @@ class GitHubAdapter(BridgeAdapter, BacklogAdapterMixin, BacklogAdapter):
3245
3249
 
3246
3250
  issue_number = int(item.id)
3247
3251
  url = f"{self.base_url}/repos/{self.repo_owner}/{self.repo_name}/issues/{issue_number}"
3248
- headers = {
3252
+ headers: dict[str, str | bytes] = {
3249
3253
  "Authorization": f"token {self.api_token}",
3250
3254
  "Accept": "application/vnd.github.v3+json",
3251
3255
  }
@@ -178,6 +178,11 @@ class _RootCLIGroup(ProgressiveDisclosureGroup):
178
178
  _print_missing_bundle_command_help(invoked)
179
179
  raise SystemExit(1) from None
180
180
  raise
181
+ except ValueError as exc:
182
+ if invoked in KNOWN_BUNDLE_GROUP_OR_SHIM_NAMES:
183
+ _print_missing_bundle_command_help(invoked)
184
+ raise SystemExit(1) from exc
185
+ raise
181
186
  _name, cmd, remaining = result
182
187
  if cmd is not None or not remaining:
183
188
  return result
@@ -693,6 +698,8 @@ class _LazyDelegateGroup(click.Group):
693
698
  real_group = self._get_real_click_group()
694
699
  if real_group is not None:
695
700
  return real_group.get_command(ctx, cmd_name)
701
+ if self._lazy_cmd_name in KNOWN_BUNDLE_GROUP_OR_SHIM_NAMES:
702
+ return self._delegate_cmd
696
703
  return None
697
704
 
698
705
  def _get_real_click_group(self) -> click.Group | None:
@@ -662,7 +662,13 @@ class ProjectBundle(BaseModel):
662
662
  if progress_callback:
663
663
  progress_callback(total_artifacts, total_artifacts, "bundle.manifest.yaml")
664
664
  manifest_path = bundle_dir / "bundle.manifest.yaml"
665
- dump_structured_file(self.manifest.model_dump(mode="json"), manifest_path)
665
+ manifest_data = self.manifest.model_dump(mode="json")
666
+ if num_features > 1000:
667
+ import json
668
+
669
+ manifest_path.write_text(json.dumps(manifest_data, indent=2), encoding="utf-8")
670
+ else:
671
+ dump_structured_file(manifest_data, manifest_path)
666
672
 
667
673
  @beartype
668
674
  @require(lambda self, key: isinstance(key, str) and len(key) > 0, "Feature key must be non-empty string")
@@ -1,5 +1,5 @@
1
1
  name: init
2
- version: 0.1.31
2
+ version: 0.1.33
3
3
  commands:
4
4
  - init
5
5
  category: core
@@ -17,5 +17,5 @@ publisher:
17
17
  description: Initialize SpecFact workspace and bootstrap local configuration.
18
18
  license: Apache-2.0
19
19
  integrity:
20
- checksum: sha256:0f7bc54a823bea14033fcb143ecb6c83d2bca2b5da661f03a0b545100acebe5b
21
- signature: dTatkqgBUtti4tL/pmcFBZY9bsJ61gY/V0lP9gZU8Y5W3YWK+wpgRx1oewlAmfKzkxca2NhalKcjLACQjTNvAA==
20
+ checksum: sha256:0086a4fa3e1f8744deee21640e73e6e2f24daf2b4a680b9157feed5487afc208
21
+ signature: L7DtVqvg+zqc/CgJu/mOkvxYpfH9HuddbLS3EK5Uvnf6K/j/BofqopamZDdHVlevOGAb+dbsk06kskNOIJfkDQ==
@@ -26,7 +26,13 @@ from specfact_cli.registry.module_state import write_modules_state
26
26
  from specfact_cli.runtime import debug_print, is_non_interactive
27
27
  from specfact_cli.telemetry import telemetry
28
28
  from specfact_cli.utils.contract_predicates import repo_path_exists, repo_path_is_dir
29
- from specfact_cli.utils.env_manager import EnvManager, EnvManagerInfo, build_tool_command, detect_env_manager
29
+ from specfact_cli.utils.env_manager import (
30
+ EnvManager,
31
+ EnvManagerInfo,
32
+ build_tool_command,
33
+ detect_env_manager,
34
+ env_info_from_tool_choice,
35
+ )
30
36
  from specfact_cli.utils.ide_setup import (
31
37
  IDE_CONFIG,
32
38
  PROMPT_SOURCE_CORE,
@@ -610,6 +616,11 @@ def init_ide(
610
616
  "--ide",
611
617
  help="IDE type (cursor, vscode, copilot, claude, gemini, qwen, opencode, windsurf, kilocode, auggie, roo, codebuddy, amp, q, auto)",
612
618
  ),
619
+ env_manager: EnvManager = typer.Option(
620
+ EnvManager.AUTO,
621
+ "--env-manager",
622
+ help="Environment manager override: auto, uv, hatch, poetry, or pip",
623
+ ),
613
624
  prompts: str | None = typer.Option(
614
625
  None,
615
626
  "--prompts",
@@ -638,7 +649,17 @@ def init_ide(
638
649
  console.print(f"[cyan]IDE:[/cyan] {ide_name} ({selected_ide})")
639
650
  console.print()
640
651
 
641
- env_info = detect_env_manager(repo_path)
652
+ env_info = (
653
+ detect_env_manager(repo_path)
654
+ if env_manager is EnvManager.AUTO
655
+ else env_info_from_tool_choice(env_manager, repo_path)
656
+ )
657
+ if env_manager is not EnvManager.AUTO and not env_info.available:
658
+ console.print(Panel(f"[bold red]{env_info.message}[/bold red]", border_style="red"))
659
+ raise typer.Exit(1)
660
+ if env_info.manager is not EnvManager.UNKNOWN:
661
+ console.print(f"[cyan]Environment manager:[/cyan] {env_info.manager.value}")
662
+ console.print()
642
663
  if env_info.manager == EnvManager.UNKNOWN:
643
664
  console.print(
644
665
  Panel(
@@ -673,7 +694,12 @@ def init_ide(
673
694
  copied_files, settings_path = copy_templates_to_ide(
674
695
  repo_path, selected_ide, force, prompts_by_source=selected_catalog
675
696
  )
676
- write_ide_prompt_export_state(repo_path, selected_ide, sorted(selected_catalog.keys()))
697
+ write_ide_prompt_export_state(
698
+ repo_path,
699
+ selected_ide,
700
+ sorted(selected_catalog.keys()),
701
+ env_manager=env_info.manager.value,
702
+ )
677
703
  _copy_backlog_field_mapping_templates(repo_path, force, console)
678
704
 
679
705
  console.print()
@@ -1,5 +1,5 @@
1
1
  name: module-registry
2
- version: 0.1.23
2
+ version: 0.1.26
3
3
  commands:
4
4
  - module
5
5
  category: core
@@ -17,5 +17,5 @@ publisher:
17
17
  description: 'Manage modules: search, list, show, install, and upgrade.'
18
18
  license: Apache-2.0
19
19
  integrity:
20
- checksum: sha256:f500281d2249d712be23a1b25b5660374694dfa47634f60ae4378eb2cdb753ca
21
- signature: cunuat95bD44IcNUBs35NTlHYOR8atydVz4ZyZTjIzwJL6Wz5YOfBYGq/WtgDnOFv3KplfvhRZbTo3CwmS/KBQ==
20
+ checksum: sha256:52a65869fb3d2d36910308c954e0ba199cb1c73f24136ee0d4aa7254f0996a22
21
+ signature: Uo2eJ40HiWBqe77N8gjiDg7zGhsvJE/hN82+Jcto7yUoqbH7bidLw9DfVQ0CrJfgM9Y0XHAUS1+LMyPr1jAaBg==
@@ -5,6 +5,7 @@ from __future__ import annotations
5
5
  import inspect
6
6
  import os
7
7
  import shutil
8
+ import tempfile
8
9
  from collections.abc import Callable, Iterator
9
10
  from contextlib import contextmanager
10
11
  from dataclasses import dataclass
@@ -27,7 +28,12 @@ from specfact_cli.registry.alias_manager import create_alias, list_aliases, remo
27
28
  from specfact_cli.registry.custom_registries import add_registry, fetch_all_indexes, list_registries, remove_registry
28
29
  from specfact_cli.registry.help_cache import run_discovery_and_write_cache
29
30
  from specfact_cli.registry.marketplace_client import fetch_registry_index
30
- from specfact_cli.registry.module_discovery import discover_all_modules, discover_all_modules_for_project
31
+ from specfact_cli.registry.module_discovery import (
32
+ DiscoveredModule,
33
+ discover_all_modules,
34
+ discover_all_modules_for_project,
35
+ discover_all_modules_for_project_with_shadowed,
36
+ )
31
37
  from specfact_cli.registry.module_installer import (
32
38
  REGISTRY_ID_FILE,
33
39
  USER_MODULES_ROOT,
@@ -187,7 +193,10 @@ def _normalize_project_repo(repo: Path | None) -> Path | None:
187
193
  if repo is None:
188
194
  return None
189
195
  repo_path = repo.resolve()
196
+ temp_root = Path(tempfile.gettempdir()).resolve()
190
197
  for candidate in [repo_path, *repo_path.parents]:
198
+ if candidate == temp_root and candidate != repo_path:
199
+ return repo_path
191
200
  if (candidate / ".git").exists():
192
201
  return candidate
193
202
  return repo_path
@@ -1062,6 +1071,107 @@ def _print_bundled_available_table(available: list[ModulePackageMetadata]) -> No
1062
1071
  console.print("[dim]Install bundled modules into project scope: specfact module init --scope project[/dim]")
1063
1072
 
1064
1073
 
1074
+ def _doctor_entry_matches(entry: DiscoveredModule, module_id: str | None) -> bool:
1075
+ if module_id is None:
1076
+ return True
1077
+ requested = module_id.strip()
1078
+ if not requested:
1079
+ return True
1080
+ discovered_id = entry.metadata.name
1081
+ if requested == discovered_id:
1082
+ return True
1083
+ if "/" in requested:
1084
+ return False
1085
+ return requested.rsplit("/", 1)[-1] == discovered_id.rsplit("/", 1)[-1]
1086
+
1087
+
1088
+ def _doctor_dev_roots() -> list[tuple[str, str]]:
1089
+ roots: list[tuple[str, str]] = []
1090
+ for env_name in ("SPECFACT_MODULES_REPO", "SPECFACT_CLI_MODULES_REPO"):
1091
+ value = os.environ.get(env_name, "").strip()
1092
+ if value:
1093
+ roots.append((env_name, value))
1094
+ for raw_root in os.environ.get("SPECFACT_MODULES_ROOTS", "").split(os.pathsep):
1095
+ value = raw_root.strip()
1096
+ if value:
1097
+ roots.append(("SPECFACT_MODULES_ROOTS", value))
1098
+ return roots
1099
+
1100
+
1101
+ def _doctor_status(
1102
+ entry: DiscoveredModule,
1103
+ seen_by_module_id: set[str],
1104
+ enabled_state: dict[str, dict[str, Any]],
1105
+ ) -> str:
1106
+ module_id = entry.metadata.name
1107
+ if module_id in seen_by_module_id:
1108
+ return "shadowed"
1109
+ seen_by_module_id.add(module_id)
1110
+ if enabled_state.get(module_id, {}).get("enabled", True) is False:
1111
+ return "disabled"
1112
+ return "effective"
1113
+
1114
+
1115
+ def _print_doctor_recovery(entries: list[tuple[DiscoveredModule, str]]) -> None:
1116
+ for entry, status in entries:
1117
+ if status != "shadowed" or entry.source != "user":
1118
+ continue
1119
+ console.print(f"[yellow]Recovery:[/yellow] specfact module uninstall {entry.metadata.name} --scope user")
1120
+
1121
+
1122
+ @app.command(name="doctor")
1123
+ @beartype
1124
+ @require(_module_id_optional_nonempty, "module_id must be non-empty if provided")
1125
+ def doctor(
1126
+ module_id: str | None = typer.Argument(None, help="Optional module id to inspect"),
1127
+ repo: Path | None = typer.Option(None, "--repo", help="Repository path for project scope (default: current dir)"),
1128
+ ) -> None:
1129
+ """Diagnose module scope, shadowing, and development source roots."""
1130
+ repo_path = (repo or Path.cwd()).resolve()
1131
+ discovered = [
1132
+ entry
1133
+ for entry in discover_all_modules_for_project_with_shadowed(repo_path)
1134
+ if _doctor_entry_matches(entry, module_id)
1135
+ ]
1136
+ if not discovered:
1137
+ console.print("[yellow]No matching modules discovered.[/yellow]")
1138
+ else:
1139
+ state = read_modules_state()
1140
+ seen_by_module_id: set[str] = set()
1141
+ rows: list[tuple[DiscoveredModule, str]] = []
1142
+ table = Table(title="Module Doctor")
1143
+ table.add_column("Module", style="cyan")
1144
+ table.add_column("Version", style="magenta")
1145
+ table.add_column("Status", style="yellow")
1146
+ table.add_column("Origin", style="blue")
1147
+ table.add_column("Enabled", style="green")
1148
+ table.add_column("Path", overflow="fold")
1149
+ for entry in discovered:
1150
+ status = _doctor_status(entry, seen_by_module_id, state)
1151
+ enabled = state.get(entry.metadata.name, {}).get("enabled", True) is not False
1152
+ rows.append((entry, status))
1153
+ table.add_row(
1154
+ entry.metadata.name,
1155
+ entry.metadata.version,
1156
+ status,
1157
+ entry.source,
1158
+ "yes" if enabled else "no",
1159
+ str(entry.package_dir),
1160
+ )
1161
+ console.print(table)
1162
+ _print_doctor_recovery(rows)
1163
+
1164
+ dev_roots = _doctor_dev_roots()
1165
+ if not dev_roots:
1166
+ return
1167
+ dev_table = Table(title="Development Source Roots")
1168
+ dev_table.add_column("Source", style="cyan")
1169
+ dev_table.add_column("Path", overflow="fold")
1170
+ for source, path in dev_roots:
1171
+ dev_table.add_row(source, path)
1172
+ console.print(dev_table)
1173
+
1174
+
1065
1175
  @app.command(name="list")
1066
1176
  @beartype
1067
1177
  @require(