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