feat: implement BMAD Auto Loop with automation scripts for Claude Code
This commit is contained in:
parent
323cd75efd
commit
592dfd1da5
|
|
@ -1,4 +1,6 @@
|
|||
const chalk = require('chalk');
|
||||
const fs = require('fs-extra');
|
||||
const path = require('node:path');
|
||||
|
||||
/**
|
||||
* Core Module Installer
|
||||
|
|
@ -40,21 +42,119 @@ async function install(options) {
|
|||
|
||||
/**
|
||||
* Configure Core module for specific IDE
|
||||
* @param {string} ide - The IDE code (e.g., 'claude-code', 'cursor')
|
||||
* @param {string} projectRoot - Path to the target project root
|
||||
* @param {Object} config - Module configuration
|
||||
* @param {Object} logger - Logger instance
|
||||
* @private
|
||||
*/
|
||||
async function configureForIDE(ide) {
|
||||
// Add IDE-specific configurations here
|
||||
async function configureForIDE(ide, projectRoot, config, logger) {
|
||||
switch (ide) {
|
||||
case 'claude-code': {
|
||||
// Claude Code specific Core configurations
|
||||
// Install BMAD Auto scripts for automated development workflow
|
||||
await installBmadAutoScripts(projectRoot, config, logger);
|
||||
break;
|
||||
}
|
||||
// Add more IDEs as needed
|
||||
default: {
|
||||
// No specific configuration needed
|
||||
// No specific configuration needed for other IDEs
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Install BMAD Auto automation scripts for Claude Code
|
||||
*
|
||||
* This function:
|
||||
* 1. Creates the .scripts/bmad-auto/claude/ directory in the target project
|
||||
* 2. Copies the automation scripts (PS1, SH, MD files)
|
||||
* 3. Generates a config file with project-specific paths
|
||||
*
|
||||
* @param {string} projectRoot - Path to the target project root
|
||||
* @param {Object} config - Module configuration containing output_folder
|
||||
* @param {Object} logger - Logger instance for output
|
||||
* @private
|
||||
*/
|
||||
async function installBmadAutoScripts(projectRoot, config, logger) {
|
||||
// Source directory: where our template scripts are stored
|
||||
// Located relative to this installer file: ../scripts/bmad-auto/
|
||||
const scriptsSource = path.join(__dirname, '..', 'scripts', 'bmad-auto');
|
||||
|
||||
// Target directory: where scripts will be installed in user's project
|
||||
const scriptsTarget = path.join(projectRoot, '.scripts', 'bmad-auto', 'claude');
|
||||
|
||||
// Check if source scripts exist
|
||||
if (!(await fs.pathExists(scriptsSource))) {
|
||||
logger.log(chalk.yellow(' ⚠ BMAD Auto scripts source not found, skipping'));
|
||||
return;
|
||||
}
|
||||
|
||||
// Create target directory
|
||||
await fs.ensureDir(scriptsTarget);
|
||||
|
||||
// List of script files to copy
|
||||
const scriptFiles = [
|
||||
'bmad-loop.ps1', // Windows PowerShell version
|
||||
'bmad-loop.sh', // Linux/macOS Bash version
|
||||
'bmad-prompt.md', // Prompt template for Claude Code
|
||||
'README.md', // Usage documentation
|
||||
];
|
||||
|
||||
// Copy each script file
|
||||
for (const file of scriptFiles) {
|
||||
const sourcePath = path.join(scriptsSource, file);
|
||||
const targetPath = path.join(scriptsTarget, file);
|
||||
|
||||
if (await fs.pathExists(sourcePath)) {
|
||||
await fs.copy(sourcePath, targetPath);
|
||||
}
|
||||
}
|
||||
|
||||
// Generate the configuration file with project-specific paths
|
||||
// The output_folder may contain template variables like {project-root}/
|
||||
const outputFolder = (config.output_folder || '_bmad-output')
|
||||
.replace('{project-root}/', '')
|
||||
.replace('{project-root}\\', '')
|
||||
.replace(/^\//, '') // Remove leading slash if present
|
||||
.replace(/^\\/, ''); // Remove leading backslash if present
|
||||
|
||||
// Create config YAML content
|
||||
// Using forward slashes for cross-platform compatibility
|
||||
const configContent = `# BMAD Auto Configuration
|
||||
# ============================================================
|
||||
# This file is auto-generated during BMAD installation.
|
||||
# It contains paths specific to your project setup.
|
||||
#
|
||||
# You can modify these values if you change your BMAD configuration:
|
||||
# - output_folder: Where BMAD stores generated files
|
||||
# - implementation_artifacts: Where sprint-status.yaml is located
|
||||
# ============================================================
|
||||
|
||||
# Absolute path to your project root directory
|
||||
project_root: "${projectRoot.replaceAll('\\', '/')}"
|
||||
|
||||
# Relative path to BMAD output folder (from project root)
|
||||
output_folder: "${outputFolder}"
|
||||
|
||||
# Relative path to implementation artifacts (where sprint-status.yaml lives)
|
||||
implementation_artifacts: "${outputFolder}/implementation-artifacts"
|
||||
`;
|
||||
|
||||
// Write the config file
|
||||
const configPath = path.join(scriptsTarget, 'bmad-auto-config.yaml');
|
||||
await fs.writeFile(configPath, configContent, 'utf8');
|
||||
|
||||
// Make bash script executable on Unix systems
|
||||
const bashScriptPath = path.join(scriptsTarget, 'bmad-loop.sh');
|
||||
if (await fs.pathExists(bashScriptPath)) {
|
||||
try {
|
||||
await fs.chmod(bashScriptPath, 0o755);
|
||||
} catch {
|
||||
// Ignore chmod errors on Windows
|
||||
}
|
||||
}
|
||||
|
||||
logger.log(chalk.green(' ✓ BMAD Auto scripts installed to .scripts/bmad-auto/claude/'));
|
||||
}
|
||||
|
||||
module.exports = { install };
|
||||
|
|
|
|||
|
|
@ -0,0 +1,106 @@
|
|||
# BMAD Auto - Automated Development Loop for Claude Code
|
||||
|
||||
This folder contains automation scripts that run the complete BMAD story development
|
||||
cycle automatically using Claude Code CLI.
|
||||
|
||||
## What It Does
|
||||
|
||||
The automation loop continuously:
|
||||
1. Reads `sprint-status.yaml` to determine story states
|
||||
2. Executes the appropriate BMAD workflow command:
|
||||
- `backlog` stories → `/bmad-bmm-create-story`
|
||||
- `ready-for-dev` stories → `/bmad-bmm-dev-story`
|
||||
- `review` stories → `/bmad-bmm-code-review`
|
||||
3. Auto-commits code after successful code reviews
|
||||
4. Repeats until all stories are `done`
|
||||
|
||||
## Files
|
||||
|
||||
| File | Description |
|
||||
|------|-------------|
|
||||
| `bmad-loop.ps1` | Windows PowerShell automation script |
|
||||
| `bmad-loop.sh` | Linux/macOS Bash automation script |
|
||||
| `bmad-prompt.md` | Prompt template sent to Claude Code |
|
||||
| `bmad-auto-config.yaml` | Configuration file (auto-generated) |
|
||||
| `bmad-progress.log` | Execution log (created on first run) |
|
||||
|
||||
## Usage
|
||||
|
||||
### Windows (PowerShell)
|
||||
|
||||
```powershell
|
||||
# Navigate to your project root
|
||||
cd C:\path\to\your\project
|
||||
|
||||
# Run with default settings (30 max iterations)
|
||||
.\.scripts\bmad-auto\claude\bmad-loop.ps1
|
||||
|
||||
# Run with custom max iterations
|
||||
.\.scripts\bmad-auto\claude\bmad-loop.ps1 -MaxIterations 50
|
||||
|
||||
# Run with verbose logging
|
||||
.\.scripts\bmad-auto\claude\bmad-loop.ps1 -Verbose
|
||||
```
|
||||
|
||||
### Linux/macOS (Bash)
|
||||
|
||||
```bash
|
||||
# Navigate to your project root
|
||||
cd /path/to/your/project
|
||||
|
||||
# Make script executable (first time only)
|
||||
chmod +x ./.scripts/bmad-auto/claude/bmad-loop.sh
|
||||
|
||||
# Run with default settings (30 max iterations)
|
||||
./.scripts/bmad-auto/claude/bmad-loop.sh
|
||||
|
||||
# Run with custom max iterations
|
||||
./.scripts/bmad-auto/claude/bmad-loop.sh 50
|
||||
|
||||
# Run with verbose logging
|
||||
./.scripts/bmad-auto/claude/bmad-loop.sh 50 --verbose
|
||||
```
|
||||
|
||||
## Prerequisites
|
||||
|
||||
Before running the automation:
|
||||
|
||||
1. **Claude Code CLI** must be installed and in your system PATH
|
||||
- Test with: `claude --version`
|
||||
|
||||
2. **BMAD installed** with Claude Code support
|
||||
- Run: `npx bmad-method install` and select Claude Code
|
||||
|
||||
3. **Sprint planning completed** - you must have a `sprint-status.yaml` file
|
||||
- Run: `/bmad-bmm-sprint-planning` in Claude Code
|
||||
|
||||
## Configuration
|
||||
|
||||
The `bmad-auto-config.yaml` file is automatically generated during BMAD installation.
|
||||
It contains:
|
||||
|
||||
```yaml
|
||||
project_root: "/path/to/your/project"
|
||||
output_folder: "_bmad-output"
|
||||
implementation_artifacts: "_bmad-output/implementation-artifacts"
|
||||
```
|
||||
|
||||
If you change your output folder after installation, update this file accordingly.
|
||||
|
||||
## Safety Features
|
||||
|
||||
- **Max iterations limit**: Prevents infinite loops (default: 30)
|
||||
- **Git status check**: Only commits if there are actual changes
|
||||
- **Error handling**: Logs errors and continues to next iteration
|
||||
- **Progress logging**: All actions logged to `bmad-progress.log`
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### "Configuration file not found"
|
||||
Re-run `npx bmad-method install` and select Claude Code.
|
||||
|
||||
### "sprint-status.yaml not found"
|
||||
Run `/bmad-bmm-sprint-planning` to create the sprint status file.
|
||||
|
||||
### "Claude Code not found"
|
||||
Ensure Claude Code CLI is installed and in your PATH.
|
||||
|
|
@ -0,0 +1,270 @@
|
|||
<#
|
||||
.SYNOPSIS
|
||||
BMAD Auto Loop - Automated Story Development Cycle
|
||||
.DESCRIPTION
|
||||
Executes BMAD workflow in a loop: create-story -> dev-story -> code-review -> commit
|
||||
Continues until all stories reach 'done' state in sprint-status.yaml
|
||||
.PARAMETER MaxIterations
|
||||
Safety limit for maximum loop iterations (default: 30)
|
||||
.PARAMETER Verbose
|
||||
Enable detailed debug logging
|
||||
.EXAMPLE
|
||||
.\bmad-loop.ps1
|
||||
.\bmad-loop.ps1 -MaxIterations 50 -Verbose
|
||||
#>
|
||||
|
||||
param(
|
||||
[int]$MaxIterations = 30,
|
||||
[switch]$Verbose
|
||||
)
|
||||
|
||||
$ErrorActionPreference = "Continue"
|
||||
$ScriptDir = Split-Path -Parent $MyInvocation.MyCommand.Path
|
||||
|
||||
# ============================================================
|
||||
# CONFIGURATION LOADING
|
||||
# ============================================================
|
||||
# The bmad-auto-config.yaml file is generated during BMAD installation.
|
||||
# It contains the project root and output folder paths specific to this project.
|
||||
# This allows the script to work with any output folder configuration.
|
||||
|
||||
$ConfigPath = Join-Path $ScriptDir "bmad-auto-config.yaml"
|
||||
|
||||
if (-not (Test-Path $ConfigPath)) {
|
||||
Write-Host @"
|
||||
[ERROR] Configuration file not found: $ConfigPath
|
||||
|
||||
This file is generated automatically when you install BMAD with Claude Code support.
|
||||
To fix this, run: npx bmad-method install
|
||||
And select 'Claude Code' as your IDE.
|
||||
"@ -ForegroundColor Red
|
||||
exit 1
|
||||
}
|
||||
|
||||
# Simple YAML parser for flat key-value config
|
||||
# Handles both quoted and unquoted values
|
||||
$config = @{}
|
||||
Get-Content $ConfigPath | ForEach-Object {
|
||||
# Match lines like: key: "value" or key: value (ignore comments starting with #)
|
||||
if ($_ -match '^([^#]\w+):\s*"?([^"]+)"?\s*$') {
|
||||
$config[$Matches[1].Trim()] = $Matches[2].Trim()
|
||||
}
|
||||
}
|
||||
|
||||
# Validate required config values
|
||||
$ProjectRoot = $config['project_root']
|
||||
if (-not $ProjectRoot) {
|
||||
Write-Host "[ERROR] 'project_root' not found in config file" -ForegroundColor Red
|
||||
exit 1
|
||||
}
|
||||
|
||||
$ImplementationArtifacts = $config['implementation_artifacts']
|
||||
if (-not $ImplementationArtifacts) {
|
||||
# Fallback to default if not specified
|
||||
$ImplementationArtifacts = "_bmad-output/implementation-artifacts"
|
||||
}
|
||||
|
||||
# Build paths from config
|
||||
$SprintStatusPath = Join-Path $ProjectRoot $ImplementationArtifacts "sprint-status.yaml"
|
||||
$ProgressLog = Join-Path $ScriptDir "bmad-progress.log"
|
||||
$PromptTemplate = Join-Path $ScriptDir "bmad-prompt.md"
|
||||
|
||||
# ============================================================
|
||||
# LOGGING FUNCTION
|
||||
# ============================================================
|
||||
|
||||
function Write-Log {
|
||||
param(
|
||||
[string]$Message,
|
||||
[string]$Color = "White"
|
||||
)
|
||||
|
||||
$timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
|
||||
Write-Host $Message -ForegroundColor $Color
|
||||
Add-Content -Path $ProgressLog -Value "[$timestamp] $Message"
|
||||
}
|
||||
|
||||
# ============================================================
|
||||
# GET NEXT ACTION
|
||||
# ============================================================
|
||||
# Analyzes sprint-status.yaml and returns the next action to take
|
||||
# Priority order: code-review > dev-story > create-story
|
||||
|
||||
function Get-NextAction {
|
||||
if (-not (Test-Path $SprintStatusPath)) {
|
||||
Write-Log "[ERROR] sprint-status.yaml not found: $SprintStatusPath" "Red"
|
||||
return "error"
|
||||
}
|
||||
|
||||
# Read file content
|
||||
$content = Get-Content $SprintStatusPath -Raw
|
||||
$lines = $content -split "`n"
|
||||
|
||||
# Filter story lines (pattern: digits-digits-*) excluding retrospectives
|
||||
$storyLines = $lines | Where-Object {
|
||||
$_ -match '^\s*[0-9]+-[0-9]+-' -and $_ -notmatch 'retrospective'
|
||||
}
|
||||
|
||||
if ($Verbose) {
|
||||
Write-Log "[DEBUG] Found $($storyLines.Count) story lines" "Gray"
|
||||
}
|
||||
|
||||
# Count stories in each state
|
||||
$reviewCount = ($storyLines | Where-Object { $_ -match ':\s*review\s*$' }).Count
|
||||
$readyCount = ($storyLines | Where-Object { $_ -match ':\s*ready-for-dev\s*$' }).Count
|
||||
$backlogCount = ($storyLines | Where-Object { $_ -match ':\s*backlog\s*$' }).Count
|
||||
$doneCount = ($storyLines | Where-Object { $_ -match ':\s*done\s*$' }).Count
|
||||
$totalCount = $storyLines.Count
|
||||
|
||||
if ($Verbose) {
|
||||
Write-Log "[DEBUG] Review: $reviewCount, Ready: $readyCount, Backlog: $backlogCount, Done: $doneCount" "Gray"
|
||||
}
|
||||
|
||||
# Priority order: review > ready-for-dev > backlog
|
||||
if ($reviewCount -gt 0) {
|
||||
Write-Log "[NEXT] Found story in REVIEW state" "Green"
|
||||
return "code-review"
|
||||
}
|
||||
|
||||
if ($readyCount -gt 0) {
|
||||
Write-Log "[NEXT] Found story in READY-FOR-DEV state" "Green"
|
||||
return "dev-story"
|
||||
}
|
||||
|
||||
if ($backlogCount -gt 0) {
|
||||
Write-Log "[NEXT] Found story in BACKLOG state" "Green"
|
||||
return "create-story"
|
||||
}
|
||||
|
||||
# Check if all done
|
||||
if ($doneCount -eq $totalCount -and $totalCount -gt 0) {
|
||||
return "complete"
|
||||
}
|
||||
|
||||
# Check for in-progress (waiting state)
|
||||
$inProgressCount = ($storyLines | Where-Object { $_ -match ':\s*in-progress\s*$' }).Count
|
||||
if ($inProgressCount -gt 0) {
|
||||
Write-Log "[WAIT] Found $inProgressCount story(ies) in-progress" "Yellow"
|
||||
}
|
||||
|
||||
return "wait"
|
||||
}
|
||||
|
||||
# ============================================================
|
||||
# INVOKE CLAUDE COMMAND
|
||||
# ============================================================
|
||||
|
||||
function Invoke-ClaudeCommand {
|
||||
param([string]$Command)
|
||||
|
||||
Write-Log "[EXEC] Executing Claude Code: $Command" "Cyan"
|
||||
|
||||
# Create customized prompt from template
|
||||
$prompt = Get-Content $PromptTemplate -Raw
|
||||
$prompt = $prompt -replace '\{COMMAND\}', $Command
|
||||
$prompt = $prompt -replace '\{TIMESTAMP\}', (Get-Date -Format "yyyy-MM-dd HH:mm:ss")
|
||||
|
||||
# Save to temp file
|
||||
$tempPrompt = [System.IO.Path]::GetTempFileName()
|
||||
Set-Content -Path $tempPrompt -Value $prompt
|
||||
|
||||
try {
|
||||
# Execute Claude Code
|
||||
Get-Content $tempPrompt | claude --dangerously-skip-permissions
|
||||
$success = $LASTEXITCODE -eq 0
|
||||
|
||||
if (-not $success) {
|
||||
Write-Log "[ERROR] Claude execution failed with exit code: $LASTEXITCODE" "Red"
|
||||
}
|
||||
|
||||
return $success
|
||||
}
|
||||
finally {
|
||||
Remove-Item $tempPrompt -Force -ErrorAction SilentlyContinue
|
||||
}
|
||||
}
|
||||
|
||||
# ============================================================
|
||||
# GIT COMMIT
|
||||
# ============================================================
|
||||
|
||||
function Invoke-GitCommit {
|
||||
Write-Log "[GIT] Checking for changes to commit..." "Yellow"
|
||||
|
||||
$status = git status --porcelain
|
||||
|
||||
if (-not $status) {
|
||||
Write-Log "[INFO] No changes to commit" "Gray"
|
||||
return $true
|
||||
}
|
||||
|
||||
Write-Log "[GIT] Committing changes..." "Yellow"
|
||||
|
||||
try {
|
||||
git add -A
|
||||
git commit -m "feat: BMAD auto-commit after code-review"
|
||||
Write-Log "[SUCCESS] Changes committed successfully" "Green"
|
||||
return $true
|
||||
}
|
||||
catch {
|
||||
Write-Log "[ERROR] Git commit failed: $_" "Red"
|
||||
return $false
|
||||
}
|
||||
}
|
||||
|
||||
# ============================================================
|
||||
# MAIN LOOP
|
||||
# ============================================================
|
||||
|
||||
Write-Log "[START] BMAD Auto Loop Started" "Green"
|
||||
Write-Log "Max iterations: $MaxIterations" "Gray"
|
||||
Write-Log "Project root: $ProjectRoot" "Gray"
|
||||
Write-Log "Sprint status: $SprintStatusPath" "Gray"
|
||||
Write-Host ""
|
||||
|
||||
for ($iteration = 1; $iteration -le $MaxIterations; $iteration++) {
|
||||
Write-Log "=======================================" "Cyan"
|
||||
Write-Log "=== Iteration $iteration/$MaxIterations ===" "Cyan"
|
||||
Write-Log "=======================================" "Cyan"
|
||||
|
||||
$action = Get-NextAction
|
||||
|
||||
switch ($action) {
|
||||
"create-story" {
|
||||
Write-Log "[ACTION] CREATE STORY" "Yellow"
|
||||
Invoke-ClaudeCommand "/bmad-bmm-create-story"
|
||||
}
|
||||
"dev-story" {
|
||||
Write-Log "[ACTION] DEVELOP STORY" "Yellow"
|
||||
Invoke-ClaudeCommand "/bmad-bmm-dev-story"
|
||||
}
|
||||
"code-review" {
|
||||
Write-Log "[ACTION] CODE REVIEW" "Yellow"
|
||||
$success = Invoke-ClaudeCommand "/bmad-bmm-code-review"
|
||||
if ($success) {
|
||||
Invoke-GitCommit
|
||||
}
|
||||
}
|
||||
"complete" {
|
||||
Write-Log "[COMPLETE] ALL STORIES COMPLETED!" "Green"
|
||||
Write-Log "Sprint status: All stories are DONE" "Green"
|
||||
Write-Log "Total iterations: $iteration" "Gray"
|
||||
exit 0
|
||||
}
|
||||
"wait" {
|
||||
Write-Log "[WAIT] Waiting state - story might be in-progress" "Yellow"
|
||||
Write-Log "Skipping this iteration..." "Gray"
|
||||
}
|
||||
"error" {
|
||||
Write-Log "[ERROR] Error state - stopping loop" "Red"
|
||||
exit 1
|
||||
}
|
||||
}
|
||||
|
||||
Write-Host ""
|
||||
Start-Sleep -Seconds 5
|
||||
}
|
||||
|
||||
Write-Log "[TIMEOUT] Max iterations reached without completion" "Yellow"
|
||||
Write-Log "Check sprint-status.yaml for current state" "Gray"
|
||||
exit 1
|
||||
|
|
@ -0,0 +1,268 @@
|
|||
#!/bin/bash
|
||||
# ============================================================
|
||||
# BMAD Auto Loop - Automated Story Development Cycle
|
||||
# ============================================================
|
||||
#
|
||||
# DESCRIPTION:
|
||||
# Executes BMAD workflow in a loop: create-story -> dev-story -> code-review -> commit
|
||||
# Continues until all stories reach 'done' state in sprint-status.yaml
|
||||
#
|
||||
# USAGE:
|
||||
# ./bmad-loop.sh # Run with default 30 max iterations
|
||||
# ./bmad-loop.sh 50 # Run with 50 max iterations
|
||||
# ./bmad-loop.sh 50 --verbose # Run with verbose logging
|
||||
#
|
||||
# PREREQUISITES:
|
||||
# - Claude Code CLI installed and in PATH
|
||||
# - BMAD installed with Claude Code support
|
||||
# - Sprint planning completed (sprint-status.yaml exists)
|
||||
#
|
||||
# ============================================================
|
||||
|
||||
set -e # Exit on error
|
||||
|
||||
# Parse arguments
|
||||
MAX_ITERATIONS=${1:-30}
|
||||
VERBOSE=false
|
||||
if [[ "$2" == "--verbose" || "$2" == "-v" ]]; then
|
||||
VERBOSE=true
|
||||
fi
|
||||
|
||||
# Get script directory (works even if script is called via symlink)
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
CONFIG_PATH="$SCRIPT_DIR/bmad-auto-config.yaml"
|
||||
PROGRESS_LOG="$SCRIPT_DIR/bmad-progress.log"
|
||||
PROMPT_TEMPLATE="$SCRIPT_DIR/bmad-prompt.md"
|
||||
|
||||
# ============================================================
|
||||
# CONFIGURATION LOADING
|
||||
# ============================================================
|
||||
# The bmad-auto-config.yaml file is generated during BMAD installation.
|
||||
# It contains the project root and output folder paths.
|
||||
|
||||
if [[ ! -f "$CONFIG_PATH" ]]; then
|
||||
echo "[ERROR] Configuration file not found: $CONFIG_PATH"
|
||||
echo ""
|
||||
echo "This file is generated automatically when you install BMAD with Claude Code support."
|
||||
echo "To fix this, run: npx bmad-method install"
|
||||
echo "And select 'Claude Code' as your IDE."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Parse YAML config (simple key: value format)
|
||||
# Uses grep and sed to extract values, handles quoted and unquoted values
|
||||
PROJECT_ROOT=$(grep "^project_root:" "$CONFIG_PATH" | sed 's/project_root: *"\{0,1\}\([^"]*\)"\{0,1\}/\1/' | tr -d '\r')
|
||||
IMPL_ARTIFACTS=$(grep "^implementation_artifacts:" "$CONFIG_PATH" | sed 's/implementation_artifacts: *"\{0,1\}\([^"]*\)"\{0,1\}/\1/' | tr -d '\r')
|
||||
|
||||
# Validate required config
|
||||
if [[ -z "$PROJECT_ROOT" ]]; then
|
||||
echo "[ERROR] 'project_root' not found in config file"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Fallback for implementation_artifacts
|
||||
if [[ -z "$IMPL_ARTIFACTS" ]]; then
|
||||
IMPL_ARTIFACTS="_bmad-output/implementation-artifacts"
|
||||
fi
|
||||
|
||||
# Build paths from config
|
||||
SPRINT_STATUS_PATH="$PROJECT_ROOT/$IMPL_ARTIFACTS/sprint-status.yaml"
|
||||
|
||||
# ============================================================
|
||||
# LOGGING FUNCTION
|
||||
# ============================================================
|
||||
|
||||
log() {
|
||||
local message="$1"
|
||||
local color="${2:-white}"
|
||||
local timestamp=$(date "+%Y-%m-%d %H:%M:%S")
|
||||
|
||||
# Color codes
|
||||
case $color in
|
||||
red) color_code="\033[0;31m" ;;
|
||||
green) color_code="\033[0;32m" ;;
|
||||
yellow) color_code="\033[0;33m" ;;
|
||||
cyan) color_code="\033[0;36m" ;;
|
||||
gray) color_code="\033[0;90m" ;;
|
||||
*) color_code="\033[0m" ;;
|
||||
esac
|
||||
|
||||
echo -e "${color_code}${message}\033[0m"
|
||||
echo "[$timestamp] $message" >> "$PROGRESS_LOG"
|
||||
}
|
||||
|
||||
# ============================================================
|
||||
# GET NEXT ACTION
|
||||
# ============================================================
|
||||
# Analyzes sprint-status.yaml and returns the next action to take
|
||||
# Priority order: code-review > dev-story > create-story
|
||||
|
||||
get_next_action() {
|
||||
if [[ ! -f "$SPRINT_STATUS_PATH" ]]; then
|
||||
log "[ERROR] sprint-status.yaml not found: $SPRINT_STATUS_PATH" "red"
|
||||
echo "error"
|
||||
return
|
||||
fi
|
||||
|
||||
# Filter story lines (pattern: digits-digits-*) excluding retrospectives
|
||||
local story_lines=$(grep -E '^\s*[0-9]+-[0-9]+-' "$SPRINT_STATUS_PATH" | grep -v "retrospective")
|
||||
|
||||
if $VERBOSE; then
|
||||
local count=$(echo "$story_lines" | wc -l)
|
||||
log "[DEBUG] Found $count story lines" "gray"
|
||||
fi
|
||||
|
||||
# Count stories in each state
|
||||
local review_count=$(echo "$story_lines" | grep -c ': *review *$' || true)
|
||||
local ready_count=$(echo "$story_lines" | grep -c ': *ready-for-dev *$' || true)
|
||||
local backlog_count=$(echo "$story_lines" | grep -c ': *backlog *$' || true)
|
||||
local done_count=$(echo "$story_lines" | grep -c ': *done *$' || true)
|
||||
local total_count=$(echo "$story_lines" | grep -c '.' || true)
|
||||
|
||||
if $VERBOSE; then
|
||||
log "[DEBUG] Review: $review_count, Ready: $ready_count, Backlog: $backlog_count, Done: $done_count" "gray"
|
||||
fi
|
||||
|
||||
# Priority order: review > ready-for-dev > backlog
|
||||
if [[ $review_count -gt 0 ]]; then
|
||||
log "[NEXT] Found story in REVIEW state" "green"
|
||||
echo "code-review"
|
||||
return
|
||||
fi
|
||||
|
||||
if [[ $ready_count -gt 0 ]]; then
|
||||
log "[NEXT] Found story in READY-FOR-DEV state" "green"
|
||||
echo "dev-story"
|
||||
return
|
||||
fi
|
||||
|
||||
if [[ $backlog_count -gt 0 ]]; then
|
||||
log "[NEXT] Found story in BACKLOG state" "green"
|
||||
echo "create-story"
|
||||
return
|
||||
fi
|
||||
|
||||
# Check if all done
|
||||
if [[ $done_count -eq $total_count && $total_count -gt 0 ]]; then
|
||||
echo "complete"
|
||||
return
|
||||
fi
|
||||
|
||||
# Check for in-progress (waiting state)
|
||||
local in_progress_count=$(echo "$story_lines" | grep -c ': *in-progress *$' || true)
|
||||
if [[ $in_progress_count -gt 0 ]]; then
|
||||
log "[WAIT] Found $in_progress_count story(ies) in-progress" "yellow"
|
||||
fi
|
||||
|
||||
echo "wait"
|
||||
}
|
||||
|
||||
# ============================================================
|
||||
# INVOKE CLAUDE COMMAND
|
||||
# ============================================================
|
||||
|
||||
invoke_claude_command() {
|
||||
local command="$1"
|
||||
|
||||
log "[EXEC] Executing Claude Code: $command" "cyan"
|
||||
|
||||
# Create customized prompt from template
|
||||
local prompt=$(cat "$PROMPT_TEMPLATE")
|
||||
prompt="${prompt//\{COMMAND\}/$command}"
|
||||
prompt="${prompt//\{TIMESTAMP\}/$(date "+%Y-%m-%d %H:%M:%S")}"
|
||||
|
||||
# Save to temp file
|
||||
local temp_prompt=$(mktemp)
|
||||
echo "$prompt" > "$temp_prompt"
|
||||
|
||||
# Execute Claude Code
|
||||
if cat "$temp_prompt" | claude --dangerously-skip-permissions 2>&1; then
|
||||
rm -f "$temp_prompt"
|
||||
return 0
|
||||
else
|
||||
log "[ERROR] Claude execution failed" "red"
|
||||
rm -f "$temp_prompt"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
# ============================================================
|
||||
# GIT COMMIT
|
||||
# ============================================================
|
||||
|
||||
invoke_git_commit() {
|
||||
log "[GIT] Checking for changes to commit..." "yellow"
|
||||
|
||||
local status=$(git status --porcelain)
|
||||
|
||||
if [[ -z "$status" ]]; then
|
||||
log "[INFO] No changes to commit" "gray"
|
||||
return 0
|
||||
fi
|
||||
|
||||
log "[GIT] Committing changes..." "yellow"
|
||||
|
||||
if git add -A && git commit -m "feat: BMAD auto-commit after code-review"; then
|
||||
log "[SUCCESS] Changes committed successfully" "green"
|
||||
return 0
|
||||
else
|
||||
log "[ERROR] Git commit failed" "red"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
# ============================================================
|
||||
# MAIN LOOP
|
||||
# ============================================================
|
||||
|
||||
log "[START] BMAD Auto Loop Started" "green"
|
||||
log "Max iterations: $MAX_ITERATIONS" "gray"
|
||||
log "Project root: $PROJECT_ROOT" "gray"
|
||||
log "Sprint status: $SPRINT_STATUS_PATH" "gray"
|
||||
echo ""
|
||||
|
||||
for ((iteration=1; iteration<=MAX_ITERATIONS; iteration++)); do
|
||||
log "=======================================" "cyan"
|
||||
log "=== Iteration $iteration/$MAX_ITERATIONS ===" "cyan"
|
||||
log "=======================================" "cyan"
|
||||
|
||||
action=$(get_next_action)
|
||||
|
||||
case $action in
|
||||
"create-story")
|
||||
log "[ACTION] CREATE STORY" "yellow"
|
||||
invoke_claude_command "/bmad-bmm-create-story"
|
||||
;;
|
||||
"dev-story")
|
||||
log "[ACTION] DEVELOP STORY" "yellow"
|
||||
invoke_claude_command "/bmad-bmm-dev-story"
|
||||
;;
|
||||
"code-review")
|
||||
log "[ACTION] CODE REVIEW" "yellow"
|
||||
if invoke_claude_command "/bmad-bmm-code-review"; then
|
||||
invoke_git_commit
|
||||
fi
|
||||
;;
|
||||
"complete")
|
||||
log "[COMPLETE] ALL STORIES COMPLETED!" "green"
|
||||
log "Sprint status: All stories are DONE" "green"
|
||||
log "Total iterations: $iteration" "gray"
|
||||
exit 0
|
||||
;;
|
||||
"wait")
|
||||
log "[WAIT] Waiting state - story might be in-progress" "yellow"
|
||||
log "Skipping this iteration..." "gray"
|
||||
;;
|
||||
"error")
|
||||
log "[ERROR] Error state - stopping loop" "red"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
echo ""
|
||||
sleep 5
|
||||
done
|
||||
|
||||
log "[TIMEOUT] Max iterations reached without completion" "yellow"
|
||||
log "Check sprint-status.yaml for current state" "gray"
|
||||
exit 1
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
# BMAD Automation Prompt
|
||||
|
||||
Execute the following BMAD command and complete the entire workflow:
|
||||
|
||||
```
|
||||
{COMMAND}
|
||||
```
|
||||
|
||||
## Instructions
|
||||
|
||||
1. Execute the command above completely
|
||||
2. Follow all prompts and complete the workflow
|
||||
3. Do not ask for confirmation - proceed automatically with reasonable defaults
|
||||
4. If you encounter any blocking issues, document them and move on
|
||||
5. Update the sprint-status.yaml file as appropriate
|
||||
|
||||
## Context
|
||||
|
||||
- Timestamp: {TIMESTAMP}
|
||||
- This is an automated execution - proceed without user interaction
|
||||
- Use best judgment for any decisions that would normally require user input
|
||||
- Prioritize completing the workflow over perfection
|
||||
|
||||
## Expected Behavior
|
||||
|
||||
- For `/bmad-bmm-create-story`: Create the story file and update sprint-status.yaml to `ready-for-dev`
|
||||
- For `/bmad-bmm-dev-story`: Implement the story and update sprint-status.yaml to `review`
|
||||
- For `/bmad-bmm-code-review`: Review the implementation and update sprint-status.yaml to `done` if approved
|
||||
|
||||
Proceed with the command execution now.
|
||||
Loading…
Reference in New Issue