fix: Rewrite config_enhancer LOCAL mode to use Claude CLI properly

The previous implementation had a design flaw - it ran `claude prompt_file`
and expected Claude to magically create a JSON file in a temp directory.
This never worked because Claude CLI is interactive and doesn't auto-save.

Changes:
- Use `--dangerously-skip-permissions` flag to bypass permission prompts
- Create a dedicated temp directory for each enhancement session
- Embed absolute output file path in the prompt so Claude knows where to write
- Run Claude from the temp directory as working_dir
- Improved prompt with explicit Write tool instruction
- Better error handling and logging (file not found, JSON parse errors)
- Show settings preview in prompt for better AI context

The LOCAL mode now follows the same pattern as enhance_skill_local.py
which works correctly.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
YusufKaraaslanSpyke
2026-01-30 10:48:49 +03:00
parent be2353cf2f
commit 22e2edbc7f

View File

@@ -256,126 +256,184 @@ Focus on actionable insights that help developers understand and improve their c
def _enhance_via_local(self, result: dict) -> dict:
"""Enhance configs using Claude Code CLI"""
try:
# Create temporary prompt file
with tempfile.NamedTemporaryFile(mode="w", suffix=".md", delete=False) as f:
prompt_file = Path(f.name)
f.write(self._create_local_prompt(result))
# Create a temporary directory for this enhancement session
with tempfile.TemporaryDirectory(prefix="config_enhance_") as temp_dir:
temp_path = Path(temp_dir)
# Create output file path
output_file = prompt_file.parent / f"{prompt_file.stem}_enhanced.json"
# Define output file path (absolute path that Claude will write to)
output_file = temp_path / "config_enhancement.json"
logger.info("🖥️ Launching Claude Code CLI for config analysis...")
logger.info("⏱️ This will take 30-60 seconds...")
# Create prompt file with the output path embedded
prompt_file = temp_path / "enhance_prompt.md"
prompt_content = self._create_local_prompt(result, output_file)
prompt_file.write_text(prompt_content)
# Run Claude Code CLI
result_data = self._run_claude_cli(prompt_file, output_file)
logger.info("🖥️ Launching Claude Code CLI for config analysis...")
logger.info("⏱️ This will take 30-60 seconds...")
# Clean up
prompt_file.unlink()
if output_file.exists():
output_file.unlink()
# Run Claude Code CLI
result_data = self._run_claude_cli(prompt_file, output_file, temp_path)
if result_data:
# Merge LOCAL enhancements
result["ai_enhancements"] = result_data
logger.info("✅ LOCAL enhancement complete")
return result
else:
logger.warning("⚠️ LOCAL enhancement produced no results")
return result
if result_data:
# Merge LOCAL enhancements
result["ai_enhancements"] = result_data
logger.info("✅ LOCAL enhancement complete")
return result
else:
logger.warning("⚠️ LOCAL enhancement produced no results")
return result
except Exception as e:
logger.error(f"❌ LOCAL enhancement failed: {e}")
return result
def _create_local_prompt(self, result: dict) -> str:
"""Create prompt file for Claude Code CLI"""
def _create_local_prompt(self, result: dict, output_file: Path) -> str:
"""Create prompt file for Claude Code CLI
Args:
result: Config extraction result dict
output_file: Absolute path where Claude should write the JSON output
Returns:
Prompt content string
"""
config_files = result.get("config_files", [])
# Format config data for Claude
# Format config data for Claude (limit to 15 files for reasonable prompt size)
config_data = []
for cf in config_files[:10]:
for cf in config_files[:15]:
# Support both "type" (from config_extractor) and "config_type" (legacy)
config_type = cf.get("type", cf.get("config_type", "unknown"))
settings_preview = []
for s in cf.get("settings", [])[:3]: # Show first 3 settings
settings_preview.append(f" - {s.get('key', 'unknown')}: {str(s.get('value', ''))[:50]}")
config_data.append(f"""
### {cf["relative_path"]} ({config_type})
- Purpose: {cf["purpose"]}
- Patterns: {", ".join(cf.get("patterns", []))}
- Settings count: {len(cf.get("settings", []))}
- Patterns: {", ".join(cf.get("patterns", [])) or "none detected"}
- Settings: {len(cf.get("settings", []))} total
{chr(10).join(settings_preview) if settings_preview else " (no settings)"}
""")
prompt = f"""# Configuration Analysis Task
I need you to analyze these configuration files and provide AI-enhanced insights.
IMPORTANT: You MUST write the output to this EXACT file path:
{output_file}
## Configuration Files ({len(config_files)} total)
## Configuration Files ({len(config_files)} total, showing first 15)
{chr(10).join(config_data)}
## Your Task
Analyze these configs and create a JSON file with the following structure:
Analyze these configuration files and write a JSON file to the path specified above.
The JSON must have this EXACT structure:
```json
{{
"file_enhancements": [
{{
"file_path": "path/to/file",
"explanation": "What this config does",
"best_practice": "Suggested improvements",
"security_concern": "Security issues (if any)",
"migration_suggestion": "Consolidation opportunities",
"context": "Pattern explanation"
"file_path": "relative/path/to/config.json",
"explanation": "Brief explanation of what this config file does",
"best_practice": "Suggested improvement or 'None'",
"security_concern": "Security issue if any, or 'None'",
"migration_suggestion": "Consolidation opportunity or 'None'",
"context": "What pattern or purpose this serves"
}}
],
"overall_insights": {{
"config_count": {len(config_files)},
"security_issues_found": 0,
"consolidation_opportunities": [],
"recommended_actions": []
"consolidation_opportunities": ["List of suggestions"],
"recommended_actions": ["List of actions"]
}}
}}
```
Please save the JSON output to a file named `config_enhancement.json` in the current directory.
## Instructions
Focus on actionable insights:
1. Explain what each config does
2. Suggest best practices
3. Identify security concerns (hardcoded secrets, exposed credentials)
4. Suggest consolidation opportunities
5. Explain the detected patterns
1. Use the Write tool to create the JSON file at: {output_file}
2. Include an enhancement entry for each config file shown above
3. Focus on actionable insights:
- Explain what each config does in 1-2 sentences
- Identify any hardcoded secrets or security issues
- Suggest consolidation if configs have overlapping settings
- Note any missing best practices
DO NOT explain your work - just write the JSON file directly.
"""
return prompt
def _run_claude_cli(self, prompt_file: Path, _output_file: Path) -> dict | None:
"""Run Claude Code CLI and wait for completion"""
def _run_claude_cli(
self, prompt_file: Path, output_file: Path, working_dir: Path
) -> dict | None:
"""Run Claude Code CLI and wait for completion
Args:
prompt_file: Path to the prompt markdown file
output_file: Expected path where Claude will write the JSON output
working_dir: Working directory to run Claude from
Returns:
Parsed JSON dict if successful, None otherwise
"""
import time
try:
# Run claude command
start_time = time.time()
# Run claude command with --dangerously-skip-permissions to bypass all prompts
# This allows Claude to write files without asking for confirmation
logger.info(f" Running: claude --dangerously-skip-permissions {prompt_file.name}")
logger.info(f" Output expected at: {output_file}")
result = subprocess.run(
["claude", str(prompt_file)],
["claude", "--dangerously-skip-permissions", str(prompt_file)],
capture_output=True,
text=True,
timeout=300, # 5 minute timeout
cwd=str(working_dir),
)
elapsed = time.time() - start_time
logger.info(f" Claude finished in {elapsed:.1f} seconds")
if result.returncode != 0:
logger.error(f"❌ Claude CLI failed: {result.stderr}")
logger.error(f"❌ Claude CLI failed (exit code {result.returncode})")
if result.stderr:
logger.error(f" Error: {result.stderr[:200]}")
return None
# Try to find output file (Claude might save it with different name)
# Look for JSON files created in the last minute
import time
# Check if the expected output file was created
if output_file.exists():
try:
with open(output_file) as f:
data = json.load(f)
if "file_enhancements" in data or "overall_insights" in data:
logger.info(f"✅ Found enhancement data in {output_file.name}")
return data
else:
logger.warning("⚠️ Output file exists but missing expected keys")
except json.JSONDecodeError as e:
logger.error(f"❌ Failed to parse output JSON: {e}")
return None
# Fallback: Look for any JSON files created in the working directory
logger.info(" Looking for JSON files in working directory...")
current_time = time.time()
potential_files = []
for json_file in prompt_file.parent.glob("*.json"):
if current_time - json_file.stat().st_mtime < 120: # Created in last 2 minutes
for json_file in working_dir.glob("*.json"):
# Check if created recently (within last 2 minutes)
if current_time - json_file.stat().st_mtime < 120:
potential_files.append(json_file)
# Try to load the most recent JSON file
for json_file in sorted(potential_files, key=lambda f: f.stat().st_mtime, reverse=True):
# Try to load the most recent JSON file with expected structure
for json_file in sorted(
potential_files, key=lambda f: f.stat().st_mtime, reverse=True
):
try:
with open(json_file) as f:
data = json.load(f)
@@ -386,11 +444,17 @@ Focus on actionable insights:
continue
logger.warning("⚠️ Could not find enhancement output file")
logger.info(f" Expected file: {output_file}")
logger.info(f" Files in dir: {list(working_dir.glob('*'))}")
return None
except subprocess.TimeoutExpired:
logger.error("❌ Claude CLI timeout (5 minutes)")
return None
except FileNotFoundError:
logger.error("'claude' command not found. Is Claude Code CLI installed?")
logger.error(" Install with: npm install -g @anthropic-ai/claude-code")
return None
except Exception as e:
logger.error(f"❌ Error running Claude CLI: {e}")
return None