skill: add Claude Code + Codex delegation bundle

Adds a skill for delegating code-writing, document generation, and
analysis tasks to Claude Code and OpenAI Codex as sub-processes.

Includes:
- SKILL.md with delegation routing, prompt composition, and security docs
- scripts/delegate.sh — unified delegation launcher with API key stripping
- scripts/tmux-session.sh — tmux session manager for long-running tasks
- references/delegation-policy.md — example policy (adoptable, not prescriptive)

Security: strips 23 known AI provider API keys from sub-process
environment, forcing subscription-based auth and preventing key leakage.
This commit is contained in:
Robert Koller 2026-03-16 04:28:58 +00:00
parent 986b772a89
commit f980fa72bf
4 changed files with 642 additions and 0 deletions

View File

@ -0,0 +1,166 @@
---
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", "darwin"], "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.
## 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

View File

@ -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

View File

@ -0,0 +1,200 @@
#!/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" ]] && ! command -v script &> /dev/null; then
echo "Error: 'script' (from util-linux) is required for Codex PTY support" >&2
exit 1
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
cd "$WORKDIR"
if [[ ! -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
script -q -c "$(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"
if $BACKGROUND; then
(
timeout "$TIMEOUT" bash -c "$(declare -f run_delegation build_claude_cmd build_codex_cmd); \
AGENT='$AGENT' PROMPT='$(printf '%s' "$PROMPT" | sed "s/'/'\\\\''/g")' \
WORKDIR='$WORKDIR' LOG_FILE='$LOG_FILE' 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
timeout "$TIMEOUT" bash -c "$(declare -f run_delegation build_claude_cmd build_codex_cmd); \
AGENT='$AGENT' PROMPT='$(printf '%s' "$PROMPT" | sed "s/'/'\\\\''/g")' \
WORKDIR='$WORKDIR' LOG_FILE='$LOG_FILE' 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

View File

@ -0,0 +1,200 @@
#!/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
cd "$WORKDIR"
if [[ ! -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