@the-bearded-bear/claude-craft 8.26.0 → 8.27.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.
Files changed (187) hide show
  1. package/Dev/i18n/base/Common/commands/aliases.md +48 -48
  2. package/Dev/i18n/base/Common/skills/sub-agents-patterns/SKILL.md +1 -0
  3. package/Dev/i18n/de/Common/agents/devops-engineer.md +19 -14
  4. package/Dev/i18n/de/Common/agents/migration-specialist.md +10 -15
  5. package/Dev/i18n/de/Common/agents/mlops-engineer.md +19 -15
  6. package/Dev/i18n/de/Common/agents/ralph-conductor.md +4 -15
  7. package/Dev/i18n/de/Common/agents/refactoring-specialist.md +0 -9
  8. package/Dev/i18n/de/Common/agents/tdd-coach.md +0 -9
  9. package/Dev/i18n/de/Common/agents/uiux-orchestrator.md +0 -9
  10. package/Dev/i18n/de/Common/commands/ralph-run.md +52 -0
  11. package/Dev/i18n/de/Common/commands/research-context7.md +5 -253
  12. package/Dev/i18n/de/Common/rules/12-context-management.md +10 -8
  13. package/Dev/i18n/de/Common/templates/settings.json.template +4 -0
  14. package/Dev/i18n/de/Python/skills/coding-standards/SKILL.md +1 -1
  15. package/Dev/i18n/de/Python/skills/testing-python/SKILL.md +1 -1
  16. package/Dev/i18n/de/Python/skills/tooling/SKILL.md +1 -1
  17. package/Dev/i18n/de/Symfony/commands/check-security.md +20 -20
  18. package/Dev/i18n/de/Symfony/skills/aggregates/SKILL.md +1 -1
  19. package/Dev/i18n/de/Symfony/skills/async/SKILL.md +1 -1
  20. package/Dev/i18n/de/Symfony/skills/cqrs/SKILL.md +1 -1
  21. package/Dev/i18n/de/Symfony/skills/domain-events/SKILL.md +1 -1
  22. package/Dev/i18n/de/Symfony/skills/i18n/SKILL.md +1 -1
  23. package/Dev/i18n/de/Symfony/skills/multitenant/SKILL.md +1 -1
  24. package/Dev/i18n/de/Symfony/skills/value-objects/SKILL.md +1 -1
  25. package/Dev/i18n/de/Team/commands/audit.md +29 -4
  26. package/Dev/i18n/de/Team/commands/delivery.md +29 -4
  27. package/Dev/i18n/de/Team/commands/security.md +31 -4
  28. package/Dev/i18n/de/Team/commands/sprint.md +30 -5
  29. package/Dev/i18n/de/UIUX/commands/audit.md +1 -30
  30. package/Dev/i18n/de/UIUX/commands/component-spec.md +1 -5
  31. package/Dev/i18n/de/UIUX/commands/orchestrator.md +5 -98
  32. package/Dev/i18n/de/Workflow/commands/auto-sprint.md +7 -4
  33. package/Dev/i18n/de/Workflow/commands/plan.md +1 -2
  34. package/Dev/i18n/en/Common/agents/devops-engineer.md +19 -14
  35. package/Dev/i18n/en/Common/agents/migration-specialist.md +10 -15
  36. package/Dev/i18n/en/Common/agents/mlops-engineer.md +19 -15
  37. package/Dev/i18n/en/Common/agents/ralph-conductor.md +4 -15
  38. package/Dev/i18n/en/Common/agents/refactoring-specialist.md +0 -9
  39. package/Dev/i18n/en/Common/agents/tdd-coach.md +0 -9
  40. package/Dev/i18n/en/Common/agents/uiux-orchestrator.md +0 -9
  41. package/Dev/i18n/en/Common/commands/ralph-run.md +52 -0
  42. package/Dev/i18n/en/Common/commands/research-context7.md +5 -253
  43. package/Dev/i18n/en/Common/rules/12-context-management.md +12 -10
  44. package/Dev/i18n/en/Common/templates/settings.json.template +4 -0
  45. package/Dev/i18n/en/Python/skills/coding-standards/SKILL.md +1 -1
  46. package/Dev/i18n/en/Python/skills/testing-python/SKILL.md +1 -1
  47. package/Dev/i18n/en/Python/skills/tooling/SKILL.md +1 -1
  48. package/Dev/i18n/en/Symfony/commands/check-security.md +20 -20
  49. package/Dev/i18n/en/Symfony/skills/aggregates/SKILL.md +1 -1
  50. package/Dev/i18n/en/Symfony/skills/async/SKILL.md +1 -1
  51. package/Dev/i18n/en/Symfony/skills/cqrs/SKILL.md +1 -1
  52. package/Dev/i18n/en/Symfony/skills/domain-events/SKILL.md +1 -1
  53. package/Dev/i18n/en/Symfony/skills/i18n/SKILL.md +1 -1
  54. package/Dev/i18n/en/Symfony/skills/multitenant/SKILL.md +1 -1
  55. package/Dev/i18n/en/Symfony/skills/value-objects/SKILL.md +1 -1
  56. package/Dev/i18n/en/Team/commands/audit.md +29 -4
  57. package/Dev/i18n/en/Team/commands/delivery.md +29 -4
  58. package/Dev/i18n/en/Team/commands/security.md +31 -4
  59. package/Dev/i18n/en/Team/commands/sprint.md +30 -5
  60. package/Dev/i18n/en/UIUX/commands/audit.md +1 -30
  61. package/Dev/i18n/en/UIUX/commands/component-spec.md +1 -5
  62. package/Dev/i18n/en/UIUX/commands/orchestrator.md +5 -98
  63. package/Dev/i18n/en/Workflow/commands/auto-sprint.md +7 -4
  64. package/Dev/i18n/en/Workflow/commands/plan.md +1 -2
  65. package/Dev/i18n/es/Common/agents/devops-engineer.md +19 -14
  66. package/Dev/i18n/es/Common/agents/migration-specialist.md +10 -15
  67. package/Dev/i18n/es/Common/agents/mlops-engineer.md +19 -15
  68. package/Dev/i18n/es/Common/agents/ralph-conductor.md +4 -15
  69. package/Dev/i18n/es/Common/agents/refactoring-specialist.md +0 -9
  70. package/Dev/i18n/es/Common/agents/tdd-coach.md +0 -9
  71. package/Dev/i18n/es/Common/agents/uiux-orchestrator.md +0 -9
  72. package/Dev/i18n/es/Common/commands/ralph-run.md +52 -0
  73. package/Dev/i18n/es/Common/commands/research-context7.md +5 -253
  74. package/Dev/i18n/es/Common/rules/12-context-management.md +9 -7
  75. package/Dev/i18n/es/Common/templates/settings.json.template +4 -0
  76. package/Dev/i18n/es/Python/skills/coding-standards/SKILL.md +1 -1
  77. package/Dev/i18n/es/Python/skills/testing-python/SKILL.md +1 -1
  78. package/Dev/i18n/es/Python/skills/tooling/SKILL.md +1 -1
  79. package/Dev/i18n/es/Symfony/commands/check-security.md +20 -20
  80. package/Dev/i18n/es/Symfony/skills/aggregates/SKILL.md +1 -1
  81. package/Dev/i18n/es/Symfony/skills/async/SKILL.md +1 -1
  82. package/Dev/i18n/es/Symfony/skills/cqrs/SKILL.md +1 -1
  83. package/Dev/i18n/es/Symfony/skills/domain-events/SKILL.md +1 -1
  84. package/Dev/i18n/es/Symfony/skills/i18n/SKILL.md +1 -1
  85. package/Dev/i18n/es/Symfony/skills/multitenant/SKILL.md +1 -1
  86. package/Dev/i18n/es/Symfony/skills/value-objects/SKILL.md +1 -1
  87. package/Dev/i18n/es/Team/commands/audit.md +29 -4
  88. package/Dev/i18n/es/Team/commands/delivery.md +29 -4
  89. package/Dev/i18n/es/Team/commands/security.md +31 -4
  90. package/Dev/i18n/es/Team/commands/sprint.md +30 -5
  91. package/Dev/i18n/es/UIUX/commands/audit.md +1 -30
  92. package/Dev/i18n/es/UIUX/commands/component-spec.md +1 -5
  93. package/Dev/i18n/es/UIUX/commands/orchestrator.md +5 -98
  94. package/Dev/i18n/es/Workflow/commands/auto-sprint.md +7 -4
  95. package/Dev/i18n/es/Workflow/commands/plan.md +1 -2
  96. package/Dev/i18n/fr/Common/agents/devops-engineer.md +19 -14
  97. package/Dev/i18n/fr/Common/agents/migration-specialist.md +10 -15
  98. package/Dev/i18n/fr/Common/agents/mlops-engineer.md +19 -15
  99. package/Dev/i18n/fr/Common/agents/ralph-conductor.md +4 -15
  100. package/Dev/i18n/fr/Common/agents/refactoring-specialist.md +0 -9
  101. package/Dev/i18n/fr/Common/agents/tdd-coach.md +0 -9
  102. package/Dev/i18n/fr/Common/agents/uiux-orchestrator.md +0 -9
  103. package/Dev/i18n/fr/Common/commands/ralph-run.md +52 -0
  104. package/Dev/i18n/fr/Common/commands/research-context7.md +5 -253
  105. package/Dev/i18n/fr/Common/rules/12-context-management.md +10 -8
  106. package/Dev/i18n/fr/Common/templates/settings.json.template +4 -0
  107. package/Dev/i18n/fr/React/commands/check-compliance.md +1 -1
  108. package/Dev/i18n/fr/React/commands/check-security.md +2 -2
  109. package/Dev/i18n/fr/Symfony/commands/check-security.md +20 -20
  110. package/Dev/i18n/fr/Symfony/skills/aggregates/SKILL.md +1 -1
  111. package/Dev/i18n/fr/Symfony/skills/async/SKILL.md +1 -1
  112. package/Dev/i18n/fr/Symfony/skills/cqrs/SKILL.md +1 -1
  113. package/Dev/i18n/fr/Symfony/skills/domain-events/SKILL.md +1 -1
  114. package/Dev/i18n/fr/Symfony/skills/i18n/SKILL.md +1 -1
  115. package/Dev/i18n/fr/Symfony/skills/multitenant/SKILL.md +1 -1
  116. package/Dev/i18n/fr/Symfony/skills/value-objects/SKILL.md +1 -1
  117. package/Dev/i18n/fr/Team/commands/audit.md +29 -4
  118. package/Dev/i18n/fr/Team/commands/delivery.md +29 -4
  119. package/Dev/i18n/fr/Team/commands/security.md +31 -4
  120. package/Dev/i18n/fr/Team/commands/sprint.md +30 -5
  121. package/Dev/i18n/fr/UIUX/commands/audit.md +1 -30
  122. package/Dev/i18n/fr/UIUX/commands/component-spec.md +1 -5
  123. package/Dev/i18n/fr/UIUX/commands/orchestrator.md +5 -98
  124. package/Dev/i18n/fr/Workflow/commands/auto-sprint.md +7 -4
  125. package/Dev/i18n/fr/Workflow/commands/plan.md +1 -2
  126. package/Dev/i18n/pt/Common/agents/devops-engineer.md +19 -14
  127. package/Dev/i18n/pt/Common/agents/migration-specialist.md +10 -15
  128. package/Dev/i18n/pt/Common/agents/mlops-engineer.md +19 -15
  129. package/Dev/i18n/pt/Common/agents/ralph-conductor.md +4 -15
  130. package/Dev/i18n/pt/Common/agents/refactoring-specialist.md +0 -9
  131. package/Dev/i18n/pt/Common/agents/tdd-coach.md +0 -9
  132. package/Dev/i18n/pt/Common/agents/uiux-orchestrator.md +0 -9
  133. package/Dev/i18n/pt/Common/commands/ralph-run.md +52 -0
  134. package/Dev/i18n/pt/Common/commands/research-context7.md +5 -253
  135. package/Dev/i18n/pt/Common/rules/12-context-management.md +9 -7
  136. package/Dev/i18n/pt/Common/templates/settings.json.template +4 -0
  137. package/Dev/i18n/pt/Python/skills/coding-standards/SKILL.md +1 -1
  138. package/Dev/i18n/pt/Python/skills/testing-python/SKILL.md +1 -1
  139. package/Dev/i18n/pt/Python/skills/tooling/SKILL.md +1 -1
  140. package/Dev/i18n/pt/Symfony/commands/check-security.md +20 -20
  141. package/Dev/i18n/pt/Symfony/skills/aggregates/SKILL.md +1 -1
  142. package/Dev/i18n/pt/Symfony/skills/async/SKILL.md +1 -1
  143. package/Dev/i18n/pt/Symfony/skills/cqrs/SKILL.md +1 -1
  144. package/Dev/i18n/pt/Symfony/skills/domain-events/SKILL.md +1 -1
  145. package/Dev/i18n/pt/Symfony/skills/i18n/SKILL.md +1 -1
  146. package/Dev/i18n/pt/Symfony/skills/multitenant/SKILL.md +1 -1
  147. package/Dev/i18n/pt/Symfony/skills/value-objects/SKILL.md +1 -1
  148. package/Dev/i18n/pt/Team/commands/audit.md +29 -4
  149. package/Dev/i18n/pt/Team/commands/delivery.md +29 -4
  150. package/Dev/i18n/pt/Team/commands/security.md +31 -4
  151. package/Dev/i18n/pt/Team/commands/sprint.md +30 -5
  152. package/Dev/i18n/pt/UIUX/commands/audit.md +1 -30
  153. package/Dev/i18n/pt/UIUX/commands/component-spec.md +1 -5
  154. package/Dev/i18n/pt/UIUX/commands/orchestrator.md +5 -98
  155. package/Dev/i18n/pt/Workflow/commands/auto-sprint.md +7 -4
  156. package/Dev/i18n/pt/Workflow/commands/plan.md +1 -2
  157. package/Dev/scripts/install-common-rules.sh +8 -0
  158. package/README.md +12 -8
  159. package/Tools/Ralph/lib/checkpoint.sh +3 -3
  160. package/Tools/Ralph/lib/circuit-breaker.sh +10 -10
  161. package/Tools/Ralph/lib/context-manager.sh +56 -65
  162. package/Tools/Ralph/lib/context-reconstruction.sh +46 -79
  163. package/Tools/Ralph/lib/dod-templates.sh +2 -2
  164. package/Tools/Ralph/lib/dod-validator.sh +18 -18
  165. package/Tools/Ralph/lib/escalation-service.sh +9 -7
  166. package/Tools/Ralph/lib/hooks-generator.sh +2 -1
  167. package/Tools/Ralph/lib/loop-finalizer.sh +1 -1
  168. package/Tools/Ralph/lib/loop.sh +44 -10
  169. package/Tools/Ralph/lib/metrics-exporter.sh +1 -1
  170. package/Tools/Ralph/lib/parallel-manager.sh +173 -88
  171. package/Tools/Ralph/lib/project-detector.sh +3 -3
  172. package/Tools/Ralph/lib/recovery-engine.sh +8 -8
  173. package/Tools/Ralph/lib/session.sh +2 -2
  174. package/Tools/Ralph/lib/sprint-conductor.sh +167 -124
  175. package/Tools/Ralph/lib/sprint-progress.sh +9 -9
  176. package/Tools/Ralph/ralph.sh +66 -38
  177. package/Tools/StatusLine/statusline.sh +0 -0
  178. package/Tools/i18n/ralph/de.sh +165 -33
  179. package/Tools/i18n/ralph/en.sh +132 -0
  180. package/Tools/i18n/ralph/es.sh +209 -77
  181. package/Tools/i18n/ralph/fr.sh +234 -102
  182. package/Tools/i18n/ralph/pt.sh +227 -95
  183. package/bundles/cursor/.cursorrules +3 -3
  184. package/bundles/windsurf/.windsurfrules +3 -3
  185. package/cli/lib/doctor.js +1 -1
  186. package/config/removed-commands.yaml +61 -1
  187. package/package.json +1 -1
@@ -129,7 +129,7 @@ EOF
129
129
  PARALLEL_MAX_CONCURRENT="$ASC_PARALLEL_MAX"
130
130
  init_parallel_manager
131
131
  else
132
- print_warning "Parallel manager not available, falling back to sequential"
132
+ print_warning "$(asc_msg MSG_ASC_NO_PARALLEL_MANAGER "Parallel manager not available, falling back to sequential")"
133
133
  ASC_PARALLEL_ENABLED="false"
134
134
  fi
135
135
  fi
@@ -139,14 +139,14 @@ EOF
139
139
  enable_autonomous_mode
140
140
  fi
141
141
 
142
- log_conductor "Initialized Sprint Conductor"
143
- log_conductor " Session: $ASC_SESSION_ID"
144
- log_conductor " Mode: $ASC_MODE"
145
- log_conductor " Max stories: $ASC_MAX_STORIES"
146
- log_conductor " Max runtime: ${ASC_MAX_RUNTIME_HOURS}h"
147
- log_conductor " Parallel: $ASC_PARALLEL_ENABLED (max $ASC_PARALLEL_MAX)"
142
+ log_conductor "$(asc_msg MSG_ASC_INITIALIZED "Initialized Sprint Conductor")"
143
+ log_conductor "$(asc_msg MSG_ASC_INIT_SESSION " Session: {0}" "$ASC_SESSION_ID")"
144
+ log_conductor "$(asc_msg MSG_ASC_INIT_MODE " Mode: {0}" "$ASC_MODE")"
145
+ log_conductor "$(asc_msg MSG_ASC_INIT_MAX_STORIES " Max stories: {0}" "$ASC_MAX_STORIES")"
146
+ log_conductor "$(asc_msg MSG_ASC_INIT_MAX_RUNTIME " Max runtime: {0}h" "$ASC_MAX_RUNTIME_HOURS")"
147
+ log_conductor "$(asc_msg MSG_ASC_INIT_PARALLEL " Parallel: {0} (max {1})" "$ASC_PARALLEL_ENABLED" "$ASC_PARALLEL_MAX")"
148
148
 
149
- print_info "Sprint Conductor initialized: $ASC_SESSION_ID"
149
+ print_info "$(asc_msg MSG_ASC_INITIALIZED_ID "Sprint Conductor initialized: {0}" "$ASC_SESSION_ID")"
150
150
  }
151
151
 
152
152
  # =============================================================================
@@ -164,7 +164,7 @@ run_sprint_conductor() {
164
164
  [[ "$overnight" == "true" ]] && ASC_MODE="bounded"
165
165
  [[ "$supervised" == "true" ]] && ASC_MODE="supervised"
166
166
 
167
- log_conductor "Starting sprint conductor for: $sprint_name (mode: $ASC_MODE)"
167
+ log_conductor "$(asc_msg MSG_ASC_STARTING "Starting sprint conductor for: {0} (mode: {1})" "$sprint_name" "$ASC_MODE")"
168
168
 
169
169
  # Use parallel or sequential execution based on configuration
170
170
  if [[ "$ASC_PARALLEL_ENABLED" == "true" ]] && type process_stories_parallel &>/dev/null; then
@@ -184,20 +184,20 @@ run_sprint_conductor() {
184
184
  run_sprint_conductor_parallel() {
185
185
  local sprint_name="$1"
186
186
 
187
- log_conductor "Running in PARALLEL mode (max $ASC_PARALLEL_MAX concurrent)"
187
+ log_conductor "$(asc_msg MSG_ASC_PARALLEL_MODE "Running in PARALLEL mode (max {0} concurrent)" "$ASC_PARALLEL_MAX")"
188
188
 
189
189
  # Get all stories for the sprint
190
190
  local all_stories
191
191
  all_stories=$(get_all_ready_stories)
192
192
 
193
193
  if [[ -z "$all_stories" ]]; then
194
- log_conductor "No stories available for parallel processing"
194
+ log_conductor "$(asc_msg MSG_ASC_NO_PARALLEL_STORIES "No stories available for parallel processing")"
195
195
  return 0
196
196
  fi
197
197
 
198
198
  local story_count
199
- story_count=$(echo "$all_stories" | wc -w | tr -d ' ')
200
- log_conductor "Found $story_count stories for parallel processing"
199
+ story_count=$(echo "$all_stories" | wc -l | tr -d ' ') || story_count=0
200
+ log_conductor "$(asc_msg MSG_ASC_FOUND_STORIES "Found {0} stories for parallel processing" "$story_count")"
201
201
 
202
202
  # Build dependency graph
203
203
  if type build_dependency_graph &>/dev/null; then
@@ -211,23 +211,17 @@ run_sprint_conductor_parallel() {
211
211
  while [[ $processed -lt $story_count ]]; do
212
212
  # Check stop conditions
213
213
  if ! check_continue_conditions; then
214
- log_conductor "Stop condition met, halting parallel processing"
214
+ log_conductor "$(asc_msg MSG_ASC_STOP_PARALLEL "Stop condition met, halting parallel processing")"
215
215
  break
216
216
  fi
217
217
 
218
- log_conductor "=== Wave $wave ==="
218
+ log_conductor "$(asc_msg MSG_ASC_WAVE "=== Wave {0} ===" "$wave")"
219
219
 
220
- # Collect completed sessions from previous wave
220
+ # Collect completed sessions from previous wave (in this shell: it
221
+ # updates the tracking arrays that free the slots)
221
222
  if type collect_completed_sessions &>/dev/null; then
222
- local completed_stories
223
- completed_stories=$(collect_completed_sessions)
224
-
225
- for story_id in $completed_stories; do
226
- ASC_STORIES_COMPLETED=$((ASC_STORIES_COMPLETED + 1))
227
- ASC_CONSECUTIVE_FAILURES=0
228
- transition_story "$story_id" "review"
229
- log_conductor "Story completed in parallel: $story_id"
230
- done
223
+ collect_completed_sessions > /dev/null
224
+ process_collected_parallel_stories
231
225
  fi
232
226
 
233
227
  # Get stories ready for this wave (dependencies satisfied)
@@ -244,7 +238,7 @@ run_sprint_conductor_parallel() {
244
238
  local active
245
239
  active=$(get_active_count)
246
240
  if [[ $active -gt 0 ]]; then
247
- log_conductor "Waiting for $active active sessions to complete..."
241
+ log_conductor "$(asc_msg MSG_ASC_WAITING_ACTIVE "Waiting for {0} active sessions to complete..." "$active")"
248
242
  sleep 10
249
243
  continue
250
244
  fi
@@ -257,7 +251,7 @@ run_sprint_conductor_parallel() {
257
251
  fi
258
252
 
259
253
  if [[ $pending_escalations -gt 0 ]]; then
260
- log_conductor "Waiting for $pending_escalations pending escalations..."
254
+ log_conductor "$(asc_msg MSG_ASC_WAITING_ESCALATIONS "Waiting for {0} pending escalations..." "$pending_escalations")"
261
255
  sleep 60
262
256
  continue
263
257
  fi
@@ -274,9 +268,13 @@ run_sprint_conductor_parallel() {
274
268
  slots=$ASC_PARALLEL_MAX
275
269
  fi
276
270
 
277
- # Spawn sessions for ready stories (up to available slots)
271
+ # Spawn sessions for ready stories (up to available slots), one id
272
+ # per line: ids may contain spaces
278
273
  local spawned=0
279
- for story_id in $ready_stories; do
274
+ local -a ready_list=()
275
+ mapfile -t ready_list <<< "$ready_stories"
276
+ for story_id in "${ready_list[@]}"; do
277
+ [[ -z "$story_id" ]] && continue
280
278
  [[ $spawned -ge $slots ]] && break
281
279
 
282
280
  # Skip if already processed or in progress
@@ -287,16 +285,16 @@ run_sprint_conductor_parallel() {
287
285
  # Supervised mode - pause for confirmation
288
286
  if [[ "$ASC_MODE" == "supervised" ]]; then
289
287
  if ! confirm_story_start "$story_id"; then
290
- log_conductor "Story skipped by user: $story_id"
288
+ log_conductor "$(asc_msg MSG_ASC_SKIPPED_BY_USER "Story skipped by user: {0}" "$story_id")"
291
289
  ASC_STORIES_SKIPPED=$((ASC_STORIES_SKIPPED + 1))
292
- ((processed++))
290
+ processed=$((processed + 1))
293
291
  continue
294
292
  fi
295
293
  fi
296
294
 
297
295
  # Claim the story
298
296
  if ! claim_story "$story_id"; then
299
- log_conductor "Failed to claim story: $story_id (may be claimed by another process)"
297
+ log_conductor "$(asc_msg MSG_ASC_CLAIM_FAILED_OTHER "Failed to claim story: {0} (may be claimed by another process)" "$story_id")"
300
298
  continue
301
299
  fi
302
300
 
@@ -304,23 +302,26 @@ run_sprint_conductor_parallel() {
304
302
  local prompt
305
303
  prompt=$(build_story_prompt "$story_id")
306
304
 
307
- # Spawn parallel session
305
+ # Spawn parallel session. Not in `$(...)`: the background worker
306
+ # would keep the substitution open until it ends (no parallelism).
308
307
  if type spawn_session &>/dev/null; then
309
- local session_id
310
- session_id=$(spawn_session "$story_id" "$prompt")
308
+ local session_id=""
309
+ if spawn_session "$story_id" "$prompt"; then
310
+ session_id="$PARALLEL_SPAWNED_SESSION_ID"
311
+ fi
311
312
  if [[ -n "$session_id" ]]; then
312
- log_conductor "Spawned parallel session $session_id for story $story_id"
313
- ((spawned++))
314
- ((processed++))
313
+ log_conductor "$(asc_msg MSG_ASC_SPAWNED "Spawned parallel session {0} for story {1}" "$session_id" "$story_id")"
314
+ spawned=$((spawned + 1))
315
+ processed=$((processed + 1))
315
316
  else
316
- log_conductor "Failed to spawn session for $story_id"
317
+ log_conductor "$(asc_msg MSG_ASC_SPAWN_FAILED "Failed to spawn session for {0}" "$story_id")"
317
318
  unclaim_story "$story_id"
318
319
  ASC_STORIES_FAILED=$((ASC_STORIES_FAILED + 1))
319
320
  fi
320
321
  else
321
322
  # Fallback: execute sequentially
322
323
  execute_story_with_ralph "$story_id"
323
- ((processed++))
324
+ processed=$((processed + 1))
324
325
  fi
325
326
  done
326
327
 
@@ -331,7 +332,7 @@ run_sprint_conductor_parallel() {
331
332
  # Create checkpoint
332
333
  create_conductor_checkpoint
333
334
 
334
- ((wave++))
335
+ wave=$((wave + 1))
335
336
 
336
337
  # Small delay between waves
337
338
  sleep 2
@@ -339,18 +340,13 @@ run_sprint_conductor_parallel() {
339
340
 
340
341
  # Wait for remaining sessions
341
342
  if type wait_all_sessions &>/dev/null; then
342
- log_conductor "Waiting for all parallel sessions to complete..."
343
+ log_conductor "$(asc_msg MSG_ASC_WAITING_ALL "Waiting for all parallel sessions to complete...")"
343
344
  wait_all_sessions 0
344
345
 
345
- # Final collection
346
+ # Final collection (wait_all_sessions already collected them)
346
347
  if type collect_completed_sessions &>/dev/null; then
347
- local final_completed
348
- final_completed=$(collect_completed_sessions)
349
-
350
- for story_id in $final_completed; do
351
- ASC_STORIES_COMPLETED=$((ASC_STORIES_COMPLETED + 1))
352
- transition_story "$story_id" "review"
353
- done
348
+ collect_completed_sessions > /dev/null
349
+ process_collected_parallel_stories
354
350
  fi
355
351
  fi
356
352
 
@@ -360,14 +356,42 @@ run_sprint_conductor_parallel() {
360
356
  fi
361
357
  }
362
358
 
359
+ # Count and transition the stories collected from finished workers:
360
+ # completed -> review, failed -> blocked (as in sequential mode).
361
+ process_collected_parallel_stories() {
362
+ local -a completed_list=() failed_list=()
363
+ mapfile -t completed_list <<< "$PARALLEL_COLLECTED_STORIES"
364
+ mapfile -t failed_list <<< "$PARALLEL_FAILED_STORIES"
365
+ PARALLEL_COLLECTED_STORIES=""
366
+ PARALLEL_FAILED_STORIES=""
367
+
368
+ local story_id
369
+ for story_id in "${completed_list[@]}"; do
370
+ [[ -z "$story_id" ]] && continue
371
+ ASC_STORIES_COMPLETED=$((ASC_STORIES_COMPLETED + 1))
372
+ ASC_CONSECUTIVE_FAILURES=0
373
+ transition_story "$story_id" "review"
374
+ log_conductor "$(asc_msg MSG_ASC_PAR_COMPLETED "Story completed in parallel: {0}" "$story_id")"
375
+ done
376
+
377
+ for story_id in "${failed_list[@]}"; do
378
+ [[ -z "$story_id" ]] && continue
379
+ ASC_STORIES_FAILED=$((ASC_STORIES_FAILED + 1))
380
+ ASC_CONSECUTIVE_FAILURES=$((ASC_CONSECUTIVE_FAILURES + 1))
381
+ transition_story "$story_id" "blocked"
382
+ log_conductor "$(asc_msg MSG_ASC_PAR_FAILED "Story failed in parallel: {0}" "$story_id")"
383
+ done
384
+ }
385
+
363
386
  # Check if a story is already in progress (parallel tracking)
364
387
  is_story_in_progress() {
365
388
  local story_id="$1"
366
389
 
367
- # Check in parallel sessions
368
- if [[ -n "${PARALLEL_SESSIONS[$story_id]:-}" ]]; then
369
- return 0
370
- fi
390
+ # Check in parallel sessions (keyed by session id, the value is the story)
391
+ local session_id
392
+ for session_id in "${!PARALLEL_SESSIONS[@]}"; do
393
+ [[ "${PARALLEL_SESSIONS[$session_id]}" == "$story_id" ]] && return 0
394
+ done
371
395
 
372
396
  # Check in BMAD status
373
397
  local bmad_dir="${BMAD_DIR:-.bmad}"
@@ -375,7 +399,7 @@ is_story_in_progress() {
375
399
 
376
400
  if [[ -f "$status_file" ]] && command -v yq &>/dev/null; then
377
401
  local status
378
- status=$(yq ".stories.${story_id}.status" "$status_file" 2>/dev/null)
402
+ status=$(STORY_ID="$story_id" yq '.stories[strenv(STORY_ID)].status' "$status_file" 2>/dev/null)
379
403
  [[ "$status" == "in-progress" ]] && return 0
380
404
  fi
381
405
 
@@ -412,7 +436,7 @@ update_parallel_conductor_state() {
412
436
  run_sprint_conductor_sequential() {
413
437
  local sprint_name="$1"
414
438
 
415
- log_conductor "Running in SEQUENTIAL mode"
439
+ log_conductor "$(asc_msg MSG_ASC_SEQUENTIAL_MODE "Running in SEQUENTIAL mode")"
416
440
 
417
441
  # Main loop
418
442
  while true; do
@@ -426,14 +450,14 @@ run_sprint_conductor_sequential() {
426
450
  next_story=$(get_next_ready_story)
427
451
 
428
452
  if [[ -z "$next_story" ]]; then
429
- log_conductor "No more stories available"
453
+ log_conductor "$(asc_msg MSG_ASC_NO_MORE_STORIES "No more stories available")"
430
454
 
431
455
  # Check escalation queue
432
456
  if type get_pending_count &>/dev/null; then
433
457
  local pending
434
458
  pending=$(get_pending_count)
435
459
  if [[ $pending -gt 0 ]]; then
436
- log_conductor "Waiting for $pending pending escalations..."
460
+ log_conductor "$(asc_msg MSG_ASC_WAITING_ESCALATIONS "Waiting for {0} pending escalations..." "$pending")"
437
461
  if type check_escalation_timeouts &>/dev/null; then
438
462
  check_escalation_timeouts
439
463
  fi
@@ -449,11 +473,11 @@ run_sprint_conductor_sequential() {
449
473
  ASC_CURRENT_STORY="$next_story"
450
474
  update_conductor_state "current" "$next_story"
451
475
 
452
- log_conductor "Processing story: $next_story"
476
+ log_conductor "$(asc_msg MSG_ASC_PROCESSING "Processing story: {0}" "$next_story")"
453
477
 
454
478
  # Claim the story
455
479
  if ! claim_story "$next_story"; then
456
- log_conductor "Failed to claim story: $next_story"
480
+ log_conductor "$(asc_msg MSG_ASC_CLAIM_FAILED "Failed to claim story: {0}" "$next_story")"
457
481
  ASC_STORIES_SKIPPED=$((ASC_STORIES_SKIPPED + 1))
458
482
  continue
459
483
  fi
@@ -461,7 +485,7 @@ run_sprint_conductor_sequential() {
461
485
  # Supervised mode - pause for confirmation
462
486
  if [[ "$ASC_MODE" == "supervised" ]]; then
463
487
  if ! confirm_story_start "$next_story"; then
464
- log_conductor "Story skipped by user: $next_story"
488
+ log_conductor "$(asc_msg MSG_ASC_SKIPPED_BY_USER "Story skipped by user: {0}" "$next_story")"
465
489
  unclaim_story "$next_story"
466
490
  ASC_STORIES_SKIPPED=$((ASC_STORIES_SKIPPED + 1))
467
491
  continue
@@ -469,34 +493,35 @@ run_sprint_conductor_sequential() {
469
493
  fi
470
494
 
471
495
  # Execute story with Ralph
496
+ # `|| status=$?`: a failed story must be counted, not kill the loop.
472
497
  local result
473
- result=$(execute_story_with_ralph "$next_story")
474
- local status=$?
498
+ local status=0
499
+ result=$(execute_story_with_ralph "$next_story") || status=$?
475
500
 
476
501
  # Process result
477
502
  case $status in
478
503
  0)
479
504
  # Success
480
- log_conductor "Story completed: $next_story"
505
+ log_conductor "$(asc_msg MSG_ASC_COMPLETED "Story completed: {0}" "$next_story")"
481
506
  ASC_STORIES_COMPLETED=$((ASC_STORIES_COMPLETED + 1))
482
507
  ASC_CONSECUTIVE_FAILURES=0
483
508
  transition_story "$next_story" "review"
484
509
  ;;
485
510
  1)
486
511
  # Failed but recoverable
487
- log_conductor "Story failed: $next_story"
512
+ log_conductor "$(asc_msg MSG_ASC_FAILED "Story failed: {0}" "$next_story")"
488
513
  ASC_STORIES_FAILED=$((ASC_STORIES_FAILED + 1))
489
514
  ASC_CONSECUTIVE_FAILURES=$((ASC_CONSECUTIVE_FAILURES + 1))
490
515
  transition_story "$next_story" "blocked"
491
516
  ;;
492
517
  2)
493
518
  # Escalated
494
- log_conductor "Story escalated: $next_story"
519
+ log_conductor "$(asc_msg MSG_ASC_ESCALATED "Story escalated: {0}" "$next_story")"
495
520
  # Keep in-progress, will be handled by escalation resolution
496
521
  ;;
497
522
  3)
498
523
  # Skipped
499
- log_conductor "Story skipped: $next_story"
524
+ log_conductor "$(asc_msg MSG_ASC_SKIPPED "Story skipped: {0}" "$next_story")"
500
525
  ASC_STORIES_SKIPPED=$((ASC_STORIES_SKIPPED + 1))
501
526
  transition_story "$next_story" "ready-for-dev"
502
527
  ;;
@@ -521,13 +546,13 @@ check_continue_conditions() {
521
546
  # Check max stories
522
547
  local total_processed=$((ASC_STORIES_COMPLETED + ASC_STORIES_FAILED + ASC_STORIES_SKIPPED))
523
548
  if [[ $total_processed -ge $ASC_MAX_STORIES ]]; then
524
- log_conductor "Max stories reached: $total_processed"
549
+ log_conductor "$(asc_msg MSG_ASC_MAX_STORIES "Max stories reached: {0}" "$total_processed")"
525
550
  return 1
526
551
  fi
527
552
 
528
553
  # Check consecutive failures
529
554
  if [[ $ASC_CONSECUTIVE_FAILURES -ge $ASC_MAX_CONSECUTIVE_FAILURES ]]; then
530
- log_conductor "Max consecutive failures reached: $ASC_CONSECUTIVE_FAILURES"
555
+ log_conductor "$(asc_msg MSG_ASC_MAX_FAILURES "Max consecutive failures reached: {0}" "$ASC_CONSECUTIVE_FAILURES")"
531
556
  return 1
532
557
  fi
533
558
 
@@ -537,7 +562,7 @@ check_continue_conditions() {
537
562
  local runtime_hours=$(((current_time - ASC_START_TIME) / 3600))
538
563
 
539
564
  if [[ $runtime_hours -ge $ASC_MAX_RUNTIME_HOURS ]]; then
540
- log_conductor "Max runtime reached: ${runtime_hours}h"
565
+ log_conductor "$(asc_msg MSG_ASC_MAX_RUNTIME "Max runtime reached: {0}h" "$runtime_hours")"
541
566
  return 1
542
567
  fi
543
568
 
@@ -547,14 +572,14 @@ check_continue_conditions() {
547
572
  current_hour=$(date +%H:%M)
548
573
 
549
574
  if [[ "$current_hour" > "$ASC_STOP_WINDOW" && "$current_hour" < "12:00" ]]; then
550
- log_conductor "Stop window reached: $current_hour (window: $ASC_STOP_WINDOW)"
575
+ log_conductor "$(asc_msg MSG_ASC_STOP_WINDOW "Stop window reached: {0} (window: {1})" "$current_hour" "$ASC_STOP_WINDOW")"
551
576
  return 1
552
577
  fi
553
578
  fi
554
579
 
555
580
  # Check for critical escalations
556
581
  if type has_critical_pending &>/dev/null && has_critical_pending; then
557
- log_conductor "Critical escalation pending - pausing"
582
+ log_conductor "$(asc_msg MSG_ASC_CRITICAL_ESCALATION "Critical escalation pending - pausing")"
558
583
 
559
584
  # Wait for resolution or timeout
560
585
  sleep 300 # 5 minutes
@@ -581,7 +606,7 @@ get_next_ready_story() {
581
606
  fi
582
607
  }
583
608
 
584
- # Get all ready-for-dev stories (for parallel processing)
609
+ # Get all ready-for-dev stories (for parallel processing), one id per line
585
610
  get_all_ready_stories() {
586
611
  local bmad_dir="${BMAD_DIR:-.bmad}"
587
612
  local status_file="$bmad_dir/sprint-status.yaml"
@@ -595,16 +620,12 @@ get_all_ready_stories() {
595
620
  select(.value.status == "ready-for-dev") |
596
621
  select(.value.claimed_by == null or .value.claimed_by == "") |
597
622
  select(
598
- (.value.blocked_by // []) | length == 0 or
599
- (
600
- (.value.blocked_by // []) | all(
601
- . as $dep |
602
- ($stories[$dep].status == "done" or $stories[$dep].status == "review")
603
- )
604
- )
623
+ [(.value.blocked_by // [])[] |
624
+ select($stories[.].status != "done" and $stories[.].status != "review")
625
+ ] | length == 0
605
626
  ) |
606
627
  .key
607
- ' "$status_file" 2>/dev/null | tr '\n' ' '
628
+ ' "$status_file" 2>/dev/null
608
629
  fi
609
630
  }
610
631
 
@@ -621,7 +642,7 @@ get_independent_ready_stories() {
621
642
  select(.value.claimed_by == null or .value.claimed_by == "") |
622
643
  select((.value.blocked_by // []) | length == 0) |
623
644
  .key
624
- ' "$status_file" 2>/dev/null | tr '\n' ' '
645
+ ' "$status_file" 2>/dev/null
625
646
  fi
626
647
  }
627
648
 
@@ -634,11 +655,13 @@ claim_story() {
634
655
  local timestamp
635
656
  timestamp=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
636
657
 
637
- yq -i ".stories.${story_id}.claimed_by = \"$ASC_SESSION_ID\"" "$status_file"
638
- yq -i ".stories.${story_id}.claimed_at = \"$timestamp\"" "$status_file"
639
- yq -i ".stories.${story_id}.status = \"in-progress\"" "$status_file"
658
+ # Ids and values go through strenv(): never parsed as yq expressions
659
+ STORY_ID="$story_id" OWNER="$ASC_SESSION_ID" TS="$timestamp" yq -i '
660
+ .stories[strenv(STORY_ID)].claimed_by = strenv(OWNER) |
661
+ .stories[strenv(STORY_ID)].claimed_at = strenv(TS) |
662
+ .stories[strenv(STORY_ID)].status = "in-progress"' "$status_file"
640
663
 
641
- log_conductor "Claimed story: $story_id"
664
+ log_conductor "$(asc_msg MSG_ASC_CLAIMED "Claimed story: {0}" "$story_id")"
642
665
  return 0
643
666
  fi
644
667
 
@@ -651,10 +674,9 @@ unclaim_story() {
651
674
  local status_file="$bmad_dir/sprint-status.yaml"
652
675
 
653
676
  if [[ -f "$status_file" ]] && command -v yq &>/dev/null; then
654
- yq -i ".stories.${story_id}.claimed_by = \"\"" "$status_file"
655
- yq -i ".stories.${story_id}.claimed_at = \"\"" "$status_file"
677
+ STORY_ID="$story_id" yq -i '.stories[strenv(STORY_ID)].claimed_by = "" | .stories[strenv(STORY_ID)].claimed_at = ""' "$status_file"
656
678
 
657
- log_conductor "Unclaimed story: $story_id"
679
+ log_conductor "$(asc_msg MSG_ASC_UNCLAIMED "Unclaimed story: {0}" "$story_id")"
658
680
  return 0
659
681
  fi
660
682
 
@@ -678,11 +700,12 @@ transition_story() {
678
700
  local timestamp
679
701
  timestamp=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
680
702
 
681
- yq -i ".stories.${story_id}.previous_status = .stories.${story_id}.status" "$status_file"
682
- yq -i ".stories.${story_id}.status = \"$new_status\"" "$status_file"
683
- yq -i ".stories.${story_id}.updated_at = \"$timestamp\"" "$status_file"
703
+ STORY_ID="$story_id" NEW_STATUS="$new_status" TS="$timestamp" yq -i '
704
+ .stories[strenv(STORY_ID)].previous_status = .stories[strenv(STORY_ID)].status |
705
+ .stories[strenv(STORY_ID)].status = strenv(NEW_STATUS) |
706
+ .stories[strenv(STORY_ID)].updated_at = strenv(TS)' "$status_file"
684
707
 
685
- log_conductor "Transitioned $story_id to $new_status"
708
+ log_conductor "$(asc_msg MSG_ASC_TRANSITIONED "Transitioned {0} to {1}" "$story_id" "$new_status")"
686
709
  return 0
687
710
  fi
688
711
 
@@ -696,16 +719,17 @@ transition_story() {
696
719
  execute_story_with_ralph() {
697
720
  local story_id="$1"
698
721
 
699
- log_conductor "Spawning Ralph for story: $story_id"
722
+ log_conductor "$(asc_msg MSG_ASC_SPAWNING_RALPH "Spawning Ralph for story: {0}" "$story_id")"
700
723
 
701
724
  # Build prompt for Ralph
702
725
  local prompt
703
726
  prompt=$(build_story_prompt "$story_id")
704
727
 
705
- # Get story-specific config if available
706
- local story_config=""
707
- if [[ -f ".ralph/stories/${story_id}.yml" ]]; then
708
- story_config="--config=.ralph/stories/${story_id}.yml"
728
+ # Get story-specific config if available. An id with `/` (e.g. `../x`)
729
+ # would read a config outside .ralph/stories/: ignored.
730
+ local -a story_config=()
731
+ if [[ "$story_id" != */* && -f ".ralph/stories/${story_id}.yml" ]]; then
732
+ story_config=("--config=.ralph/stories/${story_id}.yml")
709
733
  fi
710
734
 
711
735
  # Run Ralph with autonomous profile
@@ -715,7 +739,7 @@ execute_story_with_ralph() {
715
739
  local output
716
740
  output=$("$ralph_script" \
717
741
  --lang="${LANG_ARG:-en}" \
718
- $story_config \
742
+ "${story_config[@]}" \
719
743
  "$prompt" 2>&1)
720
744
  local status=$?
721
745
 
@@ -733,7 +757,7 @@ execute_story_with_ralph() {
733
757
  ;;
734
758
  esac
735
759
  else
736
- log_conductor "Ralph script not found: $ralph_script"
760
+ log_conductor "$(asc_msg MSG_ASC_RALPH_NOT_FOUND "Ralph script not found: {0}" "$ralph_script")"
737
761
  return 1
738
762
  fi
739
763
  }
@@ -747,9 +771,9 @@ build_story_prompt() {
747
771
 
748
772
  if [[ -f "$status_file" ]] && command -v yq &>/dev/null; then
749
773
  local title description acceptance_criteria
750
- title=$(yq ".stories.${story_id}.title" "$status_file" 2>/dev/null)
751
- description=$(yq ".stories.${story_id}.description" "$status_file" 2>/dev/null)
752
- acceptance_criteria=$(yq ".stories.${story_id}.acceptance_criteria[]" "$status_file" 2>/dev/null | tr '\n' '; ')
774
+ title=$(STORY_ID="$story_id" yq '.stories[strenv(STORY_ID)].title' "$status_file" 2>/dev/null)
775
+ description=$(STORY_ID="$story_id" yq '.stories[strenv(STORY_ID)].description' "$status_file" 2>/dev/null)
776
+ acceptance_criteria=$(STORY_ID="$story_id" yq '.stories[strenv(STORY_ID)].acceptance_criteria[]' "$status_file" 2>/dev/null | tr '\n' '; ') || true
753
777
 
754
778
  prompt="Implement user story: $title
755
779
 
@@ -774,10 +798,10 @@ confirm_story_start() {
774
798
 
775
799
  echo ""
776
800
  echo "════════════════════════════════════════"
777
- echo " Supervised Mode: Story Ready"
801
+ echo " $(asc_msg MSG_ASC_SUPERVISED_TITLE "Supervised Mode: Story Ready")"
778
802
  echo "════════════════════════════════════════"
779
803
  echo ""
780
- echo "Story: $story_id"
804
+ echo "$(asc_msg MSG_ASC_SUPERVISED_STORY "Story: {0}" "$story_id")"
781
805
 
782
806
  if command -v yq &>/dev/null; then
783
807
  local bmad_dir="${BMAD_DIR:-.bmad}"
@@ -785,13 +809,13 @@ confirm_story_start() {
785
809
 
786
810
  if [[ -f "$status_file" ]]; then
787
811
  local title
788
- title=$(yq ".stories.${story_id}.title" "$status_file" 2>/dev/null)
789
- echo "Title: $title"
812
+ title=$(STORY_ID="$story_id" yq '.stories[strenv(STORY_ID)].title' "$status_file" 2>/dev/null)
813
+ echo "$(asc_msg MSG_ASC_SUPERVISED_TITLE_LINE "Title: {0}" "$title")"
790
814
  fi
791
815
  fi
792
816
 
793
817
  echo ""
794
- read -rp "Start this story? (y/n/s for skip all): " response
818
+ read -rp "$(asc_msg MSG_ASC_SUPERVISED_PROMPT "Start this story? (y/n/s for skip all): ")" response
795
819
 
796
820
  case "$response" in
797
821
  y|Y) return 0 ;;
@@ -814,7 +838,7 @@ update_conductor_state() {
814
838
 
815
839
  case "$field" in
816
840
  current)
817
- yq -i ".stories.current = \"$value\"" "$ASC_STATE_FILE"
841
+ VALUE="$value" yq -i '.stories.current = strenv(VALUE)' "$ASC_STATE_FILE"
818
842
  ;;
819
843
  stories)
820
844
  yq -i ".stories.completed = $ASC_STORIES_COMPLETED" "$ASC_STATE_FILE"
@@ -822,7 +846,7 @@ update_conductor_state() {
822
846
  yq -i ".stories.skipped = $ASC_STORIES_SKIPPED" "$ASC_STATE_FILE"
823
847
  ;;
824
848
  status)
825
- yq -i ".status = \"$value\"" "$ASC_STATE_FILE"
849
+ VALUE="$value" yq -i '.status = strenv(VALUE)' "$ASC_STATE_FILE"
826
850
  ;;
827
851
  esac
828
852
  }
@@ -835,9 +859,9 @@ create_conductor_checkpoint() {
835
859
  local timestamp
836
860
  timestamp=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
837
861
 
838
- yq -i ".checkpoints += [{
839
- \"timestamp\": \"$timestamp\",
840
- \"story\": \"$ASC_CURRENT_STORY\",
862
+ TS="$timestamp" STORY="$ASC_CURRENT_STORY" yq -i ".checkpoints += [{
863
+ \"timestamp\": strenv(TS),
864
+ \"story\": strenv(STORY),
841
865
  \"completed\": $ASC_STORIES_COMPLETED,
842
866
  \"failed\": $ASC_STORIES_FAILED,
843
867
  \"skipped\": $ASC_STORIES_SKIPPED
@@ -848,6 +872,19 @@ create_conductor_checkpoint() {
848
872
  # Logging
849
873
  # =============================================================================
850
874
 
875
+ # Leader message in the --lang language: asc_msg <MSG_ var> <English default>
876
+ # [values for {0} {1}...] (MSG_ASC_* strings live in Tools/i18n/ralph/<lang>.sh)
877
+ asc_msg() {
878
+ local msg="${!1:-$2}"
879
+ shift 2
880
+ local i=0 value
881
+ for value in "$@"; do
882
+ msg="${msg//\{$i\}/"$value"}"
883
+ i=$((i + 1))
884
+ done
885
+ printf '%s' "$msg"
886
+ }
887
+
851
888
  log_conductor() {
852
889
  local message="$1"
853
890
  local timestamp
@@ -866,7 +903,7 @@ print_header_conductor() {
866
903
 
867
904
  echo ""
868
905
  echo -e "${C_CYAN}╔════════════════════════════════════════════════════════════╗${C_RESET}"
869
- echo -e "${C_CYAN}║${C_RESET} ${C_BOLD}Autonomous Sprint Conductor (ASC)${C_RESET} ${C_CYAN}║${C_RESET}"
906
+ echo -e "${C_CYAN}║${C_RESET} ${C_BOLD}$(asc_msg MSG_ASC_HEADER "Autonomous Sprint Conductor (ASC)")${C_RESET} ${C_CYAN}║${C_RESET}"
870
907
  echo -e "${C_CYAN}║${C_RESET} ${C_DIM}$sprint_name${C_RESET} ${C_CYAN}║${C_RESET}"
871
908
  echo -e "${C_CYAN}╚════════════════════════════════════════════════════════════╝${C_RESET}"
872
909
  echo ""
@@ -897,33 +934,33 @@ finalize_sprint_conductor() {
897
934
  # Print summary
898
935
  echo ""
899
936
  echo "════════════════════════════════════════"
900
- echo " Sprint Conductor Summary"
937
+ echo " $(asc_msg MSG_ASC_SUMMARY "Sprint Conductor Summary")"
901
938
  echo "════════════════════════════════════════"
902
939
  echo ""
903
- echo "Session: $ASC_SESSION_ID"
904
- echo "Duration: ${duration_hours}h ${duration_mins}m"
940
+ echo "$(asc_msg MSG_ASC_SUMMARY_SESSION "Session: {0}" "$ASC_SESSION_ID")"
941
+ echo "$(asc_msg MSG_ASC_SUMMARY_DURATION "Duration: {0}h {1}m" "$duration_hours" "$duration_mins")"
905
942
  echo ""
906
- echo "Stories:"
907
- echo " Completed: $ASC_STORIES_COMPLETED"
908
- echo " Failed: $ASC_STORIES_FAILED"
909
- echo " Skipped: $ASC_STORIES_SKIPPED"
943
+ echo "$(asc_msg MSG_ASC_SUMMARY_STORIES "Stories:")"
944
+ echo "$(asc_msg MSG_ASC_SUMMARY_COMPLETED " Completed: {0}" "$ASC_STORIES_COMPLETED")"
945
+ echo "$(asc_msg MSG_ASC_SUMMARY_FAILED " Failed: {0}" "$ASC_STORIES_FAILED")"
946
+ echo "$(asc_msg MSG_ASC_SUMMARY_SKIPPED " Skipped: {0}" "$ASC_STORIES_SKIPPED")"
910
947
  echo ""
911
948
 
912
949
  # Show escalation stats if available
913
950
  if type get_escalation_stats &>/dev/null; then
914
- echo "Escalations:"
951
+ echo "$(asc_msg MSG_ASC_SUMMARY_ESCALATIONS "Escalations:")"
915
952
  get_escalation_stats | jq -r 'to_entries[] | " \(.key): \(.value)"' 2>/dev/null || echo " N/A"
916
953
  echo ""
917
954
  fi
918
955
 
919
956
  # Show recovery stats if available
920
957
  if type get_recovery_stats &>/dev/null; then
921
- echo "Recovery attempts:"
958
+ echo "$(asc_msg MSG_ASC_SUMMARY_RECOVERY "Recovery attempts:")"
922
959
  get_recovery_stats "$ASC_SESSION_ID" | jq -r '.total_attempts // 0' 2>/dev/null || echo " N/A"
923
960
  echo ""
924
961
  fi
925
962
 
926
- log_conductor "Sprint Conductor completed"
963
+ log_conductor "$(asc_msg MSG_ASC_DONE "Sprint Conductor completed")"
927
964
  }
928
965
 
929
966
  generate_conductor_metrics() {
@@ -969,11 +1006,17 @@ cmd_sprint_conductor() {
969
1006
  --parallel) parallel="$2"; ASC_PARALLEL_ENABLED="true"; ASC_PARALLEL_MAX="$2"; shift 2 ;;
970
1007
  --max-stories) max_stories="$2"; shift 2 ;;
971
1008
  --timeout) timeout="$2"; shift 2 ;;
1009
+ --lang=*) LANG_ARG="${1#--lang=}"; shift ;;
972
1010
  -*) shift ;;
973
1011
  *) sprint_name="$1"; shift ;;
974
1012
  esac
975
1013
  done
976
1014
 
1015
+ # Messages were loaded before --lang was read: reload them for the leader
1016
+ if type load_messages &>/dev/null; then
1017
+ load_messages
1018
+ fi
1019
+
977
1020
  if [[ -z "$sprint_name" ]]; then
978
1021
  echo "Usage: sprint-conductor.sh [options] <sprint-name>"
979
1022
  echo ""