diff --git a/CHANGELOG.md b/CHANGELOG.md index af09d8d5a..c3bc55bc5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -33,6 +33,7 @@ Docs: https://docs.clawd.bot - Tools: add `process submit` helper to send CR for PTY sessions. - Tools: respond to PTY cursor position queries to unblock interactive TUIs. - Tools: include tool outputs in verbose mode and expand verbose tool feedback. +- Skills: update coding-agent guidance to prefer PTY-enabled exec runs and simplify tmux usage. - TUI: refresh session token counts after runs complete or fail. (#1079) โ€” thanks @d-ploutarchos. - Status: trim `/status` to current-provider usage only and drop the OAuth/token block. - Directory: unify `clawdbot directory` across channels and plugin channels. diff --git a/skills/coding-agent/SKILL.md b/skills/coding-agent/SKILL.md index 2437d5184..06ba47a92 100644 --- a/skills/coding-agent/SKILL.md +++ b/skills/coding-agent/SKILL.md @@ -4,20 +4,73 @@ description: Run Codex CLI, Claude Code, OpenCode, or Pi Coding Agent via backgr metadata: {"clawdbot":{"emoji":"๐Ÿงฉ","requires":{"anyBins":["claude","codex","opencode","pi"]}}} --- -# Coding Agent (background-first) +# Coding Agent (bash-first) -Use **bash background mode** for non-interactive coding work. For interactive coding sessions, use the **tmux** skill (always, except very simple one-shot prompts). +Use **bash** (with optional background mode) for all coding agent work. Simple and effective. -## The Pattern: workdir + background +## โš ๏ธ PTY Mode Required! + +Coding agents (Codex, Claude Code, Pi) are **interactive terminal applications** that need a pseudo-terminal (PTY) to work correctly. Without PTY, you'll get broken output, missing colors, or the agent may hang. + +**Always use `pty:true`** when running coding agents: ```bash -# Create temp space for chats/scratch work -SCRATCH=$(mktemp -d) +# โœ… Correct - with PTY +bash pty:true command:"codex exec 'Your prompt'" -# Start agent in target directory ("little box" - only sees relevant files) -bash workdir:$SCRATCH background:true command:"" -# Or for project work: -bash workdir:~/project/folder background:true command:"" +# โŒ Wrong - no PTY, agent may break +bash command:"codex exec 'Your prompt'" +``` + +### Bash Tool Parameters + +| Parameter | Type | Description | +|-----------|------|-------------| +| `command` | string | The shell command to run | +| `pty` | boolean | **Use for coding agents!** Allocates a pseudo-terminal for interactive CLIs | +| `workdir` | string | Working directory (agent sees only this folder's context) | +| `background` | boolean | Run in background, returns sessionId for monitoring | +| `timeout` | number | Timeout in seconds (kills process on expiry) | +| `elevated` | boolean | Run on host instead of sandbox (if allowed) | + +### Process Tool Actions (for background sessions) + +| Action | Description | +|--------|-------------| +| `list` | List all running/recent sessions | +| `poll` | Check if session is still running | +| `log` | Get session output (with optional offset/limit) | +| `write` | Send raw data to stdin | +| `submit` | Send data + newline (like typing and pressing Enter) | +| `send-keys` | Send key tokens or hex bytes | +| `paste` | Paste text (with optional bracketed mode) | +| `kill` | Terminate the session | + +--- + +## Quick Start: One-Shot Tasks + +For quick prompts/chats, create a temp git repo and run: + +```bash +# Quick chat (Codex needs a git repo!) +SCRATCH=$(mktemp -d) && cd $SCRATCH && git init && codex exec "Your prompt here" + +# Or in a real project - with PTY! +bash pty:true workdir:~/Projects/myproject command:"codex exec 'Add error handling to the API calls'" +``` + +**Why git init?** Codex refuses to run outside a trusted git directory. Creating a temp repo solves this for scratch work. + +--- + +## The Pattern: workdir + background + pty + +For longer tasks, use background mode with PTY: + +```bash +# Start agent in target directory (with PTY!) +bash pty:true workdir:~/project background:true command:"codex exec --full-auto 'Build a snake game'" # Returns sessionId for tracking # Monitor progress @@ -29,6 +82,9 @@ process action:poll sessionId:XXX # Send input (if agent asks a question) process action:write sessionId:XXX data:"y" +# Submit with Enter (like typing "yes" and pressing Enter) +process action:submit sessionId:XXX data:"yes" + # Kill if needed process action:kill sessionId:XXX ``` @@ -41,72 +97,67 @@ process action:kill sessionId:XXX **Model:** `gpt-5.2-codex` is the default (set in ~/.codex/config.toml) -### Building/Creating (use --full-auto or --yolo) +### Flags + +| Flag | Effect | +|------|--------| +| `exec "prompt"` | One-shot execution, exits when done | +| `--full-auto` | Sandboxed but auto-approves in workspace | +| `--yolo` | NO sandbox, NO approvals (fastest, most dangerous) | + +### Building/Creating ```bash -# --full-auto: sandboxed but auto-approves in workspace -bash workdir:~/project background:true command:"codex exec --full-auto \"Build a snake game with dark theme\"" +# Quick one-shot (auto-approves) - remember PTY! +bash pty:true workdir:~/project command:"codex exec --full-auto 'Build a dark mode toggle'" -# --yolo: NO sandbox, NO approvals (fastest, most dangerous) -bash workdir:~/project background:true command:"codex --yolo \"Build a snake game with dark theme\"" - -# Note: --yolo is a shortcut for --dangerously-bypass-approvals-and-sandbox +# Background for longer work +bash pty:true workdir:~/project background:true command:"codex --yolo 'Refactor the auth module'" ``` -### Reviewing PRs (vanilla, no flags) +### Reviewing PRs **โš ๏ธ CRITICAL: Never review PRs in Clawdbot's own project folder!** -- Either use the project where the PR is submitted (if it's NOT ~/Projects/clawdbot) -- Or clone to a temp folder first +Clone to temp folder or use git worktree. ```bash -# Option 1: Review in the actual project (if NOT clawdbot) -bash workdir:~/Projects/some-other-repo background:true command:"codex review --base main" - -# Option 2: Clone to temp folder for safe review (REQUIRED for clawdbot PRs!) +# Clone to temp for safe review REVIEW_DIR=$(mktemp -d) -git clone https://github.com/clawdbot/clawdbot.git $REVIEW_DIR +git clone https://github.com/user/repo.git $REVIEW_DIR cd $REVIEW_DIR && gh pr checkout 130 -bash workdir:$REVIEW_DIR background:true command:"codex review --base origin/main" -# Clean up after: rm -rf $REVIEW_DIR +bash pty:true workdir:$REVIEW_DIR command:"codex review --base origin/main" +# Clean up after: trash $REVIEW_DIR -# Option 3: Use git worktree (keeps main intact) +# Or use git worktree (keeps main intact) git worktree add /tmp/pr-130-review pr-130-branch -bash workdir:/tmp/pr-130-review background:true command:"codex review --base main" +bash pty:true workdir:/tmp/pr-130-review command:"codex review --base main" ``` -**Why?** Checking out branches in the running Clawdbot repo can break the live instance! - ### Batch PR Reviews (parallel army!) ```bash # Fetch all PR refs first git fetch origin '+refs/pull/*/head:refs/remotes/origin/pr/*' -# Deploy the army - one Codex per PR! -bash workdir:~/project background:true command:"codex exec \"Review PR #86. git diff origin/main...origin/pr/86\"" -bash workdir:~/project background:true command:"codex exec \"Review PR #87. git diff origin/main...origin/pr/87\"" -bash workdir:~/project background:true command:"codex exec \"Review PR #95. git diff origin/main...origin/pr/95\"" -# ... repeat for all PRs +# Deploy the army - one Codex per PR (all with PTY!) +bash pty:true workdir:~/project background:true command:"codex exec 'Review PR #86. git diff origin/main...origin/pr/86'" +bash pty:true workdir:~/project background:true command:"codex exec 'Review PR #87. git diff origin/main...origin/pr/87'" # Monitor all process action:list -# Get results and post to GitHub -process action:log sessionId:XXX +# Post results to GitHub gh pr comment --body "" ``` -### Tips for PR Reviews -- **Fetch refs first:** `git fetch origin '+refs/pull/*/head:refs/remotes/origin/pr/*'` -- **Use git diff:** Tell Codex to use `git diff origin/main...origin/pr/XX` -- **Don't checkout:** Multiple parallel reviews = don't let them change branches -- **Post results:** Use `gh pr comment` to post reviews to GitHub - --- ## Claude Code ```bash -bash workdir:~/project background:true command:"claude \"Your task\"" +# With PTY for proper terminal output +bash pty:true workdir:~/project command:"claude 'Your task'" + +# Background +bash pty:true workdir:~/project background:true command:"claude 'Your task'" ``` --- @@ -114,7 +165,7 @@ bash workdir:~/project background:true command:"claude \"Your task\"" ## OpenCode ```bash -bash workdir:~/project background:true command:"opencode run \"Your task\"" +bash pty:true workdir:~/project command:"opencode run 'Your task'" ``` --- @@ -123,152 +174,65 @@ bash workdir:~/project background:true command:"opencode run \"Your task\"" ```bash # Install: npm install -g @mariozechner/pi-coding-agent -bash workdir:~/project background:true command:"pi \"Your task\"" +bash pty:true workdir:~/project command:"pi 'Your task'" + +# Non-interactive mode (PTY still recommended) +bash pty:true command:"pi -p 'Summarize src/'" + +# Different provider/model +bash pty:true command:"pi --provider openai --model gpt-4o-mini -p 'Your task'" ``` +**Note:** Pi now has Anthropic prompt caching enabled (PR #584, merged Jan 2026)! + --- -## Pi flags (common) +## Parallel Issue Fixing with git worktrees -- `--print` / `-p`: non-interactive; runs prompt and exits. -- `--provider `: pick provider (default: google). -- `--model `: pick model (default: gemini-2.5-flash). -- `--api-key `: override API key (defaults to env vars). - -Examples: +For fixing multiple issues in parallel, use git worktrees: ```bash -# Set provider + model, non-interactive -bash workdir:~/project background:true command:"pi --provider openai --model gpt-4o-mini -p \"Summarize src/\"" -``` - ---- - -## tmux (interactive sessions) - -Use the tmux skill for interactive coding sessions (always, except very simple one-shot prompts). Prefer bash background mode for non-interactive runs. - ---- - -## Parallel Issue Fixing with git worktrees + tmux - -For fixing multiple issues in parallel, use git worktrees (isolated branches) + tmux sessions: - -```bash -# 1. Clone repo to temp location -cd /tmp && git clone git@github.com:user/repo.git repo-worktrees -cd repo-worktrees - -# 2. Create worktrees for each issue (isolated branches!) +# 1. Create worktrees for each issue git worktree add -b fix/issue-78 /tmp/issue-78 main git worktree add -b fix/issue-99 /tmp/issue-99 main -# 3. Set up tmux sessions -SOCKET="${TMPDIR:-/tmp}/codex-fixes.sock" -tmux -S "$SOCKET" new-session -d -s fix-78 -tmux -S "$SOCKET" new-session -d -s fix-99 +# 2. Launch Codex in each (background + PTY!) +bash pty:true workdir:/tmp/issue-78 background:true command:"pnpm install && codex --yolo 'Fix issue #78: . Commit and push.'" +bash pty:true workdir:/tmp/issue-99 background:true command:"pnpm install && codex --yolo 'Fix issue #99: . Commit and push.'" -# 4. Launch Codex in each (after pnpm install!) -tmux -S "$SOCKET" send-keys -t fix-78 "cd /tmp/issue-78 && pnpm install && codex --yolo 'Fix issue #78: . Commit and push.'" Enter -tmux -S "$SOCKET" send-keys -t fix-99 "cd /tmp/issue-99 && pnpm install && codex --yolo 'Fix issue #99: . Commit and push.'" Enter +# 3. Monitor progress +process action:list +process action:log sessionId:XXX -# 5. Monitor progress -tmux -S "$SOCKET" capture-pane -p -t fix-78 -S -30 -tmux -S "$SOCKET" capture-pane -p -t fix-99 -S -30 - -# 6. Check if done (prompt returned) -tmux -S "$SOCKET" capture-pane -p -t fix-78 -S -3 | grep -q "โฏ" && echo "Done!" - -# 7. Create PRs after fixes +# 4. Create PRs after fixes cd /tmp/issue-78 && git push -u origin fix/issue-78 gh pr create --repo user/repo --head fix/issue-78 --title "fix: ..." --body "..." -# 8. Cleanup -tmux -S "$SOCKET" kill-server +# 5. Cleanup git worktree remove /tmp/issue-78 git worktree remove /tmp/issue-99 ``` -**Why worktrees?** Each Codex works in isolated branch, no conflicts. Can run 5+ parallel fixes! - -**Why tmux over bash background?** Codex is interactive โ€” needs TTY for proper output. tmux provides persistent sessions with full history capture. - --- ## โš ๏ธ Rules -1. **Respect tool choice** โ€” if user asks for Codex, use Codex. NEVER offer to build it yourself! -2. **Be patient** โ€” don't kill sessions because they're "slow" -3. **Monitor with process:log** โ€” check progress without interfering -4. **--full-auto for building** โ€” auto-approves changes -5. **vanilla for reviewing** โ€” no special flags needed -6. **Parallel is OK** โ€” run many Codex processes at once for batch work -7. **NEVER start Codex in ~/clawd/** โ€” it'll read your soul docs and get weird ideas about the org chart! Use the target project dir or /tmp for blank slate chats -8. **NEVER checkout branches in ~/Projects/clawdbot/** โ€” that's the LIVE Clawdbot instance! Clone to /tmp or use git worktree for PR reviews +1. **Always use pty:true** โ€” coding agents need a terminal! +2. **Respect tool choice** โ€” if user asks for Codex, use Codex. NEVER offer to build it yourself! +3. **Be patient** โ€” don't kill sessions because they're "slow" +4. **Monitor with process:log** โ€” check progress without interfering +5. **--full-auto for building** โ€” auto-approves changes +6. **vanilla for reviewing** โ€” no special flags needed +7. **Parallel is OK** โ€” run many Codex processes at once for batch work +8. **NEVER start Codex in ~/clawd/** โ€” it'll read your soul docs and get weird ideas about the org chart! +9. **NEVER checkout branches in ~/Projects/clawdbot/** โ€” that's the LIVE Clawdbot instance! --- -## PR Template (The Razor Standard) +## Learnings (Jan 2026) -When submitting PRs to external repos, use this format for quality & maintainer-friendliness: - -````markdown -## Original Prompt -[Exact request/problem statement] - -## What this does -[High-level description] - -**Features:** -- [Key feature 1] -- [Key feature 2] - -**Example usage:** -```bash -# Example -command example -``` - -## Feature intent (maintainer-friendly) -[Why useful, how it fits, workflows it enables] - -## Prompt history (timestamped) -- YYYY-MM-DD HH:MM UTC: [Step 1] -- YYYY-MM-DD HH:MM UTC: [Step 2] - -## How I tested -**Manual verification:** -1. [Test step] - Output: `[result]` -2. [Test step] - Result: [result] - -**Files tested:** -- [Detail] -- [Edge cases] - -## Session logs (implementation) -- [What was researched] -- [What was discovered] -- [Time spent] - -## Implementation details -**New files:** -- `path/file.ts` - [description] - -**Modified files:** -- `path/file.ts` - [change] - -**Technical notes:** -- [Detail 1] -- [Detail 2] - ---- -*Submitted by Razor ๐Ÿฅท - Mariano's AI agent* -```` - -**Key principles:** -1. Human-written description (no AI slop) -2. Feature intent for maintainers -3. Timestamped prompt history -4. Session logs if using Codex/agent - -**Example:** https://github.com/steipete/bird/pull/22 +- **PTY is essential:** Coding agents are interactive terminal apps. Without `pty:true`, output breaks or agent hangs. +- **Git repo required:** Codex won't run outside a git directory. Use `mktemp -d && git init` for scratch work. +- **exec is your friend:** `codex exec "prompt"` runs and exits cleanly - perfect for one-shots. +- **submit vs write:** Use `submit` to send input + Enter, `write` for raw data without newline. +- **Sass works:** Codex responds well to playful prompts. Asked it to write a haiku about being second fiddle to a space lobster, got: *"Second chair, I code / Space lobster sets the tempo / Keys glow, I follow"* ๐Ÿฆž diff --git a/skills/tmux/SKILL.md b/skills/tmux/SKILL.md deleted file mode 100644 index 42f5825cb..000000000 --- a/skills/tmux/SKILL.md +++ /dev/null @@ -1,121 +0,0 @@ ---- -name: tmux -description: Remote-control tmux sessions for interactive CLIs by sending keystrokes and scraping pane output. -metadata: {"clawdbot":{"emoji":"๐Ÿงต","os":["darwin","linux"],"requires":{"bins":["tmux"]}}} ---- - -# tmux Skill (Clawdbot) - -Use tmux only when you need an interactive TTY. Prefer exec background mode for long-running, non-interactive tasks. - -## Quickstart (isolated socket, exec tool) - -```bash -SOCKET_DIR="${CLAWDBOT_TMUX_SOCKET_DIR:-${TMPDIR:-/tmp}/clawdbot-tmux-sockets}" -mkdir -p "$SOCKET_DIR" -SOCKET="$SOCKET_DIR/clawdbot.sock" -SESSION=clawdbot-python - -tmux -S "$SOCKET" new -d -s "$SESSION" -n shell -tmux -S "$SOCKET" send-keys -t "$SESSION":0.0 -- 'PYTHON_BASIC_REPL=1 python3 -q' Enter -tmux -S "$SOCKET" capture-pane -p -J -t "$SESSION":0.0 -S -200 -``` - -After starting a session, always print monitor commands: - -``` -To monitor: - tmux -S "$SOCKET" attach -t "$SESSION" - tmux -S "$SOCKET" capture-pane -p -J -t "$SESSION":0.0 -S -200 -``` - -## Socket convention - -- Use `CLAWDBOT_TMUX_SOCKET_DIR` (default `${TMPDIR:-/tmp}/clawdbot-tmux-sockets`). -- Default socket path: `"$CLAWDBOT_TMUX_SOCKET_DIR/clawdbot.sock"`. - -## Targeting panes and naming - -- Target format: `session:window.pane` (defaults to `:0.0`). -- Keep names short; avoid spaces. -- Inspect: `tmux -S "$SOCKET" list-sessions`, `tmux -S "$SOCKET" list-panes -a`. - -## Finding sessions - -- List sessions on your socket: `{baseDir}/scripts/find-sessions.sh -S "$SOCKET"`. -- Scan all sockets: `{baseDir}/scripts/find-sessions.sh --all` (uses `CLAWDBOT_TMUX_SOCKET_DIR`). - -## Sending input safely - -- Prefer literal sends: `tmux -S "$SOCKET" send-keys -t target -l -- "$cmd"`. -- Control keys: `tmux -S "$SOCKET" send-keys -t target C-c`. - -## Watching output - -- Capture recent history: `tmux -S "$SOCKET" capture-pane -p -J -t target -S -200`. -- Wait for prompts: `{baseDir}/scripts/wait-for-text.sh -t session:0.0 -p 'pattern'`. -- Attaching is OK; detach with `Ctrl+b d`. - -## Spawning processes - -- For python REPLs, set `PYTHON_BASIC_REPL=1` (non-basic REPL breaks send-keys flows). - -## Windows / WSL - -- tmux is supported on macOS/Linux. On Windows, use WSL and install tmux inside WSL. -- This skill is gated to `darwin`/`linux` and requires `tmux` on PATH. - -## Orchestrating Coding Agents (Codex, Claude Code) - -tmux excels at running multiple coding agents in parallel: - -```bash -SOCKET="${TMPDIR:-/tmp}/codex-army.sock" - -# Create multiple sessions -for i in 1 2 3 4 5; do - tmux -S "$SOCKET" new-session -d -s "agent-$i" -done - -# Launch agents in different workdirs -tmux -S "$SOCKET" send-keys -t agent-1 "cd /tmp/project1 && codex --yolo 'Fix bug X'" Enter -tmux -S "$SOCKET" send-keys -t agent-2 "cd /tmp/project2 && codex --yolo 'Fix bug Y'" Enter - -# Poll for completion (check if prompt returned) -for sess in agent-1 agent-2; do - if tmux -S "$SOCKET" capture-pane -p -t "$sess" -S -3 | grep -q "โฏ"; then - echo "$sess: DONE" - else - echo "$sess: Running..." - fi -done - -# Get full output from completed session -tmux -S "$SOCKET" capture-pane -p -t agent-1 -S -500 -``` - -**Tips:** -- Use separate git worktrees for parallel fixes (no branch conflicts) -- `pnpm install` first before running codex in fresh clones -- Check for shell prompt (`โฏ` or `$`) to detect completion -- Codex needs `--yolo` or `--full-auto` for non-interactive fixes - -## Cleanup - -- Kill a session: `tmux -S "$SOCKET" kill-session -t "$SESSION"`. -- Kill all sessions on a socket: `tmux -S "$SOCKET" list-sessions -F '#{session_name}' | xargs -r -n1 tmux -S "$SOCKET" kill-session -t`. -- Remove everything on the private socket: `tmux -S "$SOCKET" kill-server`. - -## Helper: wait-for-text.sh - -`{baseDir}/scripts/wait-for-text.sh` polls a pane for a regex (or fixed string) with a timeout. - -```bash -{baseDir}/scripts/wait-for-text.sh -t session:0.0 -p 'pattern' [-F] [-T 20] [-i 0.5] [-l 2000] -``` - -- `-t`/`--target` pane target (required) -- `-p`/`--pattern` regex to match (required); add `-F` for fixed string -- `-T` timeout seconds (integer, default 15) -- `-i` poll interval seconds (default 0.5) -- `-l` history lines to search (integer, default 1000) diff --git a/skills/tmux/scripts/find-sessions.sh b/skills/tmux/scripts/find-sessions.sh deleted file mode 100755 index 7fbba2adb..000000000 --- a/skills/tmux/scripts/find-sessions.sh +++ /dev/null @@ -1,112 +0,0 @@ -#!/usr/bin/env bash -set -euo pipefail - -usage() { - cat <<'USAGE' -Usage: find-sessions.sh [-L socket-name|-S socket-path|-A] [-q pattern] - -List tmux sessions on a socket (default tmux socket if none provided). - -Options: - -L, --socket tmux socket name (passed to tmux -L) - -S, --socket-path tmux socket path (passed to tmux -S) - -A, --all scan all sockets under CLAWDBOT_TMUX_SOCKET_DIR - -q, --query case-insensitive substring to filter session names - -h, --help show this help -USAGE -} - -socket_name="" -socket_path="" -query="" -scan_all=false -socket_dir="${CLAWDBOT_TMUX_SOCKET_DIR:-${TMPDIR:-/tmp}/clawdbot-tmux-sockets}" - -while [[ $# -gt 0 ]]; do - case "$1" in - -L|--socket) socket_name="${2-}"; shift 2 ;; - -S|--socket-path) socket_path="${2-}"; shift 2 ;; - -A|--all) scan_all=true; shift ;; - -q|--query) query="${2-}"; shift 2 ;; - -h|--help) usage; exit 0 ;; - *) echo "Unknown option: $1" >&2; usage; exit 1 ;; - esac -done - -if [[ "$scan_all" == true && ( -n "$socket_name" || -n "$socket_path" ) ]]; then - echo "Cannot combine --all with -L or -S" >&2 - exit 1 -fi - -if [[ -n "$socket_name" && -n "$socket_path" ]]; then - echo "Use either -L or -S, not both" >&2 - exit 1 -fi - -if ! command -v tmux >/dev/null 2>&1; then - echo "tmux not found in PATH" >&2 - exit 1 -fi - -list_sessions() { - local label="$1"; shift - local tmux_cmd=(tmux "$@") - - if ! sessions="$("${tmux_cmd[@]}" list-sessions -F '#{session_name}\t#{session_attached}\t#{session_created_string}' 2>/dev/null)"; then - echo "No tmux server found on $label" >&2 - return 1 - fi - - if [[ -n "$query" ]]; then - sessions="$(printf '%s\n' "$sessions" | grep -i -- "$query" || true)" - fi - - if [[ -z "$sessions" ]]; then - echo "No sessions found on $label" - return 0 - fi - - echo "Sessions on $label:" - printf '%s\n' "$sessions" | while IFS=$'\t' read -r name attached created; do - attached_label=$([[ "$attached" == "1" ]] && echo "attached" || echo "detached") - printf ' - %s (%s, started %s)\n' "$name" "$attached_label" "$created" - done -} - -if [[ "$scan_all" == true ]]; then - if [[ ! -d "$socket_dir" ]]; then - echo "Socket directory not found: $socket_dir" >&2 - exit 1 - fi - - shopt -s nullglob - sockets=("$socket_dir"/*) - shopt -u nullglob - - if [[ "${#sockets[@]}" -eq 0 ]]; then - echo "No sockets found under $socket_dir" >&2 - exit 1 - fi - - exit_code=0 - for sock in "${sockets[@]}"; do - if [[ ! -S "$sock" ]]; then - continue - fi - list_sessions "socket path '$sock'" -S "$sock" || exit_code=$? - done - exit "$exit_code" -fi - -tmux_cmd=(tmux) -socket_label="default socket" - -if [[ -n "$socket_name" ]]; then - tmux_cmd+=(-L "$socket_name") - socket_label="socket name '$socket_name'" -elif [[ -n "$socket_path" ]]; then - tmux_cmd+=(-S "$socket_path") - socket_label="socket path '$socket_path'" -fi - -list_sessions "$socket_label" "${tmux_cmd[@]:1}" diff --git a/skills/tmux/scripts/wait-for-text.sh b/skills/tmux/scripts/wait-for-text.sh deleted file mode 100755 index 56354be83..000000000 --- a/skills/tmux/scripts/wait-for-text.sh +++ /dev/null @@ -1,83 +0,0 @@ -#!/usr/bin/env bash -set -euo pipefail - -usage() { - cat <<'USAGE' -Usage: wait-for-text.sh -t target -p pattern [options] - -Poll a tmux pane for text and exit when found. - -Options: - -t, --target tmux target (session:window.pane), required - -p, --pattern regex pattern to look for, required - -F, --fixed treat pattern as a fixed string (grep -F) - -T, --timeout seconds to wait (integer, default: 15) - -i, --interval poll interval in seconds (default: 0.5) - -l, --lines number of history lines to inspect (integer, default: 1000) - -h, --help show this help -USAGE -} - -target="" -pattern="" -grep_flag="-E" -timeout=15 -interval=0.5 -lines=1000 - -while [[ $# -gt 0 ]]; do - case "$1" in - -t|--target) target="${2-}"; shift 2 ;; - -p|--pattern) pattern="${2-}"; shift 2 ;; - -F|--fixed) grep_flag="-F"; shift ;; - -T|--timeout) timeout="${2-}"; shift 2 ;; - -i|--interval) interval="${2-}"; shift 2 ;; - -l|--lines) lines="${2-}"; shift 2 ;; - -h|--help) usage; exit 0 ;; - *) echo "Unknown option: $1" >&2; usage; exit 1 ;; - esac -done - -if [[ -z "$target" || -z "$pattern" ]]; then - echo "target and pattern are required" >&2 - usage - exit 1 -fi - -if ! [[ "$timeout" =~ ^[0-9]+$ ]]; then - echo "timeout must be an integer number of seconds" >&2 - exit 1 -fi - -if ! [[ "$lines" =~ ^[0-9]+$ ]]; then - echo "lines must be an integer" >&2 - exit 1 -fi - -if ! command -v tmux >/dev/null 2>&1; then - echo "tmux not found in PATH" >&2 - exit 1 -fi - -# End time in epoch seconds (integer, good enough for polling) -start_epoch=$(date +%s) -deadline=$((start_epoch + timeout)) - -while true; do - # -J joins wrapped lines, -S uses negative index to read last N lines - pane_text="$(tmux capture-pane -p -J -t "$target" -S "-${lines}" 2>/dev/null || true)" - - if printf '%s\n' "$pane_text" | grep $grep_flag -- "$pattern" >/dev/null 2>&1; then - exit 0 - fi - - now=$(date +%s) - if (( now >= deadline )); then - echo "Timed out after ${timeout}s waiting for pattern: $pattern" >&2 - echo "Last ${lines} lines from $target:" >&2 - printf '%s\n' "$pane_text" >&2 - exit 1 - fi - - sleep "$interval" -done