Merge 5b8332e88d6199eda9aa88e5ff09e656aa72f943 into 598f1826d8b2bc969aace2c6459824737667218c
This commit is contained in:
commit
c5051b8bd7
181
skills/claude-codex-delegation/SKILL.md
Normal file
181
skills/claude-codex-delegation/SKILL.md
Normal file
@ -0,0 +1,181 @@
|
||||
---
|
||||
name: claude-codex-delegation
|
||||
description: Delegate code-writing, document generation, and analysis tasks to Claude Code or OpenAI Codex sub-processes. Use when you need to spawn a coding agent for file generation, code modification, script writing, or any multi-step coding task. Triggers on "delegate to claude", "delegate to codex", "use claude code", "use codex", "spawn coding agent", "run delegation", or when a task requires writing/modifying code that should be handed off to a sub-process.
|
||||
metadata:
|
||||
{ "openclaw": { "os": ["linux"], "requires": { "anyBins": ["claude", "codex"], "bins": ["bash", "timeout"] } } }
|
||||
---
|
||||
|
||||
# Claude Code + Codex Delegation
|
||||
|
||||
Delegate coding tasks to Claude Code or OpenAI Codex as sub-processes. Each delegation is self-contained — the sub-process has no access to the parent agent's conversation, tools, or MCP servers. It does have full filesystem access within the working directory you specify.
|
||||
|
||||
This skill extends the built-in `coding-agent` skill with concrete delegation scripts, tmux session management, and an example delegation policy. Use `coding-agent` for prompt composition guidance; use this skill when you need the actual execution infrastructure.
|
||||
|
||||
## Installation
|
||||
|
||||
```bash
|
||||
# Install everything (Claude Code, Codex, scripts, skill file)
|
||||
./install.sh
|
||||
|
||||
# Already have Claude Code and Codex? Just install scripts
|
||||
./install.sh --skip-npm
|
||||
|
||||
# Preview what will happen
|
||||
./install.sh --dry-run
|
||||
```
|
||||
|
||||
Requires Node.js 22+, an existing OpenClaw installation, and subscription/OAuth auth for both Claude Code and Codex (API key auth will not work — the scripts strip API keys by design).
|
||||
|
||||
## Agent Selection
|
||||
|
||||
| Request | Agent | CLI |
|
||||
|---------|-------|-----|
|
||||
| Default / unspecified | Claude Code | `claude` |
|
||||
| User says "use codex" | Codex | `codex` |
|
||||
| User says "use claude code" | Claude Code | `claude` |
|
||||
|
||||
Never auto-select Codex. Only use it when the user explicitly requests it.
|
||||
|
||||
## Quick Delegation (One-Shot)
|
||||
|
||||
```bash
|
||||
# Claude Code (no PTY needed)
|
||||
cd /path/to/project && claude --permission-mode bypassPermissions --print 'Your task prompt'
|
||||
|
||||
# Codex (PTY required)
|
||||
bash pty:true workdir:/path/to/project command:"codex exec --full-auto 'Your task prompt'"
|
||||
```
|
||||
|
||||
## Delegation via Script
|
||||
|
||||
Use `scripts/delegate.sh` for structured delegation with logging:
|
||||
|
||||
```bash
|
||||
# Claude Code (default)
|
||||
scripts/delegate.sh --prompt "Build a REST API for todos" --workdir ~/project
|
||||
|
||||
# Codex
|
||||
scripts/delegate.sh --agent codex --prompt "Refactor the auth module" --workdir ~/project
|
||||
|
||||
# From a prompt file
|
||||
scripts/delegate.sh --file /tmp/task-prompt.md --workdir ~/project --log /tmp/task.log
|
||||
|
||||
# Background mode (returns immediately)
|
||||
scripts/delegate.sh --background --prompt "Long running task" --workdir ~/project
|
||||
```
|
||||
|
||||
## Long-Running Tasks in tmux
|
||||
|
||||
Use `scripts/tmux-session.sh` for persistent sessions (requires tmux):
|
||||
|
||||
```bash
|
||||
# Start a delegation in a named tmux session
|
||||
scripts/tmux-session.sh --name build-api --agent claude --workdir ~/project \
|
||||
--prompt "Build a REST API with authentication"
|
||||
|
||||
# Check session status
|
||||
scripts/tmux-session.sh --status build-api
|
||||
|
||||
# Reattach to session
|
||||
scripts/tmux-session.sh --attach build-api
|
||||
|
||||
# List all delegation sessions
|
||||
scripts/tmux-session.sh --list
|
||||
```
|
||||
|
||||
## Composing Self-Contained Prompts
|
||||
|
||||
The sub-process has no access to the parent agent's conversation, tools, or MCP servers. It can read and write files in the working directory, but has no other context. Include everything else it needs:
|
||||
|
||||
```markdown
|
||||
# Task: [description]
|
||||
|
||||
## Context
|
||||
[All relevant background — the sub-process has no conversation history]
|
||||
|
||||
## Verified Facts (use ONLY these)
|
||||
[Every fact needed — do not assume the sub-process knows anything beyond the working directory]
|
||||
|
||||
## Anti-Hallucination Rule
|
||||
Do not invent facts, numbers, names, or data not listed above.
|
||||
If information is missing, use [TBD] as placeholder.
|
||||
|
||||
## Requirements
|
||||
[Specific deliverable requirements — structure, format, audience]
|
||||
|
||||
## Output
|
||||
[File format, naming, save location]
|
||||
```
|
||||
|
||||
## Security: API Key Stripping
|
||||
|
||||
The delegation scripts strip 23 known AI provider API keys from the sub-process environment. This forces subscription/OAuth-based auth and prevents key leakage to child processes. The list is not exhaustive — if your environment includes provider keys not listed below, add them to the strip list in both scripts.
|
||||
|
||||
Keys stripped: `ANTHROPIC_API_KEY`, `OPENAI_API_KEY`, `GOOGLE_API_KEY`, `GOOGLE_GENERATIVE_AI_API_KEY`, `GEMINI_API_KEY`, `AZURE_OPENAI_API_KEY`, `COHERE_API_KEY`, `MISTRAL_API_KEY`, `OPENROUTER_API_KEY`, `DEEPSEEK_API_KEY`, `TOGETHER_API_KEY`, `FIREWORKS_API_KEY`, `GROQ_API_KEY`, `PERPLEXITY_API_KEY`, `BRAVE_API_KEY`, `BRAVE_SEARCH_API_KEY`, `REPLICATE_API_TOKEN`, `AI21_API_KEY`, `HUGGINGFACE_API_KEY`, `HF_TOKEN`, `VOYAGE_API_KEY`, `ANYSCALE_API_KEY`, `XAI_API_KEY`.
|
||||
|
||||
This is critical when running sub-processes that might:
|
||||
- Spawn their own child processes
|
||||
- Log environment variables
|
||||
- Send telemetry containing env vars
|
||||
|
||||
Both `delegate.sh` and `tmux-session.sh` handle this automatically. For manual invocations, strip at minimum:
|
||||
|
||||
```bash
|
||||
env -u ANTHROPIC_API_KEY -u OPENAI_API_KEY claude --permission-mode bypassPermissions --print 'task'
|
||||
```
|
||||
|
||||
## ACP Runtime (If Available)
|
||||
|
||||
When OpenClaw's ACP runtime is configured, use it as the primary delegation path:
|
||||
|
||||
```
|
||||
sessions_spawn with:
|
||||
runtime: "acp"
|
||||
agentId: "claude" # or "codex" if explicitly requested
|
||||
task: "full prompt" # self-contained (same rules as above)
|
||||
thread: true
|
||||
mode: "session"
|
||||
```
|
||||
|
||||
Fall back to `delegate.sh` if ACP is unhealthy.
|
||||
|
||||
## Waiting for Results
|
||||
|
||||
When running in background mode, always poll for completion:
|
||||
|
||||
1. Launch the delegation
|
||||
2. Poll with `process action:poll sessionId:XXX` or check the log file
|
||||
3. Verify the output exists (`ls -lh /path/to/expected/output`)
|
||||
4. Deliver the result to the user
|
||||
|
||||
Never end your turn after step 1 without polling. The user will not see the result otherwise.
|
||||
|
||||
## Session Resumption
|
||||
|
||||
Claude Code sessions are persistent. For follow-up amendments:
|
||||
|
||||
```bash
|
||||
# Resume the most recent session in the working directory
|
||||
cd /working/dir && claude --permission-mode bypassPermissions --print --continue "Make these changes: ..."
|
||||
|
||||
# Resume a specific session by ID
|
||||
claude --permission-mode bypassPermissions --print --resume <session-id> "Amendments: ..."
|
||||
```
|
||||
|
||||
## Resource Limits
|
||||
|
||||
- Each Claude Code process uses 300-400 MB RAM
|
||||
- Avoid running more than 2-3 concurrent delegations on machines with less than 8 GB RAM
|
||||
- Codex requires a git repository — use `mktemp -d && cd $_ && git init` for scratch work
|
||||
|
||||
## Delegation Policy
|
||||
|
||||
See `references/delegation-policy.md` for a complete example policy covering when to delegate, prompt composition, security, and result handling. Adopt or adapt it for your deployment.
|
||||
|
||||
## Rules
|
||||
|
||||
1. **Respect agent choice** — if the user asks for Codex, use Codex
|
||||
2. **Never hand-code patches yourself** when orchestrating — delegate or ask the user
|
||||
3. **Be patient** — do not kill sessions because they appear slow
|
||||
4. **Monitor with logs** — check progress without interfering
|
||||
5. **Never start coding agents in the OpenClaw config directory** — they may read and act on internal config
|
||||
271
skills/claude-codex-delegation/install.sh
Executable file
271
skills/claude-codex-delegation/install.sh
Executable file
@ -0,0 +1,271 @@
|
||||
#!/usr/bin/env bash
|
||||
# install.sh - Install Claude Code + Codex delegation bundle for OpenClaw.
|
||||
#
|
||||
# Copies the entire skill directory (SKILL.md, scripts, references) into
|
||||
# OpenClaw's shared skill location so the agent can discover and use it.
|
||||
# Optionally installs the Claude Code and Codex CLIs via npm.
|
||||
#
|
||||
# Usage:
|
||||
# install.sh [options]
|
||||
#
|
||||
# Options:
|
||||
# --skip-npm Skip npm package installation (if already installed)
|
||||
# --skill-dir DIR Override skill install location
|
||||
# (default: ~/.openclaw/skills/claude-codex-delegation)
|
||||
# --force Reinstall npm packages even if already present
|
||||
# --dry-run Show what would be done without doing it
|
||||
# -h, --help Show this help message
|
||||
#
|
||||
# Requirements:
|
||||
# - Node.js 22+ with npm
|
||||
# - Existing OpenClaw installation
|
||||
# - GNU coreutils (timeout, script) on Linux; macOS needs coreutils via brew
|
||||
# - Claude Max or OpenAI subscription (for subscription-based auth)
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
SKILL_DIR="${HOME}/.openclaw/skills/claude-codex-delegation"
|
||||
SKIP_NPM=false
|
||||
FORCE=false
|
||||
DRY_RUN=false
|
||||
|
||||
# --- Pinned versions ---
|
||||
CLAUDE_CODE_PKG="@anthropic-ai/claude-code@latest"
|
||||
CODEX_PKG="@openai/codex@latest"
|
||||
|
||||
# --- Colors ---
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
NC='\033[0m'
|
||||
|
||||
info() { echo -e "${GREEN}[OK]${NC} $*"; }
|
||||
warn() { echo -e "${YELLOW}[WARN]${NC} $*"; }
|
||||
error() { echo -e "${RED}[ERROR]${NC} $*" >&2; }
|
||||
|
||||
# --- Parse arguments ---
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case "$1" in
|
||||
--skill-dir)
|
||||
if [[ $# -lt 2 ]]; then
|
||||
echo "Error: $1 requires a value" >&2
|
||||
exit 1
|
||||
fi
|
||||
SKILL_DIR="$2"; shift 2 ;;
|
||||
--skip-npm) SKIP_NPM=true; shift ;;
|
||||
--force) FORCE=true; shift ;;
|
||||
--dry-run) DRY_RUN=true; shift ;;
|
||||
-h|--help)
|
||||
sed -n '2,/^$/p' "$0" | sed 's/^# \?//'
|
||||
exit 0
|
||||
;;
|
||||
*) echo "Error: Unknown option: $1" >&2; exit 1 ;;
|
||||
esac
|
||||
done
|
||||
|
||||
run() {
|
||||
if $DRY_RUN; then
|
||||
echo " [dry-run] $*"
|
||||
else
|
||||
"$@"
|
||||
fi
|
||||
}
|
||||
|
||||
echo ""
|
||||
echo "Claude Code + Codex Delegation - Installer"
|
||||
echo "============================================"
|
||||
echo ""
|
||||
|
||||
# --- Check Node.js ---
|
||||
if ! command -v node &> /dev/null; then
|
||||
error "Node.js is not installed. Install Node.js 22+ first."
|
||||
echo " https://nodejs.org/ or: curl -fsSL https://deb.nodesource.com/setup_22.x | sudo -E bash -"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
NODE_MAJOR="$(node -v | sed 's/^v//' | cut -d. -f1)"
|
||||
if [[ "$NODE_MAJOR" -lt 22 ]]; then
|
||||
error "Node.js $(node -v) detected. Version 22+ is required."
|
||||
echo " Install Node.js 22+: curl -fsSL https://deb.nodesource.com/setup_22.x | sudo -E bash -"
|
||||
exit 1
|
||||
fi
|
||||
info "Node.js $(node -v)"
|
||||
|
||||
if ! command -v npm &> /dev/null; then
|
||||
error "npm is not installed."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# --- Check OpenClaw ---
|
||||
if ! command -v openclaw &> /dev/null; then
|
||||
error "OpenClaw is not installed. Install with: npm install -g openclaw"
|
||||
exit 1
|
||||
fi
|
||||
info "OpenClaw detected ($(openclaw --version 2>/dev/null || echo 'unknown version'))"
|
||||
|
||||
# --- Check required system utilities ---
|
||||
echo ""
|
||||
echo "Checking system dependencies..."
|
||||
MISSING=0
|
||||
|
||||
if ! command -v timeout &> /dev/null; then
|
||||
error "'timeout' is required but not found."
|
||||
if [[ "$(uname)" == "Darwin" ]]; then
|
||||
echo " Install with: brew install coreutils"
|
||||
fi
|
||||
MISSING=$((MISSING + 1))
|
||||
else
|
||||
info "timeout: available"
|
||||
fi
|
||||
|
||||
if ! command -v script &> /dev/null; then
|
||||
error "'script' (from util-linux) is required for Codex PTY support."
|
||||
if [[ "$(uname)" == "Darwin" ]]; then
|
||||
echo " Install with: brew install util-linux"
|
||||
fi
|
||||
MISSING=$((MISSING + 1))
|
||||
else
|
||||
info "script: available"
|
||||
fi
|
||||
|
||||
if command -v tmux &> /dev/null; then
|
||||
info "tmux: available"
|
||||
else
|
||||
warn "tmux: not installed (optional, needed for tmux-session.sh)"
|
||||
fi
|
||||
|
||||
if [[ $MISSING -gt 0 ]]; then
|
||||
error "Missing $MISSING required system dependencies. Install them and rerun."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# --- Install Claude Code and Codex ---
|
||||
if ! $SKIP_NPM; then
|
||||
echo ""
|
||||
echo "Installing Claude Code..."
|
||||
if command -v claude &> /dev/null && ! $FORCE; then
|
||||
info "Claude Code already installed ($(claude --version 2>/dev/null || echo 'unknown version')). Use --force to reinstall."
|
||||
else
|
||||
run npm install -g "$CLAUDE_CODE_PKG"
|
||||
if ! $DRY_RUN; then
|
||||
info "Claude Code installed"
|
||||
fi
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "Installing OpenAI Codex CLI..."
|
||||
if command -v codex &> /dev/null && ! $FORCE; then
|
||||
info "Codex CLI already installed ($(codex --version 2>/dev/null || echo 'unknown version')). Use --force to reinstall."
|
||||
else
|
||||
run npm install -g "$CODEX_PKG"
|
||||
if ! $DRY_RUN; then
|
||||
info "Codex CLI installed"
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
# --- Install skill bundle ---
|
||||
# Copies the entire skill directory (SKILL.md, scripts/, references/) into
|
||||
# OpenClaw's shared skill location (~/.openclaw/skills/) so the agent
|
||||
# discovers it automatically. All resources stay inside the skill folder.
|
||||
echo ""
|
||||
echo "Installing skill bundle to $SKILL_DIR..."
|
||||
run mkdir -p "$SKILL_DIR/scripts"
|
||||
run mkdir -p "$SKILL_DIR/references"
|
||||
|
||||
# SKILL.md
|
||||
if [[ -f "$SCRIPT_DIR/SKILL.md" ]]; then
|
||||
run cp "$SCRIPT_DIR/SKILL.md" "$SKILL_DIR/SKILL.md"
|
||||
info "Installed SKILL.md"
|
||||
fi
|
||||
|
||||
# Scripts (inside the skill directory, not a global scripts dir)
|
||||
for script in delegate.sh tmux-session.sh; do
|
||||
SRC="$SCRIPT_DIR/scripts/$script"
|
||||
DST="$SKILL_DIR/scripts/$script"
|
||||
if [[ -f "$SRC" ]]; then
|
||||
run cp "$SRC" "$DST"
|
||||
run chmod +x "$DST"
|
||||
info "Installed scripts/$script"
|
||||
else
|
||||
error "Script not found: $SRC"
|
||||
fi
|
||||
done
|
||||
|
||||
# References
|
||||
if [[ -f "$SCRIPT_DIR/references/delegation-policy.md" ]]; then
|
||||
run cp "$SCRIPT_DIR/references/delegation-policy.md" "$SKILL_DIR/references/delegation-policy.md"
|
||||
info "Installed references/delegation-policy.md"
|
||||
fi
|
||||
|
||||
# --- Verify ---
|
||||
echo ""
|
||||
echo "Verifying installation..."
|
||||
ERRORS=0
|
||||
|
||||
if command -v claude &> /dev/null || $DRY_RUN; then
|
||||
info "claude CLI: OK"
|
||||
else
|
||||
error "claude CLI: not found"
|
||||
ERRORS=$((ERRORS + 1))
|
||||
fi
|
||||
|
||||
if command -v codex &> /dev/null || $DRY_RUN; then
|
||||
info "codex CLI: OK"
|
||||
else
|
||||
error "codex CLI: not found"
|
||||
ERRORS=$((ERRORS + 1))
|
||||
fi
|
||||
|
||||
if [[ -f "$SKILL_DIR/SKILL.md" ]] || $DRY_RUN; then
|
||||
info "SKILL.md: OK"
|
||||
else
|
||||
error "SKILL.md: not found at $SKILL_DIR"
|
||||
ERRORS=$((ERRORS + 1))
|
||||
fi
|
||||
|
||||
if [[ -x "$SKILL_DIR/scripts/delegate.sh" ]] || $DRY_RUN; then
|
||||
info "scripts/delegate.sh: OK"
|
||||
else
|
||||
error "scripts/delegate.sh: not found or not executable"
|
||||
ERRORS=$((ERRORS + 1))
|
||||
fi
|
||||
|
||||
if [[ -x "$SKILL_DIR/scripts/tmux-session.sh" ]] || $DRY_RUN; then
|
||||
info "scripts/tmux-session.sh: OK"
|
||||
else
|
||||
error "scripts/tmux-session.sh: not found or not executable"
|
||||
ERRORS=$((ERRORS + 1))
|
||||
fi
|
||||
|
||||
# --- Auth check ---
|
||||
echo ""
|
||||
echo "Checking auth..."
|
||||
echo " Claude Code: run 'claude auth' to verify subscription/OAuth is configured"
|
||||
echo " Codex: run 'codex auth' to verify OpenAI subscription is configured"
|
||||
echo ""
|
||||
echo " NOTE: The delegation scripts strip API keys from the sub-process"
|
||||
echo " environment. Both CLIs must be configured with subscription/OAuth"
|
||||
echo " auth. API key auth will NOT work."
|
||||
|
||||
# --- Summary ---
|
||||
echo ""
|
||||
if [[ $ERRORS -eq 0 ]]; then
|
||||
echo "============================================"
|
||||
info "Installation complete."
|
||||
echo ""
|
||||
echo " Skill installed to: $SKILL_DIR"
|
||||
echo ""
|
||||
echo " Delegate a task:"
|
||||
echo " $SKILL_DIR/scripts/delegate.sh --prompt 'Your task' --workdir ~/project"
|
||||
echo ""
|
||||
echo " Long-running task in tmux:"
|
||||
echo " $SKILL_DIR/scripts/tmux-session.sh --name my-task --prompt 'Your task' --workdir ~/project"
|
||||
echo ""
|
||||
echo " OpenClaw will discover the skill automatically on next session."
|
||||
echo "============================================"
|
||||
else
|
||||
error "$ERRORS verification error(s). Check the output above."
|
||||
exit 1
|
||||
fi
|
||||
@ -0,0 +1,76 @@
|
||||
# Example Delegation Policy
|
||||
|
||||
Adopt or adapt this policy to govern how your agent delegates tasks to Claude Code and Codex sub-processes.
|
||||
|
||||
## When to Delegate
|
||||
|
||||
Delegate when the task involves:
|
||||
- Writing or modifying code (Python, TypeScript, shell scripts, etc.)
|
||||
- Generating files (documents, spreadsheets, presentations)
|
||||
- Complex multi-step coding workflows (build, test, commit)
|
||||
- Refactoring or code review
|
||||
|
||||
Do NOT delegate:
|
||||
- Trivial one-liners (a single sed/awk/jq command)
|
||||
- JSON/YAML config edits
|
||||
- Tasks that only need MCP tools (email, calendar, search)
|
||||
|
||||
## Pre-Delegation Checklist
|
||||
|
||||
Before spawning a sub-process, complete these steps:
|
||||
|
||||
1. **Identify context** — what project, codebase, or domain does this relate to?
|
||||
2. **Gather facts** — collect all data the sub-process will need (it has no conversation history or MCP access, only filesystem access in the working directory)
|
||||
3. **Compose a self-contained prompt** — include context, facts, requirements, and output format
|
||||
4. **Apply anti-hallucination rules** — list verified facts and instruct the sub-process to use only those
|
||||
|
||||
## Prompt Template
|
||||
|
||||
```markdown
|
||||
# Task: [description]
|
||||
|
||||
## Context
|
||||
[Background the sub-process needs to understand the task]
|
||||
|
||||
## Verified Facts (use ONLY these)
|
||||
- [Fact 1]
|
||||
- [Fact 2]
|
||||
- [Fact N]
|
||||
|
||||
## Anti-Hallucination Rule
|
||||
Do not invent facts, numbers, names, or data not listed above.
|
||||
If information is missing, use [TBD] as placeholder.
|
||||
|
||||
## Requirements
|
||||
[Deliverable specifications — structure, format, audience, constraints]
|
||||
|
||||
## Output
|
||||
[File format, naming convention, save location]
|
||||
```
|
||||
|
||||
## Agent Selection Policy
|
||||
|
||||
- **Default to Claude Code** for all coding and generation tasks
|
||||
- **Use Codex only when the user explicitly requests it**
|
||||
- Never auto-select Codex based on task type
|
||||
|
||||
## Security Policy
|
||||
|
||||
- Always strip AI provider API keys from the sub-process environment
|
||||
- The delegation scripts strip 23 known provider keys (see `SKILL.md` for the full list). The list is not exhaustive — add any additional keys your environment uses
|
||||
- This forces subscription/OAuth auth and prevents key leakage
|
||||
- For manual invocations, use `env -u ANTHROPIC_API_KEY -u OPENAI_API_KEY ...` at minimum
|
||||
|
||||
## Result Handling
|
||||
|
||||
1. Always poll for completion — never end your turn after launching a background task
|
||||
2. Verify the output file exists
|
||||
3. Summarize results (do not dump raw logs)
|
||||
4. If the sub-process failed, read the log and either retry with fixes or report the failure
|
||||
|
||||
## Session Resumption
|
||||
|
||||
When the user requests changes to a previous delegation:
|
||||
- Resume the existing session with `--continue` or `--resume <session-id>`
|
||||
- No need to re-compose the full context
|
||||
- State only the amendments needed
|
||||
213
skills/claude-codex-delegation/scripts/delegate.sh
Executable file
213
skills/claude-codex-delegation/scripts/delegate.sh
Executable file
@ -0,0 +1,213 @@
|
||||
#!/usr/bin/env bash
|
||||
# delegate.sh — Launch Claude Code or Codex as a sub-process.
|
||||
#
|
||||
# Runs a delegation with API key stripping for security.
|
||||
# Supports prompt strings, prompt files, foreground and background
|
||||
# execution.
|
||||
#
|
||||
# Usage:
|
||||
# delegate.sh --prompt "Your task" [options]
|
||||
# delegate.sh --file /path/to/prompt.md [options]
|
||||
#
|
||||
# Options:
|
||||
# --prompt TEXT Inline prompt string
|
||||
# --file PATH Read prompt from file
|
||||
# --agent AGENT "claude" (default) or "codex"
|
||||
# --workdir DIR Working directory for the sub-process
|
||||
# --log PATH Log file path (default: /tmp/delegation-<timestamp>.log)
|
||||
# --background Run in background, return immediately
|
||||
# --full-auto Codex: enable full-auto mode (default for codex)
|
||||
# --timeout SECS Kill sub-process after N seconds (default: 3600)
|
||||
# -h, --help Show this help message
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
# --- Defaults ---
|
||||
AGENT="claude"
|
||||
PROMPT=""
|
||||
PROMPT_FILE=""
|
||||
WORKDIR="${PWD}"
|
||||
LOG_FILE=""
|
||||
BACKGROUND=false
|
||||
FULL_AUTO=true
|
||||
TIMEOUT=3600
|
||||
|
||||
# --- Parse arguments ---
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case "$1" in
|
||||
--prompt|--file|--agent|--workdir|--log|--timeout)
|
||||
if [[ $# -lt 2 ]]; then
|
||||
echo "Error: $1 requires a value" >&2
|
||||
exit 1
|
||||
fi
|
||||
;;&
|
||||
--prompt) PROMPT="$2"; shift 2 ;;
|
||||
--file) PROMPT_FILE="$2"; shift 2 ;;
|
||||
--agent) AGENT="$2"; shift 2 ;;
|
||||
--workdir) WORKDIR="$2"; shift 2 ;;
|
||||
--log) LOG_FILE="$2"; shift 2 ;;
|
||||
--background) BACKGROUND=true; shift ;;
|
||||
--full-auto) FULL_AUTO=true; shift ;;
|
||||
--no-full-auto) FULL_AUTO=false; shift ;;
|
||||
--timeout) TIMEOUT="$2"; shift 2 ;;
|
||||
-h|--help)
|
||||
sed -n '2,/^$/p' "$0" | sed 's/^# \?//'
|
||||
exit 0
|
||||
;;
|
||||
*) echo "Unknown option: $1" >&2; exit 1 ;;
|
||||
esac
|
||||
done
|
||||
|
||||
# --- Validate ---
|
||||
if [[ -z "$PROMPT" && -z "$PROMPT_FILE" ]]; then
|
||||
echo "Error: --prompt or --file is required" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ -n "$PROMPT_FILE" ]]; then
|
||||
if [[ ! -f "$PROMPT_FILE" ]]; then
|
||||
echo "Error: prompt file not found: $PROMPT_FILE" >&2
|
||||
exit 1
|
||||
fi
|
||||
PROMPT="$(cat "$PROMPT_FILE")"
|
||||
fi
|
||||
|
||||
if [[ -z "$PROMPT" ]]; then
|
||||
echo "Error: prompt is empty" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ "$AGENT" != "claude" && "$AGENT" != "codex" ]]; then
|
||||
echo "Error: --agent must be 'claude' or 'codex'" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Check agent binary exists
|
||||
if ! command -v "$AGENT" &> /dev/null; then
|
||||
if [[ "$AGENT" == "claude" ]]; then
|
||||
echo "Error: 'claude' is not installed. Install with: npm install -g @anthropic-ai/claude-code" >&2
|
||||
else
|
||||
echo "Error: 'codex' is not installed. Install with: npm install -g @openai/codex" >&2
|
||||
fi
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Check required utilities
|
||||
if ! command -v timeout &> /dev/null; then
|
||||
echo "Error: 'timeout' is required but not found" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ "$AGENT" == "codex" ]]; then
|
||||
if ! command -v script &> /dev/null; then
|
||||
echo "Error: 'script' (from util-linux) is required for Codex PTY support" >&2
|
||||
exit 1
|
||||
fi
|
||||
# BSD script (macOS) does not support -c flag
|
||||
if [[ "$(uname)" == "Darwin" ]]; then
|
||||
echo "Error: Codex delegation via delegate.sh requires Linux (GNU script)." >&2
|
||||
echo "On macOS, use tmux-session.sh or run Codex directly with a PTY." >&2
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
if [[ ! -d "$WORKDIR" ]]; then
|
||||
echo "Error: working directory not found: $WORKDIR" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Codex requires a git repository — fail fast instead of silently mutating
|
||||
if [[ "$AGENT" == "codex" ]]; then
|
||||
if ! (cd "$WORKDIR" && { [[ -d .git ]] || git rev-parse --git-dir > /dev/null 2>&1; }); then
|
||||
echo "Error: Codex requires a git repository but $WORKDIR is not one." >&2
|
||||
echo "Initialize one with: cd $WORKDIR && git init" >&2
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
# --- Set up log file ---
|
||||
if [[ -z "$LOG_FILE" ]]; then
|
||||
LOG_FILE="/tmp/delegation-$(date +%s)-$$.log"
|
||||
fi
|
||||
mkdir -p "$(dirname "$LOG_FILE")"
|
||||
|
||||
# --- Strip AI provider credentials from environment ---
|
||||
# Prevents key leakage to child processes. Forces subscription/OAuth auth.
|
||||
for var in ANTHROPIC_API_KEY OPENAI_API_KEY GOOGLE_API_KEY GOOGLE_GENERATIVE_AI_API_KEY \
|
||||
AZURE_OPENAI_API_KEY COHERE_API_KEY MISTRAL_API_KEY OPENROUTER_API_KEY \
|
||||
DEEPSEEK_API_KEY TOGETHER_API_KEY FIREWORKS_API_KEY GROQ_API_KEY \
|
||||
GEMINI_API_KEY PERPLEXITY_API_KEY BRAVE_API_KEY BRAVE_SEARCH_API_KEY \
|
||||
REPLICATE_API_TOKEN AI21_API_KEY HUGGINGFACE_API_KEY HF_TOKEN \
|
||||
VOYAGE_API_KEY ANYSCALE_API_KEY XAI_API_KEY; do
|
||||
unset "$var" 2>/dev/null || true
|
||||
done
|
||||
|
||||
# --- Build command ---
|
||||
build_claude_cmd() {
|
||||
echo "claude --permission-mode bypassPermissions --print"
|
||||
}
|
||||
|
||||
build_codex_cmd() {
|
||||
local cmd="codex exec"
|
||||
if $FULL_AUTO; then
|
||||
cmd="codex exec --full-auto"
|
||||
fi
|
||||
echo "$cmd"
|
||||
}
|
||||
|
||||
run_delegation() {
|
||||
local exit_code=0
|
||||
|
||||
cd "$WORKDIR" || exit 1
|
||||
|
||||
case "$AGENT" in
|
||||
claude)
|
||||
$(build_claude_cmd) "$PROMPT" > "$LOG_FILE" 2>&1 || exit_code=$?
|
||||
;;
|
||||
codex)
|
||||
# Codex requires a PTY — use script(1) to provide one.
|
||||
# -e: propagate child exit code (without it, script always returns 0)
|
||||
# Run through bash -c so printf '%q' quoting is interpreted correctly
|
||||
# (script passes commands to /bin/sh which may be dash, not bash).
|
||||
script -e -q -c "bash -c $(printf '%q' "$(build_codex_cmd) $(printf '%q' "$PROMPT")")" "$LOG_FILE" || exit_code=$?
|
||||
;;
|
||||
esac
|
||||
|
||||
return $exit_code
|
||||
}
|
||||
|
||||
# --- Execute ---
|
||||
echo "Delegating to $AGENT in $WORKDIR (timeout: ${TIMEOUT}s)"
|
||||
echo "Log: $LOG_FILE"
|
||||
|
||||
ESCAPED_WORKDIR="$(printf '%q' "$WORKDIR")"
|
||||
ESCAPED_LOG="$(printf '%q' "$LOG_FILE")"
|
||||
|
||||
if $BACKGROUND; then
|
||||
(
|
||||
EXIT_CODE=0
|
||||
timeout "$TIMEOUT" bash -c "$(declare -f run_delegation build_claude_cmd build_codex_cmd); \
|
||||
AGENT='$AGENT' PROMPT='$(printf '%s' "$PROMPT" | sed "s/'/'\\\\''/g")' \
|
||||
WORKDIR=$ESCAPED_WORKDIR LOG_FILE=$ESCAPED_LOG FULL_AUTO=$FULL_AUTO \
|
||||
run_delegation" || EXIT_CODE=$?
|
||||
echo ""
|
||||
echo "--- Delegation complete (exit code: $EXIT_CODE) ---"
|
||||
) >> "$LOG_FILE" 2>&1 &
|
||||
BG_PID=$!
|
||||
echo "Background PID: $BG_PID"
|
||||
echo "Monitor: tail -f $LOG_FILE"
|
||||
else
|
||||
EXIT_CODE=0
|
||||
timeout "$TIMEOUT" bash -c "$(declare -f run_delegation build_claude_cmd build_codex_cmd); \
|
||||
AGENT='$AGENT' PROMPT='$(printf '%s' "$PROMPT" | sed "s/'/'\\\\''/g")' \
|
||||
WORKDIR=$ESCAPED_WORKDIR LOG_FILE=$ESCAPED_LOG FULL_AUTO=$FULL_AUTO \
|
||||
run_delegation" || EXIT_CODE=$?
|
||||
if [[ $EXIT_CODE -eq 0 ]]; then
|
||||
echo "Delegation complete."
|
||||
elif [[ $EXIT_CODE -eq 124 ]]; then
|
||||
echo "Delegation timed out after ${TIMEOUT}s. Check log: $LOG_FILE" >&2
|
||||
else
|
||||
echo "Delegation failed (exit code: $EXIT_CODE). Check log: $LOG_FILE" >&2
|
||||
fi
|
||||
exit $EXIT_CODE
|
||||
fi
|
||||
199
skills/claude-codex-delegation/scripts/tmux-session.sh
Executable file
199
skills/claude-codex-delegation/scripts/tmux-session.sh
Executable file
@ -0,0 +1,199 @@
|
||||
#!/usr/bin/env bash
|
||||
# tmux-session.sh — Manage long-running Claude Code / Codex delegations in tmux.
|
||||
#
|
||||
# Usage:
|
||||
# tmux-session.sh --name SESSION --prompt "task" [options]
|
||||
# tmux-session.sh --attach SESSION
|
||||
# tmux-session.sh --status SESSION
|
||||
# tmux-session.sh --list
|
||||
# tmux-session.sh --kill SESSION
|
||||
#
|
||||
# Options:
|
||||
# --name NAME Session name (required for new sessions)
|
||||
# --prompt TEXT Task prompt
|
||||
# --file PATH Read prompt from file (alternative to --prompt)
|
||||
# --agent AGENT "claude" (default) or "codex"
|
||||
# --workdir DIR Working directory (default: current directory)
|
||||
# --attach NAME Attach to an existing session
|
||||
# --status NAME Check if a session is active and show last output
|
||||
# --list List all delegation sessions
|
||||
# --kill NAME Kill a session
|
||||
# -h, --help Show this help message
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
SESSION_PREFIX="delegation"
|
||||
ACTION=""
|
||||
SESSION_NAME=""
|
||||
AGENT="claude"
|
||||
PROMPT=""
|
||||
PROMPT_FILE=""
|
||||
WORKDIR="${PWD}"
|
||||
|
||||
# --- Parse arguments ---
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case "$1" in
|
||||
--name|--prompt|--file|--agent|--workdir|--attach|--status|--kill)
|
||||
if [[ $# -lt 2 ]]; then
|
||||
echo "Error: $1 requires a value" >&2
|
||||
exit 1
|
||||
fi
|
||||
;;&
|
||||
--name) SESSION_NAME="$2"; ACTION="${ACTION:-create}"; shift 2 ;;
|
||||
--prompt) PROMPT="$2"; shift 2 ;;
|
||||
--file) PROMPT_FILE="$2"; shift 2 ;;
|
||||
--agent) AGENT="$2"; shift 2 ;;
|
||||
--workdir) WORKDIR="$2"; shift 2 ;;
|
||||
--attach) ACTION="attach"; SESSION_NAME="$2"; shift 2 ;;
|
||||
--status) ACTION="status"; SESSION_NAME="$2"; shift 2 ;;
|
||||
--list) ACTION="list"; shift ;;
|
||||
--kill) ACTION="kill"; SESSION_NAME="$2"; shift 2 ;;
|
||||
-h|--help)
|
||||
sed -n '2,/^$/p' "$0" | sed 's/^# \?//'
|
||||
exit 0
|
||||
;;
|
||||
*) echo "Unknown option: $1" >&2; exit 1 ;;
|
||||
esac
|
||||
done
|
||||
|
||||
# --- Check tmux ---
|
||||
if ! command -v tmux &> /dev/null; then
|
||||
echo "Error: tmux is not installed" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
full_session_name() {
|
||||
echo "${SESSION_PREFIX}-${1}"
|
||||
}
|
||||
|
||||
# --- Actions ---
|
||||
case "$ACTION" in
|
||||
list)
|
||||
echo "Active delegation sessions:"
|
||||
tmux list-sessions -F '#{session_name} (created #{session_created_string})' 2>/dev/null \
|
||||
| grep "^${SESSION_PREFIX}-" \
|
||||
| sed "s/^${SESSION_PREFIX}-/ /" \
|
||||
|| echo " (none)"
|
||||
;;
|
||||
|
||||
status)
|
||||
FULL_NAME="$(full_session_name "$SESSION_NAME")"
|
||||
if tmux has-session -t "$FULL_NAME" 2>/dev/null; then
|
||||
echo "Session '$SESSION_NAME' is active."
|
||||
echo ""
|
||||
echo "--- Last 20 lines of output ---"
|
||||
tmux capture-pane -t "$FULL_NAME" -p | tail -20
|
||||
else
|
||||
echo "Session '$SESSION_NAME' is not running."
|
||||
fi
|
||||
;;
|
||||
|
||||
attach)
|
||||
FULL_NAME="$(full_session_name "$SESSION_NAME")"
|
||||
if tmux has-session -t "$FULL_NAME" 2>/dev/null; then
|
||||
tmux attach -t "$FULL_NAME"
|
||||
else
|
||||
echo "Session '$SESSION_NAME' not found." >&2
|
||||
exit 1
|
||||
fi
|
||||
;;
|
||||
|
||||
kill)
|
||||
FULL_NAME="$(full_session_name "$SESSION_NAME")"
|
||||
if tmux has-session -t "$FULL_NAME" 2>/dev/null; then
|
||||
tmux kill-session -t "$FULL_NAME"
|
||||
echo "Session '$SESSION_NAME' killed."
|
||||
else
|
||||
echo "Session '$SESSION_NAME' not found." >&2
|
||||
exit 1
|
||||
fi
|
||||
;;
|
||||
|
||||
create)
|
||||
if [[ -z "$SESSION_NAME" ]]; then
|
||||
echo "Error: --name is required" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Check agent binary exists
|
||||
if ! command -v "$AGENT" &> /dev/null; then
|
||||
echo "Error: '$AGENT' is not installed. Install with: npm install -g @anthropic-ai/claude-code (or @openai/codex)" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ ! -d "$WORKDIR" ]]; then
|
||||
echo "Error: working directory not found: $WORKDIR" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Codex requires a git repo
|
||||
if [[ "$AGENT" == "codex" ]]; then
|
||||
if ! (cd "$WORKDIR" && { [[ -d .git ]] || git rev-parse --git-dir > /dev/null 2>&1; }); then
|
||||
echo "Error: Codex requires a git repository but $WORKDIR is not one." >&2
|
||||
echo "Initialize one with: cd $WORKDIR && git init" >&2
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
if [[ -z "$PROMPT" && -z "$PROMPT_FILE" ]]; then
|
||||
echo "Error: --prompt or --file is required" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ -n "$PROMPT_FILE" ]]; then
|
||||
if [[ ! -f "$PROMPT_FILE" ]]; then
|
||||
echo "Error: prompt file not found: $PROMPT_FILE" >&2
|
||||
exit 1
|
||||
fi
|
||||
PROMPT="$(cat "$PROMPT_FILE")"
|
||||
fi
|
||||
|
||||
if [[ -z "$PROMPT" ]]; then
|
||||
echo "Error: prompt is empty" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
FULL_NAME="$(full_session_name "$SESSION_NAME")"
|
||||
|
||||
if tmux has-session -t "$FULL_NAME" 2>/dev/null; then
|
||||
echo "Session '$SESSION_NAME' already exists. Use --attach or --kill first." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Strip AI provider credentials from environment
|
||||
STRIP_VARS="ANTHROPIC_API_KEY OPENAI_API_KEY GOOGLE_API_KEY GOOGLE_GENERATIVE_AI_API_KEY"
|
||||
STRIP_VARS="$STRIP_VARS AZURE_OPENAI_API_KEY COHERE_API_KEY MISTRAL_API_KEY OPENROUTER_API_KEY"
|
||||
STRIP_VARS="$STRIP_VARS DEEPSEEK_API_KEY TOGETHER_API_KEY FIREWORKS_API_KEY GROQ_API_KEY"
|
||||
STRIP_VARS="$STRIP_VARS GEMINI_API_KEY PERPLEXITY_API_KEY BRAVE_API_KEY BRAVE_SEARCH_API_KEY"
|
||||
STRIP_VARS="$STRIP_VARS REPLICATE_API_TOKEN AI21_API_KEY HUGGINGFACE_API_KEY HF_TOKEN"
|
||||
STRIP_VARS="$STRIP_VARS VOYAGE_API_KEY ANYSCALE_API_KEY XAI_API_KEY"
|
||||
ENV_STRIP=""
|
||||
for v in $STRIP_VARS; do ENV_STRIP="$ENV_STRIP -u $v"; done
|
||||
|
||||
# Build the command
|
||||
case "$AGENT" in
|
||||
claude)
|
||||
CMD="cd $(printf '%q' "$WORKDIR") && env $ENV_STRIP claude --permission-mode bypassPermissions --print $(printf '%q' "$PROMPT")"
|
||||
;;
|
||||
codex)
|
||||
CMD="cd $(printf '%q' "$WORKDIR") && env $ENV_STRIP codex exec --full-auto $(printf '%q' "$PROMPT")"
|
||||
;;
|
||||
*)
|
||||
echo "Error: --agent must be 'claude' or 'codex'" >&2
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
tmux new-session -d -s "$FULL_NAME" -c "$WORKDIR" "$CMD; echo ''; echo '--- Session complete. Press Enter to close. ---'; read"
|
||||
echo "Session '$SESSION_NAME' started."
|
||||
echo " Attach: $0 --attach $SESSION_NAME"
|
||||
echo " Status: $0 --status $SESSION_NAME"
|
||||
echo " Kill: $0 --kill $SESSION_NAME"
|
||||
;;
|
||||
|
||||
*)
|
||||
echo "Error: specify an action (--name, --attach, --status, --list, --kill)" >&2
|
||||
echo "Run with --help for usage." >&2
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
Loading…
x
Reference in New Issue
Block a user