From ecc39344caa347a2d3e76468b7d205818bad2da4 Mon Sep 17 00:00:00 2001 From: David Tomaschik Date: Thu, 2 Apr 2026 16:37:51 -0700 Subject: [PATCH] Improve gemini context --- dotfiles/config/fish/conf.d/gemini.fish | 95 ++++++++++++++++++++ dotfiles/zshrc.d/gemini.zsh | 112 +++++++++++++++++++----- 2 files changed, 185 insertions(+), 22 deletions(-) create mode 100644 dotfiles/config/fish/conf.d/gemini.fish diff --git a/dotfiles/config/fish/conf.d/gemini.fish b/dotfiles/config/fish/conf.d/gemini.fish new file mode 100644 index 0000000..2b387fd --- /dev/null +++ b/dotfiles/config/fish/conf.d/gemini.fish @@ -0,0 +1,95 @@ +# Bridge to Gemini CLI Context Management (Zsh-backed) +# This allows using the Zsh implementation from Fish to avoid dual maintenance. + +# Ensure the base context directory exists +if set -q XDG_CONFIG_HOME + set -gx GEMINI_CONTEXT_DIR $XDG_CONFIG_HOME/gemini +else + set -gx GEMINI_CONTEXT_DIR $HOME/.config/gemini +end +mkdir -p "$GEMINI_CONTEXT_DIR" + +# Path to the source of truth +set -l gemini_zsh_script "$HOME/.skel/dotfiles/zshrc.d/gemini.zsh" + +function _gemini_context_bridge + # Check if Zsh script exists + if not test -f "$gemini_zsh_script" + echo "Error: Gemini Zsh script not found at $gemini_zsh_script" + return 1 + end + + # We use a temporary file to reliably pass the environment variable back + set -l env_file (mktemp -t gemini_context.XXXXXX) + + # Run zsh with -e (exit on error): + # 1. Source the context manager + # 2. Run the requested command with arguments + # 3. Write the resulting GEMINI_CLI_HOME to the temp file + command zsh -ec " + source '$gemini_zsh_script' + $argv + echo \"\$GEMINI_CLI_HOME\" > '$env_file' + " + set -l zsh_status $status + + # Only sync environment and clean up on success + if test $zsh_status -eq 0 + set -l new_home (cat $env_file | string trim) + if test -n "$new_home" + set -gx GEMINI_CLI_HOME "$new_home" + else + set -e GEMINI_CLI_HOME + end + end + + rm -f $env_file + return $zsh_status +end + +# Wrapper functions +function gemini-context-use + _gemini_context_bridge "gemini-context-use $argv" +end + +function gemini-context-create + _gemini_context_bridge "gemini-context-create $argv" +end + +function gemini-context-list + _gemini_context_bridge "gemini-context-list $argv" +end + +function gemini-context-delete + _gemini_context_bridge "gemini-context-delete $argv" +end + +function gemini-context-rename + _gemini_context_bridge "gemini-context-rename $argv" +end + +function gemini-context-edit + _gemini_context_bridge "gemini-context-edit $argv" +end + +function gemini-context-current + _gemini_context_bridge "gemini-context-current $argv" +end + +function gemini-context-unset + _gemini_context_bridge "gemini-context-unset $argv" +end + +# Aliases +alias gemctx='gemini-context-use' + +# Completion +function _gemini_context_list + zsh -c "source '$gemini_zsh_script'; _gemini_context_list_internal" +end + +complete -c gemini-context-use -f -a "(_gemini_context_list)" +complete -c gemctx -f -a "(_gemini_context_list)" +complete -c gemini-context-edit -f -a "(_gemini_context_list)" +complete -c gemini-context-delete -f -a "(_gemini_context_list)" +complete -c gemini-context-rename -f -a "(_gemini_context_list)" diff --git a/dotfiles/zshrc.d/gemini.zsh b/dotfiles/zshrc.d/gemini.zsh index e3650b9..6fd3176 100644 --- a/dotfiles/zshrc.d/gemini.zsh +++ b/dotfiles/zshrc.d/gemini.zsh @@ -7,7 +7,7 @@ export GEMINI_CONTEXT_DIR="${XDG_CONFIG_HOME:-$HOME/.config}/gemini" mkdir -p "$GEMINI_CONTEXT_DIR" # Template settings file -GEMINI_TEMPLATE_SETTINGS="${HOME}/.skel/dotfiles/config/gemini/settings.json" +GEMINI_TEMPLATE_SETTINGS="${GEMINI_CONTEXT_DIR}/settings.json" # Create a new Gemini context gemini-context-create() { @@ -19,8 +19,13 @@ gemini-context-create() { local context_name="$1" local context_path="$GEMINI_CONTEXT_DIR/$context_name" - if [[ "$context_name" =~ [/] ]]; then - echo "Error: Context name cannot contain slashes." + if [[ "$context_name" =~ [^a-zA-Z0-9_-] ]]; then + echo "Error: Context name should only contain alphanumeric characters, underscores, or dashes." + return 1 + fi + + if [[ "$context_name" == .* ]]; then + echo "Error: Context name cannot start with a dot." return 1 fi @@ -42,25 +47,90 @@ gemini-context-create() { echo "Context '$context_name' created." } -# List available Gemini contexts +# List available Gemini contexts (directories only, containing .gemini) _gemini_context_list_internal() { - command ls "$GEMINI_CONTEXT_DIR" 2>/dev/null + local contexts + # Use Zsh globbing: find directories containing a .gemini subdir + contexts=($GEMINI_CONTEXT_DIR/*/.gemini(N/:h:t)) + [[ ${#contexts} -gt 0 ]] && print -l "${contexts[@]}" } gemini-context-list() { echo "Available Gemini contexts:" - local contexts=$(_gemini_context_list_internal) - if [ -z "$contexts" ]; then + local contexts=($(_gemini_context_list_internal)) + if [ ${#contexts} -eq 0 ]; then echo " (No contexts found)" return fi - for context in $contexts; do - if [ -d "$GEMINI_CONTEXT_DIR/$context" ]; then - echo " $context" + for context in "${contexts[@]}"; do + if [ "$GEMINI_CLI_HOME" = "$GEMINI_CONTEXT_DIR/$context" ]; then + echo " * $context (active)" + else + echo " $context" fi done } +# Delete a Gemini context +gemini-context-delete() { + local context_name + if [ -z "$1" ]; then + if command -v fzf >/dev/null; then + context_name=$(_gemini_context_list_internal | fzf --prompt="Select Gemini Context to DELETE: ") + [[ -z "$context_name" ]] && return 1 + else + echo "Usage: gemini-context-delete " + return 1 + fi + else + context_name="$1" + fi + + local context_path="$GEMINI_CONTEXT_DIR/$context_name" + if [ ! -d "$context_path" ]; then + echo "Error: Context '$context_name' not found." + return 1 + fi + + if read -q "confirm?Are you sure you want to delete context '$context_name'? [y/N] "; then + echo + rm -rf "$context_path" + echo "Context '$context_name' deleted." + if [ "$GEMINI_CLI_HOME" = "$context_path" ]; then + gemini-context-unset + fi + else + echo "\nDeletion cancelled." + fi +} + +# Rename a Gemini context +gemini-context-rename() { + if [ -z "$1" ] || [ -z "$2" ]; then + echo "Usage: gemini-context-rename " + return 1 + fi + + local old_path="$GEMINI_CONTEXT_DIR/$1" + local new_path="$GEMINI_CONTEXT_DIR/$2" + + if [ ! -d "$old_path" ]; then + echo "Error: Source context '$1' not found." + return 1 + fi + + if [ -d "$new_path" ]; then + echo "Error: Destination context '$2' already exists." + return 1 + fi + + mv "$old_path" "$new_path" + echo "Context '$1' renamed to '$2'." + if [ "$GEMINI_CLI_HOME" = "$old_path" ]; then + gemini-context-use -q "$2" + fi +} + # Use a specific Gemini context gemini-context-use() { local context_name @@ -89,14 +159,13 @@ gemini-context-use() { local context_path="$GEMINI_CONTEXT_DIR/$context_name" - if [ ! -d "$context_path" ]; then - [[ "$quiet" -eq 0 ]] && echo "Error: Context '$context_name' not found at $context_path" - [[ "$quiet" -eq 0 ]] && gemini-context-list + if [ ! -d "$context_path/.gemini" ]; then + [[ "$quiet" -eq 0 ]] && echo "Error: '$context_name' is not a valid Gemini context (missing .gemini directory)." return 1 fi export GEMINI_CLI_HOME="$context_path" - [[ "$quiet" -eq 0 ]] && echo "Switched to Gemini context '$context_name' (GEMINI_CLI_HOME=$GEMINI_CLI_HOME)" + [[ "$quiet" -eq 0 ]] && echo "Switched to Gemini context '$context_name'" } # Edit a context's settings @@ -136,13 +205,12 @@ gemini-context-edit() { # Show the current Gemini context gemini-context-current() { if [ -n "$GEMINI_CLI_HOME" ]; then - local current_context=$(basename "$GEMINI_CLI_HOME") + local current_context=${GEMINI_CLI_HOME:t} if [ "$GEMINI_CONTEXT_DIR/$current_context" = "$GEMINI_CLI_HOME" ]; then echo "Current Gemini context: $current_context" else - echo "Current Gemini context: Custom" + echo "Current Gemini context: Custom ($GEMINI_CLI_HOME)" fi - echo "GEMINI_CLI_HOME=$GEMINI_CLI_HOME" else echo "No Gemini context set, using default." fi @@ -157,12 +225,12 @@ gemini-context-unset() { # Alias for gemini-context-use alias gemctx='gemini-context-use' -# Zsh Completion for gemini-context-use and gemctx +# Zsh Completion _gemini_contexts() { - local contexts + local -a contexts contexts=($(_gemini_context_list_internal)) _describe 'gemini contexts' contexts } -compdef _gemini_contexts gemini-context-use -compdef _gemini_contexts gemctx -compdef _gemini_contexts gemini-context-edit + +compdef _gemini_contexts gemini-context-use gemctx gemini-context-edit gemini-context-delete gemini-context-rename +