@ikunin/sprintpilot 2.2.12 → 2.2.13

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.
@@ -695,6 +695,13 @@ function composeRuntimeState(persisted, profile, projectRoot) {
695
695
  // across halts so the next session re-emits the prompt rather than
696
696
  // silently dropping the LLM's proposal.
697
697
  pending_alternative: persisted.pending_alternative || null,
698
+ // session_story_limit counter: per-session count of stories completed.
699
+ // adapt.advanceState increments on STORY_DONE → EPIC_BOUNDARY_CHECK;
700
+ // state-machine.nextAction emits a halt when this hits profile.session_story_limit.
701
+ // cmdStart resets to 0 on each new session boot (the limit is per-session,
702
+ // not lifetime). Persisted across in-session resumes so a `pause` mid-flow
703
+ // doesn't reset progress against the limit.
704
+ session_stories_completed: persisted.session_stories_completed || 0,
698
705
  // halt_requested is intentionally NOT carried forward here: cmdStart
699
706
  // clears it on each new session (a `pause` cleanly halts THIS session
700
707
  // and the next /sprint-autopilot-on resumes normally).
@@ -723,6 +730,7 @@ function persistRuntimeState(runtime, profile, projectRoot) {
723
730
  story_queue: Array.isArray(runtime.story_queue) ? runtime.story_queue : [],
724
731
  land_pending: runtime.land_pending,
725
732
  pending_alternative: runtime.pending_alternative || null,
733
+ session_stories_completed: runtime.session_stories_completed || 0,
726
734
  };
727
735
  return persistState(updates, profile, projectRoot, runtime.story_key || 'sprint');
728
736
  }
@@ -1133,6 +1141,12 @@ function cmdStart(opts) {
1133
1141
  // profile-aware initial phase when persisted state is empty.
1134
1142
  const runtime = composeRuntimeState(persisted, profile, projectRoot);
1135
1143
 
1144
+ // session_story_limit is per-session — a fresh `autopilot start`
1145
+ // resets the counter so the next batch of N stories can run before
1146
+ // the next halt. (state-machine.nextAction enforces the cap; adapt.js
1147
+ // increments on STORY_DONE → EPIC_BOUNDARY_CHECK.)
1148
+ runtime.session_stories_completed = 0;
1149
+
1136
1150
  const lockResult = lockUserBranchIfNeeded(runtime, profile, projectRoot);
1137
1151
  if (lockResult && lockResult.halt) {
1138
1152
  ledger.append(
@@ -592,6 +592,10 @@ function advanceState(state, profile, newPhase, signal) {
592
592
  next.story_key = null;
593
593
  next.story_file_path = null;
594
594
  next.ac_summary = null;
595
+ // session_story_limit: increment per-session completion counter so
596
+ // state-machine.js#nextAction can emit the halt at the next
597
+ // emission. The counter resets on cmdStart (new session boundary).
598
+ next.session_stories_completed = (state.session_stories_completed || 0) + 1;
595
599
  }
596
600
 
597
601
  return next;
@@ -170,6 +170,37 @@ function nextAction(state, profile) {
170
170
  handoff: 'sprint_finalize_pending',
171
171
  };
172
172
  }
173
+ // session_story_limit: when this session has completed >= limit
174
+ // stories, halt cleanly. The next /sprint-autopilot-on resets the
175
+ // counter and continues. Skipped when limit === 0 (unlimited per
176
+ // Sprintpilot.md) or limit is unset.
177
+ const sessionLimit = profile && profile.session_story_limit;
178
+ const sessionDone = state.session_stories_completed || 0;
179
+ if (
180
+ typeof sessionLimit === 'number' &&
181
+ sessionLimit > 0 &&
182
+ sessionDone >= sessionLimit &&
183
+ // Don't halt when we're at a story-start phase — that would
184
+ // create an infinite halt loop on resume. The limit check should
185
+ // fire at the boundary between stories (epic_boundary_check or
186
+ // before the next story is picked). Most natural is to halt
187
+ // before emitting the next story-start action.
188
+ (state.phase === STATES.EPIC_BOUNDARY_CHECK ||
189
+ state.phase === STATES.RETROSPECTIVE ||
190
+ state.phase === STATES.PREPARE_STORY_BRANCH ||
191
+ state.phase === STATES.CREATE_STORY ||
192
+ state.phase === STATES.NANO_QUICK_DEV)
193
+ ) {
194
+ return {
195
+ type: 'halt',
196
+ reason: 'session_story_limit_reached',
197
+ prompt:
198
+ `Session story limit reached (${sessionDone}/${sessionLimit}). ` +
199
+ `Run /sprint-autopilot-on to start a new session and continue with the next pending story.`,
200
+ session_stories_completed: sessionDone,
201
+ session_story_limit: sessionLimit,
202
+ };
203
+ }
173
204
 
174
205
  switch (state.phase) {
175
206
  case STATES.PREPARE_STORY_BRANCH: {
@@ -1,6 +1,6 @@
1
1
  addon:
2
2
  name: sprintpilot
3
- version: 2.2.12
3
+ version: 2.2.13
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:
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ikunin/sprintpilot",
3
- "version": "2.2.12",
3
+ "version": "2.2.13",
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": {