feat: add distributable skills (paseo, handoff, committee, loop)

Package the four core skills into the repo under skills/ so users can
install them via `npx skills add getpaseo/paseo`.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Mohamed Boudra
2026-03-07 21:34:39 +07:00
co-authored by Claude Opus 4.6
parent 356faa0563
commit 484edb1e1e
5 changed files with 1003 additions and 0 deletions
+221
View File
@@ -0,0 +1,221 @@
---
name: loop
description: Run a task in a worker→judge loop until acceptance criteria are met. Use when the user says "loop", "loop this", "keep trying until", or wants iterative autonomous execution.
user-invocable: true
---
# Loop Skill
You are setting up an autonomous worker→judge loop. A worker agent implements the plan, then a judge agent independently verifies the plan was implemented to the letter.
**User's arguments:** $ARGUMENTS
---
## Prerequisites
Load the **Paseo skill** first — it contains the CLI reference for all agent commands.
## What Is a Loop
A loop is two roles — **worker** and **judge** — running in alternation:
1. **Worker** receives the plan and implements it (fresh agent each iteration)
2. **Judge** receives the same plan and independently verifies it was implemented correctly, emitting a structured verdict: `{ criteria_met: boolean, reason: string }`
3. If `criteria_met` is false, a new worker is launched with the latest judge failure reason (latest only, not full history)
4. Loop exits when the judge says `criteria_met: true` or max iterations are reached
The judge verifies the **entire plan** — every step, every file change, every acceptance criterion. It does NOT suggest fixes or provide guidance. It only reports facts.
## Live Steering
Each loop run persists state in:
```text
~/.paseo/loops/<loop-id>/
plan.md
last_reason.md
history.log
```
Behavior:
- `plan.md` is read immediately before launching each worker and each judge.
- `last_reason.md` stores only the latest judge failure reason (not accumulated history).
- `history.log` appends per-iteration operational records.
Edits to `plan.md` are picked up on the next worker/judge launch without restarting the loop.
## Parsing Arguments
Parse `$ARGUMENTS` to determine:
1. **Plan** — the full implementation plan (context, steps, acceptance criteria, constraints — all in one document)
2. **Worker model** — who does the work (default: Codex `gpt-5.4`)
3. **Judge model** — who verifies (default: Claude `sonnet`)
4. **Name** — a short name for tracking iterations
5. **Max iterations** — safety cap (default: 10)
6. **Worktree** — whether to run in an isolated git worktree
### Examples
```
/loop
→ Agent derives plan from conversation context
/loop the provider list bug
→ Plan: full context about the bug, steps to fix, acceptance criteria
Name: provider-list-fix
/loop until tests pass
→ Plan: make the failing tests pass, acceptance criteria: all tests pass
Name: fix-tests
/loop in a worktree
→ Same as above but all agents run in a shared worktree
```
## Using the Script
The loop is implemented as a bash script at `~/.agents/skills/loop/bin/loop.sh`.
```bash
~/.agents/skills/loop/bin/loop.sh \
--plan "The full implementation plan" \
--name "short-name" \
--max-iterations 10 \
--worker-provider codex \
--worker-model gpt-5.4 \
--judge-provider claude \
--judge-model sonnet
```
### Arguments
| Flag | Required | Default | Description |
|---|---|---|---|
| `--plan` | Yes* | — | Full implementation plan (given to both worker and judge) |
| `--plan-file` | Yes* | — | Read the plan from a file |
| `--name` | Yes | — | Name prefix for agents (`name-work-N`, `name-verify-N`) |
| `--max-iterations` | No | 10 | Safety cap on iterations |
| `--worktree` | No | — | Worktree name. Created on first use, reused for all subsequent agents. |
| `--worker-provider` | No | `codex` | `codex` or `claude` |
| `--worker-model` | No | provider default | Model for worker |
| `--judge-provider` | No | `claude` | `codex` or `claude` |
| `--judge-model` | No | provider default | Model for judge |
| `--thinking` | No | `medium` | Thinking level for worker |
\* Provide exactly one of `--plan` or `--plan-file`.
### How the Plan Is Used
The same plan is given to both the worker and the judge, wrapped in XML tags:
**Worker receives:**
```
Implement the following plan exactly as specified.
<plan>
[your plan here]
</plan>
```
If a previous iteration failed, the worker also receives:
```
<previous-verification-failure>
[judge's failure reason from last iteration]
</previous-verification-failure>
```
**Judge receives:**
```
Verify that every step of the plan has been implemented to the letter.
<plan>
[your plan here]
</plan>
```
### Agent Naming
Agents are named `{name}-work-{N}` and `{name}-verify-{N}` so the caller can track iterations:
```
fix-bug-work-1 # First worker attempt
fix-bug-verify-1 # First judge check
fix-bug-work-2 # Second worker attempt (with failure context)
fix-bug-verify-2 # Second judge check
```
### Worktree Support
When `--worktree` is passed, all workers and judges run in the same git worktree. The worktree is created on the first agent launch and reused for all subsequent agents (the `--worktree` flag on `paseo run` is idempotent — if the worktree exists, the agent runs in it without creating a new one).
No `--base` is needed in the loop script — it automatically uses the current branch as the base.
```bash
~/.agents/skills/loop/bin/loop.sh \
--plan-file /tmp/my-plan.md \
--name "feature-x" \
--worktree "feature-x"
```
## Your Job
1. **Understand the task** from the conversation context and `$ARGUMENTS`
2. **Write the plan** — comprehensive, self-contained (the worker has zero context). The plan should include:
- Task description and context
- Relevant files and what they do
- Implementation steps (ordered, concrete)
- Acceptance criteria (factual, verifiable)
- Constraints (what NOT to do)
3. **Choose models** — default: Codex worker + Claude sonnet judge
4. **Choose a name** — short, descriptive
5. **Run the script** — call `loop.sh` with all the arguments
### Steering Guidance
- Prefer tightening or clarifying the plan when important requirements are missing.
- Rework acceptance criteria when they are causing dead loops or are not independently verifiable.
- Avoid weakening user-defined criteria unless the user explicitly asks for that change.
- Keep the plan self-contained so the loop can succeed without relying on iterative reasoning context.
### Writing a Good Plan
The plan is the single source of truth for both the worker and the judge. It must be:
1. **Self-contained** — The worker starts with zero context. Everything needed is in the plan.
2. **Specific** — Name files, functions, types. "Update the handler" is useless. "In `src/handlers/auth.ts`, modify the `handleLogin` function to..." is useful.
3. **Ordered** — Steps should be in implementation order.
4. **Verifiable** — Every step and criterion must be independently checkable by the judge.
**Good plan elements:**
- "File `src/utils.ts` exports a function named `formatDate`"
- "Running `npm test` exits with code 0"
- "The component at `src/ProviderList.tsx` renders a list of providers from the API response"
- "Typecheck passes (`npm run typecheck`)"
- "`npm run typecheck` passes"
**Bad plan elements:**
- "The code is clean" (subjective)
- "The implementation is correct" (vague)
- "It works" (unverifiable)
### Skill Stacking
You can instruct the worker to use other skills in the plan:
```bash
~/.agents/skills/loop/bin/loop.sh \
--plan "Use the /committee skill to plan and then fix the provider list bug. The bug is..." \
--name "provider-fix"
```
### Composing with Handoff
A handoff can launch a loop in a worktree:
```
/handoff a loop in a worktree
```
The handoff skill writes a prompt telling the new agent to use `/loop`, and the loop runs inside the worktree.
+255
View File
@@ -0,0 +1,255 @@
#!/usr/bin/env bash
set -euo pipefail
usage() {
cat <<'EOF'
Usage: loop.sh (--plan PROMPT | --plan-file PATH) --name NAME [options]
Required:
--plan PROMPT The implementation plan (given to both worker and judge)
--plan-file PATH Read the plan from a file
--name NAME Name prefix for agents (e.g. "fix-bug" → "fix-bug-work-1", "fix-bug-verify-1")
Options:
--max-iterations N Maximum loop iterations (default: 10)
--worktree NAME Run all agents in this worktree (created on first use, reused after)
--worker-provider P Worker provider: claude or codex (default: codex)
--worker-model M Worker model (default: provider's default)
--judge-provider P Judge provider: claude or codex (default: claude)
--judge-model M Judge model (default: provider's default)
--thinking LEVEL Thinking level for worker (default: medium)
EOF
exit 1
}
# Defaults
max_iterations=10
worker_provider="codex"
worker_model=""
judge_provider="claude"
judge_model="sonnet"
plan=""
plan_file_input=""
name=""
thinking="medium"
worktree=""
state_root="${HOME}/.paseo/loops"
while [[ $# -gt 0 ]]; do
case "$1" in
--plan) plan="$2"; shift 2 ;;
--plan-file) plan_file_input="$2"; shift 2 ;;
--name) name="$2"; shift 2 ;;
--max-iterations) max_iterations="$2"; shift 2 ;;
--worktree) worktree="$2"; shift 2 ;;
--worker-provider) worker_provider="$2"; shift 2 ;;
--worker-model) worker_model="$2"; shift 2 ;;
--judge-provider) judge_provider="$2"; shift 2 ;;
--judge-model) judge_model="$2"; shift 2 ;;
--thinking) thinking="$2"; shift 2 ;;
--help|-h) usage ;;
*) echo "Unknown option: $1"; usage ;;
esac
done
load_prompt() {
local inline_value="$1"
local file_value="$2"
local label="$3"
if [[ -n "$inline_value" && -n "$file_value" ]]; then
echo "Error: use either --${label} or --${label}-file, not both"
usage
fi
if [[ -n "$file_value" ]]; then
[[ -f "$file_value" ]] || { echo "Error: --${label}-file not found: $file_value"; exit 1; }
local file_content
file_content="$(cat "$file_value")"
[[ -n "$file_content" ]] || { echo "Error: --${label}-file is empty: $file_value"; exit 1; }
printf '%s' "$file_content"
return 0
fi
if [[ -n "$inline_value" ]]; then
printf '%s' "$inline_value"
return 0
fi
echo "Error: either --${label} or --${label}-file is required"
usage
}
plan="$(load_prompt "$plan" "$plan_file_input" "plan")"
[[ -z "$name" ]] && { echo "Error: --name is required"; usage; }
mkdir -p "$state_root"
generate_loop_id() {
uuidgen | tr '[:upper:]' '[:lower:]' | tr -d '-' | cut -c1-6
}
loop_id="$(generate_loop_id)"
state_dir="${state_root}/${loop_id}"
while [[ -e "$state_dir" ]]; do
loop_id="$(generate_loop_id)"
state_dir="${state_root}/${loop_id}"
done
mkdir -p "$state_dir"
plan_file="${state_dir}/plan.md"
last_reason_file="${state_dir}/last_reason.md"
history_log="${state_dir}/history.log"
printf '%s\n' "$plan" > "$plan_file"
printf '' > "$last_reason_file"
printf '' > "$history_log"
# Build worker flags
worker_flags=()
if [[ "$worker_provider" == "codex" ]]; then
worker_flags+=(--mode full-access --provider codex)
elif [[ "$worker_provider" == "claude" ]]; then
worker_flags+=(--mode bypassPermissions)
fi
[[ -n "$worker_model" ]] && worker_flags+=(--model "$worker_model")
[[ -n "$thinking" ]] && worker_flags+=(--thinking "$thinking")
# Build judge flags
judge_flags=()
if [[ "$judge_provider" == "codex" ]]; then
judge_flags+=(--mode full-access --provider codex)
elif [[ "$judge_provider" == "claude" ]]; then
judge_flags+=(--mode bypassPermissions)
fi
[[ -n "$judge_model" ]] && judge_flags+=(--model "$judge_model")
# Worktree flags — passed to every paseo run call
worktree_flags=()
if [[ -n "$worktree" ]]; then
base_branch="$(git branch --show-current 2>/dev/null || echo "main")"
worktree_flags+=(--worktree "$worktree" --base "$base_branch")
fi
# Judge output schema
judge_schema='{"type":"object","properties":{"criteria_met":{"type":"boolean"},"reason":{"type":"string"}},"required":["criteria_met","reason"],"additionalProperties":false}'
iteration=0
echo "=== Loop started: $name ==="
echo " Loop ID: $loop_id"
echo " State dir: $state_dir"
echo " Plan file: $plan_file"
echo " Last reason file: $last_reason_file"
echo " History log: $history_log"
echo " Steering: edits to plan.md are picked up on the next worker/judge launch."
echo " Worker: $worker_provider ${worker_model:-(default)}"
echo " Judge: $judge_provider ${judge_model:-(default)}"
if [[ -n "$worktree" ]]; then
echo " Worktree: $worktree (base: $base_branch)"
fi
echo " Max iterations: $max_iterations"
echo ""
while [[ $iteration -lt $max_iterations ]]; do
iteration=$((iteration + 1))
echo "--- Iteration $iteration/$max_iterations ---"
if [[ ! -s "$plan_file" ]]; then
echo "Error: plan file is missing or empty: $plan_file"
exit 1
fi
current_plan="$(cat "$plan_file")"
last_reason="$(cat "$last_reason_file")"
# Build worker prompt with plan in XML tags
worker_prompt="Implement the following plan exactly as specified.
<plan>
$current_plan
</plan>"
if [[ -n "$last_reason" ]]; then
worker_prompt="$worker_prompt
<previous-verification-failure>
The previous attempt was verified and did NOT pass. Address the following issues before anything else:
$last_reason
</previous-verification-failure>"
fi
# Launch worker
worker_name="${name}-work-${iteration}"
echo "Launching worker: $worker_name"
worker_id=$(paseo run -d "${worker_flags[@]}" "${worktree_flags[@]}" --name "$worker_name" "$worker_prompt" -q)
echo "Worker [$worker_name] launched. ID: $worker_id"
echo " Stream logs: paseo logs $worker_id -f"
echo " Inspect: paseo inspect $worker_id"
echo " Wait: paseo wait $worker_id"
# Wait for worker
echo ""
echo "Waiting for worker to complete..."
paseo wait "$worker_id"
echo "Worker done."
# Build judge prompt with plan in XML tags
judge_prompt="You are a fact checker. Your ONLY job is to verify whether the plan below was implemented correctly and completely.
Inspect the codebase independently. Do NOT fix anything. Do NOT suggest fixes. Do NOT provide guidance.
<plan>
$current_plan
</plan>
<instructions>
Verify that every step of the plan has been implemented to the letter. If the plan contains acceptance criteria, check each one individually.
Report ONLY facts:
- criteria_met: true if the ENTIRE plan is implemented correctly, false if ANY part is missing or wrong
- reason: for each item in the plan, state whether it was implemented correctly with evidence (file contents, test output, etc.). Be specific about what passed and what failed.
</instructions>"
# Launch judge (blocking, structured output)
judge_name="${name}-verify-${iteration}"
echo ""
echo "Launching judge: $judge_name (synchronous, blocks until verdict)"
verdict=$(paseo run "${judge_flags[@]}" "${worktree_flags[@]}" --name "$judge_name" --output-schema "$judge_schema" "$judge_prompt")
echo "Verdict: $verdict"
# Parse verdict
criteria_met=$(echo "$verdict" | jq -r '.criteria_met')
reason=$(echo "$verdict" | jq -r '.reason')
printf '[%s] iteration=%s worker_id=%s judge=%s criteria_met=%s reason=%s\n' \
"$(date -u +'%Y-%m-%dT%H:%M:%SZ')" \
"$iteration" \
"$worker_id" \
"$judge_name" \
"$criteria_met" \
"$(echo "$reason" | tr '\n' ' ')" >> "$history_log"
if [[ "$criteria_met" == "true" ]]; then
echo ""
echo "=== Loop complete: criteria met on iteration $iteration ==="
echo "Reason: $reason"
echo ""
echo "Review the final worker's changes:"
echo " paseo logs $worker_id"
echo " paseo inspect $worker_id"
exit 0
fi
echo "Criteria not met: $reason"
echo "Starting next iteration..."
# Keep only the latest judge reason for the next worker iteration.
printf '%s\n' "$reason" > "$last_reason_file"
echo ""
done
echo "=== Loop exhausted: $max_iterations iterations without meeting criteria ==="
exit 1