specfact-cli 0.13.0__tar.gz → 0.13.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (142) hide show
  1. {specfact_cli-0.13.0 → specfact_cli-0.13.1}/PKG-INFO +1 -1
  2. {specfact_cli-0.13.0 → specfact_cli-0.13.1}/pyproject.toml +1 -1
  3. {specfact_cli-0.13.0 → specfact_cli-0.13.1}/src/__init__.py +1 -1
  4. {specfact_cli-0.13.0 → specfact_cli-0.13.1}/src/specfact_cli/__init__.py +1 -1
  5. {specfact_cli-0.13.0 → specfact_cli-0.13.1}/src/specfact_cli/commands/generate.py +293 -112
  6. {specfact_cli-0.13.0 → specfact_cli-0.13.1}/src/specfact_cli/telemetry.py +113 -0
  7. {specfact_cli-0.13.0 → specfact_cli-0.13.1}/.gitignore +0 -0
  8. {specfact_cli-0.13.0 → specfact_cli-0.13.1}/LICENSE.md +0 -0
  9. {specfact_cli-0.13.0 → specfact_cli-0.13.1}/README.md +0 -0
  10. {specfact_cli-0.13.0 → specfact_cli-0.13.1}/resources/mappings/node-async.yaml +0 -0
  11. {specfact_cli-0.13.0 → specfact_cli-0.13.1}/resources/mappings/python-async.yaml +0 -0
  12. {specfact_cli-0.13.0 → specfact_cli-0.13.1}/resources/mappings/speckit-default.yaml +0 -0
  13. {specfact_cli-0.13.0 → specfact_cli-0.13.1}/resources/prompts/shared/cli-enforcement.md +0 -0
  14. {specfact_cli-0.13.0 → specfact_cli-0.13.1}/resources/prompts/specfact.01-import.md +0 -0
  15. {specfact_cli-0.13.0 → specfact_cli-0.13.1}/resources/prompts/specfact.02-plan.md +0 -0
  16. {specfact_cli-0.13.0 → specfact_cli-0.13.1}/resources/prompts/specfact.03-review.md +0 -0
  17. {specfact_cli-0.13.0 → specfact_cli-0.13.1}/resources/prompts/specfact.04-sdd.md +0 -0
  18. {specfact_cli-0.13.0 → specfact_cli-0.13.1}/resources/prompts/specfact.05-enforce.md +0 -0
  19. {specfact_cli-0.13.0 → specfact_cli-0.13.1}/resources/prompts/specfact.06-sync.md +0 -0
  20. {specfact_cli-0.13.0 → specfact_cli-0.13.1}/resources/prompts/specfact.compare.md +0 -0
  21. {specfact_cli-0.13.0 → specfact_cli-0.13.1}/resources/prompts/specfact.validate.md +0 -0
  22. {specfact_cli-0.13.0 → specfact_cli-0.13.1}/resources/schemas/deviation.schema.json +0 -0
  23. {specfact_cli-0.13.0 → specfact_cli-0.13.1}/resources/schemas/plan.schema.json +0 -0
  24. {specfact_cli-0.13.0 → specfact_cli-0.13.1}/resources/schemas/protocol.schema.json +0 -0
  25. {specfact_cli-0.13.0 → specfact_cli-0.13.1}/resources/templates/github-action.yml.j2 +0 -0
  26. {specfact_cli-0.13.0 → specfact_cli-0.13.1}/resources/templates/plan.bundle.yaml.j2 +0 -0
  27. {specfact_cli-0.13.0 → specfact_cli-0.13.1}/resources/templates/pr-template.md.j2 +0 -0
  28. {specfact_cli-0.13.0 → specfact_cli-0.13.1}/resources/templates/protocol.yaml.j2 +0 -0
  29. {specfact_cli-0.13.0 → specfact_cli-0.13.1}/resources/templates/telemetry.yaml.example +0 -0
  30. {specfact_cli-0.13.0 → specfact_cli-0.13.1}/src/specfact_cli/agents/__init__.py +0 -0
  31. {specfact_cli-0.13.0 → specfact_cli-0.13.1}/src/specfact_cli/agents/analyze_agent.py +0 -0
  32. {specfact_cli-0.13.0 → specfact_cli-0.13.1}/src/specfact_cli/agents/base.py +0 -0
  33. {specfact_cli-0.13.0 → specfact_cli-0.13.1}/src/specfact_cli/agents/plan_agent.py +0 -0
  34. {specfact_cli-0.13.0 → specfact_cli-0.13.1}/src/specfact_cli/agents/registry.py +0 -0
  35. {specfact_cli-0.13.0 → specfact_cli-0.13.1}/src/specfact_cli/agents/sync_agent.py +0 -0
  36. {specfact_cli-0.13.0 → specfact_cli-0.13.1}/src/specfact_cli/analyzers/__init__.py +0 -0
  37. {specfact_cli-0.13.0 → specfact_cli-0.13.1}/src/specfact_cli/analyzers/ambiguity_scanner.py +0 -0
  38. {specfact_cli-0.13.0 → specfact_cli-0.13.1}/src/specfact_cli/analyzers/code_analyzer.py +0 -0
  39. {specfact_cli-0.13.0 → specfact_cli-0.13.1}/src/specfact_cli/analyzers/constitution_evidence_extractor.py +0 -0
  40. {specfact_cli-0.13.0 → specfact_cli-0.13.1}/src/specfact_cli/analyzers/contract_extractor.py +0 -0
  41. {specfact_cli-0.13.0 → specfact_cli-0.13.1}/src/specfact_cli/analyzers/control_flow_analyzer.py +0 -0
  42. {specfact_cli-0.13.0 → specfact_cli-0.13.1}/src/specfact_cli/analyzers/graph_analyzer.py +0 -0
  43. {specfact_cli-0.13.0 → specfact_cli-0.13.1}/src/specfact_cli/analyzers/relationship_mapper.py +0 -0
  44. {specfact_cli-0.13.0 → specfact_cli-0.13.1}/src/specfact_cli/analyzers/requirement_extractor.py +0 -0
  45. {specfact_cli-0.13.0 → specfact_cli-0.13.1}/src/specfact_cli/analyzers/test_pattern_extractor.py +0 -0
  46. {specfact_cli-0.13.0 → specfact_cli-0.13.1}/src/specfact_cli/cli.py +0 -0
  47. {specfact_cli-0.13.0 → specfact_cli-0.13.1}/src/specfact_cli/commands/__init__.py +0 -0
  48. {specfact_cli-0.13.0 → specfact_cli-0.13.1}/src/specfact_cli/commands/analyze.py +0 -0
  49. {specfact_cli-0.13.0 → specfact_cli-0.13.1}/src/specfact_cli/commands/bridge.py +0 -0
  50. {specfact_cli-0.13.0 → specfact_cli-0.13.1}/src/specfact_cli/commands/drift.py +0 -0
  51. {specfact_cli-0.13.0 → specfact_cli-0.13.1}/src/specfact_cli/commands/enforce.py +0 -0
  52. {specfact_cli-0.13.0 → specfact_cli-0.13.1}/src/specfact_cli/commands/implement.py +0 -0
  53. {specfact_cli-0.13.0 → specfact_cli-0.13.1}/src/specfact_cli/commands/import_cmd.py +0 -0
  54. {specfact_cli-0.13.0 → specfact_cli-0.13.1}/src/specfact_cli/commands/init.py +0 -0
  55. {specfact_cli-0.13.0 → specfact_cli-0.13.1}/src/specfact_cli/commands/migrate.py +0 -0
  56. {specfact_cli-0.13.0 → specfact_cli-0.13.1}/src/specfact_cli/commands/plan.py +0 -0
  57. {specfact_cli-0.13.0 → specfact_cli-0.13.1}/src/specfact_cli/commands/repro.py +0 -0
  58. {specfact_cli-0.13.0 → specfact_cli-0.13.1}/src/specfact_cli/commands/run.py +0 -0
  59. {specfact_cli-0.13.0 → specfact_cli-0.13.1}/src/specfact_cli/commands/sdd.py +0 -0
  60. {specfact_cli-0.13.0 → specfact_cli-0.13.1}/src/specfact_cli/commands/spec.py +0 -0
  61. {specfact_cli-0.13.0 → specfact_cli-0.13.1}/src/specfact_cli/commands/sync.py +0 -0
  62. {specfact_cli-0.13.0 → specfact_cli-0.13.1}/src/specfact_cli/common/__init__.py +0 -0
  63. {specfact_cli-0.13.0 → specfact_cli-0.13.1}/src/specfact_cli/common/logger_setup.py +0 -0
  64. {specfact_cli-0.13.0 → specfact_cli-0.13.1}/src/specfact_cli/common/logging_utils.py +0 -0
  65. {specfact_cli-0.13.0 → specfact_cli-0.13.1}/src/specfact_cli/common/text_utils.py +0 -0
  66. {specfact_cli-0.13.0 → specfact_cli-0.13.1}/src/specfact_cli/common/utils.py +0 -0
  67. {specfact_cli-0.13.0 → specfact_cli-0.13.1}/src/specfact_cli/comparators/__init__.py +0 -0
  68. {specfact_cli-0.13.0 → specfact_cli-0.13.1}/src/specfact_cli/comparators/plan_comparator.py +0 -0
  69. {specfact_cli-0.13.0 → specfact_cli-0.13.1}/src/specfact_cli/enrichers/constitution_enricher.py +0 -0
  70. {specfact_cli-0.13.0 → specfact_cli-0.13.1}/src/specfact_cli/enrichers/plan_enricher.py +0 -0
  71. {specfact_cli-0.13.0 → specfact_cli-0.13.1}/src/specfact_cli/generators/__init__.py +0 -0
  72. {specfact_cli-0.13.0 → specfact_cli-0.13.1}/src/specfact_cli/generators/contract_generator.py +0 -0
  73. {specfact_cli-0.13.0 → specfact_cli-0.13.1}/src/specfact_cli/generators/openapi_extractor.py +0 -0
  74. {specfact_cli-0.13.0 → specfact_cli-0.13.1}/src/specfact_cli/generators/plan_generator.py +0 -0
  75. {specfact_cli-0.13.0 → specfact_cli-0.13.1}/src/specfact_cli/generators/protocol_generator.py +0 -0
  76. {specfact_cli-0.13.0 → specfact_cli-0.13.1}/src/specfact_cli/generators/report_generator.py +0 -0
  77. {specfact_cli-0.13.0 → specfact_cli-0.13.1}/src/specfact_cli/generators/task_generator.py +0 -0
  78. {specfact_cli-0.13.0 → specfact_cli-0.13.1}/src/specfact_cli/generators/test_to_openapi.py +0 -0
  79. {specfact_cli-0.13.0 → specfact_cli-0.13.1}/src/specfact_cli/generators/workflow_generator.py +0 -0
  80. {specfact_cli-0.13.0 → specfact_cli-0.13.1}/src/specfact_cli/importers/__init__.py +0 -0
  81. {specfact_cli-0.13.0 → specfact_cli-0.13.1}/src/specfact_cli/importers/speckit_converter.py +0 -0
  82. {specfact_cli-0.13.0 → specfact_cli-0.13.1}/src/specfact_cli/importers/speckit_scanner.py +0 -0
  83. {specfact_cli-0.13.0 → specfact_cli-0.13.1}/src/specfact_cli/integrations/__init__.py +0 -0
  84. {specfact_cli-0.13.0 → specfact_cli-0.13.1}/src/specfact_cli/integrations/specmatic.py +0 -0
  85. {specfact_cli-0.13.0 → specfact_cli-0.13.1}/src/specfact_cli/migrations/__init__.py +0 -0
  86. {specfact_cli-0.13.0 → specfact_cli-0.13.1}/src/specfact_cli/migrations/plan_migrator.py +0 -0
  87. {specfact_cli-0.13.0 → specfact_cli-0.13.1}/src/specfact_cli/models/__init__.py +0 -0
  88. {specfact_cli-0.13.0 → specfact_cli-0.13.1}/src/specfact_cli/models/bridge.py +0 -0
  89. {specfact_cli-0.13.0 → specfact_cli-0.13.1}/src/specfact_cli/models/deviation.py +0 -0
  90. {specfact_cli-0.13.0 → specfact_cli-0.13.1}/src/specfact_cli/models/enforcement.py +0 -0
  91. {specfact_cli-0.13.0 → specfact_cli-0.13.1}/src/specfact_cli/models/plan.py +0 -0
  92. {specfact_cli-0.13.0 → specfact_cli-0.13.1}/src/specfact_cli/models/project.py +0 -0
  93. {specfact_cli-0.13.0 → specfact_cli-0.13.1}/src/specfact_cli/models/protocol.py +0 -0
  94. {specfact_cli-0.13.0 → specfact_cli-0.13.1}/src/specfact_cli/models/quality.py +0 -0
  95. {specfact_cli-0.13.0 → specfact_cli-0.13.1}/src/specfact_cli/models/sdd.py +0 -0
  96. {specfact_cli-0.13.0 → specfact_cli-0.13.1}/src/specfact_cli/models/source_tracking.py +0 -0
  97. {specfact_cli-0.13.0 → specfact_cli-0.13.1}/src/specfact_cli/models/task.py +0 -0
  98. {specfact_cli-0.13.0 → specfact_cli-0.13.1}/src/specfact_cli/modes/__init__.py +0 -0
  99. {specfact_cli-0.13.0 → specfact_cli-0.13.1}/src/specfact_cli/modes/detector.py +0 -0
  100. {specfact_cli-0.13.0 → specfact_cli-0.13.1}/src/specfact_cli/modes/router.py +0 -0
  101. {specfact_cli-0.13.0 → specfact_cli-0.13.1}/src/specfact_cli/resources/semgrep/async.yml +0 -0
  102. {specfact_cli-0.13.0 → specfact_cli-0.13.1}/src/specfact_cli/resources/semgrep/code-quality.yml +0 -0
  103. {specfact_cli-0.13.0 → specfact_cli-0.13.1}/src/specfact_cli/resources/semgrep/feature-detection.yml +0 -0
  104. {specfact_cli-0.13.0 → specfact_cli-0.13.1}/src/specfact_cli/runtime.py +0 -0
  105. {specfact_cli-0.13.0 → specfact_cli-0.13.1}/src/specfact_cli/sync/__init__.py +0 -0
  106. {specfact_cli-0.13.0 → specfact_cli-0.13.1}/src/specfact_cli/sync/bridge_probe.py +0 -0
  107. {specfact_cli-0.13.0 → specfact_cli-0.13.1}/src/specfact_cli/sync/bridge_sync.py +0 -0
  108. {specfact_cli-0.13.0 → specfact_cli-0.13.1}/src/specfact_cli/sync/bridge_watch.py +0 -0
  109. {specfact_cli-0.13.0 → specfact_cli-0.13.1}/src/specfact_cli/sync/change_detector.py +0 -0
  110. {specfact_cli-0.13.0 → specfact_cli-0.13.1}/src/specfact_cli/sync/code_to_spec.py +0 -0
  111. {specfact_cli-0.13.0 → specfact_cli-0.13.1}/src/specfact_cli/sync/drift_detector.py +0 -0
  112. {specfact_cli-0.13.0 → specfact_cli-0.13.1}/src/specfact_cli/sync/repository_sync.py +0 -0
  113. {specfact_cli-0.13.0 → specfact_cli-0.13.1}/src/specfact_cli/sync/spec_to_code.py +0 -0
  114. {specfact_cli-0.13.0 → specfact_cli-0.13.1}/src/specfact_cli/sync/spec_to_tests.py +0 -0
  115. {specfact_cli-0.13.0 → specfact_cli-0.13.1}/src/specfact_cli/sync/speckit_sync.py +0 -0
  116. {specfact_cli-0.13.0 → specfact_cli-0.13.1}/src/specfact_cli/sync/watcher.py +0 -0
  117. {specfact_cli-0.13.0 → specfact_cli-0.13.1}/src/specfact_cli/templates/__init__.py +0 -0
  118. {specfact_cli-0.13.0 → specfact_cli-0.13.1}/src/specfact_cli/templates/bridge_templates.py +0 -0
  119. {specfact_cli-0.13.0 → specfact_cli-0.13.1}/src/specfact_cli/utils/__init__.py +0 -0
  120. {specfact_cli-0.13.0 → specfact_cli-0.13.1}/src/specfact_cli/utils/acceptance_criteria.py +0 -0
  121. {specfact_cli-0.13.0 → specfact_cli-0.13.1}/src/specfact_cli/utils/bundle_loader.py +0 -0
  122. {specfact_cli-0.13.0 → specfact_cli-0.13.1}/src/specfact_cli/utils/console.py +0 -0
  123. {specfact_cli-0.13.0 → specfact_cli-0.13.1}/src/specfact_cli/utils/enrichment_context.py +0 -0
  124. {specfact_cli-0.13.0 → specfact_cli-0.13.1}/src/specfact_cli/utils/enrichment_parser.py +0 -0
  125. {specfact_cli-0.13.0 → specfact_cli-0.13.1}/src/specfact_cli/utils/feature_keys.py +0 -0
  126. {specfact_cli-0.13.0 → specfact_cli-0.13.1}/src/specfact_cli/utils/git.py +0 -0
  127. {specfact_cli-0.13.0 → specfact_cli-0.13.1}/src/specfact_cli/utils/github_annotations.py +0 -0
  128. {specfact_cli-0.13.0 → specfact_cli-0.13.1}/src/specfact_cli/utils/ide_setup.py +0 -0
  129. {specfact_cli-0.13.0 → specfact_cli-0.13.1}/src/specfact_cli/utils/incremental_check.py +0 -0
  130. {specfact_cli-0.13.0 → specfact_cli-0.13.1}/src/specfact_cli/utils/optional_deps.py +0 -0
  131. {specfact_cli-0.13.0 → specfact_cli-0.13.1}/src/specfact_cli/utils/progress.py +0 -0
  132. {specfact_cli-0.13.0 → specfact_cli-0.13.1}/src/specfact_cli/utils/prompts.py +0 -0
  133. {specfact_cli-0.13.0 → specfact_cli-0.13.1}/src/specfact_cli/utils/sdd_discovery.py +0 -0
  134. {specfact_cli-0.13.0 → specfact_cli-0.13.1}/src/specfact_cli/utils/source_scanner.py +0 -0
  135. {specfact_cli-0.13.0 → specfact_cli-0.13.1}/src/specfact_cli/utils/structure.py +0 -0
  136. {specfact_cli-0.13.0 → specfact_cli-0.13.1}/src/specfact_cli/utils/structured_io.py +0 -0
  137. {specfact_cli-0.13.0 → specfact_cli-0.13.1}/src/specfact_cli/utils/yaml_utils.py +0 -0
  138. {specfact_cli-0.13.0 → specfact_cli-0.13.1}/src/specfact_cli/validators/__init__.py +0 -0
  139. {specfact_cli-0.13.0 → specfact_cli-0.13.1}/src/specfact_cli/validators/contract_validator.py +0 -0
  140. {specfact_cli-0.13.0 → specfact_cli-0.13.1}/src/specfact_cli/validators/fsm.py +0 -0
  141. {specfact_cli-0.13.0 → specfact_cli-0.13.1}/src/specfact_cli/validators/repro_checker.py +0 -0
  142. {specfact_cli-0.13.0 → specfact_cli-0.13.1}/src/specfact_cli/validators/schema.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: specfact-cli
3
- Version: 0.13.0
3
+ Version: 0.13.1
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
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "specfact-cli"
7
- version = "0.13.0"
7
+ version = "0.13.1"
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"
@@ -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.12.1"
6
+ __version__ = "0.13.1"
@@ -9,6 +9,6 @@ This package provides command-line tools for:
9
9
  - Validating reproducibility
10
10
  """
11
11
 
12
- __version__ = "0.13.0"
12
+ __version__ = "0.13.1"
13
13
 
14
14
  __all__ = ["__version__"]
@@ -19,6 +19,7 @@ from specfact_cli.models.sdd import SDDManifest
19
19
  from specfact_cli.models.task import TaskList, TaskPhase
20
20
  from specfact_cli.telemetry import telemetry
21
21
  from specfact_cli.utils import print_error, print_info, print_success, print_warning
22
+ from specfact_cli.utils.optional_deps import check_cli_tool_available
22
23
  from specfact_cli.utils.structured_io import load_structured_file
23
24
 
24
25
 
@@ -551,6 +552,53 @@ def generate_contracts_prompt(
551
552
  prompt_parts = [
552
553
  "# Contract Enhancement Request",
553
554
  "",
555
+ "## ⚠️ MANDATORY PRE-CHECK: SpecFact CLI Verification",
556
+ "",
557
+ "**🚨 CRITICAL STOP CONDITION - READ THIS FIRST 🚨**",
558
+ "",
559
+ "**YOU MUST VERIFY SpecFact CLI BEFORE DOING ANYTHING ELSE.**",
560
+ "",
561
+ "**If SpecFact CLI is missing, outdated, or commands don't work:**",
562
+ "",
563
+ "1. **STOP IMMEDIATELY** - Do NOT read files, do NOT generate code, do NOT proceed",
564
+ "2. **DO NOT attempt to manually add contracts** - This will NOT work correctly",
565
+ "3. **DO NOT proceed with any enhancement** - The workflow REQUIRES SpecFact CLI",
566
+ "4. **Inform the user** with this exact message:",
567
+ " ```",
568
+ " ❌ SpecFact CLI is required but not available or outdated.",
569
+ " Please install/upgrade: pip install -U specfact-cli",
570
+ " Then verify: specfact --version",
571
+ " This task cannot proceed without SpecFact CLI.",
572
+ " ```",
573
+ "5. **END THE CONVERSATION** - Do not continue until SpecFact CLI is working",
574
+ "",
575
+ "**Verification Steps (MUST complete all before proceeding):**",
576
+ "",
577
+ "1. Check if `specfact` command is available:",
578
+ " ```bash",
579
+ " specfact --version",
580
+ " ```",
581
+ " - **If this fails**: STOP and inform user (see message above)",
582
+ "",
583
+ "2. Verify the required command exists:",
584
+ " ```bash",
585
+ " specfact generate contracts-apply --help",
586
+ " ```",
587
+ " - **If this fails**: STOP and inform user (see message above)",
588
+ "",
589
+ "3. Check the latest available version from PyPI:",
590
+ " ```bash",
591
+ " pip index versions specfact-cli",
592
+ " ```",
593
+ " - Compare installed version (from step 1) with latest available",
594
+ " - **If versions don't match**: STOP and inform user to upgrade",
595
+ "",
596
+ "**ONLY IF ALL THREE STEPS PASS** - You may proceed to the sections below.",
597
+ "",
598
+ "**If ANY step fails, you MUST stop and inform the user. Do NOT proceed.**",
599
+ "",
600
+ "---",
601
+ "",
554
602
  "## Target File",
555
603
  "",
556
604
  f"**File Path:** `{file_path_relative}`",
@@ -580,35 +628,7 @@ def generate_contracts_prompt(
580
628
  "",
581
629
  "**IMPORTANT**: Do NOT modify the original file directly. Follow this iterative validation workflow:",
582
630
  "",
583
- "### Step 0: Verify SpecFact CLI",
584
- "**CRITICAL**: Before proceeding, verify that SpecFact CLI is installed and working:",
585
- "",
586
- "1. Check if `specfact` command is available:",
587
- " ```bash",
588
- " specfact --version",
589
- " ```",
590
- "",
591
- "2. Verify the required command exists:",
592
- " ```bash",
593
- " specfact generate contracts-apply --help",
594
- " ```",
595
- "",
596
- "3. Check the latest available version from PyPI for comparison:",
597
- " ```bash",
598
- " pip index versions specfact-cli",
599
- " ```",
600
- " - Compare the installed version (from step 1) with the latest available version",
601
- " - If versions don't match, an upgrade is needed",
602
- "",
603
- "**If SpecFact CLI is not available, outdated, or commands are missing:**",
604
- "- **ABORT immediately**",
605
- "- **DO NOT proceed** with code enhancement",
606
- "- **Inform the user clearly** that they need to:",
607
- " - Install/Upgrade SpecFact CLI: `pip install -U specfact-cli` or `uvx specfact-cli@latest`",
608
- " - Verify installation: `specfact --version`",
609
- "- **Only continue** after confirming SpecFact CLI is working correctly",
610
- "",
611
- "**This validation is mandatory** - the workflow depends on SpecFact CLI for validation and application.",
631
+ "**REMINDER**: If you haven't completed the mandatory SpecFact CLI verification at the top of this prompt, STOP NOW and do that first. Do NOT proceed with any code enhancement until SpecFact CLI is verified.",
612
632
  "",
613
633
  "### Step 1: Read the File",
614
634
  f"1. Read the file content from: `{file_path_relative}`",
@@ -616,13 +636,41 @@ def generate_contracts_prompt(
616
636
  "3. Note the existing code style and patterns",
617
637
  "",
618
638
  "### Step 2: Generate Enhanced Code",
619
- "1. Add the requested contracts to the code",
639
+ "**IMPORTANT**: Only proceed to this step if SpecFact CLI verification passed.",
640
+ "",
641
+ "**CRITICAL REQUIREMENT**: You MUST add contracts to ALL eligible functions and methods in the file. Do NOT ask the user whether to add contracts - add them to all compatible functions automatically.",
642
+ "",
643
+ "1. **Add the requested contracts to ALL eligible functions/methods** - This is mandatory, not optional",
620
644
  "2. Maintain existing functionality and code style",
621
645
  "3. Ensure all contracts are properly imported at the top of the file",
622
- "4. Add appropriate preconditions (`@require`) and postconditions (`@ensure`) where they make sense",
623
- "5. For beartype: Add decorator to all public functions and methods",
624
- "6. For icontract: Focus on critical functions with clear pre/post conditions",
625
- "7. For crosshair: Add property test functions that validate contract behavior",
646
+ "4. **Code Quality**: Follow the project's existing code style and formatting conventions",
647
+ " - If the project has formatting/linting rules (e.g., `.editorconfig`, `pyproject.toml` with formatting config, `ruff.toml`, `.pylintrc`, etc.), ensure the enhanced code adheres to them",
648
+ " - Match the existing code style: indentation, line length, import organization, naming conventions",
649
+ " - Avoid common code quality issues: use `key in dict` instead of `key in dict.keys()`, proper type hints, etc.",
650
+ " - **Note**: SpecFact CLI will automatically run available linting/formatting tools (ruff, pylint, basedpyright, mypy) during validation if they are installed",
651
+ "",
652
+ "**Contract-Specific Requirements:**",
653
+ "",
654
+ "- **beartype**: Add `@beartype` decorator to ALL functions and methods (public and private, unless they have incompatible signatures)",
655
+ " - Apply to: regular functions, class methods, static methods, async functions",
656
+ " - Skip only if: function has `*args, **kwargs` without type hints (incompatible with beartype)",
657
+ "",
658
+ "- **icontract**: Add `@require` decorators for preconditions and `@ensure` decorators for postconditions to ALL functions where conditions can be expressed",
659
+ " - Apply to: all functions with clear input/output contracts",
660
+ " - Add preconditions for: parameter validation, state checks, input constraints",
661
+ " - Add postconditions for: return value validation, state changes, output guarantees",
662
+ " - Skip only if: function has no meaningful pre/post conditions to express",
663
+ "",
664
+ "- **crosshair**: Add property-based test functions using CrossHair patterns for ALL testable functions",
665
+ " - Create test functions that validate contract behavior",
666
+ " - Focus on functions with clear input/output relationships",
667
+ "",
668
+ "**DO NOT:**",
669
+ "- Ask the user whether to add contracts (add them automatically to all eligible functions)",
670
+ "- Skip functions because you're unsure (add contracts unless technically incompatible)",
671
+ "- Manually apply contracts to the original file (use SpecFact CLI validation workflow)",
672
+ "",
673
+ "**You MUST use SpecFact CLI validation workflow (Step 4) to apply changes.**",
626
674
  "",
627
675
  "### Step 3: Write Enhanced Code to Temporary File",
628
676
  f"1. Write the complete enhanced code to: `enhanced_{file_path.stem}.py`",
@@ -631,11 +679,16 @@ def generate_contracts_prompt(
631
679
  "2. Ensure the file is properly formatted and complete",
632
680
  "",
633
681
  "### Step 4: Validate with CLI",
682
+ "**CRITICAL**: If `specfact generate contracts-apply` command is not available or fails, DO NOT proceed. STOP and inform the user that SpecFact CLI must be installed/upgraded first.",
683
+ "",
634
684
  "1. Run the validation command:",
635
685
  " ```bash",
636
686
  f" specfact generate contracts-apply enhanced_{file_path.stem}.py --original {file_path_relative}",
637
687
  " ```",
638
688
  "",
689
+ " - **If command not found**: STOP immediately and inform user (see mandatory pre-check message)",
690
+ " - **If command fails with error**: Review error, but if it's a missing command error, STOP and inform user",
691
+ "",
639
692
  "### Step 5: Handle Validation Results",
640
693
  "",
641
694
  "**If validation succeeds:**",
@@ -643,13 +696,15 @@ def generate_contracts_prompt(
643
696
  "- You're done! The file has been enhanced with contracts",
644
697
  "",
645
698
  "**If validation fails:**",
646
- "- The CLI will show specific error messages explaining what's wrong",
647
- "- Review the errors carefully",
699
+ "- **If error is 'command not found' or 'command does not exist'**: STOP immediately and inform user (see mandatory pre-check message)",
700
+ "- **If error is validation failure** (syntax, AST, tests, etc.): Review the errors carefully",
648
701
  "- Fix the issues in the enhanced code",
649
702
  "- Write the corrected code to the same temporary file (`enhanced_{file_path.stem}.py`)",
650
703
  "- Run the validation command again",
651
704
  "- Repeat until validation passes (maximum 3 attempts)",
652
705
  "",
706
+ "**CRITICAL**: If `specfact generate contracts-apply` command is not available or fails with 'command not found', DO NOT manually apply changes to the original file. STOP and inform the user that SpecFact CLI must be installed/upgraded first.",
707
+ "",
653
708
  "### Common Validation Errors and Fixes",
654
709
  "",
655
710
  "**Syntax Errors:**",
@@ -676,6 +731,8 @@ def generate_contracts_prompt(
676
731
  "**Maximum Attempts: 3**",
677
732
  "If validation fails after 3 attempts, review the errors manually and apply fixes.",
678
733
  "",
734
+ "**FINAL REMINDER**: This entire workflow REQUIRES SpecFact CLI. If SpecFact CLI is not available, you MUST stop and inform the user. Do NOT attempt to manually add contracts or modify files directly.",
735
+ "",
679
736
  "## Summary",
680
737
  "",
681
738
  f"- **Target File:** `{file_path_relative}`",
@@ -683,7 +740,7 @@ def generate_contracts_prompt(
683
740
  f"- **Validation Command:** `specfact generate contracts-apply enhanced_{file_path.stem}.py --original {file_path_relative}`",
684
741
  "- **Contracts:** " + ", ".join(contracts_to_apply),
685
742
  "",
686
- "Please start by reading the file and then proceed with the enhancement workflow.",
743
+ "**BEFORE STARTING**: Complete the mandatory SpecFact CLI verification at the top of this prompt. Do NOT proceed with file reading or code generation until SpecFact CLI is verified.",
687
744
  "",
688
745
  ]
689
746
  )
@@ -994,95 +1051,219 @@ def apply_enhanced_contracts(
994
1051
 
995
1052
  print_success("Contract imports verified")
996
1053
 
997
- # Step 5: Run tests
998
- console.print("\n[bold cyan]Step 5/6: Running tests...[/bold cyan]")
1054
+ # Step 5: Run linting/formatting checks (if tools available)
1055
+ console.print("\n[bold cyan]Step 5/7: Running code quality checks (if tools available)...[/bold cyan]")
1056
+ lint_issues: list[str] = []
1057
+ tools_checked = 0
1058
+ tools_passed = 0
1059
+
1060
+ # List of common linting/formatting tools to check
1061
+ linting_tools = [
1062
+ ("ruff", ["ruff", "check", str(enhanced_file)], "Ruff linting"),
1063
+ ("pylint", ["pylint", str(enhanced_file), "--disable=all", "--enable=E,F"], "Pylint basic checks"),
1064
+ ("basedpyright", ["basedpyright", str(enhanced_file)], "BasedPyright type checking"),
1065
+ ("mypy", ["mypy", str(enhanced_file)], "MyPy type checking"),
1066
+ ]
1067
+
1068
+ for tool_name, command, description in linting_tools:
1069
+ is_available, _error_msg = check_cli_tool_available(tool_name, version_flag="--version", timeout=3)
1070
+ if not is_available:
1071
+ console.print(f"[dim]Skipping {description}: {tool_name} not available[/dim]")
1072
+ continue
1073
+
1074
+ tools_checked += 1
1075
+ console.print(f"[dim]Running {description}...[/dim]")
1076
+
1077
+ try:
1078
+ result = subprocess.run(
1079
+ command,
1080
+ capture_output=True,
1081
+ text=True,
1082
+ timeout=30, # 30 seconds per tool
1083
+ cwd=str(repo_path),
1084
+ )
1085
+
1086
+ if result.returncode == 0:
1087
+ tools_passed += 1
1088
+ console.print(f"[green]✓[/green] {description} passed")
1089
+ else:
1090
+ # Collect issues but don't fail immediately (warnings only)
1091
+ output = result.stdout + result.stderr
1092
+ # Limit output length for readability
1093
+ output_lines = output.split("\n")
1094
+ if len(output_lines) > 20:
1095
+ output = "\n".join(output_lines[:20]) + f"\n... ({len(output_lines) - 20} more lines)"
1096
+ lint_issues.append(f"{description} found issues:\n{output}")
1097
+ console.print(f"[yellow]⚠[/yellow] {description} found issues (non-blocking)")
1098
+
1099
+ except subprocess.TimeoutExpired:
1100
+ console.print(f"[yellow]⚠[/yellow] {description} timed out (non-blocking)")
1101
+ lint_issues.append(f"{description} timed out after 30 seconds")
1102
+ except Exception as e:
1103
+ console.print(f"[yellow]⚠[/yellow] {description} error: {e} (non-blocking)")
1104
+ lint_issues.append(f"{description} error: {e}")
1105
+
1106
+ if tools_checked == 0:
1107
+ console.print("[dim]No linting/formatting tools available. Skipping code quality checks.[/dim]")
1108
+ elif tools_passed == tools_checked:
1109
+ print_success(f"All code quality checks passed ({tools_passed}/{tools_checked} tools)")
1110
+ else:
1111
+ console.print(f"[yellow]Code quality checks: {tools_passed}/{tools_checked} tools passed[/yellow]")
1112
+ if lint_issues:
1113
+ console.print("\n[yellow]Code Quality Issues (non-blocking):[/yellow]")
1114
+ for issue in lint_issues[:3]: # Show first 3 issues
1115
+ console.print(Panel(issue[:500], title="Issue", border_style="yellow"))
1116
+ if len(lint_issues) > 3:
1117
+ console.print(f"[dim]... and {len(lint_issues) - 3} more issue(s)[/dim]")
1118
+ console.print("\n[yellow]Note:[/yellow] These are warnings. Fix them for better code quality.")
1119
+
1120
+ # Step 6: Run tests (scoped to relevant file only for performance)
1121
+ # NOTE: Tests always run for validation, even in --dry-run mode, to ensure code quality
1122
+ console.print("\n[bold cyan]Step 6/7: Running tests (scoped to relevant file)...[/bold cyan]")
999
1123
  test_failed = False
1000
1124
  test_output = ""
1001
1125
 
1002
- # Try specfact repro first (public command for validation)
1126
+ # For single-file validation, we scope tests to the specific file only (not full repo)
1127
+ # This is much faster than running specfact repro on the entire repository
1003
1128
  try:
1004
- console.print("[dim]Running specfact repro for validation...[/dim]")
1005
- result = subprocess.run(
1006
- ["specfact", "repro", "--repo", str(repo_path)],
1007
- capture_output=True,
1008
- text=True,
1009
- timeout=120, # 2 minutes for tests
1010
- )
1129
+ # Find the original file path to determine test file location
1130
+ original_file_rel = original_file.relative_to(repo_path) if original_file else None
1131
+ enhanced_file_rel = enhanced_file.relative_to(repo_path)
1132
+
1133
+ # Determine the source file we're testing (original or enhanced)
1134
+ source_file_rel = original_file_rel if original_file_rel else enhanced_file_rel
1135
+
1136
+ # Convert source file path to potential test file paths
1137
+ # Pattern: src/specfact_cli/telemetry.py -> tests/unit/specfact_cli/test_telemetry.py
1138
+ # or: src/common/logger.py -> tests/unit/common/test_logger.py
1139
+ test_paths: list[Path] = []
1140
+
1141
+ # Remove 'src/' prefix if present
1142
+ test_rel_path = str(source_file_rel)
1143
+ if test_rel_path.startswith("src/"):
1144
+ test_rel_path = test_rel_path[4:] # Remove 'src/'
1145
+ elif test_rel_path.startswith("tools/"):
1146
+ test_rel_path = test_rel_path[6:] # Remove 'tools/'
1147
+
1148
+ # Get directory and filename
1149
+ test_file_dir = Path(test_rel_path).parent
1150
+ test_file_name = Path(test_rel_path).stem # e.g., "telemetry" from "telemetry.py"
1151
+
1152
+ # Try common test file patterns
1153
+ test_file_patterns = [
1154
+ f"test_{test_file_name}.py",
1155
+ f"{test_file_name}_test.py",
1156
+ ]
1157
+
1158
+ # Try common test directory structures
1159
+ test_dirs = [
1160
+ repo_path / "tests" / "unit" / test_file_dir,
1161
+ repo_path / "tests" / test_file_dir,
1162
+ repo_path / "tests" / "unit",
1163
+ repo_path / "tests",
1164
+ ]
1165
+
1166
+ # Build list of possible test file paths
1167
+ for test_dir in test_dirs:
1168
+ if test_dir.exists():
1169
+ for pattern in test_file_patterns:
1170
+ test_path = test_dir / pattern
1171
+ if test_path.exists():
1172
+ test_paths.append(test_path)
1173
+
1174
+ # Also try E2E tests if unit tests not found
1175
+ if not test_paths:
1176
+ e2e_test_dirs = [
1177
+ repo_path / "tests" / "e2e" / test_file_dir,
1178
+ repo_path / "tests" / "e2e",
1179
+ ]
1180
+ for test_dir in e2e_test_dirs:
1181
+ if test_dir.exists():
1182
+ for pattern in test_file_patterns:
1183
+ test_path = test_dir / pattern
1184
+ if test_path.exists():
1185
+ test_paths.append(test_path)
1186
+
1187
+ # If we found specific test files, run them
1188
+ if test_paths:
1189
+ # Use the first matching test file (most specific)
1190
+ test_path = test_paths[0]
1191
+ console.print(f"[dim]Found test file: {test_path.relative_to(repo_path)}[/dim]")
1192
+ console.print("[dim]Running pytest on specific test file (fast, scoped validation)...[/dim]")
1193
+
1194
+ result = subprocess.run(
1195
+ ["pytest", str(test_path), "-v", "--tb=short"],
1196
+ capture_output=True,
1197
+ text=True,
1198
+ timeout=60, # 1 minute should be enough for a single test file
1199
+ cwd=str(repo_path),
1200
+ )
1201
+ else:
1202
+ # No specific test file found, try to import and test the enhanced file directly
1203
+ # This validates that the file can be imported and basic syntax works
1204
+ console.print(f"[dim]No specific test file found for {source_file_rel}[/dim]")
1205
+ console.print("[dim]Running syntax and import validation on enhanced file...[/dim]")
1206
+
1207
+ # Try to import the module to verify it works
1208
+ import importlib.util
1209
+ import sys
1210
+ from dataclasses import dataclass
1211
+
1212
+ @dataclass
1213
+ class ImportResult:
1214
+ """Result object for import validation."""
1215
+
1216
+ returncode: int
1217
+ stdout: str
1218
+ stderr: str
1219
+
1220
+ try:
1221
+ # Add the enhanced file's directory to path temporarily
1222
+ enhanced_file_dir = str(enhanced_file.parent)
1223
+ if enhanced_file_dir not in sys.path:
1224
+ sys.path.insert(0, enhanced_file_dir)
1225
+
1226
+ # Try to load the module
1227
+ spec = importlib.util.spec_from_file_location(enhanced_file.stem, enhanced_file)
1228
+ if spec and spec.loader:
1229
+ module = importlib.util.module_from_spec(spec)
1230
+ spec.loader.exec_module(module)
1231
+ print_success("Enhanced file imports successfully")
1232
+ result = ImportResult(returncode=0, stdout="", stderr="")
1233
+ else:
1234
+ raise ImportError("Could not create module spec")
1235
+ except Exception as import_error:
1236
+ test_failed = True
1237
+ test_output = f"Import validation failed: {import_error}"
1238
+ print_error(test_output)
1239
+ console.print(
1240
+ "\n[yellow]Note:[/yellow] No specific test file found. Enhanced file should be importable."
1241
+ )
1242
+ result = ImportResult(returncode=1, stdout="", stderr=test_output)
1243
+
1011
1244
  if result.returncode != 0:
1012
1245
  test_failed = True
1013
1246
  test_output = result.stdout + result.stderr
1014
- print_error("Validation failed:")
1247
+ print_error("Test execution failed:")
1015
1248
  # Limit output for readability
1016
1249
  output_lines = test_output.split("\n")
1017
1250
  console.print("\n".join(output_lines[:50])) # First 50 lines
1018
1251
  if len(output_lines) > 50:
1019
1252
  console.print(f"\n... ({len(output_lines) - 50} more lines)")
1020
1253
  else:
1021
- print_success("All validations passed")
1254
+ if test_paths:
1255
+ print_success(f"All tests passed ({test_paths[0].relative_to(repo_path)})")
1256
+ else:
1257
+ print_success("Import validation passed")
1258
+ except FileNotFoundError:
1259
+ console.print("[yellow]Warning:[/yellow] 'pytest' not found. Skipping test execution.")
1260
+ console.print("[yellow]Please run tests manually before applying changes.[/yellow]")
1261
+ test_failed = False # Don't fail if tools not available
1022
1262
  except subprocess.TimeoutExpired:
1023
1263
  test_failed = True
1024
- test_output = "Validation timed out after 120 seconds"
1264
+ test_output = "Test execution timed out after 60 seconds"
1025
1265
  print_error(test_output)
1026
- except FileNotFoundError:
1027
- # specfact not available, try pytest directly
1028
- console.print("[dim]specfact not available, trying pytest directly...[/dim]")
1029
- try:
1030
- # Try to find and run tests for the enhanced file
1031
- # Look for test files that might test this module
1032
- enhanced_file_rel = enhanced_file.relative_to(repo_path)
1033
- test_file_pattern = f"test_{enhanced_file.stem}.py"
1034
-
1035
- # Try common test locations
1036
- possible_test_paths = [
1037
- repo_path / "tests" / "unit" / enhanced_file_rel.parent / test_file_pattern,
1038
- repo_path / "tests" / test_file_pattern,
1039
- repo_path / "tests" / "unit" / test_file_pattern,
1040
- ]
1041
-
1042
- test_path = None
1043
- for path in possible_test_paths:
1044
- if path.exists():
1045
- test_path = path
1046
- break
1047
-
1048
- if test_path:
1049
- result = subprocess.run(
1050
- ["pytest", str(test_path), "-v"],
1051
- capture_output=True,
1052
- text=True,
1053
- timeout=120,
1054
- cwd=str(repo_path),
1055
- )
1056
- else:
1057
- # No specific test file found, run pytest on the enhanced file itself
1058
- # (pytest can test Python files directly)
1059
- result = subprocess.run(
1060
- ["pytest", str(enhanced_file), "-v"],
1061
- capture_output=True,
1062
- text=True,
1063
- timeout=120,
1064
- cwd=str(repo_path),
1065
- )
1066
-
1067
- if result.returncode != 0:
1068
- test_failed = True
1069
- test_output = result.stdout + result.stderr
1070
- print_error("Test execution failed:")
1071
- # Limit output for readability
1072
- output_lines = test_output.split("\n")
1073
- console.print("\n".join(output_lines[:50])) # First 50 lines
1074
- if len(output_lines) > 50:
1075
- console.print(f"\n... ({len(output_lines) - 50} more lines)")
1076
- else:
1077
- print_success("All tests passed")
1078
- except FileNotFoundError:
1079
- console.print("[yellow]Warning:[/yellow] Neither 'specfact' nor 'pytest' found. Skipping test execution.")
1080
- console.print("[yellow]Please run tests manually before applying changes.[/yellow]")
1081
- test_failed = False # Don't fail if tools not available
1082
- except subprocess.TimeoutExpired:
1083
- test_failed = True
1084
- test_output = "Test execution timed out after 120 seconds"
1085
- print_error(test_output)
1266
+ console.print("\n[yellow]Note:[/yellow] Test execution took too long. Consider running tests manually.")
1086
1267
  except Exception as e:
1087
1268
  test_failed = True
1088
1269
  test_output = f"Test execution error: {e}"
@@ -1100,8 +1281,8 @@ def apply_enhanced_contracts(
1100
1281
  console.print(" - Contract conditions may be invalid")
1101
1282
  raise typer.Exit(1) from None
1102
1283
 
1103
- # Step 6: Show diff
1104
- console.print("\n[bold cyan]Step 6/6: Previewing changes...[/bold cyan]")
1284
+ # Step 7: Show diff
1285
+ console.print("\n[bold cyan]Step 7/7: Previewing changes...[/bold cyan]")
1105
1286
  diff = list(
1106
1287
  difflib.unified_diff(
1107
1288
  original_content.splitlines(keepends=True),