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:
parent
986b772a89
commit
f980fa72bf
166
skills/claude-codex-delegation/SKILL.md
Normal file
166
skills/claude-codex-delegation/SKILL.md
Normal 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
|
||||
@ -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
|
||||
200
skills/claude-codex-delegation/scripts/delegate.sh
Executable file
200
skills/claude-codex-delegation/scripts/delegate.sh
Executable 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
|
||||
200
skills/claude-codex-delegation/scripts/tmux-session.sh
Executable file
200
skills/claude-codex-delegation/scripts/tmux-session.sh
Executable 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
|
||||
Loading…
x
Reference in New Issue
Block a user