@the-bearded-bear/claude-craft 8.26.0-next.2a54921 → 8.26.0-next.36e6dd8
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/Dev/i18n/de/Common/commands/ralph-run.md +6 -0
- package/Dev/i18n/en/Common/commands/ralph-run.md +6 -0
- package/Dev/i18n/es/Common/commands/ralph-run.md +6 -0
- package/Dev/i18n/fr/Common/commands/ralph-run.md +6 -0
- package/Dev/i18n/pt/Common/commands/ralph-run.md +6 -0
- package/Tools/Ralph/lib/checkpoint.sh +3 -3
- package/Tools/Ralph/lib/circuit-breaker.sh +10 -10
- package/Tools/Ralph/lib/context-manager.sh +53 -43
- package/Tools/Ralph/lib/context-reconstruction.sh +13 -51
- package/Tools/Ralph/lib/dod-templates.sh +2 -2
- package/Tools/Ralph/lib/dod-validator.sh +18 -18
- package/Tools/Ralph/lib/escalation-service.sh +9 -7
- package/Tools/Ralph/lib/hooks-generator.sh +2 -1
- package/Tools/Ralph/lib/loop-finalizer.sh +1 -1
- package/Tools/Ralph/lib/loop.sh +44 -10
- package/Tools/Ralph/lib/metrics-exporter.sh +1 -1
- package/Tools/Ralph/lib/parallel-manager.sh +136 -62
- package/Tools/Ralph/lib/project-detector.sh +3 -3
- package/Tools/Ralph/lib/recovery-engine.sh +8 -8
- package/Tools/Ralph/lib/session.sh +2 -2
- package/Tools/Ralph/lib/sprint-conductor.sh +47 -42
- package/Tools/Ralph/lib/sprint-progress.sh +9 -9
- package/Tools/Ralph/ralph.sh +40 -27
- package/Tools/StatusLine/statusline.sh +0 -0
- package/Tools/i18n/ralph/de.sh +81 -33
- package/Tools/i18n/ralph/en.sh +48 -0
- package/Tools/i18n/ralph/es.sh +125 -77
- package/Tools/i18n/ralph/fr.sh +150 -102
- package/Tools/i18n/ralph/pt.sh +143 -95
- 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
|
```
|
|
@@ -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 -
|
|
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 -
|
|
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 -
|
|
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 -
|
|
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 -
|
|
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 -
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
148
|
-
|
|
149
|
-
|
|
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 "$
|
|
152
|
-
# New session ID
|
|
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[@]}" --
|
|
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[@]}" --
|
|
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
|
-
|
|
339
|
+
CONTEXT_NEW_SESSION_ID="$new_session_id"
|
|
320
340
|
return 0
|
|
321
341
|
else
|
|
322
342
|
print_error "Failed to create continuation session"
|
|
@@ -359,10 +379,10 @@ create_continuation_session() {
|
|
|
359
379
|
elif command -v xxd &>/dev/null; then
|
|
360
380
|
random_suffix=$(head -c 4 /dev/urandom | xxd -p)
|
|
361
381
|
elif command -v od &>/dev/null; then
|
|
362
|
-
random_suffix=$(head -c 4 /dev/urandom | od -An -tx1 | tr -d ' \n')
|
|
382
|
+
random_suffix=$(head -c 4 /dev/urandom | od -An -tx1 | tr -d ' \n') || true
|
|
363
383
|
else
|
|
364
384
|
# Fallback: use $RANDOM (less entropy but portable)
|
|
365
|
-
random_suffix=$(printf '%04x%04x' $RANDOM $RANDOM | head -c 8)
|
|
385
|
+
random_suffix=$(printf '%04x%04x' $RANDOM $RANDOM | head -c 8) || true
|
|
366
386
|
fi
|
|
367
387
|
|
|
368
388
|
local new_session_id="ralph-cont-${timestamp}-${random_suffix}"
|
|
@@ -385,10 +405,6 @@ create_continuation_session() {
|
|
|
385
405
|
link_session_to_progress "$new_session_id" || print_verbose "Failed to link session to progress"
|
|
386
406
|
fi
|
|
387
407
|
|
|
388
|
-
# Reset context manager for new session
|
|
389
|
-
CONTEXT_COMPACT_COUNT=0
|
|
390
|
-
CONTEXT_CONTINUATION_COUNT=$((CONTEXT_CONTINUATION_COUNT)) # Keep continuation count
|
|
391
|
-
|
|
392
408
|
echo "$new_session_id"
|
|
393
409
|
}
|
|
394
410
|
|
|
@@ -404,26 +420,28 @@ check_preventive_compact() {
|
|
|
404
420
|
return 1
|
|
405
421
|
fi
|
|
406
422
|
|
|
423
|
+
# Nothing to measure before the first Claude call of this session.
|
|
424
|
+
local claude_uuid
|
|
425
|
+
claude_uuid=$(get_claude_session_uuid "$session_id")
|
|
426
|
+
[[ -z "$claude_uuid" ]] && return 1
|
|
427
|
+
|
|
407
428
|
# Try to get context usage from Claude CLI
|
|
408
429
|
# Note: This is a best-effort check, Claude may not always report usage
|
|
409
430
|
local context_info
|
|
410
|
-
|
|
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
|
|
431
|
+
context_info=$(probe_context_usage "$claude_uuid")
|
|
415
432
|
|
|
416
433
|
# Try to extract percentage from output
|
|
417
434
|
local usage
|
|
418
|
-
usage=$(echo "$context_info" | grep -oP '\d+(?=%)' | head -1)
|
|
435
|
+
usage=$(echo "$context_info" | grep -oP '\d+(?=%)' | head -1 || true)
|
|
419
436
|
|
|
420
437
|
if [[ -n "$usage" && "$usage" -ge "$CONTEXT_PREVENTIVE_THRESHOLD" ]]; then
|
|
421
|
-
|
|
438
|
+
local msg="${MSG_CONTEXT_PREVENTIVE:-Context at ${usage}% - running preventive compact}"
|
|
439
|
+
print_warning "${msg//\{0\}/$usage}"
|
|
422
440
|
log_session "$session_id" "INFO" "Preventive compact triggered at ${usage}%"
|
|
423
441
|
|
|
424
442
|
# Save progress before preventive compact
|
|
425
443
|
if type save_sprint_progress &>/dev/null; then
|
|
426
|
-
save_sprint_progress "$session_id"
|
|
444
|
+
save_sprint_progress "$session_id" || print_verbose "No sprint progress file to save"
|
|
427
445
|
fi
|
|
428
446
|
|
|
429
447
|
run_auto_compact "$session_id"
|
|
@@ -437,23 +455,15 @@ check_preventive_compact() {
|
|
|
437
455
|
# Context Continuation Prompt
|
|
438
456
|
# =============================================================================
|
|
439
457
|
|
|
458
|
+
# Print the continuation prompt stored by a real compaction, if any.
|
|
459
|
+
# Callers using `$(...)` must reset CONTEXT_CONTINUATION_PROMPT themselves.
|
|
440
460
|
get_context_continuation_prompt() {
|
|
441
|
-
# Return the stored continuation prompt if available
|
|
442
461
|
if [[ -n "$CONTEXT_CONTINUATION_PROMPT" ]]; then
|
|
443
462
|
echo "$CONTEXT_CONTINUATION_PROMPT"
|
|
444
|
-
# Clear after use
|
|
445
463
|
CONTEXT_CONTINUATION_PROMPT=""
|
|
446
464
|
return 0
|
|
447
465
|
fi
|
|
448
466
|
|
|
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
467
|
return 1
|
|
458
468
|
}
|
|
459
469
|
|
|
@@ -109,7 +109,7 @@ build_git_section() {
|
|
|
109
109
|
echo ""
|
|
110
110
|
echo "### Uncommitted Changes"
|
|
111
111
|
local status
|
|
112
|
-
status=$(git status --porcelain 2>/dev/null | head -10)
|
|
112
|
+
status=$(git status --porcelain 2>/dev/null | head -10) || true
|
|
113
113
|
if [[ -n "$status" ]]; then
|
|
114
114
|
echo "\`\`\`"
|
|
115
115
|
echo "$status"
|
|
@@ -195,34 +195,16 @@ build_current_task_section() {
|
|
|
195
195
|
# Continuation Prompt Generation
|
|
196
196
|
# =============================================================================
|
|
197
197
|
|
|
198
|
+
# Templates come from Tools/i18n/ralph/<lang>.sh (MSG_PROMPT_*), so the
|
|
199
|
+
# prompt sent to Claude follows --lang. Replacements are quoted: bash 5.2
|
|
200
|
+
# would otherwise expand `&` in the context to the matched placeholder.
|
|
198
201
|
generate_continuation_prompt() {
|
|
199
202
|
local session_id="${SESSION_ID:-unknown}"
|
|
200
|
-
local context
|
|
203
|
+
local context
|
|
204
|
+
context=$(reconstruct_context "$session_id")
|
|
201
205
|
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
Vous reprenez le travail sur ce sprint apres une compaction de contexte.
|
|
206
|
-
Le fichier sprint-progress.md contient l'etat complet du travail effectue.
|
|
207
|
-
|
|
208
|
-
$context
|
|
209
|
-
|
|
210
|
-
## Instructions
|
|
211
|
-
|
|
212
|
-
1. **Lisez** le progres ci-dessus pour comprendre l'etat actuel
|
|
213
|
-
2. **Verifiez** les fichiers modifies si necessaire
|
|
214
|
-
3. **Reprenez** la tache en cours la ou elle s'est arretee
|
|
215
|
-
4. **Continuez** le workflow TDD (RED -> GREEN -> REFACTOR)
|
|
216
|
-
5. **Mettez a jour** sprint-progress.md apres chaque tache terminee
|
|
217
|
-
|
|
218
|
-
## Commandes Importantes
|
|
219
|
-
|
|
220
|
-
- Apres chaque tache: mettre a jour sprint-progress.md
|
|
221
|
-
- Si blocker: noter dans la section Blockers
|
|
222
|
-
- Si decision importante: ajouter dans Key Decisions
|
|
223
|
-
|
|
224
|
-
Continuez l'implementation.
|
|
225
|
-
EOF
|
|
206
|
+
local prompt="${MSG_PROMPT_CONTINUATION:-"{0}"}"
|
|
207
|
+
echo "${prompt//\{0\}/"$context"}"
|
|
226
208
|
}
|
|
227
209
|
|
|
228
210
|
# =============================================================================
|
|
@@ -231,32 +213,12 @@ EOF
|
|
|
231
213
|
|
|
232
214
|
generate_session_handoff_prompt() {
|
|
233
215
|
local previous_session="$1"
|
|
234
|
-
local context
|
|
216
|
+
local context
|
|
217
|
+
context=$(reconstruct_context "$previous_session")
|
|
235
218
|
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
Cette session continue le travail de la session precedente ($previous_session).
|
|
240
|
-
Le contexte a ete reinitialise mais le fichier sprint-progress.md contient tout le progres.
|
|
241
|
-
|
|
242
|
-
$context
|
|
243
|
-
|
|
244
|
-
## Contexte de la Transition
|
|
245
|
-
|
|
246
|
-
- Session precedente: $previous_session
|
|
247
|
-
- Raison: Limite de contexte atteinte (max compacts)
|
|
248
|
-
- Le travail a ete sauvegarde dans sprint-progress.md
|
|
249
|
-
- Les fichiers sont dans l'etat du dernier checkpoint git
|
|
250
|
-
|
|
251
|
-
## Instructions
|
|
252
|
-
|
|
253
|
-
1. **Verifiez** l'etat actuel via git status
|
|
254
|
-
2. **Lisez** sprint-progress.md pour le contexte complet
|
|
255
|
-
3. **Identifiez** la tache en cours et son etat
|
|
256
|
-
4. **Continuez** exactement ou la session precedente s'est arretee
|
|
257
|
-
|
|
258
|
-
Reprenez le travail.
|
|
259
|
-
EOF
|
|
219
|
+
local prompt="${MSG_PROMPT_HANDOFF:-"{1}"}"
|
|
220
|
+
prompt="${prompt//\{0\}/"$previous_session"}"
|
|
221
|
+
echo "${prompt//\{1\}/"$context"}"
|
|
260
222
|
}
|
|
261
223
|
|
|
262
224
|
# =============================================================================
|
|
@@ -18,7 +18,7 @@ get_dod_template() {
|
|
|
18
18
|
|
|
19
19
|
# Normalize project type (remove version suffixes)
|
|
20
20
|
local base_type
|
|
21
|
-
base_type=$(echo "$project_type" | sed 's/-[0-9.]*$//' | sed 's/-typescript$//')
|
|
21
|
+
base_type=$(echo "$project_type" | sed 's/-[0-9.]*$//' | sed 's/-typescript$//') || true
|
|
22
22
|
|
|
23
23
|
# Map to template file
|
|
24
24
|
local template_file=""
|
|
@@ -69,7 +69,7 @@ get_dod_template_path() {
|
|
|
69
69
|
local templates_dir="${2:-$DOD_TEMPLATES_DIR}"
|
|
70
70
|
|
|
71
71
|
local base_type
|
|
72
|
-
base_type=$(echo "$project_type" | sed 's/-[0-9.]*$//' | sed 's/-typescript$//')
|
|
72
|
+
base_type=$(echo "$project_type" | sed 's/-[0-9.]*$//' | sed 's/-typescript$//') || true
|
|
73
73
|
|
|
74
74
|
case "$base_type" in
|
|
75
75
|
symfony|laravel|php)
|
|
@@ -9,7 +9,7 @@ set -euo pipefail
|
|
|
9
9
|
DOD_COMPLETION_MARKER="${DOD_COMPLETION_MARKER:-<promise>COMPLETE</promise>}"
|
|
10
10
|
|
|
11
11
|
# DoD validator types
|
|
12
|
-
declare -
|
|
12
|
+
declare -gA DOD_VALIDATORS=(
|
|
13
13
|
["command"]="validate_command"
|
|
14
14
|
["output_contains"]="validate_output_contains"
|
|
15
15
|
["file_changed"]="validate_file_changed"
|
|
@@ -27,12 +27,12 @@ validate_dod() {
|
|
|
27
27
|
|
|
28
28
|
# If no config, use simple completion marker check
|
|
29
29
|
if [[ -z "$config_file" || ! -f "$config_file" ]]; then
|
|
30
|
-
|
|
30
|
+
validate_simple_completion "$output"; return
|
|
31
31
|
fi
|
|
32
32
|
|
|
33
33
|
# Check if yq is available for YAML parsing
|
|
34
34
|
if ! command -v yq &> /dev/null; then
|
|
35
|
-
|
|
35
|
+
validate_simple_completion "$output"; return
|
|
36
36
|
fi
|
|
37
37
|
|
|
38
38
|
# Get DoD checklist from config
|
|
@@ -44,7 +44,7 @@ validate_dod() {
|
|
|
44
44
|
if [[ -n "$marker" ]]; then
|
|
45
45
|
DOD_COMPLETION_MARKER="$marker"
|
|
46
46
|
fi
|
|
47
|
-
|
|
47
|
+
validate_simple_completion "$output"; return
|
|
48
48
|
fi
|
|
49
49
|
|
|
50
50
|
# Process checklist items
|
|
@@ -52,14 +52,14 @@ validate_dod() {
|
|
|
52
52
|
local results=()
|
|
53
53
|
|
|
54
54
|
# Get count of checklist items
|
|
55
|
-
local count=$(echo "$checklist" | yq e 'length' -)
|
|
55
|
+
local count=$(echo "$checklist" | yq e 'length' -) || true
|
|
56
56
|
|
|
57
57
|
for ((i=0; i<count; i++)); do
|
|
58
58
|
local item=$(yq e ".definition_of_done.checklist[$i]" "$config_file")
|
|
59
|
-
local id=$(echo "$item" | yq e '.id' -)
|
|
60
|
-
local name=$(echo "$item" | yq e '.name' -)
|
|
61
|
-
local type=$(echo "$item" | yq e '.type' -)
|
|
62
|
-
local required=$(echo "$item" | yq e '.required // true' -)
|
|
59
|
+
local id=$(echo "$item" | yq e '.id' -) || true
|
|
60
|
+
local name=$(echo "$item" | yq e '.name' -) || true
|
|
61
|
+
local type=$(echo "$item" | yq e '.type' -) || true
|
|
62
|
+
local required=$(echo "$item" | yq e '.required // true' -) || true
|
|
63
63
|
|
|
64
64
|
# Validate based on type
|
|
65
65
|
local passed=false
|
|
@@ -67,7 +67,7 @@ validate_dod() {
|
|
|
67
67
|
|
|
68
68
|
case "$type" in
|
|
69
69
|
command)
|
|
70
|
-
local cmd=$(echo "$item" | yq e '.command' -)
|
|
70
|
+
local cmd=$(echo "$item" | yq e '.command' -) || true
|
|
71
71
|
if validate_command "$cmd"; then
|
|
72
72
|
passed=true
|
|
73
73
|
message="Command passed"
|
|
@@ -76,7 +76,7 @@ validate_dod() {
|
|
|
76
76
|
fi
|
|
77
77
|
;;
|
|
78
78
|
output_contains)
|
|
79
|
-
local pattern=$(echo "$item" | yq e '.pattern' -)
|
|
79
|
+
local pattern=$(echo "$item" | yq e '.pattern' -) || true
|
|
80
80
|
if validate_output_contains "$output" "$pattern"; then
|
|
81
81
|
passed=true
|
|
82
82
|
message="Pattern found"
|
|
@@ -85,7 +85,7 @@ validate_dod() {
|
|
|
85
85
|
fi
|
|
86
86
|
;;
|
|
87
87
|
file_changed)
|
|
88
|
-
local pattern=$(echo "$item" | yq e '.pattern' -)
|
|
88
|
+
local pattern=$(echo "$item" | yq e '.pattern' -) || true
|
|
89
89
|
if validate_file_changed "$pattern"; then
|
|
90
90
|
passed=true
|
|
91
91
|
message="Files changed"
|
|
@@ -94,7 +94,7 @@ validate_dod() {
|
|
|
94
94
|
fi
|
|
95
95
|
;;
|
|
96
96
|
hook)
|
|
97
|
-
local script=$(echo "$item" | yq e '.script' -)
|
|
97
|
+
local script=$(echo "$item" | yq e '.script' -) || true
|
|
98
98
|
if validate_hook "$script" "$output"; then
|
|
99
99
|
passed=true
|
|
100
100
|
message="Hook passed"
|
|
@@ -103,7 +103,7 @@ validate_dod() {
|
|
|
103
103
|
fi
|
|
104
104
|
;;
|
|
105
105
|
human)
|
|
106
|
-
local prompt=$(echo "$item" | yq e '.prompt // ""' -)
|
|
106
|
+
local prompt=$(echo "$item" | yq e '.prompt // ""' -) || true
|
|
107
107
|
if validate_human "$name" "$prompt"; then
|
|
108
108
|
passed=true
|
|
109
109
|
message="Human approved"
|
|
@@ -209,13 +209,13 @@ validate_file_changed() {
|
|
|
209
209
|
|
|
210
210
|
# Use git to check for changes matching pattern
|
|
211
211
|
if command -v git &> /dev/null && git rev-parse --git-dir &> /dev/null; then
|
|
212
|
-
local changes=$(git status --porcelain 2>/dev/null | grep -E "$pattern" | wc -l)
|
|
212
|
+
local changes=$(git status --porcelain 2>/dev/null | grep -E "$pattern" | wc -l) || true
|
|
213
213
|
if [[ $changes -gt 0 ]]; then
|
|
214
214
|
return 0
|
|
215
215
|
fi
|
|
216
216
|
else
|
|
217
217
|
# Fallback: check for recently modified files
|
|
218
|
-
local changes=$(find . -name "$pattern" -mmin -5 2>/dev/null | wc -l)
|
|
218
|
+
local changes=$(find . -name "$pattern" -mmin -5 2>/dev/null | wc -l) || true
|
|
219
219
|
if [[ $changes -gt 0 ]]; then
|
|
220
220
|
return 0
|
|
221
221
|
fi
|
|
@@ -381,7 +381,7 @@ export_dod_status() {
|
|
|
381
381
|
|
|
382
382
|
# Count checks
|
|
383
383
|
local total
|
|
384
|
-
total=$(echo "$checklist" | yq e 'length' -)
|
|
384
|
+
total=$(echo "$checklist" | yq e 'length' -) || total=0
|
|
385
385
|
|
|
386
386
|
local required=0
|
|
387
387
|
for ((i=0; i<total; i++)); do
|
|
@@ -424,7 +424,7 @@ get_dod_summary() {
|
|
|
424
424
|
|
|
425
425
|
# Build summary
|
|
426
426
|
local count
|
|
427
|
-
count=$(echo "$checklist" | yq e 'length' -)
|
|
427
|
+
count=$(echo "$checklist" | yq e 'length' -) || count=0
|
|
428
428
|
|
|
429
429
|
local checks="["
|
|
430
430
|
for ((i=0; i<count; i++)); do
|