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.
- {specfact_cli-0.13.0 → specfact_cli-0.13.1}/PKG-INFO +1 -1
- {specfact_cli-0.13.0 → specfact_cli-0.13.1}/pyproject.toml +1 -1
- {specfact_cli-0.13.0 → specfact_cli-0.13.1}/src/__init__.py +1 -1
- {specfact_cli-0.13.0 → specfact_cli-0.13.1}/src/specfact_cli/__init__.py +1 -1
- {specfact_cli-0.13.0 → specfact_cli-0.13.1}/src/specfact_cli/commands/generate.py +293 -112
- {specfact_cli-0.13.0 → specfact_cli-0.13.1}/src/specfact_cli/telemetry.py +113 -0
- {specfact_cli-0.13.0 → specfact_cli-0.13.1}/.gitignore +0 -0
- {specfact_cli-0.13.0 → specfact_cli-0.13.1}/LICENSE.md +0 -0
- {specfact_cli-0.13.0 → specfact_cli-0.13.1}/README.md +0 -0
- {specfact_cli-0.13.0 → specfact_cli-0.13.1}/resources/mappings/node-async.yaml +0 -0
- {specfact_cli-0.13.0 → specfact_cli-0.13.1}/resources/mappings/python-async.yaml +0 -0
- {specfact_cli-0.13.0 → specfact_cli-0.13.1}/resources/mappings/speckit-default.yaml +0 -0
- {specfact_cli-0.13.0 → specfact_cli-0.13.1}/resources/prompts/shared/cli-enforcement.md +0 -0
- {specfact_cli-0.13.0 → specfact_cli-0.13.1}/resources/prompts/specfact.01-import.md +0 -0
- {specfact_cli-0.13.0 → specfact_cli-0.13.1}/resources/prompts/specfact.02-plan.md +0 -0
- {specfact_cli-0.13.0 → specfact_cli-0.13.1}/resources/prompts/specfact.03-review.md +0 -0
- {specfact_cli-0.13.0 → specfact_cli-0.13.1}/resources/prompts/specfact.04-sdd.md +0 -0
- {specfact_cli-0.13.0 → specfact_cli-0.13.1}/resources/prompts/specfact.05-enforce.md +0 -0
- {specfact_cli-0.13.0 → specfact_cli-0.13.1}/resources/prompts/specfact.06-sync.md +0 -0
- {specfact_cli-0.13.0 → specfact_cli-0.13.1}/resources/prompts/specfact.compare.md +0 -0
- {specfact_cli-0.13.0 → specfact_cli-0.13.1}/resources/prompts/specfact.validate.md +0 -0
- {specfact_cli-0.13.0 → specfact_cli-0.13.1}/resources/schemas/deviation.schema.json +0 -0
- {specfact_cli-0.13.0 → specfact_cli-0.13.1}/resources/schemas/plan.schema.json +0 -0
- {specfact_cli-0.13.0 → specfact_cli-0.13.1}/resources/schemas/protocol.schema.json +0 -0
- {specfact_cli-0.13.0 → specfact_cli-0.13.1}/resources/templates/github-action.yml.j2 +0 -0
- {specfact_cli-0.13.0 → specfact_cli-0.13.1}/resources/templates/plan.bundle.yaml.j2 +0 -0
- {specfact_cli-0.13.0 → specfact_cli-0.13.1}/resources/templates/pr-template.md.j2 +0 -0
- {specfact_cli-0.13.0 → specfact_cli-0.13.1}/resources/templates/protocol.yaml.j2 +0 -0
- {specfact_cli-0.13.0 → specfact_cli-0.13.1}/resources/templates/telemetry.yaml.example +0 -0
- {specfact_cli-0.13.0 → specfact_cli-0.13.1}/src/specfact_cli/agents/__init__.py +0 -0
- {specfact_cli-0.13.0 → specfact_cli-0.13.1}/src/specfact_cli/agents/analyze_agent.py +0 -0
- {specfact_cli-0.13.0 → specfact_cli-0.13.1}/src/specfact_cli/agents/base.py +0 -0
- {specfact_cli-0.13.0 → specfact_cli-0.13.1}/src/specfact_cli/agents/plan_agent.py +0 -0
- {specfact_cli-0.13.0 → specfact_cli-0.13.1}/src/specfact_cli/agents/registry.py +0 -0
- {specfact_cli-0.13.0 → specfact_cli-0.13.1}/src/specfact_cli/agents/sync_agent.py +0 -0
- {specfact_cli-0.13.0 → specfact_cli-0.13.1}/src/specfact_cli/analyzers/__init__.py +0 -0
- {specfact_cli-0.13.0 → specfact_cli-0.13.1}/src/specfact_cli/analyzers/ambiguity_scanner.py +0 -0
- {specfact_cli-0.13.0 → specfact_cli-0.13.1}/src/specfact_cli/analyzers/code_analyzer.py +0 -0
- {specfact_cli-0.13.0 → specfact_cli-0.13.1}/src/specfact_cli/analyzers/constitution_evidence_extractor.py +0 -0
- {specfact_cli-0.13.0 → specfact_cli-0.13.1}/src/specfact_cli/analyzers/contract_extractor.py +0 -0
- {specfact_cli-0.13.0 → specfact_cli-0.13.1}/src/specfact_cli/analyzers/control_flow_analyzer.py +0 -0
- {specfact_cli-0.13.0 → specfact_cli-0.13.1}/src/specfact_cli/analyzers/graph_analyzer.py +0 -0
- {specfact_cli-0.13.0 → specfact_cli-0.13.1}/src/specfact_cli/analyzers/relationship_mapper.py +0 -0
- {specfact_cli-0.13.0 → specfact_cli-0.13.1}/src/specfact_cli/analyzers/requirement_extractor.py +0 -0
- {specfact_cli-0.13.0 → specfact_cli-0.13.1}/src/specfact_cli/analyzers/test_pattern_extractor.py +0 -0
- {specfact_cli-0.13.0 → specfact_cli-0.13.1}/src/specfact_cli/cli.py +0 -0
- {specfact_cli-0.13.0 → specfact_cli-0.13.1}/src/specfact_cli/commands/__init__.py +0 -0
- {specfact_cli-0.13.0 → specfact_cli-0.13.1}/src/specfact_cli/commands/analyze.py +0 -0
- {specfact_cli-0.13.0 → specfact_cli-0.13.1}/src/specfact_cli/commands/bridge.py +0 -0
- {specfact_cli-0.13.0 → specfact_cli-0.13.1}/src/specfact_cli/commands/drift.py +0 -0
- {specfact_cli-0.13.0 → specfact_cli-0.13.1}/src/specfact_cli/commands/enforce.py +0 -0
- {specfact_cli-0.13.0 → specfact_cli-0.13.1}/src/specfact_cli/commands/implement.py +0 -0
- {specfact_cli-0.13.0 → specfact_cli-0.13.1}/src/specfact_cli/commands/import_cmd.py +0 -0
- {specfact_cli-0.13.0 → specfact_cli-0.13.1}/src/specfact_cli/commands/init.py +0 -0
- {specfact_cli-0.13.0 → specfact_cli-0.13.1}/src/specfact_cli/commands/migrate.py +0 -0
- {specfact_cli-0.13.0 → specfact_cli-0.13.1}/src/specfact_cli/commands/plan.py +0 -0
- {specfact_cli-0.13.0 → specfact_cli-0.13.1}/src/specfact_cli/commands/repro.py +0 -0
- {specfact_cli-0.13.0 → specfact_cli-0.13.1}/src/specfact_cli/commands/run.py +0 -0
- {specfact_cli-0.13.0 → specfact_cli-0.13.1}/src/specfact_cli/commands/sdd.py +0 -0
- {specfact_cli-0.13.0 → specfact_cli-0.13.1}/src/specfact_cli/commands/spec.py +0 -0
- {specfact_cli-0.13.0 → specfact_cli-0.13.1}/src/specfact_cli/commands/sync.py +0 -0
- {specfact_cli-0.13.0 → specfact_cli-0.13.1}/src/specfact_cli/common/__init__.py +0 -0
- {specfact_cli-0.13.0 → specfact_cli-0.13.1}/src/specfact_cli/common/logger_setup.py +0 -0
- {specfact_cli-0.13.0 → specfact_cli-0.13.1}/src/specfact_cli/common/logging_utils.py +0 -0
- {specfact_cli-0.13.0 → specfact_cli-0.13.1}/src/specfact_cli/common/text_utils.py +0 -0
- {specfact_cli-0.13.0 → specfact_cli-0.13.1}/src/specfact_cli/common/utils.py +0 -0
- {specfact_cli-0.13.0 → specfact_cli-0.13.1}/src/specfact_cli/comparators/__init__.py +0 -0
- {specfact_cli-0.13.0 → specfact_cli-0.13.1}/src/specfact_cli/comparators/plan_comparator.py +0 -0
- {specfact_cli-0.13.0 → specfact_cli-0.13.1}/src/specfact_cli/enrichers/constitution_enricher.py +0 -0
- {specfact_cli-0.13.0 → specfact_cli-0.13.1}/src/specfact_cli/enrichers/plan_enricher.py +0 -0
- {specfact_cli-0.13.0 → specfact_cli-0.13.1}/src/specfact_cli/generators/__init__.py +0 -0
- {specfact_cli-0.13.0 → specfact_cli-0.13.1}/src/specfact_cli/generators/contract_generator.py +0 -0
- {specfact_cli-0.13.0 → specfact_cli-0.13.1}/src/specfact_cli/generators/openapi_extractor.py +0 -0
- {specfact_cli-0.13.0 → specfact_cli-0.13.1}/src/specfact_cli/generators/plan_generator.py +0 -0
- {specfact_cli-0.13.0 → specfact_cli-0.13.1}/src/specfact_cli/generators/protocol_generator.py +0 -0
- {specfact_cli-0.13.0 → specfact_cli-0.13.1}/src/specfact_cli/generators/report_generator.py +0 -0
- {specfact_cli-0.13.0 → specfact_cli-0.13.1}/src/specfact_cli/generators/task_generator.py +0 -0
- {specfact_cli-0.13.0 → specfact_cli-0.13.1}/src/specfact_cli/generators/test_to_openapi.py +0 -0
- {specfact_cli-0.13.0 → specfact_cli-0.13.1}/src/specfact_cli/generators/workflow_generator.py +0 -0
- {specfact_cli-0.13.0 → specfact_cli-0.13.1}/src/specfact_cli/importers/__init__.py +0 -0
- {specfact_cli-0.13.0 → specfact_cli-0.13.1}/src/specfact_cli/importers/speckit_converter.py +0 -0
- {specfact_cli-0.13.0 → specfact_cli-0.13.1}/src/specfact_cli/importers/speckit_scanner.py +0 -0
- {specfact_cli-0.13.0 → specfact_cli-0.13.1}/src/specfact_cli/integrations/__init__.py +0 -0
- {specfact_cli-0.13.0 → specfact_cli-0.13.1}/src/specfact_cli/integrations/specmatic.py +0 -0
- {specfact_cli-0.13.0 → specfact_cli-0.13.1}/src/specfact_cli/migrations/__init__.py +0 -0
- {specfact_cli-0.13.0 → specfact_cli-0.13.1}/src/specfact_cli/migrations/plan_migrator.py +0 -0
- {specfact_cli-0.13.0 → specfact_cli-0.13.1}/src/specfact_cli/models/__init__.py +0 -0
- {specfact_cli-0.13.0 → specfact_cli-0.13.1}/src/specfact_cli/models/bridge.py +0 -0
- {specfact_cli-0.13.0 → specfact_cli-0.13.1}/src/specfact_cli/models/deviation.py +0 -0
- {specfact_cli-0.13.0 → specfact_cli-0.13.1}/src/specfact_cli/models/enforcement.py +0 -0
- {specfact_cli-0.13.0 → specfact_cli-0.13.1}/src/specfact_cli/models/plan.py +0 -0
- {specfact_cli-0.13.0 → specfact_cli-0.13.1}/src/specfact_cli/models/project.py +0 -0
- {specfact_cli-0.13.0 → specfact_cli-0.13.1}/src/specfact_cli/models/protocol.py +0 -0
- {specfact_cli-0.13.0 → specfact_cli-0.13.1}/src/specfact_cli/models/quality.py +0 -0
- {specfact_cli-0.13.0 → specfact_cli-0.13.1}/src/specfact_cli/models/sdd.py +0 -0
- {specfact_cli-0.13.0 → specfact_cli-0.13.1}/src/specfact_cli/models/source_tracking.py +0 -0
- {specfact_cli-0.13.0 → specfact_cli-0.13.1}/src/specfact_cli/models/task.py +0 -0
- {specfact_cli-0.13.0 → specfact_cli-0.13.1}/src/specfact_cli/modes/__init__.py +0 -0
- {specfact_cli-0.13.0 → specfact_cli-0.13.1}/src/specfact_cli/modes/detector.py +0 -0
- {specfact_cli-0.13.0 → specfact_cli-0.13.1}/src/specfact_cli/modes/router.py +0 -0
- {specfact_cli-0.13.0 → specfact_cli-0.13.1}/src/specfact_cli/resources/semgrep/async.yml +0 -0
- {specfact_cli-0.13.0 → specfact_cli-0.13.1}/src/specfact_cli/resources/semgrep/code-quality.yml +0 -0
- {specfact_cli-0.13.0 → specfact_cli-0.13.1}/src/specfact_cli/resources/semgrep/feature-detection.yml +0 -0
- {specfact_cli-0.13.0 → specfact_cli-0.13.1}/src/specfact_cli/runtime.py +0 -0
- {specfact_cli-0.13.0 → specfact_cli-0.13.1}/src/specfact_cli/sync/__init__.py +0 -0
- {specfact_cli-0.13.0 → specfact_cli-0.13.1}/src/specfact_cli/sync/bridge_probe.py +0 -0
- {specfact_cli-0.13.0 → specfact_cli-0.13.1}/src/specfact_cli/sync/bridge_sync.py +0 -0
- {specfact_cli-0.13.0 → specfact_cli-0.13.1}/src/specfact_cli/sync/bridge_watch.py +0 -0
- {specfact_cli-0.13.0 → specfact_cli-0.13.1}/src/specfact_cli/sync/change_detector.py +0 -0
- {specfact_cli-0.13.0 → specfact_cli-0.13.1}/src/specfact_cli/sync/code_to_spec.py +0 -0
- {specfact_cli-0.13.0 → specfact_cli-0.13.1}/src/specfact_cli/sync/drift_detector.py +0 -0
- {specfact_cli-0.13.0 → specfact_cli-0.13.1}/src/specfact_cli/sync/repository_sync.py +0 -0
- {specfact_cli-0.13.0 → specfact_cli-0.13.1}/src/specfact_cli/sync/spec_to_code.py +0 -0
- {specfact_cli-0.13.0 → specfact_cli-0.13.1}/src/specfact_cli/sync/spec_to_tests.py +0 -0
- {specfact_cli-0.13.0 → specfact_cli-0.13.1}/src/specfact_cli/sync/speckit_sync.py +0 -0
- {specfact_cli-0.13.0 → specfact_cli-0.13.1}/src/specfact_cli/sync/watcher.py +0 -0
- {specfact_cli-0.13.0 → specfact_cli-0.13.1}/src/specfact_cli/templates/__init__.py +0 -0
- {specfact_cli-0.13.0 → specfact_cli-0.13.1}/src/specfact_cli/templates/bridge_templates.py +0 -0
- {specfact_cli-0.13.0 → specfact_cli-0.13.1}/src/specfact_cli/utils/__init__.py +0 -0
- {specfact_cli-0.13.0 → specfact_cli-0.13.1}/src/specfact_cli/utils/acceptance_criteria.py +0 -0
- {specfact_cli-0.13.0 → specfact_cli-0.13.1}/src/specfact_cli/utils/bundle_loader.py +0 -0
- {specfact_cli-0.13.0 → specfact_cli-0.13.1}/src/specfact_cli/utils/console.py +0 -0
- {specfact_cli-0.13.0 → specfact_cli-0.13.1}/src/specfact_cli/utils/enrichment_context.py +0 -0
- {specfact_cli-0.13.0 → specfact_cli-0.13.1}/src/specfact_cli/utils/enrichment_parser.py +0 -0
- {specfact_cli-0.13.0 → specfact_cli-0.13.1}/src/specfact_cli/utils/feature_keys.py +0 -0
- {specfact_cli-0.13.0 → specfact_cli-0.13.1}/src/specfact_cli/utils/git.py +0 -0
- {specfact_cli-0.13.0 → specfact_cli-0.13.1}/src/specfact_cli/utils/github_annotations.py +0 -0
- {specfact_cli-0.13.0 → specfact_cli-0.13.1}/src/specfact_cli/utils/ide_setup.py +0 -0
- {specfact_cli-0.13.0 → specfact_cli-0.13.1}/src/specfact_cli/utils/incremental_check.py +0 -0
- {specfact_cli-0.13.0 → specfact_cli-0.13.1}/src/specfact_cli/utils/optional_deps.py +0 -0
- {specfact_cli-0.13.0 → specfact_cli-0.13.1}/src/specfact_cli/utils/progress.py +0 -0
- {specfact_cli-0.13.0 → specfact_cli-0.13.1}/src/specfact_cli/utils/prompts.py +0 -0
- {specfact_cli-0.13.0 → specfact_cli-0.13.1}/src/specfact_cli/utils/sdd_discovery.py +0 -0
- {specfact_cli-0.13.0 → specfact_cli-0.13.1}/src/specfact_cli/utils/source_scanner.py +0 -0
- {specfact_cli-0.13.0 → specfact_cli-0.13.1}/src/specfact_cli/utils/structure.py +0 -0
- {specfact_cli-0.13.0 → specfact_cli-0.13.1}/src/specfact_cli/utils/structured_io.py +0 -0
- {specfact_cli-0.13.0 → specfact_cli-0.13.1}/src/specfact_cli/utils/yaml_utils.py +0 -0
- {specfact_cli-0.13.0 → specfact_cli-0.13.1}/src/specfact_cli/validators/__init__.py +0 -0
- {specfact_cli-0.13.0 → specfact_cli-0.13.1}/src/specfact_cli/validators/contract_validator.py +0 -0
- {specfact_cli-0.13.0 → specfact_cli-0.13.1}/src/specfact_cli/validators/fsm.py +0 -0
- {specfact_cli-0.13.0 → specfact_cli-0.13.1}/src/specfact_cli/validators/repro_checker.py +0 -0
- {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.
|
|
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.
|
|
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"
|
|
@@ -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
|
-
"
|
|
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
|
-
"
|
|
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.
|
|
623
|
-
"
|
|
624
|
-
"
|
|
625
|
-
"
|
|
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
|
-
"-
|
|
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
|
-
"
|
|
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
|
|
998
|
-
console.print("\n[bold cyan]Step 5/
|
|
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
|
-
#
|
|
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
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
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("
|
|
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
|
-
|
|
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 = "
|
|
1264
|
+
test_output = "Test execution timed out after 60 seconds"
|
|
1025
1265
|
print_error(test_output)
|
|
1026
|
-
|
|
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
|
|
1104
|
-
console.print("\n[bold cyan]Step
|
|
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),
|