case "$tool" in
Bash)
# Deny git checkout/switch.
- if $GUIX_BIN/grep -q '"command".*"git checkout"'; then
+ if $GUIX_BIN/grep -q '"command".*"git checkout"'
+ then
$GUIX_BIN/echo \
'{"hookSpecificOutput": {' \
' "hookEventName": "PreToolUse",' \
'}}'
exit 0
fi
- if $GUIX_BIN/grep -q '"command".*"git switch"'; then
+ if $GUIX_BIN/grep -q '"command".*"git switch"'
+ then
$GUIX_BIN/echo \
'{"hookSpecificOutput": {' \
' "hookEventName": "PreToolUse",' \
branch --show-current 2>/dev/null)
if $HOME/.guix-home/profile/bin/git -C "$CLAUDE_PROJECT_DIR" \
- merge-base --is-ancestor "$branch" main 2>/dev/null; then
+ merge-base --is-ancestor "$branch" main 2>/dev/null
+ then
exit 0
fi
# unless already in this session's worktree.
# Let EnterWorktree pass through.
-if $GUIX_BIN/grep -q '"tool_name"[[:space:]]*:[[:space:]]*"EnterWorktree"'; then
+if $GUIX_BIN/grep -q '"tool_name"[[:space:]]*:[[:space:]]*"EnterWorktree"'
+then
exit 0
fi
# Allow if already is this session's worktree.
if $GUIX_BIN/echo "$PWD" | $GUIX_BIN/grep --quiet \
- "/worktrees/${CLAUDE_CODE_SESSION_ID}"; then
+ "/worktrees/${CLAUDE_CODE_SESSION_ID}"
+then
exit 0
fi
MAKEFILE="$CLAUDE_PROJECT_DIR/Makefile"
if [ -z "$($HOME/.guix-home/profile/bin/git -C "$CLAUDE_PROJECT_DIR" \
- status --porcelain --untracked-files=no)" ]; then
+ status --porcelain --untracked-files=no)" ]
+then
$GUIX_BIN/echo '{"systemMessage":"🟢 Working tree clean."}'
exit 0
fi
QUEUE_FILE="$CLAUDE_PROJECT_DIR/.claude/.build-queue.$CLAUDE_CODE_SESSION_ID"
# No queue file yet.
-if [ ! -f "$QUEUE_FILE" ]; then
+if [ ! -f "$QUEUE_FILE" ]
+then
"$CLAUDE_PROJECT_DIR/.claude/scripts/ask-build-queue"
exit 2
fi
# Build the first target if the queue is non-empty.
-if [ -s "$QUEUE_FILE" ]; then
+if [ -s "$QUEUE_FILE" ]
+then
target=$($GUIX_BIN/head --lines=1 "$QUEUE_FILE")
- if ! $GUIX_BIN/grep --quiet "^${target}:" "$MAKEFILE"; then
+ if ! $GUIX_BIN/grep --quiet "^${target}:" "$MAKEFILE"
+ then
$GUIX_BIN/sed --in-place '1d' "$QUEUE_FILE"
exec 1>&2
# guix-check — Checks to be applied after every edit of a guile file. If a check
# fails, the script exits with status code 2 to make Claude Code fix the issues.
-file=$($GUIX_BIN/sed --regexp-extended --quiet \
- 's/.*"file_path"\s*:\s*"([^"]*)".*/\1/p')
+file=$($GUIX_BIN/sed --quiet \
+ 's/.*"file_path"\s*:\s*"\([^"]*\)".*/\1/p')
[ -z "$file" ] && exit 0
-if ! $GUIX_BIN/guix style --whole-file "$file"; then
+if ! $GUIX_BIN/guix style --whole-file "$file"
+then
exec 1>&2
$GUIX_BIN/echo "❌ guix style failed on $file."
exit 2
fi
-if $GUIX_BIN/grep --quiet '^unbalanced$' "$file"; then
+if $GUIX_BIN/grep --quiet '^unbalanced$' "$file"
+then
exec 1>&2
$GUIX_BIN/echo "❌ guix style added \`unbalanced\` token to $file for each"
$GUIX_BIN/echo "extra closing parenthesis. Remove these tokens AND fix the"
exit 2
fi
-long_lines=$($GUIX_BIN/grep --line-number --extended-regexp '^.{81,}$' "$file")
-if [ -n "$long_lines" ]; then
+long_lines=$($GUIX_BIN/grep --line-number '^.\{81,\}$' "$file")
+if [ -n "$long_lines" ]
+then
exec 1>&2
$GUIX_BIN/echo "❌ Lines exceed 80 characters in $file:"
$GUIX_BIN/echo ""
# guild exits non-zero for fatal errors but still exits 0 for warnings so we
# check both.
-if [ $? -ne 0 ] || $GUIX_BIN/echo "$out" | $GUIX_BIN/grep --quiet warning; then
+if [ $? -ne 0 ] || $GUIX_BIN/echo "$out" | $GUIX_BIN/grep --quiet warning
+then
exec 1>&2
$GUIX_BIN/echo "❌ Compilation issues in $file:"
$GUIX_BIN/echo ""
# Copyright (c) 2026 Jakub Czajka <jakub@ekhem.eu.org>
# License: GPL-3.0 or later.
#
-# notify — send a desktop notification for Claude Code events.
+# notify — Send a desktop notification for Claude Code events.
type=$(sed -n \
's/.*"notification_type":"\([^"]*\)".*/\1/p')
#
# status-line - Display current session/worktree in the Claude Code prompt.
-if echo "$PWD" | grep -q '/worktrees/'; then
+if echo "$PWD" | grep -q '/worktrees/'
+then
echo "🌿 $CLAUDE_CODE_SESSION_ID"
else
branch=$(git -C "$PWD" branch --show-current 2>/dev/null)
# Print space-separated Makefile targets whose recipe matches the
# branch-appropriate guix command: reconfigure on main, build elsewhere.
build_targets() {
- if [ "$2" = "main" ]; then
+ if [ "$2" = "main" ]
+ then
pattern="guix.*reconfigure"
else
pattern="guix.*build"
--format='%(refname:short)' refs/heads/ \
| $GUIX_BIN/grep -v '^main$')
-if [ -z "$branches" ]; then
+if [ -z "$branches" ]
+then
$GUIX_BIN/echo "No other branches."
exit 0
fi
$GUIX_BIN/echo '|--------|---------|---------|'
# One row per branch.
-echo "$branches" | while read -r b; do
+echo "$branches" | while read -r b
+do
count=$($HOME/.guix-home/profile/bin/git \
-C "$CLAUDE_PROJECT_DIR" rev-list --count \
main.."$b" 2>/dev/null)
- if [ -z "$count" ] || [ "$count" -eq 0 ]; then
+ if [ -z "$count" ] || [ "$count" -eq 0 ]
+ then
summary="(even with main)"
else
summary=$(format_commits "$b")
set -eu
branch="${1:-}"
-if [ -z "$branch" ]; then
+if [ -z "$branch" ]
+then
echo "Usage: merge-worktree <branch-name>" >&2
exit 1
fi
# Validate branch.
if ! $HOME/.guix-home/profile/bin/git -C "$PROJECT_DIR" rev-parse \
- --verify "$branch" >/dev/null 2>&1; then
+ --verify "$branch" >/dev/null 2>&1
+then
echo "Error: Branch '$branch' does not exist." >&2
exit 1
fi
-if [ "$branch" = "main" ]; then
+if [ "$branch" = "main" ]
+then
echo "Error: Cannot merge main into itself." >&2
exit 1
fi
# Check main worktree is clean.
if [ -n "$($HOME/.guix-home/profile/bin/git -C "$PROJECT_DIR" \
- status --porcelain --untracked-files=no)" ]; then
+ status --porcelain --untracked-files=no)" ]
+then
echo "Error: Main worktree has uncommitted changes." >&2
echo " Commit or stash them first." >&2
exit 1
# Rebase branch onto main.
echo "=== Rebasing $branch onto main ==="
if ! $HOME/.guix-home/profile/bin/git -C "$PROJECT_DIR" \
- rebase main "$branch"; then
+ rebase main "$branch"
+then
rc=$?
echo "Error: Rebase failed (conflicts)." >&2
echo " Resolve them in the main worktree:" >&2
# Fast-forward merge.
echo "=== Merging $branch into main (fast-forward) ==="
if ! $HOME/.guix-home/profile/bin/git -C "$PROJECT_DIR" \
- merge --ff-only "$branch"; then
+ merge --ff-only "$branch"
+then
rc=$?
echo "Error: Fast-forward merge failed." >&2
echo " This should not happen after a successful rebase." >&2
#
# postswitch - Script executed after the layout switch.
-if [ `which wallpaper` ]; then
+if [ `which wallpaper` ]
+then
wallpaper
fi
# Sources program-specific profiles from $XDG_CONFIG_HOME/profile.d and starts
# Xorg. See bash(1) for more information about login shells.
-if [ -f ~/.profile ]; then
+if [ -f ~/.profile ]
+then
. ~/.profile
fi
_confdir=${XDG_CONFIG_HOME:-${HOME}/.config}
-if [ -d ${_confdir}/profile.d/ ]; then
- for _f in ${_confdir}/profile.d/*.sh; do
+if [ -d ${_confdir}/profile.d/ ]
+then
+ for _f in ${_confdir}/profile.d/*.sh
+ do
[ -r ${_f} ] && . ${_f}
done
unset _f
fi
-if [ -f ${_confdir}/X11/xbindkeysrc ]; then
+if [ -f ${_confdir}/X11/xbindkeysrc ]
+then
xbindkeys --file "${XDG_CONFIG_HOME}/X11/xbindkeysrc"
fi
-if [ -f ${_confdir}/X11/Xresources ]; then
+if [ -f ${_confdir}/X11/Xresources ]
+then
xrdb -merge -I"${HOME}" "${XDG_CONFIG_HOME}/X11/Xresources"
fi
#
# 50-bash.sh - bash environment variables.
-if [ ! -f ${XDG_DATA_HOME}/bash ]; then
+if [ ! -f ${XDG_DATA_HOME}/bash ]
+then
mkdir -p ${XDG_DATA_HOME}/bash
fi
GUIX_BIN="/run/current-system/profile/bin"
CACHE="${XDG_RUNTIME_DIR:-/tmp}/claude-apikey"
-if [ -f "${CACHE}" ]; then
+if [ -f "${CACHE}" ]
+then
"${GUIX_BIN}/cat" "${CACHE}"
else
(umask 077; pass show platforms/deepseek \
volume() {
VOLUME=$(amixer | awk -F"[][]" '/dB/ { print $2 }' | head -n1)
- if [ "${VOLUME}" = "0%" ]; then
+ if [ "${VOLUME}" = "0%" ]
+ then
echo "🔇 ${VOLUME}"
else
echo "🔊 ${VOLUME}"