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

@@ -10,7 +10,7 @@
"name": "agent-loop", "name": "agent-loop",
"source": "./", "source": "./",
"description": "Autonomous generator-evaluator agent loop for long-running coding tasks. Plan interactively, then execute with full visibility.", "description": "Autonomous generator-evaluator agent loop for long-running coding tasks. Plan interactively, then execute with full visibility.",
"version": "0.5.0", "version": "0.6.0",
"author": { "author": {
"name": "Sheldon" "name": "Sheldon"
}, },

View File

@@ -1,6 +1,6 @@
{ {
"name": "agent-loop", "name": "agent-loop",
"version": "0.5.0", "version": "0.6.0",
"description": "Autonomous generator-evaluator agent loop for long-running coding tasks. Plan with /agent-loop:init, then execute with /agent-loop:run.", "description": "Autonomous generator-evaluator agent loop for long-running coding tasks. Plan with /agent-loop:init, then execute with /agent-loop:run.",
"author": { "author": {
"name": "Sheldon" "name": "Sheldon"

View File

@@ -1,45 +1,113 @@
--- ---
name: run 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 ## Usage
``` ```
/agent-loop:run # Run until all stories pass or max iterations /agent-loop:run # Full flow: setup → stories → loop
/agent-loop:run 3 # Run at most 3 iterations /agent-loop:run 3 # Limit to 3 loop iterations
/agent-loop:run --skip-eval # Skip evaluator pass /agent-loop:run --skip-eval # Skip evaluator pass
/agent-loop:run --story US-003 # Run only a specific story /agent-loop:run --story US-003 # Run only a specific story
``` ```
## Instructions ## 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. ## Phase 1: Scaffold (if needed)
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: 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`) - Has a `userStories` array (NOT `sprints`, `stories`, or `tasks`)
- Each story has: `id`, `title`, `passes`, `priority` - Each story has: `id`, `title`, `passes`, `priority`
- If validation fails, show the error and stop. Do NOT attempt to fix it automatically. - If invalid, show the error and stop.
4. Check prompts exist. Look for `.loop/prompts/generator/_base.md` in these locations (first match wins):
- `.loop/prompts/` (local project copy) 2. Read `.loop/config.json` for `mode`, `maxIterations`, `evalRetries`, `scopeBudgets`.
- `${CLAUDE_PLUGIN_ROOT}/prompts/` (plugin install)
3. Find prompts. Check these paths (first match wins):
- `.loop/prompts/` (local)
- `~/.claude/plugins/cache/agent-loop/agent-loop/*/prompts/` (plugin cache) - `~/.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: Report:
@@ -51,47 +119,45 @@ Report:
> >
> Starting. Interrupt me at any time. > Starting. Interrupt me at any time.
### Step 2: Iteration Loop
For each iteration (1 to max iterations): 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:** **If no actionable story remains:**
- All `passes: true` → report success and stop - All `passes: true` → report success and stop
- Some `blocked: true` → report which and suggest `/agent-loop:triage` - Some `blocked: true` → report which ones
- Stop the loop - Stop
#### 2b. Report Iteration Start ### 4b. Report Iteration Start
> **Iteration {N}/{max} — {story.id}: {story.title}** > **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: Read and concatenate with `---` separator:
1. `{prompt_base_path}/generator/_base.md` 1. `{prompt_path}/generator/_base.md`
2. `{prompt_base_path}/generator/{mode}.md` 2. `{prompt_path}/generator/{mode}.md`
Substitute template variables: Substitute variables:
- `{{MAX_FILES_TO_READ}}` → from config scopeBudgets - `{{MAX_FILES_TO_READ}}` → from config scopeBudgets
- `{{MAX_LINES_TO_WRITE}}` → from config scopeBudgets - `{{MAX_LINES_TO_WRITE}}` → from config scopeBudgets
- `{{MAX_FILES_TO_MODIFY}}` → from config scopeBudgets - `{{MAX_FILES_TO_MODIFY}}` → from config scopeBudgets
- `{{MODE}}` → mode from config - `{{MODE}}` → mode
- `{{ITERATION}}` → current iteration - `{{ITERATION}}` → current iteration
- `{{MAX_ITERATIONS}}` → max iterations - `{{MAX_ITERATIONS}}` → max iterations
- `{{LOOP_DIR}}` → absolute path to `.loop/` - `{{LOOP_DIR}}` → absolute path to `.loop/`
- `{{PROJECT_ROOT}}` → project root absolute path - `{{PROJECT_ROOT}}` → project root
- `{{CURRENT_STORY_ID}}` → story ID - `{{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. Run `git rev-parse HEAD` and save the SHA.
#### 2e. Dispatch Generator Agent ### 4e. Dispatch Generator Agent
``` ```
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 Substitute same variables plus `{{PRE_GENERATOR_SHA}}` and `{{CURRENT_STORY_ID}}`.
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
``` ```
Agent( Agent(
@@ -133,52 +191,48 @@ Agent(
) )
``` ```
Parse the verdict: Parse verdict:
- `<verdict>PASS</verdict>` → PASS - `<verdict>PASS</verdict>` → PASS
- `<verdict>REJECT</verdict>` → REJECT, extract `<rejection_reason>...</rejection_reason>` - `<verdict>REJECT</verdict>` → REJECT, extract reason
- No verdict tag → REJECT (fail-safe) - No verdict → REJECT (fail-safe)
#### 2j. Update State ### 4h. Update State
**On PASS:** **PASS:** Set `passes: true` in prd.json. Report: **{story.id} PASSED**
1. Read `.loop/prd.json`, set `passes: true` for the story, write it back
2. Report: **{story.id} PASSED**
**On REJECT:** **REJECT:** Increment `rejections`, append `[REJECTED] {reason}` to `notes`. Report: **{story.id} REJECTED** — {reason}
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
#### 2k. Append Progress If `rejections >= evalRetries`: set `blocked: true`. Report: **{story.id} BLOCKED**
### 4i. Append Progress
Append to `.loop/progress.md`: Append to `.loop/progress.md`:
```markdown ```markdown
### {story.id} — {story.title} ### {story.id} — {story.title}
Date: {current date and time} Date: {date}
Iteration: {N} Iteration: {N}
Verdict: {PASS/REJECT/SKIP-EVAL} Verdict: {verdict}
--- ---
``` ```
#### 2l. Report and Continue ### 4j. Continue
Show: `{passed}/{total} stories complete` Show: `{passed}/{total} stories complete`
Continue to next iteration. Continue to next iteration.
### Step 3: Loop Exit ---
## Loop Exit
> **Loop Complete** > **Loop Complete**
> - Iterations used: {N} > - Iterations: {N}
> - Stories: {passed}/{total} complete, {blocked} blocked > - Stories: {passed}/{total} complete, {blocked} blocked
If incomplete, suggest `/agent-loop:triage`. ## Error Handling
### Error Handling - Agent fails or empty output → warn and continue
- prd.json unparseable → stop
- Agent fails or empty output → warn and continue to next iteration - User says "stop" → end loop, report status
- prd.json unparseable → stop immediately
- User says "stop" → end loop, report current status