@ikunin/sprintpilot 2.2.5 → 2.2.6

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/README.md CHANGED
@@ -192,7 +192,7 @@ Before every commit the orchestrator runs deterministic checks against the stage
192
192
  | **Secrets scan** | Greps staged content for `API_KEY`, `SECRET`, `TOKEN`, `PASSWORD`, `aws_access`, `private_key`. WARN severity by default — surfaced in the log but does not block the commit. Allowlist patterns live in `.secrets-allowlist`. |
193
193
  | **File size** | Rejects files larger than `staging.max_file_size_mb` (default `1`). |
194
194
  | **Binary detection** | Warns on binary files detected via `file --mime-encoding`. |
195
- | **Gitignore check** | Verifies `.gitignore` covers `.autopilot.lock` and `.claude/.addon-backups/`. |
195
+ | **Gitignore check** | Verifies `.gitignore` covers `.autopilot.lock` and `.claude/.sprintpilot-backups/`. |
196
196
 
197
197
  For each story, every commit (the main story commit + each code-review patch commit) runs the full check chain.
198
198
 
@@ -405,18 +405,39 @@ function handleUserInput(state, signal, profile, sideEffects) {
405
405
  // a specific story but autopilot-state.yaml still shows
406
406
  // `current_story: null` — subsequent emissions / persists / verify
407
407
  // checks all reference the wrong story.
408
+ //
409
+ // Phase advance: when the alternative carries `phase` and it's a
410
+ // valid STATES value, also advance state.phase. Pre-v2.2.6 the
411
+ // dispatch was one-shot — the alternative ran for ONE emission then
412
+ // state.phase reverted, defeating use cases like "skip dev_red /
413
+ // dev_green / code_review because the work is already done on the
414
+ // branch from a prior session." The user explicitly proposes the
415
+ // alternative including a target phase; they accept the consequences
416
+ // (e.g. verify may reject the new phase if its preconditions aren't
417
+ // met). Without this, accept_alternative is useless for cycle skips.
408
418
  const dispatch = applied.sideEffects.find((e) => e && e.kind === 'dispatch_action');
409
419
  if (dispatch && dispatch.action) {
410
420
  const a = dispatch.action;
411
421
  const slots = a.template_slots || {};
422
+ const KNOWN_PHASES = new Set(Object.values(STATES));
423
+ const phaseAdvance =
424
+ typeof a.phase === 'string' && KNOWN_PHASES.has(a.phase) && a.phase !== newState.phase
425
+ ? a.phase
426
+ : null;
412
427
  const enrichedState = {
413
428
  ...newState,
429
+ phase: phaseAdvance || newState.phase,
414
430
  story_key: newState.story_key || slots.story_key || a.story_key || null,
415
431
  current_epic:
416
432
  newState.current_epic || slots.current_epic || a.epic_key || null,
417
433
  story_file_path:
418
434
  newState.story_file_path || slots.story_file_path || null,
419
435
  ac_summary: newState.ac_summary || slots.ac_summary || null,
436
+ // Reset retry counters on phase advance so the new phase isn't
437
+ // immediately throttled by a stale retry budget from the phase
438
+ // we just skipped.
439
+ retry_count_this_phase: phaseAdvance ? 0 : newState.retry_count_this_phase,
440
+ verify_reject_count: phaseAdvance ? 0 : newState.verify_reject_count,
420
441
  };
421
442
  return {
422
443
  newState: enrichedState,
@@ -1,6 +1,6 @@
1
1
  addon:
2
2
  name: sprintpilot
3
- version: 2.2.5
3
+ version: 2.2.6
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:
@@ -42,6 +42,6 @@
42
42
  7. **Verify.** Read `_Sprintpilot/manifest.yaml` again and confirm the version updated:
43
43
  ```
44
44
  Updated to v{latest}
45
- Previous version backed up to .claude/.addon-backups/
45
+ Previous version backed up to .claude/.sprintpilot-backups/
46
46
  ```
47
47
  If the version did not change, warn the user that the update may have failed.
@@ -1262,9 +1262,18 @@ async function runInstall(options = {}) {
1262
1262
  for (const tool of selectedTools) {
1263
1263
  const toolDir = getToolDir(tool);
1264
1264
  const skillsDir = path.join(projectRoot, toolDir, 'skills');
1265
- const backupDir = path.join(projectRoot, toolDir, '.addon-backups');
1265
+ // Backup dir was renamed in v2.2.6: `.addon-backups/` → `.sprintpilot-backups/`.
1266
+ // Migrate the legacy name on upgrade so users don't end up with both
1267
+ // an old (stale) and new (active) backup dir side-by-side. Migration
1268
+ // is a single fs.rename — preserves all existing backups.
1269
+ const legacyBackupDir = path.join(projectRoot, toolDir, '.addon-backups');
1270
+ const backupDir = path.join(projectRoot, toolDir, '.sprintpilot-backups');
1271
+ if (!dryRun && (await fs.pathExists(legacyBackupDir)) && !(await fs.pathExists(backupDir))) {
1272
+ await fs.rename(legacyBackupDir, backupDir);
1273
+ console.log(`Migrated ${toolDir}/.addon-backups/ → ${toolDir}/.sprintpilot-backups/`);
1274
+ }
1266
1275
 
1267
- const backupIgnoreEntry = `${toolDir}/.addon-backups/`;
1276
+ const backupIgnoreEntry = `${toolDir}/.sprintpilot-backups/`;
1268
1277
  const backupIgnoreResult = await addIgnoreEntry(ignore.path, backupIgnoreEntry, { dryRun });
1269
1278
  if (backupIgnoreResult.added) {
1270
1279
  const name = path.basename(ignore.path);
@@ -170,10 +170,14 @@ async function runUninstall(options = {}) {
170
170
  totalRemoved += removed;
171
171
  }
172
172
 
173
- const backupDir = path.join(projectRoot, toolDir, '.addon-backups');
174
- if (await fs.pathExists(backupDir)) {
175
- await fs.remove(backupDir);
176
- console.log(`${tool}: removed backup directory`);
173
+ // Remove both the current backup dir AND the legacy name in case the
174
+ // user never ran install after v2.2.6 to migrate it.
175
+ for (const name of ['.sprintpilot-backups', '.addon-backups']) {
176
+ const backupDir = path.join(projectRoot, toolDir, name);
177
+ if (await fs.pathExists(backupDir)) {
178
+ await fs.remove(backupDir);
179
+ console.log(`${tool}: removed ${name}/`);
180
+ }
177
181
  }
178
182
 
179
183
  await removeSystemPrompt(tool, projectRoot);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ikunin/sprintpilot",
3
- "version": "2.2.5",
3
+ "version": "2.2.6",
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": {