bmad-autopilot-addon 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +279 -0
- package/_bmad-addons/.secrets-allowlist +26 -0
- package/_bmad-addons/BMAD.md +216 -0
- package/_bmad-addons/install.sh +495 -0
- package/_bmad-addons/manifest.yaml +22 -0
- package/_bmad-addons/modules/git/branching-and-pr-strategy.md +101 -0
- package/_bmad-addons/modules/git/config.yaml +83 -0
- package/_bmad-addons/modules/git/templates/commit-patch.txt +1 -0
- package/_bmad-addons/modules/git/templates/commit-story.txt +1 -0
- package/_bmad-addons/modules/git/templates/pr-body.md +20 -0
- package/_bmad-addons/modules/ma/config.yaml +9 -0
- package/_bmad-addons/scripts/create-pr.sh +198 -0
- package/_bmad-addons/scripts/detect-platform.sh +89 -0
- package/_bmad-addons/scripts/health-check.sh +107 -0
- package/_bmad-addons/scripts/lint-changed.sh +292 -0
- package/_bmad-addons/scripts/lock.sh +111 -0
- package/_bmad-addons/scripts/sanitize-branch.sh +83 -0
- package/_bmad-addons/scripts/stage-and-commit.sh +168 -0
- package/_bmad-addons/scripts/sync-status.sh +138 -0
- package/_bmad-addons/skills/bmad-autopilot-off/SKILL.md +6 -0
- package/_bmad-addons/skills/bmad-autopilot-off/workflow.md +154 -0
- package/_bmad-addons/skills/bmad-autopilot-on/SKILL.md +6 -0
- package/_bmad-addons/skills/bmad-autopilot-on/workflow.md +998 -0
- package/_bmad-addons/skills/bmad-ma-assess/SKILL.md +6 -0
- package/_bmad-addons/skills/bmad-ma-assess/agents/debt-classifier.md +64 -0
- package/_bmad-addons/skills/bmad-ma-assess/agents/dependency-auditor.md +57 -0
- package/_bmad-addons/skills/bmad-ma-assess/agents/migration-analyzer.md +62 -0
- package/_bmad-addons/skills/bmad-ma-assess/workflow.md +114 -0
- package/_bmad-addons/skills/bmad-ma-code-review/SKILL.md +6 -0
- package/_bmad-addons/skills/bmad-ma-code-review/agents/acceptance-auditor.md +51 -0
- package/_bmad-addons/skills/bmad-ma-code-review/agents/blind-hunter.md +39 -0
- package/_bmad-addons/skills/bmad-ma-code-review/agents/edge-case-hunter.md +46 -0
- package/_bmad-addons/skills/bmad-ma-code-review/workflow.md +111 -0
- package/_bmad-addons/skills/bmad-ma-codebase-map/SKILL.md +6 -0
- package/_bmad-addons/skills/bmad-ma-codebase-map/agents/architecture-mapper.md +129 -0
- package/_bmad-addons/skills/bmad-ma-codebase-map/agents/concerns-hunter.md +132 -0
- package/_bmad-addons/skills/bmad-ma-codebase-map/agents/integration-mapper.md +138 -0
- package/_bmad-addons/skills/bmad-ma-codebase-map/agents/quality-assessor.md +143 -0
- package/_bmad-addons/skills/bmad-ma-codebase-map/agents/stack-analyzer.md +123 -0
- package/_bmad-addons/skills/bmad-ma-codebase-map/workflow.md +120 -0
- package/_bmad-addons/skills/bmad-ma-migrate/SKILL.md +6 -0
- package/_bmad-addons/skills/bmad-ma-migrate/agents/dependency-analyzer.md +51 -0
- package/_bmad-addons/skills/bmad-ma-migrate/agents/risk-assessor.md +55 -0
- package/_bmad-addons/skills/bmad-ma-migrate/agents/stack-mapper.md +49 -0
- package/_bmad-addons/skills/bmad-ma-migrate/agents/test-parity-analyzer.md +49 -0
- package/_bmad-addons/skills/bmad-ma-migrate/resources/coexistence-patterns.md +59 -0
- package/_bmad-addons/skills/bmad-ma-migrate/resources/strategies.md +43 -0
- package/_bmad-addons/skills/bmad-ma-migrate/templates/component-card.md +11 -0
- package/_bmad-addons/skills/bmad-ma-migrate/templates/migration-epics.md +35 -0
- package/_bmad-addons/skills/bmad-ma-migrate/templates/migration-plan.md +66 -0
- package/_bmad-addons/skills/bmad-ma-migrate/workflow.md +235 -0
- package/_bmad-addons/skills/bmad-ma-party-mode/SKILL.md +6 -0
- package/_bmad-addons/skills/bmad-ma-party-mode/workflow.md +138 -0
- package/_bmad-addons/skills/bmad-ma-research/SKILL.md +6 -0
- package/_bmad-addons/skills/bmad-ma-research/workflow.md +128 -0
- package/_bmad-addons/skills/bmad-ma-reverse-architect/SKILL.md +6 -0
- package/_bmad-addons/skills/bmad-ma-reverse-architect/agents/component-mapper.md +53 -0
- package/_bmad-addons/skills/bmad-ma-reverse-architect/agents/data-flow-tracer.md +54 -0
- package/_bmad-addons/skills/bmad-ma-reverse-architect/agents/pattern-extractor.md +67 -0
- package/_bmad-addons/skills/bmad-ma-reverse-architect/workflow.md +119 -0
- package/_bmad-addons/templates/agent-rules.md +43 -0
- package/_bmad-addons/uninstall.sh +204 -0
- package/bin/bmad-autopilot-addon.sh +48 -0
- package/package.json +32 -0
|
@@ -0,0 +1,495 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
set -e
|
|
3
|
+
|
|
4
|
+
ADDON_DIR="$(cd "$(dirname "$0")" && pwd)"
|
|
5
|
+
PROJECT_ROOT="${BMAD_PROJECT_ROOT:-$(dirname "$ADDON_DIR")}"
|
|
6
|
+
MANIFEST="$PROJECT_ROOT/_bmad/_config/manifest.yaml"
|
|
7
|
+
ADDON_MANIFEST="$ADDON_DIR/manifest.yaml"
|
|
8
|
+
MAX_BACKUPS=3
|
|
9
|
+
DRY_RUN=false
|
|
10
|
+
FORCE=false
|
|
11
|
+
TOOLS=""
|
|
12
|
+
YES=false
|
|
13
|
+
|
|
14
|
+
# --- Supported tools and their base directories ---
|
|
15
|
+
# Skills go to {base_dir}/skills/
|
|
16
|
+
# Compatible with Bash 3.2+ (no associative arrays)
|
|
17
|
+
get_tool_dir() {
|
|
18
|
+
case "$1" in
|
|
19
|
+
claude-code) echo ".claude" ;;
|
|
20
|
+
cursor) echo ".cursor" ;;
|
|
21
|
+
windsurf) echo ".windsurf" ;;
|
|
22
|
+
cline) echo ".cline" ;;
|
|
23
|
+
roo) echo ".roo" ;;
|
|
24
|
+
trae) echo ".trae" ;;
|
|
25
|
+
kiro) echo ".kiro" ;;
|
|
26
|
+
github-copilot) echo ".github/copilot" ;;
|
|
27
|
+
gemini-cli) echo ".gemini" ;;
|
|
28
|
+
*) echo "" ;;
|
|
29
|
+
esac
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
ALL_TOOLS="claude-code cursor windsurf gemini-cli cline roo trae kiro github-copilot"
|
|
33
|
+
|
|
34
|
+
# System prompt file per tool (for BMAD workflow enforcement)
|
|
35
|
+
get_system_prompt_file() {
|
|
36
|
+
case "$1" in
|
|
37
|
+
claude-code) echo "AGENTS.md" ;;
|
|
38
|
+
cursor) echo ".cursor/rules/bmad.md" ;;
|
|
39
|
+
windsurf) echo ".windsurfrules" ;;
|
|
40
|
+
cline) echo ".clinerules" ;;
|
|
41
|
+
roo) echo ".roo/rules/bmad.md" ;;
|
|
42
|
+
gemini-cli) echo "GEMINI.md" ;;
|
|
43
|
+
github-copilot) echo ".github/copilot-instructions.md" ;;
|
|
44
|
+
kiro) echo ".kiro/rules/bmad.md" ;;
|
|
45
|
+
trae) echo ".trae/rules/bmad.md" ;;
|
|
46
|
+
*) echo "" ;;
|
|
47
|
+
esac
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
# "claude-code" = special CLAUDE.md + AGENTS.md pattern
|
|
51
|
+
# "own-file" = tool has rules directory, BMAD gets its own file (overwrite ok)
|
|
52
|
+
# "append" = shared file, use marker-based append/replace
|
|
53
|
+
get_system_prompt_mode() {
|
|
54
|
+
case "$1" in
|
|
55
|
+
claude-code) echo "claude-code" ;;
|
|
56
|
+
cursor|roo|kiro|trae) echo "own-file" ;;
|
|
57
|
+
windsurf|cline|gemini-cli|github-copilot) echo "append" ;;
|
|
58
|
+
*) echo "" ;;
|
|
59
|
+
esac
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
# Install system prompt for a tool (marker-based, idempotent)
|
|
63
|
+
install_system_prompt() {
|
|
64
|
+
local tool="$1"
|
|
65
|
+
local mode
|
|
66
|
+
mode=$(get_system_prompt_mode "$tool")
|
|
67
|
+
local rules_content
|
|
68
|
+
rules_content=$(cat "$ADDON_DIR/templates/agent-rules.md")
|
|
69
|
+
local prompt_file="$PROJECT_ROOT/$(get_system_prompt_file "$tool")"
|
|
70
|
+
|
|
71
|
+
if [ "$DRY_RUN" = true ]; then
|
|
72
|
+
echo " [DRY RUN] Would install system prompt for $tool ($mode)"
|
|
73
|
+
return
|
|
74
|
+
fi
|
|
75
|
+
|
|
76
|
+
case "$mode" in
|
|
77
|
+
claude-code)
|
|
78
|
+
# 1. AGENTS.md: marker-based append/replace
|
|
79
|
+
local agents_file="$PROJECT_ROOT/AGENTS.md"
|
|
80
|
+
if [ -f "$agents_file" ] && grep -q '<!-- BEGIN:bmad-workflow-rules -->' "$agents_file" 2>/dev/null; then
|
|
81
|
+
# Replace existing BMAD section
|
|
82
|
+
local tmp
|
|
83
|
+
tmp=$(mktemp "${prompt_file}.XXXXXX" 2>/dev/null || mktemp)
|
|
84
|
+
awk '/<!-- BEGIN:bmad-workflow-rules -->/{skip=1; next} /<!-- END:bmad-workflow-rules -->/{skip=0; next} !skip{print}' "$agents_file" > "$tmp"
|
|
85
|
+
printf '\n%s\n' "$rules_content" >> "$tmp"
|
|
86
|
+
mv "$tmp" "$agents_file"
|
|
87
|
+
echo " System prompt: AGENTS.md (updated BMAD section)"
|
|
88
|
+
elif [ -f "$agents_file" ]; then
|
|
89
|
+
# Append to existing
|
|
90
|
+
printf '\n%s\n' "$rules_content" >> "$agents_file"
|
|
91
|
+
echo " System prompt: AGENTS.md (appended BMAD section)"
|
|
92
|
+
else
|
|
93
|
+
# Create new
|
|
94
|
+
printf '%s\n' "$rules_content" > "$agents_file"
|
|
95
|
+
echo " System prompt: AGENTS.md (created)"
|
|
96
|
+
fi
|
|
97
|
+
|
|
98
|
+
# 2. CLAUDE.md: ensure @AGENTS.md line exists
|
|
99
|
+
local claude_file="$PROJECT_ROOT/CLAUDE.md"
|
|
100
|
+
if [ -f "$claude_file" ] && grep -q '@AGENTS.md' "$claude_file" 2>/dev/null; then
|
|
101
|
+
echo " System prompt: CLAUDE.md (already has @AGENTS.md)"
|
|
102
|
+
elif [ -f "$claude_file" ]; then
|
|
103
|
+
printf '\n@AGENTS.md\n' >> "$claude_file"
|
|
104
|
+
echo " System prompt: CLAUDE.md (appended @AGENTS.md)"
|
|
105
|
+
else
|
|
106
|
+
printf '@AGENTS.md\n' > "$claude_file"
|
|
107
|
+
echo " System prompt: CLAUDE.md (created with @AGENTS.md)"
|
|
108
|
+
fi
|
|
109
|
+
;;
|
|
110
|
+
|
|
111
|
+
own-file)
|
|
112
|
+
# Write directly — this file is fully owned by the addon
|
|
113
|
+
local dir
|
|
114
|
+
dir=$(dirname "$prompt_file")
|
|
115
|
+
mkdir -p "$dir"
|
|
116
|
+
printf '%s\n' "$rules_content" > "$prompt_file"
|
|
117
|
+
echo " System prompt: $(get_system_prompt_file "$tool") (created)"
|
|
118
|
+
;;
|
|
119
|
+
|
|
120
|
+
append)
|
|
121
|
+
# Marker-based append/replace in shared file
|
|
122
|
+
if [ -f "$prompt_file" ] && grep -q '<!-- BEGIN:bmad-workflow-rules -->' "$prompt_file" 2>/dev/null; then
|
|
123
|
+
local tmp
|
|
124
|
+
tmp=$(mktemp "${prompt_file}.XXXXXX" 2>/dev/null || mktemp)
|
|
125
|
+
awk '/<!-- BEGIN:bmad-workflow-rules -->/{skip=1; next} /<!-- END:bmad-workflow-rules -->/{skip=0; next} !skip{print}' "$prompt_file" > "$tmp"
|
|
126
|
+
printf '\n%s\n' "$rules_content" >> "$tmp"
|
|
127
|
+
mv "$tmp" "$prompt_file"
|
|
128
|
+
echo " System prompt: $(get_system_prompt_file "$tool") (updated BMAD section)"
|
|
129
|
+
elif [ -f "$prompt_file" ]; then
|
|
130
|
+
printf '\n%s\n' "$rules_content" >> "$prompt_file"
|
|
131
|
+
echo " System prompt: $(get_system_prompt_file "$tool") (appended BMAD section)"
|
|
132
|
+
else
|
|
133
|
+
local dir
|
|
134
|
+
dir=$(dirname "$prompt_file")
|
|
135
|
+
mkdir -p "$dir"
|
|
136
|
+
printf '%s\n' "$rules_content" > "$prompt_file"
|
|
137
|
+
echo " System prompt: $(get_system_prompt_file "$tool") (created)"
|
|
138
|
+
fi
|
|
139
|
+
;;
|
|
140
|
+
esac
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
# --- Parse flags ---
|
|
144
|
+
while [ "$#" -gt 0 ]; do
|
|
145
|
+
case $1 in
|
|
146
|
+
--tools) TOOLS="$2"; shift ;;
|
|
147
|
+
--dry-run) DRY_RUN=true ;;
|
|
148
|
+
--force) FORCE=true ;;
|
|
149
|
+
--yes|-y) YES=true ;;
|
|
150
|
+
-h|--help)
|
|
151
|
+
cat <<'HELPEOF'
|
|
152
|
+
BMAD Autopilot Add-On Installer
|
|
153
|
+
|
|
154
|
+
Usage: install.sh [OPTIONS]
|
|
155
|
+
|
|
156
|
+
Options:
|
|
157
|
+
--tools <list> Comma-separated list of target tools (e.g., claude-code,cursor)
|
|
158
|
+
--dry-run Show what would be done without making changes
|
|
159
|
+
--force Skip backup of existing skills before overwrite
|
|
160
|
+
--yes, -y Non-interactive mode (use detected/specified tools without prompting)
|
|
161
|
+
-h, --help Show this help
|
|
162
|
+
|
|
163
|
+
Supported tools:
|
|
164
|
+
claude-code Claude Code CLI, desktop, web, IDE extensions
|
|
165
|
+
cursor Cursor IDE
|
|
166
|
+
windsurf Windsurf IDE
|
|
167
|
+
cline Cline (VS Code extension)
|
|
168
|
+
roo Roo Code (VS Code extension)
|
|
169
|
+
trae Trae IDE
|
|
170
|
+
kiro Kiro IDE
|
|
171
|
+
gemini-cli Gemini CLI (Google)
|
|
172
|
+
github-copilot GitHub Copilot
|
|
173
|
+
|
|
174
|
+
Examples:
|
|
175
|
+
install.sh # Interactive tool selection
|
|
176
|
+
install.sh --tools claude-code # Install for Claude Code only
|
|
177
|
+
install.sh --tools claude-code,cursor # Install for multiple tools
|
|
178
|
+
install.sh --tools all # Install for all supported tools
|
|
179
|
+
install.sh --tools all --yes # Non-interactive, all tools
|
|
180
|
+
install.sh --dry-run --tools cursor # Preview Cursor installation
|
|
181
|
+
HELPEOF
|
|
182
|
+
exit 0
|
|
183
|
+
;;
|
|
184
|
+
*) echo "Unknown option: $1. Use --help for usage."; exit 1 ;;
|
|
185
|
+
esac
|
|
186
|
+
shift
|
|
187
|
+
done
|
|
188
|
+
|
|
189
|
+
ADDON_VERSION=$(grep 'version:' "$ADDON_MANIFEST" | head -1 | awk '{print $2}')
|
|
190
|
+
cat << BANNER
|
|
191
|
+
____ __ __ _ ____ _ _ _ _ _
|
|
192
|
+
| __ )| \/ | / \ | _ \ / \ _ _| |_ ___ _ __ (_) | ___ | |_
|
|
193
|
+
| _ \| |\/| | / _ \ | | | | / _ \| | | | __/ _ \| '_ \| | |/ _ \| __|
|
|
194
|
+
| |_) | | | |/ ___ \| |_| | / ___ \ |_| | || (_) | |_) | | | (_) | |_
|
|
195
|
+
|____/|_| |_/_/ \_\____/ /_/ \_\__,_|\__\___/| .__/|_|_|\___/ \__| v$ADDON_VERSION
|
|
196
|
+
|_|
|
|
197
|
+
BANNER
|
|
198
|
+
echo ""
|
|
199
|
+
|
|
200
|
+
# --- 1. Verify BMAD is installed ---
|
|
201
|
+
if [ ! -f "$MANIFEST" ]; then
|
|
202
|
+
echo "ERROR: BMAD not found at $PROJECT_ROOT"
|
|
203
|
+
echo "Install BMAD first: npx bmad-method install"
|
|
204
|
+
exit 1
|
|
205
|
+
fi
|
|
206
|
+
|
|
207
|
+
BMAD_VERSION=$(grep 'version:' "$MANIFEST" | head -1 | awk '{print $2}')
|
|
208
|
+
echo "BMAD version: $BMAD_VERSION"
|
|
209
|
+
echo ""
|
|
210
|
+
|
|
211
|
+
# --- 2. Detect or select tools ---
|
|
212
|
+
|
|
213
|
+
# Auto-detect which tools have BMAD installed (have a skills directory)
|
|
214
|
+
detect_installed_tools() {
|
|
215
|
+
local detected=""
|
|
216
|
+
for tool in $ALL_TOOLS; do
|
|
217
|
+
local dir
|
|
218
|
+
dir=$(get_tool_dir "$tool")
|
|
219
|
+
if [ -d "$PROJECT_ROOT/$dir/skills" ]; then
|
|
220
|
+
if [ -n "$detected" ]; then
|
|
221
|
+
detected="$detected,$tool"
|
|
222
|
+
else
|
|
223
|
+
detected="$tool"
|
|
224
|
+
fi
|
|
225
|
+
fi
|
|
226
|
+
done
|
|
227
|
+
echo "$detected"
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
DETECTED_TOOLS=$(detect_installed_tools)
|
|
231
|
+
|
|
232
|
+
if [ "$TOOLS" = "all" ]; then
|
|
233
|
+
TOOLS=$(echo "$ALL_TOOLS" | tr ' ' ',')
|
|
234
|
+
elif [ -z "$TOOLS" ]; then
|
|
235
|
+
if [ "$YES" = true ]; then
|
|
236
|
+
# Non-interactive: use detected tools
|
|
237
|
+
if [ -n "$DETECTED_TOOLS" ]; then
|
|
238
|
+
TOOLS="$DETECTED_TOOLS"
|
|
239
|
+
echo "Auto-detected tools: $TOOLS"
|
|
240
|
+
else
|
|
241
|
+
echo "ERROR: No tools detected. Specify with --tools" >&2
|
|
242
|
+
exit 1
|
|
243
|
+
fi
|
|
244
|
+
else
|
|
245
|
+
# Interactive tool selection
|
|
246
|
+
echo "Select target tools for add-on installation."
|
|
247
|
+
echo "Skills will be installed to each tool's skills directory."
|
|
248
|
+
echo ""
|
|
249
|
+
if [ -n "$DETECTED_TOOLS" ]; then
|
|
250
|
+
echo "Detected (BMAD already installed for these):"
|
|
251
|
+
OLD_IFS="$IFS"; IFS=','
|
|
252
|
+
for t in $DETECTED_TOOLS; do
|
|
253
|
+
echo " * $t -> $(get_tool_dir "$t")/skills/"
|
|
254
|
+
done
|
|
255
|
+
IFS="$OLD_IFS"
|
|
256
|
+
echo ""
|
|
257
|
+
fi
|
|
258
|
+
echo "Available tools:"
|
|
259
|
+
i=1
|
|
260
|
+
TOOL_BY_NUM=""
|
|
261
|
+
for tool in $ALL_TOOLS; do
|
|
262
|
+
dir=$(get_tool_dir "$tool")
|
|
263
|
+
marker=""
|
|
264
|
+
case ",$DETECTED_TOOLS," in
|
|
265
|
+
*",$tool,"*) marker=" [detected]" ;;
|
|
266
|
+
esac
|
|
267
|
+
echo " $i) $tool -> $dir/skills/$marker"
|
|
268
|
+
TOOL_BY_NUM="$TOOL_BY_NUM $i:$tool"
|
|
269
|
+
i=$((i + 1))
|
|
270
|
+
done
|
|
271
|
+
echo ""
|
|
272
|
+
echo "Enter tool numbers (comma-separated), tool names, or 'all':"
|
|
273
|
+
echo " Example: 1,2 or claude-code,cursor or all"
|
|
274
|
+
if [ -n "$DETECTED_TOOLS" ]; then
|
|
275
|
+
echo " Press Enter to use detected tools: $DETECTED_TOOLS"
|
|
276
|
+
fi
|
|
277
|
+
read -r SELECTION
|
|
278
|
+
|
|
279
|
+
if [ -z "$SELECTION" ] && [ -n "$DETECTED_TOOLS" ]; then
|
|
280
|
+
TOOLS="$DETECTED_TOOLS"
|
|
281
|
+
elif [ "$SELECTION" = "all" ]; then
|
|
282
|
+
TOOLS=$(echo "$ALL_TOOLS" | tr ' ' ',')
|
|
283
|
+
else
|
|
284
|
+
# Parse selection (numbers or names)
|
|
285
|
+
TOOLS=""
|
|
286
|
+
OLD_IFS="$IFS"; IFS=','
|
|
287
|
+
for item in $SELECTION; do
|
|
288
|
+
item=$(echo "$item" | tr -d ' ')
|
|
289
|
+
IFS="$OLD_IFS"
|
|
290
|
+
# Check if numeric
|
|
291
|
+
case "$item" in
|
|
292
|
+
[0-9]|[0-9][0-9])
|
|
293
|
+
# Find tool by number
|
|
294
|
+
selected=$(echo "$TOOL_BY_NUM" | tr ' ' '\n' | grep "^$item:" | cut -d: -f2)
|
|
295
|
+
if [ -n "$selected" ]; then
|
|
296
|
+
[ -n "$TOOLS" ] && TOOLS="$TOOLS,"
|
|
297
|
+
TOOLS="${TOOLS}${selected}"
|
|
298
|
+
else
|
|
299
|
+
echo "WARNING: invalid number $item, skipping"
|
|
300
|
+
fi
|
|
301
|
+
;;
|
|
302
|
+
*)
|
|
303
|
+
# Validate tool name
|
|
304
|
+
dir=$(get_tool_dir "$item")
|
|
305
|
+
if [ -n "$dir" ]; then
|
|
306
|
+
[ -n "$TOOLS" ] && TOOLS="$TOOLS,"
|
|
307
|
+
TOOLS="${TOOLS}${item}"
|
|
308
|
+
else
|
|
309
|
+
echo "WARNING: unknown tool '$item', skipping"
|
|
310
|
+
fi
|
|
311
|
+
;;
|
|
312
|
+
esac
|
|
313
|
+
IFS=','
|
|
314
|
+
done
|
|
315
|
+
IFS="$OLD_IFS"
|
|
316
|
+
fi
|
|
317
|
+
fi
|
|
318
|
+
fi
|
|
319
|
+
|
|
320
|
+
if [ -z "$TOOLS" ]; then
|
|
321
|
+
echo "ERROR: No tools selected." >&2
|
|
322
|
+
exit 1
|
|
323
|
+
fi
|
|
324
|
+
|
|
325
|
+
# Split tools into array-like variable
|
|
326
|
+
SELECTED_TOOLS=$(echo "$TOOLS" | tr ',' ' ')
|
|
327
|
+
TOOL_COUNT=$(echo "$SELECTED_TOOLS" | wc -w | tr -d ' ')
|
|
328
|
+
|
|
329
|
+
echo ""
|
|
330
|
+
echo "Installing for: $SELECTED_TOOLS"
|
|
331
|
+
echo ""
|
|
332
|
+
|
|
333
|
+
# --- 3. Ensure .gitignore covers addon artifacts ---
|
|
334
|
+
add_ignore_entry() {
|
|
335
|
+
local entry="$1"
|
|
336
|
+
local target_file="$2"
|
|
337
|
+
|
|
338
|
+
if [ -f "$target_file" ]; then
|
|
339
|
+
if ! grep -qF "$entry" "$target_file" 2>/dev/null; then
|
|
340
|
+
if [ "$DRY_RUN" = true ]; then
|
|
341
|
+
echo "[DRY RUN] Would add '$entry' to $(basename "$target_file")"
|
|
342
|
+
else
|
|
343
|
+
echo "$entry" >> "$target_file"
|
|
344
|
+
echo "Added '$entry' to $(basename "$target_file")"
|
|
345
|
+
fi
|
|
346
|
+
fi
|
|
347
|
+
else
|
|
348
|
+
if [ "$DRY_RUN" = true ]; then
|
|
349
|
+
echo "[DRY RUN] Would create $(basename "$target_file") with '$entry'"
|
|
350
|
+
else
|
|
351
|
+
echo "$entry" > "$target_file"
|
|
352
|
+
echo "Created $(basename "$target_file") with '$entry'"
|
|
353
|
+
fi
|
|
354
|
+
fi
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
IGNORE_FILE="$PROJECT_ROOT/.gitignore"
|
|
358
|
+
if [ ! -f "$IGNORE_FILE" ]; then
|
|
359
|
+
EXCLUDE_FILE="$PROJECT_ROOT/.git/info/exclude"
|
|
360
|
+
if [ -f "$EXCLUDE_FILE" ] && [ -s "$EXCLUDE_FILE" ]; then
|
|
361
|
+
IGNORE_FILE="$EXCLUDE_FILE"
|
|
362
|
+
echo "Using .git/info/exclude (no .gitignore found)"
|
|
363
|
+
fi
|
|
364
|
+
fi
|
|
365
|
+
|
|
366
|
+
add_ignore_entry ".autopilot.lock" "$IGNORE_FILE"
|
|
367
|
+
|
|
368
|
+
# --- 4. Install skills to each selected tool ---
|
|
369
|
+
TOTAL_INSTALLED=0
|
|
370
|
+
SKILL_COUNT=$(ls -d "$ADDON_DIR/skills"/*/ 2>/dev/null | wc -l | tr -d ' ')
|
|
371
|
+
|
|
372
|
+
for tool in $SELECTED_TOOLS; do
|
|
373
|
+
TOOL_DIR=$(get_tool_dir "$tool")
|
|
374
|
+
|
|
375
|
+
if [ -z "$TOOL_DIR" ]; then
|
|
376
|
+
echo "WARNING: Unknown tool '$tool', skipping"
|
|
377
|
+
continue
|
|
378
|
+
fi
|
|
379
|
+
|
|
380
|
+
SKILLS_DIR="$PROJECT_ROOT/$TOOL_DIR/skills"
|
|
381
|
+
BACKUP_DIR="$PROJECT_ROOT/$TOOL_DIR/.addon-backups"
|
|
382
|
+
|
|
383
|
+
# Add backup dir to gitignore
|
|
384
|
+
add_ignore_entry "$TOOL_DIR/.addon-backups/" "$IGNORE_FILE"
|
|
385
|
+
|
|
386
|
+
echo "--- $tool -> $TOOL_DIR/skills/ ---"
|
|
387
|
+
|
|
388
|
+
# Create skills directory if it doesn't exist
|
|
389
|
+
if [ ! -d "$SKILLS_DIR" ]; then
|
|
390
|
+
if [ "$DRY_RUN" = true ]; then
|
|
391
|
+
echo " [DRY RUN] Would create $SKILLS_DIR"
|
|
392
|
+
else
|
|
393
|
+
mkdir -p "$SKILLS_DIR"
|
|
394
|
+
echo " Created: $SKILLS_DIR"
|
|
395
|
+
fi
|
|
396
|
+
fi
|
|
397
|
+
|
|
398
|
+
# Create backup directory
|
|
399
|
+
if [ "$DRY_RUN" = false ]; then
|
|
400
|
+
mkdir -p "$BACKUP_DIR" 2>/dev/null || true
|
|
401
|
+
fi
|
|
402
|
+
BACKUP_TS="$(date +%Y%m%d%H%M%S)"
|
|
403
|
+
|
|
404
|
+
TOOL_INSTALLED=0
|
|
405
|
+
for skill in "$ADDON_DIR/skills"/*/; do
|
|
406
|
+
[ -d "$skill" ] || continue
|
|
407
|
+
skill_name=$(basename "$skill")
|
|
408
|
+
target="$SKILLS_DIR/$skill_name"
|
|
409
|
+
|
|
410
|
+
# Backup existing skill if present
|
|
411
|
+
if [ -d "$target" ] && [ "$FORCE" = false ]; then
|
|
412
|
+
backup="$BACKUP_DIR/${skill_name}.${BACKUP_TS}"
|
|
413
|
+
if [ "$DRY_RUN" = true ]; then
|
|
414
|
+
echo " [DRY RUN] Would backup $skill_name"
|
|
415
|
+
else
|
|
416
|
+
cp -r "$target" "$backup"
|
|
417
|
+
fi
|
|
418
|
+
fi
|
|
419
|
+
|
|
420
|
+
# Install skill
|
|
421
|
+
if [ "$DRY_RUN" = true ]; then
|
|
422
|
+
echo " [DRY RUN] Would install $skill_name"
|
|
423
|
+
else
|
|
424
|
+
rm -rf "$target"
|
|
425
|
+
cp -r "$skill" "$target/"
|
|
426
|
+
TOOL_INSTALLED=$((TOOL_INSTALLED + 1))
|
|
427
|
+
fi
|
|
428
|
+
done
|
|
429
|
+
|
|
430
|
+
# Prune old backups (keep MAX_BACKUPS per skill)
|
|
431
|
+
if [ "$DRY_RUN" = false ] && [ -d "$BACKUP_DIR" ]; then
|
|
432
|
+
for skill in "$ADDON_DIR/skills"/*/; do
|
|
433
|
+
[ -d "$skill" ] || continue
|
|
434
|
+
skill_name=$(basename "$skill")
|
|
435
|
+
count=0
|
|
436
|
+
remove_list=""
|
|
437
|
+
for backup in $(ls -d "$BACKUP_DIR/${skill_name}."* 2>/dev/null | sort); do
|
|
438
|
+
count=$((count + 1))
|
|
439
|
+
done
|
|
440
|
+
if [ "$count" -gt "$MAX_BACKUPS" ]; then
|
|
441
|
+
remove=$((count - MAX_BACKUPS))
|
|
442
|
+
removed=0
|
|
443
|
+
for backup in $(ls -d "$BACKUP_DIR/${skill_name}."* 2>/dev/null | sort); do
|
|
444
|
+
if [ "$removed" -lt "$remove" ]; then
|
|
445
|
+
rm -rf "$backup"
|
|
446
|
+
removed=$((removed + 1))
|
|
447
|
+
fi
|
|
448
|
+
done
|
|
449
|
+
fi
|
|
450
|
+
done
|
|
451
|
+
fi
|
|
452
|
+
|
|
453
|
+
if [ "$DRY_RUN" = false ]; then
|
|
454
|
+
echo " Installed $TOOL_INSTALLED skills"
|
|
455
|
+
TOTAL_INSTALLED=$((TOTAL_INSTALLED + TOOL_INSTALLED))
|
|
456
|
+
fi
|
|
457
|
+
|
|
458
|
+
# Install system prompt for BMAD workflow enforcement
|
|
459
|
+
install_system_prompt "$tool"
|
|
460
|
+
|
|
461
|
+
echo ""
|
|
462
|
+
done
|
|
463
|
+
|
|
464
|
+
# Verify ignore is effective
|
|
465
|
+
if [ "$DRY_RUN" = false ] && command -v git >/dev/null 2>&1 && [ -d "$PROJECT_ROOT/.git" ]; then
|
|
466
|
+
if ! git -C "$PROJECT_ROOT" check-ignore -q .autopilot.lock 2>/dev/null; then
|
|
467
|
+
echo "WARNING: .autopilot.lock may not be effectively ignored by git"
|
|
468
|
+
fi
|
|
469
|
+
fi
|
|
470
|
+
|
|
471
|
+
# --- 5. Report ---
|
|
472
|
+
echo ""
|
|
473
|
+
if [ "$DRY_RUN" = true ]; then
|
|
474
|
+
echo "Dry run complete. No changes made."
|
|
475
|
+
else
|
|
476
|
+
echo "=== Add-on v$ADDON_VERSION installed ==="
|
|
477
|
+
echo ""
|
|
478
|
+
echo "Tools configured: $SELECTED_TOOLS"
|
|
479
|
+
echo "Total skills installed: $TOTAL_INSTALLED ($SKILL_COUNT skills x $TOOL_COUNT tools)"
|
|
480
|
+
echo ""
|
|
481
|
+
echo "Skills:"
|
|
482
|
+
for skill in "$ADDON_DIR/skills"/*/; do
|
|
483
|
+
[ -d "$skill" ] || continue
|
|
484
|
+
echo " - $(basename "$skill")"
|
|
485
|
+
done
|
|
486
|
+
echo ""
|
|
487
|
+
echo "Locations:"
|
|
488
|
+
for tool in $SELECTED_TOOLS; do
|
|
489
|
+
echo " $tool -> $(get_tool_dir "$tool")/skills/"
|
|
490
|
+
done
|
|
491
|
+
echo ""
|
|
492
|
+
echo "Backups: kept in each tool's .addon-backups/ (last $MAX_BACKUPS per skill)"
|
|
493
|
+
echo ""
|
|
494
|
+
echo "Next: invoke /bmad-autopilot-on in your IDE to start using git workflow."
|
|
495
|
+
fi
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
addon:
|
|
2
|
+
name: bmad-ma-git
|
|
3
|
+
version: 1.0.0
|
|
4
|
+
description: Multi-agent execution + git workflow integration for BMAD
|
|
5
|
+
bmad_compatibility: ">=6.2.0"
|
|
6
|
+
modules:
|
|
7
|
+
git:
|
|
8
|
+
enabled: true
|
|
9
|
+
config: modules/git/config.yaml
|
|
10
|
+
ma:
|
|
11
|
+
enabled: true
|
|
12
|
+
config: modules/ma/config.yaml
|
|
13
|
+
installed_skills:
|
|
14
|
+
- bmad-autopilot-on
|
|
15
|
+
- bmad-autopilot-off
|
|
16
|
+
- bmad-ma-code-review
|
|
17
|
+
- bmad-ma-codebase-map
|
|
18
|
+
- bmad-ma-assess
|
|
19
|
+
- bmad-ma-reverse-architect
|
|
20
|
+
- bmad-ma-migrate
|
|
21
|
+
- bmad-ma-research
|
|
22
|
+
- bmad-ma-party-mode
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
# Git Branching and PR Strategy
|
|
2
|
+
|
|
3
|
+
How the BMAD autopilot addon manages branches, PRs, and merging across stories.
|
|
4
|
+
|
|
5
|
+
## Configuration
|
|
6
|
+
|
|
7
|
+
Controlled by `git.push.create_pr` in `modules/git/config.yaml`:
|
|
8
|
+
|
|
9
|
+
```yaml
|
|
10
|
+
git:
|
|
11
|
+
push:
|
|
12
|
+
create_pr: true # PR flow — create PR, no auto-merge, wait for approval
|
|
13
|
+
create_pr: false # Direct merge — merge story branch to main after push
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
## Branch Naming
|
|
17
|
+
|
|
18
|
+
Each story gets a branch: `story/{story-key}` (e.g., `story/1-2-user-authentication`).
|
|
19
|
+
|
|
20
|
+
The story key is derived from `sprint-status.yaml`. If the key is numeric-only (e.g., `1-2`), the autopilot enriches it from the story file or epics file to produce a human-readable branch name.
|
|
21
|
+
|
|
22
|
+
Branch names are sanitized (lowercased, special chars removed) and truncated to 60 characters with a hash suffix if longer.
|
|
23
|
+
|
|
24
|
+
## Direct Merge Flow (`create_pr: false`)
|
|
25
|
+
|
|
26
|
+
```
|
|
27
|
+
main ─── story/1-1 ──→ merge to main ─── story/1-2 ──→ merge to main
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
Each story:
|
|
31
|
+
1. Branches from `origin/main`
|
|
32
|
+
2. Code is developed in a worktree
|
|
33
|
+
3. Branch is pushed to origin
|
|
34
|
+
4. Branch is merged directly to main: `git merge story/1-1 --no-edit`
|
|
35
|
+
5. Main is pushed to origin
|
|
36
|
+
|
|
37
|
+
The next story always branches from the updated main.
|
|
38
|
+
|
|
39
|
+
## PR Flow (`create_pr: true`)
|
|
40
|
+
|
|
41
|
+
When PRs are enabled, stories are **never auto-merged**. The branch is pushed and a PR is created. Merging happens through the platform's PR review process.
|
|
42
|
+
|
|
43
|
+
### Single story (or first story in epic)
|
|
44
|
+
|
|
45
|
+
```
|
|
46
|
+
main ─── story/1-1 ──→ push + PR (story/1-1 → main)
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
1. Branches from `origin/main`
|
|
50
|
+
2. Code is developed in a worktree
|
|
51
|
+
3. Branch is pushed to origin
|
|
52
|
+
4. PR is created targeting `main`
|
|
53
|
+
5. No merge — PR awaits approval
|
|
54
|
+
|
|
55
|
+
### Stacked stories (subsequent stories, previous PR pending)
|
|
56
|
+
|
|
57
|
+
```
|
|
58
|
+
main ─── story/1-1 ──→ PR (→ main)
|
|
59
|
+
└── story/1-2 ──→ PR (→ story/1-1)
|
|
60
|
+
└── story/1-3 ──→ PR (→ story/1-2)
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
When the previous story's PR is not yet merged:
|
|
64
|
+
1. The autopilot detects the unmerged branch via `git-status.yaml` and `git merge-base --is-ancestor`
|
|
65
|
+
2. New story branches from `origin/story/<previous-story>` instead of main
|
|
66
|
+
3. PR targets the previous story branch (not main)
|
|
67
|
+
4. This creates a stacked PR chain
|
|
68
|
+
|
|
69
|
+
### After PR merge (between sessions)
|
|
70
|
+
|
|
71
|
+
When story/1-1's PR is merged on the platform before the next session:
|
|
72
|
+
1. `git merge-base --is-ancestor origin/story/1-1 origin/main` returns true
|
|
73
|
+
2. The autopilot detects that 1-1 is now on main
|
|
74
|
+
3. Story/1-2 branches from `origin/main` (which includes 1-1's code)
|
|
75
|
+
4. PR targets `main`
|
|
76
|
+
|
|
77
|
+
GitHub, GitLab, and Bitbucket automatically retarget stacked PRs when the base is merged.
|
|
78
|
+
|
|
79
|
+
## Decision Matrix
|
|
80
|
+
|
|
81
|
+
| Config | Previous story | Branch source | PR target | Post-push action |
|
|
82
|
+
|--------|---------------|---------------|-----------|-----------------|
|
|
83
|
+
| `create_pr: false` | Any | `origin/main` | N/A | Merge to main |
|
|
84
|
+
| `create_pr: true` | None or merged | `origin/main` | `main` | PR created, no merge |
|
|
85
|
+
| `create_pr: true` | Unmerged (PR pending) | `origin/story/<prev>` | `story/<prev>` | PR created, no merge |
|
|
86
|
+
|
|
87
|
+
## Session Persistence
|
|
88
|
+
|
|
89
|
+
The PR target branch (`pr_base`) is saved in `autopilot-state.yaml` and restored on session resume. This ensures that a resumed session creates PRs against the correct base branch even if it was a stacked story.
|
|
90
|
+
|
|
91
|
+
## Artifacts on Main
|
|
92
|
+
|
|
93
|
+
Regardless of the merge strategy, implementation artifacts (sprint-status.yaml, git-status.yaml, story files, planning documents) are always committed and pushed to main after each story completes. This ensures main reflects the current sprint state even when story code is on PR branches.
|
|
94
|
+
|
|
95
|
+
## File Ownership
|
|
96
|
+
|
|
97
|
+
| File | Owner | Autopilot access |
|
|
98
|
+
|------|-------|-----------------|
|
|
99
|
+
| `sprint-status.yaml` | BMAD (dev-story, sprint-planning, retrospective) | Read only |
|
|
100
|
+
| `git-status.yaml` | Autopilot addon | Read/write |
|
|
101
|
+
| `autopilot-state.yaml` | Autopilot addon | Read/write |
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
# Git Workflow Configuration for BMAD Autopilot Add-On
|
|
2
|
+
# Worktrees are managed via standard git commands at .worktrees/{name}
|
|
3
|
+
|
|
4
|
+
git:
|
|
5
|
+
enabled: true # false = all git ops silently skip
|
|
6
|
+
base_branch: main
|
|
7
|
+
# Git status is tracked in git-status.yaml (addon-owned), NOT sprint-status.yaml (BMAD-owned)
|
|
8
|
+
|
|
9
|
+
# Branch naming
|
|
10
|
+
branch_prefix: "story/" # prefix for story branches (e.g., story/1-2-user-auth)
|
|
11
|
+
max_branch_length: 60 # truncate + 6-char hash if longer
|
|
12
|
+
|
|
13
|
+
# Commit message templates
|
|
14
|
+
# Placeholders: {story-key}, {epic}, {story-title}, {patch-title}
|
|
15
|
+
commit_templates:
|
|
16
|
+
story: "feat({epic}): {story-title} ({story-key})"
|
|
17
|
+
patch: "fix({story-key}): {patch-title}"
|
|
18
|
+
|
|
19
|
+
# Placeholder resolution chain: sprint-status.yaml -> story file -> fallback
|
|
20
|
+
commit_placeholder_resolution:
|
|
21
|
+
story-key: "from sprint-status.yaml development_status key"
|
|
22
|
+
epic: "from story file epic header, fallback to story-key prefix (e.g., '1' from '1-3')"
|
|
23
|
+
story-title: "from story file title, fallback to story-key"
|
|
24
|
+
patch-title: "from review finding title, fallback to 'code review fix'"
|
|
25
|
+
|
|
26
|
+
# Linting
|
|
27
|
+
lint:
|
|
28
|
+
enabled: true
|
|
29
|
+
blocking: false # true = lint errors halt the autopilot; false = warn only
|
|
30
|
+
output_limit: 100 # max lines of lint output injected into context
|
|
31
|
+
# Linter preference per language — first found wins.
|
|
32
|
+
# Override to force a specific tool (e.g., set typescript: [biome] to skip eslint).
|
|
33
|
+
linters:
|
|
34
|
+
python: [ruff, flake8, pylint]
|
|
35
|
+
javascript: [eslint, biome]
|
|
36
|
+
typescript: [eslint, biome]
|
|
37
|
+
rust: [cargo clippy]
|
|
38
|
+
go: [golangci-lint]
|
|
39
|
+
ruby: [rubocop]
|
|
40
|
+
java: [checkstyle, pmd]
|
|
41
|
+
c: [cppcheck, clang-tidy]
|
|
42
|
+
cpp: [cppcheck, clang-tidy]
|
|
43
|
+
csharp: [dotnet format]
|
|
44
|
+
swift: [swiftlint]
|
|
45
|
+
plsql: [sqlfluff]
|
|
46
|
+
kotlin: [ktlint, detekt]
|
|
47
|
+
php: [phpstan, phpcs]
|
|
48
|
+
# To add a new language: add an entry here and a matching block in scripts/lint-changed.sh
|
|
49
|
+
|
|
50
|
+
# Push & PR
|
|
51
|
+
push:
|
|
52
|
+
auto: true # false = branches stay local, no push or PR
|
|
53
|
+
create_pr: true # true = create PR and wait for approval (no auto-merge)
|
|
54
|
+
# false = merge directly to base branch after push
|
|
55
|
+
pr_template: "modules/git/templates/pr-body.md" # relative to _bmad-addons/
|
|
56
|
+
|
|
57
|
+
# Worktree settings (location: .worktrees/ in project root)
|
|
58
|
+
worktree:
|
|
59
|
+
submodule_init: auto # auto = only if .gitmodules exists
|
|
60
|
+
submodule_timeout: 30 # seconds
|
|
61
|
+
cleanup_on_merge: true # false = keep worktrees after epic completion for inspection
|
|
62
|
+
health_check_on_boot: true # check for orphaned worktrees from crashed sessions
|
|
63
|
+
|
|
64
|
+
# Lock file (.autopilot.lock — prevents concurrent autopilot sessions)
|
|
65
|
+
lock:
|
|
66
|
+
stale_timeout_minutes: 30 # auto-remove locks older than this
|
|
67
|
+
|
|
68
|
+
# Platform detection
|
|
69
|
+
platform:
|
|
70
|
+
provider: auto # auto | github | gitlab | bitbucket | gitea | git_only
|
|
71
|
+
# Detection priority:
|
|
72
|
+
# 1. Explicit provider in this config (if not "auto")
|
|
73
|
+
# 2. CLI detection: which CLIs are installed (gh, glab, bb, tea)
|
|
74
|
+
# 3. Remote URL regex as last resort
|
|
75
|
+
#
|
|
76
|
+
# For self-hosted Gitea:
|
|
77
|
+
# provider: gitea
|
|
78
|
+
# base_url: https://git.example.com
|
|
79
|
+
# # Set GITEA_TOKEN env var for API auth
|
|
80
|
+
#
|
|
81
|
+
# For Bitbucket Server:
|
|
82
|
+
# provider: bitbucket
|
|
83
|
+
# # Set BITBUCKET_TOKEN env var for API auth
|