feat: single entry point /agent-loop:run handles setup, planning, and execution

This commit is contained in:
2026-03-27 09:53:52 -04:00
parent 381741509d
commit 2a78915dcf
3 changed files with 131 additions and 77 deletions

View File

@@ -1,45 +1,113 @@
---
name: run
description: Execute the generator-evaluator loop interactively inside Claude Code. Dispatches subagents with full visibility. Run /agent-loop:init and /agent-loop:stories first.
description: "Agent Loop — single entry point. Scaffolds .loop/ if missing, generates stories if no prd.json, then runs the generator-evaluator loop with full visibility."
---
# /run — Execute Agent Loop Inside Claude Code
# /run — Agent Loop
Run the generator-evaluator loop natively in Claude Code. You see every tool call and can intervene at any point.
Single entry point for the agent loop. Handles setup, planning, and execution automatically based on what's already done.
## Usage
```
/agent-loop:run # Run until all stories pass or max iterations
/agent-loop:run 3 # Run at most 3 iterations
/agent-loop:run # Full flow: setup → stories → loop
/agent-loop:run 3 # Limit to 3 loop iterations
/agent-loop:run --skip-eval # Skip evaluator pass
/agent-loop:run --story US-003 # Run only a specific story
```
## Instructions
Follow this orchestration sequence exactly.
Follow this sequence. Each phase checks what exists and skips if already done.
### Step 0: Validate Prerequisites
---
1. Check `.loop/config.json` exists. If not: tell user to run `/agent-loop:init` and stop.
2. Check `.loop/prd.json` exists. If not: tell user to run `/agent-loop:stories` and stop.
3. **Validate prd.json schema.** Read the file and verify:
## Phase 1: Scaffold (if needed)
Check if `.loop/config.json` exists.
**If it does NOT exist**, run the setup script:
Ask the user: **Mode?** (a) Implement (b) Explore (c) Fix — default is Implement.
Then run:
```bash
bash "$(ls -d ~/.claude/plugins/cache/agent-loop/agent-loop/*/setup.sh 2>/dev/null | head -1)" <mode>
```
Show the output. If setup fails, stop.
**If it already exists**, skip to Phase 2.
---
## Phase 2: Generate Stories (if needed)
Check if `.loop/prd.json` exists.
**If it does NOT exist**, generate it:
1. Search for existing specs:
- `docs/superpowers/specs/*.md`
- `docs/specs/*.md`
- `SPEC.md`, `PRD.md`, `DESIGN.md` at project root
If found: "I found a spec at `{path}`. Using it to generate stories."
If not found: ask "What do you want to build? 1-3 sentences."
2. Read the project root and tech stack info.
3. Dispatch the **agent-loop:planner** agent:
```
Agent(
subagent_type: "agent-loop:planner",
prompt: "Generate prd.json and sprint contracts.\n\nMode: {mode}\nProject root: {path}\n\nSpec:\n{spec content}\n\nTech stack: {detected stack}",
description: "Planning: generate stories"
)
```
4. After the planner finishes, read `.loop/prd.json` and present:
> **Stories generated — Review before running**
>
> 1. US-001: {title}
> 2. US-002: {title}
> ...
>
> **Review:**
> - `.loop/prd.json` — stories and acceptance criteria
> - `.loop/contracts/` — done conditions per story
>
> Let me know if you want changes, or say **go** to start the loop.
5. **STOP and wait for the user.** Do NOT start the loop automatically. The user must say "go", "start", "run", "looks good", or similar before proceeding to Phase 3.
**If `prd.json` already exists**, skip to Phase 3.
---
## Phase 3: Validate
1. Read `.loop/prd.json` and verify:
- Has a `userStories` array (NOT `sprints`, `stories`, or `tasks`)
- Each story has: `id`, `title`, `passes`, `priority`
- If validation fails, show the error and stop. Do NOT attempt to fix it automatically.
4. Check prompts exist. Look for `.loop/prompts/generator/_base.md` in these locations (first match wins):
- `.loop/prompts/` (local project copy)
- `${CLAUDE_PLUGIN_ROOT}/prompts/` (plugin install)
- If invalid, show the error and stop.
2. Read `.loop/config.json` for `mode`, `maxIterations`, `evalRetries`, `scopeBudgets`.
3. Find prompts. Check these paths (first match wins):
- `.loop/prompts/` (local)
- `~/.claude/plugins/cache/agent-loop/agent-loop/*/prompts/` (plugin cache)
Save the resolved prompt base path for later use. If no prompts found, tell user to run `/agent-loop:init` and stop.
If no prompts found, stop with error.
### Step 1: Parse Arguments and Load State
4. Parse arguments: number → max iterations, `--skip-eval`, `--story <ID>`
- Parse arguments: number → max iterations, `--skip-eval`, `--story <ID>`
- Read `.loop/config.json` for defaults
- Read `.loop/prd.json` for story list
---
## Phase 4: Execute Loop
Report:
@@ -51,47 +119,45 @@ Report:
>
> Starting. Interrupt me at any time.
### Step 2: Iteration Loop
For each iteration (1 to max iterations):
#### 2a. Find Next Story
### 4a. Find Next Story
Find the story with the lowest `priority` number where `passes` is `false` and `blocked` is not `true`. If `--story` was specified, use that story.
Find the story with the lowest `priority` where `passes` is `false` and `blocked` is not `true`. If `--story` was specified, use that story.
**If no actionable story remains:**
- All `passes: true` → report success and stop
- Some `blocked: true` → report which and suggest `/agent-loop:triage`
- Stop the loop
- Some `blocked: true` → report which ones
- Stop
#### 2b. Report Iteration Start
### 4b. Report Iteration Start
> **Iteration {N}/{max} — {story.id}: {story.title}**
If the story has `[REJECTED]` in its `notes`, summarize the feedback.
If the story has `[REJECTED]` in `notes`, summarize the feedback.
#### 2c. Assemble Generator Prompt
### 4c. Assemble Generator Prompt
Read and concatenate with `---` separator:
1. `{prompt_base_path}/generator/_base.md`
2. `{prompt_base_path}/generator/{mode}.md`
1. `{prompt_path}/generator/_base.md`
2. `{prompt_path}/generator/{mode}.md`
Substitute template variables:
Substitute variables:
- `{{MAX_FILES_TO_READ}}` → from config scopeBudgets
- `{{MAX_LINES_TO_WRITE}}` → from config scopeBudgets
- `{{MAX_FILES_TO_MODIFY}}` → from config scopeBudgets
- `{{MODE}}` → mode from config
- `{{MODE}}` → mode
- `{{ITERATION}}` → current iteration
- `{{MAX_ITERATIONS}}` → max iterations
- `{{LOOP_DIR}}` → absolute path to `.loop/`
- `{{PROJECT_ROOT}}` → project root absolute path
- `{{PROJECT_ROOT}}` → project root
- `{{CURRENT_STORY_ID}}` → story ID
#### 2d. Capture Pre-Generator Git State
### 4d. Capture Pre-Generator Git State
Run `git rev-parse HEAD` and save the SHA.
#### 2e. Dispatch Generator Agent
### 4e. Dispatch Generator Agent
```
Agent(
@@ -102,27 +168,19 @@ Agent(
)
```
Wait for completion.
### 4f. Check Completion Signal
#### 2f. Check for Completion Signal
If output contains `<promise>COMPLETE</promise>`, report done and stop.
If output contains `<promise>COMPLETE</promise>`, report all stories complete and stop.
### 4g. Evaluator (unless skipped)
#### 2g. Skip Evaluator (if configured)
If `--skip-eval` or `config.skipEval` is true, skip to 4h and treat as PASS.
If `--skip-eval` or `config.skipEval` is true, skip to 2j and treat as PASS.
Otherwise, read and concatenate:
1. `{prompt_path}/evaluator/_base.md`
2. `{prompt_path}/evaluator/{mode}.md`
#### 2h. Assemble Evaluator Prompt
Read and concatenate:
1. `{prompt_base_path}/evaluator/_base.md`
2. `{prompt_base_path}/evaluator/{mode}.md`
Substitute same variables plus:
- `{{PRE_GENERATOR_SHA}}` → SHA from step 2d
- `{{CURRENT_STORY_ID}}` → story ID
#### 2i. Dispatch Evaluator Agent
Substitute same variables plus `{{PRE_GENERATOR_SHA}}` and `{{CURRENT_STORY_ID}}`.
```
Agent(
@@ -133,52 +191,48 @@ Agent(
)
```
Parse the verdict:
Parse verdict:
- `<verdict>PASS</verdict>` → PASS
- `<verdict>REJECT</verdict>` → REJECT, extract `<rejection_reason>...</rejection_reason>`
- No verdict tag → REJECT (fail-safe)
- `<verdict>REJECT</verdict>` → REJECT, extract reason
- No verdict → REJECT (fail-safe)
#### 2j. Update State
### 4h. Update State
**On PASS:**
1. Read `.loop/prd.json`, set `passes: true` for the story, write it back
2. Report: **{story.id} PASSED**
**PASS:** Set `passes: true` in prd.json. Report: **{story.id} PASSED**
**On REJECT:**
1. Read `.loop/prd.json`, increment `rejections`, append `[REJECTED] {reason}` to `notes`, write back
2. Report: **{story.id} REJECTED** — {reason}
3. If `rejections >= evalRetries`: set `blocked: true`, append `[BLOCKED]` to notes
- Report: **{story.id} BLOCKED** — rejected {N} times, needs human review
**REJECT:** Increment `rejections`, append `[REJECTED] {reason}` to `notes`. Report: **{story.id} REJECTED** — {reason}
#### 2k. Append Progress
If `rejections >= evalRetries`: set `blocked: true`. Report: **{story.id} BLOCKED**
### 4i. Append Progress
Append to `.loop/progress.md`:
```markdown
### {story.id} — {story.title}
Date: {current date and time}
Date: {date}
Iteration: {N}
Verdict: {PASS/REJECT/SKIP-EVAL}
Verdict: {verdict}
---
```
#### 2l. Report and Continue
### 4j. Continue
Show: `{passed}/{total} stories complete`
Continue to next iteration.
### Step 3: Loop Exit
---
## Loop Exit
> **Loop Complete**
> - Iterations used: {N}
> - Iterations: {N}
> - Stories: {passed}/{total} complete, {blocked} blocked
If incomplete, suggest `/agent-loop:triage`.
## Error Handling
### Error Handling
- Agent fails or empty output → warn and continue to next iteration
- prd.json unparseable → stop immediately
- User says "stop" → end loop, report current status
- Agent fails or empty output → warn and continue
- prd.json unparseable → stop
- User says "stop" → end loop, report status