Add framework detector and setup generator
This commit is contained in:
195
src/contextgen/analyzers/setup_generator.py
Normal file
195
src/contextgen/analyzers/setup_generator.py
Normal file
@@ -0,0 +1,195 @@
|
||||
"""Setup instruction generator for project setup."""
|
||||
|
||||
import json
|
||||
import re
|
||||
from pathlib import Path
|
||||
from typing import Any
|
||||
|
||||
|
||||
class SetupInstructionGenerator:
|
||||
"""Generates setup instructions based on detected package managers."""
|
||||
|
||||
def __init__(self, project_path: Path):
|
||||
self.project_path = project_path
|
||||
|
||||
def generate(self) -> dict[str, Any]:
|
||||
"""Generate comprehensive setup instructions."""
|
||||
instructions: dict[str, Any] = {
|
||||
"install_commands": [],
|
||||
"build_commands": [],
|
||||
"run_commands": [],
|
||||
"test_commands": [],
|
||||
"environment_variables": [],
|
||||
"notes": [],
|
||||
}
|
||||
|
||||
package_manager = self._detect_package_manager()
|
||||
|
||||
if package_manager == "npm":
|
||||
self._generate_npm_instructions(instructions)
|
||||
elif package_manager == "pip":
|
||||
self._generate_pip_instructions(instructions)
|
||||
elif package_manager == "poetry":
|
||||
self._generate_poetry_instructions(instructions)
|
||||
elif package_manager == "cargo":
|
||||
self._generate_cargo_instructions(instructions)
|
||||
elif package_manager == "maven":
|
||||
self._generate_maven_instructions(instructions)
|
||||
elif package_manager == "go":
|
||||
self._generate_go_instructions(instructions)
|
||||
|
||||
self._add_generic_instructions(instructions)
|
||||
self._detect_env_vars(instructions)
|
||||
|
||||
return instructions
|
||||
|
||||
def _detect_package_manager(self) -> str | None:
|
||||
"""Detect which package manager is used."""
|
||||
indicators = {
|
||||
"npm": ["package.json", "package-lock.json"],
|
||||
"pip": ["requirements.txt", "Pipfile"],
|
||||
"poetry": ["pyproject.toml", "poetry.lock"],
|
||||
"cargo": ["Cargo.toml", "Cargo.lock"],
|
||||
"maven": ["pom.xml"],
|
||||
"go": ["go.mod", "go.sum"],
|
||||
}
|
||||
|
||||
for pm, files in indicators.items():
|
||||
for f in files:
|
||||
if (self.project_path / f).exists():
|
||||
return pm
|
||||
|
||||
return None
|
||||
|
||||
def _generate_npm_instructions(self, instructions: dict[str, Any]) -> None:
|
||||
"""Generate npm-specific instructions."""
|
||||
pkg_json = self.project_path / "package.json"
|
||||
|
||||
if pkg_json.exists():
|
||||
content = self._safe_read_file(pkg_json)
|
||||
if content:
|
||||
try:
|
||||
pkg = json.loads(content)
|
||||
deps = pkg.get("dependencies", {})
|
||||
dev_deps = pkg.get("devDependencies", {})
|
||||
|
||||
instructions["install_commands"].append("npm install")
|
||||
|
||||
if "scripts" in pkg:
|
||||
scripts = pkg["scripts"]
|
||||
if "start" in scripts:
|
||||
instructions["run_commands"].append("npm start")
|
||||
if "dev" in scripts:
|
||||
instructions["run_commands"].append("npm run dev")
|
||||
if "build" in scripts:
|
||||
instructions["build_commands"].append("npm run build")
|
||||
if "test" in scripts:
|
||||
instructions["test_commands"].append("npm test")
|
||||
|
||||
if "react-scripts" in dev_deps or "next" in deps:
|
||||
instructions["notes"].append("This appears to be a React/Next.js project")
|
||||
except json.JSONDecodeError:
|
||||
pass
|
||||
|
||||
instructions["install_commands"].extend([
|
||||
"npm install",
|
||||
"npm install --save-dev <additional-deps>",
|
||||
])
|
||||
instructions["run_commands"].append("npm start")
|
||||
instructions["build_commands"].append("npm run build")
|
||||
instructions["test_commands"].append("npm test")
|
||||
|
||||
def _generate_pip_instructions(self, instructions: dict[str, Any]) -> None:
|
||||
"""Generate pip-specific instructions."""
|
||||
requirements = self.project_path / "requirements.txt"
|
||||
|
||||
instructions["install_commands"].extend([
|
||||
"python -m venv venv",
|
||||
"source venv/bin/activate # On Windows: venv\\Scripts\\activate",
|
||||
"pip install -r requirements.txt",
|
||||
])
|
||||
|
||||
if (self.project_path / "manage.py").exists():
|
||||
instructions["run_commands"].append("python manage.py runserver")
|
||||
instructions["build_commands"].append("python manage.py migrate")
|
||||
|
||||
instructions["test_commands"].append("python -m pytest")
|
||||
|
||||
def _generate_poetry_instructions(self, instructions: dict[str, Any]) -> None:
|
||||
"""Generate Poetry-specific instructions."""
|
||||
instructions["install_commands"].extend([
|
||||
"poetry install",
|
||||
"poetry install --with dev",
|
||||
])
|
||||
|
||||
pyproject = self.project_path / "pyproject.toml"
|
||||
if pyproject.exists():
|
||||
content = self._safe_read_file(pyproject)
|
||||
if content and 'scripts' in content:
|
||||
scripts_match = re.search(r'scripts\s*=\s*\{([^}]+)\}', content)
|
||||
if scripts_match:
|
||||
script_name = scripts_match.group(1).split('=')[0].strip().strip('"').strip("'")
|
||||
instructions["run_commands"].append(f"poetry run {script_name}")
|
||||
|
||||
instructions["test_commands"].append("poetry run pytest")
|
||||
|
||||
def _generate_cargo_instructions(self, instructions: dict[str, Any]) -> None:
|
||||
"""Generate Cargo-specific instructions."""
|
||||
instructions["install_commands"].append("cargo build")
|
||||
instructions["run_commands"].append("cargo run")
|
||||
instructions["build_commands"].append("cargo build --release")
|
||||
instructions["test_commands"].append("cargo test")
|
||||
|
||||
def _generate_maven_instructions(self, instructions: dict[str, Any]) -> None:
|
||||
"""Generate Maven-specific instructions."""
|
||||
instructions["install_commands"].append("mvn clean install")
|
||||
instructions["run_commands"].append("mvn spring-boot:run")
|
||||
instructions["build_commands"].append("mvn package")
|
||||
instructions["test_commands"].append("mvn test")
|
||||
|
||||
def _generate_go_instructions(self, instructions: dict[str, Any]) -> None:
|
||||
"""Generate Go-specific instructions."""
|
||||
instructions["install_commands"].extend([
|
||||
"go mod download",
|
||||
"go mod tidy",
|
||||
])
|
||||
|
||||
main_go = self.project_path / "main.go"
|
||||
if main_go.exists():
|
||||
instructions["run_commands"].append("go run main.go")
|
||||
|
||||
instructions["build_commands"].append("go build -o app main.go")
|
||||
instructions["test_commands"].append("go test ./...")
|
||||
|
||||
def _add_generic_instructions(self, instructions: dict[str, Any]) -> None:
|
||||
"""Add generic fallback instructions."""
|
||||
if not instructions["install_commands"]:
|
||||
instructions["install_commands"].append("# Install dependencies based on your project type")
|
||||
|
||||
if not instructions["run_commands"]:
|
||||
instructions["run_commands"].append("# Run command depends on your framework")
|
||||
|
||||
if not instructions["build_commands"]:
|
||||
instructions["build_commands"].append("# Build command depends on your project")
|
||||
|
||||
if not instructions["test_commands"]:
|
||||
instructions["test_commands"].append("# Test command depends on your project")
|
||||
|
||||
def _detect_env_vars(self, instructions: dict[str, Any]) -> None:
|
||||
"""Detect required environment variables from config files."""
|
||||
env_example = self.project_path / ".env.example"
|
||||
|
||||
if env_example.exists():
|
||||
content = self._safe_read_file(env_example)
|
||||
if content:
|
||||
vars_found = re.findall(r"^([A-Z_]+)=", content, re.MULTILINE)
|
||||
instructions["environment_variables"] = vars_found
|
||||
|
||||
instructions["notes"].append("Copy .env.example to .env and configure values")
|
||||
|
||||
def _safe_read_file(self, path: Path) -> str | None:
|
||||
"""Safely read a file."""
|
||||
try:
|
||||
return path.read_text(encoding="utf-8")
|
||||
except (IOError, UnicodeDecodeError):
|
||||
return None
|
||||
Reference in New Issue
Block a user