aboutsummaryrefslogtreecommitdiff
path: root/dotfiles/common
diff options
context:
space:
mode:
authorCraig Jennings <c@cjennings.net>2026-04-20 09:28:47 -0500
committerCraig Jennings <c@cjennings.net>2026-04-20 09:28:47 -0500
commitc092ed493a9ff4a823e38c4c23e571d27286bd49 (patch)
tree7ad345ceb3c4897bef68b2c6e73d0bdf9a010baa /dotfiles/common
parentfdb68c120e8544ae29006e76fb1165c3310ab631 (diff)
downloadarchsetup-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/common')
-rw-r--r--dotfiles/common/.bashrc.d/aliases.sh54
-rwxr-xr-xdotfiles/common/.local/bin/aix291
-rw-r--r--dotfiles/common/.zshrc.d/aliases.sh54
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)