-
-
Save PaulKinlan/c1a82bd8882dc1e72b9df6cabc3484ae to your computer and use it in GitHub Desktop.
Worktree convenience aliases
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| . "/Users/paulkinlan/.deno/env" | |
| # --- Configuration --- | |
| # TODO: Paste your Z-AI API Key here | |
| export ZAI_API_KEY="" | |
| # --- Tmux Workflow Commands --- | |
| c-begin() { | |
| local name=$1 | |
| if [ -z "$name" ]; then | |
| # Generate a rememberable name if none provided | |
| local adjs=("azure" "crimson" "golden" "silent" "brave" "calm" "swift" "wise") | |
| local nouns=("falcon" "tiger" "river" "ocean" "mountain" "wolf" "eagle" "bear") | |
| local rand_adj=${adjs[$RANDOM % ${#adjs[@]}]} | |
| local rand_noun=${nouns[$RANDOM % ${#nouns[@]}]} | |
| name="${rand_adj}-${rand_noun}" | |
| fi | |
| # Check if session exists; if so, attach, otherwise create | |
| if tmux has-session -t "$name" 2>/dev/null; then | |
| echo "Session '$name' already exists. Attaching..." | |
| tmux attach-session -t "$name" | |
| else | |
| tmux new-session -s "$name" | |
| fi | |
| } | |
| c-resume() { | |
| local name=$1 | |
| if [ -z "$name" ]; then | |
| # If no name, attach to the most recently used session | |
| tmux attach-session | |
| else | |
| tmux attach-session -t "$name" | |
| fi | |
| } | |
| c-toggle-billing() { | |
| if [ -n "$ANTHROPIC_API_KEY" ]; then | |
| # Backup the key and unset it to force subscription usage | |
| export ANTHROPIC_API_KEY_BACKUP="$ANTHROPIC_API_KEY" | |
| unset ANTHROPIC_API_KEY | |
| echo "๐ธ ANTHROPIC_API_KEY unset. Using Subscription billing." | |
| elif [ -n "$ANTHROPIC_API_KEY_BACKUP" ]; then | |
| # Restore the key from backup to use API billing | |
| export ANTHROPIC_API_KEY="$ANTHROPIC_API_KEY_BACKUP" | |
| unset ANTHROPIC_API_KEY_BACKUP | |
| echo "๐ณ ANTHROPIC_API_KEY restored. Using API billing." | |
| else | |
| echo "โ No ANTHROPIC_API_KEY or backup found to toggle." | |
| fi | |
| } | |
| c-toggle-ai() { | |
| if [ -z "$ZAI_API_KEY" ]; then | |
| echo "โ ZAI_API_KEY is not set. Please edit your .profile and set it." | |
| return 1 | |
| fi | |
| if ! command -v node >/dev/null 2>&1; then | |
| echo "โ 'node' is required to update settings.json but was not found." | |
| return 1 | |
| fi | |
| local SETTINGS_FILE="$HOME/.claude/settings.json" | |
| # Check if we are currently using Z-AI by looking for the URL in the file | |
| if grep -q "api.z.ai" "$SETTINGS_FILE" 2>/dev/null; then | |
| # Switch to Anthropic (Remove Z-AI config) | |
| node -e " | |
| const fs = require('fs'); | |
| const p = '$SETTINGS_FILE'; | |
| try { | |
| let d = JSON.parse(fs.readFileSync(p, 'utf8')); | |
| if(d.env) { | |
| delete d.env.ANTHROPIC_AUTH_TOKEN; | |
| delete d.env.ANTHROPIC_BASE_URL; | |
| delete d.env.API_TIMEOUT_MS; | |
| } | |
| fs.writeFileSync(p, JSON.stringify(d, null, 2)); | |
| console.log('๐ Switched to Anthropic (Default).'); | |
| } catch(e) { console.error('Error updating settings:', e); } | |
| " | |
| else | |
| # Switch to Z-AI (Inject Z-AI config) | |
| # Pass key via environment variable to node for safety | |
| ZAI_KEY="$ZAI_API_KEY" node -e " | |
| const fs = require('fs'); | |
| const p = '$SETTINGS_FILE'; | |
| try { | |
| let d = {}; | |
| if (fs.existsSync(p)) { d = JSON.parse(fs.readFileSync(p, 'utf8')); } | |
| d.env = d.env || {}; | |
| d.env.ANTHROPIC_AUTH_TOKEN = process.env.ZAI_KEY; | |
| d.env.ANTHROPIC_BASE_URL = 'https://api.z.ai/api/anthropic'; | |
| d.env.API_TIMEOUT_MS = '3000000'; | |
| fs.writeFileSync(p, JSON.stringify(d, null, 2)); | |
| console.log('๐ Switched to Z-AI.'); | |
| } catch(e) { console.error('Error updating settings:', e); } | |
| " | |
| fi | |
| } | |
| c-do() { | |
| local branch="$1" | |
| local prompt="$2" | |
| if [ -z "$TMUX" ]; then | |
| echo "โ ๏ธ You need to be in a tmux session to use c-do." | |
| echo "Run 'c-begin' to start one, or 'c-resume' to join an existing one." | |
| return 1 | |
| fi | |
| if [ -z "$branch" ]; then | |
| echo "Usage: c-do <branch-name> <prompt>" | |
| return 1 | |
| fi | |
| # Check if a *window* already exists (tab style) | |
| # If found, we switch to it rather than splitting the current view | |
| if tmux list-windows -F '#{window_name}' | grep -q "^$branch$"; then | |
| echo "Window '$branch' already exists. Switching to it..." | |
| tmux select-window -t "$branch" | |
| return 0 | |
| fi | |
| # 1. Split the current window horizontally (columns) | |
| # -h: horizontal split (side-by-side) | |
| # -c "$PWD": start in current directory | |
| tmux split-window -h -c "$PWD" | |
| # 2. Rebalance layout to equal columns | |
| tmux select-layout even-horizontal | |
| # 3. Prepare arguments for the new shell | |
| # Escape characters that might break the quoted string | |
| local safe_prompt="${prompt//\"/\\\"}" # Escape double quotes | |
| safe_prompt="${safe_prompt//\$/\\\$}" # Escape dollar signs | |
| safe_prompt="${safe_prompt//\`/\\\`}" # Escape backticks | |
| # 4. Send keys to the new pane | |
| tmux send-keys "source ~/.profile" C-m | |
| tmux send-keys "clear" C-m | |
| tmux send-keys "c-start \"$branch\" \"$safe_prompt\"" C-m | |
| } | |
| # --- Core Logic --- | |
| c-start() { | |
| local branch=$1 | |
| local prompt=$2 | |
| # Create/Switch to worktree FIRST | |
| wt-new "$branch" || return 1 | |
| # Run npm install INSIDE the new worktree | |
| if [ -f ./package.json ]; then | |
| echo "Installing dependencies in new worktree..." | |
| npm i | |
| fi | |
| # Pass $PWD to --add-dir | |
| claude --permission-mode=acceptEdits --model=opus --chrome --add-dir="$PWD" "$prompt" | |
| } | |
| # --- Git Worktree Helpers --- | |
| wt-new() { | |
| local branch=$1 | |
| if [ -z "$branch" ]; then echo "Branch name required"; return 1; fi | |
| if ! git rev-parse --is-inside-work-tree >/dev/null 2>&1; then | |
| echo "Error: Not inside a git repository" | |
| return 1 | |
| fi | |
| git worktree add ".worktrees/$branch" -b "$branch" && cd ".worktrees/$branch" | |
| git config core.hooksPath "$(git rev-parse --show-toplevel)/.githooks" | |
| } | |
| wt-checkout() { | |
| local branch=$1 | |
| if [ -z "$branch" ]; then echo "Branch name required"; return 1; fi | |
| # Try to add worktree from local branch, otherwise create tracking branch | |
| git worktree add ".worktrees/$branch" "$branch" 2>/dev/null || \ | |
| git worktree add ".worktrees/$branch" -b "$branch" --track "origin/$branch" | |
| cd ".worktrees/$branch" | |
| git config core.hooksPath "$(git rev-parse --show-toplevel)/.githooks" | |
| } | |
| wt-sync() { | |
| git fetch origin && git rebase origin/main | |
| } | |
| wt-merge() { | |
| local target=${1:-main} | |
| local current | |
| current=$(git branch --show-current) | |
| if [ -z "$current" ]; then | |
| echo "Error: Not on a branch." | |
| return 1 | |
| fi | |
| if [ "$current" = "$target" ]; then | |
| echo "You are already on '$target'." | |
| return 1 | |
| fi | |
| # Find path of target branch using porcelain output for safety | |
| # Looks for: branch refs/heads/<target> | |
| # And grabs the associated 'worktree' path from the stanza (usually 2 lines up) | |
| local target_path | |
| target_path=$(git worktree list --porcelain | grep -B 2 -F "refs/heads/$target" | grep "^worktree " | cut -d ' ' -f 2-) | |
| if [ -z "$target_path" ]; then | |
| echo "Error: Branch '$target' is not checked out in any worktree." | |
| echo "Cannot merge into a branch that isn't checked out locally." | |
| return 1 | |
| fi | |
| echo "Merging '$current' into '$target' (at $target_path)..." | |
| # Perform the merge in the target's directory | |
| # We use a subshell so we don't actually change our current shell's directory | |
| ( | |
| cd "$target_path" && \ | |
| git merge "$current" | |
| ) | |
| } | |
| wt-remove() { | |
| local branch=$1 | |
| if [ -z "$branch" ]; then echo "Branch name required"; return 1; fi | |
| git worktree remove ".worktrees/$branch" | |
| } | |
| wt-list() { | |
| git worktree list | |
| } | |
| wt-done() { | |
| local branch | |
| branch=$(git branch --show-current) | |
| # Cannot remove current working directory, so move up (to repo root) first | |
| # Assuming standard .worktrees/<branch> structure | |
| cd ../.. | |
| git worktree remove ".worktrees/$branch" | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment