From f980fa72bf4daaf7a8c371a1739aa96ecf2c287d Mon Sep 17 00:00:00 2001 From: Robert Koller Date: Mon, 16 Mar 2026 04:28:58 +0000 Subject: [PATCH 1/6] skill: add Claude Code + Codex delegation bundle MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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. --- skills/claude-codex-delegation/SKILL.md | 166 +++++++++++++++ .../references/delegation-policy.md | 76 +++++++ .../scripts/delegate.sh | 200 ++++++++++++++++++ .../scripts/tmux-session.sh | 200 ++++++++++++++++++ 4 files changed, 642 insertions(+) create mode 100644 skills/claude-codex-delegation/SKILL.md create mode 100644 skills/claude-codex-delegation/references/delegation-policy.md create mode 100755 skills/claude-codex-delegation/scripts/delegate.sh create mode 100755 skills/claude-codex-delegation/scripts/tmux-session.sh diff --git a/skills/claude-codex-delegation/SKILL.md b/skills/claude-codex-delegation/SKILL.md new file mode 100644 index 00000000000..d5d41166155 --- /dev/null +++ b/skills/claude-codex-delegation/SKILL.md @@ -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 "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 diff --git a/skills/claude-codex-delegation/references/delegation-policy.md b/skills/claude-codex-delegation/references/delegation-policy.md new file mode 100644 index 00000000000..4bc331193cd --- /dev/null +++ b/skills/claude-codex-delegation/references/delegation-policy.md @@ -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 ` +- No need to re-compose the full context +- State only the amendments needed diff --git a/skills/claude-codex-delegation/scripts/delegate.sh b/skills/claude-codex-delegation/scripts/delegate.sh new file mode 100755 index 00000000000..99d0a4cb883 --- /dev/null +++ b/skills/claude-codex-delegation/scripts/delegate.sh @@ -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-.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 diff --git a/skills/claude-codex-delegation/scripts/tmux-session.sh b/skills/claude-codex-delegation/scripts/tmux-session.sh new file mode 100755 index 00000000000..a8701b0b647 --- /dev/null +++ b/skills/claude-codex-delegation/scripts/tmux-session.sh @@ -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 From 126a8f906945245450f96ee129fcdcaaeddfcdd5 Mon Sep 17 00:00:00 2001 From: Robert Koller Date: Mon, 16 Mar 2026 05:08:27 +0000 Subject: [PATCH 2/6] fix: escape WORKDIR and LOG_FILE in bash -c invocations Paths containing single quotes would break the generated bash -c script. Use printf '%q' to safely escape both variables, matching the existing PROMPT escaping approach. --- skills/claude-codex-delegation/scripts/delegate.sh | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/skills/claude-codex-delegation/scripts/delegate.sh b/skills/claude-codex-delegation/scripts/delegate.sh index 99d0a4cb883..f73d3a38a6a 100755 --- a/skills/claude-codex-delegation/scripts/delegate.sh +++ b/skills/claude-codex-delegation/scripts/delegate.sh @@ -170,11 +170,14 @@ run_delegation() { 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 ( 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 \ + WORKDIR=$ESCAPED_WORKDIR LOG_FILE=$ESCAPED_LOG FULL_AUTO=$FULL_AUTO \ run_delegation" EXIT_CODE=$? echo "" @@ -186,7 +189,7 @@ if $BACKGROUND; then 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 \ + WORKDIR=$ESCAPED_WORKDIR LOG_FILE=$ESCAPED_LOG FULL_AUTO=$FULL_AUTO \ run_delegation" EXIT_CODE=$? if [[ $EXIT_CODE -eq 0 ]]; then From c8d5808888b7f6c0c003645a05d384cb426b17fb Mon Sep 17 00:00:00 2001 From: Robert Koller Date: Mon, 16 Mar 2026 05:10:29 +0000 Subject: [PATCH 3/6] fix: add macOS guard for Codex and fix script/sh quoting - Codex delegation via delegate.sh now errors on macOS where BSD script does not support -c (points user to tmux-session.sh instead) - Wrap script -c command through bash -c so printf '%q' output is interpreted by bash, not /bin/sh (which may be dash) --- .../scripts/delegate.sh | 20 ++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/skills/claude-codex-delegation/scripts/delegate.sh b/skills/claude-codex-delegation/scripts/delegate.sh index f73d3a38a6a..6403de9ae4e 100755 --- a/skills/claude-codex-delegation/scripts/delegate.sh +++ b/skills/claude-codex-delegation/scripts/delegate.sh @@ -98,9 +98,17 @@ if ! command -v timeout &> /dev/null; then 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 +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 @@ -158,8 +166,10 @@ run_delegation() { $(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=$? + # Codex requires a PTY — use script(1) to provide one. + # Run through bash -c so printf '%q' quoting is interpreted correctly + # (script passes commands to /bin/sh which may be dash, not bash). + script -q -c "bash -c $(printf '%q' "$(build_codex_cmd) $(printf '%q' "$PROMPT")")" "$LOG_FILE" || exit_code=$? ;; esac From caf8a05e04b52915560e388339f1423dfa518de6 Mon Sep 17 00:00:00 2001 From: Robert Koller Date: Mon, 16 Mar 2026 05:13:18 +0000 Subject: [PATCH 4/6] feat: add install.sh for full bundle installation Installs Claude Code + Codex via npm, copies delegation scripts into ~/.openclaw/scripts, installs skill definition, and verifies the setup. Supports --skip-npm, --scripts-dir, and --dry-run. Checks Node.js 22+, OpenClaw, and subscription auth requirements. --- skills/claude-codex-delegation/SKILL.md | 15 ++ skills/claude-codex-delegation/install.sh | 231 ++++++++++++++++++++++ 2 files changed, 246 insertions(+) create mode 100755 skills/claude-codex-delegation/install.sh diff --git a/skills/claude-codex-delegation/SKILL.md b/skills/claude-codex-delegation/SKILL.md index d5d41166155..f64f77f6b53 100644 --- a/skills/claude-codex-delegation/SKILL.md +++ b/skills/claude-codex-delegation/SKILL.md @@ -11,6 +11,21 @@ Delegate coding tasks to Claude Code or OpenAI Codex as sub-processes. Each dele 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 | diff --git a/skills/claude-codex-delegation/install.sh b/skills/claude-codex-delegation/install.sh new file mode 100755 index 00000000000..915dbbe0950 --- /dev/null +++ b/skills/claude-codex-delegation/install.sh @@ -0,0 +1,231 @@ +#!/usr/bin/env bash +# install.sh — Install Claude Code + Codex delegation bundle for OpenClaw. +# +# Installs the Claude Code and Codex CLIs, copies delegation scripts +# into your OpenClaw workspace, and verifies the setup. +# +# Usage: +# install.sh [options] +# +# Options: +# --skip-npm Skip npm package installation (if already installed) +# --scripts-dir DIR Where to install scripts (default: ~/.openclaw/scripts) +# --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 +# - Claude Max or OpenAI subscription (for subscription-based auth) + +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +SCRIPTS_DIR="${HOME}/.openclaw/scripts" +SKIP_NPM=false +DRY_RUN=false + +# --- Versions (pin to known-good releases) --- +CLAUDE_CODE_PKG="@anthropic-ai/claude-code" +CODEX_PKG="@openai/codex" + +# --- 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 + --skip-npm|--scripts-dir|--dry-run) + if [[ "$1" == "--scripts-dir" ]]; then + if [[ $# -lt 2 ]]; then + echo "Error: $1 requires a value" >&2 + exit 1 + fi + fi + ;;& + --skip-npm) SKIP_NPM=true; shift ;; + --scripts-dir) SCRIPTS_DIR="$2"; shift 2 ;; + --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 + warn "Node.js $(node -v) detected. Version 22+ is recommended." +fi + +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'))" + +# --- Install Claude Code --- +if ! $SKIP_NPM; then + echo "" + echo "Installing Claude Code..." + if command -v claude &> /dev/null; then + info "Claude Code already installed ($(claude --version 2>/dev/null || echo 'unknown version'))" + 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; then + info "Codex CLI already installed ($(codex --version 2>/dev/null || echo 'unknown version'))" + else + run npm install -g "$CODEX_PKG" + if ! $DRY_RUN; then + info "Codex CLI installed" + fi + fi +fi + +# --- Install delegation scripts --- +echo "" +echo "Installing delegation scripts to $SCRIPTS_DIR..." +run mkdir -p "$SCRIPTS_DIR" + +for script in delegate.sh tmux-session.sh; do + SRC="$SCRIPT_DIR/scripts/$script" + DST="$SCRIPTS_DIR/$script" + if [[ -f "$SRC" ]]; then + run cp "$SRC" "$DST" + run chmod +x "$DST" + info "Installed $script" + else + warn "Script not found: $SRC" + fi +done + +# --- Install skill file --- +echo "" +echo "Installing skill definition..." +SKILL_DIR="${HOME}/.openclaw/workspace/skills/claude-codex-delegation" +run mkdir -p "$SKILL_DIR/references" +if [[ -f "$SCRIPT_DIR/SKILL.md" ]]; then + run cp "$SCRIPT_DIR/SKILL.md" "$SKILL_DIR/SKILL.md" + info "Installed SKILL.md" +fi +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 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++)) +fi + +if command -v codex &> /dev/null || $DRY_RUN; then + info "codex CLI: OK" +else + error "codex CLI: not found" + ((ERRORS++)) +fi + +if [[ -x "$SCRIPTS_DIR/delegate.sh" ]] || $DRY_RUN; then + info "delegate.sh: OK" +else + error "delegate.sh: not found or not executable" + ((ERRORS++)) +fi + +if [[ -x "$SCRIPTS_DIR/tmux-session.sh" ]] || $DRY_RUN; then + info "tmux-session.sh: OK" +else + error "tmux-session.sh: not found or not executable" + ((ERRORS++)) +fi + +# --- Optional dependencies --- +echo "" +if command -v tmux &> /dev/null; then + info "tmux: available (for long-running sessions)" +else + warn "tmux: not installed (optional — needed for tmux-session.sh)" +fi + +if command -v timeout &> /dev/null; then + info "timeout: available" +else + warn "timeout: not found (needed for delegate.sh)" +fi + +# --- Auth check --- +echo "" +echo "Checking auth..." +echo " Claude Code auth: run 'claude auth' to verify subscription/OAuth is configured" +echo " Codex auth: 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 Claude Code and Codex must be configured with" +echo " subscription/OAuth auth — API key auth will NOT work." + +# --- Summary --- +echo "" +if [[ $ERRORS -eq 0 ]]; then + echo "============================================" + info "Installation complete." + echo "" + echo " Delegate a task:" + echo " $SCRIPTS_DIR/delegate.sh --prompt 'Your task' --workdir ~/project" + echo "" + echo " Long-running task in tmux:" + echo " $SCRIPTS_DIR/tmux-session.sh --name my-task --prompt 'Your task' --workdir ~/project" + echo "" + echo " See SKILL.md for full usage and security documentation." + echo "============================================" +else + error "$ERRORS verification error(s). Check the output above." + exit 1 +fi From 6fbffd98fa2320282920ea2283be22732b65a49f Mon Sep 17 00:00:00 2001 From: Robert Koller Date: Mon, 16 Mar 2026 05:18:40 +0000 Subject: [PATCH 5/6] fix: install skill as self-contained bundle, harden installer - Install entire skill dir (SKILL.md + scripts/ + references/) into ~/.openclaw/skills/ (shared skill location per OpenClaw docs) - Scripts live inside the skill folder, not a separate global dir - Node.js 22 check is now a hard error, not a warning - timeout check is a hard error (required for delegate.sh) - Add --force flag to reinstall npm packages on rerun - Pin npm packages to @latest explicitly - Drop darwin from os metadata (requires GNU coreutils) - Fix set -e killing exit code capture in delegate.sh --- skills/claude-codex-delegation/SKILL.md | 2 +- skills/claude-codex-delegation/install.sh | 166 +++++++++++------- .../scripts/delegate.sh | 8 +- 3 files changed, 103 insertions(+), 73 deletions(-) diff --git a/skills/claude-codex-delegation/SKILL.md b/skills/claude-codex-delegation/SKILL.md index f64f77f6b53..322b9a7dcd2 100644 --- a/skills/claude-codex-delegation/SKILL.md +++ b/skills/claude-codex-delegation/SKILL.md @@ -2,7 +2,7 @@ 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"] } } } + { "openclaw": { "os": ["linux"], "requires": { "anyBins": ["claude", "codex"], "bins": ["bash", "timeout"] } } } --- # Claude Code + Codex Delegation diff --git a/skills/claude-codex-delegation/install.sh b/skills/claude-codex-delegation/install.sh index 915dbbe0950..cf9976f0d8e 100755 --- a/skills/claude-codex-delegation/install.sh +++ b/skills/claude-codex-delegation/install.sh @@ -1,33 +1,38 @@ #!/usr/bin/env bash -# install.sh — Install Claude Code + Codex delegation bundle for OpenClaw. +# install.sh - Install Claude Code + Codex delegation bundle for OpenClaw. # -# Installs the Claude Code and Codex CLIs, copies delegation scripts -# into your OpenClaw workspace, and verifies the setup. +# 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) -# --scripts-dir DIR Where to install scripts (default: ~/.openclaw/scripts) +# --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)" -SCRIPTS_DIR="${HOME}/.openclaw/scripts" +SKILL_DIR="${HOME}/.openclaw/skills/claude-codex-delegation" SKIP_NPM=false +FORCE=false DRY_RUN=false -# --- Versions (pin to known-good releases) --- -CLAUDE_CODE_PKG="@anthropic-ai/claude-code" -CODEX_PKG="@openai/codex" +# --- Pinned versions --- +CLAUDE_CODE_PKG="@anthropic-ai/claude-code@latest" +CODEX_PKG="@openai/codex@latest" # --- Colors --- RED='\033[0;31m' @@ -42,16 +47,14 @@ error() { echo -e "${RED}[ERROR]${NC} $*" >&2; } # --- Parse arguments --- while [[ $# -gt 0 ]]; do case "$1" in - --skip-npm|--scripts-dir|--dry-run) - if [[ "$1" == "--scripts-dir" ]]; then - if [[ $# -lt 2 ]]; then - echo "Error: $1 requires a value" >&2 - exit 1 - fi + --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 ;; - --scripts-dir) SCRIPTS_DIR="$2"; shift 2 ;; + --force) FORCE=true; shift ;; --dry-run) DRY_RUN=true; shift ;; -h|--help) sed -n '2,/^$/p' "$0" | sed 's/^# \?//' @@ -70,7 +73,7 @@ run() { } echo "" -echo "Claude Code + Codex Delegation — Installer" +echo "Claude Code + Codex Delegation - Installer" echo "============================================" echo "" @@ -83,8 +86,11 @@ fi NODE_MAJOR="$(node -v | sed 's/^v//' | cut -d. -f1)" if [[ "$NODE_MAJOR" -lt 22 ]]; then - warn "Node.js $(node -v) detected. Version 22+ is recommended." + 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." @@ -98,12 +104,38 @@ if ! command -v openclaw &> /dev/null; then fi info "OpenClaw detected ($(openclaw --version 2>/dev/null || echo 'unknown version'))" -# --- Install Claude Code --- +# --- 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++)) +else + info "timeout: 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 dependency. Install it 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; then - info "Claude Code already installed ($(claude --version 2>/dev/null || echo 'unknown version'))" + 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 @@ -113,8 +145,8 @@ if ! $SKIP_NPM; then echo "" echo "Installing OpenAI Codex CLI..." - if command -v codex &> /dev/null; then - info "Codex CLI already installed ($(codex --version 2>/dev/null || echo 'unknown version'))" + 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 @@ -123,35 +155,38 @@ if ! $SKIP_NPM; then fi fi -# --- Install delegation scripts --- +# --- 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 delegation scripts to $SCRIPTS_DIR..." -run mkdir -p "$SCRIPTS_DIR" - -for script in delegate.sh tmux-session.sh; do - SRC="$SCRIPT_DIR/scripts/$script" - DST="$SCRIPTS_DIR/$script" - if [[ -f "$SRC" ]]; then - run cp "$SRC" "$DST" - run chmod +x "$DST" - info "Installed $script" - else - warn "Script not found: $SRC" - fi -done - -# --- Install skill file --- -echo "" -echo "Installing skill definition..." -SKILL_DIR="${HOME}/.openclaw/workspace/skills/claude-codex-delegation" +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 delegation-policy.md" + info "Installed references/delegation-policy.md" fi # --- Verify --- @@ -173,43 +208,36 @@ else ((ERRORS++)) fi -if [[ -x "$SCRIPTS_DIR/delegate.sh" ]] || $DRY_RUN; then - info "delegate.sh: OK" +if [[ -f "$SKILL_DIR/SKILL.md" ]] || $DRY_RUN; then + info "SKILL.md: OK" else - error "delegate.sh: not found or not executable" + error "SKILL.md: not found at $SKILL_DIR" ((ERRORS++)) fi -if [[ -x "$SCRIPTS_DIR/tmux-session.sh" ]] || $DRY_RUN; then - info "tmux-session.sh: OK" +if [[ -x "$SKILL_DIR/scripts/delegate.sh" ]] || $DRY_RUN; then + info "scripts/delegate.sh: OK" else - error "tmux-session.sh: not found or not executable" + error "scripts/delegate.sh: not found or not executable" ((ERRORS++)) fi -# --- Optional dependencies --- -echo "" -if command -v tmux &> /dev/null; then - info "tmux: available (for long-running sessions)" +if [[ -x "$SKILL_DIR/scripts/tmux-session.sh" ]] || $DRY_RUN; then + info "scripts/tmux-session.sh: OK" else - warn "tmux: not installed (optional — needed for tmux-session.sh)" -fi - -if command -v timeout &> /dev/null; then - info "timeout: available" -else - warn "timeout: not found (needed for delegate.sh)" + error "scripts/tmux-session.sh: not found or not executable" + ((ERRORS++)) fi # --- Auth check --- echo "" echo "Checking auth..." -echo " Claude Code auth: run 'claude auth' to verify subscription/OAuth is configured" -echo " Codex auth: run 'codex auth' to verify OpenAI subscription is configured" +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 Claude Code and Codex must be configured with" -echo " subscription/OAuth auth — API key auth will NOT work." +echo " environment. Both CLIs must be configured with subscription/OAuth" +echo " auth. API key auth will NOT work." # --- Summary --- echo "" @@ -217,13 +245,15 @@ if [[ $ERRORS -eq 0 ]]; then echo "============================================" info "Installation complete." echo "" + echo " Skill installed to: $SKILL_DIR" + echo "" echo " Delegate a task:" - echo " $SCRIPTS_DIR/delegate.sh --prompt 'Your task' --workdir ~/project" + echo " $SKILL_DIR/scripts/delegate.sh --prompt 'Your task' --workdir ~/project" echo "" echo " Long-running task in tmux:" - echo " $SCRIPTS_DIR/tmux-session.sh --name my-task --prompt 'Your task' --workdir ~/project" + echo " $SKILL_DIR/scripts/tmux-session.sh --name my-task --prompt 'Your task' --workdir ~/project" echo "" - echo " See SKILL.md for full usage and security documentation." + echo " OpenClaw will discover the skill automatically on next session." echo "============================================" else error "$ERRORS verification error(s). Check the output above." diff --git a/skills/claude-codex-delegation/scripts/delegate.sh b/skills/claude-codex-delegation/scripts/delegate.sh index 6403de9ae4e..4fddf524ee6 100755 --- a/skills/claude-codex-delegation/scripts/delegate.sh +++ b/skills/claude-codex-delegation/scripts/delegate.sh @@ -185,11 +185,11 @@ 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=$? + run_delegation" || EXIT_CODE=$? echo "" echo "--- Delegation complete (exit code: $EXIT_CODE) ---" ) >> "$LOG_FILE" 2>&1 & @@ -197,11 +197,11 @@ if $BACKGROUND; then 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=$? + run_delegation" || EXIT_CODE=$? if [[ $EXIT_CODE -eq 0 ]]; then echo "Delegation complete." elif [[ $EXIT_CODE -eq 124 ]]; then From 5b8332e88d6199eda9aa88e5ff09e656aa72f943 Mon Sep 17 00:00:00 2001 From: Robert Koller Date: Mon, 16 Mar 2026 05:26:05 +0000 Subject: [PATCH 6/6] fix: propagate codex exit code, safe arithmetic, preserve cwd - Add script -e flag so codex exit code propagates through PTY wrapper - Use subshell for git repo check to avoid changing caller's cwd (fixes broken relative --workdir paths) - Replace ((ERRORS++)) with ERRORS=$((ERRORS + 1)) to avoid set -e terminating on first verification failure - Add script binary to installer dependency check --- skills/claude-codex-delegation/install.sh | 24 +++++++++++++------ .../scripts/delegate.sh | 6 ++--- .../scripts/tmux-session.sh | 3 +-- 3 files changed, 21 insertions(+), 12 deletions(-) diff --git a/skills/claude-codex-delegation/install.sh b/skills/claude-codex-delegation/install.sh index cf9976f0d8e..c0837a13e93 100755 --- a/skills/claude-codex-delegation/install.sh +++ b/skills/claude-codex-delegation/install.sh @@ -114,11 +114,21 @@ if ! command -v timeout &> /dev/null; then if [[ "$(uname)" == "Darwin" ]]; then echo " Install with: brew install coreutils" fi - ((MISSING++)) + 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 @@ -126,7 +136,7 @@ else fi if [[ $MISSING -gt 0 ]]; then - error "Missing $MISSING required system dependency. Install it and rerun." + error "Missing $MISSING required system dependencies. Install them and rerun." exit 1 fi @@ -198,35 +208,35 @@ if command -v claude &> /dev/null || $DRY_RUN; then info "claude CLI: OK" else error "claude CLI: not found" - ((ERRORS++)) + 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=$((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=$((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=$((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=$((ERRORS + 1)) fi # --- Auth check --- diff --git a/skills/claude-codex-delegation/scripts/delegate.sh b/skills/claude-codex-delegation/scripts/delegate.sh index 4fddf524ee6..38c49cf2304 100755 --- a/skills/claude-codex-delegation/scripts/delegate.sh +++ b/skills/claude-codex-delegation/scripts/delegate.sh @@ -118,8 +118,7 @@ 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 + 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 @@ -167,9 +166,10 @@ run_delegation() { ;; 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 -q -c "bash -c $(printf '%q' "$(build_codex_cmd) $(printf '%q' "$PROMPT")")" "$LOG_FILE" || exit_code=$? + script -e -q -c "bash -c $(printf '%q' "$(build_codex_cmd) $(printf '%q' "$PROMPT")")" "$LOG_FILE" || exit_code=$? ;; esac diff --git a/skills/claude-codex-delegation/scripts/tmux-session.sh b/skills/claude-codex-delegation/scripts/tmux-session.sh index a8701b0b647..2fe80dee542 100755 --- a/skills/claude-codex-delegation/scripts/tmux-session.sh +++ b/skills/claude-codex-delegation/scripts/tmux-session.sh @@ -128,8 +128,7 @@ case "$ACTION" in # Codex requires a git repo if [[ "$AGENT" == "codex" ]]; then - cd "$WORKDIR" - if [[ ! -d .git ]] && ! git rev-parse --git-dir > /dev/null 2>&1; 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