From c514fe6199cc166dd8fe7f8796d21cd0b22016d6 Mon Sep 17 00:00:00 2001 From: sommerfeld Date: Fri, 22 May 2026 10:41:23 +0100 Subject: feat(claude): add tuicr skill (zellij-adapted) Adapts upstream agavra/tuicr's tmux-based wrapper to zellij: - Detects $ZELLIJ instead of $TMUX - Spawns child via 'zellij action new-pane --floating' (or --direction with TUICR_PANE_MODE=split / TUICR_SPLIT_DIR=down) - Replaces tmux 'wait-for' with a mkfifo / printf done sentinel - Uses 'pgrep -x tuicr' for the cross-pane already-running probe (zellij has no list-panes equivalent) SKILL.md updated for zellij keybinds and a nix install path note. --- .../skills/tuicr/executable_tuicr-wrapper.sh | 102 +++++++++++++++++++++ 1 file changed, 102 insertions(+) create mode 100755 dot_claude/skills/tuicr/executable_tuicr-wrapper.sh (limited to 'dot_claude/skills/tuicr/executable_tuicr-wrapper.sh') diff --git a/dot_claude/skills/tuicr/executable_tuicr-wrapper.sh b/dot_claude/skills/tuicr/executable_tuicr-wrapper.sh new file mode 100755 index 0000000..d3457fa --- /dev/null +++ b/dot_claude/skills/tuicr/executable_tuicr-wrapper.sh @@ -0,0 +1,102 @@ +#!/usr/bin/env bash +# tuicr-wrapper: open tuicr in a zellij floating pane (or split) from a +# non-TTY context (e.g. claude-code, copilot CLI). Blocks on a FIFO until +# the spawned tuicr process exits, then emits any instructions written by +# tuicr's --output to stdout, wrapped in markers the agent recognises. +# +# Adapted from agavra/tuicr's tmux-based reference wrapper. +# See: dot_claude/skills/tuicr/SKILL.md +set -euo pipefail + +TARGET_DIR="${1:-$PWD}" + +if [ -z "${ZELLIJ:-}" ]; then + cat <<'EOF' >&2 +ERROR: not running inside a zellij session. + +Restart the agent from inside a zellij session and try again: + + zellij + # then, inside zellij: + claude # or: copilot +EOF + exit 1 +fi + +if ! command -v tuicr >/dev/null 2>&1; then + cat <<'EOF' >&2 +ERROR: 'tuicr' is not installed. + +Provisioned via nix; rebuild your profile: + + # host (Arch): + just nix-switch + + # remote-dev VM: + cd ~/.local/share/dotfiles/remote-dev + home-manager switch --impure --flake .#vm -b backup +EOF + exit 1 +fi + +if ! git -C "$TARGET_DIR" rev-parse --git-dir >/dev/null 2>&1; then + echo "ERROR: '$TARGET_DIR' is not a git repository" >&2 + exit 1 +fi + +# Refuse to nest if another tuicr is already running (zellij has no +# tmux-style 'list-panes | grep'; pgrep is the closest cross-pane probe). +if pgrep -x tuicr >/dev/null 2>&1; then + echo "ERROR: tuicr is already running in this session" >&2 + exit 1 +fi + +# Per-invocation tmpdir holds: +# done - FIFO; child writes one byte on exit, parent blocks read +# output - tuicr --output target (instructions to relay back) +TMPDIR_TUICR="$(mktemp -d -t tuicr.XXXXXX)" +DONE_FIFO="$TMPDIR_TUICR/done" +OUTPUT_FILE="$TMPDIR_TUICR/output" +trap 'rm -rf "$TMPDIR_TUICR"' EXIT +mkfifo "$DONE_FIFO" +: >"$OUTPUT_FILE" + +PANE_MODE="${TUICR_PANE_MODE:-floating}" +SPLIT_DIR="${TUICR_SPLIT_DIR:-down}" + +# Child command: cd into the repo, run tuicr exporting instructions, then +# signal the parent regardless of exit status. +CHILD_CMD="cd $(printf %q "$TARGET_DIR") && \ + tuicr --output $(printf %q "$OUTPUT_FILE"); \ + printf done > $(printf %q "$DONE_FIFO")" + +case "$PANE_MODE" in + floating) + zellij action new-pane --floating \ + --cwd "$TARGET_DIR" \ + -- bash -lc "$CHILD_CMD" >/dev/null + ;; + split) + zellij action new-pane \ + --direction "$SPLIT_DIR" \ + --cwd "$TARGET_DIR" \ + -- bash -lc "$CHILD_CMD" >/dev/null + ;; + *) + echo "ERROR: invalid TUICR_PANE_MODE='$PANE_MODE' (expected floating|split)" >&2 + exit 1 + ;; +esac + +# Block until the child writes its sentinel. +read -r _ <"$DONE_FIFO" + +# Relay instructions back to the agent if tuicr wrote any. +if [ -s "$OUTPUT_FILE" ]; then + echo "=== TUICR INSTRUCTIONS ===" + cat "$OUTPUT_FILE" + echo + echo "=== END TUICR INSTRUCTIONS ===" +else + echo "tuicr exited with no instructions" >&2 +fi -- cgit v1.3.1