@ikunin/sprintpilot 1.0.4 → 1.0.5

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.
@@ -1,6 +1,6 @@
1
1
  addon:
2
2
  name: sprintpilot
3
- version: 1.0.4
3
+ version: 1.0.5
4
4
  description: Sprintpilot — autopilot and multi-agent addon for BMad Method (git workflow, parallel agents, autonomous story execution)
5
5
  bmad_compatibility: ">=6.2.0"
6
6
  modules:
@@ -8,23 +8,19 @@ You do NOT hardcode the workflow sequence. After each completed skill, read its
8
8
 
9
9
  **Git integration** is additive. If `_Sprintpilot/manifest.yaml` doesn't exist or `git.enabled: false`, all git operations are silently skipped and this workflow behaves identically to the stock autopilot.
10
10
 
11
- ### Shell portability (IMPORTANT)
11
+ ### Shell portability
12
12
 
13
- Sprintpilot runs under any LLM CLI (Claude Code, Gemini CLI, Cursor, etc.) on any OS. The shell that executes commands may be **bash, zsh, PowerShell, or cmd** depending on platform and CLI. Shell-specific idioms will fail silently when the wrong shell is used.
13
+ The executing shell may be bash, zsh, PowerShell, or cmd translate bash idioms as needed:
14
14
 
15
- **When you encounter bash-style idioms below, translate them to your shell.** The table applies to **external commands** (like `git`); cmdlets have slightly different conventions.
15
+ | Bash | PowerShell |
16
+ |---|---|
17
+ | `A && B` | `A; if ($LASTEXITCODE -eq 0) { B }` |
18
+ | `A \|\| true` | `A; $LASTEXITCODE = 0` |
19
+ | `2>/dev/null` | `2>$null` |
20
+ | `rm -rf <dir>` | `Remove-Item -Recurse -Force <dir>` |
21
+ | `if [ -f X ]; then ... fi` | `if (Test-Path -PathType Leaf X) { ... }` |
16
22
 
17
- | Bash idiom | PowerShell equivalent | Meaning |
18
- |---|---|---|
19
- | `A && B` | `A; if ($LASTEXITCODE -eq 0) { B }` (or separate commands, guarding B manually) | Run B only if A succeeded |
20
- | `A \|\| true` | `A; $LASTEXITCODE = 0` (or `try { A } catch {}` for cmdlets) | Run A, ignore failures |
21
- | `2>/dev/null` | `2>$null` | Suppress stderr |
22
- | `rm -rf <dir>` | `Remove-Item -Recurse -Force <dir>` | Recursive delete |
23
- | `if [ -f X ]; then ... fi` | `if (Test-Path -PathType Leaf X) { ... }` | File-exists check (regular file, not dir) |
24
-
25
- **Safer:** when in doubt, use the cross-platform Node helpers under `_Sprintpilot/scripts/`. For ad-hoc file ops, invoke Node inline: `node -e "require('fs').rmSync('<path>', {recursive: true, force: true})"`.
26
-
27
- If a step below uses `&&` to chain "run B only on A's success", and you cannot express that in one line, **run the commands separately and STOP if any step fails** — do not proceed past a failed step.
23
+ For cross-platform file ops prefer the Node helpers under `_Sprintpilot/scripts/`, or inline: `node -e "require('fs').rmSync('<path>', {recursive: true, force: true})"`. When a step chains commands with `&&` and you cannot express it in one line, run them separately and STOP on any failure.
28
24
 
29
25
  ---
30
26
 
@@ -75,55 +71,20 @@ For everything else: decide, document briefly, continue.
75
71
 
76
72
  ## DECISION LOGGING
77
73
 
78
- Every non-trivial decision made during autopilot execution MUST be logged to `{decision_log_file}`. This creates an audit trail the user reviews at the end of each session.
79
-
80
- ### When to log
81
-
82
- Log a decision whenever you:
83
- - Choose an architecture pattern, data structure, or design approach (`architecture`)
84
- - Select a test strategy or skip a test category (`test-strategy`)
85
- - Add, remove, or substitute a dependency (`dependency`)
86
- - Dismiss a code review finding (`review-triage`)
87
- - Accept and apply a code review finding (`review-accept`)
88
- - Recover from a HALT condition (`halt-recovery`)
89
- - Implement something not explicitly in the story spec (`scope`)
90
- - Apply a workaround for a tool limitation or false positive (`workaround`)
91
-
92
- Do NOT log routine actions (running tests, staging files, creating branches).
93
-
94
- ### File format
95
-
96
- Initialize `{decision_log_file}` on first decision (if it does not exist):
97
-
98
- ```yaml
99
- generated: {current_date}
100
- last_updated: {current_datetime}
101
-
102
- decisions: []
103
- ```
74
+ Log every non-trivial decision to `{decision_log_file}` (skip routine actions running tests, staging files, creating branches). Create the file on first decision; update `last_updated` on every append.
104
75
 
105
- Append each decision as a new entry:
76
+ **Categories:** `architecture`, `test-strategy`, `dependency`, `review-triage` (dismissed finding), `review-accept` (applied fix), `halt-recovery`, `scope` (outside story spec), `workaround`.
77
+ **Impact:** `low` (reversible/cosmetic), `medium` (affects one component), `high` (cross-cutting or deviates from spec).
78
+ **Phase format:** `{skill}:{sub_phase}` — e.g. `dev-story:RED`, `code-review:triage`, `autopilot:routing`.
106
79
 
80
+ **File schema:**
107
81
  ```yaml
108
- - id: {auto_increment}
109
- timestamp: "{current_datetime_iso8601}"
110
- story: "{current_story or sprint-level}"
111
- phase: "{skill}:{sub_phase}"
112
- category: {architecture|test-strategy|dependency|review-triage|review-accept|halt-recovery|scope|workaround}
113
- decision: "{what was decided — one line}"
114
- rationale: "{why — one line}"
115
- impact: {low|medium|high}
82
+ generated: {date}
83
+ last_updated: {datetime}
84
+ decisions:
85
+ - { id, timestamp, story, phase, category, decision, rationale, impact }
116
86
  ```
117
87
 
118
- **Phase format:** `dev-story:RED`, `dev-story:GREEN`, `code-review:triage`, `code-review:patch`, `autopilot:init`, `autopilot:routing`, etc.
119
-
120
- **Impact levels:**
121
- - `low` — easily reversible, cosmetic, or standard practice
122
- - `medium` — affects behavior but contained to one story/component
123
- - `high` — cross-cutting, hard to reverse, or deviates from spec
124
-
125
- Always update `last_updated` when appending.
126
-
127
88
  ---
128
89
 
129
90
  ## SKILL AUTOMATABLE REFERENCE
@@ -156,6 +117,8 @@ Resolve:
156
117
  - `project_root` = absolute path of current working directory (store for later use)
157
118
  - `session_story_limit` is loaded below from `modules/autopilot/config.yaml` (default: 3)
158
119
 
120
+ **`{state_file}` schema** (referenced as `STATE_FIELDS` below): `last_updated`, `current_story`, `current_bmad_step`, `completed_skill`, `next_skill`, `session_stories_done`, `stories_remaining`, `git_enabled`, `platform`, `in_worktree`, `pr_base`. Always update `last_updated` on every write.
121
+
159
122
  ### Git integration bootstrap
160
123
 
161
124
  <action>Check if `{project-root}/_Sprintpilot/manifest.yaml` exists</action>
@@ -200,6 +163,17 @@ Resolve:
200
163
  <action>STOP</action>
201
164
  </check>
202
165
 
166
+ <action>**Check for `origin` remote** — run: `git remote get-url origin`
167
+ If the command fails (exit code != 0), no `origin` remote is configured. Set `{{has_origin}}` = false.
168
+ Otherwise set `{{has_origin}}` = true.
169
+ </action>
170
+ <check if="{{has_origin}} is false">
171
+ <action>Log: "WARN: no `origin` remote configured — running in local-only mode. Remote operations (fetch, push, PR, branch reconciliation) will be skipped. Add a remote later with: `git remote add origin <url>`"</action>
172
+ <action>Set `{{push_auto}}` = false</action>
173
+ <action>Set `{{create_pr}}` = false</action>
174
+ <action>Set `{{platform}}` = "git_only"</action>
175
+ </check>
176
+
203
177
  <action>**Lock file** — run: `node {{project_root}}/_Sprintpilot/scripts/lock.js acquire`
204
178
  Output will be one of:
205
179
  - `ACQUIRED:<session-id>` → proceed
@@ -235,7 +209,8 @@ Resolve:
235
209
  </action>
236
210
 
237
211
  <action>**Branch reconciliation** — detect pushed-but-unmerged story branches.
238
- Run as separate commands — **if `git fetch origin` fails (network/auth), STOP branch reconciliation and log a warning; do not operate on stale local refs**:
212
+ Skip this entire section if `{{has_origin}}` is false (no remote nothing to reconcile).
213
+ Run as separate commands — **if `git fetch origin` fails (no remote/network/auth), STOP branch reconciliation and log a warning; do not operate on stale local refs**:
239
214
  1. `git fetch origin`
240
215
  2. `git branch -r --list "origin/{{branch_prefix}}*"`
241
216
  For each remote branch:
@@ -319,30 +294,14 @@ Resolve:
319
294
  - Update `{state_file}` with reconciled values
320
295
  </action>
321
296
 
322
- <!-- Resume from a `retrospective_mode: stop` pause.
323
- The user was told to run /bmad-retrospective interactively. If they
324
- did, the epic is now `done` in {status_file} (or a retrospective
325
- artifact exists). Otherwise, re-issue the instructions and halt. -->
297
+ <!-- Resume from a `retrospective_mode: stop` pause. -->
326
298
  <check if="{state_file}.paused_at is epic-complete-awaiting-retrospective">
327
- <action>Set `{{paused_epic_id}}` from `{state_file}.paused_epic_id`</action>
328
- <action>Check whether epic `{{paused_epic_id}}` is now `done` in `{status_file}` OR an artifact exists at `{implementation_artifacts}/retrospectives/epic-{{paused_epic_id}}-*.md`</action>
299
+ <action>Set `{{paused_epic_id}}` from `{state_file}.paused_epic_id`. Check if epic `{{paused_epic_id}}` is `done` in `{status_file}` OR an artifact exists at `{implementation_artifacts}/retrospectives/epic-{{paused_epic_id}}-*.md`.</action>
329
300
  <check if="epic is done OR retrospective artifact exists">
330
- <action>Clear `paused_at`, `paused_epic_id`, and `next_action` from `{state_file}`</action>
331
- <action>Log: "Epic {{paused_epic_id}} retrospective detected — resuming autopilot"</action>
301
+ <action>Clear `paused_at`, `paused_epic_id`, `next_action` from `{state_file}`. Log: "Epic {{paused_epic_id}} retrospective detected — resuming autopilot".</action>
332
302
  </check>
333
303
  <check if="epic is NOT done AND no retrospective artifact">
334
- <action>Report:
335
- ```
336
- Autopilot still paused — epic {{paused_epic_id}} retrospective not yet done.
337
-
338
- Run `/bmad-retrospective` interactively for epic {{paused_epic_id}},
339
- then re-run `/sprint-autopilot-on` to continue.
340
-
341
- (To bypass, edit _Sprintpilot/modules/autopilot/config.yaml and set
342
- retrospective_mode to `auto` or `skip`.)
343
- ```
344
- </action>
345
- <action>STOP</action>
304
+ <action>Report: "Autopilot still paused — epic {{paused_epic_id}} retrospective not yet done. Run `/bmad-retrospective` interactively, then re-run `/sprint-autopilot-on`. (To bypass: set `retrospective_mode` to `auto` or `skip` in `_Sprintpilot/modules/autopilot/config.yaml`.)" Then STOP.</action>
346
305
  </check>
347
306
  </check>
348
307
 
@@ -350,22 +309,11 @@ Resolve:
350
309
  </check>
351
310
 
352
311
  <check if="state_file does NOT exist">
353
- <action>Check if `{status_file}` exists if not, invoke `bmad-sprint-planning` first</action>
354
-
355
- <check if="{{git_enabled}} AND status_file did not exist (sprint-planning just ran)">
356
- <action>Run `git fetch origin` to ensure remote refs are current</action>
357
- <action>Initialize `{git_status_file}` with git_integration block:
358
- ```yaml
359
- # Sprintpilot — Git Status
360
- git_integration:
361
- enabled: true
362
- base_branch: {git.base_branch from config}
363
- platform: {{platform}}
364
-
365
- stories:
366
- ```
367
- Note: this is the addon's own file — NEVER write git fields to sprint-status.yaml.
368
- </action>
312
+ <action>Check if `{status_file}` exists. If NOT, do NOT jump to `bmad-sprint-planning` (Phase 4 skill, requires Phase 1–3 artifacts). Invoke `bmad-help` — "No sprint-status.yaml found. What is the current phase and which skill should run first?" — and set `{{next_skill}}` from its response. Expected routing: no PRD → `bmad-create-prd` (BLOCKER); PRD → `bmad-create-architecture`; architecture → `bmad-create-epics-and-stories`; epics → `bmad-sprint-planning`.</action>
313
+
314
+ <check if="{{git_enabled}} AND status_file did not exist AND {{next_skill}} is bmad-sprint-planning (planning just completed earlier in this flow)">
315
+ <action>Run `git fetch origin` warn + skip on failure (no remote/auth/network), do not abort bootstrap.</action>
316
+ <action>Initialize `{git_status_file}` (addon-owned — NEVER write git fields to sprint-status.yaml) with: `git_integration: { enabled: true, base_branch: <from config>, platform: {{platform}} }` and empty `stories:`.</action>
369
317
  </check>
370
318
 
371
319
  <action>Read `{status_file}` — find all stories not yet `done`</action>
@@ -376,21 +324,7 @@ Resolve:
376
324
  - `{{session_stories_done}}` = 0
377
325
  </action>
378
326
  <action>Create master task: "Sprintpilot — Full Sprint Execution" → `in_progress`</action>
379
- <action>Write initial `{state_file}`:
380
- ```yaml
381
- last_updated: {current_datetime}
382
- current_story: null
383
- current_bmad_step: null
384
- completed_skill: bmad-help
385
- next_skill: {{next_skill}}
386
- session_stories_done: 0
387
- stories_remaining: [list from sprint-status]
388
- git_enabled: {{git_enabled}}
389
- platform: {{platform}}
390
- in_worktree: false
391
- pr_base: {{base_branch}}
392
- ```
393
- </action>
327
+ <action>Write initial `{state_file}` with STATE_FIELDS: `current_story = null`, `current_bmad_step = null`, `completed_skill = bmad-help`, `session_stories_done = 0`, `stories_remaining = [from sprint-status]`, `in_worktree = false`, `pr_base = {{base_branch}}`.</action>
394
328
  <action>Report to user:
395
329
  ```
396
330
  Sprintpilot ON
@@ -483,90 +417,33 @@ Resolve:
483
417
 
484
418
  <!-- GIT: Enter worktree before dev-story -->
485
419
  <check if="{{git_enabled}} AND {{next_skill}} is bmad-dev-story">
486
- <action>**Sanitize branch name** run:
487
- `node {{project_root}}/_Sprintpilot/scripts/sanitize-branch.js "{{current_story}}" --prefix "{{branch_prefix}}" --max-length 60`
488
- Output: sanitized name (without prefix). Set `{{branch_name}}` = output.
489
- Full branch ref will be `{{branch_prefix}}{{branch_name}}`.
490
- </action>
420
+ <action>**Sanitize branch name**: `node {{project_root}}/_Sprintpilot/scripts/sanitize-branch.js "{{current_story}}" --prefix "{{branch_prefix}}" --max-length 60`. Set `{{branch_name}}` = output. Full ref: `{{branch_prefix}}{{branch_name}}`.</action>
491
421
 
492
- <action>**Check if branch already registered** in `{status_file}` for this story.
493
- If yes AND worktree already exists → skip creation (idempotent).
494
- If yes AND no worktree → recovery mode (see health check).
495
- If no → proceed with creation.
496
- </action>
497
-
498
- <action>**Prepare for worktree** — determine the correct branch point.
499
- Run: `git fetch origin`
422
+ <action>**Idempotency check** — if branch is already registered in `{status_file}` for this story AND its worktree exists, skip creation. If registered without worktree → recovery mode (see health check). Otherwise proceed.</action>
500
423
 
501
- Check if there is a previous story in this epic with a pushed but unmerged branch (PR pending):
502
- - Read `{git_status_file}` for earlier stories in the same epic
503
- - Find the latest story branch where `push_status` = "pushed" AND `pr_url` is a valid URL
504
- - Check if that branch has been merged to `{{base_branch}}`: `git merge-base --is-ancestor origin/{{branch_prefix}}<prev-branch> origin/{{base_branch}}`
424
+ <action>**Pick branch point.** If `{{has_origin}}` is true: `git fetch origin` (warn + continue on failure). If `{{has_origin}}` is false: skip fetch, use local refs.
505
425
 
506
- If an unmerged previous story branch exists:
507
- - Branch from it: `git checkout origin/{{branch_prefix}}<prev-branch>`
508
- - Set `{{pr_base}}` = `{{branch_prefix}}<prev-branch>` (PR should target previous story, not main)
509
- - Log: "Branching from {{branch_prefix}}<prev-branch> (PR pending merge)"
510
- Otherwise:
511
- - Branch from base: `git checkout origin/{{base_branch}}`
512
- - Set `{{pr_base}}` = `{{base_branch}}`
426
+ Read `{git_status_file}` for earlier stories in this epic; find the latest with `push_status = "pushed"` AND a valid `pr_url`; check if merged to base: `git merge-base --is-ancestor origin/{{branch_prefix}}<prev-branch> origin/{{base_branch}}`.
427
+ - If unmerged previous story exists (requires `{{has_origin}}`): `git checkout origin/{{branch_prefix}}<prev-branch>`, set `{{pr_base}}` = `{{branch_prefix}}<prev-branch>`.
428
+ - Otherwise: `git checkout origin/{{base_branch}}` (or local `{{base_branch}}` if no origin), set `{{pr_base}}` = `{{base_branch}}`.
513
429
 
514
- (Detached HEAD is fine — the worktree add below creates a new branch from HEAD)
430
+ Detached HEAD is fine — `git worktree add` below creates a new branch from HEAD.
515
431
  </action>
516
432
 
517
- <action>**Create worktree** using standard git commands (works in any coding agent):
518
- ```
519
- git worktree add "{{project_root}}/.worktrees/{{current_story}}" -b "{{branch_prefix}}{{branch_name}}" 2>&1
520
- ```
521
- This creates `.worktrees/{{current_story}}/` with a new branch `{{branch_prefix}}{{branch_name}}` from HEAD.
522
-
523
- If worktree add fails (branch already exists):
524
- ```
525
- git worktree add "{{project_root}}/.worktrees/{{current_story}}" "{{branch_prefix}}{{branch_name}}" 2>&1
526
- ```
433
+ <action>**Create worktree.** Try: `git worktree add "{{project_root}}/.worktrees/{{current_story}}" -b "{{branch_prefix}}{{branch_name}}" 2>&1`. If it fails because the branch already exists, retry without `-b`: `git worktree add "{{project_root}}/.worktrees/{{current_story}}" "{{branch_prefix}}{{branch_name}}" 2>&1`.
527
434
 
528
- **If both fail** (disk full, permissions, etc.):
529
- - Log: "WARN: git worktree add failed — continuing without worktree isolation"
530
- - Set `{{in_worktree}}` = false
531
- - Create branch manually: `git checkout -b {{branch_prefix}}{{branch_name}}`
532
- If checkout also fails (branch already exists): `git checkout {{branch_prefix}}{{branch_name}}`
533
- If both fail: HALT — "Could not create or switch to branch {{branch_prefix}}{{branch_name}}"
534
- - Continue with the skill invocation in PROJECT_ROOT (no isolation)
535
- - Git operations (commit, push, PR) still work on the branch
435
+ If both fail (disk/permissions): log "WARN: worktree add failed — continuing without isolation", set `{{in_worktree}}` = false, and fall back to branch-only mode: `git checkout -b {{branch_prefix}}{{branch_name}}` (retry without `-b` if branch exists). HALT only if the checkout also fails. Git push/PR still work on the branch.
536
436
  </action>
537
437
 
538
438
  <check if="worktree add succeeded">
539
- <action>**Change working directory** to the worktree:
540
- `cd {{project_root}}/.worktrees/{{current_story}}`
541
- All subsequent file operations and commands MUST use this directory.
542
- Set `{{worktree_path}}` = `{{project_root}}/.worktrees/{{current_story}}`
543
- </action>
544
-
545
- <action>**Init submodules** if needed.
546
- First check for `.gitmodules` (use your file-exists tool, or `node -e "process.exit(require('fs').existsSync('.gitmodules')?0:1)"`). If not present, skip this step.
547
- If present, run `git submodule update --init --recursive` (give it ~30 seconds). If the command fails or hangs, warn "Submodule init failed (may need auth). Continuing without." and proceed.
548
- </action>
549
-
439
+ <action>`cd {{project_root}}/.worktrees/{{current_story}}`. All subsequent commands run from here. Set `{{worktree_path}}` = this path.</action>
440
+ <action>**Init submodules** if `.gitmodules` exists (check with your file-exists tool or `node -e "process.exit(require('fs').existsSync('.gitmodules')?0:1)"`). Run `git submodule update --init --recursive` (~30s). On failure/hang: warn "Submodule init failed (may need auth). Continuing." and proceed.</action>
550
441
  <action>Set `{{in_worktree}}` = true</action>
551
442
  </check>
552
- <action>Update `{state_file}` (write to worktree copy since we're now IN the worktree)</action>
443
+ <action>Update `{state_file}` (write to the worktree copy since cwd is now the worktree)</action>
553
444
  </check>
554
445
 
555
- <action>Update `{state_file}`:
556
- ```yaml
557
- last_updated: {current_datetime}
558
- current_story: {{current_story}}
559
- current_bmad_step: executing
560
- completed_skill: {previous skill}
561
- next_skill: {{next_skill}}
562
- session_stories_done: {{session_stories_done}}
563
- stories_remaining: {{stories_remaining}}
564
- git_enabled: {{git_enabled}}
565
- platform: {{platform}}
566
- in_worktree: {{in_worktree}}
567
- pr_base: {{pr_base}}
568
- ```
569
- </action>
446
+ <action>Update `{state_file}` with STATE_FIELDS (set `current_bmad_step = executing`, `completed_skill = <previous skill>`).</action>
570
447
 
571
448
  <!-- Autopilot menu handling rules apply — see AUTOPILOT RULES section above -->
572
449
 
@@ -637,7 +514,8 @@ pr_base: {{pr_base}}
637
514
  </check>
638
515
 
639
516
  <check if="{{completed_skill}} was bmad-sprint-planning AND {{git_enabled}}">
640
- <action>Run `git fetch origin`</action>
517
+ <action>If `{{has_origin}}` is true, run `git fetch origin` (log a warning on failure and continue — do not abort).
518
+ If `{{has_origin}}` is false, skip the fetch.</action>
641
519
  <action>Initialize `{git_status_file}` if it doesn't exist (with git_integration block)</action>
642
520
  </check>
643
521
 
@@ -653,22 +531,10 @@ pr_base: {{pr_base}}
653
531
  </action>
654
532
  </check>
655
533
 
656
- <!-- GIT: Commit planning artifacts to main after planning skills -->
657
534
  <check if="{{git_enabled}} AND {{completed_skill}} is a planning skill (bmad-create-prd, bmad-create-architecture, bmad-create-ux-design, bmad-create-epics-and-stories, bmad-sprint-planning, bmad-check-implementation-readiness, bmad-create-story)">
658
- <action>**Commit planning artifacts to main** — keep track of all planning decisions in git.
659
- Stage all changed artifacts (ignore errors — any of these paths may not yet exist):
660
- ```
661
- git add _bmad-output/planning-artifacts/ _bmad-output/implementation-artifacts/ _bmad-output/stories/
662
- ```
663
- Check if there's anything staged; if yes, commit:
664
- ```
665
- git diff --cached --quiet
666
- ```
667
- If that exits non-zero (there are staged changes), run: `git commit -m "docs: {{completed_skill}} artifacts"`
668
- Then push (log a warning if push fails; do not halt autopilot):
669
- ```
670
- git push origin {{base_branch}}
671
- ```
535
+ <action>**Commit planning artifacts to main.**
536
+ 1. `git add _bmad-output/planning-artifacts/ _bmad-output/implementation-artifacts/ _bmad-output/stories/` (ignore missing-path errors)
537
+ 2. If `git diff --cached --quiet` exits non-zero: `git commit -m "docs: {{completed_skill}} artifacts"` then `git push origin {{base_branch}}` (warn on push failure, do not halt).
672
538
  </action>
673
539
  </check>
674
540
 
@@ -679,32 +545,17 @@ pr_base: {{pr_base}}
679
545
 
680
546
  <step n="5" goal="Determine next skill — from skill output first, bmad-help as fallback">
681
547
 
682
- <action>Read the output of `{{completed_skill}}`</action>
683
-
684
- <check if="output contains 'Next Steps', 'What to do next', 'Run next', or equivalent">
685
- <action>Extract `{{next_skill}}` from that section</action>
686
- <action>Log: "Next step from skill output: {{next_skill}}"</action>
687
- </check>
548
+ <action>Read the output of `{{completed_skill}}`. If it contains "Next Steps", "What to do next", "Run next", or equivalent, extract `{{next_skill}}` from that section. Otherwise invoke `bmad-help` — "{{completed_skill}} just finished. What is the next required workflow step?" — and extract `{{next_skill}}` from its response. Log the source ("skill output" vs "bmad-help fallback").</action>
688
549
 
689
- <check if="output contains NO clear next step">
690
- <action>Invoke `bmad-help` "{{completed_skill}} just finished. What is the next required workflow step?"</action>
691
- <action>Extract `{{next_skill}}` from bmad-help response</action>
692
- <action>Log: "Next step from bmad-help fallback: {{next_skill}}"</action>
693
- </check>
694
-
695
- <check if="{{next_skill}} is null, empty, or signals completion (no further steps / sprint done / all done)">
696
- <action>**Verify against source of truth**re-read `{status_file}` and check for any story with status != "done"</action>
697
- <check if="undone stories exist in status_file">
698
- <action>Set `{{current_story}}` = first undone story from `{status_file}`</action>
699
- <action>Determine `{{next_skill}}` based on that story's current status and BMAD step:
700
- - If story has no story file yet → `bmad-create-story`
701
- - If story file exists but status is `ready-for-dev` → `bmad-check-implementation-readiness`
702
- - If story is `in-progress` and `current_bmad_step` is before `code-review` (i.e. RED or GREEN phase) → `bmad-dev-story`
703
- - If story is `in-progress` and `current_bmad_step` is `code-review` or later → `bmad-code-review`
704
- - Otherwise → invoke `bmad-help` for precise determination
705
- </action>
706
- <action>Log: "next_skill was empty but undone stories remain — resolved to {{next_skill}} for {{current_story}}"</action>
707
- </check>
550
+ <check if="{{next_skill}} is null, empty, or signals completion">
551
+ <action>**Verify against source of truth** — re-read `{status_file}`. If undone stories exist, set `{{current_story}}` = first one and determine `{{next_skill}}`:
552
+ - No story file → `bmad-create-story`
553
+ - Story file + status `ready-for-dev` `bmad-check-implementation-readiness`
554
+ - Status `in-progress` and `current_bmad_step` before `code-review` → `bmad-dev-story`
555
+ - Status `in-progress` and `current_bmad_step` ≥ `code-review` → `bmad-code-review`
556
+ - Else invoke `bmad-help` for precise determination
557
+ Log: "next_skill was empty but undone stories remain resolved to {{next_skill}} for {{current_story}}".
558
+ </action>
708
559
  <check if="all stories in status_file are done">
709
560
  <goto step="10">Sprint complete</goto>
710
561
  </check>
@@ -815,37 +666,15 @@ Instruct: "Re-verify code review for story {{current_story}} — all patch findi
815
666
  </action>
816
667
 
817
668
  <check if="{{create_pr}} is false OR {{platform}} is git_only OR {{pr_url}} is null or SKIPPED">
818
- <action>**Merge story branch to main** tracked and retryable.
819
- Run:
820
- ```
821
- git checkout -B {{base_branch}} origin/{{base_branch}}
822
- git merge {{branch_prefix}}{{branch_name}} --no-edit
823
- ```
824
- If succeeds:
825
- - `git push origin {{base_branch}}`
826
- - Set `{{merge_status}}` = "merged"
827
- If fails (conflict):
828
- - `git merge --abort`
829
- - `git fetch origin`
830
- - `git checkout -B {{base_branch}} origin/{{base_branch}}`
831
- - Retry merge once: `git merge {{branch_prefix}}{{branch_name}} --no-edit`
832
- - If retry succeeds: push, set `{{merge_status}}` = "merged"
833
- - If retry fails: set `{{merge_status}}` = "failed"
834
- Log: "WARN: merge failed for {{current_story}} — will retry on next boot"
835
-
836
- If `{{merge_status}}` == "failed":
837
- Log warning but do NOT halt. The branch is pushed and preserved.
838
- Boot reconciliation (INITIALIZATION branch reconciliation) will retry on next session.
839
-
840
- Note: `{{merge_status}}` is persisted by the full sync-status.js call later in this step (via `--merge-status`). Do NOT call sync-status.js separately here — it does full block replacement and would destroy other fields.
669
+ <action>**Merge story branch to main.** If `{{has_origin}}` is false (local-only), substitute `origin/{{base_branch}}` → `{{base_branch}}` and skip all `git push origin` / `git fetch origin` calls below.
670
+ 1. `git checkout -B {{base_branch}} origin/{{base_branch}}` then `git merge {{branch_prefix}}{{branch_name}} --no-edit`.
671
+ 2. On success: `git push origin {{base_branch}}`, set `{{merge_status}}` = "merged".
672
+ 3. On conflict: `git merge --abort`, `git fetch origin`, re-checkout base, retry merge once. On retry success: push + merged. On retry failure: `{{merge_status}}` = "failed", log warning, continue — the branch is preserved and boot reconciliation retries next session.
673
+
674
+ `{{merge_status}}` is persisted by the sync-status.js call later in this step (via `--merge-status`). Do NOT call sync-status.js here — it does full block replacement and would destroy other fields.
841
675
  </action>
842
676
  <check if="{{cleanup_on_merge}} is true">
843
- <action>**Cleanup worktree** for merged story branch was merged locally, worktree is no longer needed. Ignore failures from the remove (the worktree may already be gone):
844
- ```
845
- git worktree remove .worktrees/{{current_story}} --force
846
- git worktree prune
847
- ```
848
- </action>
677
+ <action>**Cleanup worktree** (ignore failuresmay already be gone): `git worktree remove .worktrees/{{current_story}} --force` then `git worktree prune`</action>
849
678
  </check>
850
679
  </check>
851
680
  <check if="{{pr_url}} is a valid URL (not null, not SKIPPED)">
@@ -854,29 +683,11 @@ Instruct: "Re-verify code review for story {{current_story}} — all patch findi
854
683
  <action>Log: "Story {{current_story}} pushed — PR awaiting review: {{pr_url}}"</action>
855
684
  </check>
856
685
 
857
- <!-- Commit all implementation artifacts and status updates to main after each story -->
858
- <action>**Commit story completion artifacts to main** — ensure main always reflects current sprint state.
859
- ```
860
- git checkout -B {{base_branch}} origin/{{base_branch}}
861
- ```
862
- </action>
863
-
864
- <action>**Write git status** to addon's own file (NEVER modify sprint-status.yaml) — runs AFTER checkout to base branch so the file persists in the working tree for the commit below:
865
- `node {{project_root}}/_Sprintpilot/scripts/sync-status.js --story "{{current_story}}" --git-status-file "{{project_root}}/_bmad-output/implementation-artifacts/git-status.yaml" --branch "{{branch_prefix}}{{branch_name}}" --commit "{{story_commit}}" --patch-commits "{{patch_commits_csv}}" --push-status "{{push_status}}" --merge-status "{{merge_status}}" --pr-url "{{pr_url}}" --lint-result "{{lint_result}}" --worktree "{{project_root}}/.worktrees/{{current_story}}" --platform "{{platform}}" --base-branch "{{base_branch}}"`
866
- This writes to `git-status.yaml` (addon-owned). Sprint-status.yaml is BMAD-owned — updated by BMAD skills only.
867
- </action>
868
-
869
- <action>**Stage and commit artifacts** — explicitly include git-status.yaml and decision-log.yaml. Ignore errors from the `git add` (any listed path may not yet exist):
870
- ```
871
- git add _bmad-output/implementation-artifacts/sprint-status.yaml _bmad-output/implementation-artifacts/git-status.yaml _bmad-output/implementation-artifacts/autopilot-state.yaml _bmad-output/implementation-artifacts/decision-log.yaml _bmad-output/stories/ _bmad-output/planning-artifacts/
872
- ```
873
- Check if anything is staged: `git diff --cached --quiet`. If that exits non-zero, commit:
874
- `git commit -m "docs: story {{current_story}} done — {{test_count}} tests{{#if pr_url}}, PR: {{pr_url}}{{/if}}"`
875
- Then push (log a warning if push fails; do not halt autopilot):
876
- ```
877
- git push origin {{base_branch}}
878
- ```
879
- This ensures sprint-status.yaml, git-status.yaml, story files, and any updated artifacts are on main even when story code is on a PR branch.
686
+ <action>**Commit story artifacts to main** keeps main in sync even when story code is on a PR branch.
687
+ 1. `git checkout -B {{base_branch}} origin/{{base_branch}}`
688
+ 2. Write git-status.yaml (addon-owned — never touch sprint-status.yaml): `node {{project_root}}/_Sprintpilot/scripts/sync-status.js --story "{{current_story}}" --git-status-file "{{project_root}}/_bmad-output/implementation-artifacts/git-status.yaml" --branch "{{branch_prefix}}{{branch_name}}" --commit "{{story_commit}}" --patch-commits "{{patch_commits_csv}}" --push-status "{{push_status}}" --merge-status "{{merge_status}}" --pr-url "{{pr_url}}" --lint-result "{{lint_result}}" --worktree "{{project_root}}/.worktrees/{{current_story}}" --platform "{{platform}}" --base-branch "{{base_branch}}"`
689
+ 3. Stage artifacts (ignore errors for missing paths): `git add _bmad-output/implementation-artifacts/sprint-status.yaml _bmad-output/implementation-artifacts/git-status.yaml _bmad-output/implementation-artifacts/autopilot-state.yaml _bmad-output/implementation-artifacts/decision-log.yaml _bmad-output/stories/ _bmad-output/planning-artifacts/`
690
+ 4. If `git diff --cached --quiet` exits non-zero: `git commit -m "docs: story {{current_story}} done — {{test_count}} tests{{#if pr_url}}, PR: {{pr_url}}{{/if}}"` then `git push origin {{base_branch}}` (warn on push failure, do not halt).
880
691
  </action>
881
692
  </check>
882
693
 
@@ -897,134 +708,34 @@ Instruct: "Re-verify code review for story {{current_story}} — all patch findi
897
708
  <action>Resolve `{{epic_id}}` (e.g. "1") and `{{epic_title}}` from `{status_file}` for the current epic</action>
898
709
  <action>Create task "[epic {{epic_id}}] retrospective" → `in_progress`</action>
899
710
 
900
- <!-- Retrospective handling is driven by `autopilot.retrospective_mode`
901
- in modules/autopilot/config.yaml. See the SKILL AUTOMATABLE REFERENCE
902
- table for rationale. The external `bmad-retrospective` skill is
903
- NEVER invoked from autopilot — it enters a multi-persona discussion
904
- loop under some CLIs. -->
711
+ <!-- Retrospective: driven by `autopilot.retrospective_mode`. The external
712
+ `bmad-retrospective` skill is NEVER invoked from autopilot (multi-persona
713
+ discussion loop under some CLIs). -->
905
714
 
906
715
  <check if="{{retrospective_mode}} is auto">
907
- <!-- Deterministic single-pass retrospective. No persona simulation,
908
- no rounds, no external skill call. All inputs are on-disk. -->
909
- <action>Collect from `{status_file}` for epic `{{epic_id}}`:
910
- - list of done stories with { story-key, title, test_pass_count, patch_count }
911
- - epic title, start/end dates if present
912
- </action>
913
- <action>Collect decision-log entries for epic `{{epic_id}}` from `{decision_log_file}` (match on `story` prefix `{{epic_id}}-` or `phase: autopilot:*` entries tagged to this epic)</action>
914
- <action>Identify open risks / carry-over notes from sprint-status (any story with `notes` or `risks` fields, any `workaround` decisions in the log for this epic)</action>
915
- <action>Ensure directory `{implementation_artifacts}/retrospectives/` exists</action>
916
- <action>Write `{implementation_artifacts}/retrospectives/epic-{{epic_id}}-retrospective.md` using this template:
917
- ```markdown
918
- # Epic {{epic_id}} — {{epic_title}} — Retrospective
919
-
920
- **Completed:** {current_date}
921
- **Stories done:** {{n_done}}/{{n_total}}
922
-
923
- ## Stories
924
- {{#each stories}}
925
- - **{{story-key}}** — {{title}}
926
- - Tests: {{test_pass_count}}
927
- - Patches applied: {{patch_count}}
928
- {{/each}}
929
-
930
- ## Key decisions
931
- {{#each decisions}}
932
- - [{{impact}}] {{category}}: {{decision}} — {{rationale}}
933
- {{/each}}
934
-
935
- ## Risks carried forward
936
- {{#each open_risks}}
937
- - {{risk}}
938
- {{/each}}
939
-
940
- ## Notes
941
- Generated inline by Sprintpilot autopilot per `autopilot.retrospective_mode: auto`.
942
- ```
943
- </action>
944
- <action>Update `{status_file}`:
945
- - `epics.{{epic_id}}.status` = `done`
946
- - `epics.{{epic_id}}.retrospective_path` = the retrospective file path (relative to project root)
947
- - `epics.{{epic_id}}.completed_at` = {current_date}
948
- </action>
949
- <action>Append decision-log entry:
950
- `{ category: workaround, decision: "retrospective generated inline", rationale: "autopilot.retrospective_mode=auto — avoids external skill's multi-persona loop", impact: low, phase: autopilot:retrospective, story: "epic-{{epic_id}}" }`
951
- </action>
952
- <action>Mark retrospective task → `completed`</action>
953
- <action>Set `{{completed_skill}}` = `retrospective-auto`</action>
716
+ <action>Collect from `{status_file}` for epic `{{epic_id}}`: done stories `{ story-key, title, test_pass_count, patch_count }`, epic title, dates if present.</action>
717
+ <action>Collect decision-log entries for epic `{{epic_id}}` (match `story` prefix `{{epic_id}}-` or `phase: autopilot:*` tagged to this epic). Identify open risks / carry-over notes from any story `notes`/`risks` fields or `workaround` decisions for this epic.</action>
718
+ <action>Ensure `{implementation_artifacts}/retrospectives/` exists. Read template `{{project_root}}/_Sprintpilot/templates/epic-retrospective.md`, fill mustache placeholders, write to `{implementation_artifacts}/retrospectives/epic-{{epic_id}}-retrospective.md`.</action>
719
+ <action>Update `{status_file}`: `epics.{{epic_id}}.status = done`, `.retrospective_path = <file>`, `.completed_at = {current_date}`.</action>
720
+ <action>Append decision-log entry: `{ category: workaround, decision: "retrospective generated inline", rationale: "retrospective_mode=auto", impact: low, phase: autopilot:retrospective, story: "epic-{{epic_id}}" }`.</action>
721
+ <action>Mark retrospective task → `completed`. Set `{{completed_skill}}` = `retrospective-auto`.</action>
954
722
  </check>
955
723
 
956
724
  <check if="{{retrospective_mode}} is stop">
957
- <!-- Pause autopilot so user can run /bmad-retrospective interactively.
958
- On the next /sprint-autopilot-on the resume logic in step 1 will
959
- detect the cleared state and move to the next epic. -->
960
- <action>Update `{state_file}`:
961
- - `paused_at` = `epic-complete-awaiting-retrospective`
962
- - `paused_epic_id` = `{{epic_id}}`
963
- - `next_action` = "run /bmad-retrospective interactively for epic {{epic_id}}, then re-run /sprint-autopilot-on"
964
- </action>
965
- <action>Append decision-log entry:
966
- `{ category: workaround, decision: "paused for interactive retrospective", rationale: "autopilot.retrospective_mode=stop", impact: low, phase: autopilot:retrospective, story: "epic-{{epic_id}}" }`
967
- </action>
968
- <action>Mark retrospective task → `completed` (handed off to user)</action>
969
- <action>Report:
970
- ```
971
- Autopilot paused — epic {{epic_id}} complete, retrospective handed off.
972
-
973
- Per `autopilot.retrospective_mode: stop` in
974
- _Sprintpilot/modules/autopilot/config.yaml, autopilot does not run the
975
- retrospective automatically.
976
-
977
- To continue:
978
- 1. Run `/bmad-retrospective` interactively for epic {{epic_id}}
979
- 2. When done, run `/sprint-autopilot-on` to resume with the next epic
980
-
981
- State saved to: {state_file}
982
- ```
983
- </action>
984
- <action>STOP</action>
725
+ <action>Update `{state_file}`: `paused_at = epic-complete-awaiting-retrospective`, `paused_epic_id = {{epic_id}}`, `next_action = "run /bmad-retrospective interactively for epic {{epic_id}}, then re-run /sprint-autopilot-on"`.</action>
726
+ <action>Append decision-log entry: `{ category: workaround, decision: "paused for interactive retrospective", rationale: "retrospective_mode=stop", impact: low, phase: autopilot:retrospective, story: "epic-{{epic_id}}" }`. Mark retrospective task `completed`.</action>
727
+ <action>Report: "Autopilot paused epic {{epic_id}} complete, retrospective handed off. Run `/bmad-retrospective` interactively for epic {{epic_id}}, then re-run `/sprint-autopilot-on`. State saved to: {state_file}." Then STOP.</action>
985
728
  </check>
986
729
 
987
730
  <check if="{{retrospective_mode}} is skip">
988
- <!-- User opted out of retrospective. Record and move on. -->
989
- <action>Update `{status_file}`:
990
- - `epics.{{epic_id}}.status` = `done`
991
- - `epics.{{epic_id}}.retrospective_path` = null
992
- - `epics.{{epic_id}}.retrospective_skipped` = true
993
- - `epics.{{epic_id}}.completed_at` = {current_date}
994
- </action>
995
- <action>Append decision-log entry:
996
- `{ category: workaround, decision: "retrospective skipped", rationale: "autopilot.retrospective_mode=skip (NOT RECOMMENDED)", impact: medium, phase: autopilot:retrospective, story: "epic-{{epic_id}}" }`
997
- </action>
998
- <action>Mark retrospective task → `completed` (skipped)</action>
999
- <action>Set `{{completed_skill}}` = `retrospective-skip`</action>
1000
- <action>Log: "Epic {{epic_id}} retrospective skipped per config — continuing with next epic"</action>
731
+ <action>Update `{status_file}`: `epics.{{epic_id}}.status = done`, `.retrospective_path = null`, `.retrospective_skipped = true`, `.completed_at = {current_date}`.</action>
732
+ <action>Append decision-log entry: `{ category: workaround, decision: "retrospective skipped", rationale: "retrospective_mode=skip (NOT RECOMMENDED)", impact: medium, phase: autopilot:retrospective, story: "epic-{{epic_id}}" }`. Mark retrospective task → `completed`. Set `{{completed_skill}}` = `retrospective-skip`.</action>
1001
733
  </check>
1002
734
 
1003
- <!-- GIT: Epic completion — suggest merge, cleanup worktrees -->
1004
735
  <check if="{{git_enabled}}">
1005
- <action>**List all epic PR/MR URLs** from `{status_file}` for this epic's stories</action>
1006
- <action>Report:
1007
- ```
1008
- Epic complete — PR/MR summary:
1009
- {{#each epic_stories}}
1010
- - {{story-key}}: {{pr_url}}
1011
- {{/each}}
1012
-
1013
- Ready to merge. Review PRs and confirm when ready.
1014
- ```
1015
- </action>
736
+ <action>**Epic PR summary** — list all epic PR/MR URLs from `{status_file}` and report as "Epic complete — PR/MR summary: [list]. Ready to merge — review PRs and confirm when ready."</action>
1016
737
  <check if="{{cleanup_on_merge}} is true">
1017
- <action>**Cleanup worktrees** for completed stories:
1018
- For each story in this epic:
1019
- 1. Check if worktree at `.worktrees/{{story-key}}` exists
1020
- 2. Check if clean: `git -C .worktrees/{{story-key}} status --porcelain`
1021
- 3. If clean → `git worktree remove .worktrees/{{story-key}}` + `git worktree prune`
1022
- Update `{git_status_file}` for this story: `worktree_cleaned: true`
1023
- 4. If dirty → warn user, skip cleanup
1024
- </action>
1025
- </check>
1026
- <check if="{{cleanup_on_merge}} is false">
1027
- <action>Log: "Worktree cleanup skipped (git.worktree.cleanup_on_merge = false)"</action>
738
+ <action>**Cleanup worktrees** for completed stories. For each: if `.worktrees/{{story-key}}` exists, check cleanliness via `git -C .worktrees/{{story-key}} status --porcelain`. If clean → `git worktree remove` + `git worktree prune` and set `worktree_cleaned: true` in `{git_status_file}`. If dirty → warn and skip.</action>
1028
739
  </check>
1029
740
  </check>
1030
741
  </check>
@@ -1041,21 +752,7 @@ Instruct: "Re-verify code review for story {{current_story}} — all patch findi
1041
752
 
1042
753
  <step n="8" goal="Save state and continue">
1043
754
 
1044
- <action>Update `{state_file}`:
1045
- ```yaml
1046
- last_updated: {current_datetime}
1047
- current_story: {{current_story}}
1048
- current_bmad_step: {{current_bmad_step}}
1049
- completed_skill: {{completed_skill}}
1050
- next_skill: {{next_skill}}
1051
- session_stories_done: {{session_stories_done}}
1052
- stories_remaining: {{stories_remaining}}
1053
- git_enabled: {{git_enabled}}
1054
- platform: {{platform}}
1055
- in_worktree: {{in_worktree}}
1056
- pr_base: {{pr_base}}
1057
- ```
1058
- </action>
755
+ <action>Update `{state_file}` with STATE_FIELDS.</action>
1059
756
 
1060
757
  <goto step="2">Continue execution loop</goto>
1061
758
 
@@ -1090,21 +787,7 @@ pr_base: {{pr_base}}
1090
787
  </action>
1091
788
  </check>
1092
789
 
1093
- <action>Update `{state_file}`:
1094
- ```yaml
1095
- last_updated: {current_datetime}
1096
- current_story: {{current_story}}
1097
- current_bmad_step: {{current_bmad_step}}
1098
- completed_skill: {{completed_skill}}
1099
- next_skill: {{next_skill}}
1100
- session_stories_done: {{session_stories_done}}
1101
- stories_remaining: {{stories_remaining}}
1102
- git_enabled: {{git_enabled}}
1103
- platform: {{platform}}
1104
- in_worktree: {{in_worktree}}
1105
- pr_base: {{pr_base}}
1106
- ```
1107
- </action>
790
+ <action>Update `{state_file}` with STATE_FIELDS.</action>
1108
791
 
1109
792
  <action>Read `{decision_log_file}` — count medium/high decisions from this session's stories</action>
1110
793
 
@@ -1154,133 +837,53 @@ No work will be repeated.
1154
837
  <action>Run full test suite — report `N/N passed`</action>
1155
838
 
1156
839
  <!-- Generate project documentation after sprint completion -->
1157
- <action>**Generate documentation** — create or update project README and docs.
1158
- Invoke `bmad-document-project` skill to auto-generate documentation from the completed implementation.
1159
- If the skill is not available or fails, generate a minimal README.md:
1160
- - Project name and description (from product brief / PRD)
1161
- - How to install (`npm install` / `pip install` / etc.)
1162
- - How to run (`npm start` / the launch command)
1163
- - How to test (`npm test`)
1164
- - Architecture overview (from architecture doc if it exists)
1165
- </action>
1166
-
1167
- <!-- GIT: Commit documentation and final artifacts to main -->
1168
- <check if="{{git_enabled}}">
1169
- <action>**Commit final artifacts and documentation to main**. Run each step; if an early step fails, STOP and log — don't proceed past a failed step. `git add` may fail for missing optional paths (`docs/`, `README.md`); ignore those path-specific errors. Failure of the final push should log a warning but not halt autopilot:
1170
- ```
1171
- git checkout -B {{base_branch}} origin/{{base_branch}}
1172
- git add _bmad-output/ README.md docs/
1173
- ```
1174
- Check if anything is staged: `git diff --cached --quiet`. If that exits non-zero, commit:
1175
- `git commit -m "docs: project documentation and final artifacts"`
1176
- Then: `git push origin {{base_branch}}`
1177
- </action>
1178
- </check>
1179
-
1180
- <action>Read `{status_file}` and collect:
1181
- - All completed stories grouped by epic, with their story titles
1182
- - Total story count, total epic count
1183
- - Final test count
1184
- - If git_enabled: all PR/MR URLs, patch counts, dismissed findings per story
1185
- </action>
1186
-
1187
- <action>Read `{decision_log_file}` and collect:
1188
- - All decisions with impact `medium` or `high`
1189
- - Count of `review-accept` entries (patches applied)
1190
- - Count of `review-triage` entries (findings dismissed)
1191
- - Total review rounds (count of code-review invocations)
1192
- - Per-story summary: patches applied and findings dismissed
840
+ <action>**Resolve stack** — set `{{stack}}` = `{ name, install_cmd, run_cmd, test_cmd }` using the first successful source:
841
+
842
+ 1. **`project-context.md`** (glob `**/project-context.md`, canonical `{output_folder}/project-context.md`) extract from "Technology Stack & Versions" and any install/run/test subsections.
843
+ 2. **`architecture.md`** (`{planning_artifacts}/architecture.md`) extract from "Tech Stack" / "Runtime" / "Build & Deploy" / "Commands" sections.
844
+ 3. **Manifest heuristics** map manifest file stack idiomatic commands:
845
+
846
+ | Manifest | Stack | install / run / test |
847
+ |---|---|---|
848
+ | `package.json` | Node/JS/TS | `<pm> install` / `<pm> run start\|dev\|serve` (or `node <bin>`) / `<pm> test` — `<pm>` = `pnpm`/`yarn`/`bun`/`npm` by lockfile |
849
+ | `pyproject.toml`, `requirements.txt`, `setup.py` | Python | `pip install -r requirements.txt` (or `-e .`) / Django `python manage.py runserver` → Flask → FastAPI `uvicorn app:app` → `python main.py`/`app.py` / `pytest` |
850
+ | `go.mod` | Go | `go mod download` / `go run .` (or `./cmd/<name>`) / `go test ./...` |
851
+ | `Cargo.toml` | Rust | `cargo build` / `cargo run` (or `--bin <name>`) / `cargo test` |
852
+ | `pom.xml` | Java/Kotlin (Maven) | `mvn install` / `mvn spring-boot:run` or `mvn exec:java` / `mvn test` |
853
+ | `build.gradle(.kts)` | Java/Kotlin (Gradle) | `./gradlew build` / `./gradlew bootRun` or `run` / `./gradlew test` |
854
+ | `Gemfile` | Ruby | `bundle install` / `rails server` or `bundle exec ruby <entry>` / `bundle exec rspec` |
855
+ | `*.csproj`/`*.sln` | .NET | `dotnet restore` / `dotnet run` (or `--project`) / `dotnet test` |
856
+ | `composer.json` | PHP | `composer install` / `php artisan serve` (Laravel) or `php -S localhost:8000 -t public` / `vendor/bin/phpunit` |
857
+ | `mix.exs` | Elixir | `mix deps.get` / `mix phx.server` or `mix run --no-halt` / `mix test` |
858
+ | (none of the above) | Explicit launcher | `./run.sh`/`./run_gui.sh`/`./start.sh`, `make run\|start\|dev`, `docker compose up`, `docker build` + `docker run` |
859
+
860
+ 4. **No match** — all fields `null`. Downstream omits the line; never guess.
861
+
862
+ Set `{{launch_cmd}}` = `{{stack.run_cmd}}`.
863
+ If `{{stack}}` came from (3) and `project-context.md` exists without stack info, log: "Consider running `bmad-generate-project-context` to capture stack commands."
1193
864
  </action>
1194
865
 
1195
- <action>Find the app launch command by checking (in order):
1196
- 1. `run_gui.sh` or `run.sh` in the project root
1197
- 2. `main.py` in the project root
1198
- 3. Check `pyproject.toml`, `package.json`, or `setup.py` for scripts
1199
- Record as `{{launch_cmd}}`
1200
- </action>
866
+ <action>**Generate documentation** invoke `bmad-document-project`. If unavailable or it fails, write a minimal README using `{{stack}}`: project name + description (from brief/PRD); install/run/test lines for each non-null `{{stack.*_cmd}}` (omit lines where null); architecture overview if `architecture.md` exists.</action>
1201
867
 
1202
- <!-- GIT: Final worktree cleanup — safety net for any worktrees not cleaned during epic completion -->
1203
868
  <check if="{{git_enabled}}">
1204
- <action>**Cleanup all remaining worktrees**:
1205
- Run: `git worktree list --porcelain`
1206
- For each worktree that is NOT the main worktree, run the following — log and continue on failure; some worktrees may already be gone:
1207
- `git worktree remove <path> --force`
1208
- Then: `git worktree prune`
869
+ <action>**Commit final artifacts + docs to main.**
870
+ 1. `git checkout -B {{base_branch}} origin/{{base_branch}}`
871
+ 2. `git add _bmad-output/ README.md docs/` (ignore missing-path errors)
872
+ 3. If `git diff --cached --quiet` exits non-zero: `git commit -m "docs: project documentation and final artifacts"` then `git push origin {{base_branch}}` (warn on push failure).
1209
873
  </action>
1210
874
  </check>
1211
875
 
1212
- <!-- GIT: Release lock -->
876
+ <action>**Collect report data** from `{status_file}` (stories grouped by epic with titles, totals, final test count; PR/MR URLs, patch/dismissed counts per story if git_enabled) and `{decision_log_file}` (medium/high-impact decisions; counts of `review-accept`, `review-triage`, code-review rounds; per-story patches-applied / findings-dismissed).</action>
877
+
1213
878
  <check if="{{git_enabled}}">
879
+ <action>**Cleanup remaining worktrees** (safety net): `git worktree list --porcelain` → for each non-main worktree: `git worktree remove <path> --force` then `git worktree prune` (log + continue on failure).</action>
1214
880
  <action>Release lock: `node {{project_root}}/_Sprintpilot/scripts/lock.js release`</action>
1215
881
  </check>
1216
882
 
1217
883
  <action>Delete `{state_file}` — sprint complete</action>
1218
884
  <action>Mark master task "Sprintpilot — Full Sprint Execution" → `completed`</action>
1219
885
 
1220
- <action>Report (use exact format):
1221
- ```
1222
- ╔═══════════════════════════════════════════════════════════════╗
1223
- ║ BMAD AUTOPILOT — REPORT ║
1224
- ╚═══════════════════════════════════════════════════════════════╝
1225
-
1226
- SUMMARY
1227
- Stories completed : {{done_count}}/{{total_stories}}
1228
- Epics completed : {{done_epics}}/{{total_epics}}
1229
- Total tests : {{N}}/{{N}} passed
1230
- {{#if git_enabled}}
1231
- Platform : {{platform}}
1232
- {{/if}}
1233
-
1234
- STORIES
1235
- {{#each epic}}
1236
- Epic {{epic_number}}: {{epic_title}}
1237
- {{#each stories}}
1238
- ✓ {{story-key}} — {{test_count}} tests{{#if pr_url}} PR: {{pr_url}}{{/if}}
1239
- {{/each}}
1240
- {{/each}}
1241
- {{#if remaining_stories}}
1242
- Not started:
1243
- {{#each remaining_stories}}
1244
- · {{story-key}}
1245
- {{/each}}
1246
- {{/if}}
1247
-
1248
- DECISIONS REQUIRING REVIEW (high/medium impact)
1249
- {{#each medium_high_decisions}}
1250
- #{{id}} [{{impact}}] {{story}} / {{phase}}
1251
- {{decision}}
1252
- → {{rationale}}
1253
- {{/each}}
1254
- {{#if no_medium_high_decisions}}
1255
- None — all decisions were low-impact.
1256
- {{/if}}
1257
-
1258
- Full log: {decision_log_file}
1259
-
1260
- REVIEW FINDINGS APPLIED
1261
- Patches applied : {{total_patches}}
1262
- Findings dismissed : {{total_dismissed}}
1263
- Review rounds : {{total_review_rounds}}
1264
-
1265
- CODE REVIEW SUMMARY (per story)
1266
- {{#each completed_stories}}
1267
- {{story-key}} : {{patches_applied}} patches applied, {{findings_dismissed}} dismissed
1268
- {{/each}}
1269
-
1270
- WHAT TO DO NEXT
1271
- 1. Review decisions marked medium/high above
1272
- {{#if has_pr_urls}}
1273
- 2. Merge open PRs: {{pr_urls_list}}
1274
- {{/if}}
1275
- {{#if launch_cmd}}
1276
- {{next_number}}. Run the app: {{launch_cmd}}
1277
- {{/if}}
1278
- {{next_number}}. Manual smoke test checklist:
1279
- {{#each completed_stories}}
1280
- · [{{story-key}}] {{smoke_test_suggestion}}
1281
- {{/each}}
1282
- ```
1283
- </action>
886
+ <action>Read template `{{project_root}}/_Sprintpilot/templates/sprint-report.txt`, fill mustache placeholders with the collected data, and print the result verbatim as the final message.</action>
1284
887
 
1285
888
  </step>
1286
889
 
@@ -34,8 +34,9 @@
34
34
 
35
35
  6. **Install the update.** On confirmation, run:
36
36
  ```bash
37
- npx @ikunin/sprintpilot@latest@{latest} install --yes
37
+ npx @ikunin/sprintpilot@{latest} install --yes
38
38
  ```
39
+ (`{latest}` is the version resolved in step 2 — e.g., `npx @ikunin/sprintpilot@1.0.4 install --yes`.)
39
40
  Stream the output so the user can see progress.
40
41
 
41
42
  7. **Verify.** Read `_Sprintpilot/manifest.yaml` again and confirm the version updated:
@@ -0,0 +1,24 @@
1
+ # Epic {{epic_id}} — {{epic_title}} — Retrospective
2
+
3
+ **Completed:** {current_date}
4
+ **Stories done:** {{n_done}}/{{n_total}}
5
+
6
+ ## Stories
7
+ {{#each stories}}
8
+ - **{{story-key}}** — {{title}}
9
+ - Tests: {{test_pass_count}}
10
+ - Patches applied: {{patch_count}}
11
+ {{/each}}
12
+
13
+ ## Key decisions
14
+ {{#each decisions}}
15
+ - [{{impact}}] {{category}}: {{decision}} — {{rationale}}
16
+ {{/each}}
17
+
18
+ ## Risks carried forward
19
+ {{#each open_risks}}
20
+ - {{risk}}
21
+ {{/each}}
22
+
23
+ ## Notes
24
+ Generated inline by Sprintpilot autopilot per `autopilot.retrospective_mode: auto`.
@@ -0,0 +1,60 @@
1
+ ╔═══════════════════════════════════════════════════════════════╗
2
+ ║ BMAD AUTOPILOT — REPORT ║
3
+ ╚═══════════════════════════════════════════════════════════════╝
4
+
5
+ SUMMARY
6
+ Stories completed : {{done_count}}/{{total_stories}}
7
+ Epics completed : {{done_epics}}/{{total_epics}}
8
+ Total tests : {{N}}/{{N}} passed
9
+ {{#if git_enabled}}
10
+ Platform : {{platform}}
11
+ {{/if}}
12
+
13
+ STORIES
14
+ {{#each epic}}
15
+ Epic {{epic_number}}: {{epic_title}}
16
+ {{#each stories}}
17
+ ✓ {{story-key}} — {{test_count}} tests{{#if pr_url}} PR: {{pr_url}}{{/if}}
18
+ {{/each}}
19
+ {{/each}}
20
+ {{#if remaining_stories}}
21
+ Not started:
22
+ {{#each remaining_stories}}
23
+ · {{story-key}}
24
+ {{/each}}
25
+ {{/if}}
26
+
27
+ DECISIONS REQUIRING REVIEW (high/medium impact)
28
+ {{#each medium_high_decisions}}
29
+ #{{id}} [{{impact}}] {{story}} / {{phase}}
30
+ {{decision}}
31
+ → {{rationale}}
32
+ {{/each}}
33
+ {{#if no_medium_high_decisions}}
34
+ None — all decisions were low-impact.
35
+ {{/if}}
36
+
37
+ Full log: {decision_log_file}
38
+
39
+ REVIEW FINDINGS APPLIED
40
+ Patches applied : {{total_patches}}
41
+ Findings dismissed : {{total_dismissed}}
42
+ Review rounds : {{total_review_rounds}}
43
+
44
+ CODE REVIEW SUMMARY (per story)
45
+ {{#each completed_stories}}
46
+ {{story-key}} : {{patches_applied}} patches applied, {{findings_dismissed}} dismissed
47
+ {{/each}}
48
+
49
+ WHAT TO DO NEXT
50
+ 1. Review decisions marked medium/high above
51
+ {{#if has_pr_urls}}
52
+ 2. Merge open PRs: {{pr_urls_list}}
53
+ {{/if}}
54
+ {{#if launch_cmd}}
55
+ {{next_number}}. Run the app: {{launch_cmd}}
56
+ {{/if}}
57
+ {{next_number}}. Manual smoke test checklist:
58
+ {{#each completed_stories}}
59
+ · [{{story-key}}] {{smoke_test_suggestion}}
60
+ {{/each}}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ikunin/sprintpilot",
3
- "version": "1.0.4",
3
+ "version": "1.0.5",
4
4
  "description": "Sprintpilot — autopilot and multi-agent addon for BMad Method v6: git workflow, parallel agents, autonomous story execution",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {