diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 61b98e4e..6f2ba408 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -61,13 +61,26 @@ Each complete change ideally includes four elements: ``` 2. Create and activate a Conda environment (recommended): ```bash - conda create -n pdd python=3.12 - conda activate pdd + conda create -n pdd-dev python=3.12 + conda activate pdd-dev + ``` + To use a different environment name, set the `PDD_CONDA_ENV` environment variable before running make commands: + ```bash + # Option 1: Export for the session + export PDD_CONDA_ENV=my-env + make + + # Option 2: Set inline for a single command + PDD_CONDA_ENV=my-env make build + + # Option 3: Pass as a make argument + make test PDD_CONDA_ENV=my-env ``` 3. Install dependencies in editable mode with dev extras: ```bash pip install -e .[dev] ``` + Note: Many Makefile targets (e.g., `make test`, `make coverage`, `make lint`) automatically install dev dependencies into the conda environment before each run. 4. Optional: Use Docker for reproducible environments, especially for tricky, environment‑dependent bugs. ## Running Tests diff --git a/Makefile b/Makefile index 7346b213..ff365cd1 100644 --- a/Makefile +++ b/Makefile @@ -1,3 +1,21 @@ +# Detect if we're under a worktrees directory +_PARENT_DIR := $(notdir $(patsubst %/,%,$(dir $(CURDIR)))) +_CURRENT_DIR := $(notdir $(CURDIR)) + +# Conda environment precedence: +# 1. PDD_CONDA_ENV from environment (highest priority) +# 2. Current directory name if under worktrees/ +# 3. "pdd" (default) +ifndef PDD_CONDA_ENV + ifeq ($(_PARENT_DIR),worktrees) + PDD_CONDA_ENV := $(_CURRENT_DIR) + else + PDD_CONDA_ENV := pdd + endif +endif + +PDD_PYTHON_VERSION := 3.12 + # Directories PROD_DIR := pdd STAGING_DIR := . @@ -58,7 +76,26 @@ help: @echo " make staging - Copy files to staging" @echo " make production - Copy files from staging to pdd" @echo " make update-extension - Update VS Code extension" - + @echo "" + @echo "Environment Setup:" + @echo " make dev-setup - Run full developer setup (interactive)" + @echo " make create-conda-env - Create conda env (requires worktrees dir)" + +# Create conda environment (only valid if under worktrees directory) +.PHONY: create-conda-env +create-conda-env: +ifeq ($(_PARENT_DIR),worktrees) + @echo "Creating conda environment '$(_CURRENT_DIR)' with Python $(PDD_PYTHON_VERSION)" + @conda create -n $(_CURRENT_DIR) python=$(PDD_PYTHON_VERSION) -y +else + $(error Cannot create conda environment. Current directory must be immediately under a 'worktrees' directory. Current parent directory: $(_PARENT_DIR)) +endif + +# Run the full developer setup script +.PHONY: dev-setup +dev-setup: + @bash scripts/dev-setup.sh + # Public repo paths (override via env if needed) PUBLIC_PDD_REPO_DIR ?= staging/public/pdd PUBLIC_PDD_REMOTE ?= https://github.com/promptdriven/pdd.git @@ -186,31 +223,31 @@ run-examples: $(EXAMPLE_FILES) @echo "Running examples one by one:" @$(foreach example_file,$(EXAMPLE_FILES), \ echo "Running $(example_file)"; \ - conda run -n pdd --no-capture-output PYTHONPATH=$(STAGING_DIR):$(PDD_DIR):$$PYTHONPATH python $(example_file) || exit 1; \ + conda run -n $(PDD_CONDA_ENV) --no-capture-output PYTHONPATH=$(STAGING_DIR):$(PDD_DIR):$$PYTHONPATH python $(example_file) || exit 1; \ ) @echo "All examples ran successfully" # Ensure dev dependencies are installed before running tests ensure-dev-deps: - @echo "Updating pdd conda environment with dev dependencies" - @conda run -n pdd --no-capture-output pip install -e '.[dev]' + @echo "Updating $(PDD_CONDA_ENV) conda environment with dev dependencies" + @conda run -n $(PDD_CONDA_ENV) --no-capture-output pip install -e '.[dev]' # Run tests test: ensure-dev-deps @echo "Running staging tests" @cd $(STAGING_DIR) - @conda run -n pdd --no-capture-output PDD_RUN_REAL_LLM_TESTS=1 PDD_RUN_LLM_TESTS=1 PDD_PATH=$(abspath $(PDD_DIR)) PYTHONPATH=$(PDD_DIR):$$PYTHONPATH python -m pytest -vv -n auto $(TESTS_DIR) + @conda run -n $(PDD_CONDA_ENV) --no-capture-output PDD_RUN_REAL_LLM_TESTS=1 PDD_RUN_LLM_TESTS=1 PDD_PATH=$(abspath $(PDD_DIR)) PYTHONPATH=$(PDD_DIR):$$PYTHONPATH python -m pytest -vv -n auto $(TESTS_DIR) # Run tests with coverage coverage: ensure-dev-deps @echo "Running tests with coverage" @cd $(STAGING_DIR) - @conda run -n pdd --no-capture-output PDD_PATH=$(STAGING_DIR) PYTHONPATH=$(PDD_DIR):$$PYTHONPATH python -m pytest --cov=$(PDD_DIR) --cov-report=term-missing --cov-report=html $(TESTS_DIR) + @conda run -n $(PDD_CONDA_ENV) --no-capture-output PDD_PATH=$(STAGING_DIR) PYTHONPATH=$(PDD_DIR):$$PYTHONPATH python -m pytest --cov=$(PDD_DIR) --cov-report=term-missing --cov-report=html $(TESTS_DIR) # Run pylint lint: ensure-dev-deps @echo "Running pylint" - @conda run -n pdd --no-capture-output pylint pdd tests + @conda run -n $(PDD_CONDA_ENV) --no-capture-output pylint pdd tests # Fix crashes in code crash: @@ -265,7 +302,7 @@ ifdef MODULE $(eval RESULTS_FILE := $(MODULE)_verify_results.log) @echo "Verifying $(PY_FILE) functionality..." - -conda run -n pdd --no-capture-output pdd --strength .9 --verbose verify --max-attempts 3 --budget 5.0 --output-code $(PDD_DIR)/$(MODULE)_verified.py --output-program $(CONTEXT_DIR)/$(MODULE)_example_verified.py --output-results $(RESULTS_FILE) $(PY_PROMPT) $(PY_FILE) $(PROGRAM_FILE) + -conda run -n $(PDD_CONDA_ENV) --no-capture-output pdd --strength .9 --verbose verify --max-attempts 3 --budget 5.0 --output-code $(PDD_DIR)/$(MODULE)_verified.py --output-program $(CONTEXT_DIR)/$(MODULE)_example_verified.py --output-results $(RESULTS_FILE) $(PY_PROMPT) $(PY_FILE) $(PROGRAM_FILE) else @echo "Please specify a MODULE to verify" @echo "Usage: make verify MODULE=" @@ -292,7 +329,7 @@ ifdef CHANGE_FILE $(eval CHANGE_FILE_BASENAME := $(basename $(notdir $(CHANGE_FILE)))) $(eval DETECT_OUTPUT_FILE := $(CHANGE_FILE_BASENAME)_detect.csv) @echo "Output will be saved to $(DETECT_OUTPUT_FILE)" - @conda run -n pdd --no-capture-output pdd detect --output $(DETECT_OUTPUT_FILE) $(ALL_PROMPT_FILES) $(CHANGE_FILE) + @conda run -n $(PDD_CONDA_ENV) --no-capture-output pdd detect --output $(DETECT_OUTPUT_FILE) $(ALL_PROMPT_FILES) $(CHANGE_FILE) @echo "Detection complete. Results in $(DETECT_OUTPUT_FILE)" else @echo "Please specify a CHANGE_FILE to detect changes against." @@ -321,7 +358,7 @@ ifdef PROMPT_FILE $(eval ACTUAL_OUTPUT_FILE := $(if $(OUTPUT_FILE),$(OUTPUT_FILE),$(DEFAULT_OUTPUT_FILE))) @echo "Output will be saved to $(ACTUAL_OUTPUT_FILE)" - @conda run -n pdd --no-capture-output pdd change --output $(ACTUAL_OUTPUT_FILE) $(CHANGE_PROMPT) $(CODE_FILE) $(PROMPT_FILE) + @conda run -n $(PDD_CONDA_ENV) --no-capture-output pdd change --output $(ACTUAL_OUTPUT_FILE) $(CHANGE_PROMPT) $(CODE_FILE) $(PROMPT_FILE) @echo "Single prompt modification complete. Output at $(ACTUAL_OUTPUT_FILE)" else @echo "Error: PROMPT_FILE must be specified for single prompt change mode." @@ -371,8 +408,8 @@ ifdef CSV_FILE ) @if [ ! -z "$(OUTPUT_LOCATION)" ]; then echo "Output location specified by user: $(OUTPUT_LOCATION)"; fi - @echo "Executing from $(PROMPTS_DIR): conda run -n pdd --no-capture-output pdd --force change --budget 10.0 --csv $(CMD_OUTPUT_ARG) $(REL_CSV_FILE) $(REL_CODE_DIR)" - @cd $(PROMPTS_DIR) && conda run -n pdd --no-capture-output pdd --force change --budget 10.0 --csv $(CMD_OUTPUT_ARG) $(REL_CSV_FILE) $(REL_CODE_DIR) + @echo "Executing from $(PROMPTS_DIR): conda run -n $(PDD_CONDA_ENV) --no-capture-output pdd --force change --budget 10.0 --csv $(CMD_OUTPUT_ARG) $(REL_CSV_FILE) $(REL_CODE_DIR)" + @cd $(PROMPTS_DIR) && conda run -n $(PDD_CONDA_ENV) --no-capture-output pdd --force change --budget 10.0 --csv $(CMD_OUTPUT_ARG) $(REL_CSV_FILE) $(REL_CODE_DIR) @echo "CSV batch prompt modification complete." else # This means CSV_FILE was not defined, which contradicts the outer ifeq logic. This branch likely won't be hit if CSV_FILE is the primary condition. @echo "Error: CSV_FILE must be specified for CSV batch change mode." # This case should ideally not be reached due to outer ifeq @@ -390,7 +427,7 @@ ifdef MODULE prompt="$(PROMPTS_DIR)/$${name}_python.prompt"; \ echo "Fixing $$name"; \ if [ -f "$(CONTEXT_DIR)/$${name}_example.py" ]; then \ - conda run -n pdd --no-capture-output python -m pdd.cli --time 1 --strength .9 --temperature 0 --verbose --force fix --loop --auto-submit --max-attempts 5 --output-test output/ --output-code output/ --verification-program $(CONTEXT_DIR)/$${name}_example.py $$prompt $(PDD_DIR)/$${name}.py $(TESTS_DIR)/test_$${name}.py $${name}.log; \ + conda run -n $(PDD_CONDA_ENV) --no-capture-output python -m pdd.cli --time 1 --strength .9 --temperature 0 --verbose --force fix --loop --auto-submit --max-attempts 5 --output-test output/ --output-code output/ --verification-program $(CONTEXT_DIR)/$${name}_example.py $$prompt $(PDD_DIR)/$${name}.py $(TESTS_DIR)/test_$${name}.py $${name}.log; \ else \ echo "Warning: No verification program found for $$name"; \ fi; @@ -401,7 +438,7 @@ else name="$${rel_path%_python.prompt}"; \ echo "Fixing $$name"; \ if [ -f "$(CONTEXT_DIR)/$${name}_example.py" ]; then \ - conda run -n pdd --no-capture-output python -m pdd.cli --strength .9 --temperature 0 --verbose --force fix --loop --auto-submit --max-attempts 5 --output-test output/ --output-code output/ --verification-program $(CONTEXT_DIR)/$${name}_example.py $$prompt $(PDD_DIR)/$${name}.py $(TESTS_DIR)/test_$${name}.py $${name}.log; \ + conda run -n $(PDD_CONDA_ENV) --no-capture-output python -m pdd.cli --strength .9 --temperature 0 --verbose --force fix --loop --auto-submit --max-attempts 5 --output-test output/ --output-code output/ --verification-program $(CONTEXT_DIR)/$${name}_example.py $$prompt $(PDD_DIR)/$${name}.py $(TESTS_DIR)/test_$${name}.py $${name}.log; \ else \ echo "Warning: No verification program found for $$name"; \ fi; \ @@ -425,10 +462,10 @@ ifdef MODULE fi @echo "Updating $(PY_PROMPT) based on changes in $(PY_FILE)" - conda run -n pdd --no-capture-output pdd --verbose update --git $(PY_PROMPT) $(PY_FILE) + conda run -n $(PDD_CONDA_ENV) --no-capture-output pdd --verbose update --git $(PY_PROMPT) $(PY_FILE) else @echo "Running repository-wide prompt update" - conda run -n pdd --no-capture-output pdd --verbose update --directory pdd --extensions py + conda run -n $(PDD_CONDA_ENV) --no-capture-output pdd --verbose update --directory pdd --extensions py endif # Generate requirements.txt @@ -520,12 +557,12 @@ test-all-ci: ensure-dev-deps @mkdir -p test_results ifdef PR_NUMBER ifdef PR_URL - @conda run -n pdd --no-capture-output python scripts/run_all_tests_with_results.py --pr-number $(PR_NUMBER) --pr-url $(PR_URL) + @conda run -n $(PDD_CONDA_ENV) --no-capture-output python scripts/run_all_tests_with_results.py --pr-number $(PR_NUMBER) --pr-url $(PR_URL) else - @conda run -n pdd --no-capture-output python scripts/run_all_tests_with_results.py --pr-number $(PR_NUMBER) + @conda run -n $(PDD_CONDA_ENV) --no-capture-output python scripts/run_all_tests_with_results.py --pr-number $(PR_NUMBER) endif else - @conda run -n pdd --no-capture-output python scripts/run_all_tests_with_results.py + @conda run -n $(PDD_CONDA_ENV) --no-capture-output python scripts/run_all_tests_with_results.py endif # Run all tests with Infisical (for local development and CI) @@ -540,12 +577,12 @@ test-all-with-infisical: ensure-dev-deps @mkdir -p test_results ifdef PR_NUMBER ifdef PR_URL - @infisical run -- conda run -n pdd --no-capture-output python scripts/run_all_tests_with_results.py --pr-number $(PR_NUMBER) --pr-url $(PR_URL) + @infisical run -- conda run -n $(PDD_CONDA_ENV) --no-capture-output python scripts/run_all_tests_with_results.py --pr-number $(PR_NUMBER) --pr-url $(PR_URL) else - @infisical run -- conda run -n pdd --no-capture-output python scripts/run_all_tests_with_results.py --pr-number $(PR_NUMBER) + @infisical run -- conda run -n $(PDD_CONDA_ENV) --no-capture-output python scripts/run_all_tests_with_results.py --pr-number $(PR_NUMBER) endif else - @infisical run -- conda run -n pdd --no-capture-output python scripts/run_all_tests_with_results.py + @infisical run -- conda run -n $(PDD_CONDA_ENV) --no-capture-output python scripts/run_all_tests_with_results.py endif # Test a PR from a public or private repo by triggering GitHub Actions @@ -583,14 +620,14 @@ install: build: @echo "Building pdd" @rm -rf dist - @conda run -n pdd --no-capture-output python -m build + @conda run -n $(PDD_CONDA_ENV) --no-capture-output python -m build @rm dist/*.tar.gz #don't upload source distribution # Post-process the wheel with preprocessed prompts @echo "Post-processing wheel with preprocessed prompts..." - @conda run -n pdd --no-capture-output python scripts/preprocess_wheel.py 'dist/*.whl' + @conda run -n $(PDD_CONDA_ENV) --no-capture-output python scripts/preprocess_wheel.py 'dist/*.whl' - @conda run -n pdd --no-capture-output twine upload --repository pypi dist/*.whl + @conda run -n $(PDD_CONDA_ENV) --no-capture-output twine upload --repository pypi dist/*.whl publish: @echo "Building and uploading package" diff --git a/scripts/dev-setup.sh b/scripts/dev-setup.sh new file mode 100755 index 00000000..7abda164 --- /dev/null +++ b/scripts/dev-setup.sh @@ -0,0 +1,437 @@ +#!/bin/bash +# +# PDD Developer Setup Script +# +# This script sets up a development environment for contributing to PDD. +# It handles: +# 1. Optional worktree creation from main branch +# 2. Conda environment creation with Python 3.12 +# 3. Symbolic links for prompts/ and data/ +# 4. .env file with PDD_PATH +# 5. Conda environment variable for PDD_PATH +# 6. Development dependencies installation +# +# Usage: +# ./scripts/dev-setup.sh [options] +# +# Options: +# --worktree Create a git worktree with this name +# --env-name Name for the conda environment (default: worktree name or "pdd") +# --python Python version (default: 3.12) +# --no-worktree Skip worktree creation prompts +# --help Show this help message +# + +set -e + +# Colors for output +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +BLUE='\033[0;34m' +NC='\033[0m' # No Color + +# Default values +PYTHON_VERSION="3.12" +WORKTREE_NAME="" +ENV_NAME="" +NO_WORKTREE=false +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" + +# PROJECT_ROOT is the main git repository root (where worktrees will be created) +# This works even when run from within a worktree +PROJECT_ROOT="" + +# REPO_ROOT is the working directory for setup (may be a worktree) +REPO_ROOT="" + +# Print functions +info() { + echo -e "${BLUE}[INFO]${NC} $1" +} + +success() { + echo -e "${GREEN}[OK]${NC} $1" +} + +warn() { + echo -e "${YELLOW}[WARN]${NC} $1" +} + +error() { + echo -e "${RED}[ERROR]${NC} $1" + exit 1 +} + +show_help() { + cat << EOF +PDD Developer Setup Script + +Usage: $(basename "$0") [options] + +Options: + --worktree Create a git worktree with this name + --env-name Name for the conda environment (default: worktree name or "pdd") + --python Python version (default: 3.12) + --no-worktree Skip worktree creation, set up current directory + --help Show this help message + +Examples: + # Interactive setup in current directory + $(basename "$0") + + # Create worktree and set up + $(basename "$0") --worktree fix-issue-123 + + # Set up current directory with custom env name + $(basename "$0") --no-worktree --env-name my-pdd-dev + +EOF +} + +# Parse command line arguments +while [[ $# -gt 0 ]]; do + case $1 in + --worktree) + WORKTREE_NAME="$2" + shift 2 + ;; + --env-name) + ENV_NAME="$2" + shift 2 + ;; + --python) + PYTHON_VERSION="$2" + shift 2 + ;; + --no-worktree) + NO_WORKTREE=true + shift + ;; + --help) + show_help + exit 0 + ;; + *) + error "Unknown option: $1. Use --help for usage." + ;; + esac +done + +# Check prerequisites +check_prerequisites() { + info "Checking prerequisites..." + + # Check for git + if ! command -v git &> /dev/null; then + error "git is not installed. Please install git first." + fi + + # Check for conda + if ! command -v conda &> /dev/null; then + error "conda is not installed. Please install Miniconda or Anaconda first." + fi + + # Check we're in a git repo + if ! git rev-parse --git-dir &> /dev/null; then + error "Not in a git repository. Please run from within the PDD repo." + fi + + # Determine PROJECT_ROOT (main repository root, works from worktrees too) + # git-common-dir points to the shared .git directory across all worktrees + local git_common_dir + git_common_dir="$(git rev-parse --git-common-dir)" + PROJECT_ROOT="$(cd "$git_common_dir/.." && pwd)" + info "Project root: $PROJECT_ROOT" + + # Default REPO_ROOT to current git toplevel (may change if we create a worktree) + REPO_ROOT="$(git rev-parse --show-toplevel)" + + success "Prerequisites check passed" +} + +# Prompt for worktree creation +prompt_worktree() { + if [[ "$NO_WORKTREE" == "true" ]]; then + return + fi + + if [[ -n "$WORKTREE_NAME" ]]; then + return + fi + + echo "" + echo -e "${BLUE}Would you like to create a git worktree?${NC}" + echo "Worktrees allow you to work on multiple branches simultaneously." + echo "" + read -p "Create worktree? [y/N]: " response + + if [[ "$response" =~ ^[Yy]$ ]]; then + read -p "Enter worktree name (e.g., fix-issue-123): " WORKTREE_NAME + if [[ -z "$WORKTREE_NAME" ]]; then + warn "No worktree name provided, skipping worktree creation" + fi + fi +} + +# Create git worktree +create_worktree() { + if [[ -z "$WORKTREE_NAME" ]]; then + return + fi + + info "Creating git worktree '$WORKTREE_NAME'..." + + # Worktrees are always created under the main project root + WORKTREES_DIR="$PROJECT_ROOT/.pdd/worktrees" + WORKTREE_PATH="$WORKTREES_DIR/$WORKTREE_NAME" + + info "Worktrees directory: $WORKTREES_DIR" + + # Check if worktree already exists + if [[ -d "$WORKTREE_PATH" ]]; then + warn "Worktree already exists at $WORKTREE_PATH" + read -p "Use existing worktree? [Y/n]: " response + if [[ "$response" =~ ^[Nn]$ ]]; then + error "Worktree already exists. Choose a different name." + fi + else + # Create worktrees directory if needed + mkdir -p "$WORKTREES_DIR" + + # Fetch latest main (run from project root to ensure we're in the right context) + info "Fetching latest from origin..." + git -C "$PROJECT_ROOT" fetch origin main + + # Create worktree from main + git -C "$PROJECT_ROOT" worktree add -b "$WORKTREE_NAME" "$WORKTREE_PATH" origin/main + success "Created worktree at $WORKTREE_PATH" + fi + + # Change to worktree directory + cd "$WORKTREE_PATH" + REPO_ROOT="$WORKTREE_PATH" + + success "Now working in worktree: $WORKTREE_PATH" +} + +# Determine conda environment name +determine_env_name() { + if [[ -n "$ENV_NAME" ]]; then + return + fi + + # If we created a worktree, use its name + if [[ -n "$WORKTREE_NAME" ]]; then + ENV_NAME="$WORKTREE_NAME" + info "Using worktree name for conda environment: $ENV_NAME" + return + fi + + # Check if we're under a worktrees directory + PARENT_DIR=$(basename "$(dirname "$REPO_ROOT")") + CURRENT_DIR=$(basename "$REPO_ROOT") + + if [[ "$PARENT_DIR" == "worktrees" ]]; then + ENV_NAME="$CURRENT_DIR" + info "Detected worktree directory, using name: $ENV_NAME" + return + fi + + # Default to "pdd" + ENV_NAME="pdd" + info "Using default conda environment name: $ENV_NAME" +} + +# Create conda environment +create_conda_env() { + info "Setting up conda environment '$ENV_NAME' with Python $PYTHON_VERSION..." + + # Check if environment already exists + if conda env list | grep -q "^$ENV_NAME "; then + warn "Conda environment '$ENV_NAME' already exists" + read -p "Recreate environment? [y/N]: " response + if [[ "$response" =~ ^[Yy]$ ]]; then + info "Removing existing environment..." + conda env remove -n "$ENV_NAME" -y + else + info "Using existing environment" + return + fi + fi + + # Create the environment + conda create -n "$ENV_NAME" python="$PYTHON_VERSION" -y + success "Created conda environment '$ENV_NAME'" +} + +# Create symbolic links +create_symlinks() { + info "Creating symbolic links for prompts/ and data/..." + + cd "$REPO_ROOT" + + # Create prompts symlink in pdd/ + if [[ -L "pdd/prompts" ]]; then + info "Symlink pdd/prompts already exists" + elif [[ -e "pdd/prompts" ]]; then + warn "pdd/prompts exists but is not a symlink" + else + if [[ -d "prompts" ]]; then + ln -s ../prompts pdd/prompts + success "Created symlink: pdd/prompts -> ../prompts" + else + warn "prompts/ directory not found, skipping symlink" + fi + fi + + # Create data symlink in pdd/ + if [[ -L "pdd/data" ]]; then + info "Symlink pdd/data already exists" + elif [[ -e "pdd/data" ]]; then + warn "pdd/data exists but is not a symlink" + else + if [[ -d "data" ]]; then + ln -s ../data pdd/data + success "Created symlink: pdd/data -> ../data" + else + warn "data/ directory not found, skipping symlink" + fi + fi +} + +# Create .env file +create_env_file() { + info "Setting up .env file..." + + cd "$REPO_ROOT" + PDD_PATH="$REPO_ROOT/pdd" + + # Check if .env exists + if [[ -f ".env" ]]; then + # Check if PDD_PATH is already set + if grep -q "^PDD_PATH=" ".env"; then + info "PDD_PATH already set in .env" + else + # Append PDD_PATH + echo "" >> .env + echo "# PDD source directory path" >> .env + echo "PDD_PATH=$PDD_PATH" >> .env + success "Added PDD_PATH to existing .env" + fi + else + # Create new .env + cat > .env << EOF +# PDD Environment Configuration +# This file is auto-generated by dev-setup.sh + +# PDD source directory path +PDD_PATH=$PDD_PATH + +# Add your API keys below: +# OPENAI_API_KEY=sk-your-key-here +# ANTHROPIC_API_KEY=sk-ant-your-key-here +# GOOGLE_API_KEY=your-google-api-key +EOF + success "Created .env file with PDD_PATH" + fi + + # Add .env to .gitignore if not already there + if [[ -f ".gitignore" ]]; then + if ! grep -q "^\.env$" ".gitignore"; then + echo "" >> .gitignore + echo "# Local environment file" >> .gitignore + echo ".env" >> .gitignore + success "Added .env to .gitignore" + else + info ".env already in .gitignore" + fi + else + echo ".env" > .gitignore + success "Created .gitignore with .env" + fi +} + +# Set PDD_PATH in conda environment +set_conda_env_var() { + info "Setting PDD_PATH in conda environment '$ENV_NAME'..." + + PDD_PATH="$REPO_ROOT/pdd" + + # Use conda to set the environment variable + conda env config vars set PDD_PATH="$PDD_PATH" -n "$ENV_NAME" + + success "Set PDD_PATH=$PDD_PATH in conda environment" + info "Note: You'll need to reactivate the environment for this to take effect" +} + +# Install development dependencies +install_dev_deps() { + info "Installing development dependencies..." + + cd "$REPO_ROOT" + + # Run pip install in the conda environment + conda run -n "$ENV_NAME" --no-capture-output pip install -e '.[dev]' + + success "Installed development dependencies" +} + +# Print summary +print_summary() { + echo "" + echo -e "${GREEN}========================================${NC}" + echo -e "${GREEN} PDD Developer Setup Complete!${NC}" + echo -e "${GREEN}========================================${NC}" + echo "" + echo "Environment Details:" + echo " Conda environment: $ENV_NAME" + echo " Python version: $PYTHON_VERSION" + echo " Main project root: $PROJECT_ROOT" + echo " Working directory: $REPO_ROOT" + echo " PDD_PATH: $REPO_ROOT/pdd" + echo "" + echo "Next Steps:" + echo " 1. Activate the environment:" + echo -e " ${YELLOW}conda activate $ENV_NAME${NC}" + echo "" + echo " 2. Verify setup:" + echo -e " ${YELLOW}echo \$PDD_PATH${NC}" + echo -e " ${YELLOW}pdd --version${NC}" + echo "" + echo " 3. Run tests:" + echo -e " ${YELLOW}make test${NC}" + echo "" + if [[ -n "$WORKTREE_NAME" ]]; then + echo " 4. Your worktree is at:" + echo -e " ${YELLOW}$REPO_ROOT${NC}" + echo "" + echo " Worktrees are stored under:" + echo -e " ${YELLOW}$PROJECT_ROOT/.pdd/worktrees/${NC}" + echo "" + fi + echo "For more information, see docs/ONBOARDING.md" + echo "" +} + +# Main execution +main() { + echo "" + echo -e "${BLUE}========================================${NC}" + echo -e "${BLUE} PDD Developer Setup Script${NC}" + echo -e "${BLUE}========================================${NC}" + echo "" + + check_prerequisites + prompt_worktree + create_worktree + determine_env_name + create_conda_env + create_symlinks + create_env_file + set_conda_env_var + install_dev_deps + print_summary +} + +main "$@"