Fix Windows UTF-8 encoding errors in spec file reading (#744)

* Fix Windows UTF-8 encoding errors in spec file reading

Fixes UnicodeDecodeError on Windows when reading spec files with non-ASCII
characters. Python's read_text() defaults to cp1252 on Windows instead of
UTF-8, causing crashes when spec.md contains Unicode characters.

Changes:
- spec_document_validator.py: Add encoding='utf-8' to spec.md reading
- compaction.py: Add encoding='utf-8' to phase output file reading
- validation_strategy.py: Add encoding='utf-8' to requirements/pyproject reading
- agent_runner.py: Add encoding='utf-8' to prompt file reading

Impact:
- Fixes spec generation crashes on Windows with Unicode content
- No impact on Unix/Mac (already default to UTF-8)
- Prevents charmap codec errors during spec validation

Tested on Windows 10 with Auto-Claude v2.7.3

* Add error handling for file read operations

Implements Gemini Code Assist review suggestions to improve robustness:

1. spec_document_validator.py:
   - Wrap read_text() in try-except to handle corrupted UTF-8 files
   - Provide clear error message when spec.md is unreadable

2. validation_strategy.py:
   - Silently skip unreadable requirements.txt/pyproject.toml files
   - Prevents crashes during project type detection

This defensive programming prevents crashes from:
- Corrupted or invalid UTF-8 files
- Permission errors (OSError)
- Other file system issues

Addresses code review feedback from @gemini-code-assist

* Fix Windows UTF-8 encoding errors in project analyzer

- Add encoding='utf-8' to file reads in analyzer.py (load/save profile)
- Add encoding='utf-8' to file reads in config_parser.py (read_json, read_text)
- Add encoding='utf-8' to file reads in stack_detector.py (YAML files)

This fixes the 'charmap' codec error that was blocking roadmap generation on Windows:
  Warning: Could not load security profile: 'charmap' codec can't decode byte 0x8d

Same root cause as spec file reading - Windows defaults to cp1252 instead of UTF-8.

* Revert "Fix Windows UTF-8 encoding errors in project analyzer"

This reverts commit 41deed0aa388d8599371db16f822d9278464f09b.

---------

Co-authored-by: TamerineSky <[email protected]>
This commit is contained in:
TamerineSky
2026-01-07 10:56:00 +01:00
committed by GitHub
co-authored by TamerineSky
parent e8b7880617
commit a01634d548
4 changed files with 16 additions and 5 deletions
+1 -1
View File
@@ -141,7 +141,7 @@ def gather_phase_outputs(spec_dir: Path, phase_name: str) -> str:
file_path = spec_dir / filename
if file_path.exists():
try:
content = file_path.read_text()
content = file_path.read_text(encoding="utf-8")
# Limit individual file size
if len(content) > 10000:
content = content[:10000] + "\n\n[... file truncated ...]"
+1 -1
View File
@@ -82,7 +82,7 @@ class AgentRunner:
return False, f"Prompt not found: {prompt_path}"
# Load prompt
prompt = prompt_path.read_text()
prompt = prompt_path.read_text(encoding="utf-8")
debug_detailed(
"agent_runner",
"Loaded prompt file",
@@ -40,7 +40,12 @@ class SpecDocumentValidator:
fixes.append("Create spec.md with required sections")
return ValidationResult(False, "spec", errors, warnings, fixes)
content = spec_file.read_text()
try:
content = spec_file.read_text(encoding="utf-8")
except (OSError, UnicodeDecodeError) as e:
errors.append(f"Failed to read spec.md: {e}")
fixes.append("Ensure spec.md is a valid UTF-8 encoded file")
return ValidationResult(False, "spec", errors, warnings, fixes)
# Check for required sections
for section in SPEC_REQUIRED_SECTIONS:
+8 -2
View File
@@ -171,9 +171,15 @@ def detect_project_type(project_dir: Path) -> str:
# Try to detect API framework
deps_text = ""
if requirements.exists():
deps_text = requirements.read_text().lower()
try:
deps_text = requirements.read_text(encoding="utf-8").lower()
except (OSError, UnicodeDecodeError):
pass # Skip if file is unreadable
if pyproject.exists():
deps_text += pyproject.read_text().lower()
try:
deps_text += pyproject.read_text(encoding="utf-8").lower()
except (OSError, UnicodeDecodeError):
pass # Skip if file is unreadable
if "fastapi" in deps_text or "flask" in deps_text or "django" in deps_text:
return "python_api"