From trusty-cage
Orchestrates trusty-cage containers for autonomous AI work. Use when the user wants to delegate a task to an isolated Claude Code instance inside a trusty-cage container, or when the user says "spin up a cage", "run this in a cage", "let Claude go wild on this". Do NOT use for manual trusty-cage commands the user wants to run themselves. If running inside a trusty-cage container (TRUSTY_CAGE=1 or user is `trustycage`), follow inner-agent protocol instead.
How this skill is triggered — by the user, by Claude, or both
Slash command
/trusty-cage:cage-orchestratorThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
Spin up an isolated trusty-cage container, launch an inner Claude Code agent to work autonomously, monitor for completion, and overlay results back onto the host repo.
Spin up an isolated trusty-cage container, launch an inner Claude Code agent to work autonomously, monitor for completion, and overlay results back onto the host repo.
Check if running inside a cage:
echo $TRUSTY_CAGE — if value is 1, load inner-agent-protocol.md and follow it instead of this workflowwhoami — if output is trustycage, load inner-agent-protocol.md and follow it instead of this workflowSet up a project-local venv with trusty-cage:
venv/bin/tc exists in the project directorypython3 -m venv venv
venv/bin/pip install trusty-cage
tc commands in this workflow must use venv/bin/tc (not a global install)Then check the remaining prerequisites. If any fail, stop and report the error with fix instructions.
| Check | Command | Error Message |
|---|---|---|
| trusty-cage installed | venv/bin/tc --help | "trusty-cage venv setup failed. Check Python and pip." |
| Docker running | docker info >/dev/null 2>&1 | "Docker is not running. Start Docker or OrbStack first." |
| Git repo | git rev-parse --is-inside-work-tree | "Current directory is not a git repository." |
Optionally check for a git remote — this determines how the cage is created (Step 5/6):
REPO_URL=$(git remote get-url origin 2>/dev/null || echo "")
If REPO_URL is empty, the cage will be created from the local directory using --dir. A remote is not required.
Important: Ensure the project has a .gitignore that includes venv/ so the venv is not committed to git and is protected from deletion during tc export (which uses rsync --delete but respects .gitignore patterns).
TASK_DESCRIPTIONPer dotfiles conventions, if currently on main (or master), prompt:
"You're on the main branch. Would you like to create a feature branch for the cage output? This lets you review changes via PR."
If the user agrees, create the branch before proceeding. If they decline, continue on current branch.
Use the REPO_URL value determined in Step 2. If a remote exists and the URL is SSH format ([email protected]:...), convert to HTTPS:
if [ -n "$REPO_URL" ]; then
REPO_URL=$(${CLAUDE_PLUGIN_ROOT}/skills/cage-orchestrator/scripts/tc-url-convert.sh "$REPO_URL")
fi
If REPO_URL is empty, the cage will be created from the local directory in Step 6.
Derive an environment name from the repo directory name, or let the user specify one.
ENV_NAME="${ENV_NAME:-$(basename $(git rev-parse --show-toplevel))}"
Check if the environment already exists:
venv/bin/tc exists "$ENV_NAME"
If exit code is 0 (exists), ask the user: reuse, destroy and recreate, or abort.
Choose auth mode:
ANTHROPIC_API_KEY is set, use --auth-mode api_key (API billing)--auth-mode subscription (Claude Pro/Max — extracts OAuth tokens from macOS Keychain automatically)Create the cage — use URL mode if a remote exists, otherwise use local directory mode:
if [ -n "$REPO_URL" ]; then
venv/bin/tc create "$REPO_URL" --name "$ENV_NAME" --auth-mode <mode> --no-attach
else
venv/bin/tc create --dir "$(git rev-parse --show-toplevel)" --name "$ENV_NAME" --auth-mode <mode> --no-attach
fi
The create command automatically initializes messaging directories at /home/trustycage/.cage/{outbox,inbox,cursor} inside the container and installs cage-send at /usr/local/bin/cage-send.
Pre-flight check — verify Claude can start before sending the real task:
venv/bin/tc launch "$ENV_NAME" --test
If it fails, run venv/bin/tc auth "$ENV_NAME" --login to fix credentials interactively.
Construct the inner prompt from two parts — a task-specific section (which you write) and the Standard Messaging Block (which is always appended automatically):
Part 1 — Task Prompt (customize this per task):
You are an AI coding agent working inside an isolated trusty-cage container.
Your project is at /home/trustycage/project.
TASK:
{TASK_DESCRIPTION}
INSTRUCTIONS:
- Work entirely within /home/trustycage/project
- You have full permissions — install packages, edit any file, run any command
- Use git locally to checkpoint your work (git add, git commit) but you cannot push
- Do not attempt to use cage-orchestrator or any orchestration skills
Part 2 — Standard Messaging Block (appended automatically to every inner prompt — do not modify or omit):
## Messaging
Use the `cage-send` command to communicate with the outer orchestrator.
### Sending messages
cage-send progress_update '{"status":"working on X","detail":"2 of 5 done"}'
cage-send error '{"error_type":"missing_dep","message":"need ffmpeg","recoverable":true}'
cage-send task_complete '{"summary":"What you did","exit_code":0}'
### Message types
- progress_update: Report what you're working on (send periodically)
- error: Report you're stuck (set recoverable to false if you can't continue)
- info_request: Request files from outside: cage-send info_request '{"request_id":"req-001","description":"Need package.json","paths":["package.json"]}'
- task_complete: REQUIRED when done. exit_code 0 for success, 1 for failure.
### Reading responses
After sending info_request, check ~/.cage/inbox/ for responses:
ls ~/.cage/inbox/*.json 2>/dev/null | sort | while read f; do cat "$f"; echo; done
### Important
- You MUST send a task_complete message when your work is done
- Send progress_update messages every few minutes during long tasks
- Do not attempt to read outside ~/.cage/inbox/ — you cannot see the host filesystem
## After Task Completion
When you have completed the task:
1. Send a `task_complete` message via `cage-send`
2. Run the following polling script to wait for revised instructions:
```bash
ELAPSED=0
CURSOR_FILE=~/.cage/cursor/inbox.cursor
CURSOR=$(cat "$CURSOR_FILE" 2>/dev/null || echo "")
while true; do
for f in $(ls -1 ~/.cage/inbox/ 2>/dev/null | sort); do
if [ -z "$CURSOR" ] || [ "$f" \> "$CURSOR" ]; then
cat ~/.cage/inbox/"$f"
echo "$f" > "$CURSOR_FILE"
exit 0
fi
done
if [ $ELAPSED -lt 900 ]; then
INTERVAL=10
elif [ $ELAPSED -lt 3600 ]; then
INTERVAL=30
elif [ $ELAPSED -lt 7200 ]; then
INTERVAL=60
else
break
fi
sleep $INTERVAL
ELAPSED=$((ELAPSED + INTERVAL))
done
echo "POLL_TIMEOUT"
task_revision message. Read the payload.instructions field and continue working on the project accordingly. When done, repeat from step 1.POLL_TIMEOUT, send a going_idle message via cage-send:
cage-send going_idle '{"reason":"No task_revision received within polling timeout","waited_seconds":7200}'
Then stop working.
**Assemble and launch** — combine Part 1 + Part 2 into `INNER_PROMPT`. For short prompts, pass inline:
```bash
venv/bin/tc launch "$ENV_NAME" --prompt "$INNER_PROMPT" --background
For long prompts, write to a temp file first:
echo "$INNER_PROMPT" > /tmp/cage-prompt-$ENV_NAME.txt
venv/bin/tc launch "$ENV_NAME" --prompt-file /tmp/cage-prompt-$ENV_NAME.txt --background
Tell the user: "Inner Claude is working in the cage. I'll check on it periodically."
Monitor via CLI commands:
venv/bin/tc logs "$ENV_NAME" -fvenv/bin/tc outbox "$ENV_NAME" --pollWatch the stream (optional): Show the user they can observe in real-time:
venv/bin/tc logs "$ENV_NAME" -f
Use tc outbox --poll to block until a task_complete message arrives:
venv/bin/tc outbox "$ENV_NAME" --poll --timeout 1800 --interval 30
tc outbox --poll will:
progress_update and error messages as they arrivetask_complete is receivedgoing_idle is received (inner Claude timed out waiting for revisions)If you need more control (e.g., to handle info_request messages), poll manually:
venv/bin/tc outbox "$ENV_NAME"
To respond to an info_request:
# Read the requested file from the host
venv/bin/tc inbox "$ENV_NAME" info_response '{"request_id":"req-001","content":"file contents here","files":[{"path":"package.json","content":"..."}]}'
Fallback process check: If polling times out, verify Claude is still running:
docker exec -u trustycage "isolated-dev-$ENV_NAME" pgrep -f claude
Handle the exit code:
venv/bin/tc logs)9a — Pre-export snapshot:
Record the working tree state before exporting so you can detect unexpected changes:
git status --short
9b — Preview with stats:
Before exporting, show the user what the inner agent changed with language-aware code statistics:
venv/bin/tc diff "$ENV_NAME" --stats
This shows a file change summary (added/modified/deleted) plus a per-language breakdown of lines added, removed, and modified. If cloc is installed on the host, stats are language-aware; otherwise a built-in line counter is used.
9c — Export:
venv/bin/tc export "$ENV_NAME" --yes --stats --output-dir .
This rsyncs container files into the current directory, excluding .git/ so the host repo's git history is preserved. The --stats flag prints a code statistics summary after export.
9d — Post-export validation and restore:
Immediately after export, validate and fix the results:
Show what changed:
git diff --stat
Restore protected files that tc export may have overwritten or deleted:
git checkout -- .gitignore
If .gitignore was modified or deleted, warn the user: "Note: .gitignore was overwritten by tc export and has been restored."
Compare git diff --stat output against the pre-export snapshot. Flag any unexpected file deletions or modifications that fall outside the task scope.
If a test suite exists (e.g., pytest, npm test, make test), offer to run it to verify the exported code works on the host.
Present a summary to the user:
Show the user the detailed diff for review:
git diff
Discuss the results. Let the user test, inspect, or ask questions.
Do NOT auto-commit. Let the user review and decide.
Ask the user:
"Would you like to revise and send inner Claude back to work, or are we done?"
If revise:
venv/bin/tc inbox "$ENV_NAME" task_revision '{"instructions": "<user feedback here>"}'
If done: Proceed to Step 12.
Ask the user:
"Would you like to destroy the cage environment (
tc destroy $ENV_NAME), or keep it for follow-up work?"
If destroy, run:
venv/bin/tc destroy "$ENV_NAME" --yes
Inner Claude goes idle during revision cycle:
If tc outbox --poll exits with code 2 (going_idle), inner Claude's polling timed out. The cage is still alive. Inform the user and offer to re-launch:
venv/bin/tc launch "$ENV_NAME" --prompt "Check your inbox for instructions and continue working on the project"
This starts a fresh Claude session (losing conversational context) but preserves all file state.
Inner Claude crashes:
If tc outbox --poll times out (exit 1) without receiving task_complete, verify inner Claude is alive before sending a task_revision. Check container status or try venv/bin/tc logs "$ENV_NAME". If dead, offer to re-launch.
Multiple rapid revisions:
Not supported — always wait for task_complete before offering another revision cycle.
tc outbox --poll checks every 30 seconds by default (configurable with --interval).TRUSTY_CAGE=1). Load this when the Detection Gate (Step 1) determines you are the inner agent.npx claudepluginhub areese801/trusty-cage-plugin --plugin trusty-cageCreates, edits, and optimizes skills for Claude Code, including drafting, evaluating with test prompts, iterating on performance, and improving skill descriptions for better triggering accuracy.