@the-bearded-bear/claude-craft 8.26.0-next.2a54921 → 8.26.0-next.3cb6254

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 (34) hide show
  1. package/Dev/i18n/de/Common/commands/ralph-run.md +6 -0
  2. package/Dev/i18n/en/Common/commands/ralph-run.md +6 -0
  3. package/Dev/i18n/es/Common/commands/ralph-run.md +6 -0
  4. package/Dev/i18n/fr/Common/commands/ralph-run.md +6 -0
  5. package/Dev/i18n/pt/Common/commands/ralph-run.md +6 -0
  6. package/Dev/scripts/install-common-rules.sh +8 -0
  7. package/README.md +2 -2
  8. package/Tools/Ralph/lib/checkpoint.sh +3 -3
  9. package/Tools/Ralph/lib/circuit-breaker.sh +10 -10
  10. package/Tools/Ralph/lib/context-manager.sh +56 -65
  11. package/Tools/Ralph/lib/context-reconstruction.sh +46 -79
  12. package/Tools/Ralph/lib/dod-templates.sh +2 -2
  13. package/Tools/Ralph/lib/dod-validator.sh +18 -18
  14. package/Tools/Ralph/lib/escalation-service.sh +9 -7
  15. package/Tools/Ralph/lib/hooks-generator.sh +2 -1
  16. package/Tools/Ralph/lib/loop-finalizer.sh +1 -1
  17. package/Tools/Ralph/lib/loop.sh +44 -10
  18. package/Tools/Ralph/lib/metrics-exporter.sh +1 -1
  19. package/Tools/Ralph/lib/parallel-manager.sh +173 -88
  20. package/Tools/Ralph/lib/project-detector.sh +3 -3
  21. package/Tools/Ralph/lib/recovery-engine.sh +8 -8
  22. package/Tools/Ralph/lib/session.sh +2 -2
  23. package/Tools/Ralph/lib/sprint-conductor.sh +167 -124
  24. package/Tools/Ralph/lib/sprint-progress.sh +9 -9
  25. package/Tools/Ralph/ralph.sh +66 -38
  26. package/Tools/StatusLine/statusline.sh +0 -0
  27. package/Tools/i18n/ralph/de.sh +165 -33
  28. package/Tools/i18n/ralph/en.sh +132 -0
  29. package/Tools/i18n/ralph/es.sh +209 -77
  30. package/Tools/i18n/ralph/fr.sh +234 -102
  31. package/Tools/i18n/ralph/pt.sh +227 -95
  32. package/bundles/cursor/.cursorrules +1 -1
  33. package/bundles/windsurf/.windsurfrules +1 -1
  34. package/package.json +1 -1
@@ -51,6 +51,12 @@ Führt Claude in einer kontinuierlichen Schleife aus, bis die Aufgabe abgeschlos
51
51
  - Maximale Iterationen, Timeouts, DoD-Kriterien festlegen
52
52
  - Hooks initialisieren, falls aktiviert
53
53
 
54
+ #### Isolierung von Sessions und parallelen Workern
55
+
56
+ - **Explizite Claude-Session**: Jede Ralph-Session legt ihre eigene Claude-Konversations-ID fest — `claude --session-id <uuid>` in der ersten Iteration, danach `claude --resume <uuid>`. Die UUID wird in `.ralph/sessions/<session>/claude_session_id` gespeichert, sodass `--continue=<session>` die richtige Konversation fortsetzt. Ralph verwendet `claude --continue` nicht mehr, das „die neueste Konversation im Verzeichnis“ fortsetzte (geteilt zwischen Workern oder sogar mit der interaktiven Session des Benutzers).
57
+ - **Ein Git-Worktree pro Worker** (paralleler Modus, `lib/parallel-manager.sh`): Jede Story läuft in einem eigenen Worktree `.ralph/parallel/worktrees/<session>` auf dem Branch `ralph/<session>`, mit ihrem Ralph-Zustand unter `.ralph/parallel/sessions/<session>/ralph/`. Am Ende des Laufs werden verbleibende Änderungen als `wip(ralph): <story>` auf diesem Branch committet, danach wird der Worktree entfernt (`git worktree remove`). Schlägt dieser Commit fehl, bleibt der Worktree erhalten (kein Datenverlust) und wird in `output.log` gemeldet. Die Arbeit wird durch Mergen der `ralph/*`-Branches übernommen.
58
+ - **Außerhalb eines Git-Repositorys**: kein Worktree; der Worker läuft im aktuellen Verzeichnis (eine Warnung wird ausgegeben), die Isolierung der Claude-Sessions bleibt bestehen.
59
+
54
60
  ### 2. Hauptschleife mit Dashboard
55
61
 
56
62
  ```
@@ -51,6 +51,12 @@ Execute Claude in a continuous loop until the task is complete or the Definition
51
51
  - Set max iterations, timeouts, DoD criteria
52
52
  - Initialize hooks if enabled
53
53
 
54
+ #### Session and parallel worker isolation
55
+
56
+ - **Explicit Claude session**: each Ralph session pins its own Claude conversation ID — `claude --session-id <uuid>` on the first iteration, then `claude --resume <uuid>`. The UUID is stored in `.ralph/sessions/<session>/claude_session_id`, so `--continue=<session>` resumes the right conversation. Ralph no longer uses `claude --continue`, which resumed "the most recent conversation in the directory" (shared between workers, or even with the user's interactive session).
57
+ - **One git worktree per worker** (parallel mode, `lib/parallel-manager.sh`): each story runs in a dedicated worktree `.ralph/parallel/worktrees/<session>` on branch `ralph/<session>`, with its Ralph state under `.ralph/parallel/sessions/<session>/ralph/`. At the end of the run, leftover changes are committed as `wip(ralph): <story>` on that branch, then the worktree is removed (`git worktree remove`). If that commit fails, the worktree is kept (no data loss) and reported in `output.log`. Collect the work by merging the `ralph/*` branches.
58
+ - **Outside a git repository**: no worktree; the worker runs in the current directory (a warning is printed), Claude session isolation still applies.
59
+
54
60
  ### 2. Main Loop with Dashboard
55
61
 
56
62
  ```
@@ -51,6 +51,12 @@ Ejecuta Claude en un bucle continuo hasta que la tarea esté completa o se cumpl
51
51
  - Establecer máximo de iteraciones, timeouts y criterios DoD
52
52
  - Inicializar hooks si están habilitados
53
53
 
54
+ #### Aislamiento de sesiones y workers paralelos
55
+
56
+ - **Sesión de Claude explícita**: cada sesión de Ralph fija su propio identificador de conversación de Claude — `claude --session-id <uuid>` en la primera iteración y luego `claude --resume <uuid>`. El UUID se guarda en `.ralph/sessions/<session>/claude_session_id`, de modo que `--continue=<session>` reanuda la conversación correcta. Ralph ya no usa `claude --continue`, que reanudaba «la conversación más reciente del directorio» (compartida entre workers, o incluso con la sesión interactiva del usuario).
57
+ - **Un worktree de git por worker** (modo paralelo, `lib/parallel-manager.sh`): cada historia se ejecuta en un worktree dedicado `.ralph/parallel/worktrees/<session>` sobre la rama `ralph/<session>`, con su estado de Ralph en `.ralph/parallel/sessions/<session>/ralph/`. Al final de la ejecución, los cambios pendientes se confirman como `wip(ralph): <story>` en esa rama y después se elimina el worktree (`git worktree remove`). Si ese commit falla, el worktree se conserva (sin pérdida de datos) y se indica en `output.log`. El trabajo se recupera fusionando las ramas `ralph/*`.
58
+ - **Fuera de un repositorio git**: sin worktree; el worker se ejecuta en el directorio actual (se muestra un aviso) y el aislamiento de sesiones de Claude se mantiene.
59
+
54
60
  ### 2. Bucle Principal con Dashboard
55
61
 
56
62
  ```
@@ -51,6 +51,12 @@ Exécuter Claude en boucle continue jusqu'à ce que la tâche soit terminée ou
51
51
  - Définir les itérations max, les timeouts, les critères DoD
52
52
  - Initialiser les hooks si activés
53
53
 
54
+ #### Isolation des sessions et des workers parallèles
55
+
56
+ - **Session Claude explicite** : chaque session Ralph fixe son propre identifiant de conversation Claude — `claude --session-id <uuid>` à la première itération, puis `claude --resume <uuid>`. L'UUID est stocké dans `.ralph/sessions/<session>/claude_session_id`, ce qui permet à `--continue=<session>` de reprendre la bonne conversation. Ralph n'utilise plus `claude --continue`, qui reprenait « la conversation la plus récente du répertoire » (partagée entre workers, voire avec une session interactive de l'utilisateur).
57
+ - **Worktree git par worker** (mode parallèle, `lib/parallel-manager.sh`) : chaque story s'exécute dans un worktree dédié `.ralph/parallel/worktrees/<session>` sur la branche `ralph/<session>`, avec son état Ralph sous `.ralph/parallel/sessions/<session>/ralph/`. En fin de run, les modifications restantes sont commitées en `wip(ralph): <story>` sur cette branche, puis le worktree est retiré (`git worktree remove`). Si ce commit échoue, le worktree est conservé (aucune perte) et signalé dans `output.log`. Le travail se récupère en fusionnant les branches `ralph/*`.
58
+ - **Hors dépôt git** : pas de worktree ; le worker s'exécute dans le répertoire courant (avertissement affiché), l'isolation des sessions Claude restant assurée.
59
+
54
60
  ### 2. Boucle principale avec Dashboard
55
61
 
56
62
  ```
@@ -51,6 +51,12 @@ Executa o Claude em loop contínuo até que a tarefa seja concluída ou os crit
51
51
  - Definir máximo de iterações, timeouts e critérios de DoD
52
52
  - Inicializar hooks se habilitados
53
53
 
54
+ #### Isolamento de sessões e workers paralelos
55
+
56
+ - **Sessão Claude explícita**: cada sessão Ralph fixa seu próprio identificador de conversa Claude — `claude --session-id <uuid>` na primeira iteração e depois `claude --resume <uuid>`. O UUID é armazenado em `.ralph/sessions/<session>/claude_session_id`, para que `--continue=<session>` retome a conversa correta. O Ralph não usa mais `claude --continue`, que retomava "a conversa mais recente do diretório" (compartilhada entre workers, ou até com a sessão interativa do usuário).
57
+ - **Um worktree git por worker** (modo paralelo, `lib/parallel-manager.sh`): cada história é executada em um worktree dedicado `.ralph/parallel/worktrees/<session>` na branch `ralph/<session>`, com seu estado Ralph em `.ralph/parallel/sessions/<session>/ralph/`. No fim da execução, as alterações restantes são commitadas como `wip(ralph): <story>` nessa branch e o worktree é removido (`git worktree remove`). Se esse commit falhar, o worktree é mantido (sem perda de dados) e sinalizado em `output.log`. O trabalho é recuperado fazendo merge das branches `ralph/*`.
58
+ - **Fora de um repositório git**: sem worktree; o worker é executado no diretório atual (um aviso é exibido) e o isolamento das sessões Claude continua garantido.
59
+
54
60
  ### 2. Loop Principal com Painel
55
61
 
56
62
  ```
@@ -465,6 +465,14 @@ install_commands() {
465
465
  done
466
466
  IFS="$OLD_IFS"
467
467
 
468
+ # The deprecated common:pre-commit-check|pre-merge-check|release-checklist
469
+ # stubs redirect to /gate:check, which otherwise ships only with the Project
470
+ # package: install it here too so a Dev-only install keeps them working.
471
+ local gate_check="$SCRIPT_DIR/../../Project/i18n/$lang/Gate/commands/check.md"
472
+ if [[ -f "$gate_check" ]]; then
473
+ copy_file "$gate_check" "$target_dir/.claude/commands/gate/check.md"
474
+ fi
475
+
468
476
  # Delete commands retired from the distribution (config/removed-commands.yaml)
469
477
  $dry_run || bash "$SCRIPT_DIR/prune-removed-commands.sh" "$target_dir"
470
478
 
package/README.md CHANGED
@@ -26,7 +26,7 @@ Plus **11 stack-specific reviewers** (@symfony-reviewer, @react-reviewer, @pytho
26
26
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
27
27
  [![Mutation testing badge](https://img.shields.io/endpoint?style=flat&url=https%3A%2F%2Fbadge-api.stryker-mutator.io%2Fgithub.com%2FTheBeardedBearSAS%2Fclaude-craft%2Fmain)](https://dashboard.stryker-mutator.io/reports/github.com/TheBeardedBearSAS/claude-craft/main)
28
28
 
29
- A comprehensive framework for AI-assisted development with [Claude Code](https://claude.ai/code). Install standardized rules, agents, and commands for your projects across multiple technology stacks — **33 specialized agents (+39 infra agents on-demand), 139 commands across 17 namespaces (238 total, including deprecated redirect stubs kept until 9.0), 55 skills**, all token-optimized via `context: fork` and sub-agent model routing.
29
+ A comprehensive framework for AI-assisted development with [Claude Code](https://claude.ai/code). Install standardized rules, agents, and commands for your projects across multiple technology stacks — **33 specialized agents (+39 infra agents on-demand), 140 commands across 18 namespaces (238 total, including deprecated redirect stubs kept until 9.0), 55 skills**, all token-optimized via `context: fork` and sub-agent model routing.
30
30
 
31
31
  ## What's New in v8.26.0
32
32
 
@@ -187,7 +187,7 @@ Claude Code is powerful on its own. Claude Craft makes it **consistent and team-
187
187
 
188
188
  - **Standardized rules** -- SOLID, Clean Architecture, TDD enforced across your team, not just suggested
189
189
  - **33 default agents + 39 infra agents on-demand** -- reviewers, architects, coaches that know your stack deeply (72 total potentially installable)
190
- - **139 slash commands across 17 namespaces** -- repeatable workflows for audits, code generation, sprint management
190
+ - **140 slash commands across 18 namespaces** -- repeatable workflows for audits, code generation, sprint management
191
191
  - **Quality gates** -- automated checks at every stage from PRD to deployment
192
192
  - **5 languages** -- English, French, Spanish, German, Portuguese
193
193
 
@@ -207,12 +207,12 @@ list_checkpoints() {
207
207
 
208
208
  # Find commits matching the checkpoint pattern
209
209
  local commits
210
- commits=$(git log --oneline --grep="checkpoint: Ralph" 2>/dev/null | head -20)
210
+ commits=$(git log --oneline --grep="checkpoint: Ralph" 2>/dev/null | head -20) || true
211
211
 
212
212
  if [[ -n "$commits" ]]; then
213
213
  echo "$commits" | while read -r line; do
214
- local hash=$(echo "$line" | cut -d' ' -f1)
215
- local msg=$(echo "$line" | cut -d' ' -f2-)
214
+ local hash=$(echo "$line" | cut -d' ' -f1) || true
215
+ local msg=$(echo "$line" | cut -d' ' -f2-) || true
216
216
  echo "{\"hash\":\"$hash\",\"message\":\"$msg\"}"
217
217
  done | jq -s '.'
218
218
  else
@@ -30,35 +30,35 @@ CB_LEARNING_MIN_SAMPLES=5
30
30
  # =============================================================================
31
31
  # Each profile defines thresholds optimized for different task types
32
32
 
33
- declare -A CB_PROFILE_QUICK_FIX=(
33
+ declare -gA CB_PROFILE_QUICK_FIX=(
34
34
  ["no_changes"]=2
35
35
  ["errors"]=3
36
36
  ["max_iterations"]=10
37
37
  ["keywords"]="fix bug typo hotfix patch"
38
38
  )
39
39
 
40
- declare -A CB_PROFILE_SMALL_FEATURE=(
40
+ declare -gA CB_PROFILE_SMALL_FEATURE=(
41
41
  ["no_changes"]=3
42
42
  ["errors"]=4
43
43
  ["max_iterations"]=15
44
44
  ["keywords"]="add implement small simple"
45
45
  )
46
46
 
47
- declare -A CB_PROFILE_MEDIUM_FEATURE=(
47
+ declare -gA CB_PROFILE_MEDIUM_FEATURE=(
48
48
  ["no_changes"]=4
49
49
  ["errors"]=6
50
50
  ["max_iterations"]=25
51
51
  ["keywords"]="feature create with tests"
52
52
  )
53
53
 
54
- declare -A CB_PROFILE_LARGE_FEATURE=(
54
+ declare -gA CB_PROFILE_LARGE_FEATURE=(
55
55
  ["no_changes"]=5
56
56
  ["errors"]=8
57
57
  ["max_iterations"]=50
58
58
  ["keywords"]="refactor migrate complex large"
59
59
  )
60
60
 
61
- declare -A CB_PROFILE_EXPLORATION=(
61
+ declare -gA CB_PROFILE_EXPLORATION=(
62
62
  ["no_changes"]=10
63
63
  ["errors"]=15
64
64
  ["max_iterations"]=100
@@ -66,7 +66,7 @@ declare -A CB_PROFILE_EXPLORATION=(
66
66
  )
67
67
 
68
68
  # Autonomous profile for overnight/unattended runs
69
- declare -A CB_PROFILE_AUTONOMOUS=(
69
+ declare -gA CB_PROFILE_AUTONOMOUS=(
70
70
  ["no_changes"]=5
71
71
  ["errors"]=8
72
72
  ["max_iterations"]=50
@@ -162,7 +162,7 @@ init_circuit_breaker() {
162
162
  detect_task_profile() {
163
163
  local prompt="$1"
164
164
  local prompt_lower
165
- prompt_lower=$(echo "$prompt" | tr '[:upper:]' '[:lower:]')
165
+ prompt_lower=$(echo "$prompt" | tr '[:upper:]' '[:lower:]') || true
166
166
 
167
167
  # Check each profile's keywords
168
168
  local profiles=("quick_fix" "small_feature" "medium_feature" "large_feature" "exploration" "autonomous")
@@ -265,7 +265,7 @@ apply_learned_adjustments() {
265
265
  ' "$history_file" 2>/dev/null)
266
266
 
267
267
  local samples
268
- samples=$(echo "$profile_data" | jq -r '.samples // 0')
268
+ samples=$(echo "$profile_data" | jq -r '.samples // 0') || samples=0
269
269
 
270
270
  if [[ $samples -lt $CB_LEARNING_MIN_SAMPLES ]]; then
271
271
  print_verbose "Not enough samples for learning ($samples < $CB_LEARNING_MIN_SAMPLES)"
@@ -274,10 +274,10 @@ apply_learned_adjustments() {
274
274
 
275
275
  # Apply adjustments based on historical success rate
276
276
  local success_rate
277
- success_rate=$(echo "$profile_data" | jq -r '.success_rate // 0')
277
+ success_rate=$(echo "$profile_data" | jq -r '.success_rate // 0') || success_rate=0
278
278
 
279
279
  local avg_iterations
280
- avg_iterations=$(echo "$profile_data" | jq -r '.avg_iterations // 0')
280
+ avg_iterations=$(echo "$profile_data" | jq -r '.avg_iterations // 0') || avg_iterations=0
281
281
 
282
282
  # If success rate is low, increase thresholds
283
283
  if [[ $success_rate -lt 50 ]]; then
@@ -32,6 +32,9 @@ IFS=' ' read -r -a CLAUDE_CMD <<< "$CLAUDE_COMMAND"
32
32
  CONTEXT_COMPACT_COUNT=0
33
33
  CONTEXT_CONTINUATION_COUNT=0
34
34
  CONTEXT_CONTINUATION_PROMPT=""
35
+ # Set by run_auto_compact (return 3) / handle_max_compacts_reached: call them
36
+ # in this shell, never in `$(...)`, or the counters above are lost.
37
+ CONTEXT_NEW_SESSION_ID=""
35
38
 
36
39
  # =============================================================================
37
40
  # Context Limit Detection
@@ -106,6 +109,17 @@ init_context_manager() {
106
109
  print_verbose " - Smart reconstruction: $CONTEXT_SMART_RECONSTRUCTION"
107
110
  }
108
111
 
112
+ # Print the first lines of `/context` for a Claude session UUID.
113
+ # Best effort: prints nothing on failure, always succeeds (set -e safe).
114
+ probe_context_usage() {
115
+ local claude_uuid="$1"
116
+ if command -v timeout &> /dev/null; then
117
+ timeout 10s "${CLAUDE_CMD[@]}" --resume "$claude_uuid" -p "/context" 2>/dev/null | head -5 || true
118
+ else
119
+ "${CLAUDE_CMD[@]}" --resume "$claude_uuid" -p "/context" 2>/dev/null | head -5 || true
120
+ fi
121
+ }
122
+
109
123
  # =============================================================================
110
124
  # Auto-Compact
111
125
  # =============================================================================
@@ -120,20 +134,27 @@ run_auto_compact() {
120
134
  return 1
121
135
  fi
122
136
 
137
+ # /context and /compact must target this Ralph session's Claude session,
138
+ # never "the most recent conversation in this directory" (--continue).
139
+ local claude_uuid
140
+ claude_uuid=$(get_claude_session_uuid "$session_id")
141
+ if [[ -z "$claude_uuid" ]]; then
142
+ log_session "$session_id" "WARN" "No Claude session yet, nothing to compact"
143
+ return 1
144
+ fi
145
+
123
146
  # Check if context is above minimum threshold before compacting
124
147
  if [[ "$CONTEXT_MIN_THRESHOLD" -gt 0 ]]; then
125
148
  local context_info
126
- if command -v timeout &> /dev/null; then
127
- context_info=$(timeout 10s "${CLAUDE_CMD[@]}" -p "/context" 2>/dev/null | head -5)
128
- else
129
- context_info=$("${CLAUDE_CMD[@]}" -p "/context" 2>/dev/null | head -5)
130
- fi
149
+ context_info=$(probe_context_usage "$claude_uuid")
131
150
  local current_usage
132
- current_usage=$(echo "$context_info" | grep -oP '\d+(?=%)' | head -1)
151
+ current_usage=$(echo "$context_info" | grep -oP '\d+(?=%)' | head -1 || true)
133
152
 
134
153
  if [[ -n "$current_usage" && "$current_usage" -lt "$CONTEXT_MIN_THRESHOLD" ]]; then
135
154
  log_session "$session_id" "INFO" "Context at ${current_usage}% - below minimum threshold (${CONTEXT_MIN_THRESHOLD}%), skipping compact"
136
- print_verbose "Context at ${current_usage}% - below min threshold (${CONTEXT_MIN_THRESHOLD}%), skipping compact"
155
+ local msg="${MSG_CONTEXT_BELOW_MIN:-"Context at {0}% - below minimum threshold ({1}%), skipping compact"}"
156
+ msg="${msg//\{0\}/"$current_usage"}"
157
+ print_verbose "${msg//\{1\}/"$CONTEXT_MIN_THRESHOLD"}"
137
158
  return 1
138
159
  fi
139
160
  fi
@@ -144,13 +165,12 @@ run_auto_compact() {
144
165
  print_warning "${MSG_CONTEXT_MAX_REACHED:-Maximum compacts reached} ($CONTEXT_MAX_COMPACTS)"
145
166
 
146
167
  # Handle overflow strategy
147
- local overflow_result
148
- overflow_result=$(handle_max_compacts_reached "$session_id")
149
- local overflow_status=$?
168
+ local overflow_status=0
169
+ CONTEXT_NEW_SESSION_ID=""
170
+ handle_max_compacts_reached "$session_id" || overflow_status=$?
150
171
 
151
- if [[ $overflow_status -eq 0 && -n "$overflow_result" ]]; then
152
- # New session ID returned - signal continuation
153
- echo "$overflow_result"
172
+ if [[ $overflow_status -eq 0 && -n "$CONTEXT_NEW_SESSION_ID" ]]; then
173
+ # New session ID in CONTEXT_NEW_SESSION_ID - signal continuation
154
174
  return 3 # Special return code for session continuation
155
175
  fi
156
176
 
@@ -185,11 +205,10 @@ run_auto_compact() {
185
205
 
186
206
  # Execute /compact via Claude CLI
187
207
  local compact_output
188
- local compact_status
208
+ local compact_status=0
189
209
 
190
210
  if command -v timeout &> /dev/null; then
191
- compact_output=$(timeout 60s "${CLAUDE_CMD[@]}" --continue -p "/compact" 2>&1)
192
- compact_status=$?
211
+ compact_output=$(timeout 60s "${CLAUDE_CMD[@]}" --resume "$claude_uuid" -p "/compact" 2>&1) || compact_status=$?
193
212
 
194
213
  # Handle timeout specifically (exit code 124)
195
214
  if [[ $compact_status -eq 124 ]]; then
@@ -198,8 +217,7 @@ run_auto_compact() {
198
217
  return 1
199
218
  fi
200
219
  else
201
- compact_output=$("${CLAUDE_CMD[@]}" --continue -p "/compact" 2>&1)
202
- compact_status=$?
220
+ compact_output=$("${CLAUDE_CMD[@]}" --resume "$claude_uuid" -p "/compact" 2>&1) || compact_status=$?
203
221
  fi
204
222
 
205
223
  if [[ $compact_status -eq 0 ]]; then
@@ -313,10 +331,12 @@ handle_max_compacts_reached() {
313
331
  new_session_id=$(create_continuation_session "$session_id")
314
332
 
315
333
  if [[ -n "$new_session_id" ]]; then
334
+ # Reset here: create_continuation_session ran in a subshell
335
+ CONTEXT_COMPACT_COUNT=0
316
336
  CONTEXT_CONTINUATION_COUNT=$((CONTEXT_CONTINUATION_COUNT + 1))
317
337
  print_success "${MSG_CONTEXT_CONTINUATION_CREATED:-Continuation session created}: $new_session_id"
318
338
  log_session "$session_id" "INFO" "Created continuation session: $new_session_id"
319
- echo "$new_session_id"
339
+ CONTEXT_NEW_SESSION_ID="$new_session_id"
320
340
  return 0
321
341
  else
322
342
  print_error "Failed to create continuation session"
@@ -349,30 +369,11 @@ handle_max_compacts_reached() {
349
369
  create_continuation_session() {
350
370
  local previous_session="$1"
351
371
 
352
- # Generate new session ID with portable random suffix generation
353
- local timestamp=$(date +%Y%m%d_%H%M%S)
354
- local random_suffix
355
-
356
- # Use utils function if available, otherwise fallback
357
- if type generate_random_suffix &>/dev/null; then
358
- random_suffix=$(generate_random_suffix 4)
359
- elif command -v xxd &>/dev/null; then
360
- random_suffix=$(head -c 4 /dev/urandom | xxd -p)
361
- elif command -v od &>/dev/null; then
362
- random_suffix=$(head -c 4 /dev/urandom | od -An -tx1 | tr -d ' \n')
363
- else
364
- # Fallback: use $RANDOM (less entropy but portable)
365
- random_suffix=$(printf '%04x%04x' $RANDOM $RANDOM | head -c 8)
366
- fi
367
-
368
- local new_session_id="ralph-cont-${timestamp}-${random_suffix}"
369
-
370
- # Create session directory if create_session is available
371
- if type create_session &>/dev/null; then
372
- if ! create_session "$new_session_id" 2>/dev/null; then
373
- print_warning "Failed to create session directory for $new_session_id"
374
- fi
375
- fi
372
+ # create_session takes a prompt and prints the id it created (with its
373
+ # state.json and session.log): the id must come from it, or the session
374
+ # directory is an orphan and the continuation id has no state.
375
+ local new_session_id
376
+ new_session_id=$(create_session "Continuation of $previous_session") || return 1
376
377
 
377
378
  # Link sessions in state files (with error handling)
378
379
  if type update_session_state &>/dev/null; then
@@ -385,10 +386,6 @@ create_continuation_session() {
385
386
  link_session_to_progress "$new_session_id" || print_verbose "Failed to link session to progress"
386
387
  fi
387
388
 
388
- # Reset context manager for new session
389
- CONTEXT_COMPACT_COUNT=0
390
- CONTEXT_CONTINUATION_COUNT=$((CONTEXT_CONTINUATION_COUNT)) # Keep continuation count
391
-
392
389
  echo "$new_session_id"
393
390
  }
394
391
 
@@ -404,26 +401,28 @@ check_preventive_compact() {
404
401
  return 1
405
402
  fi
406
403
 
404
+ # Nothing to measure before the first Claude call of this session.
405
+ local claude_uuid
406
+ claude_uuid=$(get_claude_session_uuid "$session_id")
407
+ [[ -z "$claude_uuid" ]] && return 1
408
+
407
409
  # Try to get context usage from Claude CLI
408
410
  # Note: This is a best-effort check, Claude may not always report usage
409
411
  local context_info
410
- if command -v timeout &> /dev/null; then
411
- context_info=$(timeout 10s "${CLAUDE_CMD[@]}" -p "/context" 2>/dev/null | head -5)
412
- else
413
- context_info=$("${CLAUDE_CMD[@]}" -p "/context" 2>/dev/null | head -5)
414
- fi
412
+ context_info=$(probe_context_usage "$claude_uuid")
415
413
 
416
414
  # Try to extract percentage from output
417
415
  local usage
418
- usage=$(echo "$context_info" | grep -oP '\d+(?=%)' | head -1)
416
+ usage=$(echo "$context_info" | grep -oP '\d+(?=%)' | head -1 || true)
419
417
 
420
418
  if [[ -n "$usage" && "$usage" -ge "$CONTEXT_PREVENTIVE_THRESHOLD" ]]; then
421
- print_warning "${MSG_CONTEXT_PREVENTIVE:-Context at ${usage}% - running preventive compact}"
419
+ local msg="${MSG_CONTEXT_PREVENTIVE:-Context at ${usage}% - running preventive compact}"
420
+ print_warning "${msg//\{0\}/$usage}"
422
421
  log_session "$session_id" "INFO" "Preventive compact triggered at ${usage}%"
423
422
 
424
423
  # Save progress before preventive compact
425
424
  if type save_sprint_progress &>/dev/null; then
426
- save_sprint_progress "$session_id"
425
+ save_sprint_progress "$session_id" || print_verbose "No sprint progress file to save"
427
426
  fi
428
427
 
429
428
  run_auto_compact "$session_id"
@@ -437,23 +436,15 @@ check_preventive_compact() {
437
436
  # Context Continuation Prompt
438
437
  # =============================================================================
439
438
 
439
+ # Print the continuation prompt stored by a real compaction, if any.
440
+ # Callers using `$(...)` must reset CONTEXT_CONTINUATION_PROMPT themselves.
440
441
  get_context_continuation_prompt() {
441
- # Return the stored continuation prompt if available
442
442
  if [[ -n "$CONTEXT_CONTINUATION_PROMPT" ]]; then
443
443
  echo "$CONTEXT_CONTINUATION_PROMPT"
444
- # Clear after use
445
444
  CONTEXT_CONTINUATION_PROMPT=""
446
445
  return 0
447
446
  fi
448
447
 
449
- # Generate fresh if smart reconstruction is enabled
450
- if [[ "$CONTEXT_SMART_RECONSTRUCTION" == "true" ]]; then
451
- if type generate_continuation_prompt &>/dev/null; then
452
- generate_continuation_prompt
453
- return 0
454
- fi
455
- fi
456
-
457
448
  return 1
458
449
  }
459
450