specfact-cli 0.46.2__tar.gz → 0.46.9__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (278) hide show
  1. {specfact_cli-0.46.2 → specfact_cli-0.46.9}/PKG-INFO +11 -10
  2. {specfact_cli-0.46.2 → specfact_cli-0.46.9}/README.md +4 -3
  3. {specfact_cli-0.46.2 → specfact_cli-0.46.9}/pyproject.toml +40 -25
  4. specfact_cli-0.46.9/resources/bundled-module-registry/index.json +20 -0
  5. {specfact_cli-0.46.2 → specfact_cli-0.46.9}/src/__init__.py +1 -1
  6. {specfact_cli-0.46.2 → specfact_cli-0.46.9}/src/specfact_cli/__init__.py +1 -1
  7. {specfact_cli-0.46.2 → specfact_cli-0.46.9}/src/specfact_cli/analyzers/code_analyzer.py +27 -7
  8. {specfact_cli-0.46.2 → specfact_cli-0.46.9}/src/specfact_cli/analyzers/graph_analyzer.py +33 -39
  9. {specfact_cli-0.46.2 → specfact_cli-0.46.9}/src/specfact_cli/cli.py +23 -0
  10. {specfact_cli-0.46.2 → specfact_cli-0.46.9}/src/specfact_cli/modules/init/module-package.yaml +3 -3
  11. {specfact_cli-0.46.2 → specfact_cli-0.46.9}/src/specfact_cli/modules/init/src/commands.py +33 -13
  12. {specfact_cli-0.46.2 → specfact_cli-0.46.9}/src/specfact_cli/modules/module_registry/module-package.yaml +3 -3
  13. {specfact_cli-0.46.2 → specfact_cli-0.46.9}/src/specfact_cli/modules/module_registry/src/commands.py +73 -8
  14. specfact_cli-0.46.9/src/specfact_cli/registry/module_availability.py +214 -0
  15. {specfact_cli-0.46.2 → specfact_cli-0.46.9}/src/specfact_cli/registry/module_discovery.py +104 -33
  16. {specfact_cli-0.46.2 → specfact_cli-0.46.9}/src/specfact_cli/registry/module_installer.py +46 -4
  17. {specfact_cli-0.46.2 → specfact_cli-0.46.9}/src/specfact_cli/registry/module_packages.py +22 -5
  18. {specfact_cli-0.46.2 → specfact_cli-0.46.9}/src/specfact_cli/utils/optional_deps.py +19 -16
  19. {specfact_cli-0.46.2 → specfact_cli-0.46.9}/src/specfact_cli/utils/project_artifact_write.py +7 -6
  20. {specfact_cli-0.46.2 → specfact_cli-0.46.9}/.gitignore +0 -0
  21. {specfact_cli-0.46.2 → specfact_cli-0.46.9}/LICENSE +0 -0
  22. {specfact_cli-0.46.2 → specfact_cli-0.46.9}/resources/keys/README.md +0 -0
  23. {specfact_cli-0.46.2 → specfact_cli-0.46.9}/resources/keys/module-signing-public.pem +0 -0
  24. {specfact_cli-0.46.2 → specfact_cli-0.46.9}/resources/mappings/node-async.yaml +0 -0
  25. {specfact_cli-0.46.2 → specfact_cli-0.46.9}/resources/mappings/python-async.yaml +0 -0
  26. {specfact_cli-0.46.2 → specfact_cli-0.46.9}/resources/mappings/speckit-default.yaml +0 -0
  27. {specfact_cli-0.46.2 → specfact_cli-0.46.9}/resources/schemas/deviation.schema.json +0 -0
  28. {specfact_cli-0.46.2 → specfact_cli-0.46.9}/resources/schemas/plan.schema.json +0 -0
  29. {specfact_cli-0.46.2 → specfact_cli-0.46.9}/resources/schemas/protocol.schema.json +0 -0
  30. {specfact_cli-0.46.2 → specfact_cli-0.46.9}/resources/templates/github-action.yml.j2 +0 -0
  31. {specfact_cli-0.46.2 → specfact_cli-0.46.9}/resources/templates/persona/architect.md.j2 +0 -0
  32. {specfact_cli-0.46.2 → specfact_cli-0.46.9}/resources/templates/persona/developer.md.j2 +0 -0
  33. {specfact_cli-0.46.2 → specfact_cli-0.46.9}/resources/templates/persona/product-owner.md.j2 +0 -0
  34. {specfact_cli-0.46.2 → specfact_cli-0.46.9}/resources/templates/plan.bundle.yaml.j2 +0 -0
  35. {specfact_cli-0.46.2 → specfact_cli-0.46.9}/resources/templates/policies/kanban.yaml +0 -0
  36. {specfact_cli-0.46.2 → specfact_cli-0.46.9}/resources/templates/policies/mixed.yaml +0 -0
  37. {specfact_cli-0.46.2 → specfact_cli-0.46.9}/resources/templates/policies/safe.yaml +0 -0
  38. {specfact_cli-0.46.2 → specfact_cli-0.46.9}/resources/templates/policies/scrum.yaml +0 -0
  39. {specfact_cli-0.46.2 → specfact_cli-0.46.9}/resources/templates/pr-template.md.j2 +0 -0
  40. {specfact_cli-0.46.2 → specfact_cli-0.46.9}/resources/templates/protocol.yaml.j2 +0 -0
  41. {specfact_cli-0.46.2 → specfact_cli-0.46.9}/resources/templates/telemetry.yaml.example +0 -0
  42. {specfact_cli-0.46.2 → specfact_cli-0.46.9}/src/specfact_cli/__main__.py +0 -0
  43. {specfact_cli-0.46.2 → specfact_cli-0.46.9}/src/specfact_cli/adapters/__init__.py +0 -0
  44. {specfact_cli-0.46.2 → specfact_cli-0.46.9}/src/specfact_cli/adapters/ado.py +0 -0
  45. {specfact_cli-0.46.2 → specfact_cli-0.46.9}/src/specfact_cli/adapters/backlog_base.py +0 -0
  46. {specfact_cli-0.46.2 → specfact_cli-0.46.9}/src/specfact_cli/adapters/base.py +0 -0
  47. {specfact_cli-0.46.2 → specfact_cli-0.46.9}/src/specfact_cli/adapters/github.py +0 -0
  48. {specfact_cli-0.46.2 → specfact_cli-0.46.9}/src/specfact_cli/adapters/openspec.py +0 -0
  49. {specfact_cli-0.46.2 → specfact_cli-0.46.9}/src/specfact_cli/adapters/openspec_parser.py +0 -0
  50. {specfact_cli-0.46.2 → specfact_cli-0.46.9}/src/specfact_cli/adapters/registry.py +0 -0
  51. {specfact_cli-0.46.2 → specfact_cli-0.46.9}/src/specfact_cli/adapters/speckit.py +0 -0
  52. {specfact_cli-0.46.2 → specfact_cli-0.46.9}/src/specfact_cli/agents/__init__.py +0 -0
  53. {specfact_cli-0.46.2 → specfact_cli-0.46.9}/src/specfact_cli/agents/analyze_agent.py +0 -0
  54. {specfact_cli-0.46.2 → specfact_cli-0.46.9}/src/specfact_cli/agents/base.py +0 -0
  55. {specfact_cli-0.46.2 → specfact_cli-0.46.9}/src/specfact_cli/agents/plan_agent.py +0 -0
  56. {specfact_cli-0.46.2 → specfact_cli-0.46.9}/src/specfact_cli/agents/registry.py +0 -0
  57. {specfact_cli-0.46.2 → specfact_cli-0.46.9}/src/specfact_cli/agents/sync_agent.py +0 -0
  58. {specfact_cli-0.46.2 → specfact_cli-0.46.9}/src/specfact_cli/analyzers/__init__.py +0 -0
  59. {specfact_cli-0.46.2 → specfact_cli-0.46.9}/src/specfact_cli/analyzers/ambiguity_scanner.py +0 -0
  60. {specfact_cli-0.46.2 → specfact_cli-0.46.9}/src/specfact_cli/analyzers/constitution_evidence_extractor.py +0 -0
  61. {specfact_cli-0.46.2 → specfact_cli-0.46.9}/src/specfact_cli/analyzers/contract_extractor.py +0 -0
  62. {specfact_cli-0.46.2 → specfact_cli-0.46.9}/src/specfact_cli/analyzers/control_flow_analyzer.py +0 -0
  63. {specfact_cli-0.46.2 → specfact_cli-0.46.9}/src/specfact_cli/analyzers/relationship_mapper.py +0 -0
  64. {specfact_cli-0.46.2 → specfact_cli-0.46.9}/src/specfact_cli/analyzers/requirement_extractor.py +0 -0
  65. {specfact_cli-0.46.2 → specfact_cli-0.46.9}/src/specfact_cli/analyzers/test_pattern_extractor.py +0 -0
  66. {specfact_cli-0.46.2 → specfact_cli-0.46.9}/src/specfact_cli/backlog/__init__.py +0 -0
  67. {specfact_cli-0.46.2 → specfact_cli-0.46.9}/src/specfact_cli/backlog/adapters/__init__.py +0 -0
  68. {specfact_cli-0.46.2 → specfact_cli-0.46.9}/src/specfact_cli/backlog/adapters/base.py +0 -0
  69. {specfact_cli-0.46.2 → specfact_cli-0.46.9}/src/specfact_cli/backlog/converter.py +0 -0
  70. {specfact_cli-0.46.2 → specfact_cli-0.46.9}/src/specfact_cli/backlog/filters.py +0 -0
  71. {specfact_cli-0.46.2 → specfact_cli-0.46.9}/src/specfact_cli/backlog/mappers/__init__.py +0 -0
  72. {specfact_cli-0.46.2 → specfact_cli-0.46.9}/src/specfact_cli/backlog/mappers/ado_mapper.py +0 -0
  73. {specfact_cli-0.46.2 → specfact_cli-0.46.9}/src/specfact_cli/backlog/mappers/base.py +0 -0
  74. {specfact_cli-0.46.2 → specfact_cli-0.46.9}/src/specfact_cli/backlog/mappers/github_mapper.py +0 -0
  75. {specfact_cli-0.46.2 → specfact_cli-0.46.9}/src/specfact_cli/backlog/mappers/template_config.py +0 -0
  76. {specfact_cli-0.46.2 → specfact_cli-0.46.9}/src/specfact_cli/commands/__init__.py +0 -0
  77. {specfact_cli-0.46.2 → specfact_cli-0.46.9}/src/specfact_cli/commands/_bundle_shim.py +0 -0
  78. {specfact_cli-0.46.2 → specfact_cli-0.46.9}/src/specfact_cli/commands/analyze.py +0 -0
  79. {specfact_cli-0.46.2 → specfact_cli-0.46.9}/src/specfact_cli/commands/contract_cmd.py +0 -0
  80. {specfact_cli-0.46.2 → specfact_cli-0.46.9}/src/specfact_cli/commands/drift.py +0 -0
  81. {specfact_cli-0.46.2 → specfact_cli-0.46.9}/src/specfact_cli/commands/enforce.py +0 -0
  82. {specfact_cli-0.46.2 → specfact_cli-0.46.9}/src/specfact_cli/commands/generate.py +0 -0
  83. {specfact_cli-0.46.2 → specfact_cli-0.46.9}/src/specfact_cli/commands/import_cmd.py +0 -0
  84. {specfact_cli-0.46.2 → specfact_cli-0.46.9}/src/specfact_cli/commands/init.py +0 -0
  85. {specfact_cli-0.46.2 → specfact_cli-0.46.9}/src/specfact_cli/commands/migrate.py +0 -0
  86. {specfact_cli-0.46.2 → specfact_cli-0.46.9}/src/specfact_cli/commands/plan.py +0 -0
  87. {specfact_cli-0.46.2 → specfact_cli-0.46.9}/src/specfact_cli/commands/project_cmd.py +0 -0
  88. {specfact_cli-0.46.2 → specfact_cli-0.46.9}/src/specfact_cli/commands/repro.py +0 -0
  89. {specfact_cli-0.46.2 → specfact_cli-0.46.9}/src/specfact_cli/commands/sdd.py +0 -0
  90. {specfact_cli-0.46.2 → specfact_cli-0.46.9}/src/specfact_cli/commands/spec.py +0 -0
  91. {specfact_cli-0.46.2 → specfact_cli-0.46.9}/src/specfact_cli/commands/sync.py +0 -0
  92. {specfact_cli-0.46.2 → specfact_cli-0.46.9}/src/specfact_cli/commands/update.py +0 -0
  93. {specfact_cli-0.46.2 → specfact_cli-0.46.9}/src/specfact_cli/commands/validate.py +0 -0
  94. {specfact_cli-0.46.2 → specfact_cli-0.46.9}/src/specfact_cli/common/__init__.py +0 -0
  95. {specfact_cli-0.46.2 → specfact_cli-0.46.9}/src/specfact_cli/common/bundle_factory.py +0 -0
  96. {specfact_cli-0.46.2 → specfact_cli-0.46.9}/src/specfact_cli/common/logger_setup.py +0 -0
  97. {specfact_cli-0.46.2 → specfact_cli-0.46.9}/src/specfact_cli/common/logging_utils.py +0 -0
  98. {specfact_cli-0.46.2 → specfact_cli-0.46.9}/src/specfact_cli/common/text_utils.py +0 -0
  99. {specfact_cli-0.46.2 → specfact_cli-0.46.9}/src/specfact_cli/common/utils.py +0 -0
  100. {specfact_cli-0.46.2 → specfact_cli-0.46.9}/src/specfact_cli/comparators/__init__.py +0 -0
  101. {specfact_cli-0.46.2 → specfact_cli-0.46.9}/src/specfact_cli/comparators/plan_comparator.py +0 -0
  102. {specfact_cli-0.46.2 → specfact_cli-0.46.9}/src/specfact_cli/contracts/__init__.py +0 -0
  103. {specfact_cli-0.46.2 → specfact_cli-0.46.9}/src/specfact_cli/contracts/crosshair_props.py +0 -0
  104. {specfact_cli-0.46.2 → specfact_cli-0.46.9}/src/specfact_cli/contracts/module_interface.py +0 -0
  105. {specfact_cli-0.46.2 → specfact_cli-0.46.9}/src/specfact_cli/enrichers/constitution_enricher.py +0 -0
  106. {specfact_cli-0.46.2 → specfact_cli-0.46.9}/src/specfact_cli/enrichers/plan_enricher.py +0 -0
  107. {specfact_cli-0.46.2 → specfact_cli-0.46.9}/src/specfact_cli/generators/__init__.py +0 -0
  108. {specfact_cli-0.46.2 → specfact_cli-0.46.9}/src/specfact_cli/generators/contract_generator.py +0 -0
  109. {specfact_cli-0.46.2 → specfact_cli-0.46.9}/src/specfact_cli/generators/openapi_extractor.py +0 -0
  110. {specfact_cli-0.46.2 → specfact_cli-0.46.9}/src/specfact_cli/generators/persona_exporter.py +0 -0
  111. {specfact_cli-0.46.2 → specfact_cli-0.46.9}/src/specfact_cli/generators/plan_generator.py +0 -0
  112. {specfact_cli-0.46.2 → specfact_cli-0.46.9}/src/specfact_cli/generators/protocol_generator.py +0 -0
  113. {specfact_cli-0.46.2 → specfact_cli-0.46.9}/src/specfact_cli/generators/report_generator.py +0 -0
  114. {specfact_cli-0.46.2 → specfact_cli-0.46.9}/src/specfact_cli/generators/task_generator.py +0 -0
  115. {specfact_cli-0.46.2 → specfact_cli-0.46.9}/src/specfact_cli/generators/test_to_openapi.py +0 -0
  116. {specfact_cli-0.46.2 → specfact_cli-0.46.9}/src/specfact_cli/generators/workflow_generator.py +0 -0
  117. {specfact_cli-0.46.2 → specfact_cli-0.46.9}/src/specfact_cli/groups/__init__.py +0 -0
  118. {specfact_cli-0.46.2 → specfact_cli-0.46.9}/src/specfact_cli/groups/codebase_group.py +0 -0
  119. {specfact_cli-0.46.2 → specfact_cli-0.46.9}/src/specfact_cli/groups/govern_group.py +0 -0
  120. {specfact_cli-0.46.2 → specfact_cli-0.46.9}/src/specfact_cli/groups/member_group.py +0 -0
  121. {specfact_cli-0.46.2 → specfact_cli-0.46.9}/src/specfact_cli/groups/project_group.py +0 -0
  122. {specfact_cli-0.46.2 → specfact_cli-0.46.9}/src/specfact_cli/groups/spec_group.py +0 -0
  123. {specfact_cli-0.46.2 → specfact_cli-0.46.9}/src/specfact_cli/importers/__init__.py +0 -0
  124. {specfact_cli-0.46.2 → specfact_cli-0.46.9}/src/specfact_cli/importers/speckit_converter.py +0 -0
  125. {specfact_cli-0.46.2 → specfact_cli-0.46.9}/src/specfact_cli/importers/speckit_scanner.py +0 -0
  126. {specfact_cli-0.46.2 → specfact_cli-0.46.9}/src/specfact_cli/integrations/__init__.py +0 -0
  127. {specfact_cli-0.46.2 → specfact_cli-0.46.9}/src/specfact_cli/integrations/specmatic.py +0 -0
  128. {specfact_cli-0.46.2 → specfact_cli-0.46.9}/src/specfact_cli/merge/__init__.py +0 -0
  129. {specfact_cli-0.46.2 → specfact_cli-0.46.9}/src/specfact_cli/merge/resolver.py +0 -0
  130. {specfact_cli-0.46.2 → specfact_cli-0.46.9}/src/specfact_cli/migrations/__init__.py +0 -0
  131. {specfact_cli-0.46.2 → specfact_cli-0.46.9}/src/specfact_cli/migrations/plan_migrator.py +0 -0
  132. {specfact_cli-0.46.2 → specfact_cli-0.46.9}/src/specfact_cli/models/__init__.py +0 -0
  133. {specfact_cli-0.46.2 → specfact_cli-0.46.9}/src/specfact_cli/models/backlog_item.py +0 -0
  134. {specfact_cli-0.46.2 → specfact_cli-0.46.9}/src/specfact_cli/models/bridge.py +0 -0
  135. {specfact_cli-0.46.2 → specfact_cli-0.46.9}/src/specfact_cli/models/capabilities.py +0 -0
  136. {specfact_cli-0.46.2 → specfact_cli-0.46.9}/src/specfact_cli/models/change.py +0 -0
  137. {specfact_cli-0.46.2 → specfact_cli-0.46.9}/src/specfact_cli/models/contract.py +0 -0
  138. {specfact_cli-0.46.2 → specfact_cli-0.46.9}/src/specfact_cli/models/deviation.py +0 -0
  139. {specfact_cli-0.46.2 → specfact_cli-0.46.9}/src/specfact_cli/models/dor_config.py +0 -0
  140. {specfact_cli-0.46.2 → specfact_cli-0.46.9}/src/specfact_cli/models/enforcement.py +0 -0
  141. {specfact_cli-0.46.2 → specfact_cli-0.46.9}/src/specfact_cli/models/module_package.py +0 -0
  142. {specfact_cli-0.46.2 → specfact_cli-0.46.9}/src/specfact_cli/models/persona_template.py +0 -0
  143. {specfact_cli-0.46.2 → specfact_cli-0.46.9}/src/specfact_cli/models/plan.py +0 -0
  144. {specfact_cli-0.46.2 → specfact_cli-0.46.9}/src/specfact_cli/models/project.py +0 -0
  145. {specfact_cli-0.46.2 → specfact_cli-0.46.9}/src/specfact_cli/models/protocol.py +0 -0
  146. {specfact_cli-0.46.2 → specfact_cli-0.46.9}/src/specfact_cli/models/quality.py +0 -0
  147. {specfact_cli-0.46.2 → specfact_cli-0.46.9}/src/specfact_cli/models/sdd.py +0 -0
  148. {specfact_cli-0.46.2 → specfact_cli-0.46.9}/src/specfact_cli/models/source_tracking.py +0 -0
  149. {specfact_cli-0.46.2 → specfact_cli-0.46.9}/src/specfact_cli/models/task.py +0 -0
  150. {specfact_cli-0.46.2 → specfact_cli-0.46.9}/src/specfact_cli/models/validation.py +0 -0
  151. {specfact_cli-0.46.2 → specfact_cli-0.46.9}/src/specfact_cli/modes/__init__.py +0 -0
  152. {specfact_cli-0.46.2 → specfact_cli-0.46.9}/src/specfact_cli/modes/detector.py +0 -0
  153. {specfact_cli-0.46.2 → specfact_cli-0.46.9}/src/specfact_cli/modes/router.py +0 -0
  154. {specfact_cli-0.46.2 → specfact_cli-0.46.9}/src/specfact_cli/modules/__init__.py +0 -0
  155. {specfact_cli-0.46.2 → specfact_cli-0.46.9}/src/specfact_cli/modules/_bundle_import.py +0 -0
  156. {specfact_cli-0.46.2 → specfact_cli-0.46.9}/src/specfact_cli/modules/init/src/__init__.py +0 -0
  157. {specfact_cli-0.46.2 → specfact_cli-0.46.9}/src/specfact_cli/modules/init/src/app.py +0 -0
  158. {specfact_cli-0.46.2 → specfact_cli-0.46.9}/src/specfact_cli/modules/init/src/first_run_selection.py +0 -0
  159. {specfact_cli-0.46.2 → specfact_cli-0.46.9}/src/specfact_cli/modules/module_io_shim.py +0 -0
  160. {specfact_cli-0.46.2 → specfact_cli-0.46.9}/src/specfact_cli/modules/module_registry/src/__init__.py +0 -0
  161. {specfact_cli-0.46.2 → specfact_cli-0.46.9}/src/specfact_cli/modules/module_registry/src/app.py +0 -0
  162. {specfact_cli-0.46.2 → specfact_cli-0.46.9}/src/specfact_cli/modules/upgrade/module-package.yaml +0 -0
  163. {specfact_cli-0.46.2 → specfact_cli-0.46.9}/src/specfact_cli/modules/upgrade/src/__init__.py +0 -0
  164. {specfact_cli-0.46.2 → specfact_cli-0.46.9}/src/specfact_cli/modules/upgrade/src/app.py +0 -0
  165. {specfact_cli-0.46.2 → specfact_cli-0.46.9}/src/specfact_cli/modules/upgrade/src/commands.py +0 -0
  166. {specfact_cli-0.46.2 → specfact_cli-0.46.9}/src/specfact_cli/parsers/__init__.py +0 -0
  167. {specfact_cli-0.46.2 → specfact_cli-0.46.9}/src/specfact_cli/parsers/persona_importer.py +0 -0
  168. {specfact_cli-0.46.2 → specfact_cli-0.46.9}/src/specfact_cli/registry/__init__.py +0 -0
  169. {specfact_cli-0.46.2 → specfact_cli-0.46.9}/src/specfact_cli/registry/alias_manager.py +0 -0
  170. {specfact_cli-0.46.2 → specfact_cli-0.46.9}/src/specfact_cli/registry/bootstrap.py +0 -0
  171. {specfact_cli-0.46.2 → specfact_cli-0.46.9}/src/specfact_cli/registry/bridge_registry.py +0 -0
  172. {specfact_cli-0.46.2 → specfact_cli-0.46.9}/src/specfact_cli/registry/crypto_validator.py +0 -0
  173. {specfact_cli-0.46.2 → specfact_cli-0.46.9}/src/specfact_cli/registry/custom_registries.py +0 -0
  174. {specfact_cli-0.46.2 → specfact_cli-0.46.9}/src/specfact_cli/registry/dependency_resolver.py +0 -0
  175. {specfact_cli-0.46.2 → specfact_cli-0.46.9}/src/specfact_cli/registry/extension_registry.py +0 -0
  176. {specfact_cli-0.46.2 → specfact_cli-0.46.9}/src/specfact_cli/registry/help_cache.py +0 -0
  177. {specfact_cli-0.46.2 → specfact_cli-0.46.9}/src/specfact_cli/registry/marketplace_client.py +0 -0
  178. {specfact_cli-0.46.2 → specfact_cli-0.46.9}/src/specfact_cli/registry/metadata.py +0 -0
  179. {specfact_cli-0.46.2 → specfact_cli-0.46.9}/src/specfact_cli/registry/module_grouping.py +0 -0
  180. {specfact_cli-0.46.2 → specfact_cli-0.46.9}/src/specfact_cli/registry/module_lifecycle.py +0 -0
  181. {specfact_cli-0.46.2 → specfact_cli-0.46.9}/src/specfact_cli/registry/module_security.py +0 -0
  182. {specfact_cli-0.46.2 → specfact_cli-0.46.9}/src/specfact_cli/registry/module_state.py +0 -0
  183. {specfact_cli-0.46.2 → specfact_cli-0.46.9}/src/specfact_cli/registry/registry.py +0 -0
  184. {specfact_cli-0.46.2 → specfact_cli-0.46.9}/src/specfact_cli/resources/semgrep/async.yml +0 -0
  185. {specfact_cli-0.46.2 → specfact_cli-0.46.9}/src/specfact_cli/resources/semgrep/code-quality.yml +0 -0
  186. {specfact_cli-0.46.2 → specfact_cli-0.46.9}/src/specfact_cli/resources/semgrep/feature-detection.yml +0 -0
  187. {specfact_cli-0.46.2 → specfact_cli-0.46.9}/src/specfact_cli/runtime.py +0 -0
  188. {specfact_cli-0.46.2 → specfact_cli-0.46.9}/src/specfact_cli/sync/__init__.py +0 -0
  189. {specfact_cli-0.46.2 → specfact_cli-0.46.9}/src/specfact_cli/sync/bridge_probe.py +0 -0
  190. {specfact_cli-0.46.2 → specfact_cli-0.46.9}/src/specfact_cli/sync/bridge_sync.py +0 -0
  191. {specfact_cli-0.46.2 → specfact_cli-0.46.9}/src/specfact_cli/sync/bridge_sync_openspec_md_parse.py +0 -0
  192. {specfact_cli-0.46.2 → specfact_cli-0.46.9}/src/specfact_cli/sync/bridge_sync_requirement_from_proposal.py +0 -0
  193. {specfact_cli-0.46.2 → specfact_cli-0.46.9}/src/specfact_cli/sync/bridge_sync_requirement_helpers.py +0 -0
  194. {specfact_cli-0.46.2 → specfact_cli-0.46.9}/src/specfact_cli/sync/bridge_sync_tasks_from_proposal.py +0 -0
  195. {specfact_cli-0.46.2 → specfact_cli-0.46.9}/src/specfact_cli/sync/bridge_sync_what_changes_format.py +0 -0
  196. {specfact_cli-0.46.2 → specfact_cli-0.46.9}/src/specfact_cli/sync/bridge_sync_write_openspec_from_proposal.py +0 -0
  197. {specfact_cli-0.46.2 → specfact_cli-0.46.9}/src/specfact_cli/sync/bridge_watch.py +0 -0
  198. {specfact_cli-0.46.2 → specfact_cli-0.46.9}/src/specfact_cli/sync/change_detector.py +0 -0
  199. {specfact_cli-0.46.2 → specfact_cli-0.46.9}/src/specfact_cli/sync/code_to_spec.py +0 -0
  200. {specfact_cli-0.46.2 → specfact_cli-0.46.9}/src/specfact_cli/sync/drift_detector.py +0 -0
  201. {specfact_cli-0.46.2 → specfact_cli-0.46.9}/src/specfact_cli/sync/repository_sync.py +0 -0
  202. {specfact_cli-0.46.2 → specfact_cli-0.46.9}/src/specfact_cli/sync/spec_to_code.py +0 -0
  203. {specfact_cli-0.46.2 → specfact_cli-0.46.9}/src/specfact_cli/sync/spec_to_tests.py +0 -0
  204. {specfact_cli-0.46.2 → specfact_cli-0.46.9}/src/specfact_cli/sync/watcher.py +0 -0
  205. {specfact_cli-0.46.2 → specfact_cli-0.46.9}/src/specfact_cli/sync/watcher_enhanced.py +0 -0
  206. {specfact_cli-0.46.2 → specfact_cli-0.46.9}/src/specfact_cli/telemetry.py +0 -0
  207. {specfact_cli-0.46.2 → specfact_cli-0.46.9}/src/specfact_cli/templates/__init__.py +0 -0
  208. {specfact_cli-0.46.2 → specfact_cli-0.46.9}/src/specfact_cli/templates/defaults/defect_v1.yaml +0 -0
  209. {specfact_cli-0.46.2 → specfact_cli-0.46.9}/src/specfact_cli/templates/defaults/enabler_v1.yaml +0 -0
  210. {specfact_cli-0.46.2 → specfact_cli-0.46.9}/src/specfact_cli/templates/defaults/spike_v1.yaml +0 -0
  211. {specfact_cli-0.46.2 → specfact_cli-0.46.9}/src/specfact_cli/templates/defaults/user_story_v1.yaml +0 -0
  212. {specfact_cli-0.46.2 → specfact_cli-0.46.9}/src/specfact_cli/templates/frameworks/scrum/user_story_v1.yaml +0 -0
  213. {specfact_cli-0.46.2 → specfact_cli-0.46.9}/src/specfact_cli/templates/personas/product-owner/user_story_v1.yaml +0 -0
  214. {specfact_cli-0.46.2 → specfact_cli-0.46.9}/src/specfact_cli/templates/providers/ado/work_item_v1.yaml +0 -0
  215. {specfact_cli-0.46.2 → specfact_cli-0.46.9}/src/specfact_cli/templates/registry.py +0 -0
  216. {specfact_cli-0.46.2 → specfact_cli-0.46.9}/src/specfact_cli/templates/specification_templates.py +0 -0
  217. {specfact_cli-0.46.2 → specfact_cli-0.46.9}/src/specfact_cli/utils/__init__.py +0 -0
  218. {specfact_cli-0.46.2 → specfact_cli-0.46.9}/src/specfact_cli/utils/acceptance_criteria.py +0 -0
  219. {specfact_cli-0.46.2 → specfact_cli-0.46.9}/src/specfact_cli/utils/auth_tokens.py +0 -0
  220. {specfact_cli-0.46.2 → specfact_cli-0.46.9}/src/specfact_cli/utils/bundle_converters.py +0 -0
  221. {specfact_cli-0.46.2 → specfact_cli-0.46.9}/src/specfact_cli/utils/bundle_loader.py +0 -0
  222. {specfact_cli-0.46.2 → specfact_cli-0.46.9}/src/specfact_cli/utils/code_change_detector.py +0 -0
  223. {specfact_cli-0.46.2 → specfact_cli-0.46.9}/src/specfact_cli/utils/console.py +0 -0
  224. {specfact_cli-0.46.2 → specfact_cli-0.46.9}/src/specfact_cli/utils/content_sanitizer.py +0 -0
  225. {specfact_cli-0.46.2 → specfact_cli-0.46.9}/src/specfact_cli/utils/context_detection.py +0 -0
  226. {specfact_cli-0.46.2 → specfact_cli-0.46.9}/src/specfact_cli/utils/contract_predicates.py +0 -0
  227. {specfact_cli-0.46.2 → specfact_cli-0.46.9}/src/specfact_cli/utils/enrichment_context.py +0 -0
  228. {specfact_cli-0.46.2 → specfact_cli-0.46.9}/src/specfact_cli/utils/enrichment_parser.py +0 -0
  229. {specfact_cli-0.46.2 → specfact_cli-0.46.9}/src/specfact_cli/utils/env_manager.py +0 -0
  230. {specfact_cli-0.46.2 → specfact_cli-0.46.9}/src/specfact_cli/utils/feature_keys.py +0 -0
  231. {specfact_cli-0.46.2 → specfact_cli-0.46.9}/src/specfact_cli/utils/git.py +0 -0
  232. {specfact_cli-0.46.2 → specfact_cli-0.46.9}/src/specfact_cli/utils/github_annotations.py +0 -0
  233. {specfact_cli-0.46.2 → specfact_cli-0.46.9}/src/specfact_cli/utils/icontract_helpers.py +0 -0
  234. {specfact_cli-0.46.2 → specfact_cli-0.46.9}/src/specfact_cli/utils/ide_setup.py +0 -0
  235. {specfact_cli-0.46.2 → specfact_cli-0.46.9}/src/specfact_cli/utils/incremental_check.py +0 -0
  236. {specfact_cli-0.46.2 → specfact_cli-0.46.9}/src/specfact_cli/utils/metadata.py +0 -0
  237. {specfact_cli-0.46.2 → specfact_cli-0.46.9}/src/specfact_cli/utils/performance.py +0 -0
  238. {specfact_cli-0.46.2 → specfact_cli-0.46.9}/src/specfact_cli/utils/persona_ownership.py +0 -0
  239. {specfact_cli-0.46.2 → specfact_cli-0.46.9}/src/specfact_cli/utils/progress.py +0 -0
  240. {specfact_cli-0.46.2 → specfact_cli-0.46.9}/src/specfact_cli/utils/progressive_disclosure.py +0 -0
  241. {specfact_cli-0.46.2 → specfact_cli-0.46.9}/src/specfact_cli/utils/prompts.py +0 -0
  242. {specfact_cli-0.46.2 → specfact_cli-0.46.9}/src/specfact_cli/utils/sdd_discovery.py +0 -0
  243. {specfact_cli-0.46.2 → specfact_cli-0.46.9}/src/specfact_cli/utils/source_scanner.py +0 -0
  244. {specfact_cli-0.46.2 → specfact_cli-0.46.9}/src/specfact_cli/utils/startup_checks.py +0 -0
  245. {specfact_cli-0.46.2 → specfact_cli-0.46.9}/src/specfact_cli/utils/structure.py +0 -0
  246. {specfact_cli-0.46.2 → specfact_cli-0.46.9}/src/specfact_cli/utils/structured_io.py +0 -0
  247. {specfact_cli-0.46.2 → specfact_cli-0.46.9}/src/specfact_cli/utils/suggestions.py +0 -0
  248. {specfact_cli-0.46.2 → specfact_cli-0.46.9}/src/specfact_cli/utils/terminal.py +0 -0
  249. {specfact_cli-0.46.2 → specfact_cli-0.46.9}/src/specfact_cli/utils/yaml_utils.py +0 -0
  250. {specfact_cli-0.46.2 → specfact_cli-0.46.9}/src/specfact_cli/validation/__init__.py +0 -0
  251. {specfact_cli-0.46.2 → specfact_cli-0.46.9}/src/specfact_cli/validation/command_audit.py +0 -0
  252. {specfact_cli-0.46.2 → specfact_cli-0.46.9}/src/specfact_cli/validators/__init__.py +0 -0
  253. {specfact_cli-0.46.2 → specfact_cli-0.46.9}/src/specfact_cli/validators/agile_validation.py +0 -0
  254. {specfact_cli-0.46.2 → specfact_cli-0.46.9}/src/specfact_cli/validators/change_proposal_integration.py +0 -0
  255. {specfact_cli-0.46.2 → specfact_cli-0.46.9}/src/specfact_cli/validators/cli_first_validator.py +0 -0
  256. {specfact_cli-0.46.2 → specfact_cli-0.46.9}/src/specfact_cli/validators/contract_validator.py +0 -0
  257. {specfact_cli-0.46.2 → specfact_cli-0.46.9}/src/specfact_cli/validators/fsm.py +0 -0
  258. {specfact_cli-0.46.2 → specfact_cli-0.46.9}/src/specfact_cli/validators/repro_checker.py +0 -0
  259. {specfact_cli-0.46.2 → specfact_cli-0.46.9}/src/specfact_cli/validators/schema.py +0 -0
  260. {specfact_cli-0.46.2 → specfact_cli-0.46.9}/src/specfact_cli/validators/sidecar/__init__.py +0 -0
  261. {specfact_cli-0.46.2 → specfact_cli-0.46.9}/src/specfact_cli/validators/sidecar/contract_populator.py +0 -0
  262. {specfact_cli-0.46.2 → specfact_cli-0.46.9}/src/specfact_cli/validators/sidecar/crosshair_runner.py +0 -0
  263. {specfact_cli-0.46.2 → specfact_cli-0.46.9}/src/specfact_cli/validators/sidecar/crosshair_summary.py +0 -0
  264. {specfact_cli-0.46.2 → specfact_cli-0.46.9}/src/specfact_cli/validators/sidecar/dependency_installer.py +0 -0
  265. {specfact_cli-0.46.2 → specfact_cli-0.46.9}/src/specfact_cli/validators/sidecar/framework_detector.py +0 -0
  266. {specfact_cli-0.46.2 → specfact_cli-0.46.9}/src/specfact_cli/validators/sidecar/frameworks/__init__.py +0 -0
  267. {specfact_cli-0.46.2 → specfact_cli-0.46.9}/src/specfact_cli/validators/sidecar/frameworks/base.py +0 -0
  268. {specfact_cli-0.46.2 → specfact_cli-0.46.9}/src/specfact_cli/validators/sidecar/frameworks/django.py +0 -0
  269. {specfact_cli-0.46.2 → specfact_cli-0.46.9}/src/specfact_cli/validators/sidecar/frameworks/drf.py +0 -0
  270. {specfact_cli-0.46.2 → specfact_cli-0.46.9}/src/specfact_cli/validators/sidecar/frameworks/fastapi.py +0 -0
  271. {specfact_cli-0.46.2 → specfact_cli-0.46.9}/src/specfact_cli/validators/sidecar/frameworks/flask.py +0 -0
  272. {specfact_cli-0.46.2 → specfact_cli-0.46.9}/src/specfact_cli/validators/sidecar/harness_generator.py +0 -0
  273. {specfact_cli-0.46.2 → specfact_cli-0.46.9}/src/specfact_cli/validators/sidecar/models.py +0 -0
  274. {specfact_cli-0.46.2 → specfact_cli-0.46.9}/src/specfact_cli/validators/sidecar/orchestrator.py +0 -0
  275. {specfact_cli-0.46.2 → specfact_cli-0.46.9}/src/specfact_cli/validators/sidecar/specmatic_runner.py +0 -0
  276. {specfact_cli-0.46.2 → specfact_cli-0.46.9}/src/specfact_cli/validators/sidecar/unannotated_detector.py +0 -0
  277. {specfact_cli-0.46.2 → specfact_cli-0.46.9}/src/specfact_cli/versioning/__init__.py +0 -0
  278. {specfact_cli-0.46.2 → specfact_cli-0.46.9}/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.46.2
3
+ Version: 0.46.9
4
4
  Summary: The swiss knife CLI for agile DevOps teams. Keep backlog, specs, tests, and code in sync with validation and contract enforcement for new projects and long-lived codebases.
5
5
  Project-URL: Homepage, https://github.com/nold-ai/specfact-cli
6
6
  Project-URL: Repository, https://github.com/nold-ai/specfact-cli.git
@@ -224,12 +224,12 @@ Classifier: Topic :: Software Development :: Testing
224
224
  Requires-Python: >=3.11
225
225
  Requires-Dist: azure-identity>=1.17.1
226
226
  Requires-Dist: beartype>=0.22.4
227
+ Requires-Dist: commentjson>=0.9.0
227
228
  Requires-Dist: cryptography>=43.0.0
228
229
  Requires-Dist: gitpython>=3.1.45
229
230
  Requires-Dist: graphviz>=0.20.1
230
231
  Requires-Dist: icontract>=2.7.1
231
232
  Requires-Dist: jinja2>=3.1.6
232
- Requires-Dist: json5>=0.9.28
233
233
  Requires-Dist: jsonschema>=4.23.0
234
234
  Requires-Dist: networkx>=3.4.2
235
235
  Requires-Dist: opentelemetry-exporter-otlp-proto-http>=1.27.0
@@ -248,16 +248,18 @@ Provides-Extra: contracts
248
248
  Requires-Dist: crosshair-tool>=0.0.97; extra == 'contracts'
249
249
  Requires-Dist: hypothesis>=6.142.4; extra == 'contracts'
250
250
  Provides-Extra: dev
251
+ Requires-Dist: bandit>=1.7.0; extra == 'dev'
251
252
  Requires-Dist: basedpyright>=1.32.1; extra == 'dev'
252
- Requires-Dist: bearer>=3.1.0; extra == 'dev'
253
253
  Requires-Dist: beartype>=0.22.4; extra == 'dev'
254
254
  Requires-Dist: crosshair-tool>=0.0.97; extra == 'dev'
255
255
  Requires-Dist: graphviz>=0.20.1; extra == 'dev'
256
256
  Requires-Dist: hypothesis>=6.142.4; extra == 'dev'
257
257
  Requires-Dist: icontract>=2.7.1; extra == 'dev'
258
258
  Requires-Dist: isort>=7.0.0; extra == 'dev'
259
+ Requires-Dist: pip-audit>=2.0.0; extra == 'dev'
260
+ Requires-Dist: pip-licenses>=4.0.0; extra == 'dev'
259
261
  Requires-Dist: pip-tools>=7.5.1; extra == 'dev'
260
- Requires-Dist: pyan3>=1.2.0; extra == 'dev'
262
+ Requires-Dist: pycg==0.0.7; extra == 'dev'
261
263
  Requires-Dist: pylint>=4.0.2; extra == 'dev'
262
264
  Requires-Dist: pytest-asyncio>=1.2.0; extra == 'dev'
263
265
  Requires-Dist: pytest-cov>=7.0.0; extra == 'dev'
@@ -271,10 +273,8 @@ Requires-Dist: setuptools<82,>=69.0.0; extra == 'dev'
271
273
  Requires-Dist: tomlkit>=0.13.3; extra == 'dev'
272
274
  Requires-Dist: types-pyyaml>=6.0.12.20250516; extra == 'dev'
273
275
  Provides-Extra: enhanced-analysis
274
- Requires-Dist: bearer>=3.1.0; extra == 'enhanced-analysis'
275
276
  Requires-Dist: graphviz>=0.20.1; extra == 'enhanced-analysis'
276
- Requires-Dist: pyan3>=1.2.0; extra == 'enhanced-analysis'
277
- Requires-Dist: syft>=0.9.5; extra == 'enhanced-analysis'
277
+ Requires-Dist: pycg==0.0.7; extra == 'enhanced-analysis'
278
278
  Provides-Extra: scanning
279
279
  Requires-Dist: semgrep>=1.144.0; extra == 'scanning'
280
280
  Description-Content-Type: text/markdown
@@ -308,7 +308,7 @@ uvx specfact-cli code review run --path . --scope full
308
308
  **Sample output:**
309
309
 
310
310
  ```text
311
- SpecFact CLI - v0.46.1
311
+ SpecFact CLI - v0.46.4
312
312
 
313
313
  Running Ruff checks...
314
314
  Running Radon complexity checks...
@@ -367,14 +367,15 @@ This repository uses a **modular** local hook layout (parity with `specfact-cli-
367
367
  separate verify / format / YAML / Markdown / workflow / lint / Block 2 hooks). If you copy
368
368
  [`.pre-commit-config.yaml`](.pre-commit-config.yaml) into another repo, you must also vendor the
369
369
  referenced `scripts/*.sh` entrypoints (at minimum `scripts/pre-commit-quality-checks.sh`,
370
- `scripts/pre-commit-verify-modules.sh`, and `scripts/git-branch-module-signature-flag.sh`) so hook
370
+ `scripts/pre-commit-verify-modules.sh`, `scripts/module-verify-policy.sh`, and
371
+ `scripts/git-branch-module-signature-flag.sh`) so hook
371
372
  `entry:` paths resolve. Alternatively, skip vendoring the modular file and use the remote hook below.
372
373
 
373
374
  For a **single-hook** setup in downstream repos, keep using the stable id and script shim:
374
375
 
375
376
  ```yaml
376
377
  - repo: https://github.com/nold-ai/specfact-cli
377
- rev: v0.46.1
378
+ rev: v0.46.4
378
379
  hooks:
379
380
  - id: specfact-smart-checks
380
381
  ```
@@ -27,7 +27,7 @@ uvx specfact-cli code review run --path . --scope full
27
27
  **Sample output:**
28
28
 
29
29
  ```text
30
- SpecFact CLI - v0.46.1
30
+ SpecFact CLI - v0.46.4
31
31
 
32
32
  Running Ruff checks...
33
33
  Running Radon complexity checks...
@@ -86,14 +86,15 @@ This repository uses a **modular** local hook layout (parity with `specfact-cli-
86
86
  separate verify / format / YAML / Markdown / workflow / lint / Block 2 hooks). If you copy
87
87
  [`.pre-commit-config.yaml`](.pre-commit-config.yaml) into another repo, you must also vendor the
88
88
  referenced `scripts/*.sh` entrypoints (at minimum `scripts/pre-commit-quality-checks.sh`,
89
- `scripts/pre-commit-verify-modules.sh`, and `scripts/git-branch-module-signature-flag.sh`) so hook
89
+ `scripts/pre-commit-verify-modules.sh`, `scripts/module-verify-policy.sh`, and
90
+ `scripts/git-branch-module-signature-flag.sh`) so hook
90
91
  `entry:` paths resolve. Alternatively, skip vendoring the modular file and use the remote hook below.
91
92
 
92
93
  For a **single-hook** setup in downstream repos, keep using the stable id and script shim:
93
94
 
94
95
  ```yaml
95
96
  - repo: https://github.com/nold-ai/specfact-cli
96
- rev: v0.46.1
97
+ rev: v0.46.4
97
98
  hooks:
98
99
  - id: specfact-smart-checks
99
100
  ```
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "specfact-cli"
7
- version = "0.46.2"
7
+ version = "0.46.9"
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"
@@ -77,6 +77,7 @@ dependencies = [
77
77
  "graphviz>=0.20.1", # Graph visualization (requires system Graphviz: apt-get install graphviz)
78
78
 
79
79
  # Git operations
80
+ # gitpython: CVE history (CVE-2022-24439, CVE-2023-41040, CVE-2023-40590). Phase 2: replace with dulwich.
80
81
  "gitpython>=3.1.45",
81
82
 
82
83
  # YAML utilities
@@ -84,8 +85,8 @@ dependencies = [
84
85
 
85
86
  # Schema validation
86
87
  "jsonschema>=4.23.0",
87
- # VS Code settings.json is often JSON with comments (JSONC); parse/emit via JSON5 subset
88
- "json5>=0.9.28",
88
+ # VS Code settings.json is JSONC (comments + trailing commas); commentjson (MIT) strips them, delegates to stdlib json
89
+ "commentjson>=0.9.0",
89
90
 
90
91
  # Contract-First (runtime decorators; exploration tools are optional extra `contracts`)
91
92
  "icontract>=2.7.1", # Design-by-contract decorators
@@ -114,13 +115,13 @@ dev = [
114
115
  "pytest-xdist>=3.8.0",
115
116
  "basedpyright>=1.32.1",
116
117
  "isort>=7.0.0",
117
- "pylint>=4.0.2",
118
+ "pylint>=4.0.2", # GPL-2.0-or-later — dev-only exception (not in module manifests). Phase 2: replace with ruff --select ALL.
118
119
  "ruff>=0.14.2",
119
120
  "radon>=6.0.1",
120
121
  "tomlkit>=0.13.3", # Style-preserving TOML library (recommended successor to pytoml)
121
122
  "types-PyYAML>=6.0.12.20250516",
122
123
  "pip-tools>=7.5.1",
123
- "semgrep>=1.144.0", # Latest version compatible with rich~=13.5.2
124
+ "semgrep>=1.144.0", # LGPL-2.1 required for code analysis; invoked as subprocess (accepted exception)
124
125
 
125
126
  # Same contract exploration stack as [contracts] (extras cannot self-reference)
126
127
  "crosshair-tool>=0.0.97",
@@ -131,11 +132,14 @@ dev = [
131
132
  "beartype>=0.22.4",
132
133
 
133
134
  # Enhanced Analysis Tools (for local development)
134
- # Note: syft excluded from dev/test due to rich version conflict with semgrep
135
- # Install separately: pip install specfact-cli[enhanced-analysis] if needed
136
135
  "graphviz>=0.20.1", # Graph visualization (requires system Graphviz: apt-get install graphviz)
137
- "pyan3>=1.2.0", # Python call graph analysis
138
- "bearer>=3.1.0", # Data flow analysis for security
136
+ "pycg==0.0.7", # Python call graph analysis (Apache-2.0; replaces removed GPL-2.0 pyan3; pin avoids 0.0.8)
137
+ # Removed from dev: pyan3 (GPL-2.0) and bearer (wrong PyPI package; SaaS auth, not scanner)
138
+ # syft removed — wrong PyPI package (OpenMined ML framework, not Anchore SBOM)
139
+ "bandit>=1.7.0", # SAST scanner (MIT)
140
+ "pip-licenses>=4.0.0", # License enumeration for compliance gate (MIT)
141
+ "pip-audit>=2.0.0", # CVE audit via OSV database (Apache-2.0)
142
+ # pylint: GPL-2.0-or-later — dev-only exception (not in module manifests). Phase 2: replace with ruff --select ALL.
139
143
  ]
140
144
 
141
145
  scanning = [
@@ -144,9 +148,8 @@ scanning = [
144
148
 
145
149
  enhanced-analysis = [
146
150
  "graphviz>=0.20.1", # Graph visualization (requires system Graphviz: apt-get install graphviz)
147
- "pyan3>=1.2.0", # Python call graph analysis
148
- "syft>=0.9.5", # Software Bill of Materials (SBOM) generation
149
- "bearer>=3.1.0", # Data flow analysis for security
151
+ "pycg==0.0.7", # Python call graph analysis (Apache-2.0; replaces removed GPL-2.0 pyan3; pin avoids 0.0.8)
152
+ # Removed from enhanced-analysis: pyan3 (GPL-2.0), bearer, and syft (wrong PyPI packages)
150
153
  ]
151
154
 
152
155
  # Note: Specmatic integration (specfact spec commands) requires the Specmatic CLI tool
@@ -188,29 +191,37 @@ dependencies = [
188
191
  "pylint>=4.0.2",
189
192
  "ruff>=0.14.2",
190
193
  "radon>=6.0.1",
191
- "yamllint>=1.37.1",
192
- "semgrep>=1.144.0", # Latest version compatible with rich~=13.5.2
194
+ "yamllint>=1.37.1", # GPL-3.0-or-later — dev/test-only exception. Phase 2: replace with non-GPL YAML lint path.
195
+ "semgrep>=1.144.0", # LGPL-2.1 required for code analysis; invoked as subprocess (accepted exception)
193
196
  # Contract-First Development Dependencies
194
197
  "icontract>=2.7.1",
195
- "beartype>=0.22.4",
198
+ "beartype>=0.22.4",
196
199
  "crosshair-tool>=0.0.97",
197
200
  "hypothesis>=6.142.4",
198
201
  # Enhanced Analysis Tools (for local development)
199
- # Note: syft excluded from dev/test due to rich version conflict with semgrep
200
- # Install separately: pip install specfact-cli[enhanced-analysis] if needed
201
202
  "graphviz>=0.20.1", # Graph visualization (requires system Graphviz: apt-get install graphviz)
202
- "pyan3>=1.2.0", # Python call graph analysis
203
- "bearer>=3.1.0", # Data flow analysis for security
203
+ "pycg==0.0.7", # Python call graph analysis (Apache-2.0; replaces removed GPL-2.0 pyan3; pin avoids 0.0.8)
204
+ # Removed from Hatch default: pyan3 (GPL-2.0), bearer, and syft (wrong PyPI packages)
205
+ "bandit>=1.7.0", # SAST scanner (MIT)
206
+ "pip-licenses>=4.0.0", # License enumeration for compliance gate (MIT)
207
+ "pip-audit>=2.0.0", # CVE audit via OSV database (Apache-2.0)
208
+ # pylint: GPL-2.0-or-later — dev-only exception (not in module manifests). Phase 2: replace with ruff --select ALL.
204
209
  ]
205
210
 
206
211
  [tool.hatch.envs.default.scripts]
207
212
  validate-prompts = "python tools/validate_prompts.py"
213
+ # Security and license compliance gates
214
+ bandit-scan = "bandit -r src/ -ll"
215
+ license-check = "python scripts/check_license_compliance.py"
216
+ # Wrap pip-audit so JSON parsing and CVSS threshold checks fail closed.
217
+ security-audit = "python scripts/security_audit_gate.py"
208
218
  # Development scripts
209
219
  test = "pytest {args}"
210
220
  test-cov = "pytest --cov=src --cov-report=term-missing {args}"
211
221
  type-check = "basedpyright --pythonpath $(python -c 'import sys; print(sys.executable)') {args}"
212
222
  # basedpyright --level error: suppress warning noise in pre-commit (Block 1 runs `hatch run lint`).
213
223
  lint = "ruff format . --check && basedpyright --level error --pythonpath $(python -c 'import sys; print(sys.executable)') && ruff check . && pylint src tests tools && python scripts/verify_safe_project_writes.py"
224
+ lint-changed = "python scripts/run_changed_lint.py {args}"
214
225
  governance = "pylint src tests tools --reports=y --output-format=parseable"
215
226
  format = "ruff check . --fix && ruff format ."
216
227
 
@@ -261,10 +272,15 @@ smart-test-e2e = "python tools/smart_test_coverage.py run --level e2e {args}"
261
272
  smart-test-full = "python tools/smart_test_coverage.py run --level full {args}"
262
273
  smart-test-auto = "python tools/smart_test_coverage.py run --level auto {args}"
263
274
 
275
+ # Bundled module verify (flags from scripts/module-verify-policy.sh via run_verify_modules_policy.sh)
276
+ verify-modules-signature = "bash scripts/run_verify_modules_policy.sh strict {args}"
277
+ verify-modules-signature-pr = "bash scripts/run_verify_modules_policy.sh pr {args}"
278
+ verify-modules-signature-push = "bash scripts/run_verify_modules_policy.sh push-orchestrator {args}"
279
+
264
280
  # Module migration pre-deletion gate
265
281
  verify-removal-gate = [
266
282
  "python scripts/verify-bundle-published.py --modules project,plan,import_cmd,sync,migrate,backlog,policy_engine,analyze,drift,validate,repro,contract,spec,sdd,generate,enforce,patch_mode",
267
- "python scripts/verify-modules-signature.py --require-signature",
283
+ "hatch run verify-modules-signature",
268
284
  ]
269
285
  export-change-github = "python scripts/export-change-to-github.py {args}"
270
286
 
@@ -314,13 +330,12 @@ dependencies = [
314
330
  "beartype>=0.22.4",
315
331
  "crosshair-tool>=0.0.97",
316
332
  "hypothesis>=6.142.4",
317
- "yamllint>=1.37.1",
333
+ "yamllint>=1.37.1", # GPL-3.0-or-later — dev/test-only exception. Phase 2: replace with non-GPL YAML lint path.
318
334
  # Enhanced Analysis Tools (for testing)
319
- # Note: syft excluded from test due to rich version conflict with semgrep
320
- # Install separately: pip install specfact-cli[enhanced-analysis] if needed
321
335
  "graphviz>=0.20.1", # Graph visualization (requires system Graphviz: apt-get install graphviz)
322
- "pyan3>=1.2.0", # Python call graph analysis
323
- "bearer>=3.1.0", # Data flow analysis for security
336
+ "pycg==0.0.7", # Python call graph analysis (Apache-2.0; replaces removed GPL-2.0 pyan3; pin avoids 0.0.8)
337
+ # Removed from Hatch test: pyan3 (GPL-2.0), bearer, and syft (wrong PyPI packages)
338
+ "commentjson>=0.9.0", # JSONC parser (MIT; replaces json5 for VS Code settings JSONC support)
324
339
  ]
325
340
  dev-mode = true
326
341
  parallel = true
@@ -0,0 +1,20 @@
1
+ {
2
+ "modules": [
3
+ {
4
+ "id": "init",
5
+ "latest_version": "0.1.30"
6
+ },
7
+ {
8
+ "id": "upgrade",
9
+ "latest_version": "0.1.4"
10
+ },
11
+ {
12
+ "id": "module-registry",
13
+ "latest_version": "0.1.19"
14
+ },
15
+ {
16
+ "id": "bundle-mapper",
17
+ "latest_version": "0.1.9"
18
+ }
19
+ ]
20
+ }
@@ -3,4 +3,4 @@ SpecFact CLI - Spec→Contract→Sentinel tool for contract-driven development.
3
3
  """
4
4
 
5
5
  # Package version: keep in sync with pyproject.toml, setup.py, src/specfact_cli/__init__.py
6
- __version__ = "0.46.2"
6
+ __version__ = "0.46.9"
@@ -45,6 +45,6 @@ def _bootstrap_bundle_paths() -> None:
45
45
 
46
46
  _bootstrap_bundle_paths()
47
47
 
48
- __version__ = "0.46.2"
48
+ __version__ = "0.46.9"
49
49
 
50
50
  __all__ = ["__version__"]
@@ -32,6 +32,24 @@ from specfact_cli.utils.feature_keys import to_classname_key, to_sequential_key
32
32
  console = Console()
33
33
 
34
34
 
35
+ def _ensure_semgrep_runtime_dir(repo_path: Path, relative: str) -> str:
36
+ """Create and return a stable repo-local runtime directory for Semgrep."""
37
+ path = (repo_path / relative).resolve()
38
+ path.mkdir(parents=True, exist_ok=True)
39
+ return str(path)
40
+
41
+
42
+ def _build_semgrep_env(repo_path: Path) -> dict[str, str]:
43
+ """Build a repo-local Semgrep runtime env so CLI startup stays deterministic."""
44
+ env = dict(os.environ)
45
+ env["XDG_CONFIG_HOME"] = _ensure_semgrep_runtime_dir(repo_path, ".specfact/config")
46
+ env["XDG_CACHE_HOME"] = _ensure_semgrep_runtime_dir(repo_path, ".specfact/cache")
47
+ env["SEMGREP_VERSION_CACHE_PATH"] = _ensure_semgrep_runtime_dir(repo_path, ".specfact/cache/semgrep")
48
+ semgrep_log_dir = _ensure_semgrep_runtime_dir(repo_path, ".specfact/logs")
49
+ env["SEMGREP_LOG_FILE"] = str((Path(semgrep_log_dir) / "semgrep.log").resolve())
50
+ return env
51
+
52
+
35
53
  @dataclass
36
54
  class _SemgrepFeatureBuckets:
37
55
  api_endpoints: list[str] = field(default_factory=list)
@@ -457,6 +475,7 @@ class CodeAnalyzer:
457
475
  capture_output=True,
458
476
  text=True,
459
477
  timeout=5, # Increased timeout to 5s (Semgrep may need time to initialize)
478
+ env=_build_semgrep_env(self.repo_path),
460
479
  )
461
480
  return result.returncode == 0
462
481
  except (FileNotFoundError, subprocess.TimeoutExpired, OSError):
@@ -507,16 +526,16 @@ class CodeAnalyzer:
507
526
  }
508
527
  )
509
528
 
510
- # Dependency Graph Analysis (requires pyan3 and networkx)
511
- pyan3_available, _ = check_cli_tool_available("pyan3")
529
+ # Dependency Graph Analysis (requires pycg and networkx)
530
+ pycg_available, _ = check_cli_tool_available("pycg")
512
531
  networkx_available = check_python_package_available("networkx")
513
- graph_enabled = pyan3_available and networkx_available
532
+ graph_enabled = pycg_available and networkx_available
514
533
  graph_used = graph_enabled # Used if both dependencies are available
515
534
 
516
- if not pyan3_available and not networkx_available:
517
- reason = "pyan3 and networkx not installed (install: pip install pyan3 networkx)"
518
- elif not pyan3_available:
519
- reason = "pyan3 not installed (install: pip install pyan3)"
535
+ if not pycg_available and not networkx_available:
536
+ reason = "pycg and networkx not installed (install: pip install pycg networkx)"
537
+ elif not pycg_available:
538
+ reason = "pycg not installed (install: pip install pycg)"
520
539
  elif not networkx_available:
521
540
  reason = "networkx not installed (install: pip install networkx)"
522
541
  else:
@@ -566,6 +585,7 @@ class CodeAnalyzer:
566
585
  capture_output=True,
567
586
  text=True,
568
587
  timeout=timeout,
588
+ env=_build_semgrep_env(self.repo_path),
569
589
  )
570
590
 
571
591
  # Semgrep may return non-zero for valid findings
@@ -3,6 +3,8 @@ Graph-based dependency and call graph analysis.
3
3
 
4
4
  Enhances AST and Semgrep analysis with graph-based dependency tracking,
5
5
  call graph extraction, and architecture visualization.
6
+
7
+ Call graph extraction uses pycg (MIT) via subprocess. pyan3 (GPL-2.0) removed.
6
8
  """
7
9
 
8
10
  from __future__ import annotations
@@ -28,7 +30,7 @@ class GraphAnalyzer:
28
30
  """
29
31
  Graph-based dependency and call graph analysis.
30
32
 
31
- Uses pyan for call graphs, NetworkX for dependency graphs,
33
+ Uses pycg for call graphs, NetworkX for dependency graphs,
32
34
  and provides graph-based insights to complement AST and Semgrep.
33
35
  """
34
36
 
@@ -55,7 +57,7 @@ class GraphAnalyzer:
55
57
  @ensure(lambda result: isinstance(result, dict), "Must return dict")
56
58
  def extract_call_graph(self, file_path: Path) -> dict[str, list[str]]:
57
59
  """
58
- Extract call graph using pyan.
60
+ Extract call graph using pycg (MIT).
59
61
 
60
62
  Args:
61
63
  file_path: Path to Python file
@@ -63,75 +65,67 @@ class GraphAnalyzer:
63
65
  Returns:
64
66
  Dictionary mapping function names to list of called functions
65
67
  """
66
- # Check if pyan3 is available using utility function
67
68
  from specfact_cli.utils.optional_deps import check_cli_tool_available
68
69
 
69
- is_available, _ = check_cli_tool_available("pyan3")
70
+ is_available, _ = check_cli_tool_available("pycg")
70
71
  if not is_available:
71
- # pyan3 not available, return empty
72
+ # pycg not available return empty (graceful degradation)
72
73
  return {}
73
74
 
74
- # Run pyan to generate DOT file
75
- with tempfile.NamedTemporaryFile(mode="w", suffix=".dot", delete=False) as dot_file:
76
- dot_path = Path(dot_file.name)
75
+ # Run pycg to generate JSON call graph
76
+ with tempfile.NamedTemporaryFile(mode="w", suffix=".json", delete=False) as json_file:
77
+ json_path = Path(json_file.name)
77
78
  try:
78
79
  result = subprocess.run(
79
- ["pyan3", str(file_path), "--dot", "--no-defines", "--uses", "--defines"],
80
- stdout=dot_file,
80
+ ["pycg", "--package", str(self.repo_path), str(file_path), "--output", str(json_path)],
81
81
  stderr=subprocess.PIPE,
82
82
  text=True,
83
- timeout=15, # Reduced from 30 to 15 seconds for faster processing
83
+ timeout=15,
84
84
  )
85
85
 
86
86
  if result.returncode == 0:
87
- # Parse DOT file to extract call relationships
88
- call_graph = self._parse_dot_file(dot_path)
87
+ call_graph = self._parse_pycg_json(json_path)
89
88
  file_key = str(file_path.relative_to(self.repo_path))
90
89
  self.call_graphs[file_key] = call_graph
91
90
  return call_graph
92
91
  finally:
93
- # Clean up temp file
94
- if dot_path.exists():
95
- dot_path.unlink()
92
+ if json_path.exists():
93
+ json_path.unlink()
96
94
 
97
95
  return {}
98
96
 
99
97
  @beartype
100
- @require(lambda dot_path: isinstance(dot_path, Path), "DOT path must be Path")
98
+ @require(lambda json_path: isinstance(json_path, Path), "JSON path must be Path")
101
99
  @ensure(lambda result: isinstance(result, dict), "Must return dict")
102
- def _parse_dot_file(self, dot_path: Path) -> dict[str, list[str]]:
100
+ def _parse_pycg_json(self, json_path: Path) -> dict[str, list[str]]:
103
101
  """
104
- Parse DOT file to extract call graph.
102
+ Parse pycg JSON output into caller → [callees] format.
103
+
104
+ PyCG's simple JSON format is an adjacency list: edge ``(src, dst)`` is
105
+ ``dst`` in the list for key ``src`` (caller → callees). See PyCG README
106
+ "Simple JSON format".
105
107
 
106
108
  Args:
107
- dot_path: Path to DOT file
109
+ json_path: Path to pycg JSON output file
108
110
 
109
111
  Returns:
110
112
  Dictionary mapping function names to list of called functions
111
113
  """
112
- call_graph: dict[str, list[str]] = defaultdict(list)
114
+ import json
113
115
 
114
- if not dot_path.exists():
116
+ if not json_path.exists():
115
117
  return {}
116
118
 
117
119
  try:
118
- content = dot_path.read_text(encoding="utf-8")
119
- # Parse DOT format: "function_a" -> "function_b"
120
- import re
121
-
122
- # Pattern: "function_a" -> "function_b"
123
- edge_pattern = r'"([^"]+)"\s*->\s*"([^"]+)"'
124
- matches = re.finditer(edge_pattern, content)
125
-
126
- for match in matches:
127
- caller = match.group(1)
128
- callee = match.group(2)
129
- # Filter out internal Python functions (start with __)
120
+ raw: dict[str, list[str]] = json.loads(json_path.read_text(encoding="utf-8"))
121
+ except (json.JSONDecodeError, UnicodeDecodeError, OSError):
122
+ return {}
123
+
124
+ call_graph: dict[str, list[str]] = defaultdict(list)
125
+ for caller, callees in raw.items():
126
+ for callee in callees:
130
127
  if not caller.startswith("__") and not callee.startswith("__"):
131
128
  call_graph[caller].append(callee)
132
- except (UnicodeDecodeError, Exception):
133
- # Skip if parsing fails
134
- pass
135
129
 
136
130
  return dict(call_graph)
137
131
 
@@ -209,7 +203,7 @@ class GraphAnalyzer:
209
203
  wait_on_shutdown: bool,
210
204
  progress_callback: Any | None,
211
205
  ) -> None:
212
- """Populate graph with edges derived from pyan call graphs (parallel phase 2)."""
206
+ """Populate graph with edges derived from pycg call graphs (parallel phase 2)."""
213
207
  from concurrent.futures import ThreadPoolExecutor, as_completed
214
208
 
215
209
  loaded_contents = self._load_python_file_contents_index(python_files)
@@ -238,7 +232,7 @@ class GraphAnalyzer:
238
232
  """
239
233
  Build comprehensive dependency graph using NetworkX.
240
234
 
241
- Combines AST-based imports with pyan call graphs for complete
235
+ Combines AST-based imports with pycg call graphs for complete
242
236
  dependency tracking.
243
237
 
244
238
  Args:
@@ -66,6 +66,7 @@ from specfact_cli.registry import CommandRegistry
66
66
  from specfact_cli.registry.alias_manager import resolve_command
67
67
  from specfact_cli.registry.bootstrap import register_builtin_commands
68
68
  from specfact_cli.registry.metadata import CommandMetadata
69
+ from specfact_cli.registry.module_availability import ModuleAvailabilityStatus, classify_module_availability
69
70
  from specfact_cli.runtime import get_configured_console, init_debug_log_file, set_debug_mode
70
71
  from specfact_cli.utils.progressive_disclosure import ProgressiveDisclosureGroup
71
72
  from specfact_cli.utils.structured_io import StructuredFormat
@@ -124,6 +125,28 @@ def _print_missing_bundle_command_help(invoked: str) -> None:
124
125
  module_id = _INVOKED_TO_MARKETPLACE_MODULE.get(invoked)
125
126
  console = get_configured_console()
126
127
  if module_id is not None:
128
+ availability = classify_module_availability(module_id=module_id, command_name=invoked)
129
+ if availability.status is ModuleAvailabilityStatus.DISABLED:
130
+ console.print(
131
+ f"[bold red]Module '{availability.module_id or module_id}' is installed but disabled.[/bold red]\n"
132
+ f"The [bold]{invoked}[/bold] command group is provided by that module. "
133
+ f"Enable with [bold]{availability.recovery_command}[/bold]."
134
+ )
135
+ return
136
+ if availability.status is ModuleAvailabilityStatus.SKIPPED:
137
+ console.print(
138
+ f"[bold red]Module '{availability.module_id or module_id}' is installed but skipped.[/bold red]\n"
139
+ f"Reason: {availability.reason}. "
140
+ "Inspect with [bold]specfact module list --show-origin[/bold]."
141
+ )
142
+ return
143
+ if availability.status is ModuleAvailabilityStatus.SHADOWED:
144
+ console.print(
145
+ f"[bold red]Module '{availability.module_id or module_id}' is shadowed in this workspace.[/bold red]\n"
146
+ f"Shadowed by: {availability.shadowed_by}. "
147
+ "Inspect with [bold]specfact module list --show-origin[/bold]."
148
+ )
149
+ return
127
150
  console.print(
128
151
  f"[bold red]Module '{module_id}' is not installed.[/bold red]\n"
129
152
  f"The [bold]{invoked}[/bold] command group is provided by that module. "
@@ -1,5 +1,5 @@
1
1
  name: init
2
- version: 0.1.30
2
+ version: 0.1.31
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:9e03421972d3254082307834b474e7673957de8c11ffacc563f2da3f35e7cf05
21
- signature: ikUYhUJ8AFU9RkB+ZBnp0lKrDLT7ZIqkauRYUMRY/swrIjRCTRzHIpNcvCmujK6h1w6EtT/+wGG1v5bZtZB8CQ==
20
+ checksum: sha256:0f7bc54a823bea14033fcb143ecb6c83d2bca2b5da661f03a0b545100acebe5b
21
+ signature: dTatkqgBUtti4tL/pmcFBZY9bsJ61gY/V0lP9gZU8Y5W3YWK+wpgRx1oewlAmfKzkxca2NhalKcjLACQjTNvAA==