flake-update-20260201
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 # review-<tool>/* - Review sessions with specified tool (cr, copilot, opencode, oco)
62 #
63 # Model selection (optional):
64 # claude:opus/home - Use opus model
65 # oco:sonnet/project - Use sonnet model
66 #
67 # Examples:
68 # ssh host/claude/home # Claude in ~/src/home
69 # ssh host/claude:opus/home # Claude with opus in ~/src/home
70 # ssh host/review-copilot/pr-123="prompt" # Review with copilot
71
72 set -euo pipefail
73
74 # Parse session spec: "claude/home=src/home" -> session="claude/home", dir="src/home"
75 SESSION_SPEC="''${1}"
76 if [[ "$SESSION_SPEC" =~ ^([^=]+)=(.+)$ ]]; then
77 SESSION_NAME="''${BASH_REMATCH[1]}"
78 WORK_DIR="''${BASH_REMATCH[2]}"
79 else
80 SESSION_NAME="$SESSION_SPEC"
81 WORK_DIR=""
82 fi
83
84 # Function to run shpool with a specific command
85 run_with_command() {
86 local session="$1"
87 local cmd="$2"
88 shift 2
89 exec ${pkgs.shpool}/bin/shpool attach -f -c "$cmd $*" "$session"
90 }
91
92 # Function to parse tool:model from prefix
93 # e.g., "claude:opus" -> TOOL=claude, MODEL=opus
94 # "claude" -> TOOL=claude, MODEL=""
95 parse_tool_model() {
96 local prefix="$1"
97 if [[ "$prefix" =~ ^([^:]+):(.+)$ ]]; then
98 TOOL="''${BASH_REMATCH[1]}"
99 MODEL="''${BASH_REMATCH[2]}"
100 else
101 TOOL="$prefix"
102 MODEL=""
103 fi
104 }
105
106 # Function to get default directory from session suffix
107 # e.g., "claude/home" -> "src/home", "claude:opus/home" -> "src/home"
108 get_default_dir() {
109 local session="$1"
110 local prefix="$2"
111 # Remove prefix (which may include :model)
112 local suffix="''${session#$prefix/}"
113 echo "src/$suffix"
114 }
115
116 # Function to run an AI tool session
117 # Args: session_name, tool_prefix, tool_command
118 run_ai_session() {
119 local session="$1"
120 local prefix_pattern="$2"
121 local default_cmd="$3"
122
123 # Extract the prefix part (before first /)
124 local prefix="''${session%%/*}"
125
126 # Parse tool:model from prefix
127 parse_tool_model "$prefix"
128
129 # Use default command if TOOL matches expected pattern
130 local cmd="$default_cmd"
131
132 # Get work directory
133 if [ -z "$WORK_DIR" ]; then
134 WORK_DIR=$(get_default_dir "$session" "$prefix")
135 fi
136
137 run_with_command "$session" "${run-in-dir}" "$cmd" "$WORK_DIR" "$MODEL"
138 }
139
140 # Check session name patterns and run appropriate commands
141 case "$SESSION_NAME" in
142 claude:*/*)
143 # Claude Code with model selection
144 run_ai_session "$SESSION_NAME" "claude" "cr"
145 ;;
146 claude/*)
147 # Claude Code session - run with Vertex AI environment via cr
148 run_ai_session "$SESSION_NAME" "claude" "cr"
149 ;;
150 copilot:*/*)
151 # GitHub Copilot CLI with model selection
152 run_ai_session "$SESSION_NAME" "copilot" "copilot"
153 ;;
154 copilot/*)
155 # GitHub Copilot CLI session
156 run_ai_session "$SESSION_NAME" "copilot" "copilot"
157 ;;
158 opencode:*/*)
159 # OpenCode CLI with model selection
160 run_ai_session "$SESSION_NAME" "opencode" "opencode"
161 ;;
162 opencode/*)
163 # OpenCode CLI session
164 run_ai_session "$SESSION_NAME" "opencode" "opencode"
165 ;;
166 oco:*/*)
167 # OpenCode with Vertex AI and model selection
168 run_ai_session "$SESSION_NAME" "oco" "oco"
169 ;;
170 oco/*)
171 # OpenCode with Vertex AI session
172 run_ai_session "$SESSION_NAME" "oco" "oco"
173 ;;
174 review-cr/*|review-claude/*)
175 # Review with Claude Code (cr)
176 if [ -z "$WORK_DIR" ]; then
177 if ${pkgs.shpool}/bin/shpool list 2>/dev/null | grep -q "^$SESSION_NAME "; then
178 exec ${pkgs.shpool}/bin/shpool attach -f "$SESSION_NAME"
179 else
180 echo "Error: new review sessions require a prompt (use session=prompt syntax)" >&2
181 echo "Example: ssh host/review-cr/pr-123=\"Review https://github.com/...\"" >&2
182 exit 1
183 fi
184 fi
185 run_with_command "$SESSION_NAME" "cr" "$WORK_DIR"
186 ;;
187 review-copilot/*)
188 # Review with GitHub Copilot
189 if [ -z "$WORK_DIR" ]; then
190 if ${pkgs.shpool}/bin/shpool list 2>/dev/null | grep -q "^$SESSION_NAME "; then
191 exec ${pkgs.shpool}/bin/shpool attach -f "$SESSION_NAME"
192 else
193 echo "Error: new review sessions require a prompt (use session=prompt syntax)" >&2
194 echo "Example: ssh host/review-copilot/pr-123=\"Review https://github.com/...\"" >&2
195 exit 1
196 fi
197 fi
198 run_with_command "$SESSION_NAME" "copilot" "$WORK_DIR"
199 ;;
200 review-opencode/*)
201 # Review with OpenCode
202 if [ -z "$WORK_DIR" ]; then
203 if ${pkgs.shpool}/bin/shpool list 2>/dev/null | grep -q "^$SESSION_NAME "; then
204 exec ${pkgs.shpool}/bin/shpool attach -f "$SESSION_NAME"
205 else
206 echo "Error: new review sessions require a prompt (use session=prompt syntax)" >&2
207 echo "Example: ssh host/review-opencode/pr-123=\"Review https://github.com/...\"" >&2
208 exit 1
209 fi
210 fi
211 run_with_command "$SESSION_NAME" "opencode" "$WORK_DIR"
212 ;;
213 review-oco/*)
214 # Review with OpenCode Vertex
215 if [ -z "$WORK_DIR" ]; then
216 if ${pkgs.shpool}/bin/shpool list 2>/dev/null | grep -q "^$SESSION_NAME "; then
217 exec ${pkgs.shpool}/bin/shpool attach -f "$SESSION_NAME"
218 else
219 echo "Error: new review sessions require a prompt (use session=prompt syntax)" >&2
220 echo "Example: ssh host/review-oco/pr-123=\"Review https://github.com/...\"" >&2
221 exit 1
222 fi
223 fi
224 run_with_command "$SESSION_NAME" "oco" "$WORK_DIR"
225 ;;
226 *)
227 # If work directory specified, cd into it before attaching
228 if [ -n "$WORK_DIR" ]; then
229 # Start shell and cd into directory
230 exec ${pkgs.shpool}/bin/shpool attach -f -c "${pkgs.zsh}/bin/zsh -c 'cd $WORK_DIR && exec ${pkgs.zsh}/bin/zsh'" "$SESSION_NAME"
231 else
232 # Default: just attach to session with default shell
233 exec ${pkgs.shpool}/bin/shpool attach -f "$SESSION_NAME"
234 fi
235 ;;
236 esac
237''