From 74f3b90c7d96628ee7e4534fe504df70078a0f0d Mon Sep 17 00:00:00 2001 From: Wessel Tip Date: Mon, 22 Sep 2025 14:17:17 +0200 Subject: [PATCH] feat(fish): Add custom env management system --- .gitignore | 6 + fish/config.fish | 5 +- fish/environments/templates/README.md | 71 ++++ fish/environments/templates/basic.fish | 49 +++ fish/environments/templates/conda.fish | 69 ++++ fish/environments/templates/node.fish | 65 ++++ fish/environments/templates/python.fish | 73 ++++ fish/environments/templates/ros2.fish | 71 ++++ fish/functions/env.fish | 485 ++++++++++++++++++++++++ 9 files changed, 893 insertions(+), 1 deletion(-) create mode 100644 fish/environments/templates/README.md create mode 100644 fish/environments/templates/basic.fish create mode 100644 fish/environments/templates/conda.fish create mode 100644 fish/environments/templates/node.fish create mode 100644 fish/environments/templates/python.fish create mode 100644 fish/environments/templates/ros2.fish create mode 100644 fish/functions/env.fish diff --git a/.gitignore b/.gitignore index 868649e..2407fc5 100644 --- a/.gitignore +++ b/.gitignore @@ -11,6 +11,12 @@ !fish !fish/config.fish +!fish/functions +!fish/functions/env.fish +!fish/environments +!fish/environments/templates +!fish/environments/templates/* + !ranger !ranger/rc.conf diff --git a/fish/config.fish b/fish/config.fish index 7cc622b..206ac66 100644 --- a/fish/config.fish +++ b/fish/config.fish @@ -125,7 +125,10 @@ function check_directory_for_new_repository end function check_and_source_activate - if test -f (pwd)/activate.fish + if test -f (pwd)/.fish/activate.fish + echo (set_color green)"Found .fish/activate.fish in current directory, sourcing..."(set_color normal) + source (pwd)/.fish/activate.fish + else if test -f (pwd)/activate.fish echo (set_color green)"Found activate.fish in current directory, sourcing..."(set_color normal) source (pwd)/activate.fish end diff --git a/fish/environments/templates/README.md b/fish/environments/templates/README.md new file mode 100644 index 0000000..820bd99 --- /dev/null +++ b/fish/environments/templates/README.md @@ -0,0 +1,71 @@ +# Exported Fish Environment: {{ENV_NAME}} + +This directory contains a self-contained Fish shell environment that can be used +without requiring the original Fish configuration. + +## Files Structure +``` +.fish/ +├── activate.fish # Main environment configuration +└── README.md # This file +``` + +## Usage + +### Automatic Activation (Recommended) +The environment will automatically activate when you `cd` into this directory +if your Fish shell is configured with the auto-activation feature that checks +for `.fish/activate.fish`. + +### Manual Activation +To manually activate the environment, run from the project root: +```bash +source ./.fish/activate.fish +``` + +### Deactivation +To deactivate the environment, run: +```bash +env deactivate +``` + +Or simply `cd` to a different directory if using auto-activation. + +## What This Environment Provides + +- Custom prompt showing the environment name +- Environment-specific aliases and functions +- Custom environment variables +- Automatic cleanup when deactivated + +## Requirements + +- Fish shell +- If this is a ROS2 environment: `bass` plugin (`fisher install edc/bass`) + +## Sharing + +This environment is completely self-contained. You can: +1. Copy this directory to another machine +2. Share it with other Fish shell users +3. Version control it with your project (add .fish/ to your repo) + +The environment will work on any system with Fish shell, regardless of whether +they have the original environment management system installed. + +## Auto-activation Setup + +To enable auto-activation for .fish/activate.fish, add this to your Fish config.fish: +```fish +function check_and_source_activate + if test -f (pwd)/.fish/activate.fish + source (pwd)/.fish/activate.fish + elif test -f (pwd)/activate.fish + source (pwd)/activate.fish + end +end + +function cd + builtin cd $argv && check_and_source_activate +end +``` \ No newline at end of file diff --git a/fish/environments/templates/basic.fish b/fish/environments/templates/basic.fish new file mode 100644 index 0000000..d39e634 --- /dev/null +++ b/fish/environments/templates/basic.fish @@ -0,0 +1,49 @@ +# Basic environment with custom prompt +# Environment: {{ENV_NAME}} + +# Check if a previous initialization has occurred +if test -n "$__ENV_INITIALIZED" + echo (set_color yellow)"Environment already initialized"(set_color normal) + return 0 +end + +# Mark as initialized +set -gx __ENV_INITIALIZED "1" +set -gx CURRENT_ENV "{{ENV_NAME}}" + +# Save the original prompt function if it exists +# Only save if we don't already have a backup or if current prompt is not from an environment +if not functions -q __env_orig_prompt + if functions -q fish_prompt + functions -c fish_prompt __env_orig_prompt + else + function __env_orig_prompt + echo -n (whoami)'@'(prompt_hostname)' '(set_color $fish_color_cwd)(prompt_pwd)(set_color normal)'> ' + end + end +else + # If we already have a backup, we're switching environments + # No need to create a new backup +end + +# Define new prompt with environment prefix +function fish_prompt + echo -n (set_color green)'({{ENV_NAME}})'(set_color normal)' ' + __env_orig_prompt +end + +# Add custom environment variables here +# set -gx MY_CUSTOM_VAR "value" + +# Add custom paths here +# fish_add_path /path/to/custom/bin + +# Custom initialization commands +echo (set_color green)"Activated environment: {{ENV_NAME}}"(set_color normal) + +# Optional: Define custom deactivation function +function __env_custom_deactivate + # Add any cleanup commands here + # unset custom variables, restore paths, etc. + echo (set_color blue)"Custom cleanup for {{ENV_NAME}} completed"(set_color normal) +end \ No newline at end of file diff --git a/fish/environments/templates/conda.fish b/fish/environments/templates/conda.fish new file mode 100644 index 0000000..a04e047 --- /dev/null +++ b/fish/environments/templates/conda.fish @@ -0,0 +1,69 @@ +# Conda environment wrapper +# Environment: {{ENV_NAME}} + +# Check if a previous initialization has occurred +if test -n "$__ENV_INITIALIZED" + echo (set_color yellow)"Environment already initialized"(set_color normal) + return 0 +end + +# Mark as initialized +set -gx __ENV_INITIALIZED "1" +set -gx CURRENT_ENV "{{ENV_NAME}}" + +# Save the original prompt function if it exists +# Only save if we don't already have a backup or if current prompt is not from an environment +if not functions -q __env_orig_prompt + if functions -q fish_prompt + functions -c fish_prompt __env_orig_prompt + else + function __env_orig_prompt + echo -n (whoami)'@'(prompt_hostname)' '(set_color $fish_color_cwd)(prompt_pwd)(set_color normal)'> ' + end + end +else + # If we already have a backup, we're switching environments + # No need to create a new backup +end + +# Define new prompt with environment prefix +function fish_prompt + echo -n (set_color blue)'({{ENV_NAME}})'(set_color normal)' ' + __env_orig_prompt +end + +# Conda-specific setup +if type -q conda + # Look for environment.yml or conda-env.yml + if test -f ./environment.yml + echo (set_color blue)"Found environment.yml - use 'conda env create -f environment.yml'"(set_color normal) + end + + if test -f ./conda-env.yml + echo (set_color blue)"Found conda-env.yml - use 'conda env create -f conda-env.yml'"(set_color normal) + end +else + echo (set_color yellow)"Warning: conda not found in PATH"(set_color normal) +end + +# Conda aliases +alias ce="conda env" +alias cel="conda env list" +alias cea="conda env create" +alias cer="conda env remove" +alias ca="conda activate" +alias cd="conda deactivate" +alias ci="conda install" +alias cu="conda update" +alias cr="conda remove" +alias cl="conda list" + +echo (set_color green)"Activated Conda environment: {{ENV_NAME}}"(set_color normal) + +# Custom deactivation function +function __env_custom_deactivate + # Remove Conda aliases + functions -e ce cel cea cer ca cd ci cu cr cl 2>/dev/null + + echo (set_color blue)"Conda environment cleanup completed"(set_color normal) +end \ No newline at end of file diff --git a/fish/environments/templates/node.fish b/fish/environments/templates/node.fish new file mode 100644 index 0000000..5263da6 --- /dev/null +++ b/fish/environments/templates/node.fish @@ -0,0 +1,65 @@ +# Node.js development environment +# Environment: {{ENV_NAME}} + +# Check if a previous initialization has occurred +if test -n "$__ENV_INITIALIZED" + echo (set_color yellow)"Environment already initialized"(set_color normal) + return 0 +end + +# Mark as initialized +set -gx __ENV_INITIALIZED "1" +set -gx CURRENT_ENV "{{ENV_NAME}}" + +# Save the original prompt function if it exists +# Only save if we don't already have a backup or if current prompt is not from an environment +if not functions -q __env_orig_prompt + if functions -q fish_prompt + functions -c fish_prompt __env_orig_prompt + else + function __env_orig_prompt + echo -n (whoami)'@'(prompt_hostname)' '(set_color $fish_color_cwd)(prompt_pwd)(set_color normal)'> ' + end + end +else + # If we already have a backup, we're switching environments + # No need to create a new backup +end + +# Define new prompt with environment prefix +function fish_prompt + echo -n (set_color yellow)'({{ENV_NAME}})'(set_color normal)' ' + __env_orig_prompt +end + +# Node.js specific setup +if test -f ./package.json + echo (set_color blue)"Found package.json - Node.js project detected"(set_color normal) +end + +# Add node_modules/.bin to PATH if it exists +if test -d ./node_modules/.bin + fish_add_path ./node_modules/.bin + echo (set_color blue)"Added ./node_modules/.bin to PATH"(set_color normal) +end + +# Node.js aliases +alias nr="npm run" +alias ni="npm install" +alias nid="npm install --save-dev" +alias nig="npm install --global" +alias nu="npm uninstall" +alias nug="npm uninstall --global" +alias nt="npm test" +alias ns="npm start" +alias nb="npm run build" + +echo (set_color green)"Activated Node.js environment: {{ENV_NAME}}"(set_color normal) + +# Custom deactivation function +function __env_custom_deactivate + # Remove Node.js aliases + functions -e nr ni nid nig nu nug nt ns nb 2>/dev/null + + echo (set_color blue)"Node.js environment cleanup completed"(set_color normal) +end \ No newline at end of file diff --git a/fish/environments/templates/python.fish b/fish/environments/templates/python.fish new file mode 100644 index 0000000..20e43bf --- /dev/null +++ b/fish/environments/templates/python.fish @@ -0,0 +1,73 @@ +# Python development environment +# Environment: {{ENV_NAME}} + +# Check if a previous initialization has occurred +if test -n "$__ENV_INITIALIZED" + echo (set_color yellow)"Environment already initialized"(set_color normal) + return 0 +end + +# Mark as initialized +set -gx __ENV_INITIALIZED "1" +set -gx CURRENT_ENV "{{ENV_NAME}}" + +# Save the original prompt function if it exists +# Only save if we don't already have a backup or if current prompt is not from an environment +if not functions -q __env_orig_prompt + if functions -q fish_prompt + functions -c fish_prompt __env_orig_prompt + else + function __env_orig_prompt + echo -n (whoami)'@'(prompt_hostname)' '(set_color $fish_color_cwd)(prompt_pwd)(set_color normal)'> ' + end + end +else + # If we already have a backup, we're switching environments + # No need to create a new backup +end + +# Define new prompt with environment prefix +function fish_prompt + echo -n (set_color cyan)'({{ENV_NAME}})'(set_color normal)' ' + __env_orig_prompt +end + +# Python-specific environment setup +set -gx PYTHONPATH (pwd) +set -gx VIRTUAL_ENV_DISABLE_PROMPT 1 + +# Check for Python virtual environment +if test -d ./venv + set -gx VIRTUAL_ENV (pwd)/venv + fish_add_path $VIRTUAL_ENV/bin + echo (set_color blue)"Using Python virtual environment: ./venv"(set_color normal) +elif test -d ./.venv + set -gx VIRTUAL_ENV (pwd)/.venv + fish_add_path $VIRTUAL_ENV/bin + echo (set_color blue)"Using Python virtual environment: ./.venv"(set_color normal) +end + +# Python aliases +alias py="python" +alias pip="python -m pip" +alias pytest="python -m pytest" +alias black="python -m black" +alias isort="python -m isort" +alias mypy="python -m mypy" + +echo (set_color green)"Activated Python environment: {{ENV_NAME}}"(set_color normal) + +# Custom deactivation function +function __env_custom_deactivate + # Remove Python-specific paths and variables + if test -n "$VIRTUAL_ENV" + set -e VIRTUAL_ENV + end + set -e PYTHONPATH + set -e VIRTUAL_ENV_DISABLE_PROMPT + + # Remove Python aliases + functions -e py pip pytest black isort mypy 2>/dev/null + + echo (set_color blue)"Python environment cleanup completed"(set_color normal) +end \ No newline at end of file diff --git a/fish/environments/templates/ros2.fish b/fish/environments/templates/ros2.fish new file mode 100644 index 0000000..ce2480c --- /dev/null +++ b/fish/environments/templates/ros2.fish @@ -0,0 +1,71 @@ +# ROS2 development environment (requires distrobox) +# Environment: {{ENV_NAME}} + +# Check if a previous initialization has occurred +if test -n "$__ENV_INITIALIZED" + echo (set_color yellow)"Environment already initialized"(set_color normal) + return 0 +end + +# Mark as initialized +set -gx __ENV_INITIALIZED "1" +set -gx CURRENT_ENV "{{ENV_NAME}}" + +# Check if running inside distrobox +if test -f /run/.containerenv; or test -n "$CONTAINER_ID" + # Source ROS2 setup files using bass + if type -q bass + bass source /opt/ros/jazzy/setup.bash + if test -f ./install/setup.bash + bass source ./install/setup.bash + end + else + echo (set_color red)"Error: bass is required for ROS2 environment. Install with: fisher install edc/bass"(set_color normal) + return 1 + end + + # Set environment variable for the prompt prefix + set -gx ROS2_ACTIVE 1 + + # Save the original prompt function if it exists + # Only save if we don't already have a backup or if current prompt is not from an environment + if not functions -q __env_orig_prompt + if functions -q fish_prompt + functions -c fish_prompt __env_orig_prompt + else + function __env_orig_prompt + echo -n (whoami)'@'(prompt_hostname)' '(set_color $fish_color_cwd)(prompt_pwd)(set_color normal)'> ' + end + end + else + # If we already have a backup, we're switching environments + # No need to create a new backup + end + + # Define new prompt with ROS2 prefix + function fish_prompt + echo -n (set_color magenta)'({{ENV_NAME}})'(set_color normal)' ' + __env_orig_prompt + end + + # ROS2 aliases and functions + alias cb="colcon build" + alias cbs="colcon build --symlink-install" + alias cbt="colcon build --packages-select" + alias ct="colcon test" + alias ctr="colcon test-result" + + echo (set_color green)"Activated ROS2 environment: {{ENV_NAME}}"(set_color normal) +else + echo (set_color red)"This ROS2 environment should only be run inside a distrobox container"(set_color normal) + return 1 +end + +# Custom deactivation function +function __env_custom_deactivate + # Remove ROS2-specific variables and aliases + set -e ROS2_ACTIVE + functions -e cb cbs cbt ct ctr 2>/dev/null + + echo (set_color blue)"ROS2 environment cleanup completed"(set_color normal) +end \ No newline at end of file diff --git a/fish/functions/env.fish b/fish/functions/env.fish new file mode 100644 index 0000000..358f56c --- /dev/null +++ b/fish/functions/env.fish @@ -0,0 +1,485 @@ + +# Environment management system configuration +set -g __ENV_BASE_DIR "$HOME/.config/fish/environments" +set -g __ENV_CONFIGS_DIR "$__ENV_BASE_DIR/configs" +set -g __ENV_TEMPLATES_DIR "$__ENV_BASE_DIR/templates" + +function env -d "Environment management system" + set -l subcommand $argv[1] + + if test -z "$subcommand" + __env_help + return 0 + end + + switch $subcommand + case create new + __env_create $argv[2..] + case list ls + __env_list $argv[2..] + case activate use + __env_activate $argv[2..] + case deactivate exit + __env_deactivate $argv[2..] + case remove rm delete + __env_remove $argv[2..] + case link ln + __env_link $argv[2..] + case info show + __env_info $argv[2..] + case edit + __env_edit $argv[2..] + case copy clone + __env_copy $argv[2..] + case export + __env_export $argv[2..] + case help h --help -h + __env_help $argv[2..] + case "*" + echo (set_color red)"'$subcommand': Unknown subcommand (see env help)"(set_color normal) + return 1 + end +end + +# Internal functions (prefixed with __ to indicate they're private) +function __env_help -d "Show help for env command" + echo (set_color blue)"Environment Management System"(set_color normal) + echo + echo "usage: env [options]" + echo + echo (set_color green)"Subcommands:"(set_color normal) + echo " create, new [template] Create a new environment" + echo " list, ls List all environments" + echo " activate, use Activate an environment" + echo " deactivate, exit Deactivate current environment" + echo " remove, rm Remove an environment" + echo " link, ln [dir] Link environment to directory" + echo " info, show Show environment information" + echo " edit Edit environment configuration" + echo " copy, clone Copy an environment" + echo " export [dir] Export environment to directory" + echo " help, h Show this help" + echo + echo (set_color green)"Templates:"(set_color normal) + __env_list_templates + echo + echo (set_color green)"Examples:"(set_color normal) + echo " env create myproject python # Create Python environment" + echo " env activate myproject # Activate environment" + echo " env link myproject ./myapp # Link to directory" + echo " env export myproject ./myapp # Export self-contained env to directory" + echo " env list # Show all environments" + echo " env deactivate # Deactivate current environment" +end + +function __env_create -d "Create a new environment" + set -l env_name $argv[1] + set -l template_type $argv[2] + + if test -z "$env_name" + echo (set_color red)"environment name required"(set_color normal) + echo "usage: env create [template]" + echo "available templates:" + __env_list_templates + return 1 + end + + if test -z "$template_type" + set template_type basic + end + + set -l env_dir "$__ENV_CONFIGS_DIR/$env_name" + set -l template_file "$__ENV_TEMPLATES_DIR/$template_type.fish" + + if test -d "$env_dir" + echo (set_color yellow)"'$env_name': environment already exists"(set_color normal) + return 1 + end + + if not test -f "$template_file" + echo (set_color red)"'$template_type': template not found"(set_color normal) + echo "Available templates:" + __env_list_templates + return 1 + end + + # Create environment directory + mkdir -p "$env_dir" + + # Copy template and customize + sed "s/{{ENV_NAME}}/$env_name/g" "$template_file" >"$env_dir/activate.fish" + chmod +x "$env_dir/activate.fish" + + # Create environment config file + echo "# Environment: $env_name" >"$env_dir/config.toml" + echo "name = \"$env_name\"" >>"$env_dir/config.toml" + echo "template = \"$template_type\"" >>"$env_dir/config.toml" + echo "created = \"$(date --iso-8601)\"" >>"$env_dir/config.toml" + echo "# Add custom environment variables here" >>"$env_dir/config.toml" + echo "# [env]" >>"$env_dir/config.toml" + echo "# CUSTOM_VAR = \"value\"" >>"$env_dir/config.toml" + + echo (set_color green)"environment '$env_name' ($template_type) created"(set_color normal) + echo "environment directory: $env_dir" + echo "to activate: env activate $env_name" +end + +function __env_list -d "List all available environments" + set -l env_dir "$__ENV_CONFIGS_DIR" + + if not test -d "$env_dir" + echo (set_color yellow)"no environments found"(set_color normal) + return 0 + end + + # Check if directory exists but is empty + set -l env_count (count $env_dir/*) + if test $env_count -eq 0 + echo (set_color yellow)"no environments found"(set_color normal) + return 0 + end + + echo (set_color blue)"available environments:"(set_color normal) + echo + + for env_path in $env_dir/* + if test -d "$env_path" + set env_name (basename "$env_path") + set config_file "$env_path/config.toml" + + if test -f "$config_file" + set template (grep "template =" "$config_file" | cut -d'"' -f2) + set created (grep "created =" "$config_file" | cut -d'"' -f2) + echo (set_color green)" $env_name"(set_color normal)" (template: $template, created: $created)" + else + echo (set_color green)" $env_name"(set_color normal)" (no config found)" + end + end + end + + if test -n "$CURRENT_ENV" + echo + echo (set_color cyan)"'$CURRENT_ENV': currently active"(set_color normal) + end +end + +function __env_activate -d "Activate an environment" + set -l env_name $argv[1] + + if test -z "$env_name" + echo (set_color red)"environment name required"(set_color normal) + echo "usage: env activate " + echo "available environments:" + __env_list + return 1 + end + + set -l env_dir "$__ENV_CONFIGS_DIR/$env_name" + set -l activate_file "$env_dir/activate.fish" + + if not test -f "$activate_file" + echo (set_color red)"'$env_name': environment not found"(set_color normal) + echo "available environments:" + __env_list + return 1 + end + + # Check if another environment is already active + if test -n "$CURRENT_ENV" + echo (set_color yellow)"'$CURRENT_ENV': deactivating current environment"(set_color normal) + __env_deactivate + end + + echo (set_color green)"'$env_name': activating environment"(set_color normal) + source "$activate_file" +end + +function __env_deactivate -d "Deactivate the current environment" + if test -z "$CURRENT_ENV" + echo (set_color yellow)"no environment currently active"(set_color normal) + return 0 + end + + echo (set_color blue)"'$CURRENT_ENV': deactivating environment"(set_color normal) + + # Restore original prompt if it was saved + if functions -q __env_orig_prompt + # Remove current fish_prompt first, then restore the original + functions -e fish_prompt + functions -c __env_orig_prompt fish_prompt + functions -e __env_orig_prompt + end + + # Clear environment-specific variables + set -e CURRENT_ENV + set -e __ENV_INITIALIZED + + # Call custom deactivation function if it exists + if functions -q __env_custom_deactivate + __env_custom_deactivate + functions -e __env_custom_deactivate + end + + echo (set_color green)"environment deactivated"(set_color normal) +end + +function __env_remove -d "Remove an environment" + set -l env_name $argv[1] + + if test -z "$env_name" + echo (set_color red)"environment name required"(set_color normal) + echo "usage: env remove " + return 1 + end + + set -l env_dir "$__ENV_CONFIGS_DIR/$env_name" + + if not test -d "$env_dir" + echo (set_color red)"'$env_name': environment not found"(set_color normal) + return 1 + end + + # Check if this environment is currently active + if test "$CURRENT_ENV" = "$env_name" + echo (set_color yellow)"deactivating environment before removal..."(set_color normal) + __env_deactivate + end + + # Confirm removal + echo (set_color yellow)"are you sure you want to remove environment '$env_name'? [y/N]"(set_color normal) + read -l confirm + + if test "$confirm" = y -o "$confirm" = Y + rm -rf "$env_dir" + echo (set_color green)"Environment '$env_name' removed"(set_color normal) + else + echo (set_color blue)"Removal cancelled"(set_color normal) + end +end + +function __env_link -d "Link current directory to an environment" + set -l env_name $argv[1] + set -l target_dir $argv[2] + + if test -z "$env_name" + echo (set_color red)"environment name required"(set_color normal) + echo "usage: env link [target_directory]" + return 1 + end + + if test -z "$target_dir" + set target_dir (pwd) + end + + set -l env_dir "$__ENV_CONFIGS_DIR/$env_name" + set -l activate_file "$env_dir/activate.fish" + + if not test -f "$activate_file" + echo (set_color red)"environment '$env_name' not found"(set_color normal) + return 1 + end + + set -l target_activate "$target_dir/activate.fish" + + if test -f "$target_activate" + echo (set_color yellow)"Warning: activate.fish already exists in $target_dir"(set_color normal) + echo "Overwrite? [y/N]" + read -l confirm + if test "$confirm" != y -a "$confirm" != Y + echo (set_color blue)"Link cancelled"(set_color normal) + return 0 + end + end + + ln -sf "$activate_file" "$target_activate" + echo (set_color green)"Linked environment '$env_name' to $target_dir"(set_color normal) + echo "The environment will be activated automatically when you cd into this directory" +end + +function __env_info -d "Show environment information" + set -l env_name $argv[1] + + if test -z "$env_name" + echo (set_color red)"environment name required"(set_color normal) + echo "usage: env info " + return 1 + end + + set -l env_dir "$__ENV_CONFIGS_DIR/$env_name" + set -l config_file "$env_dir/config.toml" + + if not test -f "$config_file" + echo (set_color red)"environment '$env_name' not found"(set_color normal) + return 1 + end + + echo (set_color blue)"Environment: $env_name"(set_color normal) + echo "Config file: $config_file" + echo + cat "$config_file" +end + +function __env_edit -d "Edit environment configuration" + set -l env_name $argv[1] + + if test -z "$env_name" + echo (set_color red)"environment name required"(set_color normal) + echo "usage: env edit " + return 1 + end + + set -l env_dir "$__ENV_CONFIGS_DIR/$env_name" + set -l activate_file "$env_dir/activate.fish" + + if not test -f "$activate_file" + echo (set_color red)"environment '$env_name' not found"(set_color normal) + return 1 + end + + $EDITOR "$activate_file" +end + +function __env_copy -d "Copy an environment" + set -l src_name $argv[1] + set -l dest_name $argv[2] + + if test -z "$src_name" -o -z "$dest_name" + echo (set_color red)"Error: Source and destination names required"(set_color normal) + echo "usage: env copy " + return 1 + end + + set -l src_dir "$__ENV_CONFIGS_DIR/$src_name" + set -l dest_dir "$__ENV_CONFIGS_DIR/$dest_name" + + if not test -d "$src_dir" + echo (set_color red)"Error: Source environment '$src_name' not found"(set_color normal) + return 1 + end + + if test -d "$dest_dir" + echo (set_color yellow)"Warning: Destination environment '$dest_name' already exists"(set_color normal) + return 1 + end + + # Copy the entire environment directory + cp -r "$src_dir" "$dest_dir" + + # Update the activate.fish file to use the new name + sed -i "s/$src_name/$dest_name/g" "$dest_dir/activate.fish" + + # Update the config file + sed -i "s/name = \"$src_name\"/name = \"$dest_name\"/" "$dest_dir/config.toml" + sed -i "s/# Environment: $src_name/# Environment: $dest_name/" "$dest_dir/config.toml" + echo "copied_from = \"$src_name\"" >>"$dest_dir/config.toml" + echo "copied_date = \"$(date --iso-8601)\"" >>"$dest_dir/config.toml" + + echo (set_color green)"Copied environment '$src_name' to '$dest_name'"(set_color normal) +end + +function __env_export -d "Export environment to a self-contained directory" + set -l env_name $argv[1] + set -l target_dir $argv[2] + + if test -z "$env_name" + echo (set_color red)"environment name required"(set_color normal) + echo "usage: env export [target_directory]" + return 1 + end + + if test -z "$target_dir" + set target_dir (pwd) + end + + # Convert relative path to absolute path + set target_dir (realpath "$target_dir") + + set -l env_dir "$__ENV_CONFIGS_DIR/$env_name" + set -l activate_source "$env_dir/activate.fish" + + if not test -f "$activate_source" + echo (set_color red)"environment '$env_name' not found"(set_color normal) + return 1 + end + + if not test -d "$target_dir" + echo (set_color red)"Error: Target directory '$target_dir' does not exist"(set_color normal) + return 1 + end + + set -l fish_dir "$target_dir/.fish" + set -l target_activate "$fish_dir/activate.fish" + set -l target_readme "$fish_dir/README.md" + + # Create .fish directory + if not test -d "$fish_dir" + mkdir -p "$fish_dir" + end + + if test -f "$target_activate" + echo (set_color yellow)"Warning: .fish/activate.fish already exists in $target_dir"(set_color normal) + echo "Overwrite? [y/N]" + read -l confirm + if test "$confirm" != y -a "$confirm" != Y + echo (set_color blue)"Export cancelled"(set_color normal) + return 0 + end + end + + # Create a self-contained activate.fish in .fish directory + echo "# Self-contained environment: $env_name" >"$target_activate" + echo "# Exported on: $(date)" >>"$target_activate" + echo "# Original environment from: $env_dir" >>"$target_activate" + echo "" >>"$target_activate" + + # Copy the original activate.fish content but make it self-contained + cat "$activate_source" | sed 's/{{ENV_NAME}}/'"$env_name"'/g' >>"$target_activate" + + # Make it executable + chmod +x "$target_activate" + + # Create a README from template + set -l readme_template "$__ENV_TEMPLATES_DIR/README.md" + if test -f "$readme_template" + sed "s/{{ENV_NAME}}/$env_name/g" "$readme_template" >"$target_readme" + else + echo "# Exported Fish Environment: $env_name" >"$target_readme" + echo "" >>"$target_readme" + echo "README template not found. Please create $readme_template for custom documentation." >>"$target_readme" + end + + echo (set_color green)"Environment '$env_name' exported to: $target_dir"(set_color normal) + echo (set_color blue)"Files created:"(set_color normal) + echo " - $target_activate (main environment)" + echo " - $target_readme (documentation)" + echo + echo (set_color cyan)"To use this environment:"(set_color normal) + echo " cd $target_dir" + echo " source ./.fish/activate.fish" +end + +function __env_list_templates -d "List available templates with descriptions" + set -l template_dir "$__ENV_TEMPLATES_DIR" + + if not test -d "$template_dir" + echo " (no templates found)" + return 0 + end + + for template_file in "$template_dir"/*.fish + if test -f "$template_file" + set template_name (basename "$template_file" .fish) + + # Extract description from first comment line (skip shebang if present) + set description "No description" + set first_comment_line (head -n 5 "$template_file" | grep -E "^#[^!]" | head -n 1 | sed 's/^# *//') + + if test -n "$first_comment_line" + set description "$first_comment_line" + end + + # Format with proper alignment + printf " %-12s %s\n" "$template_name" "$description" + end + end +end