diff options
| author | Craig Jennings <c@cjennings.net> | 2026-04-20 09:28:47 -0500 |
|---|---|---|
| committer | Craig Jennings <c@cjennings.net> | 2026-04-20 09:28:47 -0500 |
| commit | c092ed493a9ff4a823e38c4c23e571d27286bd49 (patch) | |
| tree | 7ad345ceb3c4897bef68b2c6e73d0bdf9a010baa /dotfiles | |
| parent | fdb68c120e8544ae29006e76fb1165c3310ab631 (diff) | |
| download | archsetup-c092ed493a9ff4a823e38c4c23e571d27286bd49.tar.gz archsetup-c092ed493a9ff4a823e38c4c23e571d27286bd49.zip | |
remove aix and hey: 'ai' lives in claude-templates now
Both the aix script and hey shell function are superseded by the
unified 'ai' launcher in claude-templates (bin/ai, installed via
make install). Single command, three modes, smart in-tmux behavior
so all sessions survive Hyprland crashes.
Per-machine setup adds a step:
cd ~/projects/claude-templates && make install
The orphaned ~/.local/bin/aix symlink should be removed manually on
each machine after pulling this change (rm ~/.local/bin/aix). Stow
re-stow would also clean it up.
Diffstat (limited to 'dotfiles')
| -rw-r--r-- | dotfiles/common/.bashrc.d/aliases.sh | 54 | ||||
| -rwxr-xr-x | dotfiles/common/.local/bin/aix | 291 | ||||
| -rw-r--r-- | dotfiles/common/.zshrc.d/aliases.sh | 54 |
3 files changed, 6 insertions, 393 deletions
diff --git a/dotfiles/common/.bashrc.d/aliases.sh b/dotfiles/common/.bashrc.d/aliases.sh index 6c5519f..7c75d26 100644 --- a/dotfiles/common/.bashrc.d/aliases.sh +++ b/dotfiles/common/.bashrc.d/aliases.sh @@ -78,57 +78,9 @@ alias cc="gcc -Wall -Wstrict-prototypes -Wmissing-prototypes -Wshadow -Wconversi alias gdbx="gdb --batch --ex r --ex bt --ex q --args" # ============================================================================= -# Claude Code -# ============================================================================= -# hey — launch Claude in the current directory (must be a .ai/ template project). -# If the repo has a remote and FETCH_HEAD is older than 10 minutes, fetches -# first so status info is accurate. Auto-pulls when clean, behind, and not -# ahead; warns for anything ambiguous. -hey() { - if [ ! -f .ai/protocols.org ]; then - echo "hey: no .ai/protocols.org in $(pwd) — not a Claude-template project" >&2 - return 1 - fi - - if git rev-parse --is-inside-work-tree >/dev/null 2>&1; then - # Fetch upstream if FETCH_HEAD is stale (>10 min) or missing - local fetch_stale=1 gitdir age - gitdir=$(git rev-parse --git-dir 2>/dev/null) - if [ -f "$gitdir/FETCH_HEAD" ]; then - age=$(( $(date +%s) - $(stat -c %Y "$gitdir/FETCH_HEAD" 2>/dev/null || echo 0) )) - [ "$age" -lt 600 ] && fetch_stale=0 - fi - [ "$fetch_stale" -eq 1 ] && git fetch --quiet 2>/dev/null - - local upstream ahead=0 behind=0 dirty="" - upstream=$(git rev-parse --abbrev-ref --symbolic-full-name @{u} 2>/dev/null) - - if [ -n "$upstream" ]; then - ahead=$(git rev-list --count "$upstream..HEAD" 2>/dev/null || echo 0) - behind=$(git rev-list --count "HEAD..$upstream" 2>/dev/null || echo 0) - - if ! git diff --quiet 2>/dev/null \ - || ! git diff --cached --quiet 2>/dev/null \ - || [ -n "$(git ls-files --others --exclude-standard 2>/dev/null)" ]; then - dirty="dirty" - fi - - # Auto-pull if clean, behind, not ahead - if [ -z "$dirty" ] && [ "${ahead:-0}" -eq 0 ] && [ "${behind:-0}" -gt 0 ]; then - echo "hey: pulling $behind commit(s) from $upstream..." >&2 - git pull --ff-only --quiet - elif [ "${ahead:-0}" -gt 0 ] || [ "${behind:-0}" -gt 0 ] || [ -n "$dirty" ]; then - local parts=() - [ "${ahead:-0}" -gt 0 ] && parts+=("↑$ahead") - [ "${behind:-0}" -gt 0 ] && parts+=("↓$behind") - [ -n "$dirty" ] && parts+=("$dirty") - echo "hey: git ${parts[*]}" >&2 - fi - fi - fi - - claude "Read .ai/protocols.org and follow all instructions." -} +# Claude Code — the 'ai' launcher lives in claude-templates (make install +# symlinks bin/ai into ~/.local/bin). No alias here. +# ============================================================================= # ============================================================================= # Phenomenology RAG (ollama/deepseek) diff --git a/dotfiles/common/.local/bin/aix b/dotfiles/common/.local/bin/aix deleted file mode 100755 index f16af2e..0000000 --- a/dotfiles/common/.local/bin/aix +++ /dev/null @@ -1,291 +0,0 @@ -#!/bin/bash -# Launch tmux session with an LLM in fzf-selected project directories - -SESSION="ai" - -# Switch to or attach to the session -attach_session() { - if [ -n "$TMUX" ]; then - tmux switch-client -t "$SESSION" - else - tmux attach-session -t "$SESSION" - fi -} - -# Dependency checks -for cmd in fzf tmux claude; do - if ! command -v "$cmd" &>/dev/null; then - echo "Error: $cmd is not installed" >&2 - exit 1 - fi -done - -# Claude command — separated for safe editing -AI_CMD="claude" -AI_INSTRUCTIONS='Read .ai/protocols.org and follow all instructions.' - -# Create a window in the session and launch claude; prints window ID -create_window() { - local dir="$1" name="$2" wid - wid=$(tmux new-window -t "$SESSION" -n "$name" -c "$dir" -P -F '#{window_id}') - sleep 0.1 - tmux send-keys -t "$wid" "$AI_CMD \"$AI_INSTRUCTIONS\"" Enter - echo "$wid" -} - -# Read fzf selections into the 'selected' array — strip any " (annotation)" -# suffix so downstream code sees the raw tilde path. -read_selections() { - selected=() - while IFS= read -r line; do - selected+=("${line%% (*}") - done <<<"$1" -} - -# Add a directory to candidates only if it's a Claude-template project -# (has .ai/protocols.org). New projects need manual setup outside aix. -maybe_add_candidate() { - local dir="$1" - [ -f "$dir/.ai/protocols.org" ] && candidates+=("~/${dir#"$HOME"/}") -} - -# Build candidate directory list -build_candidates() { - candidates=() - maybe_add_candidate "$HOME/.emacs.d" - if [ -d "$HOME/code" ]; then - while IFS= read -r d; do - maybe_add_candidate "$d" - done < <(find "$HOME/code" -maxdepth 1 -mindepth 1 -type d | sort) - fi - if [ -d "$HOME/projects" ]; then - while IFS= read -r d; do - maybe_add_candidate "$d" - done < <(find "$HOME/projects" -maxdepth 1 -mindepth 1 -type d | sort) - fi -} - -# Fetch all candidate repos in parallel (capped concurrency). -# Blocking call — synchronous relative to the rest of the script. -fetch_candidates() { - local max=6 running=0 dir - for c in "${candidates[@]}"; do - dir="${c/#\~/$HOME}" - [ -d "$dir/.git" ] || continue - git -C "$dir" fetch --quiet 2>/dev/null & - ((running++)) - if ((running >= max)); then - wait - running=0 - fi - done - wait -} - -# Produce a git-status indicator string for a directory, e.g. -# "(↑1 ↓3 dirty)" or "(no upstream)". Empty string if clean. -git_status_indicator() { - local dir="$1" upstream ahead behind parts=() - [ -d "$dir/.git" ] || return - - upstream=$(git -C "$dir" rev-parse --abbrev-ref --symbolic-full-name @{u} 2>/dev/null) - if [ -n "$upstream" ]; then - ahead=$(git -C "$dir" rev-list --count "$upstream..HEAD" 2>/dev/null || echo 0) - behind=$(git -C "$dir" rev-list --count "HEAD..$upstream" 2>/dev/null || echo 0) - [ "${ahead:-0}" -gt 0 ] 2>/dev/null && parts+=("↑$ahead") - [ "${behind:-0}" -gt 0 ] 2>/dev/null && parts+=("↓$behind") - else - parts+=("no upstream") - fi - - if ! git -C "$dir" diff --quiet 2>/dev/null \ - || ! git -C "$dir" diff --cached --quiet 2>/dev/null \ - || [ -n "$(git -C "$dir" ls-files --others --exclude-standard 2>/dev/null)" ]; then - parts+=("dirty") - fi - - if [ ${#parts[@]} -gt 0 ]; then - local IFS=' ' - printf ' (%s)' "${parts[*]}" - fi -} - -# Annotate the 'candidates' array with git-status indicators -annotate_candidates() { - local dir status annotated=() - for c in "${candidates[@]}"; do - dir="${c/#\~/$HOME}" - status=$(git_status_indicator "$dir") - annotated+=("${c}${status}") - done - candidates=("${annotated[@]}") -} - -# Auto-pull a directory if its working tree is clean, upstream is set, -# the branch is behind (not ahead, not diverged). No-op otherwise. -auto_pull_if_clean() { - local dir="$1" upstream ahead behind - [ -d "$dir/.git" ] || return - - # Dirty? Skip. - if ! git -C "$dir" diff --quiet 2>/dev/null \ - || ! git -C "$dir" diff --cached --quiet 2>/dev/null \ - || [ -n "$(git -C "$dir" ls-files --others --exclude-standard 2>/dev/null)" ]; then - return - fi - - upstream=$(git -C "$dir" rev-parse --abbrev-ref --symbolic-full-name @{u} 2>/dev/null) - [ -z "$upstream" ] && return - - ahead=$(git -C "$dir" rev-list --count "$upstream..HEAD" 2>/dev/null || echo 0) - [ "${ahead:-0}" -gt 0 ] 2>/dev/null && return - - behind=$(git -C "$dir" rev-list --count "HEAD..$upstream" 2>/dev/null || echo 0) - [ "${behind:-0}" -eq 0 ] 2>/dev/null && return - - git -C "$dir" pull --ff-only --quiet 2>/dev/null -} - -# Sort windows: non-project windows at base-index, projects alphabetically after -sort_windows() { - local windows others projects base_idx project_names - base_idx=$(tmux show-option -gv base-index 2>/dev/null || echo 0) - windows=$(tmux list-windows -t "$SESSION" -F '#{window_name}'$'\t''#{window_id}') - - # Build list of known project basenames from candidate directories - build_candidates - project_names="" - for c in "${candidates[@]}"; do - project_names+="$(basename "${c/#\~/$HOME}")"$'\n' - done - - # Classify: windows matching a project basename are projects, rest are non-project - others="" - projects="" - while IFS=$'\t' read -r wname wid; do - [ -z "$wname" ] && continue - if echo "$project_names" | grep -qxF "$wname"; then - projects+="${wname}"$'\t'"${wid}"$'\n' - else - others+="${wname}"$'\t'"${wid}"$'\n' - fi - done <<<"$windows" - others=$(echo -n "$others" | sort -t$'\t' -k1,1f) - projects=$(echo -n "$projects" | sort -t$'\t' -k1,1f) - - local all - all=$(printf '%s\n' "$others" "$projects" | sed '/^$/d') - - # Move all to high temp indices to avoid collisions - local i=900 - while IFS=$'\t' read -r _name wid; do - tmux move-window -s "$wid" -t "$SESSION:$i" - ((i++)) - done <<<"$all" - - # Place non-project windows at base-index, projects contiguously after - i=$base_idx - if [ -n "$others" ]; then - while IFS=$'\t' read -r _name wid; do - tmux move-window -s "$wid" -t "$SESSION:$i" - ((i++)) - done <<<"$others" - fi - if [ -n "$projects" ]; then - while IFS=$'\t' read -r _name wid; do - tmux move-window -s "$wid" -t "$SESSION:$i" - ((i++)) - done <<<"$projects" - fi -} - -# Attach directly to existing session -if [ "$1" = "--attach" ]; then - if ! tmux has-session -t "$SESSION" 2>/dev/null; then - echo "No $SESSION session to attach to." >&2 - exit 1 - fi - sort_windows - attach_session - exit 0 -fi - -# If session exists, add new windows to it -if tmux has-session -t "$SESSION" 2>/dev/null; then - # Get existing window names to filter duplicates - existing=$(tmux list-windows -t "$SESSION" -F '#{window_name}') - - build_candidates - - # Filter out candidates that already have a window open - filtered=() - for c in "${candidates[@]}"; do - name="$(basename "${c/#\~/$HOME}")" - if ! echo "$existing" | grep -qxF "$name"; then - filtered+=("$c") - fi - done - - if [ ${#filtered[@]} -eq 0 ]; then - echo "All projects already have windows open." - else - # Annotate filtered list with git-status indicators (fetch first) - candidates=("${filtered[@]}") - echo "Fetching remotes..." >&2 - fetch_candidates - annotate_candidates - - selections=$(printf '%s\n' "${candidates[@]}" | fzf --multi --height=70% --reverse) - - if [ -n "$selections" ]; then - read_selections "$selections" - - first_wid="" - for entry in "${selected[@]}"; do - dir="${entry/#\~/$HOME}" - name="$(basename "$dir")" - auto_pull_if_clean "$dir" - wid=$(create_window "$dir" "$name") - [ -z "$first_wid" ] && first_wid="$wid" - done - fi - fi - - sort_windows - [ -n "$first_wid" ] && tmux select-window -t "$first_wid" - attach_session - exit 0 -fi - -# New session: select projects and create session -build_candidates - -echo "Fetching remotes..." >&2 -fetch_candidates -annotate_candidates - -selections=$(printf '%s\n' "${candidates[@]}" | fzf --multi --height=70% --reverse) -[ -z "$selections" ] && exit 0 - -read_selections "$selections" - -# Create session with first selection -first="${selected[0]}" -dir="${first/#\~/$HOME}" -name="$(basename "$dir")" -auto_pull_if_clean "$dir" -first_wid=$(tmux new-session -d -s "$SESSION" -n "$name" -c "$dir" -P -F '#{window_id}') -tmux send-keys -t "$first_wid" "$AI_CMD \"$AI_INSTRUCTIONS\"" Enter - -# Create remaining windows -for entry in "${selected[@]:1}"; do - dir="${entry/#\~/$HOME}" - name="$(basename "$dir")" - auto_pull_if_clean "$dir" - create_window "$dir" "$name" > /dev/null -done - -# Sort windows and select first project -sort_windows -tmux select-window -t "$first_wid" -attach_session diff --git a/dotfiles/common/.zshrc.d/aliases.sh b/dotfiles/common/.zshrc.d/aliases.sh index 6c5519f..7c75d26 100644 --- a/dotfiles/common/.zshrc.d/aliases.sh +++ b/dotfiles/common/.zshrc.d/aliases.sh @@ -78,57 +78,9 @@ alias cc="gcc -Wall -Wstrict-prototypes -Wmissing-prototypes -Wshadow -Wconversi alias gdbx="gdb --batch --ex r --ex bt --ex q --args" # ============================================================================= -# Claude Code -# ============================================================================= -# hey — launch Claude in the current directory (must be a .ai/ template project). -# If the repo has a remote and FETCH_HEAD is older than 10 minutes, fetches -# first so status info is accurate. Auto-pulls when clean, behind, and not -# ahead; warns for anything ambiguous. -hey() { - if [ ! -f .ai/protocols.org ]; then - echo "hey: no .ai/protocols.org in $(pwd) — not a Claude-template project" >&2 - return 1 - fi - - if git rev-parse --is-inside-work-tree >/dev/null 2>&1; then - # Fetch upstream if FETCH_HEAD is stale (>10 min) or missing - local fetch_stale=1 gitdir age - gitdir=$(git rev-parse --git-dir 2>/dev/null) - if [ -f "$gitdir/FETCH_HEAD" ]; then - age=$(( $(date +%s) - $(stat -c %Y "$gitdir/FETCH_HEAD" 2>/dev/null || echo 0) )) - [ "$age" -lt 600 ] && fetch_stale=0 - fi - [ "$fetch_stale" -eq 1 ] && git fetch --quiet 2>/dev/null - - local upstream ahead=0 behind=0 dirty="" - upstream=$(git rev-parse --abbrev-ref --symbolic-full-name @{u} 2>/dev/null) - - if [ -n "$upstream" ]; then - ahead=$(git rev-list --count "$upstream..HEAD" 2>/dev/null || echo 0) - behind=$(git rev-list --count "HEAD..$upstream" 2>/dev/null || echo 0) - - if ! git diff --quiet 2>/dev/null \ - || ! git diff --cached --quiet 2>/dev/null \ - || [ -n "$(git ls-files --others --exclude-standard 2>/dev/null)" ]; then - dirty="dirty" - fi - - # Auto-pull if clean, behind, not ahead - if [ -z "$dirty" ] && [ "${ahead:-0}" -eq 0 ] && [ "${behind:-0}" -gt 0 ]; then - echo "hey: pulling $behind commit(s) from $upstream..." >&2 - git pull --ff-only --quiet - elif [ "${ahead:-0}" -gt 0 ] || [ "${behind:-0}" -gt 0 ] || [ -n "$dirty" ]; then - local parts=() - [ "${ahead:-0}" -gt 0 ] && parts+=("↑$ahead") - [ "${behind:-0}" -gt 0 ] && parts+=("↓$behind") - [ -n "$dirty" ] && parts+=("$dirty") - echo "hey: git ${parts[*]}" >&2 - fi - fi - fi - - claude "Read .ai/protocols.org and follow all instructions." -} +# Claude Code — the 'ai' launcher lives in claude-templates (make install +# symlinks bin/ai into ~/.local/bin). No alias here. +# ============================================================================= # ============================================================================= # Phenomenology RAG (ollama/deepseek) |
