feat: agent loop harness with Claude Code plugin support
Generator-evaluator architecture with iterative context-reset for long-running coding tasks. Ships as a Claude Code plugin — install with /plugin and use /agent-loop:init, /agent-loop:plan, /agent-loop:run.
This commit is contained in:
203
skills/loop-run/SKILL.md
Normal file
203
skills/loop-run/SKILL.md
Normal file
@@ -0,0 +1,203 @@
|
||||
---
|
||||
name: run
|
||||
description: Execute the generator-evaluator loop interactively inside Claude Code. Dispatches subagents with full visibility and intervention capability. Run /agent-loop:init and /agent-loop:plan first.
|
||||
---
|
||||
|
||||
# /run — Execute Agent Loop Inside Claude Code
|
||||
|
||||
Run the generator-evaluator loop natively in Claude Code using subagents. Unlike `loop.sh` (headless), this gives you full visibility into each agent's work and the ability to intervene at any point.
|
||||
|
||||
## Usage
|
||||
|
||||
```
|
||||
/agent-loop:run # Run until all stories pass or max iterations
|
||||
/agent-loop:run 3 # Run at most 3 iterations
|
||||
/agent-loop:run --skip-eval # Skip evaluator (generator marks stories done)
|
||||
/agent-loop:run --story US-003 # Run only a specific story
|
||||
```
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- `.loop/config.json` exists (run `/agent-loop:init` first)
|
||||
- `.loop/prd.json` exists with stories (run `/agent-loop:plan` first)
|
||||
|
||||
## Instructions
|
||||
|
||||
When the user invokes `/loop-run`, follow this orchestration sequence exactly.
|
||||
|
||||
### Step 0: Parse Arguments
|
||||
|
||||
- If a number is provided, use it as max iterations. Otherwise read `maxIterations` from `.loop/config.json`.
|
||||
- If `--skip-eval` is provided, skip the evaluator pass.
|
||||
- If `--story <ID>` is provided, only work on that specific story.
|
||||
|
||||
### Step 1: Load State
|
||||
|
||||
1. Read `.loop/config.json` — get `mode`, `maxIterations`, `evalRetries`, `scopeBudgets`
|
||||
2. Read `.loop/prd.json` — get the story list and their statuses
|
||||
3. Check `.loop/progress.md` exists; if not, create it from `.loop/templates/progress.md.template`
|
||||
|
||||
Report to the user:
|
||||
|
||||
> **Loop Ready**
|
||||
> - Mode: {mode}
|
||||
> - Stories: {passed}/{total} complete
|
||||
> - Max iterations: {N}
|
||||
> - Eval: {on/off}
|
||||
>
|
||||
> Starting loop. You can interrupt me at any time to adjust course.
|
||||
|
||||
### Step 2: Iteration Loop
|
||||
|
||||
For each iteration (1 to max iterations):
|
||||
|
||||
#### 2a. Find Next Story
|
||||
|
||||
Find the highest-priority story in `prd.json` where `passes` is `false` and `blocked` is not `true`. If `--story` was specified, use that story instead.
|
||||
|
||||
**If no actionable story remains:**
|
||||
- If all stories have `passes: true` → report success and stop
|
||||
- If some stories are `blocked: true` → report which are blocked and suggest `/agent-loop:triage`
|
||||
- Stop the loop
|
||||
|
||||
#### 2b. Report Iteration Start
|
||||
|
||||
Tell the user:
|
||||
> **Iteration {N}/{max} — {story.id}: {story.title}**
|
||||
|
||||
If the story has `[REJECTED]` entries in its `notes` field, summarize the previous feedback so the user has context.
|
||||
|
||||
#### 2c. Assemble Generator Prompt
|
||||
|
||||
Read these files and concatenate them with `---` separators:
|
||||
1. `.loop/prompts/generator/_base.md`
|
||||
2. `.loop/prompts/generator/{mode}.md`
|
||||
|
||||
Then substitute these template variables in the assembled text:
|
||||
- `{{MAX_FILES_TO_READ}}` → from `config.scopeBudgets.{mode}.maxFilesToRead`
|
||||
- `{{MAX_LINES_TO_WRITE}}` → from `config.scopeBudgets.{mode}.maxLinesToWrite`
|
||||
- `{{MAX_FILES_TO_MODIFY}}` → from `config.scopeBudgets.{mode}.maxFilesToModify`
|
||||
- `{{MODE}}` → the mode
|
||||
- `{{ITERATION}}` → current iteration number
|
||||
- `{{MAX_ITERATIONS}}` → max iterations
|
||||
- `{{LOOP_DIR}}` → path to `.loop/` directory
|
||||
- `{{PROJECT_ROOT}}` → project root path
|
||||
- `{{CURRENT_STORY_ID}}` → the story ID being worked on
|
||||
|
||||
#### 2d. Capture Pre-Generator Git State
|
||||
|
||||
Run `git rev-parse HEAD` and save it. This is needed for the evaluator's diff.
|
||||
|
||||
#### 2e. Dispatch Generator Agent
|
||||
|
||||
Use the **Agent tool** to launch the generator:
|
||||
|
||||
```
|
||||
Agent(
|
||||
prompt: <assembled generator prompt>,
|
||||
description: "Generator: {story.id}",
|
||||
subagent_type: "general-purpose",
|
||||
mode: "auto"
|
||||
)
|
||||
```
|
||||
|
||||
**IMPORTANT:** Use `mode: "auto"` so the user can see tool calls but isn't prompted for every action. If the user has expressed a preference for more control, use `mode: "default"` instead.
|
||||
|
||||
Wait for the agent to complete. The Agent tool returns the generator's final output.
|
||||
|
||||
#### 2f. Check for Completion Signal
|
||||
|
||||
If the generator output contains `<promise>COMPLETE</promise>`, report all stories complete and stop.
|
||||
|
||||
#### 2g. Skip Evaluator (if configured)
|
||||
|
||||
If `--skip-eval` was specified or `config.skipEval` is true, skip to step 2j.
|
||||
|
||||
#### 2h. Assemble Evaluator Prompt
|
||||
|
||||
Read these files and concatenate them:
|
||||
1. `.loop/prompts/evaluator/_base.md`
|
||||
2. `.loop/prompts/evaluator/{mode}.md`
|
||||
|
||||
Substitute the same template variables as the generator, plus:
|
||||
- `{{PRE_GENERATOR_SHA}}` → the git SHA captured in step 2d
|
||||
- `{{CURRENT_STORY_ID}}` → the story ID
|
||||
|
||||
#### 2i. Dispatch Evaluator Agent
|
||||
|
||||
Use the **Agent tool** to launch the evaluator:
|
||||
|
||||
```
|
||||
Agent(
|
||||
prompt: <assembled evaluator prompt>,
|
||||
description: "Evaluator: {story.id}",
|
||||
subagent_type: "general-purpose",
|
||||
mode: "auto"
|
||||
)
|
||||
```
|
||||
|
||||
Wait for completion. Parse the verdict from the output:
|
||||
|
||||
- Look for `<verdict>PASS</verdict>` → story passes
|
||||
- Look for `<verdict>REJECT</verdict>` → story rejected; extract reason from `<rejection_reason>...</rejection_reason>`
|
||||
- No verdict tag found → treat as REJECT (fail-safe)
|
||||
|
||||
#### 2j. Update State Based on Verdict
|
||||
|
||||
**On PASS (or skip-eval):**
|
||||
1. Update `.loop/prd.json` — set `passes: true` for the story
|
||||
2. Report to user: ✓ **{story.id} PASSED**
|
||||
|
||||
**On REJECT:**
|
||||
1. Update `.loop/prd.json`:
|
||||
- Keep `passes: false`
|
||||
- Increment `rejections` count
|
||||
- Append `[REJECTED] {reason}` to `notes`
|
||||
2. Report to user: ✗ **{story.id} REJECTED** — {reason}
|
||||
3. Check if `rejections` >= `evalRetries` from config:
|
||||
- If yes: set `blocked: true` in prd.json, append `[BLOCKED]` to notes
|
||||
- Report: ⚠ **{story.id} BLOCKED** — rejected {N} times, needs human review
|
||||
|
||||
#### 2k. Append Progress Entry
|
||||
|
||||
Append to `.loop/progress.md`:
|
||||
|
||||
```markdown
|
||||
### {story.id} — {story.title}
|
||||
Date: {current date and time}
|
||||
Iteration: {N}
|
||||
Verdict: {PASS/REJECT/SKIP-EVAL}
|
||||
|
||||
---
|
||||
```
|
||||
|
||||
#### 2l. Report Iteration Summary
|
||||
|
||||
Show current story counts: `{passed}/{total} stories complete`
|
||||
|
||||
If there are more iterations and more stories, continue to the next iteration.
|
||||
|
||||
### Step 3: Loop Exit
|
||||
|
||||
When the loop ends (all stories done, max iterations, or all remaining blocked), report:
|
||||
|
||||
> **Loop Complete**
|
||||
> - Iterations used: {N}
|
||||
> - Stories: {passed}/{total} complete, {blocked} blocked
|
||||
> - {Suggest `/agent-loop:triage` if anything is blocked or incomplete}
|
||||
|
||||
### Error Handling
|
||||
|
||||
- If an Agent subagent fails or returns empty output, log a warning and continue to the next iteration. Do NOT stop the loop for a single agent failure.
|
||||
- If `prd.json` cannot be parsed, stop immediately and report the error.
|
||||
- If the user interrupts (denies a tool call, says "stop", etc.), gracefully end the loop and report current status.
|
||||
|
||||
### Key Differences from loop.sh
|
||||
|
||||
| Feature | loop.sh | /loop-run |
|
||||
|---------|---------|-----------|
|
||||
| Execution | Headless (`claude --print`) | Visible in Claude Code |
|
||||
| Intervention | Kill the process | Deny tool calls, chat mid-loop |
|
||||
| Permissions | `--dangerously-skip-permissions` | User-controlled |
|
||||
| Context | Fresh process per agent | Fresh Agent subagent per agent |
|
||||
| State updates | Shell functions | Claude Code reads/writes files directly |
|
||||
Reference in New Issue
Block a user