Robert Koller f980fa72bf 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.
2026-03-16 04:56:31 +00:00

201 lines
6.2 KiB
Bash
Executable File

#!/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