From ecfbd0bb37eda4cc4c8915cb7ecd3b10d4fff47f Mon Sep 17 00:00:00 2001 From: Sheldon Finlay Date: Thu, 2 Apr 2026 11:21:17 -0400 Subject: [PATCH] feat: worktree-based run isolation for parallel loops MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Each /agent-loop:run now creates a git worktree for the feature branch before generating stories. This provides full isolation: - Multiple loops can run in parallel on different specs in the same project - Main working directory stays on main, always available - Each worktree has its own .loop/ state, tmux session, and branch - Completed runs are archived to main's .loop/archive/ with runs.log Changes: - setup.sh: add --init-worktree mode for initializing worktree .loop/ - archive.sh: add archive_from_worktree() for cross-directory archiving - loop.sh: replace branch checkout with validation (worktree is pre-checked-out) - agents/planner.md: accept absolute path prefix for worktree .loop/ writes - skills/run/SKILL.md: full rewrite — worktree creation in Phase 2, launch in Phase 3, archive on completion, .active-worktree tracking file - skills/stories/SKILL.md: worktree-aware, defer to /run for full flow Bump to 0.12.0. --- .claude-plugin/marketplace.json | 2 +- .claude-plugin/plugin.json | 2 +- agents/planner.md | 6 +- lib/archive.sh | 48 ++++++ loop.sh | 6 +- setup.sh | 57 +++++++- skills/run/SKILL.md | 252 +++++++++++++++++++++----------- skills/stories/SKILL.md | 24 ++- 8 files changed, 285 insertions(+), 112 deletions(-) diff --git a/.claude-plugin/marketplace.json b/.claude-plugin/marketplace.json index dc8e898..f6f3589 100644 --- a/.claude-plugin/marketplace.json +++ b/.claude-plugin/marketplace.json @@ -10,7 +10,7 @@ "name": "agent-loop", "source": "./", "description": "Autonomous generator-evaluator agent loop for long-running coding tasks. Plan interactively, then execute with full visibility.", - "version": "0.11.0", + "version": "0.12.0", "author": { "name": "Sheldon" }, diff --git a/.claude-plugin/plugin.json b/.claude-plugin/plugin.json index 07a0ffd..4f29361 100644 --- a/.claude-plugin/plugin.json +++ b/.claude-plugin/plugin.json @@ -1,6 +1,6 @@ { "name": "agent-loop", - "version": "0.11.0", + "version": "0.12.0", "description": "Autonomous generator-evaluator agent loop for long-running coding tasks. Run /agent-loop:run to start.", "author": { "name": "Sheldon" diff --git a/agents/planner.md b/agents/planner.md index bfbb9b0..2015ca5 100644 --- a/agents/planner.md +++ b/agents/planner.md @@ -11,12 +11,16 @@ You are a planner agent for the agent loop harness. Your job is to decompose a f ## CONSTRAINTS -- You may ONLY write files inside the `.loop/` directory +- You may ONLY write files inside the `.loop/` directory (or the absolute loop directory path if one is provided) - You may NOT write any project source code (.js, .ts, .py, .go, .rs, .html, .css, etc.) - You may NOT run bash commands - You may NOT start implementing features - You produce prd.json and contracts, then STOP +## OUTPUT DIRECTORY + +If the prompt specifies an absolute path for the loop directory (e.g., "Write all files to /path/to/worktree/.loop/"), use that absolute path for ALL file writes. Otherwise, use the relative `.loop/` path. + ## YOUR TASK You will be given a feature spec or description. Decompose it into stories. diff --git a/lib/archive.sh b/lib/archive.sh index 8bca8a3..6eb3b8a 100644 --- a/lib/archive.sh +++ b/lib/archive.sh @@ -150,6 +150,54 @@ print(json.load(open(os.environ['LOOP_PRD'])).get('branchName', ''), end='') echo "[archive] .loop/ reset — ready for new stories" } +# Archive a completed run from a worktree back to the main project's .loop/archive/. +# Called by the /run skill's completion handler after the loop finishes in a worktree. +# +# Usage: archive_from_worktree +# worktree_loop_dir: absolute path to the worktree's .loop/ (source) +# main_loop_dir: absolute path to the main project's .loop/ (destination) +archive_from_worktree() { + local wt_loop_dir="$1" + local main_loop_dir="$2" + local wt_prd="$wt_loop_dir/prd.json" + + [ -f "$wt_prd" ] || { echo "[archive] WARNING: No prd.json in worktree — nothing to archive"; return 1; } + + # Read branch name from worktree's prd.json + local branch_name="" + if command -v jq &>/dev/null; then + branch_name=$(jq -r '.branchName // empty' "$wt_prd" 2>/dev/null) + elif command -v python3 &>/dev/null; then + branch_name=$(LOOP_PRD="$wt_prd" python3 -c " +import json, os +print(json.load(open(os.environ['LOOP_PRD'])).get('branchName', ''), end='') +" 2>/dev/null) + fi + + local feature_name + feature_name=$(echo "${branch_name:-unknown}" | sed 's|.*/||') + + local archive_dir="$main_loop_dir/archive/$(date +%Y-%m-%d)-${feature_name}" + mkdir -p "$archive_dir" + + # Copy artifacts from worktree + [ -f "$wt_prd" ] && cp "$wt_prd" "$archive_dir/" + [ -f "$wt_loop_dir/progress.md" ] && cp "$wt_loop_dir/progress.md" "$archive_dir/" + [ -f "$wt_loop_dir/progress-archive.md" ] && cp "$wt_loop_dir/progress-archive.md" "$archive_dir/" + [ -d "$wt_loop_dir/contracts" ] && cp -r "$wt_loop_dir/contracts" "$archive_dir/" + [ -d "$wt_loop_dir/triage" ] && cp -r "$wt_loop_dir/triage" "$archive_dir/" + + # Verify archive has content + if ! find "$archive_dir" -maxdepth 1 -type f | read -r; then + echo "[archive] WARNING: Archive directory is empty — copy may have failed" + return 1 + fi + + append_runs_log "$branch_name" "$archive_dir" + + echo "[archive] Archived worktree run to $archive_dir" +} + # Append a one-line summary to the runs log. append_runs_log() { local branch_name="$1" diff --git a/loop.sh b/loop.sh index 2b51a81..62c565a 100755 --- a/loop.sh +++ b/loop.sh @@ -181,12 +181,14 @@ if [ -f "$LOOP_DIR/init.sh" ]; then bash "$LOOP_DIR/init.sh" fi -# Ensure correct git branch +# Verify we're on the expected branch (worktree should already be on it) BRANCH=$(prd_branch_name 2>/dev/null || echo "") if [ -n "$BRANCH" ]; then CURRENT_BRANCH=$(git rev-parse --abbrev-ref HEAD 2>/dev/null || echo "") if [ "$CURRENT_BRANCH" != "$BRANCH" ]; then - log "Switching to branch: $BRANCH" + log "WARNING: Expected branch '$BRANCH' but on '$CURRENT_BRANCH'" + log "If running in a worktree, the branch should already be checked out." + log "Attempting to switch..." git checkout "$BRANCH" 2>/dev/null || \ git checkout -b "$BRANCH" "origin/$BRANCH" 2>/dev/null || \ git checkout -b "$BRANCH" diff --git a/setup.sh b/setup.sh index 7eebcec..c1e9e75 100755 --- a/setup.sh +++ b/setup.sh @@ -11,16 +11,30 @@ set -euo pipefail # --- Parse arguments --- -UPDATE_ONLY=false +ACTION="scaffold" MODE="${1:-implement}" +WORKTREE_PATH="" +MAIN_LOOP_DIR="" -if [ "$MODE" = "--update" ]; then - UPDATE_ONLY=true - MODE="${2:-implement}" # mode not used in update, but keep arg parsing clean -fi +case "$MODE" in + --update) + ACTION="update" + MODE="${2:-implement}" + ;; + --init-worktree) + ACTION="init-worktree" + WORKTREE_PATH="$2" + MAIN_LOOP_DIR="$3" + if [ -z "$WORKTREE_PATH" ] || [ -z "$MAIN_LOOP_DIR" ]; then + echo "[setup] ERROR: --init-worktree requires " + echo "[setup] Usage: setup.sh --init-worktree /path/to/worktree /path/to/main/.loop" + exit 1 + fi + ;; +esac # --- Validate mode --- -if [ "$UPDATE_ONLY" = false ] && [[ ! "$MODE" =~ ^(implement|explore|fix)$ ]]; then +if [ "$ACTION" = "scaffold" ] && [[ ! "$MODE" =~ ^(implement|explore|fix)$ ]]; then echo "[setup] ERROR: Invalid mode '$MODE'. Must be: implement, explore, fix" exit 1 fi @@ -63,7 +77,7 @@ if [ -f "$HARNESS_SRC/.claude-plugin/plugin.json" ]; then fi # --- Update-only mode: refresh harness files without touching run state --- -if [ "$UPDATE_ONLY" = true ]; then +if [ "$ACTION" = "update" ]; then LOOP_DIR="$(pwd)/.loop" if [ ! -d "$LOOP_DIR" ]; then echo "[setup] ERROR: No .loop/ directory found. Run setup first." @@ -83,6 +97,34 @@ if [ "$UPDATE_ONLY" = true ]; then exit 0 fi +# --- Init-worktree mode: initialize .loop/ in a worktree from main's config --- +if [ "$ACTION" = "init-worktree" ]; then + LOOP_DIR="$WORKTREE_PATH/.loop" + mkdir -p "$LOOP_DIR" + + # Copy harness files from plugin source + cp -r "$HARNESS_SRC/prompts" "$LOOP_DIR/" + cp -r "$HARNESS_SRC/templates" "$LOOP_DIR/" + cp -r "$HARNESS_SRC/lib" "$LOOP_DIR/" + cp "$HARNESS_SRC/loop.sh" "$LOOP_DIR/" + chmod +x "$LOOP_DIR/loop.sh" + + # Copy project config and init from main's .loop/ + [ -f "$MAIN_LOOP_DIR/config.json" ] && cp "$MAIN_LOOP_DIR/config.json" "$LOOP_DIR/" + [ -f "$MAIN_LOOP_DIR/init.sh" ] && cp "$MAIN_LOOP_DIR/init.sh" "$LOOP_DIR/" + + # Stamp harness version + [ -n "$PLUGIN_VERSION" ] && echo "$PLUGIN_VERSION" > "$LOOP_DIR/.harness-version" + + # Create .gitignore for worktree's .loop/ + cat > "$LOOP_DIR/.gitignore" << 'GITIGNORE' +* +GITIGNORE + + echo "[setup] Worktree .loop/ initialized at $LOOP_DIR" + exit 0 +fi + # --- Ensure git repo exists --- if ! git rev-parse --git-dir &>/dev/null; then echo "[setup] No git repo found. Initializing..." @@ -141,6 +183,7 @@ archive/ .archive-staging/ .last-branch .harness-version +.active-worktree .loop.lock GITIGNORE diff --git a/skills/run/SKILL.md b/skills/run/SKILL.md index 90141f6..d9bf0fb 100644 --- a/skills/run/SKILL.md +++ b/skills/run/SKILL.md @@ -1,16 +1,18 @@ --- name: run -description: "Agent Loop — single entry point. Scaffolds .loop/ if missing, generates stories if no prd.json, then launches autonomous execution in tmux." +description: "Agent Loop — single entry point. Scaffolds .loop/ if missing, creates a worktree, generates stories, then launches autonomous execution in tmux." --- # /run — Agent Loop -Single entry point for the agent loop. Handles setup and planning interactively, then launches autonomous execution in a tmux session. +Single entry point for the agent loop. Handles setup and planning interactively, then launches autonomous execution in a git worktree via tmux. + +Each run gets its own worktree (isolated working directory on a feature branch). Multiple loops can run in parallel on different specs. Completed runs are archived to the main project's `.loop/archive/`. ## Usage ``` -/agent-loop:run # Full flow: setup → stories → launch +/agent-loop:run # Full flow: setup → worktree → stories → launch /agent-loop:run --skip-eval # Skip evaluator pass ``` @@ -20,9 +22,9 @@ Follow this sequence. Each phase checks what exists and skips if already done. --- -## Phase 1: Scaffold or Update Harness +## Phase 1: Scaffold Main .loop/ (if needed) -Check if `.loop/config.json` exists. +Check if `.loop/config.json` exists in the current project root. **If it does NOT exist**, run the setup script: @@ -31,7 +33,7 @@ Ask the user: **Mode?** (a) Implement (b) Explore (c) Fix — default is Impleme Then run: ```bash -bash "$(ls -d ~/.claude/plugins/cache/agent-loop/agent-loop/*/setup.sh 2>/dev/null | head -1)" +bash "$(ls -d ~/.claude/plugins/cache/agent-loop/agent-loop/*/setup.sh 2>/dev/null | tail -1)" ``` Show the output. If setup fails, stop. @@ -50,144 +52,179 @@ If the versions differ (or installed is "unknown"), update the harness files: bash "$(ls -d ~/.claude/plugins/cache/agent-loop/agent-loop/*/setup.sh 2>/dev/null | tail -1)" --update ``` -Tell the user: *"Updated harness files to v{version}."* Then continue to Phase 2. - -If versions match, skip to Phase 2. +Tell the user: *"Updated harness files to v{version}."* --- -## Phase 2: Generate Stories (if needed) +## Phase 2: Create Worktree and Generate Stories -Check if `.loop/prd.json` exists. +### 2a. Find the spec -**If it exists**, check whether the previous run is complete. Run: +Search for existing specs or plans: +- `docs/superpowers/specs/*.md` +- `docs/superpowers/plans/*.md` +- `docs/specs/*.md` +- `docs/plans/*.md` +- `SPEC.md`, `PRD.md`, `DESIGN.md`, `PLAN.md` at project root +- Any markdown file that looks like a feature spec or implementation plan -```bash -source .loop/lib/state.sh 2>/dev/null; LOOP_DIR=.loop all_stories_pass 2>/dev/null && echo "ALL_PASSED" || echo "IN_PROGRESS" -``` +If found: "I found a spec at `{path}`. Using it to generate stories." -Also check if the feature branch from prd.json still exists: +If NOT found, stop and tell the user: -```bash -jq -r '.branchName // empty' .loop/prd.json -``` - -If the result is `ALL_PASSED` **or** the branch no longer exists (deleted after merge), the previous run is done. Archive it and reset: - -```bash -LOOP_DIR=.loop source .loop/lib/state.sh && source .loop/lib/archive.sh && archive_and_reset .loop -``` - -Tell the user: *"Archived previous run ({branch}). Starting fresh."* - -Then proceed to generate new stories below. - -If the result is `IN_PROGRESS` and the branch still exists, the previous run is still active. Tell the user: - -> **Existing run found** — {passed}/{total} stories complete on `{branch}`. +> **No spec or plan found.** Agent Loop decomposes existing plans into stories — it doesn't create plans from scratch. > -> - Say **go** to resume the existing run -> - Say **archive** to archive this run and start fresh +> Create a plan first, then re-run `/agent-loop:run`: +> - Describe your idea to Claude and ask it to write a spec +> - Use `/plan` if available +> - Or create a markdown file at `docs/specs/` or `SPEC.md` +> +> The plan should describe what to build, the tech stack, and key requirements. -Wait for the user to decide before proceeding. +**STOP here. Do NOT ask the user to describe the project in a few sentences. Do NOT proceed without a spec file.** -**If `prd.json` does NOT exist**, generate it: +### 2b. Derive names and create worktree -1. Search for existing specs or plans: - - `docs/superpowers/specs/*.md` - - `docs/superpowers/plans/*.md` - - `docs/specs/*.md` - - `docs/plans/*.md` - - `SPEC.md`, `PRD.md`, `DESIGN.md`, `PLAN.md` at project root - - Any markdown file that looks like a feature spec or implementation plan +Read the spec title or filename to derive a feature slug. Examples: +- `SPEC.md` with title "# Enhanced Spikes Editor" → slug: `enhanced-spikes-editor` +- `docs/specs/auth-system.md` → slug: `auth-system` - If found: "I found a spec at `{path}`. Using it to generate stories." +Derive paths: - If NOT found, stop and tell the user: +```bash +PROJECT_DIR=$(basename "$(pwd)") +FEATURE_SLUG="" +BRANCH_NAME="loop/${FEATURE_SLUG}" +WORKTREE_PATH="../${PROJECT_DIR}--loop-${FEATURE_SLUG}" +MAIN_LOOP_DIR="$(pwd)/.loop" +``` - > **No spec or plan found.** Agent Loop decomposes existing plans into stories — it doesn't create plans from scratch. - > - > Create a plan first, then re-run `/agent-loop:run`: - > - Describe your idea to Claude and ask it to write a spec - > - Use `/plan` if available - > - Or create a markdown file at `docs/specs/` or `SPEC.md` - > - > The plan should describe what to build, the tech stack, and key requirements. +Check if the worktree already exists: - **STOP here. Do NOT ask the user to describe the project in a few sentences. Do NOT proceed without a spec file.** +```bash +if [ -d "$WORKTREE_PATH" ]; then + echo "WORKTREE_EXISTS" +else + echo "WORKTREE_NEW" +fi +``` -2. Read the project root and tech stack info. +**If worktree exists**, check its state: +- Read `{WORKTREE_PATH}/.loop/prd.json` — are all stories passed? +- If all passed: ask user — "Previous run on `{BRANCH_NAME}` is complete. Archive and start fresh, or resume?" +- If in progress: ask user — "Run in progress on `{BRANCH_NAME}` ({passed}/{total}). Resume, or archive and start fresh?" +- If user says resume: skip to Phase 3 (launch in existing worktree) +- If user says archive/fresh: archive from worktree to main, remove worktree, then continue below -3. Dispatch the **agent-loop:planner** agent: +**If worktree is new**, create it: + +```bash +git worktree add "$WORKTREE_PATH" -b "$BRANCH_NAME" +``` + +If the branch already exists (e.g., from a previous run): + +```bash +git worktree add "$WORKTREE_PATH" "$BRANCH_NAME" +``` + +Initialize the worktree's `.loop/`: + +```bash +bash "$(ls -d ~/.claude/plugins/cache/agent-loop/agent-loop/*/setup.sh 2>/dev/null | tail -1)" --init-worktree "$WORKTREE_PATH" "$MAIN_LOOP_DIR" +``` + +Initialize submodules if the project uses them: + +```bash +git -C "$WORKTREE_PATH" submodule update --init --recursive 2>/dev/null || true +``` + +### 2c. Generate stories + +Read the project root listing and tech stack info. + +Dispatch the **agent-loop:planner** agent. Pass the **absolute worktree path** so the planner writes to the worktree's `.loop/`: ``` 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}", + prompt: "Generate prd.json and sprint contracts.\n\nIMPORTANT: Write ALL files using absolute paths under: {WORKTREE_PATH}/.loop/\n- PRD: {WORKTREE_PATH}/.loop/prd.json\n- Contracts: {WORKTREE_PATH}/.loop/contracts/\n- Progress: {WORKTREE_PATH}/.loop/progress.md\n\nBranch name to use in prd.json: {BRANCH_NAME}\n\nMode: {mode}\nProject root: {WORKTREE_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: +### 2d. Present stories + +After the planner finishes, read `{WORKTREE_PATH}/.loop/prd.json` and present: > **Stories generated — Review before running** > +> Worktree: `{WORKTREE_PATH}` (branch: `{BRANCH_NAME}`) +> > 1. US-001: {title} > 2. US-002: {title} > ... > > **Review:** -> - `.loop/prd.json` — stories and acceptance criteria -> - `.loop/contracts/` — done conditions per story +> - `{WORKTREE_PATH}/.loop/prd.json` — stories and acceptance criteria +> - `{WORKTREE_PATH}/.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. +**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. --- ## Phase 3: Validate and Launch -1. Read `.loop/prd.json` and verify: +1. Read `{WORKTREE_PATH}/.loop/prd.json` and verify: - Has a `userStories` array (NOT `sprints`, `stories`, or `tasks`) - Each story has: `id`, `title`, `passes`, `priority` - If invalid, show the error and stop. -2. Read `.loop/config.json` for `mode`, `maxIterations`. +2. Read `{WORKTREE_PATH}/.loop/config.json` for `mode`, `maxIterations`. -3. Verify `.loop/loop.sh` exists and is executable. +3. Verify `{WORKTREE_PATH}/.loop/loop.sh` exists and is executable. 4. Parse arguments for any flags to pass through (e.g., `--skip-eval`). 5. Build the loop.sh command and derive a unique tmux session name: ```bash -LOOP_CMD=".loop/loop.sh" +LOOP_CMD="{WORKTREE_PATH}/.loop/loop.sh" # Add --skip-eval if requested # Add --max N if specified -# Derive tmux session name from project directory name. -# This allows multiple loops to run in parallel on different projects. -SESSION_NAME="agent-loop-$(basename "$(pwd)")" +# Derive tmux session name from worktree directory name +WORKTREE_DIR=$(basename "$WORKTREE_PATH") +SESSION_NAME="agent-loop-${WORKTREE_DIR}" ``` -6. Kill any existing tmux session for THIS project, then launch detached: +6. Kill any existing tmux session with this name, then launch detached in the worktree: ```bash -tmux kill-session -t "$SESSION_NAME" 2>/dev/null; tmux new-session -d -s "$SESSION_NAME" -c "$LOOP_CMD" +tmux kill-session -t "$SESSION_NAME" 2>/dev/null; tmux new-session -d -s "$SESSION_NAME" -c "$WORKTREE_PATH" "$LOOP_CMD" ``` -7. Start a **background watcher** that waits for the loop to finish. Use the Bash tool with `run_in_background: true`: +7. Save the worktree path and session name for the completion handler. Write a tracking file in main's .loop/: + +```bash +cat > .loop/.active-worktree << EOF +WORKTREE_PATH={WORKTREE_PATH} +SESSION_NAME={SESSION_NAME} +BRANCH_NAME={BRANCH_NAME} +MAIN_LOOP_DIR={MAIN_LOOP_DIR} +EOF +``` + +8. Start a **background watcher** that waits for the loop to finish. Use the Bash tool with `run_in_background: true`: ```bash while tmux has-session -t "$SESSION_NAME" 2>/dev/null; do sleep 10; done; echo "LOOP_COMPLETE" ``` -This runs silently. When the tmux session exits, Claude Code gets notified automatically. - -8. Tell the user: +9. Tell the user: > **Loop launched** as tmux session `{SESSION_NAME}`. Watch it live: > ``` @@ -200,6 +237,13 @@ This runs silently. When the tmux session exits, Claude Code gets notified autom > - Ask me "status" anytime and I'll check progress. > > I'll notify you when the loop finishes. +> +> When complete, merge with: +> ``` +> git merge {BRANCH_NAME} +> git worktree remove {WORKTREE_PATH} +> git branch -d {BRANCH_NAME} +> ``` --- @@ -207,18 +251,45 @@ This runs silently. When the tmux session exits, Claude Code gets notified autom When you receive the background task notification (the watcher prints "LOOP_COMPLETE"), the loop has finished. Automatically: -1. Read `.loop/prd.json` — count passed/failed/blocked stories -2. Read `.loop/progress.md` — show the latest session log entries -3. Check `git log --oneline` for commits made during the run -4. Present a summary: +1. Read the tracking file to get paths: + +```bash +cat .loop/.active-worktree +``` + +2. Read `{WORKTREE_PATH}/.loop/prd.json` — count passed/failed/blocked stories +3. Read `{WORKTREE_PATH}/.loop/progress.md` — show the latest session log entries +4. Check `git log --oneline` on the feature branch for commits made during the run + +5. Archive the run to main's `.loop/archive/`: + +```bash +source .loop/lib/state.sh && source .loop/lib/archive.sh && archive_from_worktree "{WORKTREE_PATH}/.loop" "$(pwd)/.loop" +``` + +6. Clean up the tracking file: + +```bash +rm -f .loop/.active-worktree +``` + +7. Present a summary: > **Loop Complete** > - Stories: {passed}/{total} complete, {blocked} blocked > - Iterations: {from progress.md} > - Commits: {list from git log} +> - Archived to: `.loop/archive/{date}-{feature}/` > > {If any stories blocked: "Some stories need human review. Run /agent-loop:triage for details."} > {If all passed: "All stories complete. Review the code and test it."} +> +> **When ready to merge:** +> ``` +> git merge {BRANCH_NAME} +> git worktree remove {WORKTREE_PATH} +> git branch -d {BRANCH_NAME} +> ``` --- @@ -226,12 +297,23 @@ When you receive the background task notification (the watcher prints "LOOP_COMP If the user asks about progress (e.g., "status", "how's it going"): -1. Read `.loop/prd.json` — count passed/failed/blocked -2. Derive the session name and capture recent tmux output: +1. Check for active worktree tracking: + +```bash +cat .loop/.active-worktree 2>/dev/null +``` + +If no tracking file, check for tmux sessions matching the pattern: + +```bash +tmux list-sessions 2>/dev/null | grep "^agent-loop-" +``` + +2. Read `{WORKTREE_PATH}/.loop/prd.json` — count passed/failed/blocked +3. Capture recent tmux output: ```bash -SESSION_NAME="agent-loop-$(basename "$(pwd)")" tmux capture-pane -t "$SESSION_NAME" -p | tail -20 ``` -3. Report current status. +4. Report current status. diff --git a/skills/stories/SKILL.md b/skills/stories/SKILL.md index bdb2c24..9128865 100644 --- a/skills/stories/SKILL.md +++ b/skills/stories/SKILL.md @@ -7,26 +7,14 @@ description: "Generate prd.json and sprint contracts by dispatching the planner Dispatch the planner agent to decompose a spec into stories. The planner agent cannot write source code or run bash commands — it can only write to `.loop/`. +**Note:** In most cases, use `/agent-loop:run` instead — it handles worktree creation, story generation, and launching the loop in one flow. Use `/agent-loop:stories` only if you want to generate stories without launching the loop. + ## Instructions ### 1. Check prerequisites Verify `.loop/config.json` exists. If not, tell the user to run `/agent-loop:setup` first and stop. -If `.loop/prd.json` already exists, check whether the previous run is complete: - -```bash -source .loop/lib/state.sh 2>/dev/null; LOOP_DIR=.loop all_stories_pass 2>/dev/null && echo "ALL_PASSED" || echo "IN_PROGRESS" -``` - -If `ALL_PASSED`, archive the completed run before generating new stories: - -```bash -LOOP_DIR=.loop source .loop/lib/state.sh && source .loop/lib/archive.sh && archive_and_reset .loop -``` - -If `IN_PROGRESS`, warn the user that a run is active and ask whether to archive it or abort. - ### 2. Find the spec Check these locations: @@ -54,9 +42,15 @@ Agent( ) ``` +If a worktree path is known (e.g., passed as context), include it in the prompt: + +``` +IMPORTANT: Write ALL files using absolute paths under: {WORKTREE_PATH}/.loop/ +``` + ### 5. Present results -After the planner finishes, read `.loop/prd.json` and show the user: +After the planner finishes, read `.loop/prd.json` (or `{WORKTREE_PATH}/.loop/prd.json`) and show the user: > **Plan Ready — Review Before Running** >