From 5ca4d2745e116c2b4051d33e3be64d20309d7477 Mon Sep 17 00:00:00 2001 From: Ivan Malison Date: Sat, 25 Apr 2026 13:21:27 -0700 Subject: [PATCH] Add zellij agent helpers --- dotfiles/config/zellij/config.kdl | 24 +++++++++++++ dotfiles/lib/functions/zclaude | 7 ++++ dotfiles/lib/functions/zcodex | 23 ++++++++++++ dotfiles/lib/functions/zrw | 59 +++++++++++++++++++++++++++++++ nix-darwin/home/common.nix | 5 ++- nixos/environment.nix | 5 ++- nixos/flake.lock | 4 +-- nixos/home-manager.nix | 4 +++ shared/multiplexer-aliases.nix | 4 +++ 9 files changed, 127 insertions(+), 8 deletions(-) create mode 100644 dotfiles/config/zellij/config.kdl create mode 100644 dotfiles/lib/functions/zclaude create mode 100644 dotfiles/lib/functions/zcodex create mode 100644 dotfiles/lib/functions/zrw create mode 100644 shared/multiplexer-aliases.nix diff --git a/dotfiles/config/zellij/config.kdl b/dotfiles/config/zellij/config.kdl new file mode 100644 index 00000000..aeb8bd1c --- /dev/null +++ b/dotfiles/config/zellij/config.kdl @@ -0,0 +1,24 @@ +keybinds { + // Keep Ctrl-p available for readline/history/up in shells and editors. + unbind "Ctrl p" + + shared_except "locked" "pane" { + bind "Ctrl Space" { SwitchToMode "Pane"; } + } + + pane { + bind "Ctrl Space" { SwitchToMode "Normal"; } + } + + tmux { + // Ctrl-b C: start a Codex pane from the current zellij tab. + bind "C" { + Run "codex" "--dangerously-bypass-approvals-and-sandbox" { + name "codex" + } + SwitchToMode "Normal" + } + } +} + +default_mode "locked" diff --git a/dotfiles/lib/functions/zclaude b/dotfiles/lib/functions/zclaude new file mode 100644 index 00000000..cc022828 --- /dev/null +++ b/dotfiles/lib/functions/zclaude @@ -0,0 +1,7 @@ +#!/usr/bin/env sh + +function zclaude { + ZRW_NAME=claude zrw claude --dangerously-skip-permissions "$@" +} + +zclaude "$@" diff --git a/dotfiles/lib/functions/zcodex b/dotfiles/lib/functions/zcodex new file mode 100644 index 00000000..a99143cf --- /dev/null +++ b/dotfiles/lib/functions/zcodex @@ -0,0 +1,23 @@ +#!/usr/bin/env sh + +function zcodex { + # Record launch directories so rofi_tmcodex can offer good defaults. + state_dir="${XDG_STATE_HOME:-$HOME/.local/state}/rofi-tmcodex" + history_file="$state_dir/dirs" + mkdir -p "$state_dir" 2>/dev/null || true + if [ -d "$PWD" ]; then + tmp="$(mktemp 2>/dev/null || true)" + if [ -n "$tmp" ]; then + { printf '%s\n' "$PWD"; cat "$history_file" 2>/dev/null || true; } \ + | awk 'NF && !seen[$0]++' \ + | head -n 200 >"$tmp" 2>/dev/null || true + mv -f "$tmp" "$history_file" 2>/dev/null || true + else + printf '%s\n' "$PWD" >>"$history_file" 2>/dev/null || true + fi + fi + + ZRW_NAME=codex zrw codex --dangerously-bypass-approvals-and-sandbox "$@" +} + +zcodex "$@" diff --git a/dotfiles/lib/functions/zrw b/dotfiles/lib/functions/zrw new file mode 100644 index 00000000..7206c84e --- /dev/null +++ b/dotfiles/lib/functions/zrw @@ -0,0 +1,59 @@ +#!/usr/bin/env sh + +function _zrw_kdl_quote { + printf '"' + printf '%s' "$1" | sed 's/\\/\\\\/g; s/"/\\"/g' + printf '"' +} + +function _zrw_layout_string { + command="$1" + shift + + printf 'layout {\n' + printf ' pane command=' + _zrw_kdl_quote "$command" + printf ' name=' + _zrw_kdl_quote "${ZRW_NAME:-$(basename -- "$command")}" + printf ' cwd=' + _zrw_kdl_quote "$PWD" + if [ "$#" -eq 0 ]; then + printf '\n' + else + printf ' {\n' + printf ' args' + for arg in "$@"; do + printf ' ' + _zrw_kdl_quote "$arg" + done + printf '\n' + printf ' }\n' + fi + printf '}\n' +} + +function _zrw_session_name { + command_name="$(basename -- "$1")" + base="$(basename -- "$PWD")" + ck="$(printf '%s' "$PWD" | cksum | awk '{print $1}')" + session="${ZRW_SESSION:-${command_name}-${base}-${ck}}" + printf '%s' "$session" | tr -cs 'A-Za-z0-9_-' '-' | sed 's/^-//;s/-$//' +} + +function zrw { + if [ "$#" -eq 0 ]; then + echo "Usage: zrw COMMAND [ARG ...]" >&2 + return 2 + fi + + tab_name="${ZRW_NAME:-$(basename -- "$1")}" + if [ -n "$ZELLIJ" ]; then + zellij action new-tab --cwd "$PWD" --name "$tab_name" -- "$@" + else + session="$(_zrw_session_name "$1")" + layout="$(_zrw_layout_string "$@")" + zellij --session "$session" --layout-string "$layout" + fi +} + +zrw "$@" diff --git a/nix-darwin/home/common.nix b/nix-darwin/home/common.nix index cc87c313..87e002f6 100644 --- a/nix-darwin/home/common.nix +++ b/nix-darwin/home/common.nix @@ -66,6 +66,7 @@ --passphrase-file "$passphrase_path" \ --import "$normalized_key_file" ''; + multiplexerAliases = import ../../shared/multiplexer-aliases.nix; excludedTopLevelEntries = [ "config" @@ -228,9 +229,7 @@ in { }; shellAliases = { df_ssh = "TERM='xterm-256color' ssh -o StrictHostKeyChecking=no"; - ta = "tmux attach"; - za = "zellij attach"; - }; + } // multiplexerAliases; initContent = lib.mkMerge [ (lib.mkOrder 550 '' fpath+="${libDir}/functions" diff --git a/nixos/environment.nix b/nixos/environment.nix index 66635d83..b965ce0b 100644 --- a/nixos/environment.nix +++ b/nixos/environment.nix @@ -12,6 +12,7 @@ let ++ (map (machineName: "${machineName}.local") machineNames) ++ extraManagedSshHosts; managedSshHostCasePattern = lib.concatStringsSep "|" managedSshHostPatterns; + multiplexerAliases = import ../shared/multiplexer-aliases.nix; in with lib; { @@ -81,9 +82,7 @@ with lib; shellAliases = { df_ssh = "TERM=xterm-256color ssh -o StrictHostKeyChecking=no"; fix_nix = "LD_LIBRARY_PATH='' nix"; - ta = "tmux attach"; - za = "zellij attach"; - }; + } // multiplexerAliases; variables = { ROFI_SYSTEMD_TERM = "ghostty -e"; NIXPKGS_GIT_REV = "${inputs.nixpkgs.rev}"; diff --git a/nixos/flake.lock b/nixos/flake.lock index 9fafb15e..80f27589 100644 --- a/nixos/flake.lock +++ b/nixos/flake.lock @@ -1548,8 +1548,8 @@ ] }, "locked": { - "lastModified": 1777078756, - "narHash": "sha256-514PXbaRust8FYqpArC2ar1Y2pyBrQOhWrL8EHbhJYs=", + "lastModified": 1777148012, + "narHash": "sha256-ExYOcgFOu8yTU00ZvZC0QLLuigaest/zhc3ecJsXnfs=", "path": "/home/imalison/Projects/keepbook", "type": "path" }, diff --git a/nixos/home-manager.nix b/nixos/home-manager.nix index 6084d583..b11c2f42 100644 --- a/nixos/home-manager.nix +++ b/nixos/home-manager.nix @@ -27,6 +27,10 @@ in { static_history = [] ''; + xdg.configFile."zellij/config.kdl".source = + config.lib.file.mkOutOfStoreSymlink + "${config.home.homeDirectory}/dotfiles/dotfiles/config/zellij/config.kdl"; + xdg.mimeApps = lib.mkIf nixos.config.myModules.desktop.enable ( let browser = "google-chrome.desktop"; diff --git a/shared/multiplexer-aliases.nix b/shared/multiplexer-aliases.nix new file mode 100644 index 00000000..6b6b8212 --- /dev/null +++ b/shared/multiplexer-aliases.nix @@ -0,0 +1,4 @@ +{ + ta = "tmux attach"; + za = "zellij attach"; +}