summaryrefslogtreecommitdiff
path: root/prompts/skills
diff options
context:
space:
mode:
Diffstat (limited to 'prompts/skills')
-rw-r--r--prompts/skills/continue-reminder/SKILL.md99
-rw-r--r--prompts/skills/continue-reminder/references/amp.md25
-rw-r--r--prompts/skills/continue-reminder/references/claude.md114
-rw-r--r--prompts/skills/continue-reminder/references/codex.md28
-rw-r--r--prompts/skills/continue-reminder/references/opencode.md30
-rw-r--r--prompts/skills/rocky-vm-setup/SKILL.md16
-rw-r--r--prompts/skills/rocky-vm-setup/references/git-remotes.md13
-rw-r--r--prompts/skills/rocky-vm-setup/references/notes.md5
-rw-r--r--prompts/skills/rocky-vm-setup/references/overview.md42
-rw-r--r--prompts/skills/rocky-vm-setup/references/privileges.md20
-rw-r--r--prompts/skills/rocky-vm-setup/references/rex.md22
-rw-r--r--prompts/skills/rocky-vm-setup/references/scripts.md20
-rw-r--r--prompts/skills/rocky-vm-setup/references/tmux.md72
-rw-r--r--prompts/skills/rocky-vm-setup/references/tools.md69
-rw-r--r--prompts/skills/rocky-vm-setup/references/zrepl.md12
15 files changed, 583 insertions, 4 deletions
diff --git a/prompts/skills/continue-reminder/SKILL.md b/prompts/skills/continue-reminder/SKILL.md
new file mode 100644
index 0000000..b927a31
--- /dev/null
+++ b/prompts/skills/continue-reminder/SKILL.md
@@ -0,0 +1,99 @@
+---
+name: continue-reminder
+description: Resume a paused coding-agent session after a rate limit, cooldown, or scheduled break by using systemd user timers to send keystrokes to the correct tmux pane. Supports Claude, Codex, OpenCode, and AMP agents.
+---
+
+# Continue Reminder via Systemd
+
+Use this skill when a coding agent running inside a tmux pane gets paused (rate-limited, user-throttled, or intentionally interrupted) and you want to **automatically resume** it at a known future time using systemd user timers and services.
+
+## When to Use
+
+- You hit a rate limit in a coding agent (e.g., Claude Code "session limit")
+- You need to schedule a delayed `continue` or equivalent command
+- The agent lives in a tmux pane and accepts keyboard input to resume
+- You want the resume to happen automatically even if you step away
+- You support multiple coding agents (Claude, Codex, OpenCode, ampcode) and need per-agent specifics
+
+## How It Works
+
+1. **Inspect** the tmux pane where the agent is blocked.
+2. **Note** the reset time / condition.
+3. **Create** a systemd user timer that triggers at (or after) that time.
+4. **Create** a oneshot service that runs a script sending the right keystrokes to the tmux pane.
+5. **Enable** the timer; systemd wakes up and resumes the session automatically.
+
+## Reference Files
+
+Each coding agent has its own quirks (prompt text, resume command, key sequences). See the agent-specific sub-references below:
+
+- [Claude](references/claude.md) — Claude Code CLI in tmux, rate-limit screen, `continue`
+- [Codex](references/codex.md) — Placeholder for Codex CLI specifics
+- [OpenCode](references/opencode.md) — Placeholder for OpenCode Agent specifics
+- [ampcode](references/amp.md) — Placeholder for ampcode agent specifics
+
+## Common Ingredients
+
+These pieces are reused across all agent implementations:
+
+| Component | Typical Path / Value |
+|-----------|---------------------|
+| TMUX socket | `/tmp/tmux-$(id -u)/default` |
+| Script dir | `~/.local/bin/` |
+| Service dir | `~/.config/systemd/user/` |
+| Timer type | `OnCalendar` for wall-clock triggers |
+| Service type | `oneshot` |
+
+### Skeleton Script
+
+```bash
+#!/bin/bash
+set -euo pipefail
+
+TMUX_SOCKET="/tmp/tmux-$(id -u)/default"
+TARGET="<session>:<window>.<pane>"
+
+# 1) Acknowledge / select "stop and wait"
+tmux -S "$TMUX_SOCKET" send-keys -t "$TARGET" Enter
+
+# 2) Sleep past the reset window
+sleep <buffer_seconds>
+
+# 3) Send resume command
+tmux -S "$TMUX_SOCKET" send-keys -t "$TARGET" "<resume_command>" Enter
+```
+
+### Skeleton Service
+
+```ini
+[Unit]
+Description=Resume <agent> in tmux pane
+
+[Service]
+Type=oneshot
+ExecStart=%h/.local/bin/<script>.sh
+Environment="PATH=/usr/local/bin:/usr/bin:/bin"
+```
+
+### Skeleton Timer
+
+```ini
+[Unit]
+Description=One-off timer for <agent> tmux resume
+
+[Timer]
+OnCalendar=<ISO_datetime>
+AccuracySec=1s
+Persistent=false
+
+[Install]
+WantedBy=timers.target
+```
+
+### Enable / Check
+
+```bash
+systemctl --user daemon-reload
+systemctl --user enable --now <timer>.timer
+systemctl --user status <timer>.timer
+```
diff --git a/prompts/skills/continue-reminder/references/amp.md b/prompts/skills/continue-reminder/references/amp.md
new file mode 100644
index 0000000..2e14424
--- /dev/null
+++ b/prompts/skills/continue-reminder/references/amp.md
@@ -0,0 +1,25 @@
+# ampcode Agent — Continue Reminder
+
+> **PLACEHOLDER** — This reference has not been populated yet because the ampcode agent was not active in the originating session.
+
+## Known Differences from Claude
+
+| Aspect | Expected Behavior |
+|--------|-------------------|
+| Rate-limit screen | Unknown; ampcode may handle throttling server-side |
+| UI paradigm | Unknown; could be TUI, web UI, or API-driven |
+| Resume command | Unknown |
+| Session model | ampcode might use long-running processes or ephemeral workers |
+
+## TODO
+
+1. Determine ampcode's runtime environment:
+ - Does it run in a tmux pane?
+ - Is it daemonized?
+ - Does it expose a CLI that can be scripted?
+
+2. If ampcode has a resumable terminal session, capture the pane state and record:
+ - Pause / limit indicator text
+ - Keystrokes or commands required to resume
+
+3. Update this file with a concrete example mirroring the Claude reference structure.
diff --git a/prompts/skills/continue-reminder/references/claude.md b/prompts/skills/continue-reminder/references/claude.md
new file mode 100644
index 0000000..10a4d37
--- /dev/null
+++ b/prompts/skills/continue-reminder/references/claude.md
@@ -0,0 +1,114 @@
+# Claude Code CLI — Continue Reminder
+
+Concrete example: Claude Code CLI running in a tmux pane hits a **session rate limit** and presents a `/rate-limit-options` menu.
+
+## Scenario
+
+- tmux session: `ior`
+- Window: `0`
+- Pane: `0.0` (Claude Code blocked)
+- Message: *"You've hit your session limit · resets 1:10pm (Europe/Sofia)"*
+- Menu option 1: **"Stop and wait for limit to reset"**
+- Resume command after reset: **`continue`**
+
+## What the Blocked Pane Looks Like
+
+```
+ ❯ 1. Stop and wait for limit to reset
+ 2. Upgrade your plan
+ 3. Upgrade to Team plan
+
+ Enter to confirm · Esc to cancel
+```
+
+## Steps Taken
+
+1. **Capture the pane** to confirm state:
+ ```bash
+ tmux capture-pane -t ior:0.0 -p
+ ```
+
+2. **Create the resume script** at `~/.local/bin/ior-tmux-resume.sh`:
+ ```bash
+ #!/bin/bash
+ set -euo pipefail
+
+ TMUX_SOCKET="/tmp/tmux-$(id -u)/default"
+ TARGET="ior:0.0"
+
+ tmux -S "$TMUX_SOCKET" has-session -t "$TARGET" || {
+ echo "Target pane $TARGET not found"; exit 1;
+ }
+
+ # Select option 1: "Stop and wait for limit to reset"
+ tmux -S "$TMUX_SOCKET" send-keys -t "$TARGET" Enter
+
+ # Wait 5 minutes past the advertised reset time
+ sleep 300
+
+ # Resume the Claude session
+ tmux -S "$TMUX_SOCKET" send-keys -t "$TARGET" "continue" Enter
+ ```
+
+3. **Create the systemd user service** at `~/.config/systemd/user/ior-resume.service`:
+ ```ini
+ [Unit]
+ Description=Resume Claude in ior tmux pane after rate-limit reset
+
+ [Service]
+ Type=oneshot
+ ExecStart=/home/paul/.local/bin/ior-tmux-resume.sh
+ Environment="PATH=/usr/local/bin:/usr/bin:/bin"
+ ```
+
+4. **Create the systemd user timer** at `~/.config/systemd/user/ior-resume.timer`:
+ ```ini
+ [Unit]
+ Description=One-off timer for ior tmux resume at 13:10 today
+
+ [Timer]
+ OnCalendar=2026-06-11 13:10:00
+ AccuracySec=1s
+ Persistent=false
+
+ [Install]
+ WantedBy=timers.target
+ ```
+
+5. **Make executable and enable**:
+ ```bash
+ chmod +x ~/.local/bin/ior-tmux-resume.sh
+ systemctl --user daemon-reload
+ systemctl --user enable --now ior-resume.timer
+ systemctl --user status ior-resume.timer
+ ```
+
+## Verification
+
+Check timer state:
+```bash
+systemctl --user status ior-resume.timer
+```
+
+Expected output:
+```
+ Trigger: Thu 2026-06-11 13:10:00 EEST; 4h 3min left
+ Triggers: ● ior-resume.service
+```
+
+## Key Details
+
+| Item | Value |
+|------|-------|
+| Rate-limit reset time | Shown in the Claude banner (tz-aware) |
+| TMUX socket | `/tmp/tmux-$(id -u)/default` |
+| First keystroke | `Enter` confirms menu option 1 |
+| Buffer sleep | `300` seconds (5 min) past reset time |
+| Resume command | `continue` |
+| Command suffix | `Enter` |
+
+## Caveats
+
+- The `continue` command is specific to Claude Code CLI; other agents use different resume verbs.
+- If Claude changes its rate-limit UI (e.g., adds a countdown instead of a menu), update the first `send-keys` accordingly.
+- `tmux send-keys` injects literal keystrokes; if the pane has scrolled or lost focus, the keys may land in the wrong place. Consider pinning the pane or using `tmux select-pane` first.
diff --git a/prompts/skills/continue-reminder/references/codex.md b/prompts/skills/continue-reminder/references/codex.md
new file mode 100644
index 0000000..a754638
--- /dev/null
+++ b/prompts/skills/continue-reminder/references/codex.md
@@ -0,0 +1,28 @@
+# Codex CLI — Continue Reminder
+
+> **PLACEHOLDER** — This reference has not been populated yet because Codex CLI was not active in the originating session.
+
+## Known Differences from Claude
+
+| Aspect | Expected Behavior |
+|--------|-------------------|
+| Rate-limit screen | May differ; verify by capturing the tmux pane |
+| Menu confirmation | May require `Enter` or a different key |
+| Resume command | Likely **not** `continue`; inspect the prompt text |
+| Session ID | Typically tied to a `codex` tmux session name |
+
+## TODO
+
+1. Capture a Codex rate-limit screen in a tmux pane and record:
+ - Exact prompt text
+ - Available options / key bindings
+ - Reset time format and timezone
+ - Correct resume command (e.g., `resume`, `go`, `y`, etc.)
+
+2. Update this file with a concrete example mirroring the Claude reference structure:
+ - Scenario
+ - Steps Taken
+ - Verification commands
+ - Key Details table
+
+3. Update `../SKILL.md` if Codex requires a fundamentally different approach (unlikely, but possible).
diff --git a/prompts/skills/continue-reminder/references/opencode.md b/prompts/skills/continue-reminder/references/opencode.md
new file mode 100644
index 0000000..f2fe7c7
--- /dev/null
+++ b/prompts/skills/continue-reminder/references/opencode.md
@@ -0,0 +1,30 @@
+# OpenCode Agent — Continue Reminder
+
+> **PLACEHOLDER** — This reference has not been populated yet because the OpenCode agent was not active in the originating session.
+
+## Known Differences from Claude
+
+| Aspect | Expected Behavior |
+|--------|-------------------|
+| Rate-limit screen | Unknown; OpenCode may throttle differently |
+| UI paradigm | May be chat-based rather than menu-based |
+| Resume command | Unknown; likely not `continue` |
+| Integration | May run outside tmux (e.g., VS Code extension or standalone binary) |
+
+## TODO
+
+1. Identify how OpenCode signals a pause / rate limit:
+ - Does it print a message in the terminal?
+ - Does it spawn an interactive prompt?
+ - Is there a headless mode that behaves differently?
+
+2. If OpenCode runs in tmux, capture the pane state and record:
+ - Pause / limit indicator text
+ - Required keystrokes to acknowledge
+ - Resume command or hotkey
+
+3. If OpenCode runs outside tmux (e.g., via LSP or IDE), determine whether:
+ - `tmux send-keys` is still viable (IDE integrated terminal)
+ - An alternative IPC mechanism is needed (API, file watcher, signal)
+
+4. Update this file with a concrete example mirroring the Claude reference structure.
diff --git a/prompts/skills/rocky-vm-setup/SKILL.md b/prompts/skills/rocky-vm-setup/SKILL.md
index 661b9ae..f21061a 100644
--- a/prompts/skills/rocky-vm-setup/SKILL.md
+++ b/prompts/skills/rocky-vm-setup/SKILL.md
@@ -59,6 +59,7 @@ Short LAN aliases for all f3s hosts (short, `.lan`, and `.lan.buetow.org` varian
| tmux | 3.2a | `dnf install -y tmux` |
| tmux prefix | C-g | **Rocky override** — nested tmux (see below) |
| fish | 3.7.1 | `dnf install -y fish` (EPEL) |
+| helix | 25.07.1 | `dnf install -y helix helix-themes` (EPEL) |
| amp | 0.7.1 | Downloaded binary from GitHub releases |
| claude-code | 2.1.169 | `npm install -g @anthropic-ai/claude-code` |
| pi coding agent | 0.79.0 | `npm install -g @earendil-works/pi-coding-agent` |
@@ -75,10 +76,10 @@ Earth (outer tmux) uses the default **C-b** prefix. Rocky (inner tmux) uses **C-
**Visual distinction — you'll never confuse the two:**
-| Layer | Prefix | Active Border | Status Bar | Pane Indicators |
-|-------|--------|---------------|------------|-----------------|
-| **Earth (outer)** | `C-b` | **Magenta** | White-on-purple | Default blue |
-| **Rocky (inner)** | `C-g` | **Bright Red** | **Black-on-orange** with `[ROCKY]` label | **Red/orange** pane numbers, border labels |
+| Layer | Prefix | Active Border | Status Bar | Pane Indicators | TERM |
+|-------|--------|---------------|------------|-----------------|------|
+| **Earth (outer)** | `C-b` | **Magenta** | White-on-purple | Default blue | `tmux-256color` |
+| **Rocky (inner)** | `C-g` | **Bright Red** | **Black-on-orange** with `[ROCKY]` label | **Red/orange** pane numbers, border labels | `tmux-256color` |
**Workflow:**
| Key | Action |
@@ -109,6 +110,13 @@ set -g display-panes-active-colour brightred # active pane number
set -g pane-border-status top # show pane info on borders
set -g pane-border-format '#[fg=colour208] #{pane_index} #[fg=brightred]#{pane_title} '
set -g window-status-current-format ' #I*#[bg=brightred,fg=white] #W '
+
+# TERM inside tmux — must advertise 256 colors so helix/fzf/etc work
+set -g default-terminal 'tmux-256color'
+set -ga terminal-overrides ',xterm-256color:Tc,*-256color:Tc'
+
+# Pass COLORTERM through from outer SSH session so helix knows truecolor is available
+set -g update-environment 'DISPLAY SSH_ASKPASS SSH_AGENT_LAUNCHER SSH_AUTH_SOCK SSH_CONNECTION SSH_TTY WINDOWID XAUTHORITY TERM COLORTERM'
```
This is deployed by the `home_tmux_rocky` Rex task (runs only when `hostname =~ /rocky/`).
diff --git a/prompts/skills/rocky-vm-setup/references/git-remotes.md b/prompts/skills/rocky-vm-setup/references/git-remotes.md
new file mode 100644
index 0000000..bddc6c3
--- /dev/null
+++ b/prompts/skills/rocky-vm-setup/references/git-remotes.md
@@ -0,0 +1,13 @@
+# Git Remotes on Rocky
+
+All repos available on the local git server have `r0`, `r1`, `r2` remotes replacing any codeberg ones:
+
+```
+url = ssh://git@r0:30022/repos/REPO.git
+url = ssh://git@r1:30022/repos/REPO.git
+url = ssh://git@r2:30022/repos/REPO.git
+```
+
+Repos pushed: conf, dotfiles, gemtexter, gitsyncer, goprecords, gt, hexai, hypr, ior, photoalbum, rcm, snonux, tasksamurai, wireguardmeshgenerator
+
+The public keys of both `root` and `paul` on rocky are in the k3s `git-server-authorized-keys` secret (namespace `cicd`).
diff --git a/prompts/skills/rocky-vm-setup/references/notes.md b/prompts/skills/rocky-vm-setup/references/notes.md
new file mode 100644
index 0000000..124c345
--- /dev/null
+++ b/prompts/skills/rocky-vm-setup/references/notes.md
@@ -0,0 +1,5 @@
+# Notes
+
+- The `claude` wrapper must **not** be a shell script calling the JS wrapper — that caused a fork bomb because `cli-wrapper.cjs` tried to exec the `claude` binary but found the script instead. Use a direct symlink or the npm-installed binary.
+- Node.js is installed via `dnf module install nodejs:22/common` (pi 0.75.0+ requires Node >= 22.19.0).
+- `amp` panics in non-TTY environments — that's expected for a TUI editor.
diff --git a/prompts/skills/rocky-vm-setup/references/overview.md b/prompts/skills/rocky-vm-setup/references/overview.md
new file mode 100644
index 0000000..f4256d2
--- /dev/null
+++ b/prompts/skills/rocky-vm-setup/references/overview.md
@@ -0,0 +1,42 @@
+# Rocky VM Overview
+
+The `rocky` VM is a plain Rocky Linux 9 bhyve guest on **f3** (LAN IP `192.168.1.123`, WireGuard `192.168.2.123`). It is **not** part of the k3s cluster and serves as a general-purpose build / dev / git client VM.
+
+Parent infrastructure: see the [`f3s`](skills/f3s) skill (f3 host, zrepl, bhyve, git server).
+
+## SSH Keys
+
+| Key | Path | Purpose |
+|-----|------|---------|
+| Root VM key | `/root/.ssh/id_ed25519` | Git server SSH auth |
+| Paul VM key | `/home/paul/.ssh/id_ed25519` | Git server SSH auth, local remotes |
+
+The public keys are added to the k3s `git-server-authorized-keys` secret (namespace `cicd`) so both `root` and `paul` can push/pull via `git@r{N}:30022`.
+
+```sh
+# Regenerate if needed
+ssh-keygen -t ed25519 -N '' -f /root/.ssh/id_ed25519 -C 'root@rocky.f3s.lan.buetow.org'
+```
+
+## /etc/hosts
+
+Short LAN aliases for all f3s hosts (short, `.lan`, and `.lan.buetow.org` variants):
+
+```
+# f3s k3s node LAN aliases
+192.168.1.120 r0 r0.lan r0.lan.buetow.org
+192.168.1.121 r1 r1.lan r1.lan.buetow.org
+192.168.1.122 r2 r2.lan r2.lan.buetow.org
+
+# f3s FreeBSD host LAN aliases
+192.168.1.130 f0 f0.lan f0.lan.buetow.org
+192.168.1.131 f1 f1.lan f1.lan.buetow.org
+192.168.1.132 f2 f2.lan f2.lan.buetow.org
+192.168.1.133 f3 f3.lan f3.lan.buetow.org
+
+# f3s Raspberry Pi LAN aliases
+192.168.1.125 pi0 pi0.lan pi0.lan.buetow.org
+192.168.1.126 pi1 pi1.lan pi1.lan.buetow.org
+192.168.1.127 pi2 pi2.lan pi2.lan.buetow.org
+192.168.1.128 pi3 pi3.lan pi3.lan.buetow.org
+```
diff --git a/prompts/skills/rocky-vm-setup/references/privileges.md b/prompts/skills/rocky-vm-setup/references/privileges.md
new file mode 100644
index 0000000..d8f25ba
--- /dev/null
+++ b/prompts/skills/rocky-vm-setup/references/privileges.md
@@ -0,0 +1,20 @@
+# User and Privileges
+
+**`root`** — full root, used for package installs and Rex tasks.
+
+**`paul`**
+- **Removed from `wheel`** group. No general `sudo` access.
+- **Only** allowed to run without password:
+ ```
+ /home/paul/scripts/update-coding-agents
+ ```
+- Home: `/home/paul`
+- Git repos: `~/git/` (cloned via local `r0`/`r1`/`r2` remotes)
+
+## Sudoers config
+
+```
+paul ALL=(root) NOPASSWD: /home/paul/scripts/update-coding-agents
+```
+
+No other sudo privileges. The `wheel-nopasswd` file was removed and paul was removed from the `wheel` group.
diff --git a/prompts/skills/rocky-vm-setup/references/rex.md b/prompts/skills/rocky-vm-setup/references/rex.md
new file mode 100644
index 0000000..1eba83a
--- /dev/null
+++ b/prompts/skills/rocky-vm-setup/references/rex.md
@@ -0,0 +1,22 @@
+# Rex Usage
+
+The dotfiles repo (`~/git/dotfiles`) contains the main `Rexfile`.
+
+```sh
+# Install packages (as root)
+rex pkg_rocky
+
+# Deploy dotfiles (as paul)
+rex home
+```
+
+`pkg_rocky` uses Rex's `pkg` directive which requires root — no `sudo` wrappers.
+
+## Rocky-specific Rex tasks
+
+| Task | Runs when | What it does |
+|------|-----------|--------------|
+| `home_tmux_rocky` | `hostname =~ /rocky/` | Sources `tmux.rocky.conf` at the **end** of `tmux.conf` so red/orange colors win |
+| `pkg_rocky` | Manually (as root) | Installs packages: tmux, fish, helix, zoxide, fzf, golang, nodejs, etc. |
+
+The `home_tmux_rocky` task also cleans stale references from `tmux.local.conf` and strips the `extended-keys-format` line for tmux 3.2a compatibility.
diff --git a/prompts/skills/rocky-vm-setup/references/scripts.md b/prompts/skills/rocky-vm-setup/references/scripts.md
new file mode 100644
index 0000000..14fa7e0
--- /dev/null
+++ b/prompts/skills/rocky-vm-setup/references/scripts.md
@@ -0,0 +1,20 @@
+# Scripts
+
+## `/home/paul/scripts/update-coding-agents`
+
+```sh
+#!/bin/sh
+set -e
+if [ "$(id -u)" -ne 0 ]; then
+ exec sudo "$0" "$@"
+fi
+echo "Updating Claude Code..."
+npm update -g @anthropic-ai/claude-code @anthropic-ai/claude-code-linux-x64
+echo "Updating pi coding agent..."
+npm update -g @earendil-works/pi-coding-agent
+echo "All coding agents updated."
+```
+
+Run as paul: `$ /home/paul/scripts/update-coding-agents`
+
+Sudoers allows this specific script without a password. No other root access for paul.
diff --git a/prompts/skills/rocky-vm-setup/references/tmux.md b/prompts/skills/rocky-vm-setup/references/tmux.md
new file mode 100644
index 0000000..c861d78
--- /dev/null
+++ b/prompts/skills/rocky-vm-setup/references/tmux.md
@@ -0,0 +1,72 @@
+# Nested tmux on Rocky
+
+Earth (outer tmux) uses the default **C-b** prefix. Rocky (inner tmux) uses **C-g** so you can control both layers.
+
+## Visual distinction
+
+| Layer | Prefix | Active Border | Status Bar | Pane Indicators |
+|-------|--------|---------------|------------|-----------------|
+| **Earth (outer)** | `C-b` | **Magenta** | White-on-purple | Default blue |
+| **Rocky (inner)** | `C-g` | **Bright Red** | **Black-on-orange** (`colour208`) with `[ROCKY]` label | **Red/orange** pane numbers, border labels |
+
+## Workflow
+
+| Key | Action |
+|-----|--------|
+| `C-b c` | Create window in outer tmux (earth) |
+| `C-g c` | Create window in inner tmux (rocky) |
+| `C-b b` | Send `C-b` through to inner tmux |
+| `C-g g` | Send `C-g` through to inner-inner tmux |
+
+## Config
+
+Rocky config is in `~/.config/tmux/tmux.rocky.conf` and sourced from `tmux.local.conf`:
+
+```sh
+# ~/.config/tmux/tmux.rocky.conf
+unbind C-b
+set -g prefix C-g
+bind C-g send-prefix
+
+# Drastic RED/ORANGE color scheme
+set -g pane-active-border-style 'fg=brightred,bold'
+set -g status-style 'bg=colour208,fg=black,bold'
+set -g status-left ' [ROCKY] #[bg=brightred,fg=white] #S '
+set -g window-status-current-style 'bg=brightred,fg=white,bold'
+set -g window-status-style 'bg=colour208,fg=black'
+
+# Active pane indicators
+set -g display-panes-colour colour208 # prefix+q pane numbers
+set -g display-panes-active-colour brightred # active pane number
+set -g pane-border-status top # show pane info on borders
+set -g pane-border-format '#[fg=colour208] #{pane_index} #[fg=brightred]#{pane_title} '
+set -g window-status-current-format ' #I*#[bg=brightred,fg=white] #W '
+```
+
+## Important: source ordering
+
+The rocky config must be sourced **at the end of** `~/.config/tmux/tmux.conf` so its color overrides win over the shared config. The `home_tmux_rocky` Rex task handles this by:
+
+1. Cleaning any stale reference from `tmux.local.conf`
+2. Appending `source-file ~/.config/tmux/tmux.rocky.conf` to the **end** of `tmux.conf`
+
+## Terminal / color support
+
+The inner tmux must advertise 256 colors (or helix/fzf/etc fall back to 16):
+
+```sh
+set -g default-terminal 'tmux-256color'
+set -ga terminal-overrides ',xterm-256color:Tc,*-256color:Tc'
+```
+
+Without `default-terminal`, tmux defaults to `TERM=screen` (8 colors).
+
+### Truecolor (24-bit / 16777216 colors)
+
+For truecolor themes in helix, `COLORTERM=truecolor` must also be present inside tmux. tmux strips it by default via `update-environment`. Pass it through:
+
+```sh
+set -g update-environment '... TERM COLORTERM'
+```
+
+Without this, helix sees `COLORTERM=` and falls back to 256 colors only, breaking truecolor themes.
diff --git a/prompts/skills/rocky-vm-setup/references/tools.md b/prompts/skills/rocky-vm-setup/references/tools.md
new file mode 100644
index 0000000..c753414
--- /dev/null
+++ b/prompts/skills/rocky-vm-setup/references/tools.md
@@ -0,0 +1,69 @@
+# Installed Tools
+
+| Tool | Version | How Installed |
+|------|---------|---------------|
+| tmux | 3.2a | `dnf install -y tmux` |
+| tmux prefix | C-g | **Rocky override** — nested tmux (see [tmux.md](tmux.md)) |
+| fish | 3.7.1 | `dnf install -y fish` (EPEL) |
+| helix | 25.07.1 | `dnf install -y helix helix-themes` (EPEL) |
+| amp | 0.7.1 | Downloaded binary from GitHub releases |
+| claude-code | 2.1.169 | `npm install -g @anthropic-ai/claude-code` |
+| pi coding agent | 0.79.0 | `npm install -g @earendil-works/pi-coding-agent` |
+| taskwarrior | 2.6.2 | **Built from source** (see below) |
+| Rex | 1.16.1 | `cpanm Rex` (requires expat-devel, perl-LWP-Protocol-https) |
+| zoxide | 0.9.8 | `dnf install -y zoxide` (EPEL) |
+| fzf | 0.58.0 | `dnf install -y fzf` (EPEL) |
+| fzf fish plugin | — | **fisher install PatrickF1/fzf.fish** |
+| ask, hexai*, gt, gitsyncer, etc. | — | `go install codeberg.org/snonux/...` (see update::tools) |
+
+## Building taskwarrior from source
+
+Rocky 9 does not ship `task`/`taskwarrior`. v3.x requires Rust; v2.6.2 compiles cleanly.
+
+```sh
+# deps
+dnf install -y cmake gcc-c++ make libuuid-devel gnutls-devel libssh2-devel
+
+# build
+git clone --depth 1 --branch v2.6.2 \
+ https://github.com/GothenburgBitFactory/taskwarrior.git /tmp/tw-build
+cd /tmp/tw-build
+cmake -S . -B build -DCMAKE_BUILD_TYPE=Release
+cmake --build build -j$(nproc)
+cmake --install build
+
+# verify
+/usr/local/bin/task --version # 2.6.2
+```
+
+## First-run fish setup
+
+After the dotfiles `home` task deploys fish config, some plugins and binaries are expected but not yet present:
+
+```sh
+# 1. Install fisher (fish plugin manager)
+curl -sL https://raw.githubusercontent.com/jorgebucaran/fisher/main/functions/fisher.fish | source
+fisher install jorgebucaran/fisher
+
+# 2. Install the fzf.fish plugin (provides fzf_configure_bindings)
+fisher install PatrickF1/fzf.fish
+
+# 3. First-run taskwarrior creates ~/.taskrc
+yes | task >/dev/null 2>&1
+
+# 4. Install Go tooling binaries (run as paul)
+for prog in ask hexai hexai-lsp-server hexai-tmux-action hexai-tmux-edit hexai-mcp-server; do
+ go install codeberg.org/snonux/hexai/cmd/$prog@latest
+done
+for prog in tasksamurai timesamurai gt; do
+ go install codeberg.org/snonux/$prog/cmd/$prog@latest
+done
+for prog in gitsyncer gos snonux; do
+ go install codeberg.org/snonux/$prog/cmd/$prog@latest
+done
+# (foostore, loadbars, totalrecall, goprecords may need X11/GL deps for GUI — skip on headless)
+```
+
+## tmux 3.2a compatibility note
+
+The dotfiles `tmux.conf` includes `set -g extended-keys-format csi-u` (tmux 3.3+). On rocky this line is automatically stripped by the `home_tmux_rocky` Rex task. If you deploy manually, remove or comment out that line.
diff --git a/prompts/skills/rocky-vm-setup/references/zrepl.md b/prompts/skills/rocky-vm-setup/references/zrepl.md
new file mode 100644
index 0000000..4fafe64
--- /dev/null
+++ b/prompts/skills/rocky-vm-setup/references/zrepl.md
@@ -0,0 +1,12 @@
+# ZFS Snapshot / Replication
+
+The rocky VM dataset `zroot/bhyve/rocky` is managed by **zrepl** on the FreeBSD host f3. It is **not** included in local `zfs-periodic` snapshots.
+
+| Property | Value |
+|------------|-------|
+| Snapshots | Every 10 minutes via zrepl (`zrepl_` prefix) |
+| Replication | f3 → f2 (`zroot/sink/f3/zroot/bhyve/rocky`) |
+| Retention | 10 immediate + 24 hourly + 14 daily |
+| Local snap job | `zroot/bhyve/rocky` excluded from `local_zfs_snapshots` |
+
+See [`f3s` skill zrepl.md](skills/f3s/references/storage/zrepl.md) for full config.