specfact-cli 0.42.6__tar.gz → 0.43.2__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 (285) hide show
  1. {specfact_cli-0.42.6 → specfact_cli-0.43.2}/PKG-INFO +6 -2
  2. {specfact_cli-0.42.6 → specfact_cli-0.43.2}/README.md +5 -1
  3. {specfact_cli-0.42.6 → specfact_cli-0.43.2}/pyproject.toml +15 -2
  4. {specfact_cli-0.42.6 → specfact_cli-0.43.2}/src/__init__.py +1 -1
  5. {specfact_cli-0.42.6 → specfact_cli-0.43.2}/src/specfact_cli/__init__.py +4 -1
  6. {specfact_cli-0.42.6 → specfact_cli-0.43.2}/src/specfact_cli/adapters/speckit.py +89 -23
  7. {specfact_cli-0.42.6 → specfact_cli-0.43.2}/src/specfact_cli/importers/speckit_scanner.py +123 -1
  8. {specfact_cli-0.42.6 → specfact_cli-0.43.2}/src/specfact_cli/models/bridge.py +66 -3
  9. {specfact_cli-0.42.6 → specfact_cli-0.43.2}/src/specfact_cli/models/capabilities.py +6 -0
  10. {specfact_cli-0.42.6 → specfact_cli-0.43.2}/src/specfact_cli/modules/init/module-package.yaml +3 -3
  11. {specfact_cli-0.42.6 → specfact_cli-0.43.2}/src/specfact_cli/modules/init/src/commands.py +3 -4
  12. {specfact_cli-0.42.6 → specfact_cli-0.43.2}/src/specfact_cli/registry/module_lifecycle.py +5 -3
  13. {specfact_cli-0.42.6 → specfact_cli-0.43.2}/src/specfact_cli/utils/ide_setup.py +4 -2
  14. {specfact_cli-0.42.6 → specfact_cli-0.43.2}/src/specfact_cli/utils/startup_checks.py +53 -32
  15. specfact_cli-0.42.6/resources/prompts/shared/cli-enforcement.md +0 -119
  16. specfact_cli-0.42.6/resources/prompts/specfact.01-import.md +0 -263
  17. specfact_cli-0.42.6/resources/prompts/specfact.02-plan.md +0 -177
  18. specfact_cli-0.42.6/resources/prompts/specfact.03-review.md +0 -714
  19. specfact_cli-0.42.6/resources/prompts/specfact.04-sdd.md +0 -160
  20. specfact_cli-0.42.6/resources/prompts/specfact.05-enforce.md +0 -166
  21. specfact_cli-0.42.6/resources/prompts/specfact.06-sync.md +0 -202
  22. specfact_cli-0.42.6/resources/prompts/specfact.07-contracts.md +0 -364
  23. specfact_cli-0.42.6/resources/prompts/specfact.compare.md +0 -159
  24. specfact_cli-0.42.6/resources/prompts/specfact.validate.md +0 -166
  25. {specfact_cli-0.42.6 → specfact_cli-0.43.2}/.gitignore +0 -0
  26. {specfact_cli-0.42.6 → specfact_cli-0.43.2}/LICENSE +0 -0
  27. {specfact_cli-0.42.6 → specfact_cli-0.43.2}/resources/keys/README.md +0 -0
  28. {specfact_cli-0.42.6 → specfact_cli-0.43.2}/resources/keys/module-signing-public.pem +0 -0
  29. {specfact_cli-0.42.6 → specfact_cli-0.43.2}/resources/mappings/node-async.yaml +0 -0
  30. {specfact_cli-0.42.6 → specfact_cli-0.43.2}/resources/mappings/python-async.yaml +0 -0
  31. {specfact_cli-0.42.6 → specfact_cli-0.43.2}/resources/mappings/speckit-default.yaml +0 -0
  32. {specfact_cli-0.42.6 → specfact_cli-0.43.2}/resources/schemas/deviation.schema.json +0 -0
  33. {specfact_cli-0.42.6 → specfact_cli-0.43.2}/resources/schemas/plan.schema.json +0 -0
  34. {specfact_cli-0.42.6 → specfact_cli-0.43.2}/resources/schemas/protocol.schema.json +0 -0
  35. {specfact_cli-0.42.6 → specfact_cli-0.43.2}/resources/templates/github-action.yml.j2 +0 -0
  36. {specfact_cli-0.42.6 → specfact_cli-0.43.2}/resources/templates/persona/architect.md.j2 +0 -0
  37. {specfact_cli-0.42.6 → specfact_cli-0.43.2}/resources/templates/persona/developer.md.j2 +0 -0
  38. {specfact_cli-0.42.6 → specfact_cli-0.43.2}/resources/templates/persona/product-owner.md.j2 +0 -0
  39. {specfact_cli-0.42.6 → specfact_cli-0.43.2}/resources/templates/plan.bundle.yaml.j2 +0 -0
  40. {specfact_cli-0.42.6 → specfact_cli-0.43.2}/resources/templates/policies/kanban.yaml +0 -0
  41. {specfact_cli-0.42.6 → specfact_cli-0.43.2}/resources/templates/policies/mixed.yaml +0 -0
  42. {specfact_cli-0.42.6 → specfact_cli-0.43.2}/resources/templates/policies/safe.yaml +0 -0
  43. {specfact_cli-0.42.6 → specfact_cli-0.43.2}/resources/templates/policies/scrum.yaml +0 -0
  44. {specfact_cli-0.42.6 → specfact_cli-0.43.2}/resources/templates/pr-template.md.j2 +0 -0
  45. {specfact_cli-0.42.6 → specfact_cli-0.43.2}/resources/templates/protocol.yaml.j2 +0 -0
  46. {specfact_cli-0.42.6 → specfact_cli-0.43.2}/resources/templates/telemetry.yaml.example +0 -0
  47. {specfact_cli-0.42.6 → specfact_cli-0.43.2}/src/specfact_cli/__main__.py +0 -0
  48. {specfact_cli-0.42.6 → specfact_cli-0.43.2}/src/specfact_cli/adapters/__init__.py +0 -0
  49. {specfact_cli-0.42.6 → specfact_cli-0.43.2}/src/specfact_cli/adapters/ado.py +0 -0
  50. {specfact_cli-0.42.6 → specfact_cli-0.43.2}/src/specfact_cli/adapters/backlog_base.py +0 -0
  51. {specfact_cli-0.42.6 → specfact_cli-0.43.2}/src/specfact_cli/adapters/base.py +0 -0
  52. {specfact_cli-0.42.6 → specfact_cli-0.43.2}/src/specfact_cli/adapters/github.py +0 -0
  53. {specfact_cli-0.42.6 → specfact_cli-0.43.2}/src/specfact_cli/adapters/openspec.py +0 -0
  54. {specfact_cli-0.42.6 → specfact_cli-0.43.2}/src/specfact_cli/adapters/openspec_parser.py +0 -0
  55. {specfact_cli-0.42.6 → specfact_cli-0.43.2}/src/specfact_cli/adapters/registry.py +0 -0
  56. {specfact_cli-0.42.6 → specfact_cli-0.43.2}/src/specfact_cli/agents/__init__.py +0 -0
  57. {specfact_cli-0.42.6 → specfact_cli-0.43.2}/src/specfact_cli/agents/analyze_agent.py +0 -0
  58. {specfact_cli-0.42.6 → specfact_cli-0.43.2}/src/specfact_cli/agents/base.py +0 -0
  59. {specfact_cli-0.42.6 → specfact_cli-0.43.2}/src/specfact_cli/agents/plan_agent.py +0 -0
  60. {specfact_cli-0.42.6 → specfact_cli-0.43.2}/src/specfact_cli/agents/registry.py +0 -0
  61. {specfact_cli-0.42.6 → specfact_cli-0.43.2}/src/specfact_cli/agents/sync_agent.py +0 -0
  62. {specfact_cli-0.42.6 → specfact_cli-0.43.2}/src/specfact_cli/analyzers/__init__.py +0 -0
  63. {specfact_cli-0.42.6 → specfact_cli-0.43.2}/src/specfact_cli/analyzers/ambiguity_scanner.py +0 -0
  64. {specfact_cli-0.42.6 → specfact_cli-0.43.2}/src/specfact_cli/analyzers/code_analyzer.py +0 -0
  65. {specfact_cli-0.42.6 → specfact_cli-0.43.2}/src/specfact_cli/analyzers/constitution_evidence_extractor.py +0 -0
  66. {specfact_cli-0.42.6 → specfact_cli-0.43.2}/src/specfact_cli/analyzers/contract_extractor.py +0 -0
  67. {specfact_cli-0.42.6 → specfact_cli-0.43.2}/src/specfact_cli/analyzers/control_flow_analyzer.py +0 -0
  68. {specfact_cli-0.42.6 → specfact_cli-0.43.2}/src/specfact_cli/analyzers/graph_analyzer.py +0 -0
  69. {specfact_cli-0.42.6 → specfact_cli-0.43.2}/src/specfact_cli/analyzers/relationship_mapper.py +0 -0
  70. {specfact_cli-0.42.6 → specfact_cli-0.43.2}/src/specfact_cli/analyzers/requirement_extractor.py +0 -0
  71. {specfact_cli-0.42.6 → specfact_cli-0.43.2}/src/specfact_cli/analyzers/test_pattern_extractor.py +0 -0
  72. {specfact_cli-0.42.6 → specfact_cli-0.43.2}/src/specfact_cli/backlog/__init__.py +0 -0
  73. {specfact_cli-0.42.6 → specfact_cli-0.43.2}/src/specfact_cli/backlog/adapters/__init__.py +0 -0
  74. {specfact_cli-0.42.6 → specfact_cli-0.43.2}/src/specfact_cli/backlog/adapters/base.py +0 -0
  75. {specfact_cli-0.42.6 → specfact_cli-0.43.2}/src/specfact_cli/backlog/converter.py +0 -0
  76. {specfact_cli-0.42.6 → specfact_cli-0.43.2}/src/specfact_cli/backlog/filters.py +0 -0
  77. {specfact_cli-0.42.6 → specfact_cli-0.43.2}/src/specfact_cli/backlog/mappers/__init__.py +0 -0
  78. {specfact_cli-0.42.6 → specfact_cli-0.43.2}/src/specfact_cli/backlog/mappers/ado_mapper.py +0 -0
  79. {specfact_cli-0.42.6 → specfact_cli-0.43.2}/src/specfact_cli/backlog/mappers/base.py +0 -0
  80. {specfact_cli-0.42.6 → specfact_cli-0.43.2}/src/specfact_cli/backlog/mappers/github_mapper.py +0 -0
  81. {specfact_cli-0.42.6 → specfact_cli-0.43.2}/src/specfact_cli/backlog/mappers/template_config.py +0 -0
  82. {specfact_cli-0.42.6 → specfact_cli-0.43.2}/src/specfact_cli/cli.py +0 -0
  83. {specfact_cli-0.42.6 → specfact_cli-0.43.2}/src/specfact_cli/commands/__init__.py +0 -0
  84. {specfact_cli-0.42.6 → specfact_cli-0.43.2}/src/specfact_cli/commands/_bundle_shim.py +0 -0
  85. {specfact_cli-0.42.6 → specfact_cli-0.43.2}/src/specfact_cli/commands/analyze.py +0 -0
  86. {specfact_cli-0.42.6 → specfact_cli-0.43.2}/src/specfact_cli/commands/contract_cmd.py +0 -0
  87. {specfact_cli-0.42.6 → specfact_cli-0.43.2}/src/specfact_cli/commands/drift.py +0 -0
  88. {specfact_cli-0.42.6 → specfact_cli-0.43.2}/src/specfact_cli/commands/enforce.py +0 -0
  89. {specfact_cli-0.42.6 → specfact_cli-0.43.2}/src/specfact_cli/commands/generate.py +0 -0
  90. {specfact_cli-0.42.6 → specfact_cli-0.43.2}/src/specfact_cli/commands/import_cmd.py +0 -0
  91. {specfact_cli-0.42.6 → specfact_cli-0.43.2}/src/specfact_cli/commands/init.py +0 -0
  92. {specfact_cli-0.42.6 → specfact_cli-0.43.2}/src/specfact_cli/commands/migrate.py +0 -0
  93. {specfact_cli-0.42.6 → specfact_cli-0.43.2}/src/specfact_cli/commands/plan.py +0 -0
  94. {specfact_cli-0.42.6 → specfact_cli-0.43.2}/src/specfact_cli/commands/project_cmd.py +0 -0
  95. {specfact_cli-0.42.6 → specfact_cli-0.43.2}/src/specfact_cli/commands/repro.py +0 -0
  96. {specfact_cli-0.42.6 → specfact_cli-0.43.2}/src/specfact_cli/commands/sdd.py +0 -0
  97. {specfact_cli-0.42.6 → specfact_cli-0.43.2}/src/specfact_cli/commands/spec.py +0 -0
  98. {specfact_cli-0.42.6 → specfact_cli-0.43.2}/src/specfact_cli/commands/sync.py +0 -0
  99. {specfact_cli-0.42.6 → specfact_cli-0.43.2}/src/specfact_cli/commands/update.py +0 -0
  100. {specfact_cli-0.42.6 → specfact_cli-0.43.2}/src/specfact_cli/commands/validate.py +0 -0
  101. {specfact_cli-0.42.6 → specfact_cli-0.43.2}/src/specfact_cli/common/__init__.py +0 -0
  102. {specfact_cli-0.42.6 → specfact_cli-0.43.2}/src/specfact_cli/common/bundle_factory.py +0 -0
  103. {specfact_cli-0.42.6 → specfact_cli-0.43.2}/src/specfact_cli/common/logger_setup.py +0 -0
  104. {specfact_cli-0.42.6 → specfact_cli-0.43.2}/src/specfact_cli/common/logging_utils.py +0 -0
  105. {specfact_cli-0.42.6 → specfact_cli-0.43.2}/src/specfact_cli/common/text_utils.py +0 -0
  106. {specfact_cli-0.42.6 → specfact_cli-0.43.2}/src/specfact_cli/common/utils.py +0 -0
  107. {specfact_cli-0.42.6 → specfact_cli-0.43.2}/src/specfact_cli/comparators/__init__.py +0 -0
  108. {specfact_cli-0.42.6 → specfact_cli-0.43.2}/src/specfact_cli/comparators/plan_comparator.py +0 -0
  109. {specfact_cli-0.42.6 → specfact_cli-0.43.2}/src/specfact_cli/contracts/__init__.py +0 -0
  110. {specfact_cli-0.42.6 → specfact_cli-0.43.2}/src/specfact_cli/contracts/crosshair_props.py +0 -0
  111. {specfact_cli-0.42.6 → specfact_cli-0.43.2}/src/specfact_cli/contracts/module_interface.py +0 -0
  112. {specfact_cli-0.42.6 → specfact_cli-0.43.2}/src/specfact_cli/enrichers/constitution_enricher.py +0 -0
  113. {specfact_cli-0.42.6 → specfact_cli-0.43.2}/src/specfact_cli/enrichers/plan_enricher.py +0 -0
  114. {specfact_cli-0.42.6 → specfact_cli-0.43.2}/src/specfact_cli/generators/__init__.py +0 -0
  115. {specfact_cli-0.42.6 → specfact_cli-0.43.2}/src/specfact_cli/generators/contract_generator.py +0 -0
  116. {specfact_cli-0.42.6 → specfact_cli-0.43.2}/src/specfact_cli/generators/openapi_extractor.py +0 -0
  117. {specfact_cli-0.42.6 → specfact_cli-0.43.2}/src/specfact_cli/generators/persona_exporter.py +0 -0
  118. {specfact_cli-0.42.6 → specfact_cli-0.43.2}/src/specfact_cli/generators/plan_generator.py +0 -0
  119. {specfact_cli-0.42.6 → specfact_cli-0.43.2}/src/specfact_cli/generators/protocol_generator.py +0 -0
  120. {specfact_cli-0.42.6 → specfact_cli-0.43.2}/src/specfact_cli/generators/report_generator.py +0 -0
  121. {specfact_cli-0.42.6 → specfact_cli-0.43.2}/src/specfact_cli/generators/task_generator.py +0 -0
  122. {specfact_cli-0.42.6 → specfact_cli-0.43.2}/src/specfact_cli/generators/test_to_openapi.py +0 -0
  123. {specfact_cli-0.42.6 → specfact_cli-0.43.2}/src/specfact_cli/generators/workflow_generator.py +0 -0
  124. {specfact_cli-0.42.6 → specfact_cli-0.43.2}/src/specfact_cli/groups/__init__.py +0 -0
  125. {specfact_cli-0.42.6 → specfact_cli-0.43.2}/src/specfact_cli/groups/codebase_group.py +0 -0
  126. {specfact_cli-0.42.6 → specfact_cli-0.43.2}/src/specfact_cli/groups/govern_group.py +0 -0
  127. {specfact_cli-0.42.6 → specfact_cli-0.43.2}/src/specfact_cli/groups/member_group.py +0 -0
  128. {specfact_cli-0.42.6 → specfact_cli-0.43.2}/src/specfact_cli/groups/project_group.py +0 -0
  129. {specfact_cli-0.42.6 → specfact_cli-0.43.2}/src/specfact_cli/groups/spec_group.py +0 -0
  130. {specfact_cli-0.42.6 → specfact_cli-0.43.2}/src/specfact_cli/importers/__init__.py +0 -0
  131. {specfact_cli-0.42.6 → specfact_cli-0.43.2}/src/specfact_cli/importers/speckit_converter.py +0 -0
  132. {specfact_cli-0.42.6 → specfact_cli-0.43.2}/src/specfact_cli/integrations/__init__.py +0 -0
  133. {specfact_cli-0.42.6 → specfact_cli-0.43.2}/src/specfact_cli/integrations/specmatic.py +0 -0
  134. {specfact_cli-0.42.6 → specfact_cli-0.43.2}/src/specfact_cli/merge/__init__.py +0 -0
  135. {specfact_cli-0.42.6 → specfact_cli-0.43.2}/src/specfact_cli/merge/resolver.py +0 -0
  136. {specfact_cli-0.42.6 → specfact_cli-0.43.2}/src/specfact_cli/migrations/__init__.py +0 -0
  137. {specfact_cli-0.42.6 → specfact_cli-0.43.2}/src/specfact_cli/migrations/plan_migrator.py +0 -0
  138. {specfact_cli-0.42.6 → specfact_cli-0.43.2}/src/specfact_cli/models/__init__.py +0 -0
  139. {specfact_cli-0.42.6 → specfact_cli-0.43.2}/src/specfact_cli/models/backlog_item.py +0 -0
  140. {specfact_cli-0.42.6 → specfact_cli-0.43.2}/src/specfact_cli/models/change.py +0 -0
  141. {specfact_cli-0.42.6 → specfact_cli-0.43.2}/src/specfact_cli/models/contract.py +0 -0
  142. {specfact_cli-0.42.6 → specfact_cli-0.43.2}/src/specfact_cli/models/deviation.py +0 -0
  143. {specfact_cli-0.42.6 → specfact_cli-0.43.2}/src/specfact_cli/models/dor_config.py +0 -0
  144. {specfact_cli-0.42.6 → specfact_cli-0.43.2}/src/specfact_cli/models/enforcement.py +0 -0
  145. {specfact_cli-0.42.6 → specfact_cli-0.43.2}/src/specfact_cli/models/module_package.py +0 -0
  146. {specfact_cli-0.42.6 → specfact_cli-0.43.2}/src/specfact_cli/models/persona_template.py +0 -0
  147. {specfact_cli-0.42.6 → specfact_cli-0.43.2}/src/specfact_cli/models/plan.py +0 -0
  148. {specfact_cli-0.42.6 → specfact_cli-0.43.2}/src/specfact_cli/models/project.py +0 -0
  149. {specfact_cli-0.42.6 → specfact_cli-0.43.2}/src/specfact_cli/models/protocol.py +0 -0
  150. {specfact_cli-0.42.6 → specfact_cli-0.43.2}/src/specfact_cli/models/quality.py +0 -0
  151. {specfact_cli-0.42.6 → specfact_cli-0.43.2}/src/specfact_cli/models/sdd.py +0 -0
  152. {specfact_cli-0.42.6 → specfact_cli-0.43.2}/src/specfact_cli/models/source_tracking.py +0 -0
  153. {specfact_cli-0.42.6 → specfact_cli-0.43.2}/src/specfact_cli/models/task.py +0 -0
  154. {specfact_cli-0.42.6 → specfact_cli-0.43.2}/src/specfact_cli/models/validation.py +0 -0
  155. {specfact_cli-0.42.6 → specfact_cli-0.43.2}/src/specfact_cli/modes/__init__.py +0 -0
  156. {specfact_cli-0.42.6 → specfact_cli-0.43.2}/src/specfact_cli/modes/detector.py +0 -0
  157. {specfact_cli-0.42.6 → specfact_cli-0.43.2}/src/specfact_cli/modes/router.py +0 -0
  158. {specfact_cli-0.42.6 → specfact_cli-0.43.2}/src/specfact_cli/modules/__init__.py +0 -0
  159. {specfact_cli-0.42.6 → specfact_cli-0.43.2}/src/specfact_cli/modules/_bundle_import.py +0 -0
  160. {specfact_cli-0.42.6 → specfact_cli-0.43.2}/src/specfact_cli/modules/init/src/__init__.py +0 -0
  161. {specfact_cli-0.42.6 → specfact_cli-0.43.2}/src/specfact_cli/modules/init/src/app.py +0 -0
  162. {specfact_cli-0.42.6 → specfact_cli-0.43.2}/src/specfact_cli/modules/init/src/first_run_selection.py +0 -0
  163. {specfact_cli-0.42.6 → specfact_cli-0.43.2}/src/specfact_cli/modules/module_io_shim.py +0 -0
  164. {specfact_cli-0.42.6 → specfact_cli-0.43.2}/src/specfact_cli/modules/module_registry/module-package.yaml +0 -0
  165. {specfact_cli-0.42.6 → specfact_cli-0.43.2}/src/specfact_cli/modules/module_registry/src/__init__.py +0 -0
  166. {specfact_cli-0.42.6 → specfact_cli-0.43.2}/src/specfact_cli/modules/module_registry/src/app.py +0 -0
  167. {specfact_cli-0.42.6 → specfact_cli-0.43.2}/src/specfact_cli/modules/module_registry/src/commands.py +0 -0
  168. {specfact_cli-0.42.6 → specfact_cli-0.43.2}/src/specfact_cli/modules/upgrade/module-package.yaml +0 -0
  169. {specfact_cli-0.42.6 → specfact_cli-0.43.2}/src/specfact_cli/modules/upgrade/src/__init__.py +0 -0
  170. {specfact_cli-0.42.6 → specfact_cli-0.43.2}/src/specfact_cli/modules/upgrade/src/app.py +0 -0
  171. {specfact_cli-0.42.6 → specfact_cli-0.43.2}/src/specfact_cli/modules/upgrade/src/commands.py +0 -0
  172. {specfact_cli-0.42.6 → specfact_cli-0.43.2}/src/specfact_cli/parsers/__init__.py +0 -0
  173. {specfact_cli-0.42.6 → specfact_cli-0.43.2}/src/specfact_cli/parsers/persona_importer.py +0 -0
  174. {specfact_cli-0.42.6 → specfact_cli-0.43.2}/src/specfact_cli/registry/__init__.py +0 -0
  175. {specfact_cli-0.42.6 → specfact_cli-0.43.2}/src/specfact_cli/registry/alias_manager.py +0 -0
  176. {specfact_cli-0.42.6 → specfact_cli-0.43.2}/src/specfact_cli/registry/bootstrap.py +0 -0
  177. {specfact_cli-0.42.6 → specfact_cli-0.43.2}/src/specfact_cli/registry/bridge_registry.py +0 -0
  178. {specfact_cli-0.42.6 → specfact_cli-0.43.2}/src/specfact_cli/registry/crypto_validator.py +0 -0
  179. {specfact_cli-0.42.6 → specfact_cli-0.43.2}/src/specfact_cli/registry/custom_registries.py +0 -0
  180. {specfact_cli-0.42.6 → specfact_cli-0.43.2}/src/specfact_cli/registry/dependency_resolver.py +0 -0
  181. {specfact_cli-0.42.6 → specfact_cli-0.43.2}/src/specfact_cli/registry/extension_registry.py +0 -0
  182. {specfact_cli-0.42.6 → specfact_cli-0.43.2}/src/specfact_cli/registry/help_cache.py +0 -0
  183. {specfact_cli-0.42.6 → specfact_cli-0.43.2}/src/specfact_cli/registry/marketplace_client.py +0 -0
  184. {specfact_cli-0.42.6 → specfact_cli-0.43.2}/src/specfact_cli/registry/metadata.py +0 -0
  185. {specfact_cli-0.42.6 → specfact_cli-0.43.2}/src/specfact_cli/registry/module_discovery.py +0 -0
  186. {specfact_cli-0.42.6 → specfact_cli-0.43.2}/src/specfact_cli/registry/module_grouping.py +0 -0
  187. {specfact_cli-0.42.6 → specfact_cli-0.43.2}/src/specfact_cli/registry/module_installer.py +0 -0
  188. {specfact_cli-0.42.6 → specfact_cli-0.43.2}/src/specfact_cli/registry/module_packages.py +0 -0
  189. {specfact_cli-0.42.6 → specfact_cli-0.43.2}/src/specfact_cli/registry/module_security.py +0 -0
  190. {specfact_cli-0.42.6 → specfact_cli-0.43.2}/src/specfact_cli/registry/module_state.py +0 -0
  191. {specfact_cli-0.42.6 → specfact_cli-0.43.2}/src/specfact_cli/registry/registry.py +0 -0
  192. {specfact_cli-0.42.6 → specfact_cli-0.43.2}/src/specfact_cli/resources/semgrep/async.yml +0 -0
  193. {specfact_cli-0.42.6 → specfact_cli-0.43.2}/src/specfact_cli/resources/semgrep/code-quality.yml +0 -0
  194. {specfact_cli-0.42.6 → specfact_cli-0.43.2}/src/specfact_cli/resources/semgrep/feature-detection.yml +0 -0
  195. {specfact_cli-0.42.6 → specfact_cli-0.43.2}/src/specfact_cli/runtime.py +0 -0
  196. {specfact_cli-0.42.6 → specfact_cli-0.43.2}/src/specfact_cli/sync/__init__.py +0 -0
  197. {specfact_cli-0.42.6 → specfact_cli-0.43.2}/src/specfact_cli/sync/bridge_probe.py +0 -0
  198. {specfact_cli-0.42.6 → specfact_cli-0.43.2}/src/specfact_cli/sync/bridge_sync.py +0 -0
  199. {specfact_cli-0.42.6 → specfact_cli-0.43.2}/src/specfact_cli/sync/bridge_sync_openspec_md_parse.py +0 -0
  200. {specfact_cli-0.42.6 → specfact_cli-0.43.2}/src/specfact_cli/sync/bridge_sync_requirement_from_proposal.py +0 -0
  201. {specfact_cli-0.42.6 → specfact_cli-0.43.2}/src/specfact_cli/sync/bridge_sync_requirement_helpers.py +0 -0
  202. {specfact_cli-0.42.6 → specfact_cli-0.43.2}/src/specfact_cli/sync/bridge_sync_tasks_from_proposal.py +0 -0
  203. {specfact_cli-0.42.6 → specfact_cli-0.43.2}/src/specfact_cli/sync/bridge_sync_what_changes_format.py +0 -0
  204. {specfact_cli-0.42.6 → specfact_cli-0.43.2}/src/specfact_cli/sync/bridge_sync_write_openspec_from_proposal.py +0 -0
  205. {specfact_cli-0.42.6 → specfact_cli-0.43.2}/src/specfact_cli/sync/bridge_watch.py +0 -0
  206. {specfact_cli-0.42.6 → specfact_cli-0.43.2}/src/specfact_cli/sync/change_detector.py +0 -0
  207. {specfact_cli-0.42.6 → specfact_cli-0.43.2}/src/specfact_cli/sync/code_to_spec.py +0 -0
  208. {specfact_cli-0.42.6 → specfact_cli-0.43.2}/src/specfact_cli/sync/drift_detector.py +0 -0
  209. {specfact_cli-0.42.6 → specfact_cli-0.43.2}/src/specfact_cli/sync/repository_sync.py +0 -0
  210. {specfact_cli-0.42.6 → specfact_cli-0.43.2}/src/specfact_cli/sync/spec_to_code.py +0 -0
  211. {specfact_cli-0.42.6 → specfact_cli-0.43.2}/src/specfact_cli/sync/spec_to_tests.py +0 -0
  212. {specfact_cli-0.42.6 → specfact_cli-0.43.2}/src/specfact_cli/sync/watcher.py +0 -0
  213. {specfact_cli-0.42.6 → specfact_cli-0.43.2}/src/specfact_cli/sync/watcher_enhanced.py +0 -0
  214. {specfact_cli-0.42.6 → specfact_cli-0.43.2}/src/specfact_cli/telemetry.py +0 -0
  215. {specfact_cli-0.42.6 → specfact_cli-0.43.2}/src/specfact_cli/templates/__init__.py +0 -0
  216. {specfact_cli-0.42.6 → specfact_cli-0.43.2}/src/specfact_cli/templates/defaults/defect_v1.yaml +0 -0
  217. {specfact_cli-0.42.6 → specfact_cli-0.43.2}/src/specfact_cli/templates/defaults/enabler_v1.yaml +0 -0
  218. {specfact_cli-0.42.6 → specfact_cli-0.43.2}/src/specfact_cli/templates/defaults/spike_v1.yaml +0 -0
  219. {specfact_cli-0.42.6 → specfact_cli-0.43.2}/src/specfact_cli/templates/defaults/user_story_v1.yaml +0 -0
  220. {specfact_cli-0.42.6 → specfact_cli-0.43.2}/src/specfact_cli/templates/frameworks/scrum/user_story_v1.yaml +0 -0
  221. {specfact_cli-0.42.6 → specfact_cli-0.43.2}/src/specfact_cli/templates/personas/product-owner/user_story_v1.yaml +0 -0
  222. {specfact_cli-0.42.6 → specfact_cli-0.43.2}/src/specfact_cli/templates/providers/ado/work_item_v1.yaml +0 -0
  223. {specfact_cli-0.42.6 → specfact_cli-0.43.2}/src/specfact_cli/templates/registry.py +0 -0
  224. {specfact_cli-0.42.6 → specfact_cli-0.43.2}/src/specfact_cli/templates/specification_templates.py +0 -0
  225. {specfact_cli-0.42.6 → specfact_cli-0.43.2}/src/specfact_cli/utils/__init__.py +0 -0
  226. {specfact_cli-0.42.6 → specfact_cli-0.43.2}/src/specfact_cli/utils/acceptance_criteria.py +0 -0
  227. {specfact_cli-0.42.6 → specfact_cli-0.43.2}/src/specfact_cli/utils/auth_tokens.py +0 -0
  228. {specfact_cli-0.42.6 → specfact_cli-0.43.2}/src/specfact_cli/utils/bundle_converters.py +0 -0
  229. {specfact_cli-0.42.6 → specfact_cli-0.43.2}/src/specfact_cli/utils/bundle_loader.py +0 -0
  230. {specfact_cli-0.42.6 → specfact_cli-0.43.2}/src/specfact_cli/utils/code_change_detector.py +0 -0
  231. {specfact_cli-0.42.6 → specfact_cli-0.43.2}/src/specfact_cli/utils/console.py +0 -0
  232. {specfact_cli-0.42.6 → specfact_cli-0.43.2}/src/specfact_cli/utils/content_sanitizer.py +0 -0
  233. {specfact_cli-0.42.6 → specfact_cli-0.43.2}/src/specfact_cli/utils/context_detection.py +0 -0
  234. {specfact_cli-0.42.6 → specfact_cli-0.43.2}/src/specfact_cli/utils/contract_predicates.py +0 -0
  235. {specfact_cli-0.42.6 → specfact_cli-0.43.2}/src/specfact_cli/utils/enrichment_context.py +0 -0
  236. {specfact_cli-0.42.6 → specfact_cli-0.43.2}/src/specfact_cli/utils/enrichment_parser.py +0 -0
  237. {specfact_cli-0.42.6 → specfact_cli-0.43.2}/src/specfact_cli/utils/env_manager.py +0 -0
  238. {specfact_cli-0.42.6 → specfact_cli-0.43.2}/src/specfact_cli/utils/feature_keys.py +0 -0
  239. {specfact_cli-0.42.6 → specfact_cli-0.43.2}/src/specfact_cli/utils/git.py +0 -0
  240. {specfact_cli-0.42.6 → specfact_cli-0.43.2}/src/specfact_cli/utils/github_annotations.py +0 -0
  241. {specfact_cli-0.42.6 → specfact_cli-0.43.2}/src/specfact_cli/utils/icontract_helpers.py +0 -0
  242. {specfact_cli-0.42.6 → specfact_cli-0.43.2}/src/specfact_cli/utils/incremental_check.py +0 -0
  243. {specfact_cli-0.42.6 → specfact_cli-0.43.2}/src/specfact_cli/utils/metadata.py +0 -0
  244. {specfact_cli-0.42.6 → specfact_cli-0.43.2}/src/specfact_cli/utils/optional_deps.py +0 -0
  245. {specfact_cli-0.42.6 → specfact_cli-0.43.2}/src/specfact_cli/utils/performance.py +0 -0
  246. {specfact_cli-0.42.6 → specfact_cli-0.43.2}/src/specfact_cli/utils/persona_ownership.py +0 -0
  247. {specfact_cli-0.42.6 → specfact_cli-0.43.2}/src/specfact_cli/utils/progress.py +0 -0
  248. {specfact_cli-0.42.6 → specfact_cli-0.43.2}/src/specfact_cli/utils/progressive_disclosure.py +0 -0
  249. {specfact_cli-0.42.6 → specfact_cli-0.43.2}/src/specfact_cli/utils/prompts.py +0 -0
  250. {specfact_cli-0.42.6 → specfact_cli-0.43.2}/src/specfact_cli/utils/sdd_discovery.py +0 -0
  251. {specfact_cli-0.42.6 → specfact_cli-0.43.2}/src/specfact_cli/utils/source_scanner.py +0 -0
  252. {specfact_cli-0.42.6 → specfact_cli-0.43.2}/src/specfact_cli/utils/structure.py +0 -0
  253. {specfact_cli-0.42.6 → specfact_cli-0.43.2}/src/specfact_cli/utils/structured_io.py +0 -0
  254. {specfact_cli-0.42.6 → specfact_cli-0.43.2}/src/specfact_cli/utils/suggestions.py +0 -0
  255. {specfact_cli-0.42.6 → specfact_cli-0.43.2}/src/specfact_cli/utils/terminal.py +0 -0
  256. {specfact_cli-0.42.6 → specfact_cli-0.43.2}/src/specfact_cli/utils/yaml_utils.py +0 -0
  257. {specfact_cli-0.42.6 → specfact_cli-0.43.2}/src/specfact_cli/validation/__init__.py +0 -0
  258. {specfact_cli-0.42.6 → specfact_cli-0.43.2}/src/specfact_cli/validation/command_audit.py +0 -0
  259. {specfact_cli-0.42.6 → specfact_cli-0.43.2}/src/specfact_cli/validators/__init__.py +0 -0
  260. {specfact_cli-0.42.6 → specfact_cli-0.43.2}/src/specfact_cli/validators/agile_validation.py +0 -0
  261. {specfact_cli-0.42.6 → specfact_cli-0.43.2}/src/specfact_cli/validators/change_proposal_integration.py +0 -0
  262. {specfact_cli-0.42.6 → specfact_cli-0.43.2}/src/specfact_cli/validators/cli_first_validator.py +0 -0
  263. {specfact_cli-0.42.6 → specfact_cli-0.43.2}/src/specfact_cli/validators/contract_validator.py +0 -0
  264. {specfact_cli-0.42.6 → specfact_cli-0.43.2}/src/specfact_cli/validators/fsm.py +0 -0
  265. {specfact_cli-0.42.6 → specfact_cli-0.43.2}/src/specfact_cli/validators/repro_checker.py +0 -0
  266. {specfact_cli-0.42.6 → specfact_cli-0.43.2}/src/specfact_cli/validators/schema.py +0 -0
  267. {specfact_cli-0.42.6 → specfact_cli-0.43.2}/src/specfact_cli/validators/sidecar/__init__.py +0 -0
  268. {specfact_cli-0.42.6 → specfact_cli-0.43.2}/src/specfact_cli/validators/sidecar/contract_populator.py +0 -0
  269. {specfact_cli-0.42.6 → specfact_cli-0.43.2}/src/specfact_cli/validators/sidecar/crosshair_runner.py +0 -0
  270. {specfact_cli-0.42.6 → specfact_cli-0.43.2}/src/specfact_cli/validators/sidecar/crosshair_summary.py +0 -0
  271. {specfact_cli-0.42.6 → specfact_cli-0.43.2}/src/specfact_cli/validators/sidecar/dependency_installer.py +0 -0
  272. {specfact_cli-0.42.6 → specfact_cli-0.43.2}/src/specfact_cli/validators/sidecar/framework_detector.py +0 -0
  273. {specfact_cli-0.42.6 → specfact_cli-0.43.2}/src/specfact_cli/validators/sidecar/frameworks/__init__.py +0 -0
  274. {specfact_cli-0.42.6 → specfact_cli-0.43.2}/src/specfact_cli/validators/sidecar/frameworks/base.py +0 -0
  275. {specfact_cli-0.42.6 → specfact_cli-0.43.2}/src/specfact_cli/validators/sidecar/frameworks/django.py +0 -0
  276. {specfact_cli-0.42.6 → specfact_cli-0.43.2}/src/specfact_cli/validators/sidecar/frameworks/drf.py +0 -0
  277. {specfact_cli-0.42.6 → specfact_cli-0.43.2}/src/specfact_cli/validators/sidecar/frameworks/fastapi.py +0 -0
  278. {specfact_cli-0.42.6 → specfact_cli-0.43.2}/src/specfact_cli/validators/sidecar/frameworks/flask.py +0 -0
  279. {specfact_cli-0.42.6 → specfact_cli-0.43.2}/src/specfact_cli/validators/sidecar/harness_generator.py +0 -0
  280. {specfact_cli-0.42.6 → specfact_cli-0.43.2}/src/specfact_cli/validators/sidecar/models.py +0 -0
  281. {specfact_cli-0.42.6 → specfact_cli-0.43.2}/src/specfact_cli/validators/sidecar/orchestrator.py +0 -0
  282. {specfact_cli-0.42.6 → specfact_cli-0.43.2}/src/specfact_cli/validators/sidecar/specmatic_runner.py +0 -0
  283. {specfact_cli-0.42.6 → specfact_cli-0.43.2}/src/specfact_cli/validators/sidecar/unannotated_detector.py +0 -0
  284. {specfact_cli-0.42.6 → specfact_cli-0.43.2}/src/specfact_cli/versioning/__init__.py +0 -0
  285. {specfact_cli-0.42.6 → specfact_cli-0.43.2}/src/specfact_cli/versioning/analyzer.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: specfact-cli
3
- Version: 0.42.6
3
+ Version: 0.43.2
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
@@ -282,11 +282,15 @@ Description-Content-Type: text/markdown
282
282
  # SpecFact CLI
283
283
 
284
284
  > **The "swiss knife" CLI that turns any codebase into a clear, safe, and shippable workflow.**
285
- > Keep backlog, specs, tests, and code in sync so AI-assisted changes do not break production.
285
+ > Keep backlog, specs, tests, and code in sync so changes made by people or AI copilots do not break production.
286
286
  > Works for brand-new projects and long-lived codebases - even if you are new to coding.
287
287
 
288
288
  **No API keys required. Works offline. Zero vendor lock-in.**
289
289
 
290
+ SpecFact CLI does **not** include built-in AI. It is a deterministic local CLI
291
+ that can be paired with IDE slash-command prompts so your chosen AI copilot can
292
+ invoke SpecFact as part of a command chain.
293
+
290
294
  [![PyPI version](https://img.shields.io/pypi/v/specfact-cli.svg?color=22c55e)](https://pypi.org/project/specfact-cli/)
291
295
  [![Python versions](https://img.shields.io/pypi/pyversions/specfact-cli.svg)](https://pypi.org/project/specfact-cli/)
292
296
  [![License](https://img.shields.io/badge/license-Apache%202.0-blue.svg)](LICENSE)
@@ -1,11 +1,15 @@
1
1
  # SpecFact CLI
2
2
 
3
3
  > **The "swiss knife" CLI that turns any codebase into a clear, safe, and shippable workflow.**
4
- > Keep backlog, specs, tests, and code in sync so AI-assisted changes do not break production.
4
+ > Keep backlog, specs, tests, and code in sync so changes made by people or AI copilots do not break production.
5
5
  > Works for brand-new projects and long-lived codebases - even if you are new to coding.
6
6
 
7
7
  **No API keys required. Works offline. Zero vendor lock-in.**
8
8
 
9
+ SpecFact CLI does **not** include built-in AI. It is a deterministic local CLI
10
+ that can be paired with IDE slash-command prompts so your chosen AI copilot can
11
+ invoke SpecFact as part of a command chain.
12
+
9
13
  [![PyPI version](https://img.shields.io/pypi/v/specfact-cli.svg?color=22c55e)](https://pypi.org/project/specfact-cli/)
10
14
  [![Python versions](https://img.shields.io/pypi/pyversions/specfact-cli.svg)](https://pypi.org/project/specfact-cli/)
11
15
  [![License](https://img.shields.io/badge/license-Apache%202.0-blue.svg)](LICENSE)
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "specfact-cli"
7
- version = "0.42.6"
7
+ version = "0.43.2"
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"
@@ -217,6 +217,12 @@ workflows-lint = "bash scripts/yaml-tools.sh workflows-lint {args}"
217
217
  yaml-fix-all = "bash scripts/yaml-tools.sh fix-all {args}"
218
218
  yaml-check-all = "bash scripts/yaml-tools.sh check-all {args}"
219
219
 
220
+ # Docs validation (docs-12): command examples vs CLI; modules.specfact.io URLs in docs
221
+ check-docs-commands = "python scripts/check-docs-commands.py"
222
+ check-cross-site-links = "python scripts/check-cross-site-links.py"
223
+ doc-frontmatter-check = "python scripts/check_doc_frontmatter.py"
224
+ docs-validate = "python scripts/check-docs-commands.py && python scripts/check-cross-site-links.py --warn-only && python scripts/check_doc_frontmatter.py"
225
+
220
226
  # Legacy entry (kept for compatibility); prefer `workflows-lint` above
221
227
  lint-workflows = "bash scripts/run_actionlint.sh {args}"
222
228
 
@@ -386,7 +392,6 @@ only-include = [
386
392
  sources = ["src"]
387
393
 
388
394
  [tool.hatch.build.targets.wheel.force-include]
389
- "resources/prompts" = "specfact_cli/resources/prompts"
390
395
  "resources/templates" = "specfact_cli/resources/templates"
391
396
  "resources/schemas" = "specfact_cli/resources/schemas"
392
397
  "resources/mappings" = "specfact_cli/resources/mappings"
@@ -714,6 +719,14 @@ ignore = [
714
719
  "SLF001", # private member accessed
715
720
  ]
716
721
 
722
+ "tests/unit/tools/test_smart_test_coverage.py" = [
723
+ "E402", # imports after sys.path bootstrap for tool harness loading
724
+ ]
725
+
726
+ "tests/unit/tools/test_smart_test_coverage_enhanced.py" = [
727
+ "E402", # imports after sys.path bootstrap for tool harness loading
728
+ ]
729
+
717
730
  # Tools and scripts can be more lenient
718
731
  "tools/**/*" = [
719
732
  "T20", # print found
@@ -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.42.6"
6
+ __version__ = "0.43.2"
@@ -6,6 +6,9 @@ This package provides command-line tools for:
6
6
  - Keeping backlog, specs, tests, and code in sync
7
7
  - Enforcing validation and contract checks before production
8
8
  - Supporting agile ceremonies and team workflows
9
+
10
+ When a sibling ``specfact-cli-modules`` checkout exists, startup prepends each bundle's ``src``
11
+ to ``sys.path`` so local development can load marketplace packages without installing wheels.
9
12
  """
10
13
 
11
14
  from __future__ import annotations
@@ -42,6 +45,6 @@ def _bootstrap_bundle_paths() -> None:
42
45
 
43
46
  _bootstrap_bundle_paths()
44
47
 
45
- __version__ = "0.42.6"
48
+ __version__ = "0.43.2"
46
49
 
47
50
  __all__ = ["__version__"]
@@ -9,6 +9,8 @@ from __future__ import annotations
9
9
 
10
10
  import hashlib
11
11
  import re
12
+ import shutil
13
+ import subprocess
12
14
  from pathlib import Path
13
15
  from typing import Any
14
16
 
@@ -16,6 +18,7 @@ from beartype import beartype
16
18
  from icontract import ensure, require
17
19
 
18
20
  from specfact_cli.adapters.base import BridgeAdapter
21
+ from specfact_cli.common import get_bridge_logger
19
22
  from specfact_cli.importers.speckit_converter import SpecKitConverter
20
23
  from specfact_cli.importers.speckit_scanner import SpecKitScanner
21
24
  from specfact_cli.models.bridge import BridgeConfig
@@ -35,6 +38,9 @@ from specfact_cli.utils.icontract_helpers import (
35
38
  )
36
39
 
37
40
 
41
+ logger = get_bridge_logger(__name__)
42
+
43
+
38
44
  class SpecKitAdapter(BridgeAdapter):
39
45
  """
40
46
  Spec-Kit bridge adapter implementing BridgeAdapter interface.
@@ -83,6 +89,73 @@ class SpecKitAdapter(BridgeAdapter):
83
89
  or (docs_specs_dir.exists() and docs_specs_dir.is_dir())
84
90
  )
85
91
 
92
+ @beartype
93
+ @ensure(lambda result: result is None or isinstance(result, str), "Must return str or None")
94
+ def _detect_version_from_cli(self, repo_path: Path) -> str | None:
95
+ """Attempt to detect spec-kit version by running the specify CLI."""
96
+ if not shutil.which("specify"):
97
+ return None
98
+ try:
99
+ result = subprocess.run(
100
+ ["specify", "--version"],
101
+ capture_output=True,
102
+ text=True,
103
+ timeout=5,
104
+ cwd=str(repo_path),
105
+ )
106
+ if result.returncode == 0 and result.stdout.strip():
107
+ version_match = re.search(r"(\d+\.\d+\.\d+)", result.stdout)
108
+ if version_match:
109
+ return version_match.group(1)
110
+ except subprocess.TimeoutExpired:
111
+ logger.debug("specify --version timed out after 5 seconds")
112
+ except OSError as exc:
113
+ logger.debug("specify --version failed due to OSError: %s", exc)
114
+ return None
115
+
116
+ @beartype
117
+ @ensure(lambda result: result is None or isinstance(result, str), "Must return str or None")
118
+ def _detect_version_from_heuristics(self, repo_path: Path) -> str | None:
119
+ """Estimate spec-kit version from directory structure presence."""
120
+ if (repo_path / "presets").is_dir():
121
+ return ">=0.3.0"
122
+ if (repo_path / "extensions").is_dir():
123
+ return ">=0.2.0"
124
+ if (repo_path / ".specify").is_dir():
125
+ return ">=0.1.0"
126
+ return None
127
+
128
+ @staticmethod
129
+ def _detect_layout(base_path: Path) -> tuple[str, str]:
130
+ """Determine spec-kit layout type and specs directory from repo structure."""
131
+ if (base_path / "docs" / "specs").exists():
132
+ return "modern", "docs/specs"
133
+ if (base_path / ".specify").exists():
134
+ return "modern", "specs"
135
+ return "classic", "specs"
136
+
137
+ def _detect_version(self, base_path: Path, *, skip_cli: bool) -> tuple[str | None, str | None]:
138
+ """Detect spec-kit version, returning (version, source)."""
139
+ if not skip_cli:
140
+ version = self._detect_version_from_cli(base_path)
141
+ if version:
142
+ return version, "cli"
143
+ version = self._detect_version_from_heuristics(base_path)
144
+ if version:
145
+ return version, "heuristic"
146
+ return None, None
147
+
148
+ @staticmethod
149
+ def _extract_extension_fields(
150
+ ext_list: list[dict[str, Any]],
151
+ ) -> tuple[list[str] | None, dict[str, list[str]] | None]:
152
+ """Extract extension names and command maps from scanner output."""
153
+ if not ext_list:
154
+ return None, None
155
+ names = [e["name"] for e in ext_list]
156
+ commands = {e["name"]: e.get("commands", []) for e in ext_list}
157
+ return names, commands
158
+
86
159
  @beartype
87
160
  @require(require_repo_path_exists, "Repository path must exist")
88
161
  @require(require_repo_path_is_dir, "Repository path must be a directory")
@@ -98,37 +171,30 @@ class SpecKitAdapter(BridgeAdapter):
98
171
  Returns:
99
172
  ToolCapabilities instance for Spec-Kit adapter
100
173
  """
101
- base_path = repo_path
102
- if bridge_config and bridge_config.external_base_path:
103
- base_path = bridge_config.external_base_path
104
-
105
- # Determine layout (classic vs modern)
106
- specify_dir = base_path / ".specify"
107
- docs_specs_dir = base_path / "docs" / "specs"
108
-
109
- if docs_specs_dir.exists():
110
- layout = "modern"
111
- specs_dir_path = "docs/specs"
112
- elif specify_dir.exists():
113
- layout = "modern"
114
- specs_dir_path = "specs"
115
- else:
116
- layout = "classic"
117
- specs_dir_path = "specs"
174
+ is_cross_repo = bridge_config is not None and bridge_config.external_base_path is not None
175
+ base_path: Path = bridge_config.external_base_path if is_cross_repo else repo_path # type: ignore[assignment]
118
176
 
119
- # Check for constitution file (set has_custom_hooks flag)
177
+ layout, specs_dir_path = self._detect_layout(base_path)
120
178
  scanner = SpecKitScanner(base_path)
121
179
  has_constitution, _ = scanner.has_constitution()
122
- has_custom_hooks = has_constitution
180
+ version, detected_version_source = self._detect_version(base_path, skip_cli=is_cross_repo)
181
+ extensions, extension_commands = self._extract_extension_fields(scanner.scan_extensions())
182
+ preset_names = scanner.scan_presets()
183
+ hook_list = scanner.scan_hook_events()
123
184
 
124
185
  return ToolCapabilities(
125
186
  tool="speckit",
126
- version=None, # Spec-Kit version not tracked in files
187
+ version=version,
127
188
  layout=layout,
128
189
  specs_dir=specs_dir_path,
129
- has_external_config=bridge_config is not None and bridge_config.external_base_path is not None,
130
- has_custom_hooks=has_custom_hooks,
131
- supported_sync_modes=["bidirectional", "unidirectional"], # Spec-Kit supports bidirectional sync
190
+ has_external_config=is_cross_repo,
191
+ has_custom_hooks=has_constitution,
192
+ supported_sync_modes=["bidirectional", "unidirectional"],
193
+ extensions=extensions,
194
+ extension_commands=extension_commands,
195
+ presets=preset_names or None,
196
+ hook_events=hook_list or None,
197
+ detected_version_source=detected_version_source,
132
198
  )
133
199
 
134
200
  @beartype
@@ -11,14 +11,20 @@ generate markdown artifacts in specs/ and .specify/ directories.
11
11
 
12
12
  from __future__ import annotations
13
13
 
14
+ import json
14
15
  import re
15
16
  from contextlib import suppress
16
17
  from pathlib import Path
17
- from typing import Any
18
+ from typing import Any, cast
18
19
 
19
20
  from beartype import beartype
20
21
  from icontract import ensure, require
21
22
 
23
+ from specfact_cli.common import get_bridge_logger
24
+
25
+
26
+ logger = get_bridge_logger(__name__)
27
+
22
28
 
23
29
  def _spec_file_is_markdown(spec_file: Path) -> bool:
24
30
  return spec_file.suffix == ".md"
@@ -687,6 +693,122 @@ class SpecKitScanner:
687
693
  self._parse_constitution_principles(content, memory_data)
688
694
  self._parse_constitution_governance_constraints(content, memory_data)
689
695
 
696
+ def _load_extensionignore(self) -> set[str]:
697
+ """Load extension names to ignore from .extensionignore file."""
698
+ extensionignore = self.repo_path / ".extensionignore"
699
+ if not extensionignore.exists():
700
+ return set()
701
+ try:
702
+ content = extensionignore.read_text(encoding="utf-8")
703
+ return {line.strip() for line in content.splitlines() if line.strip() and not line.startswith("#")}
704
+ except OSError:
705
+ logger.debug("Failed to read .extensionignore, proceeding without ignore rules")
706
+ return set()
707
+
708
+ def _parse_catalog_file(self, catalog_path: Path, ignored: set[str], extensions: list[dict[str, Any]]) -> None:
709
+ """Parse a single extension catalog JSON file and append results."""
710
+ if not catalog_path.exists():
711
+ return
712
+ try:
713
+ raw = json.loads(catalog_path.read_text(encoding="utf-8"))
714
+ parsed: Any = raw
715
+ items: list[Any] = (
716
+ parsed if isinstance(parsed, list) else cast("dict[str, Any]", parsed).get("extensions", [])
717
+ )
718
+ for item in items:
719
+ if not isinstance(item, dict):
720
+ continue
721
+ item_dict = cast("dict[str, Any]", item)
722
+ name: str = str(item_dict.get("name", ""))
723
+ if name and name not in ignored:
724
+ commands: list[str] = list(item_dict.get("commands") or [])
725
+ ext_version = item_dict.get("version")
726
+ extensions.append(
727
+ {"name": name, "commands": commands, "version": str(ext_version) if ext_version else None}
728
+ )
729
+ except (json.JSONDecodeError, OSError) as exc:
730
+ logger.warning("Malformed extension catalog %s: %s", catalog_path, exc)
731
+
732
+ @beartype
733
+ @ensure(lambda result: isinstance(result, list), "Must return list")
734
+ def scan_extensions(self) -> list[dict[str, Any]]:
735
+ """
736
+ Scan for spec-kit extension catalogs and return parsed extension metadata.
737
+
738
+ Parses extensions/catalog.community.json and extensions/catalog.core.json,
739
+ filtering out extensions listed in .extensionignore.
740
+
741
+ Returns:
742
+ List of extension metadata dicts with at minimum 'name' and 'commands' keys.
743
+ """
744
+ extensions_dir = self.repo_path / "extensions"
745
+ if not extensions_dir.exists() or not extensions_dir.is_dir():
746
+ return []
747
+
748
+ ignored = self._load_extensionignore()
749
+ extensions: list[dict[str, Any]] = []
750
+ for catalog_name in ("catalog.core.json", "catalog.community.json"):
751
+ self._parse_catalog_file(extensions_dir / catalog_name, ignored, extensions)
752
+
753
+ return extensions
754
+
755
+ @beartype
756
+ @ensure(lambda result: isinstance(result, list), "Must return list")
757
+ def scan_presets(self) -> list[str]:
758
+ """
759
+ Scan for spec-kit preset catalogs in the presets/ directory.
760
+
761
+ Returns:
762
+ List of detected preset names.
763
+ """
764
+ presets_dir = self.repo_path / "presets"
765
+ if not presets_dir.exists() or not presets_dir.is_dir():
766
+ return []
767
+
768
+ preset_names: list[str] = []
769
+ for item in presets_dir.iterdir():
770
+ if item.is_file() and item.suffix == ".json":
771
+ try:
772
+ data: Any = json.loads(item.read_text(encoding="utf-8"))
773
+ name = (
774
+ str(cast("dict[str, Any]", data).get("name", item.stem))
775
+ if isinstance(data, dict)
776
+ else item.stem
777
+ )
778
+ preset_names.append(name)
779
+ except (json.JSONDecodeError, OSError):
780
+ preset_names.append(item.stem)
781
+ elif item.is_dir():
782
+ preset_names.append(item.name)
783
+ return preset_names
784
+
785
+ @beartype
786
+ @ensure(lambda result: isinstance(result, list), "Must return list")
787
+ def scan_hook_events(self) -> list[str]:
788
+ """
789
+ Detect before/after hook event wiring in .specify/prompts/ template files.
790
+
791
+ Returns:
792
+ List of detected hook event types (e.g., ["before_task", "after_task"]).
793
+ """
794
+ prompts_dir = self.repo_path / ".specify" / "prompts"
795
+ if not prompts_dir.exists() or not prompts_dir.is_dir():
796
+ return []
797
+
798
+ hook_events: set[str] = set()
799
+ hook_pattern = re.compile(r"(before|after)[_-](task|plan|specify|implement|constitution)", re.IGNORECASE)
800
+
801
+ for template_file in prompts_dir.glob("*.md"):
802
+ try:
803
+ content = template_file.read_text(encoding="utf-8")
804
+ for match in hook_pattern.finditer(content):
805
+ event = f"{match.group(1).lower()}_{match.group(2).lower()}"
806
+ hook_events.add(event)
807
+ except OSError:
808
+ continue
809
+
810
+ return sorted(hook_events)
811
+
690
812
  @ensure(lambda result: isinstance(result, dict), "Must return dict")
691
813
  def parse_memory_files(self, memory_dir: Path) -> dict[str, Any]:
692
814
  """
@@ -255,7 +255,7 @@ class BridgeConfig(BaseModel):
255
255
  }
256
256
 
257
257
  commands = {
258
- "analyze": CommandMapping(
258
+ "specify": CommandMapping(
259
259
  trigger="/speckit.specify",
260
260
  input_ref="specification",
261
261
  ),
@@ -264,6 +264,27 @@ class BridgeConfig(BaseModel):
264
264
  input_ref="specification",
265
265
  output_ref="plan",
266
266
  ),
267
+ "tasks": CommandMapping(
268
+ trigger="/speckit.tasks",
269
+ input_ref="plan",
270
+ output_ref="tasks",
271
+ ),
272
+ "implement": CommandMapping(
273
+ trigger="/speckit.implement",
274
+ input_ref="tasks",
275
+ ),
276
+ "constitution": CommandMapping(
277
+ trigger="/speckit.constitution",
278
+ input_ref="constitution",
279
+ ),
280
+ "clarify": CommandMapping(
281
+ trigger="/speckit.clarify",
282
+ input_ref="specification",
283
+ ),
284
+ "analyze": CommandMapping(
285
+ trigger="/speckit.analyze",
286
+ input_ref="specification",
287
+ ),
267
288
  }
268
289
 
269
290
  templates = TemplateMapping(
@@ -319,7 +340,7 @@ class BridgeConfig(BaseModel):
319
340
  }
320
341
 
321
342
  commands = {
322
- "analyze": CommandMapping(
343
+ "specify": CommandMapping(
323
344
  trigger="/speckit.specify",
324
345
  input_ref="specification",
325
346
  ),
@@ -328,6 +349,27 @@ class BridgeConfig(BaseModel):
328
349
  input_ref="specification",
329
350
  output_ref="plan",
330
351
  ),
352
+ "tasks": CommandMapping(
353
+ trigger="/speckit.tasks",
354
+ input_ref="plan",
355
+ output_ref="tasks",
356
+ ),
357
+ "implement": CommandMapping(
358
+ trigger="/speckit.implement",
359
+ input_ref="tasks",
360
+ ),
361
+ "constitution": CommandMapping(
362
+ trigger="/speckit.constitution",
363
+ input_ref="constitution",
364
+ ),
365
+ "clarify": CommandMapping(
366
+ trigger="/speckit.clarify",
367
+ input_ref="specification",
368
+ ),
369
+ "analyze": CommandMapping(
370
+ trigger="/speckit.analyze",
371
+ input_ref="specification",
372
+ ),
331
373
  }
332
374
 
333
375
  templates = TemplateMapping(
@@ -380,7 +422,7 @@ class BridgeConfig(BaseModel):
380
422
  }
381
423
 
382
424
  commands = {
383
- "analyze": CommandMapping(
425
+ "specify": CommandMapping(
384
426
  trigger="/speckit.specify",
385
427
  input_ref="specification",
386
428
  ),
@@ -389,6 +431,27 @@ class BridgeConfig(BaseModel):
389
431
  input_ref="specification",
390
432
  output_ref="plan",
391
433
  ),
434
+ "tasks": CommandMapping(
435
+ trigger="/speckit.tasks",
436
+ input_ref="plan",
437
+ output_ref="tasks",
438
+ ),
439
+ "implement": CommandMapping(
440
+ trigger="/speckit.implement",
441
+ input_ref="tasks",
442
+ ),
443
+ "constitution": CommandMapping(
444
+ trigger="/speckit.constitution",
445
+ input_ref="constitution",
446
+ ),
447
+ "clarify": CommandMapping(
448
+ trigger="/speckit.clarify",
449
+ input_ref="specification",
450
+ ),
451
+ "analyze": CommandMapping(
452
+ trigger="/speckit.analyze",
453
+ input_ref="specification",
454
+ ),
392
455
  }
393
456
 
394
457
  templates = TemplateMapping(
@@ -18,3 +18,9 @@ class ToolCapabilities:
18
18
  supported_sync_modes: list[str] | None = (
19
19
  None # Supported sync modes (e.g., ["bidirectional", "unidirectional", "read-only", "export-only"])
20
20
  )
21
+ # Spec-Kit v0.4.x alignment fields
22
+ extensions: list[str] | None = None # Detected extension names (e.g., ["reconcile", "sync", "verify"])
23
+ extension_commands: dict[str, list[str]] | None = None # Extension name → provided commands
24
+ presets: list[str] | None = None # Active preset names
25
+ hook_events: list[str] | None = None # Detected hook event types (e.g., ["before_task", "after_task"])
26
+ detected_version_source: str | None = None # How version was detected: "cli", "heuristic", or None
@@ -1,5 +1,5 @@
1
1
  name: init
2
- version: 0.1.18
2
+ version: 0.1.19
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:218801ddd11b02e90e386a3019685add0d14a9a09d246ef958c05f53c9b46a72
21
- signature: o9QdwF5+ASt8dJy5D38PgMy7pysqZUJqOaDHHjcRPXoI1dGpeSyOKjJOOtboqoH9qN2a+4nhZX6S5NGp8hlPCA==
20
+ checksum: sha256:a0ca0fb136f278a11a113be78047c3c7037de9a393c27f8e677a26c4ab2ba659
21
+ signature: r3czsyG/tinaxMTd/lNkhjXQqRUU0ecLywXt1iDWPO0QuExVM+msp5tuAud03QPnuCsMXNdyBWWSDBovPeY+Aw==
@@ -38,7 +38,6 @@ from specfact_cli.utils.ide_setup import (
38
38
  discover_prompt_sources_catalog,
39
39
  discover_prompt_template_files,
40
40
  expected_ide_prompt_export_paths,
41
- find_package_resources_path,
42
41
  load_ide_prompt_export_source_ids,
43
42
  write_ide_prompt_export_state,
44
43
  )
@@ -310,8 +309,8 @@ def _select_module_ids_interactive(action: str, modules_list: list[dict[str, Any
310
309
 
311
310
 
312
311
  def _resolve_templates_dir(repo_path: Path) -> Path | None:
313
- """Resolve templates directory from repo checkout or installed package."""
314
- prompt_files = discover_prompt_template_files(repo_path, include_package_fallback=False)
312
+ """Resolve a representative templates directory from installed modules or a dev repo checkout."""
313
+ prompt_files = discover_prompt_template_files(repo_path, include_package_fallback=True)
315
314
  if prompt_files:
316
315
  return prompt_files[0].parent
317
316
 
@@ -319,7 +318,7 @@ def _resolve_templates_dir(repo_path: Path) -> Path | None:
319
318
  if dev_templates_dir.exists():
320
319
  return dev_templates_dir
321
320
 
322
- return find_package_resources_path("specfact_cli", "resources/prompts")
321
+ return None
323
322
 
324
323
 
325
324
  def _audit_prompt_installation(repo_path: Path) -> None:
@@ -2,7 +2,7 @@
2
2
 
3
3
  from __future__ import annotations
4
4
 
5
- from typing import Any
5
+ from typing import Any, cast
6
6
 
7
7
  from beartype import beartype
8
8
  from icontract import ensure, require
@@ -120,7 +120,8 @@ def _questionary_style() -> Any:
120
120
  import questionary # type: ignore[reportMissingImports]
121
121
  except ImportError:
122
122
  return None
123
- return questionary.Style(
123
+ q = cast(Any, questionary)
124
+ return q.Style(
124
125
  [
125
126
  ("qmark", "fg:#00af87 bold"),
126
127
  ("question", "bold"),
@@ -209,7 +210,8 @@ def select_module_ids_interactive(action: str, modules_list: list[dict[str, Any]
209
210
  console.print(f"[cyan]{action_title} Modules[/cyan] (currently {current_state})")
210
211
  console.print("[dim]Controls: arrows navigate, space toggle, enter confirm[/dim]")
211
212
  display_to_id, choices = _checkbox_choices_for_modules(candidates)
212
- selected: list[str] | None = questionary.checkbox(
213
+ q = cast(Any, questionary)
214
+ selected: list[str] | None = q.checkbox(
213
215
  f"{action_title} module(s):",
214
216
  choices=choices,
215
217
  instruction="(multi-select)",
@@ -259,8 +259,10 @@ def discover_prompt_sources_catalog(
259
259
  """
260
260
  Build prompt templates grouped by owning source: ``core`` or a module id (``module-package.yaml`` name).
261
261
 
262
- Core templates come from the repo checkout or the installed ``specfact_cli`` package. Module templates
263
- are discovered from effective module roots (builtin, project, user, marketplace, custom).
262
+ Core templates may come from a repo checkout under ``resources/prompts`` or the installed package when
263
+ present; workflow prompts are normally provided by bundle modules under ``resources/prompts`` at the
264
+ module root. Module templates are discovered from effective module roots (builtin, project, user,
265
+ marketplace, custom).
264
266
 
265
267
  When a module ships a template with the same source filename as core (e.g. ``specfact.01-import.md``),
266
268
  the module copy wins: core does not list that basename so exports stay single-sourced.