specfact-cli 0.23.0__tar.gz → 0.24.0__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 (210) hide show
  1. {specfact_cli-0.23.0 → specfact_cli-0.24.0}/PKG-INFO +15 -2
  2. {specfact_cli-0.23.0 → specfact_cli-0.24.0}/README.md +14 -1
  3. {specfact_cli-0.23.0 → specfact_cli-0.24.0}/pyproject.toml +1 -1
  4. {specfact_cli-0.23.0 → specfact_cli-0.24.0}/resources/templates/sidecar/common/run_sidecar.sh +124 -35
  5. {specfact_cli-0.23.0 → specfact_cli-0.24.0}/src/__init__.py +1 -1
  6. {specfact_cli-0.23.0 → specfact_cli-0.24.0}/src/specfact_cli/__init__.py +1 -1
  7. {specfact_cli-0.23.0 → specfact_cli-0.24.0}/src/specfact_cli/analyzers/contract_extractor.py +9 -2
  8. {specfact_cli-0.23.0 → specfact_cli-0.24.0}/src/specfact_cli/cli.py +2 -0
  9. {specfact_cli-0.23.0 → specfact_cli-0.24.0}/src/specfact_cli/commands/__init__.py +2 -0
  10. {specfact_cli-0.23.0 → specfact_cli-0.24.0}/src/specfact_cli/commands/import_cmd.py +479 -91
  11. {specfact_cli-0.23.0 → specfact_cli-0.24.0}/src/specfact_cli/commands/init.py +3 -0
  12. {specfact_cli-0.23.0 → specfact_cli-0.24.0}/src/specfact_cli/commands/repro.py +45 -0
  13. specfact_cli-0.24.0/src/specfact_cli/commands/validate.py +158 -0
  14. {specfact_cli-0.23.0 → specfact_cli-0.24.0}/src/specfact_cli/generators/openapi_extractor.py +268 -75
  15. {specfact_cli-0.23.0 → specfact_cli-0.24.0}/src/specfact_cli/sync/bridge_sync.py +7 -1
  16. {specfact_cli-0.23.0 → specfact_cli-0.24.0}/src/specfact_cli/utils/code_change_detector.py +7 -1
  17. {specfact_cli-0.23.0 → specfact_cli-0.24.0}/src/specfact_cli/utils/incremental_check.py +68 -1
  18. {specfact_cli-0.23.0 → specfact_cli-0.24.0}/src/specfact_cli/utils/progress.py +42 -12
  19. specfact_cli-0.24.0/src/specfact_cli/validators/sidecar/__init__.py +11 -0
  20. specfact_cli-0.24.0/src/specfact_cli/validators/sidecar/contract_populator.py +145 -0
  21. specfact_cli-0.24.0/src/specfact_cli/validators/sidecar/crosshair_runner.py +98 -0
  22. specfact_cli-0.24.0/src/specfact_cli/validators/sidecar/crosshair_summary.py +164 -0
  23. specfact_cli-0.24.0/src/specfact_cli/validators/sidecar/framework_detector.py +184 -0
  24. specfact_cli-0.24.0/src/specfact_cli/validators/sidecar/frameworks/__init__.py +12 -0
  25. specfact_cli-0.24.0/src/specfact_cli/validators/sidecar/frameworks/base.py +84 -0
  26. specfact_cli-0.24.0/src/specfact_cli/validators/sidecar/frameworks/django.py +322 -0
  27. specfact_cli-0.24.0/src/specfact_cli/validators/sidecar/frameworks/drf.py +95 -0
  28. specfact_cli-0.24.0/src/specfact_cli/validators/sidecar/frameworks/fastapi.py +225 -0
  29. specfact_cli-0.24.0/src/specfact_cli/validators/sidecar/harness_generator.py +148 -0
  30. specfact_cli-0.24.0/src/specfact_cli/validators/sidecar/models.py +184 -0
  31. specfact_cli-0.24.0/src/specfact_cli/validators/sidecar/orchestrator.py +357 -0
  32. specfact_cli-0.24.0/src/specfact_cli/validators/sidecar/specmatic_runner.py +106 -0
  33. specfact_cli-0.24.0/src/specfact_cli/validators/sidecar/unannotated_detector.py +140 -0
  34. {specfact_cli-0.23.0 → specfact_cli-0.24.0}/.gitignore +0 -0
  35. {specfact_cli-0.23.0 → specfact_cli-0.24.0}/LICENSE.md +0 -0
  36. {specfact_cli-0.23.0 → specfact_cli-0.24.0}/resources/mappings/node-async.yaml +0 -0
  37. {specfact_cli-0.23.0 → specfact_cli-0.24.0}/resources/mappings/python-async.yaml +0 -0
  38. {specfact_cli-0.23.0 → specfact_cli-0.24.0}/resources/mappings/speckit-default.yaml +0 -0
  39. {specfact_cli-0.23.0 → specfact_cli-0.24.0}/resources/prompts/shared/cli-enforcement.md +0 -0
  40. {specfact_cli-0.23.0 → specfact_cli-0.24.0}/resources/prompts/specfact.01-import.md +0 -0
  41. {specfact_cli-0.23.0 → specfact_cli-0.24.0}/resources/prompts/specfact.02-plan.md +0 -0
  42. {specfact_cli-0.23.0 → specfact_cli-0.24.0}/resources/prompts/specfact.03-review.md +0 -0
  43. {specfact_cli-0.23.0 → specfact_cli-0.24.0}/resources/prompts/specfact.04-sdd.md +0 -0
  44. {specfact_cli-0.23.0 → specfact_cli-0.24.0}/resources/prompts/specfact.05-enforce.md +0 -0
  45. {specfact_cli-0.23.0 → specfact_cli-0.24.0}/resources/prompts/specfact.06-sync.md +0 -0
  46. {specfact_cli-0.23.0 → specfact_cli-0.24.0}/resources/prompts/specfact.07-contracts.md +0 -0
  47. {specfact_cli-0.23.0 → specfact_cli-0.24.0}/resources/prompts/specfact.compare.md +0 -0
  48. {specfact_cli-0.23.0 → specfact_cli-0.24.0}/resources/prompts/specfact.sync-backlog.md +0 -0
  49. {specfact_cli-0.23.0 → specfact_cli-0.24.0}/resources/prompts/specfact.validate.md +0 -0
  50. {specfact_cli-0.23.0 → specfact_cli-0.24.0}/resources/schemas/deviation.schema.json +0 -0
  51. {specfact_cli-0.23.0 → specfact_cli-0.24.0}/resources/schemas/plan.schema.json +0 -0
  52. {specfact_cli-0.23.0 → specfact_cli-0.24.0}/resources/schemas/protocol.schema.json +0 -0
  53. {specfact_cli-0.23.0 → specfact_cli-0.24.0}/resources/templates/github-action.yml.j2 +0 -0
  54. {specfact_cli-0.23.0 → specfact_cli-0.24.0}/resources/templates/persona/architect.md.j2 +0 -0
  55. {specfact_cli-0.23.0 → specfact_cli-0.24.0}/resources/templates/persona/developer.md.j2 +0 -0
  56. {specfact_cli-0.23.0 → specfact_cli-0.24.0}/resources/templates/persona/product-owner.md.j2 +0 -0
  57. {specfact_cli-0.23.0 → specfact_cli-0.24.0}/resources/templates/plan.bundle.yaml.j2 +0 -0
  58. {specfact_cli-0.23.0 → specfact_cli-0.24.0}/resources/templates/pr-template.md.j2 +0 -0
  59. {specfact_cli-0.23.0 → specfact_cli-0.24.0}/resources/templates/protocol.yaml.j2 +0 -0
  60. {specfact_cli-0.23.0 → specfact_cli-0.24.0}/resources/templates/sidecar/STRUCTURE.md +0 -0
  61. {specfact_cli-0.23.0 → specfact_cli-0.24.0}/resources/templates/sidecar/__init__.py +0 -0
  62. {specfact_cli-0.23.0 → specfact_cli-0.24.0}/resources/templates/sidecar/common/README.md +0 -0
  63. {specfact_cli-0.23.0 → specfact_cli-0.24.0}/resources/templates/sidecar/common/adapters.py +0 -0
  64. {specfact_cli-0.23.0 → specfact_cli-0.24.0}/resources/templates/sidecar/common/bindings.yaml.example +0 -0
  65. {specfact_cli-0.23.0 → specfact_cli-0.24.0}/resources/templates/sidecar/common/crosshair_plugin.py +0 -0
  66. {specfact_cli-0.23.0 → specfact_cli-0.24.0}/resources/templates/sidecar/common/generate_harness.py +0 -0
  67. {specfact_cli-0.23.0 → specfact_cli-0.24.0}/resources/templates/sidecar/common/harness_contracts.py.example +0 -0
  68. {specfact_cli-0.23.0 → specfact_cli-0.24.0}/resources/templates/sidecar/common/populate_contracts.py +0 -0
  69. {specfact_cli-0.23.0 → specfact_cli-0.24.0}/resources/templates/sidecar/common/sidecar-init.sh +0 -0
  70. {specfact_cli-0.23.0 → specfact_cli-0.24.0}/resources/templates/sidecar/frameworks/django/__init__.py +0 -0
  71. {specfact_cli-0.23.0 → specfact_cli-0.24.0}/resources/templates/sidecar/frameworks/django/crosshair_django_wrapper.py +0 -0
  72. {specfact_cli-0.23.0 → specfact_cli-0.24.0}/resources/templates/sidecar/frameworks/django/django_form_extractor.py +0 -0
  73. {specfact_cli-0.23.0 → specfact_cli-0.24.0}/resources/templates/sidecar/frameworks/django/django_url_extractor.py +0 -0
  74. {specfact_cli-0.23.0 → specfact_cli-0.24.0}/resources/templates/sidecar/frameworks/drf/__init__.py +0 -0
  75. {specfact_cli-0.23.0 → specfact_cli-0.24.0}/resources/templates/sidecar/frameworks/drf/drf_serializer_extractor.py +0 -0
  76. {specfact_cli-0.23.0 → specfact_cli-0.24.0}/resources/templates/sidecar/frameworks/fastapi/__init__.py +0 -0
  77. {specfact_cli-0.23.0 → specfact_cli-0.24.0}/resources/templates/sidecar/frameworks/fastapi/fastapi_route_extractor.py +0 -0
  78. {specfact_cli-0.23.0 → specfact_cli-0.24.0}/resources/templates/telemetry.yaml.example +0 -0
  79. {specfact_cli-0.23.0 → specfact_cli-0.24.0}/src/specfact_cli/adapters/__init__.py +0 -0
  80. {specfact_cli-0.23.0 → specfact_cli-0.24.0}/src/specfact_cli/adapters/base.py +0 -0
  81. {specfact_cli-0.23.0 → specfact_cli-0.24.0}/src/specfact_cli/adapters/github.py +0 -0
  82. {specfact_cli-0.23.0 → specfact_cli-0.24.0}/src/specfact_cli/adapters/openspec.py +0 -0
  83. {specfact_cli-0.23.0 → specfact_cli-0.24.0}/src/specfact_cli/adapters/openspec_parser.py +0 -0
  84. {specfact_cli-0.23.0 → specfact_cli-0.24.0}/src/specfact_cli/adapters/registry.py +0 -0
  85. {specfact_cli-0.23.0 → specfact_cli-0.24.0}/src/specfact_cli/adapters/speckit.py +0 -0
  86. {specfact_cli-0.23.0 → specfact_cli-0.24.0}/src/specfact_cli/agents/__init__.py +0 -0
  87. {specfact_cli-0.23.0 → specfact_cli-0.24.0}/src/specfact_cli/agents/analyze_agent.py +0 -0
  88. {specfact_cli-0.23.0 → specfact_cli-0.24.0}/src/specfact_cli/agents/base.py +0 -0
  89. {specfact_cli-0.23.0 → specfact_cli-0.24.0}/src/specfact_cli/agents/plan_agent.py +0 -0
  90. {specfact_cli-0.23.0 → specfact_cli-0.24.0}/src/specfact_cli/agents/registry.py +0 -0
  91. {specfact_cli-0.23.0 → specfact_cli-0.24.0}/src/specfact_cli/agents/sync_agent.py +0 -0
  92. {specfact_cli-0.23.0 → specfact_cli-0.24.0}/src/specfact_cli/analyzers/__init__.py +0 -0
  93. {specfact_cli-0.23.0 → specfact_cli-0.24.0}/src/specfact_cli/analyzers/ambiguity_scanner.py +0 -0
  94. {specfact_cli-0.23.0 → specfact_cli-0.24.0}/src/specfact_cli/analyzers/code_analyzer.py +0 -0
  95. {specfact_cli-0.23.0 → specfact_cli-0.24.0}/src/specfact_cli/analyzers/constitution_evidence_extractor.py +0 -0
  96. {specfact_cli-0.23.0 → specfact_cli-0.24.0}/src/specfact_cli/analyzers/control_flow_analyzer.py +0 -0
  97. {specfact_cli-0.23.0 → specfact_cli-0.24.0}/src/specfact_cli/analyzers/graph_analyzer.py +0 -0
  98. {specfact_cli-0.23.0 → specfact_cli-0.24.0}/src/specfact_cli/analyzers/relationship_mapper.py +0 -0
  99. {specfact_cli-0.23.0 → specfact_cli-0.24.0}/src/specfact_cli/analyzers/requirement_extractor.py +0 -0
  100. {specfact_cli-0.23.0 → specfact_cli-0.24.0}/src/specfact_cli/analyzers/test_pattern_extractor.py +0 -0
  101. {specfact_cli-0.23.0 → specfact_cli-0.24.0}/src/specfact_cli/commands/analyze.py +0 -0
  102. {specfact_cli-0.23.0 → specfact_cli-0.24.0}/src/specfact_cli/commands/contract_cmd.py +0 -0
  103. {specfact_cli-0.23.0 → specfact_cli-0.24.0}/src/specfact_cli/commands/drift.py +0 -0
  104. {specfact_cli-0.23.0 → specfact_cli-0.24.0}/src/specfact_cli/commands/enforce.py +0 -0
  105. {specfact_cli-0.23.0 → specfact_cli-0.24.0}/src/specfact_cli/commands/generate.py +0 -0
  106. {specfact_cli-0.23.0 → specfact_cli-0.24.0}/src/specfact_cli/commands/migrate.py +0 -0
  107. {specfact_cli-0.23.0 → specfact_cli-0.24.0}/src/specfact_cli/commands/plan.py +0 -0
  108. {specfact_cli-0.23.0 → specfact_cli-0.24.0}/src/specfact_cli/commands/project_cmd.py +0 -0
  109. {specfact_cli-0.23.0 → specfact_cli-0.24.0}/src/specfact_cli/commands/sdd.py +0 -0
  110. {specfact_cli-0.23.0 → specfact_cli-0.24.0}/src/specfact_cli/commands/spec.py +0 -0
  111. {specfact_cli-0.23.0 → specfact_cli-0.24.0}/src/specfact_cli/commands/sync.py +0 -0
  112. {specfact_cli-0.23.0 → specfact_cli-0.24.0}/src/specfact_cli/common/__init__.py +0 -0
  113. {specfact_cli-0.23.0 → specfact_cli-0.24.0}/src/specfact_cli/common/logger_setup.py +0 -0
  114. {specfact_cli-0.23.0 → specfact_cli-0.24.0}/src/specfact_cli/common/logging_utils.py +0 -0
  115. {specfact_cli-0.23.0 → specfact_cli-0.24.0}/src/specfact_cli/common/text_utils.py +0 -0
  116. {specfact_cli-0.23.0 → specfact_cli-0.24.0}/src/specfact_cli/common/utils.py +0 -0
  117. {specfact_cli-0.23.0 → specfact_cli-0.24.0}/src/specfact_cli/comparators/__init__.py +0 -0
  118. {specfact_cli-0.23.0 → specfact_cli-0.24.0}/src/specfact_cli/comparators/plan_comparator.py +0 -0
  119. {specfact_cli-0.23.0 → specfact_cli-0.24.0}/src/specfact_cli/enrichers/constitution_enricher.py +0 -0
  120. {specfact_cli-0.23.0 → specfact_cli-0.24.0}/src/specfact_cli/enrichers/plan_enricher.py +0 -0
  121. {specfact_cli-0.23.0 → specfact_cli-0.24.0}/src/specfact_cli/generators/__init__.py +0 -0
  122. {specfact_cli-0.23.0 → specfact_cli-0.24.0}/src/specfact_cli/generators/contract_generator.py +0 -0
  123. {specfact_cli-0.23.0 → specfact_cli-0.24.0}/src/specfact_cli/generators/persona_exporter.py +0 -0
  124. {specfact_cli-0.23.0 → specfact_cli-0.24.0}/src/specfact_cli/generators/plan_generator.py +0 -0
  125. {specfact_cli-0.23.0 → specfact_cli-0.24.0}/src/specfact_cli/generators/protocol_generator.py +0 -0
  126. {specfact_cli-0.23.0 → specfact_cli-0.24.0}/src/specfact_cli/generators/report_generator.py +0 -0
  127. {specfact_cli-0.23.0 → specfact_cli-0.24.0}/src/specfact_cli/generators/task_generator.py +0 -0
  128. {specfact_cli-0.23.0 → specfact_cli-0.24.0}/src/specfact_cli/generators/test_to_openapi.py +0 -0
  129. {specfact_cli-0.23.0 → specfact_cli-0.24.0}/src/specfact_cli/generators/workflow_generator.py +0 -0
  130. {specfact_cli-0.23.0 → specfact_cli-0.24.0}/src/specfact_cli/importers/__init__.py +0 -0
  131. {specfact_cli-0.23.0 → specfact_cli-0.24.0}/src/specfact_cli/importers/speckit_converter.py +0 -0
  132. {specfact_cli-0.23.0 → specfact_cli-0.24.0}/src/specfact_cli/importers/speckit_scanner.py +0 -0
  133. {specfact_cli-0.23.0 → specfact_cli-0.24.0}/src/specfact_cli/integrations/__init__.py +0 -0
  134. {specfact_cli-0.23.0 → specfact_cli-0.24.0}/src/specfact_cli/integrations/specmatic.py +0 -0
  135. {specfact_cli-0.23.0 → specfact_cli-0.24.0}/src/specfact_cli/merge/__init__.py +0 -0
  136. {specfact_cli-0.23.0 → specfact_cli-0.24.0}/src/specfact_cli/merge/resolver.py +0 -0
  137. {specfact_cli-0.23.0 → specfact_cli-0.24.0}/src/specfact_cli/migrations/__init__.py +0 -0
  138. {specfact_cli-0.23.0 → specfact_cli-0.24.0}/src/specfact_cli/migrations/plan_migrator.py +0 -0
  139. {specfact_cli-0.23.0 → specfact_cli-0.24.0}/src/specfact_cli/models/__init__.py +0 -0
  140. {specfact_cli-0.23.0 → specfact_cli-0.24.0}/src/specfact_cli/models/bridge.py +0 -0
  141. {specfact_cli-0.23.0 → specfact_cli-0.24.0}/src/specfact_cli/models/capabilities.py +0 -0
  142. {specfact_cli-0.23.0 → specfact_cli-0.24.0}/src/specfact_cli/models/change.py +0 -0
  143. {specfact_cli-0.23.0 → specfact_cli-0.24.0}/src/specfact_cli/models/contract.py +0 -0
  144. {specfact_cli-0.23.0 → specfact_cli-0.24.0}/src/specfact_cli/models/deviation.py +0 -0
  145. {specfact_cli-0.23.0 → specfact_cli-0.24.0}/src/specfact_cli/models/enforcement.py +0 -0
  146. {specfact_cli-0.23.0 → specfact_cli-0.24.0}/src/specfact_cli/models/persona_template.py +0 -0
  147. {specfact_cli-0.23.0 → specfact_cli-0.24.0}/src/specfact_cli/models/plan.py +0 -0
  148. {specfact_cli-0.23.0 → specfact_cli-0.24.0}/src/specfact_cli/models/project.py +0 -0
  149. {specfact_cli-0.23.0 → specfact_cli-0.24.0}/src/specfact_cli/models/protocol.py +0 -0
  150. {specfact_cli-0.23.0 → specfact_cli-0.24.0}/src/specfact_cli/models/quality.py +0 -0
  151. {specfact_cli-0.23.0 → specfact_cli-0.24.0}/src/specfact_cli/models/sdd.py +0 -0
  152. {specfact_cli-0.23.0 → specfact_cli-0.24.0}/src/specfact_cli/models/source_tracking.py +0 -0
  153. {specfact_cli-0.23.0 → specfact_cli-0.24.0}/src/specfact_cli/models/task.py +0 -0
  154. {specfact_cli-0.23.0 → specfact_cli-0.24.0}/src/specfact_cli/modes/__init__.py +0 -0
  155. {specfact_cli-0.23.0 → specfact_cli-0.24.0}/src/specfact_cli/modes/detector.py +0 -0
  156. {specfact_cli-0.23.0 → specfact_cli-0.24.0}/src/specfact_cli/modes/router.py +0 -0
  157. {specfact_cli-0.23.0 → specfact_cli-0.24.0}/src/specfact_cli/parsers/__init__.py +0 -0
  158. {specfact_cli-0.23.0 → specfact_cli-0.24.0}/src/specfact_cli/parsers/persona_importer.py +0 -0
  159. {specfact_cli-0.23.0 → specfact_cli-0.24.0}/src/specfact_cli/resources/semgrep/async.yml +0 -0
  160. {specfact_cli-0.23.0 → specfact_cli-0.24.0}/src/specfact_cli/resources/semgrep/code-quality.yml +0 -0
  161. {specfact_cli-0.23.0 → specfact_cli-0.24.0}/src/specfact_cli/resources/semgrep/feature-detection.yml +0 -0
  162. {specfact_cli-0.23.0 → specfact_cli-0.24.0}/src/specfact_cli/runtime.py +0 -0
  163. {specfact_cli-0.23.0 → specfact_cli-0.24.0}/src/specfact_cli/sync/__init__.py +0 -0
  164. {specfact_cli-0.23.0 → specfact_cli-0.24.0}/src/specfact_cli/sync/bridge_probe.py +0 -0
  165. {specfact_cli-0.23.0 → specfact_cli-0.24.0}/src/specfact_cli/sync/bridge_watch.py +0 -0
  166. {specfact_cli-0.23.0 → specfact_cli-0.24.0}/src/specfact_cli/sync/change_detector.py +0 -0
  167. {specfact_cli-0.23.0 → specfact_cli-0.24.0}/src/specfact_cli/sync/code_to_spec.py +0 -0
  168. {specfact_cli-0.23.0 → specfact_cli-0.24.0}/src/specfact_cli/sync/drift_detector.py +0 -0
  169. {specfact_cli-0.23.0 → specfact_cli-0.24.0}/src/specfact_cli/sync/repository_sync.py +0 -0
  170. {specfact_cli-0.23.0 → specfact_cli-0.24.0}/src/specfact_cli/sync/spec_to_code.py +0 -0
  171. {specfact_cli-0.23.0 → specfact_cli-0.24.0}/src/specfact_cli/sync/spec_to_tests.py +0 -0
  172. {specfact_cli-0.23.0 → specfact_cli-0.24.0}/src/specfact_cli/sync/watcher.py +0 -0
  173. {specfact_cli-0.23.0 → specfact_cli-0.24.0}/src/specfact_cli/sync/watcher_enhanced.py +0 -0
  174. {specfact_cli-0.23.0 → specfact_cli-0.24.0}/src/specfact_cli/telemetry.py +0 -0
  175. {specfact_cli-0.23.0 → specfact_cli-0.24.0}/src/specfact_cli/templates/__init__.py +0 -0
  176. {specfact_cli-0.23.0 → specfact_cli-0.24.0}/src/specfact_cli/templates/bridge_templates.py +0 -0
  177. {specfact_cli-0.23.0 → specfact_cli-0.24.0}/src/specfact_cli/templates/specification_templates.py +0 -0
  178. {specfact_cli-0.23.0 → specfact_cli-0.24.0}/src/specfact_cli/utils/__init__.py +0 -0
  179. {specfact_cli-0.23.0 → specfact_cli-0.24.0}/src/specfact_cli/utils/acceptance_criteria.py +0 -0
  180. {specfact_cli-0.23.0 → specfact_cli-0.24.0}/src/specfact_cli/utils/bundle_loader.py +0 -0
  181. {specfact_cli-0.23.0 → specfact_cli-0.24.0}/src/specfact_cli/utils/console.py +0 -0
  182. {specfact_cli-0.23.0 → specfact_cli-0.24.0}/src/specfact_cli/utils/content_sanitizer.py +0 -0
  183. {specfact_cli-0.23.0 → specfact_cli-0.24.0}/src/specfact_cli/utils/context_detection.py +0 -0
  184. {specfact_cli-0.23.0 → specfact_cli-0.24.0}/src/specfact_cli/utils/enrichment_context.py +0 -0
  185. {specfact_cli-0.23.0 → specfact_cli-0.24.0}/src/specfact_cli/utils/enrichment_parser.py +0 -0
  186. {specfact_cli-0.23.0 → specfact_cli-0.24.0}/src/specfact_cli/utils/env_manager.py +0 -0
  187. {specfact_cli-0.23.0 → specfact_cli-0.24.0}/src/specfact_cli/utils/feature_keys.py +0 -0
  188. {specfact_cli-0.23.0 → specfact_cli-0.24.0}/src/specfact_cli/utils/git.py +0 -0
  189. {specfact_cli-0.23.0 → specfact_cli-0.24.0}/src/specfact_cli/utils/github_annotations.py +0 -0
  190. {specfact_cli-0.23.0 → specfact_cli-0.24.0}/src/specfact_cli/utils/ide_setup.py +0 -0
  191. {specfact_cli-0.23.0 → specfact_cli-0.24.0}/src/specfact_cli/utils/optional_deps.py +0 -0
  192. {specfact_cli-0.23.0 → specfact_cli-0.24.0}/src/specfact_cli/utils/performance.py +0 -0
  193. {specfact_cli-0.23.0 → specfact_cli-0.24.0}/src/specfact_cli/utils/progressive_disclosure.py +0 -0
  194. {specfact_cli-0.23.0 → specfact_cli-0.24.0}/src/specfact_cli/utils/prompts.py +0 -0
  195. {specfact_cli-0.23.0 → specfact_cli-0.24.0}/src/specfact_cli/utils/sdd_discovery.py +0 -0
  196. {specfact_cli-0.23.0 → specfact_cli-0.24.0}/src/specfact_cli/utils/source_scanner.py +0 -0
  197. {specfact_cli-0.23.0 → specfact_cli-0.24.0}/src/specfact_cli/utils/structure.py +0 -0
  198. {specfact_cli-0.23.0 → specfact_cli-0.24.0}/src/specfact_cli/utils/structured_io.py +0 -0
  199. {specfact_cli-0.23.0 → specfact_cli-0.24.0}/src/specfact_cli/utils/suggestions.py +0 -0
  200. {specfact_cli-0.23.0 → specfact_cli-0.24.0}/src/specfact_cli/utils/terminal.py +0 -0
  201. {specfact_cli-0.23.0 → specfact_cli-0.24.0}/src/specfact_cli/utils/yaml_utils.py +0 -0
  202. {specfact_cli-0.23.0 → specfact_cli-0.24.0}/src/specfact_cli/validators/__init__.py +0 -0
  203. {specfact_cli-0.23.0 → specfact_cli-0.24.0}/src/specfact_cli/validators/agile_validation.py +0 -0
  204. {specfact_cli-0.23.0 → specfact_cli-0.24.0}/src/specfact_cli/validators/cli_first_validator.py +0 -0
  205. {specfact_cli-0.23.0 → specfact_cli-0.24.0}/src/specfact_cli/validators/contract_validator.py +0 -0
  206. {specfact_cli-0.23.0 → specfact_cli-0.24.0}/src/specfact_cli/validators/fsm.py +0 -0
  207. {specfact_cli-0.23.0 → specfact_cli-0.24.0}/src/specfact_cli/validators/repro_checker.py +0 -0
  208. {specfact_cli-0.23.0 → specfact_cli-0.24.0}/src/specfact_cli/validators/schema.py +0 -0
  209. {specfact_cli-0.23.0 → specfact_cli-0.24.0}/src/specfact_cli/versioning/__init__.py +0 -0
  210. {specfact_cli-0.23.0 → specfact_cli-0.24.0}/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.23.0
3
+ Version: 0.24.0
4
4
  Summary: Brownfield-first CLI: Reverse engineer legacy Python → specs → enforced contracts. Automate legacy code documentation and prevent modernization regressions.
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
@@ -368,6 +368,10 @@ specfact init --ide cursor --install-deps
368
368
  ```bash
369
369
  # Analyze legacy codebase (most common use case)
370
370
  specfact import from-code my-project --repo .
371
+
372
+ # Or validate external codebase without modifying source (sidecar validation)
373
+ specfact validate sidecar init my-project /path/to/repo
374
+ specfact validate sidecar run my-project /path/to/repo
371
375
  ```
372
376
 
373
377
  **⏱️ Timing:** Analysis typically takes **10-15 minutes** for typical repositories (e.g., `specfact-cli` itself with several hundred features & contracts). Smaller codebases may complete in 2-5 minutes. Large codebases (3000+ features) may take 15-30 minutes, but progress reporting shows real-time status. The analysis performs AST parsing, Semgrep pattern detection, and Specmatic integration.
@@ -432,8 +436,10 @@ specfact import from-code my-project --repo .
432
436
  - **Prevent regressions** with runtime contract validation
433
437
  - **CI/CD integration** - Block bad code from merging
434
438
  - **Works offline** - No cloud required
439
+ - **Sidecar validation** - Validate external codebases without modifying source code
435
440
 
436
- 👉 **[Command Reference](docs/reference/commands.md)** - All enforcement commands
441
+ 👉 **[Command Reference](docs/reference/commands.md)** - All enforcement commands
442
+ 👉 **[Sidecar Validation Guide](docs/guides/sidecar-validation.md)** - Validate external codebases
437
443
 
438
444
  ### 👥 Team Collaboration
439
445
 
@@ -463,6 +469,12 @@ specfact import from-code my-project --repo .
463
469
 
464
470
  👉 **[Brownfield Modernization Guide](docs/guides/brownfield-engineer.md)** - Complete walkthrough
465
471
 
472
+ ### 1.5. Validating External Codebases (Sidecar Validation) 🆕
473
+
474
+ **Problem:** Need to validate third-party libraries or legacy codebases without modifying source code
475
+
476
+ 👉 **[Sidecar Validation Guide](docs/guides/sidecar-validation.md)** - Validate external codebases with contract testing
477
+
466
478
  ### 2. Working with a Team
467
479
 
468
480
  **Problem:** Need team collaboration with role-based workflows
@@ -502,6 +514,7 @@ specfact import from-code my-project --repo .
502
514
  - **[AI IDE Workflow](docs/guides/ai-ide-workflow.md)** ⭐ **NEW** - AI-assisted development
503
515
  - **[Agile/Scrum Workflows](docs/guides/agile-scrum-workflows.md)** ⭐ - Team collaboration
504
516
  - **[Integrations Overview](docs/guides/integrations-overview.md)** ⭐ **NEW** - All integrations
517
+ - **[Sidecar Validation](docs/guides/sidecar-validation.md)** 🆕 - Validate external codebases without modifying source
505
518
  - **[Use Cases](docs/guides/use-cases.md)** - Common scenarios
506
519
 
507
520
  ### Integration Guides
@@ -93,6 +93,10 @@ specfact init --ide cursor --install-deps
93
93
  ```bash
94
94
  # Analyze legacy codebase (most common use case)
95
95
  specfact import from-code my-project --repo .
96
+
97
+ # Or validate external codebase without modifying source (sidecar validation)
98
+ specfact validate sidecar init my-project /path/to/repo
99
+ specfact validate sidecar run my-project /path/to/repo
96
100
  ```
97
101
 
98
102
  **⏱️ Timing:** Analysis typically takes **10-15 minutes** for typical repositories (e.g., `specfact-cli` itself with several hundred features & contracts). Smaller codebases may complete in 2-5 minutes. Large codebases (3000+ features) may take 15-30 minutes, but progress reporting shows real-time status. The analysis performs AST parsing, Semgrep pattern detection, and Specmatic integration.
@@ -157,8 +161,10 @@ specfact import from-code my-project --repo .
157
161
  - **Prevent regressions** with runtime contract validation
158
162
  - **CI/CD integration** - Block bad code from merging
159
163
  - **Works offline** - No cloud required
164
+ - **Sidecar validation** - Validate external codebases without modifying source code
160
165
 
161
- 👉 **[Command Reference](docs/reference/commands.md)** - All enforcement commands
166
+ 👉 **[Command Reference](docs/reference/commands.md)** - All enforcement commands
167
+ 👉 **[Sidecar Validation Guide](docs/guides/sidecar-validation.md)** - Validate external codebases
162
168
 
163
169
  ### 👥 Team Collaboration
164
170
 
@@ -188,6 +194,12 @@ specfact import from-code my-project --repo .
188
194
 
189
195
  👉 **[Brownfield Modernization Guide](docs/guides/brownfield-engineer.md)** - Complete walkthrough
190
196
 
197
+ ### 1.5. Validating External Codebases (Sidecar Validation) 🆕
198
+
199
+ **Problem:** Need to validate third-party libraries or legacy codebases without modifying source code
200
+
201
+ 👉 **[Sidecar Validation Guide](docs/guides/sidecar-validation.md)** - Validate external codebases with contract testing
202
+
191
203
  ### 2. Working with a Team
192
204
 
193
205
  **Problem:** Need team collaboration with role-based workflows
@@ -227,6 +239,7 @@ specfact import from-code my-project --repo .
227
239
  - **[AI IDE Workflow](docs/guides/ai-ide-workflow.md)** ⭐ **NEW** - AI-assisted development
228
240
  - **[Agile/Scrum Workflows](docs/guides/agile-scrum-workflows.md)** ⭐ - Team collaboration
229
241
  - **[Integrations Overview](docs/guides/integrations-overview.md)** ⭐ **NEW** - All integrations
242
+ - **[Sidecar Validation](docs/guides/sidecar-validation.md)** 🆕 - Validate external codebases without modifying source
230
243
  - **[Use Cases](docs/guides/use-cases.md)** - Common scenarios
231
244
 
232
245
  ### Integration Guides
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "specfact-cli"
7
- version = "0.23.0"
7
+ version = "0.24.0"
8
8
  description = "Brownfield-first CLI: Reverse engineer legacy Python → specs → enforced contracts. Automate legacy code documentation and prevent modernization regressions."
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.11"
@@ -148,6 +148,54 @@ _filter_crosshair_dirs() {
148
148
  echo "${filtered[@]}"
149
149
  }
150
150
 
151
+ # Convert source directory paths to Python module names for CrossHair
152
+ # CrossHair expects module names (e.g., "sqlalchemy") not paths (e.g., "lib/sqlalchemy")
153
+ # This function extracts the module name and ensures PYTHONPATH includes the parent directory
154
+ _path_to_module() {
155
+ local source_path="$1"
156
+ local repo_path="${2:-${REPO_PATH}}"
157
+
158
+ # Remove trailing slash
159
+ source_path="${source_path%/}"
160
+
161
+ # If it's an absolute path, make it relative to repo
162
+ if [[ "$source_path" == /* ]]; then
163
+ source_path="${source_path#${repo_path}/}"
164
+ fi
165
+
166
+ # Handle common patterns: lib/pkg, src/pkg, backend/app, pkg
167
+ # Extract the module name (last component that's a valid Python package)
168
+ local module_name=""
169
+ local parent_dir=""
170
+
171
+ if [[ "$source_path" == *"/"* ]]; then
172
+ # Path has directory structure (e.g., lib/sqlalchemy, src/mypackage)
173
+ parent_dir="${source_path%/*}" # Everything before last /
174
+ module_name="${source_path##*/}" # Last component
175
+
176
+ # Check if the module directory has __init__.py (is a package)
177
+ local full_module_path="${repo_path}/${source_path}"
178
+ if [[ -f "${full_module_path}/__init__.py" ]]; then
179
+ # It's a package - return module name and parent dir
180
+ echo "${module_name}|${repo_path}/${parent_dir}"
181
+ return 0
182
+ fi
183
+
184
+ # Check subdirectories for packages (e.g., lib/sqlalchemy where sqlalchemy is the package)
185
+ for subdir in "${full_module_path}"/*; do
186
+ if [[ -d "$subdir" ]] && [[ -f "${subdir}/__init__.py" ]]; then
187
+ # Found a package subdirectory
188
+ echo "$(basename "$subdir")|${full_module_path}"
189
+ return 0
190
+ fi
191
+ done
192
+ fi
193
+
194
+ # No directory structure or no package found - use the path as-is
195
+ # This handles cases like "mypackage" where PYTHONPATH is already set correctly
196
+ echo "${source_path}|"
197
+ }
198
+
151
199
  run_with_timeout() {
152
200
  local timeout_secs="$1"
153
201
  shift
@@ -427,36 +475,68 @@ if [[ "${RUN_CROSSHAIR}" == "1" ]] && command -v crosshair >/dev/null 2>&1; then
427
475
  if [[ -z "${CROSSHAIR_FILTERED_DIRS}" ]]; then
428
476
  echo "[sidecar] warning: all source directories filtered out (contain tests), skipping source code analysis"
429
477
  else
430
- if [[ "${FRAMEWORK_TYPE}" == "django" ]]; then
431
- # Use Django-aware wrapper for source code analysis
432
- CROSSHAIR_WRAPPER="${SIDECAR_DIR}/../frameworks/django/crosshair_django_wrapper.py"
433
- if [[ -f "${CROSSHAIR_WRAPPER}" ]]; then
434
- echo "[sidecar] using Django-aware CrossHair wrapper for source analysis"
435
- # Export environment variables for Django initialization
436
- CROSSHAIR_ENV=""
437
- if [[ -n "${DJANGO_SETTINGS_MODULE:-}" ]]; then
438
- CROSSHAIR_ENV="DJANGO_SETTINGS_MODULE=${DJANGO_SETTINGS_MODULE} "
478
+ # Convert source paths to module names for CrossHair
479
+ # CrossHair expects module names (e.g., "sqlalchemy") not paths (e.g., "lib/sqlalchemy")
480
+ CROSSHAIR_MODULES=""
481
+ CROSSHAIR_EXTRA_PYTHONPATH=""
482
+ for src_dir in ${CROSSHAIR_FILTERED_DIRS}; do
483
+ MODULE_INFO=$(_path_to_module "$src_dir" "${REPO_PATH}")
484
+ MODULE_NAME="${MODULE_INFO%%|*}"
485
+ MODULE_PARENT="${MODULE_INFO##*|}"
486
+
487
+ if [[ -n "$MODULE_NAME" ]]; then
488
+ CROSSHAIR_MODULES="${CROSSHAIR_MODULES} ${MODULE_NAME}"
489
+ if [[ -n "$MODULE_PARENT" ]] && [[ ":${CROSSHAIR_EXTRA_PYTHONPATH}:" != *":${MODULE_PARENT}:"* ]]; then
490
+ CROSSHAIR_EXTRA_PYTHONPATH="${CROSSHAIR_EXTRA_PYTHONPATH}:${MODULE_PARENT}"
491
+ fi
439
492
  fi
440
- if [[ -n "${REPO_PATH:-}" ]]; then
441
- CROSSHAIR_ENV="${CROSSHAIR_ENV}REPO_PATH=${REPO_PATH} "
493
+ done
494
+ CROSSHAIR_MODULES="${CROSSHAIR_MODULES# }" # Trim leading space
495
+ CROSSHAIR_EXTRA_PYTHONPATH="${CROSSHAIR_EXTRA_PYTHONPATH#:}" # Trim leading colon
496
+
497
+ if [[ -z "${CROSSHAIR_MODULES}" ]]; then
498
+ echo "[sidecar] warning: could not convert source directories to modules, skipping source code analysis"
499
+ else
500
+ echo "[sidecar] analyzing modules: ${CROSSHAIR_MODULES}"
501
+ if [[ -n "${CROSSHAIR_EXTRA_PYTHONPATH}" ]]; then
502
+ echo "[sidecar] extra PYTHONPATH: ${CROSSHAIR_EXTRA_PYTHONPATH}"
442
503
  fi
443
- if [[ -n "${PYTHONPATH:-}" ]]; then
444
- CROSSHAIR_ENV="${CROSSHAIR_ENV}PYTHONPATH=${PYTHONPATH} "
504
+
505
+ # Build PYTHONPATH for CrossHair (include extra paths for module resolution)
506
+ CROSSHAIR_PYTHONPATH="${PYTHONPATH:-}"
507
+ if [[ -n "${CROSSHAIR_EXTRA_PYTHONPATH}" ]]; then
508
+ CROSSHAIR_PYTHONPATH="${CROSSHAIR_EXTRA_PYTHONPATH}:${CROSSHAIR_PYTHONPATH}"
509
+ fi
510
+
511
+ if [[ "${FRAMEWORK_TYPE}" == "django" ]]; then
512
+ # Use Django-aware wrapper for source code analysis
513
+ CROSSHAIR_WRAPPER="${SIDECAR_DIR}/../frameworks/django/crosshair_django_wrapper.py"
514
+ if [[ -f "${CROSSHAIR_WRAPPER}" ]]; then
515
+ echo "[sidecar] using Django-aware CrossHair wrapper for source analysis"
516
+ # Export environment variables for Django initialization
517
+ CROSSHAIR_ENV=""
518
+ if [[ -n "${DJANGO_SETTINGS_MODULE:-}" ]]; then
519
+ CROSSHAIR_ENV="DJANGO_SETTINGS_MODULE=${DJANGO_SETTINGS_MODULE} "
520
+ fi
521
+ if [[ -n "${REPO_PATH:-}" ]]; then
522
+ CROSSHAIR_ENV="${CROSSHAIR_ENV}REPO_PATH=${REPO_PATH} "
523
+ fi
524
+ CROSSHAIR_ENV="${CROSSHAIR_ENV}PYTHONPATH=${CROSSHAIR_PYTHONPATH} "
525
+ run_and_log "${TIMEOUT_CROSSHAIR}" \
526
+ "${SIDECAR_REPORTS_DIR}/${TIMESTAMP}-crosshair-source.log" \
527
+ env ${CROSSHAIR_ENV}"${PYTHON_CMD}" "${CROSSHAIR_WRAPPER}" check "${CROSSHAIR_ARGS[@]}" ${CROSSHAIR_MODULES}
528
+ else
529
+ echo "[sidecar] warning: Django wrapper not found, using standard CrossHair (may fail)"
530
+ run_and_log "${TIMEOUT_CROSSHAIR}" \
531
+ "${SIDECAR_REPORTS_DIR}/${TIMESTAMP}-crosshair-source.log" \
532
+ env PYTHONPATH="${CROSSHAIR_PYTHONPATH}" "${PYTHON_CMD}" -m crosshair check "${CROSSHAIR_ARGS[@]}" ${CROSSHAIR_MODULES}
533
+ fi
534
+ else
535
+ # Standard CrossHair for non-Django projects
536
+ run_and_log "${TIMEOUT_CROSSHAIR}" \
537
+ "${SIDECAR_REPORTS_DIR}/${TIMESTAMP}-crosshair-source.log" \
538
+ env PYTHONPATH="${CROSSHAIR_PYTHONPATH}" "${PYTHON_CMD}" -m crosshair check "${CROSSHAIR_ARGS[@]}" ${CROSSHAIR_MODULES}
445
539
  fi
446
- run_and_log "${TIMEOUT_CROSSHAIR}" \
447
- "${SIDECAR_REPORTS_DIR}/${TIMESTAMP}-crosshair-source.log" \
448
- env ${CROSSHAIR_ENV}"${PYTHON_CMD}" "${CROSSHAIR_WRAPPER}" check "${CROSSHAIR_ARGS[@]}" ${CROSSHAIR_FILTERED_DIRS}
449
- else
450
- echo "[sidecar] warning: Django wrapper not found, using standard CrossHair (may fail)"
451
- run_and_log "${TIMEOUT_CROSSHAIR}" \
452
- "${SIDECAR_REPORTS_DIR}/${TIMESTAMP}-crosshair-source.log" \
453
- "${PYTHON_CMD}" -m crosshair check "${CROSSHAIR_ARGS[@]}" ${CROSSHAIR_FILTERED_DIRS}
454
- fi
455
- else
456
- # Standard CrossHair for non-Django projects
457
- run_and_log "${TIMEOUT_CROSSHAIR}" \
458
- "${SIDECAR_REPORTS_DIR}/${TIMESTAMP}-crosshair-source.log" \
459
- "${PYTHON_CMD}" -m crosshair check "${CROSSHAIR_ARGS[@]}" ${CROSSHAIR_FILTERED_DIRS}
460
540
  fi
461
541
  fi
462
542
  fi
@@ -466,21 +546,30 @@ if [[ "${RUN_CROSSHAIR}" == "1" ]] && command -v crosshair >/dev/null 2>&1; then
466
546
  # This is the primary analysis method for frameworks without decorators (Django, etc.)
467
547
  if [[ -f "${HARNESS_PATH}" ]]; then
468
548
  echo "[sidecar] crosshair (harness - external contracts)..."
549
+
550
+ # Build PYTHONPATH for harness analysis:
551
+ # 1. Sidecar directory (for harness imports like 'common.adapters')
552
+ # 2. Original PYTHONPATH (for repo modules)
553
+ HARNESS_DIR="$(cd "$(dirname "${HARNESS_PATH}")" && pwd)"
554
+ HARNESS_FILE="$(basename "${HARNESS_PATH}")"
555
+ HARNESS_MODULE="${HARNESS_FILE%.py}" # Remove .py extension
556
+
557
+ # Build PYTHONPATH: sidecar dir + original PYTHONPATH
558
+ HARNESS_PYTHONPATH="${HARNESS_DIR}"
559
+ if [[ -n "${PYTHONPATH:-}" ]]; then
560
+ HARNESS_PYTHONPATH="${HARNESS_PYTHONPATH}:${PYTHONPATH}"
561
+ fi
562
+
469
563
  # Export environment variables for CrossHair subprocess
470
- CROSSHAIR_ENV=""
564
+ CROSSHAIR_ENV="PYTHONPATH=${HARNESS_PYTHONPATH} "
471
565
  if [[ -n "${DJANGO_SETTINGS_MODULE:-}" ]]; then
472
- CROSSHAIR_ENV="DJANGO_SETTINGS_MODULE=${DJANGO_SETTINGS_MODULE} "
566
+ CROSSHAIR_ENV="${CROSSHAIR_ENV}DJANGO_SETTINGS_MODULE=${DJANGO_SETTINGS_MODULE} "
473
567
  fi
474
568
  if [[ -n "${REPO_PATH:-}" ]]; then
475
569
  CROSSHAIR_ENV="${CROSSHAIR_ENV}REPO_PATH=${REPO_PATH} "
476
570
  fi
477
- if [[ -n "${PYTHONPATH:-}" ]]; then
478
- CROSSHAIR_ENV="${CROSSHAIR_ENV}PYTHONPATH=${PYTHONPATH} "
479
- fi
571
+
480
572
  # Change to harness directory to ensure valid module name (avoids hyphenated directory names in module path)
481
- HARNESS_DIR="$(dirname "${HARNESS_PATH}")"
482
- HARNESS_FILE="$(basename "${HARNESS_PATH}")"
483
- HARNESS_MODULE="${HARNESS_FILE%.py}" # Remove .py extension
484
573
  run_and_log "${TIMEOUT_CROSSHAIR}" \
485
574
  "${SIDECAR_REPORTS_DIR}/${TIMESTAMP}-crosshair-harness.log" \
486
575
  bash -c "cd '${HARNESS_DIR}' && env ${CROSSHAIR_ENV}${PYTHON_CMD} -m crosshair check ${CROSSHAIR_ARGS[*]} ${HARNESS_MODULE}"
@@ -3,4 +3,4 @@ SpecFact CLI - Spec→Contract→Sentinel tool for contract-driven development.
3
3
  """
4
4
 
5
5
  # Define the package version (kept in sync with pyproject.toml and setup.py)
6
- __version__ = "0.23.0"
6
+ __version__ = "0.24.0"
@@ -9,6 +9,6 @@ This package provides command-line tools for:
9
9
  - Validating reproducibility
10
10
  """
11
11
 
12
- __version__ = "0.23.0"
12
+ __version__ = "0.24.0"
13
13
 
14
14
  __all__ = ["__version__"]
@@ -260,8 +260,15 @@ class ContractExtractor:
260
260
  return repr(node.value)
261
261
  if isinstance(node, ast.Name):
262
262
  return node.id
263
- if isinstance(node, ast.NameConstant): # Python < 3.8
264
- return str(node.value)
263
+ # Python < 3.8 compatibility - suppress deprecation warning
264
+ import warnings
265
+
266
+ with warnings.catch_warnings():
267
+ warnings.simplefilter("ignore", DeprecationWarning)
268
+ # ast.NameConstant is deprecated in Python 3.8+, removed in 3.14
269
+ # Keep for backward compatibility with older Python versions
270
+ if hasattr(ast, "NameConstant") and isinstance(node, ast.NameConstant):
271
+ return str(node.value)
265
272
 
266
273
  # Use ast.unparse if available
267
274
  if hasattr(ast, "unparse"):
@@ -68,6 +68,7 @@ from specfact_cli.commands import (
68
68
  sdd,
69
69
  spec,
70
70
  sync,
71
+ validate,
71
72
  )
72
73
  from specfact_cli.modes import OperationalMode, detect_mode
73
74
  from specfact_cli.runtime import get_configured_console
@@ -350,6 +351,7 @@ app.add_typer(drift.app, name="drift", help="Detect drift between code and speci
350
351
 
351
352
  # 11.6. Analysis
352
353
  app.add_typer(analyze.app, name="analyze", help="Analyze codebase for contract coverage and quality")
354
+ app.add_typer(validate.app, name="validate", help="Validation commands including sidecar validation")
353
355
 
354
356
 
355
357
  def cli_main() -> None:
@@ -19,6 +19,7 @@ from specfact_cli.commands import (
19
19
  sdd,
20
20
  spec,
21
21
  sync,
22
+ validate,
22
23
  )
23
24
 
24
25
 
@@ -38,4 +39,5 @@ __all__ = [
38
39
  "sdd",
39
40
  "spec",
40
41
  "sync",
42
+ "validate",
41
43
  ]