docs: update coding-agent skill guidance
This commit is contained in:
@@ -33,6 +33,7 @@ Docs: https://docs.clawd.bot
|
|||||||
- Tools: add `process submit` helper to send CR for PTY sessions.
|
- Tools: add `process submit` helper to send CR for PTY sessions.
|
||||||
- Tools: respond to PTY cursor position queries to unblock interactive TUIs.
|
- Tools: respond to PTY cursor position queries to unblock interactive TUIs.
|
||||||
- Tools: include tool outputs in verbose mode and expand verbose tool feedback.
|
- 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.
|
- 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.
|
- Status: trim `/status` to current-provider usage only and drop the OAuth/token block.
|
||||||
- Directory: unify `clawdbot directory` across channels and plugin channels.
|
- Directory: unify `clawdbot directory` across channels and plugin channels.
|
||||||
|
|||||||
@@ -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"]}}}
|
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
|
```bash
|
||||||
# Create temp space for chats/scratch work
|
# ✅ Correct - with PTY
|
||||||
SCRATCH=$(mktemp -d)
|
bash pty:true command:"codex exec 'Your prompt'"
|
||||||
|
|
||||||
# Start agent in target directory ("little box" - only sees relevant files)
|
# ❌ Wrong - no PTY, agent may break
|
||||||
bash workdir:$SCRATCH background:true command:"<agent command>"
|
bash command:"codex exec 'Your prompt'"
|
||||||
# Or for project work:
|
```
|
||||||
bash workdir:~/project/folder background:true command:"<agent command>"
|
|
||||||
|
### 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
|
# Returns sessionId for tracking
|
||||||
|
|
||||||
# Monitor progress
|
# Monitor progress
|
||||||
@@ -29,6 +82,9 @@ process action:poll sessionId:XXX
|
|||||||
# Send input (if agent asks a question)
|
# Send input (if agent asks a question)
|
||||||
process action:write sessionId:XXX data:"y"
|
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
|
# Kill if needed
|
||||||
process action:kill sessionId:XXX
|
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)
|
**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
|
```bash
|
||||||
# --full-auto: sandboxed but auto-approves in workspace
|
# Quick one-shot (auto-approves) - remember PTY!
|
||||||
bash workdir:~/project background:true command:"codex exec --full-auto \"Build a snake game with dark theme\""
|
bash pty:true workdir:~/project command:"codex exec --full-auto 'Build a dark mode toggle'"
|
||||||
|
|
||||||
# --yolo: NO sandbox, NO approvals (fastest, most dangerous)
|
# Background for longer work
|
||||||
bash workdir:~/project background:true command:"codex --yolo \"Build a snake game with dark theme\""
|
bash pty:true workdir:~/project background:true command:"codex --yolo 'Refactor the auth module'"
|
||||||
|
|
||||||
# Note: --yolo is a shortcut for --dangerously-bypass-approvals-and-sandbox
|
|
||||||
```
|
```
|
||||||
|
|
||||||
### Reviewing PRs (vanilla, no flags)
|
### Reviewing PRs
|
||||||
|
|
||||||
**⚠️ CRITICAL: Never review PRs in Clawdbot's own project folder!**
|
**⚠️ 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)
|
Clone to temp folder or use git worktree.
|
||||||
- Or clone to a temp folder first
|
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Option 1: Review in the actual project (if NOT clawdbot)
|
# Clone to temp for safe review
|
||||||
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!)
|
|
||||||
REVIEW_DIR=$(mktemp -d)
|
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
|
cd $REVIEW_DIR && gh pr checkout 130
|
||||||
bash workdir:$REVIEW_DIR background:true command:"codex review --base origin/main"
|
bash pty:true workdir:$REVIEW_DIR command:"codex review --base origin/main"
|
||||||
# Clean up after: rm -rf $REVIEW_DIR
|
# 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
|
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!)
|
### Batch PR Reviews (parallel army!)
|
||||||
```bash
|
```bash
|
||||||
# Fetch all PR refs first
|
# Fetch all PR refs first
|
||||||
git fetch origin '+refs/pull/*/head:refs/remotes/origin/pr/*'
|
git fetch origin '+refs/pull/*/head:refs/remotes/origin/pr/*'
|
||||||
|
|
||||||
# Deploy the army - one Codex per PR!
|
# Deploy the army - one Codex per PR (all with PTY!)
|
||||||
bash 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 #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 pty:true 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
|
|
||||||
|
|
||||||
# Monitor all
|
# Monitor all
|
||||||
process action:list
|
process action:list
|
||||||
|
|
||||||
# Get results and post to GitHub
|
# Post results to GitHub
|
||||||
process action:log sessionId:XXX
|
|
||||||
gh pr comment <PR#> --body "<review content>"
|
gh pr comment <PR#> --body "<review content>"
|
||||||
```
|
```
|
||||||
|
|
||||||
### 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
|
## Claude Code
|
||||||
|
|
||||||
```bash
|
```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
|
## OpenCode
|
||||||
|
|
||||||
```bash
|
```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
|
```bash
|
||||||
# Install: npm install -g @mariozechner/pi-coding-agent
|
# 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.
|
For fixing multiple issues in parallel, use git worktrees:
|
||||||
- `--provider <name>`: pick provider (default: google).
|
|
||||||
- `--model <id>`: pick model (default: gemini-2.5-flash).
|
|
||||||
- `--api-key <key>`: override API key (defaults to env vars).
|
|
||||||
|
|
||||||
Examples:
|
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Set provider + model, non-interactive
|
# 1. Create worktrees for each issue
|
||||||
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!)
|
|
||||||
git worktree add -b fix/issue-78 /tmp/issue-78 main
|
git worktree add -b fix/issue-78 /tmp/issue-78 main
|
||||||
git worktree add -b fix/issue-99 /tmp/issue-99 main
|
git worktree add -b fix/issue-99 /tmp/issue-99 main
|
||||||
|
|
||||||
# 3. Set up tmux sessions
|
# 2. Launch Codex in each (background + PTY!)
|
||||||
SOCKET="${TMPDIR:-/tmp}/codex-fixes.sock"
|
bash pty:true workdir:/tmp/issue-78 background:true command:"pnpm install && codex --yolo 'Fix issue #78: <description>. Commit and push.'"
|
||||||
tmux -S "$SOCKET" new-session -d -s fix-78
|
bash pty:true workdir:/tmp/issue-99 background:true command:"pnpm install && codex --yolo 'Fix issue #99: <description>. Commit and push.'"
|
||||||
tmux -S "$SOCKET" new-session -d -s fix-99
|
|
||||||
|
|
||||||
# 4. Launch Codex in each (after pnpm install!)
|
# 3. Monitor progress
|
||||||
tmux -S "$SOCKET" send-keys -t fix-78 "cd /tmp/issue-78 && pnpm install && codex --yolo 'Fix issue #78: <description>. Commit and push.'" Enter
|
process action:list
|
||||||
tmux -S "$SOCKET" send-keys -t fix-99 "cd /tmp/issue-99 && pnpm install && codex --yolo 'Fix issue #99: <description>. Commit and push.'" Enter
|
process action:log sessionId:XXX
|
||||||
|
|
||||||
# 5. Monitor progress
|
# 4. Create PRs after fixes
|
||||||
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
|
|
||||||
cd /tmp/issue-78 && git push -u origin fix/issue-78
|
cd /tmp/issue-78 && git push -u origin fix/issue-78
|
||||||
gh pr create --repo user/repo --head fix/issue-78 --title "fix: ..." --body "..."
|
gh pr create --repo user/repo --head fix/issue-78 --title "fix: ..." --body "..."
|
||||||
|
|
||||||
# 8. Cleanup
|
# 5. Cleanup
|
||||||
tmux -S "$SOCKET" kill-server
|
|
||||||
git worktree remove /tmp/issue-78
|
git worktree remove /tmp/issue-78
|
||||||
git worktree remove /tmp/issue-99
|
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
|
## ⚠️ Rules
|
||||||
|
|
||||||
1. **Respect tool choice** — if user asks for Codex, use Codex. NEVER offer to build it yourself!
|
1. **Always use pty:true** — coding agents need a terminal!
|
||||||
2. **Be patient** — don't kill sessions because they're "slow"
|
2. **Respect tool choice** — if user asks for Codex, use Codex. NEVER offer to build it yourself!
|
||||||
3. **Monitor with process:log** — check progress without interfering
|
3. **Be patient** — don't kill sessions because they're "slow"
|
||||||
4. **--full-auto for building** — auto-approves changes
|
4. **Monitor with process:log** — check progress without interfering
|
||||||
5. **vanilla for reviewing** — no special flags needed
|
5. **--full-auto for building** — auto-approves changes
|
||||||
6. **Parallel is OK** — run many Codex processes at once for batch work
|
6. **vanilla for reviewing** — no special flags needed
|
||||||
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
|
7. **Parallel is OK** — run many Codex processes at once for batch work
|
||||||
8. **NEVER checkout branches in ~/Projects/clawdbot/** — that's the LIVE Clawdbot instance! Clone to /tmp or use git worktree for PR reviews
|
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:
|
- **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.
|
||||||
````markdown
|
- **exec is your friend:** `codex exec "prompt"` runs and exits cleanly - perfect for one-shots.
|
||||||
## Original Prompt
|
- **submit vs write:** Use `submit` to send input + Enter, `write` for raw data without newline.
|
||||||
[Exact request/problem statement]
|
- **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"* 🦞
|
||||||
|
|
||||||
## 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
|
|
||||||
|
|||||||
@@ -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)
|
|
||||||
@@ -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}"
|
|
||||||
@@ -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
|
|
||||||
Reference in New Issue
Block a user