From 91116053938d5a704818f5b3b1893e6fecd6772a Mon Sep 17 00:00:00 2001 From: Zhongwei Li Date: Sun, 30 Nov 2025 09:01:48 +0800 Subject: [PATCH] Initial commit --- .claude-plugin/plugin.json | 11 ++ README.md | 3 + plugin.lock.json | 65 +++++++ skills/interactive-commands/SKILL.md | 178 ++++++++++++++++++ .../scripts/tmux-wrapper.sh | 84 +++++++++ skills/truenas-docker-ops/SKILL.md | 170 +++++++++++++++++ .../references/server_layout.md | 89 +++++++++ .../scripts/docker_exec_python.sh | 18 ++ .../scripts/docker_exec_sqlite.sh | 35 ++++ 9 files changed, 653 insertions(+) create mode 100644 .claude-plugin/plugin.json create mode 100644 README.md create mode 100644 plugin.lock.json create mode 100644 skills/interactive-commands/SKILL.md create mode 100755 skills/interactive-commands/scripts/tmux-wrapper.sh create mode 100644 skills/truenas-docker-ops/SKILL.md create mode 100644 skills/truenas-docker-ops/references/server_layout.md create mode 100755 skills/truenas-docker-ops/scripts/docker_exec_python.sh create mode 100755 skills/truenas-docker-ops/scripts/docker_exec_sqlite.sh diff --git a/.claude-plugin/plugin.json b/.claude-plugin/plugin.json new file mode 100644 index 0000000..95ebd49 --- /dev/null +++ b/.claude-plugin/plugin.json @@ -0,0 +1,11 @@ +{ + "name": "homelab", + "description": "Skills for managing homelab infrastructure including remote server management", + "version": "1.2.0", + "author": { + "name": "Thurston Sandberg" + }, + "skills": [ + "./skills" + ] +} \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..4a914ee --- /dev/null +++ b/README.md @@ -0,0 +1,3 @@ +# homelab + +Skills for managing homelab infrastructure including remote server management diff --git a/plugin.lock.json b/plugin.lock.json new file mode 100644 index 0000000..96eedf2 --- /dev/null +++ b/plugin.lock.json @@ -0,0 +1,65 @@ +{ + "$schema": "internal://schemas/plugin.lock.v1.json", + "pluginId": "gh:thurstonsand/thurstons-claude-skills:homelab", + "normalized": { + "repo": null, + "ref": "refs/tags/v20251128.0", + "commit": "ba91bf390abdde8f052e6e8ab43a7b2419046550", + "treeHash": "4558f78490576ea7dd0e658e88ce7b826c15ceade4b605e4ccd4e0791db13159", + "generatedAt": "2025-11-28T10:28:40.732478Z", + "toolVersion": "publish_plugins.py@0.2.0" + }, + "origin": { + "remote": "git@github.com:zhongweili/42plugin-data.git", + "branch": "master", + "commit": "aa1497ed0949fd50e99e70d6324a29c5b34f9390", + "repoRoot": "/Users/zhongweili/projects/openmind/42plugin-data" + }, + "manifest": { + "name": "homelab", + "description": "Skills for managing homelab infrastructure including remote server management", + "version": "1.2.0" + }, + "content": { + "files": [ + { + "path": "README.md", + "sha256": "4806b463a85fd30901e39fbcadc69391daba2875f4df07d65dcf848439b35e76" + }, + { + "path": ".claude-plugin/plugin.json", + "sha256": "564d4711cae86644a11112ec042c1607a00eb326a4a491692b49f142ab0ff6e8" + }, + { + "path": "skills/truenas-docker-ops/SKILL.md", + "sha256": "a8d0a74bad1ee4120892551c7dabc5035e50f33ce444e0533a91286e77bb81b7" + }, + { + "path": "skills/truenas-docker-ops/references/server_layout.md", + "sha256": "03308a65de98c0e0f707bdf9cd572f8f7d61254f72e5704c3beb4d8abadda917" + }, + { + "path": "skills/truenas-docker-ops/scripts/docker_exec_sqlite.sh", + "sha256": "4152a50bb605a66e41dab7f1544be5c1b97cb75856e3de18b06e89002193fab0" + }, + { + "path": "skills/truenas-docker-ops/scripts/docker_exec_python.sh", + "sha256": "02a9ff11d65ab1bf3b49a156c60a6088816b7b572f4a2494148707b8e8065886" + }, + { + "path": "skills/interactive-commands/SKILL.md", + "sha256": "e47d9f3603da5b195657971fdcfc9534bac2935e66b7865b4134589646ab5e12" + }, + { + "path": "skills/interactive-commands/scripts/tmux-wrapper.sh", + "sha256": "c5d340a2a014679c57dbeb75a7248e32c03a138c77159a68b14b80e6fcc7355a" + } + ], + "dirSha256": "4558f78490576ea7dd0e658e88ce7b826c15ceade4b605e4ccd4e0791db13159" + }, + "security": { + "scannedAt": null, + "scannerVersion": null, + "flags": [] + } +} \ No newline at end of file diff --git a/skills/interactive-commands/SKILL.md b/skills/interactive-commands/SKILL.md new file mode 100644 index 0000000..a89c90b --- /dev/null +++ b/skills/interactive-commands/SKILL.md @@ -0,0 +1,178 @@ +--- +name: using-tmux-for-interactive-commands +description: Use when you need to run interactive CLI tools (vim, git rebase -i, Python REPL, etc.) that require real-time input/output - provides tmux-based approach for controlling interactive sessions through detached sessions and send-keys +--- + +# Using tmux for Interactive Commands + +## Overview + +Interactive CLI tools (vim, interactive git rebase, REPLs, etc.) cannot be controlled through standard bash because they require a real terminal. tmux provides detached sessions that can be controlled programmatically via `send-keys` and `capture-pane`. + +## When to Use + +**Use tmux when:** +- Running vim, nano, or other text editors programmatically +- Controlling interactive REPLs (Python, Node, etc.) +- Handling interactive git commands (`git rebase -i`, `git add -p`) +- Working with full-screen terminal apps (htop, etc.) +- Commands that require terminal control codes or readline + +**Don't use for:** +- Simple non-interactive commands (use regular Bash tool) +- Commands that accept input via stdin redirection +- One-shot commands that don't need interaction + +## Quick Reference + +| Task | Command | +| -------------- | ----------------------------------------- | +| Start session | `tmux new-session -d -s ` | +| Send input | `tmux send-keys -t 'text' Enter` | +| Capture output | `tmux capture-pane -t -p` | +| Stop session | `tmux kill-session -t ` | +| List sessions | `tmux list-sessions` | + +## Core Pattern + +### Before (Won't Work) +```bash +# This hangs because vim expects interactive terminal +bash -c "vim file.txt" +``` + +### After (Works) +```bash +# Create detached tmux session +tmux new-session -d -s edit_session vim file.txt + +# Send commands (Enter, Escape are tmux key names) +tmux send-keys -t edit_session 'i' 'Hello World' Escape ':wq' Enter + +# Capture what's on screen +tmux capture-pane -t edit_session -p + +# Clean up +tmux kill-session -t edit_session +``` + +## Implementation + +### Basic Workflow + +1. **Create detached session** with the interactive command +2. **Wait briefly** for initialization (100-500ms depending on command) +3. **Send input** using `send-keys` (can send special keys like Enter, Escape) +4. **Capture output** using `capture-pane -p` to see current screen state +5. **Repeat** steps 3-4 as needed +6. **Terminate** session when done + +### Special Keys + +Common tmux key names: +- `Enter` - Return/newline +- `Escape` - ESC key +- `C-c` - Ctrl+C +- `C-x` - Ctrl+X +- `Up`, `Down`, `Left`, `Right` - Arrow keys +- `Space` - Space bar +- `BSpace` - Backspace + +### Working Directory + +Specify working directory when creating session: +```bash +tmux new-session -d -s git_session -c /path/to/repo git rebase -i HEAD~3 +``` + +### Helper Wrapper + +Run the executable helper script `scripts/tmux-wrapper.sh`: +```bash +# Start session +scripts/tmux-wrapper.sh start [args...] + +# Send input +scripts/tmux-wrapper.sh send 'text' Enter + +# Capture current state +scripts/tmux-wrapper.sh capture + +# Stop +scripts/tmux-wrapper.sh stop +``` + +## Common Patterns + +### Python REPL +```bash +tmux new-session -d -s python python3 -i +tmux send-keys -t python 'import math' Enter +tmux send-keys -t python 'print(math.pi)' Enter +tmux capture-pane -t python -p # See output +tmux kill-session -t python +``` + +### Vim Editing +```bash +tmux new-session -d -s vim vim /tmp/file.txt +sleep 0.3 # Wait for vim to start +tmux send-keys -t vim 'i' 'New content' Escape ':wq' Enter +# File is now saved +``` + +### Interactive Git Rebase +```bash +tmux new-session -d -s rebase -c /repo/path git rebase -i HEAD~3 +sleep 0.5 +tmux capture-pane -t rebase -p # See rebase editor +# Send commands to modify rebase instructions +tmux send-keys -t rebase 'Down' 'Home' 'squash' Escape +tmux send-keys -t rebase ':wq' Enter +``` + +## Common Mistakes + +### Not Waiting After Session Start +**Problem:** Capturing immediately after `new-session` shows blank screen + +**Fix:** Add brief sleep (100-500ms) before first capture +```bash +tmux new-session -d -s sess command +sleep 0.3 # Let command initialize +tmux capture-pane -t sess -p +``` + +### Forgetting Enter Key +**Problem:** Commands typed but not executed + +**Fix:** Explicitly send Enter +```bash +tmux send-keys -t sess 'print("hello")' Enter # Note: Enter is separate argument +``` + +### Using Wrong Key Names +**Problem:** `tmux send-keys -t sess '\n'` doesn't work + +**Fix:** Use tmux key names: `Enter`, not `\n` +```bash +tmux send-keys -t sess 'text' Enter # ✓ +tmux send-keys -t sess 'text\n' # ✗ +``` + +### Not Cleaning Up Sessions +**Problem:** Orphaned tmux sessions accumulate + +**Fix:** Always kill sessions when done +```bash +tmux kill-session -t session_name +# Or check for existing: tmux has-session -t name 2>/dev/null +``` + +## Real-World Impact + +- Enables programmatic control of vim/nano for file editing +- Allows automation of interactive git workflows (rebase, add -p) +- Makes REPL-based testing/debugging possible +- Unblocks any tool that requires terminal interaction +- No need to build custom PTY management - tmux handles it all diff --git a/skills/interactive-commands/scripts/tmux-wrapper.sh b/skills/interactive-commands/scripts/tmux-wrapper.sh new file mode 100755 index 0000000..8d67613 --- /dev/null +++ b/skills/interactive-commands/scripts/tmux-wrapper.sh @@ -0,0 +1,84 @@ +#!/bin/bash +# Simple wrapper around tmux for Claude Code to interact with interactive programs + +set -euo pipefail + +ACTION="${1:-}" +SESSION_NAME="${2:-}" + +case "$ACTION" in + start) + COMMAND="${3:-bash}" + shift 3 || true + ARGS="$*" + + # Create new detached session + if [ -n "$ARGS" ]; then + tmux new-session -d -s "$SESSION_NAME" "$COMMAND" "$@" + else + tmux new-session -d -s "$SESSION_NAME" "$COMMAND" + fi + + # Wait for initial output + sleep 0.3 + + # Capture and display initial state + echo "Session: $SESSION_NAME" + echo "---" + tmux capture-pane -t "$SESSION_NAME" -p + ;; + + send) + shift 2 + if [ $# -eq 0 ]; then + echo "Error: No input provided" >&2 + exit 1 + fi + + # Send all arguments as separate keys (allows "Enter", "Escape", etc.) + tmux send-keys -t "$SESSION_NAME" "$@" + + # Wait a moment for output + sleep 0.2 + + # Capture and display updated state + echo "Session: $SESSION_NAME" + echo "---" + tmux capture-pane -t "$SESSION_NAME" -p + ;; + + capture) + echo "Session: $SESSION_NAME" + echo "---" + tmux capture-pane -t "$SESSION_NAME" -p + ;; + + stop) + tmux kill-session -t "$SESSION_NAME" + echo "Session $SESSION_NAME terminated" + ;; + + list) + tmux list-sessions + ;; + + *) + cat < [args...] + +Actions: + start [args...] - Start a new interactive session + send - Send input to session (use Enter for newline) + capture - Capture current pane output + stop - Terminate session + list - List all sessions + +Examples: + $0 start python_session python3 -i + $0 send python_session 'print("hello")' Enter + $0 capture python_session + $0 stop python_session +EOF + exit 1 + ;; +esac \ No newline at end of file diff --git a/skills/truenas-docker-ops/SKILL.md b/skills/truenas-docker-ops/SKILL.md new file mode 100644 index 0000000..f8eab51 --- /dev/null +++ b/skills/truenas-docker-ops/SKILL.md @@ -0,0 +1,170 @@ +--- +name: truenas-docker-ops +description: Interact with Docker containers running on a TrueNAS server via SSH. Use this skill when working with containers on the remote TrueNAS server, including querying databases, inspecting logs, executing commands, or managing container state. Particularly useful for complex operations requiring nested SSH and docker exec commands with special escape sequences. +--- + +# TrueNAS Docker Operations + +## Overview + +Enable seamless interaction with Docker containers running on a TrueNAS server accessed via SSH. The skill provides helper scripts for complex nested command execution and reference documentation for server layout and container discovery. + +## When to Use This Skill + +Use this skill when: +- Querying databases inside containers on the TrueNAS server +- Executing Python or SQL code in remote containers +- Inspecting container logs, status, or configurations +- Working with containers where nested SSH and docker exec commands require complex escape sequences +- Discovering container locations, volume mappings, or configurations on the server + +## Server Access + +Access the TrueNAS server using the pre-configured SSH alias: + +```bash +ssh truenas +``` + +Authentication is already configured. For detailed server layout information including directory structure and container patterns, refer to `references/server_layout.md`. + +## Core Operations + +### Discover Running Containers + +List all running containers: +```bash +ssh truenas docker ps +``` + +Filter for specific containers: +```bash +ssh truenas docker ps | grep +``` + +### View Container Configuration + +Check the docker-compose file for volume mappings, networks, and settings: +```bash +ssh truenas cat /mnt/performance/home/admin/Develop/nixonomicon/nas/stacks//docker-compose.yml +``` + +### Execute Python Code in Container + +For complex Python execution requiring nested heredocs and escape sequences, use the provided helper script: + +```bash +scripts/docker_exec_python.sh '' +``` + +**Example:** +```bash +scripts/docker_exec_python.sh anypod ' +import sqlite3, json +conn = sqlite3.connect("/data/db/anypod.db") +conn.row_factory = sqlite3.Row +result = conn.execute("SELECT * FROM feed").fetchall() +print(json.dumps([dict(r) for r in result], indent=2)) +conn.close() +' +``` + +The script handles complex quoting and escape sequences for: +1. SSH connection to remote server +2. Docker exec with stdin input +3. Python code containing quotes, newlines, and special characters + +**Note**: When using dictionary access in f-strings (e.g., `f"{dict['key']}"`), extract to variables first to avoid quote escaping issues: +```python +# Instead of: print(f"{row['status']}") +# Use: +status = row["status"] +print(f"{status}") +``` + +### Execute SQLite Queries in Container + +For direct SQLite queries, use the provided helper script: + +```bash +scripts/docker_exec_sqlite.sh '' +``` + +**Example:** +```bash +scripts/docker_exec_sqlite.sh anypod /data/db/anypod.db ' +SELECT feed_id, status, COUNT(*) as count +FROM download +GROUP BY feed_id, status; +' +``` + +### View Container Logs + +```bash +ssh truenas docker logs +``` + +Add `-f` to follow logs in real-time: +```bash +ssh truenas docker logs -f +``` + +### Simple Command Execution + +For straightforward commands without complex escaping needs: +```bash +ssh truenas docker exec -i +``` + +**Example:** +```bash +ssh truenas docker exec -i anypod ls -la /data +``` + +## Directory Structure Reference + +Key locations on the TrueNAS server: + +- **Compose files**: `/mnt/performance/home/admin/Develop/nixonomicon/nas/stacks/` +- **Configs**: `/mnt/performance/docker/` +- **Large data**: `/mnt/capacity/watch/` + +For complete directory structure details and container patterns, see `references/server_layout.md`. + +## Determining What to Run Where + +**Use helper scripts when:** +- Executing multi-line Python code in containers +- Running SQL queries with complex syntax +- Dealing with nested heredocs or quote escaping +- The command would require trial-and-error to get the escaping right + +**Use direct commands when:** +- Running simple single commands +- Viewing logs or status +- Listing files or inspecting basic container state +- The operation doesn't involve complex escape sequences + +## Resources + +### scripts/ + +**`docker_exec_python.sh`** - Execute Python code inside a Docker container on TrueNAS +- Handles complex escape sequences for nested SSH and docker exec with heredoc +- Usage: `scripts/docker_exec_python.sh ''` +- Avoids manual trial-and-error of nested quoting + +**`docker_exec_sqlite.sh`** - Execute SQLite queries inside a Docker container on TrueNAS +- Handles complex escape sequences for SQL queries +- Usage: `scripts/docker_exec_sqlite.sh ''` +- Simplifies database inspection workflows + +### references/ + +**`server_layout.md`** - Complete TrueNAS server structure documentation +- SSH access patterns +- Directory structure for compose files, configs, and data +- Container discovery techniques +- Common container patterns and examples +- Volume mapping conventions diff --git a/skills/truenas-docker-ops/references/server_layout.md b/skills/truenas-docker-ops/references/server_layout.md new file mode 100644 index 0000000..abd78ce --- /dev/null +++ b/skills/truenas-docker-ops/references/server_layout.md @@ -0,0 +1,89 @@ +# TrueNAS Server Layout + +## SSH Access + +Access the TrueNAS server using the pre-configured SSH alias: + +```bash +ssh truenas +``` + +Authentication is already configured on the host machine. + +## Directory Structure + +### Docker Compose Files +All container compose files are located at: +``` +/mnt/performance/home/admin/Develop/nixonomicon/nas/stacks/ +``` + +This is the **source of truth** for container configurations, volume mappings, and network settings. + +### Configuration Files +Container-specific configurations are stored at: +``` +/mnt/performance/docker/ +``` + +Typically organized as `/mnt/performance/docker//`. + +### Data Storage +Large data files (downloads, media, etc.) are stored at: +``` +/mnt/capacity/watch/ +``` + +## Discovering Container Information + +### List Running Containers +```bash +ssh truenas docker ps +``` + +### Find Container Configuration +To understand a container's volume mappings, networks, and settings: +```bash +ssh truenas cat /mnt/performance/home/admin/Develop/nixonomicon/nas/stacks//docker-compose.yml +``` + +### Common Container Pattern +Most containers follow this structure: +- **Config**: `/mnt/performance/docker//` +- **Data**: Varies by container, check compose file for volume mappings +- **Large files**: Often mapped to `/mnt/capacity/watch//` + +## Working with Containers + +### Execute Commands in Container +```bash +ssh truenas docker exec -i +``` + +### View Container Logs +```bash +ssh truenas docker logs +``` + +### Inspect Container Details +```bash +ssh truenas docker inspect +``` + +## Examples + +### Check if anypod container is running +```bash +ssh truenas docker ps | grep anypod +``` + +### View anypod compose configuration +```bash +ssh truenas cat /mnt/performance/home/admin/Develop/nixonomicon/nas/stacks/anypod/docker-compose.yml +``` + +### Access anypod database +```bash +# Database is typically at /data/db/anypod.db inside the container +ssh truenas docker exec -i anypod sqlite3 /data/db/anypod.db "SELECT * FROM feed;" +``` diff --git a/skills/truenas-docker-ops/scripts/docker_exec_python.sh b/skills/truenas-docker-ops/scripts/docker_exec_python.sh new file mode 100755 index 0000000..1d4cbde --- /dev/null +++ b/skills/truenas-docker-ops/scripts/docker_exec_python.sh @@ -0,0 +1,18 @@ +#!/bin/bash +# Execute Python code inside a Docker container on TrueNAS server +# Usage: docker_exec_python.sh + +set -euo pipefail + +if [ $# -ne 2 ]; then + echo "Usage: $0 " >&2 + echo "Example: $0 anypod 'import sys; print(sys.version)'" >&2 + exit 1 +fi + +CONTAINER_NAME="$1" +PYTHON_CODE="$2" + +# Execute Python code in container via SSH +# Pass Python code through stdin to avoid nested heredoc escaping issues +echo "$PYTHON_CODE" | ssh truenas docker exec -i "${CONTAINER_NAME}" /app/.venv/bin/python - diff --git a/skills/truenas-docker-ops/scripts/docker_exec_sqlite.sh b/skills/truenas-docker-ops/scripts/docker_exec_sqlite.sh new file mode 100755 index 0000000..a6b7aa0 --- /dev/null +++ b/skills/truenas-docker-ops/scripts/docker_exec_sqlite.sh @@ -0,0 +1,35 @@ +#!/bin/bash +# Execute SQLite queries inside a Docker container on TrueNAS server +# Usage: docker_exec_sqlite.sh + +set -euo pipefail + +if [ $# -ne 3 ]; then + echo "Usage: $0 " >&2 + echo "Example: $0 anypod /data/db/anypod.db 'SELECT * FROM feed;'" >&2 + exit 1 +fi + +CONTAINER_NAME="$1" +DB_PATH="$2" +SQL_QUERY="$3" + +# Execute SQLite query in container via Python (since sqlite3 CLI may not be available) +# This approach works universally since Python is always available in the containers +PYTHON_CODE=$(cat <