auto-update-daily-20260202
  1{ pkgs }:
  2
  3let
  4  # Helper script to run a command in a specific directory with proper environment
  5  # Usage: run-in-dir <command> <directory> [model]
  6  # If model is provided, adds --model <model> to the command
  7  run-in-dir = pkgs.writeShellScript "run-in-dir" ''
  8    CMD="''${1:-}"
  9    DIR="''${2:-}"
 10    MODEL="''${3:-}"
 11
 12    # Set TERM for proper color support
 13    export TERM=xterm-256color
 14
 15    # Command is required
 16    if [ -z "$CMD" ]; then
 17      echo "Error: command argument is required" >&2
 18      exit 1
 19    fi
 20
 21    # Directory is required
 22    if [ -z "$DIR" ]; then
 23      echo "Error: directory argument is required" >&2
 24      exit 1
 25    fi
 26
 27    # Check if directory exists
 28    if [ ! -d "$DIR" ]; then
 29      echo "Error: directory does not exist: $DIR" >&2
 30      exit 1
 31    fi
 32
 33    # Change to specified directory
 34    cd "$DIR"
 35
 36    # Build command with optional model flag
 37    if [ -n "$MODEL" ]; then
 38      FULL_CMD="$CMD --model $MODEL"
 39    else
 40      FULL_CMD="$CMD"
 41    fi
 42
 43    # Execute through zsh with proper environment
 44    # zshenv sets up PATH and other environment variables
 45    exec ${pkgs.zsh}/bin/zsh -c "source ~/.zshenv; exec $FULL_CMD"
 46  '';
 47
 48in
 49
 50pkgs.writeScriptBin "shpool-ssh-wrapper" ''
 51  #!${pkgs.bash}/bin/bash
 52  # Shpool SSH wrapper - automatically runs commands for specific session patterns
 53  #
 54  # Syntax: session-name=directory (e.g., "claude/home=src/home")
 55  #
 56  # Supported session patterns:
 57  #   claude/*        - Claude Code with Vertex AI (cr command)
 58  #   copilot/*       - GitHub Copilot CLI
 59  #   opencode/*      - OpenCode CLI
 60  #   oco/*           - OpenCode with Vertex AI
 61  #   pi/*            - Pi coding agent
 62  #   review-<tool>/* - Review sessions with specified tool (cr, copilot, opencode, oco, pi)
 63  #
 64  # Model selection (optional):
 65  #   claude:opus/home      - Use opus model
 66  #   oco:sonnet/project    - Use sonnet model
 67  #
 68  # Examples:
 69  #   ssh host/claude/home                    # Claude in ~/src/home
 70  #   ssh host/claude:opus/home               # Claude with opus in ~/src/home
 71  #   ssh host/pi/home                        # Pi coding agent in ~/src/home
 72  #   ssh host/pi:sonnet/home                 # Pi with sonnet in ~/src/home
 73  #   ssh host/review-copilot/pr-123="prompt" # Review with copilot
 74
 75  set -euo pipefail
 76
 77  # Parse session spec: "claude/home=src/home" -> session="claude/home", dir="src/home"
 78  SESSION_SPEC="''${1}"
 79  if [[ "$SESSION_SPEC" =~ ^([^=]+)=(.+)$ ]]; then
 80      SESSION_NAME="''${BASH_REMATCH[1]}"
 81      WORK_DIR="''${BASH_REMATCH[2]}"
 82  else
 83      SESSION_NAME="$SESSION_SPEC"
 84      WORK_DIR=""
 85  fi
 86
 87  # Function to run shpool with a specific command
 88  run_with_command() {
 89      local session="$1"
 90      local cmd="$2"
 91      shift 2
 92      exec ${pkgs.shpool}/bin/shpool attach -f -c "$cmd $*" "$session"
 93  }
 94
 95  # Function to parse tool:model from prefix
 96  # e.g., "claude:opus" -> TOOL=claude, MODEL=opus
 97  #       "claude" -> TOOL=claude, MODEL=""
 98  parse_tool_model() {
 99      local prefix="$1"
100      if [[ "$prefix" =~ ^([^:]+):(.+)$ ]]; then
101          TOOL="''${BASH_REMATCH[1]}"
102          MODEL="''${BASH_REMATCH[2]}"
103      else
104          TOOL="$prefix"
105          MODEL=""
106      fi
107  }
108
109  # Function to get default directory from session suffix
110  # e.g., "claude/home" -> "src/home", "claude:opus/home" -> "src/home"
111  get_default_dir() {
112      local session="$1"
113      local prefix="$2"
114      # Remove prefix (which may include :model)
115      local suffix="''${session#$prefix/}"
116      echo "src/$suffix"
117  }
118
119  # Function to run an AI tool session
120  # Args: session_name, tool_prefix, tool_command
121  run_ai_session() {
122      local session="$1"
123      local prefix_pattern="$2"
124      local default_cmd="$3"
125
126      # Extract the prefix part (before first /)
127      local prefix="''${session%%/*}"
128
129      # Parse tool:model from prefix
130      parse_tool_model "$prefix"
131
132      # Use default command if TOOL matches expected pattern
133      local cmd="$default_cmd"
134
135      # Get work directory
136      if [ -z "$WORK_DIR" ]; then
137          WORK_DIR=$(get_default_dir "$session" "$prefix")
138      fi
139
140      run_with_command "$session" "${run-in-dir}" "$cmd" "$WORK_DIR" "$MODEL"
141  }
142
143  # Check session name patterns and run appropriate commands
144  case "$SESSION_NAME" in
145      claude:*/*)
146          # Claude Code with model selection
147          run_ai_session "$SESSION_NAME" "claude" "cr"
148          ;;
149      claude/*)
150          # Claude Code session - run with Vertex AI environment via cr
151          run_ai_session "$SESSION_NAME" "claude" "cr"
152          ;;
153      copilot:*/*)
154          # GitHub Copilot CLI with model selection
155          run_ai_session "$SESSION_NAME" "copilot" "copilot"
156          ;;
157      copilot/*)
158          # GitHub Copilot CLI session
159          run_ai_session "$SESSION_NAME" "copilot" "copilot"
160          ;;
161      opencode:*/*)
162          # OpenCode CLI with model selection
163          run_ai_session "$SESSION_NAME" "opencode" "opencode"
164          ;;
165      opencode/*)
166          # OpenCode CLI session
167          run_ai_session "$SESSION_NAME" "opencode" "opencode"
168          ;;
169      oco:*/*)
170          # OpenCode with Vertex AI and model selection
171          run_ai_session "$SESSION_NAME" "oco" "oco"
172          ;;
173      oco/*)
174          # OpenCode with Vertex AI session
175          run_ai_session "$SESSION_NAME" "oco" "oco"
176          ;;
177      pi:*/*)
178          # Pi coding agent with model selection
179          run_ai_session "$SESSION_NAME" "pi" "pi"
180          ;;
181      pi/*)
182          # Pi coding agent session
183          run_ai_session "$SESSION_NAME" "pi" "pi"
184          ;;
185      review-cr/*|review-claude/*)
186          # Review with Claude Code (cr)
187          if [ -z "$WORK_DIR" ]; then
188              if ${pkgs.shpool}/bin/shpool list 2>/dev/null | grep -q "^$SESSION_NAME "; then
189                  exec ${pkgs.shpool}/bin/shpool attach -f "$SESSION_NAME"
190              else
191                  echo "Error: new review sessions require a prompt (use session=prompt syntax)" >&2
192                  echo "Example: ssh host/review-cr/pr-123=\"Review https://github.com/...\"" >&2
193                  exit 1
194              fi
195          fi
196          run_with_command "$SESSION_NAME" "cr" "$WORK_DIR"
197          ;;
198      review-copilot/*)
199          # Review with GitHub Copilot
200          if [ -z "$WORK_DIR" ]; then
201              if ${pkgs.shpool}/bin/shpool list 2>/dev/null | grep -q "^$SESSION_NAME "; then
202                  exec ${pkgs.shpool}/bin/shpool attach -f "$SESSION_NAME"
203              else
204                  echo "Error: new review sessions require a prompt (use session=prompt syntax)" >&2
205                  echo "Example: ssh host/review-copilot/pr-123=\"Review https://github.com/...\"" >&2
206                  exit 1
207              fi
208          fi
209          run_with_command "$SESSION_NAME" "copilot" "$WORK_DIR"
210          ;;
211      review-opencode/*)
212          # Review with OpenCode
213          if [ -z "$WORK_DIR" ]; then
214              if ${pkgs.shpool}/bin/shpool list 2>/dev/null | grep -q "^$SESSION_NAME "; then
215                  exec ${pkgs.shpool}/bin/shpool attach -f "$SESSION_NAME"
216              else
217                  echo "Error: new review sessions require a prompt (use session=prompt syntax)" >&2
218                  echo "Example: ssh host/review-opencode/pr-123=\"Review https://github.com/...\"" >&2
219                  exit 1
220              fi
221          fi
222          run_with_command "$SESSION_NAME" "opencode" "$WORK_DIR"
223          ;;
224      review-oco/*)
225          # Review with OpenCode Vertex
226          if [ -z "$WORK_DIR" ]; then
227              if ${pkgs.shpool}/bin/shpool list 2>/dev/null | grep -q "^$SESSION_NAME "; then
228                  exec ${pkgs.shpool}/bin/shpool attach -f "$SESSION_NAME"
229              else
230                  echo "Error: new review sessions require a prompt (use session=prompt syntax)" >&2
231                  echo "Example: ssh host/review-oco/pr-123=\"Review https://github.com/...\"" >&2
232                  exit 1
233              fi
234          fi
235          run_with_command "$SESSION_NAME" "oco" "$WORK_DIR"
236          ;;
237      review-pi/*)
238          # Review with Pi coding agent
239          if [ -z "$WORK_DIR" ]; then
240              if ${pkgs.shpool}/bin/shpool list 2>/dev/null | grep -q "^$SESSION_NAME "; then
241                  exec ${pkgs.shpool}/bin/shpool attach -f "$SESSION_NAME"
242              else
243                  echo "Error: new review sessions require a prompt (use session=prompt syntax)" >&2
244                  echo "Example: ssh host/review-pi/pr-123=\"Review https://github.com/...\"" >&2
245                  exit 1
246              fi
247          fi
248          run_with_command "$SESSION_NAME" "pi" "$WORK_DIR"
249          ;;
250      *)
251          # If work directory specified, cd into it before attaching
252          if [ -n "$WORK_DIR" ]; then
253              # Start shell and cd into directory
254              exec ${pkgs.shpool}/bin/shpool attach -f -c "${pkgs.zsh}/bin/zsh -c 'cd $WORK_DIR && exec ${pkgs.zsh}/bin/zsh'" "$SESSION_NAME"
255          else
256              # Default: just attach to session with default shell
257              exec ${pkgs.shpool}/bin/shpool attach -f "$SESSION_NAME"
258          fi
259          ;;
260  esac
261''