Add netty worktree helpers

Add netty-specific worktree helper commands and a zsh wrapper that creates sibling worktrees and cds into them.

Also split script packaging so Darwin keeps the existing wt integration while netty gets git-worktree-based helpers.

Co-authored-by: Codex <noreply@openai.com>
This commit is contained in:
Harivansh Rathi 2026-03-30 16:37:46 +00:00
parent 2a79a099c1
commit 8ad1784a82
7 changed files with 172 additions and 74 deletions

23
home/netty-worktree.nix Normal file
View file

@ -0,0 +1,23 @@
{
config,
lib,
pkgs,
...
}: let
customScripts = import ../scripts {inherit config lib pkgs;};
in {
home.packages = builtins.attrValues customScripts.nettyPackages;
programs.zsh.initContent = lib.mkAfter ''
wtc() {
if [[ $# -ne 1 ]]; then
printf 'usage: wtc <worktree-name>\n' >&2
return 1
fi
local worktree_path
worktree_path=$(command wt-create "$1") || return
cd -- "$worktree_path" || return
}
'';
}

View file

@ -1,6 +1,6 @@
{ ... }: {...}: {
{
imports = [ imports = [
./common.nix ./common.nix
./netty-worktree.nix
]; ];
} }

View file

@ -3,14 +3,14 @@
lib, lib,
pkgs, pkgs,
... ...
}: }: let
let customScripts = import ../scripts {inherit config lib pkgs;};
customScripts = import ../scripts { inherit config lib pkgs; }; in {
in home.packages =
{ builtins.attrValues customScripts.commonPackages
home.packages = builtins.attrValues customScripts.packages; ++ lib.optionals pkgs.stdenv.isDarwin (builtins.attrValues customScripts.darwinPackages);
home.activation.initializeThemeState = lib.hm.dag.entryAfter [ "writeBoundary" ] '' home.activation.initializeThemeState = lib.hm.dag.entryAfter ["writeBoundary"] ''
mkdir -p "${customScripts.theme.paths.stateDir}" "${customScripts.theme.paths.fzfDir}" "${customScripts.theme.paths.ghosttyDir}" "${customScripts.theme.paths.tmuxDir}" mkdir -p "${customScripts.theme.paths.stateDir}" "${customScripts.theme.paths.fzfDir}" "${customScripts.theme.paths.ghosttyDir}" "${customScripts.theme.paths.tmuxDir}"
if [[ -f "${customScripts.theme.paths.stateFile}" ]]; then if [[ -f "${customScripts.theme.paths.stateFile}" ]]; then

View file

@ -3,15 +3,14 @@
lib, lib,
pkgs, pkgs,
... ...
}: }: {
{
home.file.".oh-my-zsh/custom/themes/agnoster.zsh-theme".source = ../config/agnoster.zsh-theme; home.file.".oh-my-zsh/custom/themes/agnoster.zsh-theme".source = ../config/agnoster.zsh-theme;
home.activation.ensureOhMyZshCache = lib.hm.dag.entryAfter [ "writeBoundary" ] '' home.activation.ensureOhMyZshCache = lib.hm.dag.entryAfter ["writeBoundary"] ''
mkdir -p "${config.xdg.cacheHome}/oh-my-zsh" mkdir -p "${config.xdg.cacheHome}/oh-my-zsh"
''; '';
home.packages = [ pkgs.oh-my-zsh ]; home.packages = [pkgs.oh-my-zsh];
programs.zsh = { programs.zsh = {
enable = true; enable = true;
@ -22,40 +21,42 @@
autosuggestion.enable = true; autosuggestion.enable = true;
syntaxHighlighting.enable = true; syntaxHighlighting.enable = true;
shellAliases = { shellAliases =
co = "codex --dangerously-bypass-approvals-and-sandbox"; {
ca = "cursor-agent"; co = "codex --dangerously-bypass-approvals-and-sandbox";
cc = "claude"; ca = "cursor-agent";
ch = "claude-handoff"; cc = "claude";
cl = "clear"; ch = "claude-handoff";
gc = "git commit"; cl = "clear";
gd = "git diff"; gc = "git commit";
gk = "git checkout"; gd = "git diff";
gp = "git push"; gk = "git checkout";
gpo = "git pull origin"; gp = "git push";
gs = "git status"; gpo = "git pull origin";
ld = "lumen diff"; gs = "git status";
lg = "lazygit"; ld = "lumen diff";
nim = "nvim ."; lg = "lazygit";
} nim = "nvim .";
// lib.optionalAttrs pkgs.stdenv.isDarwin { }
tailscale = "/Applications/Tailscale.app/Contents/MacOS/Tailscale"; // lib.optionalAttrs pkgs.stdenv.isDarwin {
}; tailscale = "/Applications/Tailscale.app/Contents/MacOS/Tailscale";
};
envExtra = '' envExtra =
if [[ -f "$HOME/.cargo/env" ]]; then ''
. "$HOME/.cargo/env" if [[ -f "$HOME/.cargo/env" ]]; then
fi . "$HOME/.cargo/env"
export NODE_NO_WARNINGS=1 fi
'' export NODE_NO_WARNINGS=1
+ lib.optionalString pkgs.stdenv.isDarwin '' ''
# Ghostty shell integration expects a resource directory; the Nix app + lib.optionalString pkgs.stdenv.isDarwin ''
# bundle lives in the store instead of /Applications. # Ghostty shell integration expects a resource directory; the Nix app
export GHOSTTY_RESOURCES_DIR="${pkgs.ghostty-bin}/Applications/Ghostty.app/Contents/Resources/ghostty" # bundle lives in the store instead of /Applications.
'' export GHOSTTY_RESOURCES_DIR="${pkgs.ghostty-bin}/Applications/Ghostty.app/Contents/Resources/ghostty"
+ '' ''
export MANPAGER="nvim +Man!" + ''
''; export MANPAGER="nvim +Man!"
'';
initContent = lib.mkMerge [ initContent = lib.mkMerge [
(lib.mkOrder 550 '' (lib.mkOrder 550 ''
@ -86,7 +87,9 @@
export BUN_INSTALL="$HOME/.bun" export BUN_INSTALL="$HOME/.bun"
export PNPM_HOME="${ export PNPM_HOME="${
if pkgs.stdenv.isDarwin then "$HOME/Library/pnpm" else "${config.xdg.dataHome}/pnpm" if pkgs.stdenv.isDarwin
then "$HOME/Library/pnpm"
else "${config.xdg.dataHome}/pnpm"
}" }"
bindkey -v bindkey -v
typeset -U path PATH typeset -U path PATH
@ -103,9 +106,9 @@
"/run/current-system/sw/bin" "/run/current-system/sw/bin"
"/nix/var/nix/profiles/default/bin" "/nix/var/nix/profiles/default/bin"
${lib.optionalString pkgs.stdenv.isDarwin '' ${lib.optionalString pkgs.stdenv.isDarwin ''
"/opt/homebrew/bin" "/opt/homebrew/bin"
"/opt/homebrew/sbin" "/opt/homebrew/sbin"
''} ''}
$path $path
) )
@ -249,15 +252,17 @@
_codex_apply_highlight_styles _codex_apply_highlight_styles
if command -v wt >/dev/null 2>&1; then ${lib.optionalString pkgs.stdenv.isDarwin ''
eval "$(command wt config shell init zsh)" if command -v wt >/dev/null 2>&1; then
eval "$(command wt config shell init zsh)"
# `wt` changes directories by sourcing directives into the current shell, # `wt` changes directories by sourcing directives into the current shell,
# so wrappers around it must stay shell functions instead of scripts. # so wrappers around it must stay shell functions instead of scripts.
wtc() { wtc() {
wt switch --create --base @ "$@" wt switch --create --base @ "$@"
} }
fi fi
''}
'') '')
(lib.mkAfter '' (lib.mkAfter ''

View file

@ -2,22 +2,20 @@
config, config,
lib, lib,
pkgs, pkgs,
}: }: let
let theme = import ../lib/theme.nix {inherit config;};
theme = import ../lib/theme.nix { inherit config; };
tmuxConfigs = { tmuxConfigs = {
dark = pkgs.writeText "tmux-theme-dark.conf" (theme.renderTmux "dark"); dark = pkgs.writeText "tmux-theme-dark.conf" (theme.renderTmux "dark");
light = pkgs.writeText "tmux-theme-light.conf" (theme.renderTmux "light"); light = pkgs.writeText "tmux-theme-light.conf" (theme.renderTmux "light");
}; };
mkScript = mkScript = {
{ file,
file, name,
name, runtimeInputs ? [],
runtimeInputs ? [ ], replacements ? {},
replacements ? { }, }:
}:
pkgs.writeShellApplication { pkgs.writeShellApplication {
inherit name runtimeInputs; inherit name runtimeInputs;
text = lib.replaceStrings (builtins.attrNames replacements) (builtins.attrValues replacements) ( text = lib.replaceStrings (builtins.attrNames replacements) (builtins.attrValues replacements) (
@ -25,11 +23,11 @@ let
); );
}; };
packages = { commonPackages = {
ga = mkScript { ga = mkScript {
name = "ga"; name = "ga";
file = ./ga.sh; file = ./ga.sh;
runtimeInputs = with pkgs; [ git ]; runtimeInputs = with pkgs; [git];
}; };
ghpr = mkScript { ghpr = mkScript {
@ -73,7 +71,7 @@ let
ni = mkScript { ni = mkScript {
name = "ni"; name = "ni";
file = ./ni.sh; file = ./ni.sh;
runtimeInputs = with pkgs; [ nix ]; runtimeInputs = with pkgs; [nix];
}; };
theme = mkScript { theme = mkScript {
@ -104,13 +102,30 @@ let
"@TMUX_CONFIG@" = "${config.xdg.configHome}/tmux/tmux.conf"; "@TMUX_CONFIG@" = "${config.xdg.configHome}/tmux/tmux.conf";
}; };
}; };
};
darwinPackages = {
wtc = mkScript { wtc = mkScript {
name = "wtc"; name = "wtc";
file = ./wtc.sh; file = ./wtc.sh;
}; };
}; };
in
{ nettyPackages = {
inherit packages theme tmuxConfigs; wt-create = mkScript {
name = "wt-create";
file = ./wt-create.sh;
runtimeInputs = with pkgs; [coreutils git gnused];
};
wt-path = mkScript {
name = "wt-path";
file = ./wt-path.sh;
runtimeInputs = with pkgs; [coreutils git gnused];
};
};
in {
inherit commonPackages darwinPackages nettyPackages theme tmuxConfigs;
packages = commonPackages;
} }

29
scripts/wt-create.sh Normal file
View file

@ -0,0 +1,29 @@
#!/usr/bin/env bash
set -euo pipefail
if [[ $# -ne 1 ]]; then
printf 'usage: wt-create <worktree-name>\n' >&2
exit 1
fi
branch_name=$1
repo_root=$(git rev-parse --show-toplevel 2>/dev/null) || {
printf 'wt-create: not inside a git repository\n' >&2
exit 1
}
target_path=$(wt-path "$branch_name")
if [[ -e "$target_path" ]]; then
printf 'wt-create: path already exists: %s\n' "$target_path" >&2
exit 1
fi
if git -C "$repo_root" show-ref --verify --quiet "refs/heads/$branch_name"; then
git -C "$repo_root" worktree add -- "$target_path" "$branch_name" 1>&2
else
git -C "$repo_root" worktree add -b "$branch_name" -- "$target_path" HEAD 1>&2
fi
printf '%s\n' "$target_path"

26
scripts/wt-path.sh Normal file
View file

@ -0,0 +1,26 @@
#!/usr/bin/env bash
set -euo pipefail
if [[ $# -ne 1 ]]; then
printf 'usage: wt-path <worktree-name>\n' >&2
exit 1
fi
repo_root=$(git rev-parse --show-toplevel 2>/dev/null) || {
printf 'wt-path: not inside a git repository\n' >&2
exit 1
}
worktree_name=$1
clean_name=$(printf '%s' "$worktree_name" | sed -E 's#[^[:alnum:]._-]+#-#g; s#-+#-#g; s#(^[.-]+|[.-]+$)##g')
if [[ -z "$clean_name" ]]; then
printf 'wt-path: %s does not produce a usable path name\n' "$worktree_name" >&2
exit 1
fi
repo_parent=$(dirname "$repo_root")
repo_name=$(basename "$repo_root")
printf '%s/%s-%s\n' "$repo_parent" "$repo_name" "$clean_name"