diff options
| author | 2026-05-22 10:41:23 +0100 | |
|---|---|---|
| committer | 2026-05-22 10:41:23 +0100 | |
| commit | c514fe6199cc166dd8fe7f8796d21cd0b22016d6 (patch) | |
| tree | 6108811f7de6dcea9904f740df0bbdf786e83ae3 | |
| parent | 668b9846a6eccb16a619f57d305f1bd6bdaeb7bc (diff) | |
| download | dotfiles-c514fe6199cc166dd8fe7f8796d21cd0b22016d6.tar.gz dotfiles-c514fe6199cc166dd8fe7f8796d21cd0b22016d6.tar.bz2 dotfiles-c514fe6199cc166dd8fe7f8796d21cd0b22016d6.zip | |
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.
| -rw-r--r-- | dot_claude/skills/tuicr/SKILL.md | 110 | ||||
| -rwxr-xr-x | dot_claude/skills/tuicr/executable_tuicr-wrapper.sh | 102 |
2 files changed, 212 insertions, 0 deletions
diff --git a/dot_claude/skills/tuicr/SKILL.md b/dot_claude/skills/tuicr/SKILL.md new file mode 100644 index 0000000..2f8e728 --- /dev/null +++ b/dot_claude/skills/tuicr/SKILL.md @@ -0,0 +1,110 @@ +--- +name: tuicr +description: Review local git changes with tuicr TUI via zellij floating pane +--- + +# tuicr - TUI Change Reviewer + +Launch the `tuicr` TUI tool in a zellij floating pane to interactively review +local git changes. + +## Usage + +``` +/tuicr [directory] +``` + +Or simply mention wanting to review changes with tuicr. + +## How It Works + +Since coding agents cannot run interactive TUI applications directly, this +skill uses a zellij workaround: + +1. Detects if the current agent session is running inside zellij (`$ZELLIJ`). +2. If yes: opens tuicr in a floating pane, blocks until it exits, then reads + any exported instructions back from a temp file. +3. If no: instructs the user to restart the agent inside zellij. + +## Determining the Directory + +**Important:** You must determine the correct git repository directory based on +context. + +Consider: + +- The user's current working directory +- Any repository they've been working in during the session +- Explicit directory mentioned in their request +- The git status output if available + +Common patterns: + +- "review my changes" → use current working directory +- "review changes in myproject" → find that repo path +- After editing files → use the directory of those files + +## Workflow + +1. **Determine target directory** (cwd, recent file ops, ask if ambiguous). + +2. **Run the wrapper** with a 10-minute timeout: + + ```bash + <skill-directory>/tuicr-wrapper.sh [directory] + ``` + + **IMPORTANT:** Always set `timeout: 600000` (10 minutes) on the Bash tool + call. The script blocks on a FIFO until tuicr exits; without the extended + timeout the agent may background it after 2 minutes. + +3. **Handle the result**: + - Success → tuicr opened in a floating pane, user reviewed and exited. + - Not in zellij → relay the instructions to the user. + - Not a git repo → ask for the correct path. + +4. **Process instructions from tuicr output**: + + ``` + === TUICR INSTRUCTIONS === + <instructions here> + === END TUICR INSTRUCTIONS === + ``` + + If present, parse and execute them. If absent, ask the user to paste from + clipboard. + +## Configuration + +| Variable | Default | Description | +| ----------------- | ---------- | ---------------------------------------------- | +| `TUICR_PANE_MODE` | `floating` | `floating` (overlay) or `split` (horizontal) | +| `TUICR_SPLIT_DIR` | `down` | When `split`: `down`, `up`, `right`, or `left` | + +Example: + +```bash +TUICR_PANE_MODE=split TUICR_SPLIT_DIR=down \ + <skill-directory>/tuicr-wrapper.sh /path/to/repo +``` + +## Zellij Tips (relay to user if needed) + +- Move focus between panes: `Alt-h/j/k/l` +- Close the floating pane: press `q` inside tuicr (pane auto-closes) +- Toggle floating pane visibility: `Ctrl-p` then `w` +- Zoom current pane: `Ctrl-p` then `f` + +## Error Handling + +| Error | Action | +| ------------------- | ---------------------------------------------------------------------------------------------------- | +| Not in zellij | Tell the user to restart the agent inside zellij | +| Not a git repo | Ask user for correct directory | +| tuicr not installed | Tell user `tuicr` is provisioned via nix; run `home-manager switch` (VM) or `just nix-switch` (host) | + +## When NOT to use + +- User just wants `git diff` output (use git directly) +- Reviewing remote/PR changes (use `gh` CLI or web) +- User explicitly asks for non-interactive review 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 |
