431 lines
13 KiB
Markdown
431 lines
13 KiB
Markdown
---
|
|
name: tmux-task-runner
|
|
description: Run build processes, test suites, deployments, and development servers in monitored tmux sessions with persistent logging. Use when executing long-running tasks that need background execution with real-time monitoring, or when running commands like npm build, pytest, deployment scripts, or dev servers that should continue running while you work on other tasks.
|
|
---
|
|
|
|
# Tmux Long-Running Task Skill
|
|
|
|
## Overview
|
|
|
|
This skill provides a robust solution for running long-running tasks in tmux sessions, offering superior flexibility compared to standard background process execution. It enables:
|
|
|
|
- **Detached execution**: Tasks run in isolated tmux sessions
|
|
- **Real-time monitoring**: Capture and analyze logs via `tmux capture-pane`
|
|
- **Developer control**: Attach to sessions for interactive debugging
|
|
- **Persistent logging**: All output saved to timestamped log files (configurable via `LOG_DIR`)
|
|
- **Status metadata**: Exit codes, timings, and environment details saved for later review
|
|
- **Session management**: List, monitor, summarize, and clean up active sessions
|
|
|
|
## Critical Workflow
|
|
|
|
When a user requests execution of a long-running task (builds, tests, deployments, servers, etc.):
|
|
|
|
1. **Detect task type**: Identify if the task is long-running (>30s expected duration)
|
|
2. **Create session**: Generate unique session name (e.g., `task-build-1729519263`)
|
|
3. **Execute in tmux**: Run command in detached tmux session with logging
|
|
4. **Monitor output**: Use `tmux capture-pane` to read session output
|
|
5. **Report status**: Inform user of session name, log/status file locations, and monitoring options
|
|
6. **Provide access**: Give user commands to tail logs, use the `tail` helper, or attach to the session
|
|
|
|
**CRITICAL:** Always check if tmux is installed before proceeding. If not found, inform the user to install it first.
|
|
|
|
## How It Works
|
|
|
|
1. User requests a long-running task execution
|
|
2. Skill creates a tmux session with descriptive name
|
|
3. Task runs in a detached session with output captured to a timestamped log in `${LOG_DIR:-/tmp}`
|
|
4. Skill records status metadata (exit code, timings, command, overrides) in `${STATUS_DIR:-/tmp}`
|
|
5. User can monitor via log tailing, the built-in `tail` helper, the `status` summary, or by attaching
|
|
6. Session persists until task completes or user kills it
|
|
|
|
## Setup Instructions
|
|
|
|
Ensure tmux is installed on your system:
|
|
|
|
```bash
|
|
# macOS
|
|
brew install tmux
|
|
|
|
# Ubuntu/Debian
|
|
sudo apt-get install tmux
|
|
|
|
# Fedora/RHEL
|
|
sudo dnf install tmux
|
|
```
|
|
|
|
Verify installation:
|
|
```bash
|
|
tmux -V
|
|
```
|
|
|
|
## Execution Pattern
|
|
|
|
### Step 1: Validate tmux availability
|
|
|
|
```javascript
|
|
// Check if tmux is installed
|
|
const { execSync } = require('child_process');
|
|
|
|
try {
|
|
execSync('which tmux', { stdio: 'pipe' });
|
|
} catch (error) {
|
|
throw new Error('tmux is not installed. Please install it first.');
|
|
}
|
|
```
|
|
|
|
### Step 2: Prepare session metadata
|
|
|
|
```bash
|
|
# Generate unique session name and resolve directories
|
|
SESSION_NAME="task-${TASK_TYPE}-$(date +%s)"
|
|
LOG_DIR="${LOG_DIR:-/tmp}"
|
|
STATUS_DIR="${STATUS_DIR:-/tmp}"
|
|
LOG_FILE="$LOG_DIR/${SESSION_NAME}.log"
|
|
STATUS_FILE="$STATUS_DIR/${SESSION_NAME}.status"
|
|
|
|
# Optional overrides
|
|
WORKDIR="${WORKDIR:-$PWD}" # run command from a different folder
|
|
ENV_OVERRIDES=("NODE_ENV=ci" "DEBUG=1") # repeatable KEY=VALUE pairs
|
|
NOTIFY=1 # enable desktop notifications if available
|
|
```
|
|
|
|
### Step 3: Launch tmux session with logging and status tracking
|
|
|
|
```bash
|
|
tmux new-session -d -s "$SESSION_NAME" -c "$WORKDIR" bash -lc '
|
|
set -o pipefail
|
|
|
|
# Export requested environment overrides
|
|
export NODE_ENV=ci DEBUG=1
|
|
|
|
START_EPOCH=$(date +%s)
|
|
START_ISO=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
|
|
|
|
your-command-here 2>&1 | tee "'"$LOG_FILE"'"
|
|
exit_code=${PIPESTATUS[0]}
|
|
|
|
END_EPOCH=$(date +%s)
|
|
END_ISO=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
|
|
DURATION=$((END_EPOCH - START_EPOCH))
|
|
|
|
{
|
|
printf "exit_code=%q\n" "$exit_code"
|
|
printf "command=%q\n" "your-command-here"
|
|
printf "started_at_iso=%q\n" "$START_ISO"
|
|
printf "finished_at_iso=%q\n" "$END_ISO"
|
|
printf "duration_seconds=%q\n" "$DURATION"
|
|
printf "log_file=%q\n" "'"$LOG_FILE"'"
|
|
printf "workdir=%q\n" "'"$WORKDIR"'"
|
|
printf "env_vars=%q\n" "NODE_ENV=ci; DEBUG=1"
|
|
} > "'"$STATUS_FILE"'"
|
|
|
|
if [ '"$NOTIFY"' -eq 1 ]; then
|
|
message="[$SESSION_NAME] "
|
|
if [ "$exit_code" -eq 0 ]; then
|
|
message+="completed successfully"
|
|
else
|
|
message+="failed (exit $exit_code)"
|
|
fi
|
|
command -v notify-send >/dev/null 2>&1 && notify-send "tmux-task-runner" "$message"
|
|
fi
|
|
|
|
exit "$exit_code"
|
|
'
|
|
```
|
|
|
|
### Step 4: Provide monitoring and summary commands
|
|
|
|
```bash
|
|
# Tail the log file directly
|
|
tail -f "$LOG_FILE"
|
|
|
|
# Poll output without attaching
|
|
./run.sh tail "$SESSION_NAME" --interval 5 --lines 80
|
|
|
|
# Summarize session metadata (exit status, duration, timestamps)
|
|
./run.sh status "$SESSION_NAME"
|
|
|
|
# Attach to the session (interactive)
|
|
tmux attach-session -t "$SESSION_NAME"
|
|
```
|
|
|
|
## Common Patterns
|
|
|
|
### Build Tasks
|
|
|
|
```bash
|
|
./run.sh run build "npm run build"
|
|
# Monitor tail: ./run.sh tail task-build-... --interval 5
|
|
# Summarize: ./run.sh status task-build-...
|
|
```
|
|
|
|
### Test Suites
|
|
|
|
```bash
|
|
./run.sh run test --env NODE_ENV=ci "npm test -- --coverage"
|
|
```
|
|
|
|
### Development Server
|
|
|
|
```bash
|
|
./run.sh run server --workdir ./services/web "npm run dev"
|
|
```
|
|
|
|
### Deployment Scripts
|
|
|
|
```bash
|
|
./run.sh run deploy --notify "./deploy.sh production"
|
|
```
|
|
|
|
## Run Script Enhancements
|
|
|
|
### Working Directory Overrides
|
|
- Use `--workdir <path>` to run the command from a different directory without wrapping the command in `cd ... &&`.
|
|
- The script attempts to use `tmux new-session -c` when available and falls back to an inline `cd` if necessary.
|
|
|
|
### Environment Variable Injection
|
|
- Repeatable `--env KEY=VALUE` flags export additional environment variables for the task.
|
|
- Variable names are validated to prevent invalid shell identifiers.
|
|
|
|
### Completion Notifications
|
|
- `--notify` triggers a best-effort desktop notification (`notify-send`, `osascript`, or `terminal-notifier`) when the job ends.
|
|
- Notifications include the session name and whether the command succeeded.
|
|
|
|
### Status Metadata and Summaries
|
|
- Each run writes a status file to `${STATUS_DIR:-/tmp}` containing exit code, timestamps, duration, command, workdir, and env overrides.
|
|
- `./run.sh status` lists recent runs; `./run.sh status <session>` prints detailed metadata and the latest log lines.
|
|
- Status files enable tooling to expose job history without attaching to tmux directly.
|
|
|
|
### Tail Helper
|
|
- `./run.sh tail <session> [--interval N] [--lines M]` polls `tmux capture-pane` on a timer for lightweight monitoring.
|
|
- Falls back to the log file if the session has already exited.
|
|
|
|
### Configurable Directories and Pruning
|
|
- Override `LOG_DIR` and `STATUS_DIR` to store artifacts outside `/tmp`.
|
|
- `PRUNE_RETENTION_DAYS` (default 7) automatically removes stale log/status files.
|
|
- `STATUS_SUMMARY_LIMIT`, `TAIL_DEFAULT_LINES`, and `TAIL_DEFAULT_INTERVAL` tune command defaults without editing the script.
|
|
|
|
## Session Management
|
|
|
|
### List Active Sessions
|
|
|
|
```bash
|
|
# List all tmux sessions
|
|
tmux list-sessions
|
|
|
|
# List only task sessions
|
|
tmux list-sessions 2>/dev/null | grep "^task-" || echo "No active task sessions"
|
|
```
|
|
|
|
### Monitor Session Output
|
|
|
|
```bash
|
|
# Capture last 100 lines
|
|
tmux capture-pane -t SESSION_NAME -p -S -100
|
|
|
|
# Capture entire scrollback buffer
|
|
tmux capture-pane -t SESSION_NAME -p -S -
|
|
|
|
# Save to file
|
|
tmux capture-pane -t SESSION_NAME -p -S - > session-capture.txt
|
|
```
|
|
|
|
### Kill Sessions
|
|
|
|
```bash
|
|
# Kill specific session
|
|
tmux kill-session -t SESSION_NAME
|
|
|
|
# Kill all task sessions
|
|
tmux list-sessions -F "#{session_name}" | grep "^task-" | xargs -I {} tmux kill-session -t {}
|
|
```
|
|
|
|
## Helper Script Example
|
|
|
|
Create a reusable helper for common operations:
|
|
|
|
```bash
|
|
#!/bin/bash
|
|
# tmux-task.sh - Tmux task runner helper
|
|
|
|
run_task() {
|
|
local task_type=$1
|
|
shift
|
|
local command="$@"
|
|
|
|
local session="task-${task_type}-$(date +%s)"
|
|
local logfile="/tmp/${session}.log"
|
|
|
|
# Create session
|
|
tmux new-session -d -s "$session" "$command 2>&1 | tee $logfile"
|
|
|
|
# Output info
|
|
echo "✓ Task started in session: $session"
|
|
echo " Log file: $logfile"
|
|
echo ""
|
|
echo "Monitoring commands:"
|
|
echo " tail -f $logfile # Follow log output"
|
|
echo " tmux attach-session -t $session # Attach to session"
|
|
echo " tmux kill-session -t $session # Stop task"
|
|
}
|
|
|
|
check_session() {
|
|
local session=$1
|
|
|
|
if tmux has-session -t "$session" 2>/dev/null; then
|
|
echo "Session '$session' is running"
|
|
echo ""
|
|
echo "Recent output:"
|
|
tmux capture-pane -t "$session" -p -S -20
|
|
else
|
|
echo "Session '$session' has completed or doesn't exist"
|
|
fi
|
|
}
|
|
|
|
list_tasks() {
|
|
echo "Active task sessions:"
|
|
tmux list-sessions 2>/dev/null | grep "^task-" || echo " No active tasks"
|
|
}
|
|
|
|
# Usage example
|
|
case "$1" in
|
|
run)
|
|
run_task "$2" "${@:3}"
|
|
;;
|
|
check)
|
|
check_session "$2"
|
|
;;
|
|
list)
|
|
list_tasks
|
|
;;
|
|
*)
|
|
echo "Usage: $0 {run|check|list} [args...]"
|
|
exit 1
|
|
esac
|
|
```
|
|
|
|
## Tips
|
|
|
|
- **CRITICAL:** Always use unique session names to avoid conflicts
|
|
- **CRITICAL:** Include timestamps in session names for easy identification
|
|
- **CRITICAL:** Always provide log file paths to users for monitoring
|
|
- Use descriptive task type prefixes (build, test, deploy, server, etc.)
|
|
- Use `LOG_DIR` (defaults to `/tmp`) to avoid cluttering project directories
|
|
- Use `tmux capture-pane -p -S -100` to get reasonable output chunks
|
|
- For very long output, save to file instead of displaying inline
|
|
- Remind users they can attach to sessions for interactive debugging
|
|
- Clean up completed sessions periodically to avoid clutter
|
|
|
|
## Troubleshooting
|
|
|
|
**Problem:** "session not found" error
|
|
- **Solution:** Session may have completed. Check if process finished in log file.
|
|
|
|
**Problem:** Unable to capture pane output
|
|
- **Solution:** Ensure session name is correct: `tmux list-sessions`
|
|
|
|
**Problem:** Log file not updating
|
|
- **Solution:** Verify command includes `2>&1 | tee $LOG_FILE` for proper output capture
|
|
|
|
**Problem:** tmux not found
|
|
- **Solution:** Install tmux using system package manager (brew, apt, dnf, etc.)
|
|
|
|
**Problem:** Session already exists
|
|
- **Solution:** Use unique names with timestamps or kill existing session first
|
|
|
|
## Example Usage
|
|
|
|
**User:** "Run the full test suite in the background and let me monitor it"
|
|
|
|
**Claude:** I'll run your test suite in a tmux session so you can monitor it easily.
|
|
|
|
```bash
|
|
./run.sh run test --env NODE_ENV=ci "npm test -- --runInBand"
|
|
```
|
|
|
|
Your tests are now running in the background. Here's how to monitor:
|
|
|
|
**Monitor the logs:**
|
|
```bash
|
|
./run.sh tail task-test-1729519263 --interval 5 --lines 80
|
|
# or tail -f "$LOG_DIR"/task-test-1729519263.log
|
|
```
|
|
|
|
**Attach to the session:**
|
|
```bash
|
|
tmux attach-session -t task-test-1729519263
|
|
```
|
|
(Press `Ctrl+b` then `d` to detach without stopping the tests)
|
|
|
|
**Check current status summary:**
|
|
```bash
|
|
./run.sh status task-test-1729519263
|
|
```
|
|
|
|
The summary includes exit code, duration, timestamps, workdir, and environment overrides once the command finishes.
|
|
|
|
---
|
|
|
|
## Advanced Features
|
|
|
|
### Multi-Pane Monitoring
|
|
|
|
Run multiple related tasks in split panes:
|
|
|
|
```bash
|
|
SESSION="task-multiview-$(date +%s)"
|
|
|
|
# Create session with first task
|
|
tmux new-session -d -s "$SESSION" "npm run build"
|
|
|
|
# Split horizontally and run tests
|
|
tmux split-window -h -t "$SESSION" "npm test"
|
|
|
|
# Split first pane vertically for logs
|
|
tmux select-pane -t 0
|
|
tmux split-window -v -t "$SESSION" "tail -f /tmp/app.log"
|
|
|
|
echo "Multi-pane session created: $SESSION"
|
|
echo "Attach with: tmux attach-session -t $SESSION"
|
|
```
|
|
|
|
### Persistent Sessions
|
|
|
|
Configure tmux to save sessions across reboots (requires tmux-resurrect plugin):
|
|
|
|
```bash
|
|
# Install tmux plugin manager (TPM)
|
|
git clone https://github.com/tmux-plugins/tpm ~/.tmux/plugins/tpm
|
|
|
|
# Add to ~/.tmux.conf
|
|
echo "set -g @plugin 'tmux-plugins/tmux-resurrect'" >> ~/.tmux.conf
|
|
echo "run '~/.tmux/plugins/tpm/tpm'" >> ~/.tmux.conf
|
|
```
|
|
|
|
### Integration with CI/CD
|
|
|
|
Use tmux sessions for local CI/CD simulation:
|
|
|
|
```bash
|
|
SESSION="task-ci-$(date +%s)"
|
|
LOG="/tmp/${SESSION}.log"
|
|
|
|
tmux new-session -d -s "$SESSION" bash -c "
|
|
echo '=== Linting ===' && npm run lint &&
|
|
echo '=== Testing ===' && npm test &&
|
|
echo '=== Building ===' && npm run build &&
|
|
echo '=== CI Complete ===' || echo '=== CI Failed ==='
|
|
" 2>&1 | tee $LOG
|
|
```
|
|
|
|
## Best Practices
|
|
|
|
1. **Always provide monitoring commands** to users after starting a session
|
|
2. **Use descriptive task types** in session names (build, test, deploy, etc.)
|
|
3. **Capture initial output** after starting to confirm task began successfully
|
|
4. **Use LOG_DIR (defaults to /tmp)** to keep project directories clean
|
|
5. **Include cleanup instructions** for when tasks complete
|
|
6. **Check session status** before attempting operations
|
|
7. **Provide both tail and attach options** for different monitoring preferences
|
|
8. **Use tee for logging** to enable both file and real-time capture
|