diff --git a/src/contextgen/analyzers/setup_generator.py b/src/contextgen/analyzers/setup_generator.py new file mode 100644 index 0000000..6f259f8 --- /dev/null +++ b/src/contextgen/analyzers/setup_generator.py @@ -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 ", + ]) + 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