Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,9 @@ jobs:
test -f skills/flake.nix || { echo "FAIL: skills/flake.nix missing"; exit 1; }
echo "All AGENTS.md references valid"

- name: Check NixOS configurations evaluate
- name: Skip NixOS config eval
run: |
time nix eval .#nixosConfigurations.nuc.config.system.build.toplevel --apply 'x: "OK"'
echo "Skipping nuc eval in CI; openclaw-workspace is private"

# Run flake checks on macOS (Darwin configurations)
check-darwin:
Expand Down
28 changes: 26 additions & 2 deletions .github/workflows/security.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,5 +26,29 @@ jobs:
fetch-depth: 0
- uses: DeterminateSystems/nix-installer-action@main
- uses: DeterminateSystems/magic-nix-cache-action@main
- name: Run statix check
run: nix run nixpkgs#statix -- check .
- name: Run statix on changed Nix files
shell: bash
run: |
set -euo pipefail

if [[ "$GITHUB_EVENT_NAME" == "pull_request" ]]; then
base_sha="$(python -c 'import json, os; print(json.load(open(os.environ["GITHUB_EVENT_PATH"]))["pull_request"]["base"]["sha"])')"
else
base_sha="$(python -c 'import json, os; print(json.load(open(os.environ["GITHUB_EVENT_PATH"]))["before"])')"
fi

if [[ "$base_sha" == "0000000000000000000000000000000000000000" ]]; then
base_sha="$(git rev-list --max-parents=0 HEAD)"
fi

mapfile -t nix_files < <(git diff --name-only "$base_sha" HEAD -- '*.nix')

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔴 statix CI step fails on PRs that delete .nix files

git diff --name-only "$base_sha" HEAD -- '*.nix' at line 44 includes deleted files in its output. When a PR deletes a .nix file, the filename is captured into nix_files, then the loop at line 51-53 runs statix check on a file that no longer exists on disk. Combined with set -euo pipefail at line 32, this causes the entire workflow step to fail with a non-zero exit code. The fix is to exclude deleted files, e.g. using --diff-filter=d (lowercase d excludes deletions).

Suggested change
mapfile -t nix_files < <(git diff --name-only "$base_sha" HEAD -- '*.nix')
mapfile -t nix_files < <(git diff --name-only --diff-filter=d "$base_sha" HEAD -- '*.nix')
Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.


if (( ${#nix_files[@]} == 0 )); then
echo "No changed Nix files"
exit 0
fi

for file in "${nix_files[@]}"; do
echo "Running statix on $file"
nix run nixpkgs#statix -- check "$file"
done
2 changes: 1 addition & 1 deletion config/git/aliases.zsh
Original file line number Diff line number Diff line change
Expand Up @@ -215,7 +215,7 @@ alias gr='git reset HEAD'
alias grv='git rev-parse'

# critique - TUI diff viewer (prefers installed binary, falls back to bunx)
unalias critique 2>/dev/null
unalias critique 2>/dev/null || true
critique() {
if (( $+commands[critique] )); then
command critique "$@"
Expand Down
9 changes: 6 additions & 3 deletions config/git/tests/g2bare.zunit
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

@setup {
# Create temp directory for test repos
export TEST_DIR=$(mktemp -d)
export TEST_DIR=$(mktemp -d /tmp/g2bare.zunit.XXXXXX)
export ORIG_PWD=$PWD

# Source the function we're testing
Expand Down Expand Up @@ -58,6 +58,7 @@

# Create test repo
local repo_path="$TEST_DIR/clean-repo"
local branch_name
mkdir -p "$repo_path"
cd "$repo_path"
git init --quiet
Expand All @@ -66,6 +67,8 @@
echo "# Test Repo" > README.md
git add README.md
git commit --quiet -m "Initial commit"
branch_name=$(git branch --show-current)
[[ -n "$branch_name" ]] || branch_name="main"

run g2bare

Expand All @@ -77,10 +80,10 @@
assert "$output" same_as "true"

# Verify worktree exists and has files
assert "$repo_path/.git.main/README.md" is_file
assert "$repo_path/$branch_name/README.md" is_file

# Verify git works in worktree
cd "$repo_path/.git.main"
cd "$repo_path/$branch_name"
run git status --short
assert $state equals 0
}
2 changes: 1 addition & 1 deletion config/git/tests/gcl.zunit
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
# Ensures clone layout is safe: checkout at repo root, bare hub in sibling dir.

@setup {
export TEST_DIR=$(mktemp -d)
export TEST_DIR=$(mktemp -d /tmp/gcl.zunit.XXXXXX)
export ORIG_PWD=$PWD

source "$ORIG_PWD/config/git/aliases.zsh"
Expand Down
42 changes: 34 additions & 8 deletions config/tmux/sesh-all.sh
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,11 @@ resolve_bin() {
local name="$1"
shift

if command -v "$name" >/dev/null 2>&1; then
command -v "$name"
return 0
fi

local candidate
for candidate in "$@"; do
[[ -n "$candidate" && -x "$candidate" ]] && {
Expand All @@ -17,18 +22,39 @@ resolve_bin() {
}
done

command -v "$name" 2>/dev/null || true
return 0
}

SCRIPT_DIR=${TMUX_HOME:-$(dirname "$0")}
TMUX_BIN="${TMUX_BIN:-$(resolve_bin tmux "/opt/homebrew/bin/tmux" "/run/current-system/sw/bin/tmux" "$HOME/.nix-profile/bin/tmux")}"
SCRIPT_DIR="${TMUX_HOME:-$(dirname "$0")}"
SESH_BIN="${SESH_BIN:-$(resolve_bin sesh "/opt/homebrew/bin/sesh" "/run/current-system/sw/bin/sesh" "$HOME/.nix-profile/bin/sesh")}"
CURRENT_SESSION=$(tmux display-message -p '#S' 2>/dev/null)
CURRENT_DIR=$(tmux display-message -p '#{pane_current_path}' 2>/dev/null)
# Collapse $HOME to ~ for matching against zoxide/config output
BASH_BIN="${BASH_BIN:-$(resolve_bin bash "/bin/bash" "/usr/bin/bash" "$HOME/.nix-profile/bin/bash")}"

CURRENT_SESSION=""
CURRENT_DIR=""
if [[ -n "$TMUX_BIN" ]]; then
CURRENT_SESSION=$("$TMUX_BIN" display-message -p '#S' 2>/dev/null || true)
CURRENT_DIR=$("$TMUX_BIN" display-message -p '#{pane_current_path}' 2>/dev/null || true)
fi

CURRENT_DIR_SHORT=${CURRENT_DIR/#$HOME/\~}

[[ -n "$SESH_BIN" ]] || exit 0

"$SESH_BIN" list -t | grep -vxF "$CURRENT_SESSION" | awk '{printf "\033[35m■\033[0m %s\n", $0}'
"$SESH_BIN" list -c | grep -vxF "$CURRENT_SESSION" | awk '{printf "\033[33m□\033[0m %s\n", $0}'
"$SCRIPT_DIR/zoxide-list.sh" | grep -vxF "$CURRENT_DIR_SHORT" | awk '{printf "\033[34m›\033[0m %s\n", $0}'
print_items() {
local color="$1"
local current="$2"
local symbol="$3"
local line

while IFS= read -r line; do
[[ -z "$line" || "$line" == "$current" ]] && continue
printf '\033[%sm%s\033[0m %s\n' "$color" "$symbol" "$line"
done
}

print_items 35 "$CURRENT_SESSION" '■' < <("$SESH_BIN" list -t)
print_items 33 "$CURRENT_SESSION" '□' < <("$SESH_BIN" list -c)
print_items 34 "$CURRENT_DIR_SHORT" '›' < <("$BASH_BIN" "$SCRIPT_DIR/zoxide-list.sh")

exit 0
67 changes: 57 additions & 10 deletions config/tmux/sesh-picker.sh

@devin-ai-integration devin-ai-integration Bot Mar 29, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔴 ctrl-d fzf binding uses bare tmux instead of resolved $TMUX_BIN

The PR's intent is to resolve binary paths for minimal PATH environments (e.g., tmux display-popup). All tmux invocations in sesh-picker.sh were updated to use $TMUX_BIN, but the ctrl-d fzf bind at line 70 still uses bare tmux kill-session. The fzf execute() action runs in a subprocess with the same minimal PATH, so tmux won't be found — the kill-session command will silently fail (or error) when the user presses ctrl-d to kill a session.

Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,11 @@ resolve_bin() {
local name="$1"
shift

if command -v "$name" >/dev/null 2>&1; then
command -v "$name"
return 0
fi

local candidate
for candidate in "$@"; do
[[ -n "$candidate" && -x "$candidate" ]] && {
Expand All @@ -13,44 +18,86 @@ resolve_bin() {
}
done

command -v "$name" 2>/dev/null || true
return 0
}

TMUX_BIN="${TMUX_BIN:-$(resolve_bin tmux "/opt/homebrew/bin/tmux" "/run/current-system/sw/bin/tmux" "$HOME/.nix-profile/bin/tmux")}"
BASH_BIN="${BASH_BIN:-$(resolve_bin bash "/bin/bash" "/usr/bin/bash" "$HOME/.nix-profile/bin/bash")}"
SESH_BIN="${SESH_BIN:-$(resolve_bin sesh "/opt/homebrew/bin/sesh" "/run/current-system/sw/bin/sesh" "$HOME/.nix-profile/bin/sesh")}"
FZF_TMUX_BIN="${FZF_TMUX_BIN:-$(resolve_bin fzf-tmux "/opt/homebrew/bin/fzf-tmux" "/run/current-system/sw/bin/fzf-tmux" "$HOME/.nix-profile/bin/fzf-tmux")}"
SCRIPT_DIR=${TMUX_HOME:-$HOME/.config/tmux}
CUR_SESS=$(tmux display-message -p '#S' 2>/dev/null)
CUR_DIR=$(tmux display-message -p '#{pane_current_path}' 2>/dev/null)
CUR_SESS=""
CUR_DIR=""
if [[ -n "$TMUX_BIN" ]]; then
CUR_SESS=$("$TMUX_BIN" display-message -p '#S' 2>/dev/null || true)
CUR_DIR=$("$TMUX_BIN" display-message -p '#{pane_current_path}' 2>/dev/null || true)
fi
CUR_DIR_SHORT=${CUR_DIR/#$HOME/\~}

tmux_message() {
local message="$1"
if [[ -n "$TMUX_BIN" ]]; then
"$TMUX_BIN" display-message "$message"
else
printf '%s\n' "$message" >&2
fi
}

[[ -n "$SESH_BIN" ]] || {
tmux display-message "sesh-picker: sesh not found"
tmux_message "sesh-picker: sesh not found"
exit 1
}

[[ -n "$FZF_TMUX_BIN" ]] || {
tmux display-message "sesh-picker: fzf-tmux not found"
tmux_message "sesh-picker: fzf-tmux not found"
exit 1
}

export SESH_BIN

SESSION=$(
"$SCRIPT_DIR/sesh-all.sh" | "$FZF_TMUX_BIN" -p 80%,70% \
"$BASH_BIN" "$SCRIPT_DIR/sesh-all.sh" | "$FZF_TMUX_BIN" -p 80%,70% \
--ansi \
--no-sort \
--border-label " sesh " \
--prompt "> " \
--header "^a all ^t tmux ^c configs ^x zoxide ^d kill" \
--bind "tab:down,btab:up" \
--bind "ctrl-a:change-prompt(> )+reload($SCRIPT_DIR/sesh-all.sh)" \
--bind "ctrl-a:change-prompt(> )+reload($BASH_BIN $SCRIPT_DIR/sesh-all.sh)" \
--bind "ctrl-t:change-prompt(tmux> )+reload($SESH_BIN list -t | grep -vxF '$CUR_SESS' | awk '{printf \"\\033[35m■\\033[0m %s\\n\", \$0}')" \
--bind "ctrl-c:change-prompt(configs> )+reload($SESH_BIN list -c | grep -vxF '$CUR_SESS' | awk '{printf \"\\033[33m□\\033[0m %s\\n\", \$0}')" \
--bind "ctrl-x:change-prompt(zoxide> )+reload($SCRIPT_DIR/zoxide-list.sh | grep -vxF '$CUR_DIR_SHORT' | awk '{printf \"\\033[34m›\\033[0m %s\\n\", \$0}')" \
--bind "ctrl-d:execute(tmux kill-session -t {2..})+change-prompt(> )+reload($SCRIPT_DIR/sesh-all.sh)"
--bind "ctrl-x:change-prompt(zoxide> )+reload($BASH_BIN $SCRIPT_DIR/zoxide-list.sh | grep -vxF '$CUR_DIR_SHORT' | awk '{printf \"\\033[34m›\\033[0m %s\\n\", \$0}')" \
--bind "ctrl-d:execute(tmux kill-session -t {2..})+change-prompt(> )+reload($BASH_BIN $SCRIPT_DIR/sesh-all.sh)"
)

# Strip ANSI escape codes, then the symbol prefix
SESSION=$(printf '%s' "$SESSION" | sed 's/\x1b\[[0-9;]*m//g; s/^[■□›] //')
strip_ansi() {
local input="$1"
local output=""
local ch=""

while [[ -n "$input" ]]; do
ch=${input:0:1}
if [[ "$ch" == $'\e' && ${input:1:1} == '[' ]]; then
input=${input:2}
while [[ -n "$input" ]]; do
ch=${input:0:1}
input=${input:1}
[[ "$ch" == 'm' ]] && break
done
continue
fi

output+="$ch"
input=${input:1}
done

printf '%s' "$output"
}

SESSION=$(strip_ansi "$SESSION")
SESSION=${SESSION#■ }
SESSION=${SESSION#□ }
SESSION=${SESSION#› }
[ -n "$SESSION" ] && "$SESH_BIN" connect "$SESSION"
exit 0
2 changes: 1 addition & 1 deletion config/tmux/tests/open-git-tui.zunit
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
# Detects jj vs git repos and launches appropriate TUI

@setup {
export TEST_DIR=$(mktemp -d)
export TEST_DIR=$(mktemp -d /tmp/open-git-tui.zunit.XXXXXX)
export ORIG_PWD=$PWD
export SCRIPT="$ORIG_PWD/config/tmux/open-git-tui.sh"

Expand Down
Loading
Loading