anvil-dev-framework 0.1.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +719 -0
- package/VERSION +1 -0
- package/docs/ANVIL-REPO-IMPLEMENTATION-PLAN.md +441 -0
- package/docs/FIRST-SKILL-TUTORIAL.md +408 -0
- package/docs/INSTALLATION-RETRO-NOTES.md +458 -0
- package/docs/INSTALLATION.md +984 -0
- package/docs/anvil-hud.md +469 -0
- package/docs/anvil-init.md +255 -0
- package/docs/anvil-state.md +210 -0
- package/docs/boris-cherny-ralph-wiggum-insights.md +608 -0
- package/docs/command-reference.md +2022 -0
- package/docs/hooks-tts.md +368 -0
- package/docs/implementation-guide.md +810 -0
- package/docs/linear-github-integration.md +247 -0
- package/docs/local-issues.md +677 -0
- package/docs/patterns/README.md +419 -0
- package/docs/planning-responsibilities.md +139 -0
- package/docs/session-workflow.md +573 -0
- package/docs/simplification-plan-template.md +297 -0
- package/docs/simplification-principles.md +129 -0
- package/docs/specifications/CCS-RALPH-INTEGRATION-DESIGN.md +633 -0
- package/docs/specifications/CCS-RESEARCH-REPORT.md +169 -0
- package/docs/specifications/PLAN-ANV-verification-ralph-wiggum.md +403 -0
- package/docs/specifications/PLAN-parallel-tracks-anvil-memory-ccs.md +494 -0
- package/docs/specifications/SPEC-ANV-VRW/component-01-verify.md +208 -0
- package/docs/specifications/SPEC-ANV-VRW/component-02-stop-gate.md +226 -0
- package/docs/specifications/SPEC-ANV-VRW/component-03-posttooluse.md +209 -0
- package/docs/specifications/SPEC-ANV-VRW/component-04-ralph-wiggum.md +604 -0
- package/docs/specifications/SPEC-ANV-VRW/component-05-atomic-actions.md +311 -0
- package/docs/specifications/SPEC-ANV-VRW/component-06-verify-subagent.md +264 -0
- package/docs/specifications/SPEC-ANV-VRW/component-07-claude-md.md +363 -0
- package/docs/specifications/SPEC-ANV-VRW/index.md +182 -0
- package/docs/specifications/SPEC-ANV-anvil-memory.md +573 -0
- package/docs/specifications/SPEC-ANV-context-checkpoints.md +781 -0
- package/docs/specifications/SPEC-ANV-verification-ralph-wiggum.md +789 -0
- package/docs/sync.md +122 -0
- package/global/CLAUDE.md +140 -0
- package/global/agents/verify-app.md +164 -0
- package/global/commands/anvil-settings.md +527 -0
- package/global/commands/anvil-sync.md +121 -0
- package/global/commands/change.md +197 -0
- package/global/commands/clarify.md +252 -0
- package/global/commands/cleanup.md +292 -0
- package/global/commands/commit-push-pr.md +207 -0
- package/global/commands/decay-review.md +127 -0
- package/global/commands/discover.md +158 -0
- package/global/commands/doc-coverage.md +122 -0
- package/global/commands/evidence.md +307 -0
- package/global/commands/explore.md +121 -0
- package/global/commands/force-exit.md +135 -0
- package/global/commands/handoff.md +191 -0
- package/global/commands/healthcheck.md +302 -0
- package/global/commands/hud.md +84 -0
- package/global/commands/insights.md +319 -0
- package/global/commands/linear-setup.md +184 -0
- package/global/commands/lint-fix.md +198 -0
- package/global/commands/orient.md +510 -0
- package/global/commands/plan.md +228 -0
- package/global/commands/ralph.md +346 -0
- package/global/commands/ready.md +182 -0
- package/global/commands/release.md +305 -0
- package/global/commands/retro.md +96 -0
- package/global/commands/shard.md +166 -0
- package/global/commands/spec.md +227 -0
- package/global/commands/sprint.md +184 -0
- package/global/commands/tasks.md +228 -0
- package/global/commands/test-and-commit.md +151 -0
- package/global/commands/validate.md +132 -0
- package/global/commands/verify.md +251 -0
- package/global/commands/weekly-review.md +156 -0
- package/global/hooks/__pycache__/ralph_context_monitor.cpython-314.pyc +0 -0
- package/global/hooks/__pycache__/statusline_agent_sync.cpython-314.pyc +0 -0
- package/global/hooks/anvil_memory_observe.ts +322 -0
- package/global/hooks/anvil_memory_session.ts +166 -0
- package/global/hooks/anvil_memory_stop.ts +187 -0
- package/global/hooks/parse_transcript.py +116 -0
- package/global/hooks/post_merge_cleanup.sh +132 -0
- package/global/hooks/post_tool_format.sh +215 -0
- package/global/hooks/ralph_context_monitor.py +240 -0
- package/global/hooks/ralph_stop.sh +502 -0
- package/global/hooks/statusline.sh +1110 -0
- package/global/hooks/statusline_agent_sync.py +224 -0
- package/global/hooks/stop_gate.sh +250 -0
- package/global/lib/.claude/anvil-state.json +21 -0
- package/global/lib/__pycache__/agent_registry.cpython-314.pyc +0 -0
- package/global/lib/__pycache__/claim_service.cpython-314.pyc +0 -0
- package/global/lib/__pycache__/coderabbit_service.cpython-314.pyc +0 -0
- package/global/lib/__pycache__/config_service.cpython-314.pyc +0 -0
- package/global/lib/__pycache__/coordination_service.cpython-314.pyc +0 -0
- package/global/lib/__pycache__/doc_coverage_service.cpython-314.pyc +0 -0
- package/global/lib/__pycache__/gate_logger.cpython-314.pyc +0 -0
- package/global/lib/__pycache__/github_service.cpython-314.pyc +0 -0
- package/global/lib/__pycache__/hygiene_service.cpython-314.pyc +0 -0
- package/global/lib/__pycache__/issue_models.cpython-314.pyc +0 -0
- package/global/lib/__pycache__/issue_provider.cpython-314.pyc +0 -0
- package/global/lib/__pycache__/linear_data_service.cpython-314.pyc +0 -0
- package/global/lib/__pycache__/linear_provider.cpython-314.pyc +0 -0
- package/global/lib/__pycache__/local_provider.cpython-314.pyc +0 -0
- package/global/lib/__pycache__/quality_service.cpython-314.pyc +0 -0
- package/global/lib/__pycache__/ralph_state.cpython-314.pyc +0 -0
- package/global/lib/__pycache__/state_manager.cpython-314.pyc +0 -0
- package/global/lib/__pycache__/transcript_parser.cpython-314.pyc +0 -0
- package/global/lib/__pycache__/verification_runner.cpython-314.pyc +0 -0
- package/global/lib/__pycache__/verify_iteration.cpython-314.pyc +0 -0
- package/global/lib/__pycache__/verify_subagent.cpython-314.pyc +0 -0
- package/global/lib/agent_registry.py +995 -0
- package/global/lib/anvil-state.sh +435 -0
- package/global/lib/claim_service.py +515 -0
- package/global/lib/coderabbit_service.py +314 -0
- package/global/lib/config_service.py +423 -0
- package/global/lib/coordination_service.py +331 -0
- package/global/lib/doc_coverage_service.py +1305 -0
- package/global/lib/gate_logger.py +316 -0
- package/global/lib/github_service.py +310 -0
- package/global/lib/handoff_generator.py +775 -0
- package/global/lib/hygiene_service.py +712 -0
- package/global/lib/issue_models.py +257 -0
- package/global/lib/issue_provider.py +339 -0
- package/global/lib/linear_data_service.py +210 -0
- package/global/lib/linear_provider.py +987 -0
- package/global/lib/linear_provider.py.backup +671 -0
- package/global/lib/local_provider.py +486 -0
- package/global/lib/orient_fast.py +457 -0
- package/global/lib/quality_service.py +470 -0
- package/global/lib/ralph_prompt_generator.py +563 -0
- package/global/lib/ralph_state.py +1202 -0
- package/global/lib/state_manager.py +417 -0
- package/global/lib/transcript_parser.py +597 -0
- package/global/lib/verification_runner.py +557 -0
- package/global/lib/verify_iteration.py +490 -0
- package/global/lib/verify_subagent.py +250 -0
- package/global/skills/README.md +155 -0
- package/global/skills/quality-gates/SKILL.md +252 -0
- package/global/skills/skill-template/SKILL.md +109 -0
- package/global/skills/testing-strategies/SKILL.md +337 -0
- package/global/templates/CHANGE-template.md +105 -0
- package/global/templates/HANDOFF-template.md +63 -0
- package/global/templates/PLAN-template.md +111 -0
- package/global/templates/SPEC-template.md +93 -0
- package/global/templates/ralph/PROMPT.md.template +89 -0
- package/global/templates/ralph/fix_plan.md.template +31 -0
- package/global/templates/ralph/progress.txt.template +23 -0
- package/global/tests/__pycache__/test_doc_coverage.cpython-314.pyc +0 -0
- package/global/tests/test_doc_coverage.py +520 -0
- package/global/tests/test_issue_models.py +299 -0
- package/global/tests/test_local_provider.py +323 -0
- package/global/tools/README.md +178 -0
- package/global/tools/__pycache__/anvil-hud.cpython-314.pyc +0 -0
- package/global/tools/anvil-hud.py +3622 -0
- package/global/tools/anvil-hud.py.bak +3318 -0
- package/global/tools/anvil-issue.py +432 -0
- package/global/tools/anvil-memory/CLAUDE.md +49 -0
- package/global/tools/anvil-memory/README.md +42 -0
- package/global/tools/anvil-memory/bun.lock +25 -0
- package/global/tools/anvil-memory/bunfig.toml +9 -0
- package/global/tools/anvil-memory/package.json +23 -0
- package/global/tools/anvil-memory/src/__tests__/ccs/context-monitor.test.ts +535 -0
- package/global/tools/anvil-memory/src/__tests__/ccs/edge-cases.test.ts +645 -0
- package/global/tools/anvil-memory/src/__tests__/ccs/fixtures.ts +363 -0
- package/global/tools/anvil-memory/src/__tests__/ccs/index.ts +8 -0
- package/global/tools/anvil-memory/src/__tests__/ccs/integration.test.ts +417 -0
- package/global/tools/anvil-memory/src/__tests__/ccs/prompt-generator.test.ts +571 -0
- package/global/tools/anvil-memory/src/__tests__/ccs/ralph-stop.test.ts +440 -0
- package/global/tools/anvil-memory/src/__tests__/ccs/test-utils.ts +252 -0
- package/global/tools/anvil-memory/src/__tests__/commands.test.ts +657 -0
- package/global/tools/anvil-memory/src/__tests__/db.test.ts +641 -0
- package/global/tools/anvil-memory/src/__tests__/hooks.test.ts +272 -0
- package/global/tools/anvil-memory/src/__tests__/performance.test.ts +427 -0
- package/global/tools/anvil-memory/src/__tests__/test-utils.ts +113 -0
- package/global/tools/anvil-memory/src/commands/checkpoint.ts +197 -0
- package/global/tools/anvil-memory/src/commands/get.ts +115 -0
- package/global/tools/anvil-memory/src/commands/init.ts +94 -0
- package/global/tools/anvil-memory/src/commands/observe.ts +163 -0
- package/global/tools/anvil-memory/src/commands/search.ts +112 -0
- package/global/tools/anvil-memory/src/db.ts +638 -0
- package/global/tools/anvil-memory/src/index.ts +205 -0
- package/global/tools/anvil-memory/src/types.ts +122 -0
- package/global/tools/anvil-memory/tsconfig.json +29 -0
- package/global/tools/ralph-loop.sh +359 -0
- package/package.json +45 -0
- package/scripts/anvil +822 -0
- package/scripts/extract_patterns.py +222 -0
- package/scripts/init-project.sh +541 -0
- package/scripts/install.sh +229 -0
- package/scripts/postinstall.js +41 -0
- package/scripts/rollback.sh +188 -0
- package/scripts/sync.sh +623 -0
- package/scripts/test-statusline.sh +248 -0
- package/scripts/update_claude_md.py +224 -0
- package/scripts/verify.sh +255 -0
|
@@ -0,0 +1,502 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# =============================================================================
|
|
3
|
+
# ralph_stop.sh - Ralph Wiggum Stop Hook with Circuit Breaker (ANV-160)
|
|
4
|
+
# =============================================================================
|
|
5
|
+
#
|
|
6
|
+
# Stop hook for Ralph Wiggum autonomous execution mode.
|
|
7
|
+
# Blocks exit until completion promise is detected or safety limits reached.
|
|
8
|
+
#
|
|
9
|
+
# Usage: Called automatically by Claude Code on Stop events during Ralph mode
|
|
10
|
+
#
|
|
11
|
+
# Environment Variables:
|
|
12
|
+
# RALPH_MAX_ITERATIONS - Max iterations (default: 50)
|
|
13
|
+
# RALPH_COMPLETION_PROMISE - Completion text to detect (default: COMPLETE)
|
|
14
|
+
# RALPH_STATE_FILE - State file path (default: .claude/ralph-state.json)
|
|
15
|
+
# RALPH_ENABLE_LOGGING - Set to "true" to enable logging
|
|
16
|
+
#
|
|
17
|
+
# Exit Codes:
|
|
18
|
+
# 0 - Allow exit (complete, fatal error, or safety limit)
|
|
19
|
+
# 1 - Block exit and restart (continue iteration)
|
|
20
|
+
#
|
|
21
|
+
# DEPENDENCY: jq (apt install jq / brew install jq)
|
|
22
|
+
# WARNING: Windows/Git Bash users must install jq separately or use WSL
|
|
23
|
+
#
|
|
24
|
+
|
|
25
|
+
set -euo pipefail
|
|
26
|
+
|
|
27
|
+
# =============================================================================
|
|
28
|
+
# Configuration
|
|
29
|
+
# =============================================================================
|
|
30
|
+
|
|
31
|
+
MAX_ITERATIONS="${RALPH_MAX_ITERATIONS:-50}"
|
|
32
|
+
COMPLETION_PROMISE="${RALPH_COMPLETION_PROMISE:-COMPLETE}"
|
|
33
|
+
STATE_FILE="${RALPH_STATE_FILE:-.claude/ralph-state.json}"
|
|
34
|
+
ENABLE_LOGGING="${RALPH_ENABLE_LOGGING:-false}"
|
|
35
|
+
LOG_FILE=".claude/logs/ralph.log"
|
|
36
|
+
PROGRESS_FILE="progress.txt"
|
|
37
|
+
|
|
38
|
+
# Circuit breaker thresholds
|
|
39
|
+
NO_CHANGE_THRESHOLD=3
|
|
40
|
+
REPEATED_ERROR_THRESHOLD=5
|
|
41
|
+
|
|
42
|
+
# =============================================================================
|
|
43
|
+
# Logging
|
|
44
|
+
# =============================================================================
|
|
45
|
+
|
|
46
|
+
log_event() {
|
|
47
|
+
local level="$1"
|
|
48
|
+
local message="$2"
|
|
49
|
+
|
|
50
|
+
if [[ "$ENABLE_LOGGING" == "true" ]]; then
|
|
51
|
+
local timestamp
|
|
52
|
+
timestamp=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
|
|
53
|
+
mkdir -p "$(dirname "$LOG_FILE")"
|
|
54
|
+
echo "[$timestamp] [$level] $message" >> "$LOG_FILE"
|
|
55
|
+
fi
|
|
56
|
+
|
|
57
|
+
# Also output to stderr for visibility
|
|
58
|
+
case "$level" in
|
|
59
|
+
ERROR) echo "❌ Ralph: $message" >&2 ;;
|
|
60
|
+
WARN) echo "⚠️ Ralph: $message" >&2 ;;
|
|
61
|
+
INFO) echo "🔄 Ralph: $message" >&2 ;;
|
|
62
|
+
OK) echo "✅ Ralph: $message" >&2 ;;
|
|
63
|
+
esac
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
# =============================================================================
|
|
67
|
+
# State Management
|
|
68
|
+
# =============================================================================
|
|
69
|
+
|
|
70
|
+
init_state() {
|
|
71
|
+
if [[ ! -f "$STATE_FILE" ]]; then
|
|
72
|
+
mkdir -p "$(dirname "$STATE_FILE")"
|
|
73
|
+
cat > "$STATE_FILE" << 'INIT_STATE'
|
|
74
|
+
{
|
|
75
|
+
"iteration": 0,
|
|
76
|
+
"started_at": "",
|
|
77
|
+
"no_change_count": 0,
|
|
78
|
+
"last_diff_hash": "",
|
|
79
|
+
"error_hashes": [],
|
|
80
|
+
"status": "running"
|
|
81
|
+
}
|
|
82
|
+
INIT_STATE
|
|
83
|
+
# Set started_at timestamp
|
|
84
|
+
local timestamp
|
|
85
|
+
timestamp=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
|
|
86
|
+
jq --arg ts "$timestamp" '.started_at = $ts' "$STATE_FILE" > tmp.$$ && mv tmp.$$ "$STATE_FILE"
|
|
87
|
+
fi
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
read_state() {
|
|
91
|
+
local key="$1"
|
|
92
|
+
local default="${2:-}"
|
|
93
|
+
jq -r ".$key // \"$default\"" "$STATE_FILE" 2>/dev/null || echo "$default"
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
update_state() {
|
|
97
|
+
local key="$1"
|
|
98
|
+
local value="$2"
|
|
99
|
+
|
|
100
|
+
if [[ "$value" =~ ^[0-9]+$ ]]; then
|
|
101
|
+
# Numeric value
|
|
102
|
+
jq --argjson v "$value" ".$key = \$v" "$STATE_FILE" > tmp.$$ && mv tmp.$$ "$STATE_FILE"
|
|
103
|
+
else
|
|
104
|
+
# String value
|
|
105
|
+
jq --arg v "$value" ".$key = \$v" "$STATE_FILE" > tmp.$$ && mv tmp.$$ "$STATE_FILE"
|
|
106
|
+
fi
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
cleanup_state() {
|
|
110
|
+
if [[ -f "$STATE_FILE" ]]; then
|
|
111
|
+
rm -f "$STATE_FILE"
|
|
112
|
+
fi
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
# =============================================================================
|
|
116
|
+
# Circuit Breaker Detection
|
|
117
|
+
# =============================================================================
|
|
118
|
+
|
|
119
|
+
get_current_diff_hash() {
|
|
120
|
+
# Hash of current git diff to detect if changes were made
|
|
121
|
+
git diff --stat 2>/dev/null | md5sum 2>/dev/null | cut -d' ' -f1 || echo "no-git"
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
check_no_change_circuit() {
|
|
125
|
+
local current_hash
|
|
126
|
+
current_hash=$(get_current_diff_hash)
|
|
127
|
+
local last_hash
|
|
128
|
+
last_hash=$(read_state "last_diff_hash" "")
|
|
129
|
+
|
|
130
|
+
if [[ "$current_hash" == "$last_hash" && -n "$last_hash" ]]; then
|
|
131
|
+
local no_change_count
|
|
132
|
+
no_change_count=$(read_state "no_change_count" "0")
|
|
133
|
+
no_change_count=$((no_change_count + 1))
|
|
134
|
+
update_state "no_change_count" "$no_change_count"
|
|
135
|
+
|
|
136
|
+
if [[ $no_change_count -ge $NO_CHANGE_THRESHOLD ]]; then
|
|
137
|
+
log_event "ERROR" "Circuit breaker: $NO_CHANGE_THRESHOLD iterations with no file changes"
|
|
138
|
+
return 1
|
|
139
|
+
else
|
|
140
|
+
log_event "WARN" "No file changes detected (${no_change_count}/${NO_CHANGE_THRESHOLD})"
|
|
141
|
+
fi
|
|
142
|
+
else
|
|
143
|
+
update_state "no_change_count" "0"
|
|
144
|
+
update_state "last_diff_hash" "$current_hash"
|
|
145
|
+
fi
|
|
146
|
+
|
|
147
|
+
return 0
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
# =============================================================================
|
|
151
|
+
# Linear Integration (ANV-213)
|
|
152
|
+
# =============================================================================
|
|
153
|
+
|
|
154
|
+
report_linear_progress() {
|
|
155
|
+
# Report Linear sync progress to progress.txt at iteration boundaries.
|
|
156
|
+
#
|
|
157
|
+
# NOTE: The actual Linear API sync happens in ralph_state.py when
|
|
158
|
+
# mark_subtask_complete() is called - it internally calls sync_to_linear().
|
|
159
|
+
# This function reads the sync status from state and reports it to progress.txt.
|
|
160
|
+
|
|
161
|
+
if [[ ! -f "$STATE_FILE" ]]; then
|
|
162
|
+
return 0
|
|
163
|
+
fi
|
|
164
|
+
|
|
165
|
+
# Check if Linear integration is enabled
|
|
166
|
+
local linear_enabled
|
|
167
|
+
linear_enabled=$(jq -r '.linear_integration.enabled // false' "$STATE_FILE" 2>/dev/null || echo "false")
|
|
168
|
+
|
|
169
|
+
if [[ "$linear_enabled" != "true" ]]; then
|
|
170
|
+
return 0 # Not a Linear session
|
|
171
|
+
fi
|
|
172
|
+
|
|
173
|
+
local no_sync
|
|
174
|
+
no_sync=$(jq -r '.linear_integration.no_sync // false' "$STATE_FILE" 2>/dev/null || echo "false")
|
|
175
|
+
|
|
176
|
+
if [[ "$no_sync" == "true" ]]; then
|
|
177
|
+
log_event "INFO" "Linear sync disabled for this session"
|
|
178
|
+
return 0
|
|
179
|
+
fi
|
|
180
|
+
|
|
181
|
+
# Get sync info from state (set by ralph_state.py sync_to_linear)
|
|
182
|
+
local last_sync
|
|
183
|
+
last_sync=$(jq -r '.linear_integration.last_sync // ""' "$STATE_FILE" 2>/dev/null || echo "")
|
|
184
|
+
|
|
185
|
+
if [[ -n "$last_sync" ]]; then
|
|
186
|
+
# Append sync info to progress.txt
|
|
187
|
+
if [[ -f "$PROGRESS_FILE" ]]; then
|
|
188
|
+
local completed
|
|
189
|
+
completed=$(jq -r '[.linear_integration.subtasks[] | select(.status == "completed")] | length' "$STATE_FILE" 2>/dev/null || echo "0")
|
|
190
|
+
local total
|
|
191
|
+
total=$(jq -r '.linear_integration.subtasks | length' "$STATE_FILE" 2>/dev/null || echo "0")
|
|
192
|
+
|
|
193
|
+
# Get the most recently completed subtask
|
|
194
|
+
local last_completed
|
|
195
|
+
last_completed=$(jq -r '[.linear_integration.subtasks[] | select(.status == "completed")] | sort_by(.completed_at) | last | .identifier // ""' "$STATE_FILE" 2>/dev/null || echo "")
|
|
196
|
+
|
|
197
|
+
if [[ -n "$last_completed" ]]; then
|
|
198
|
+
echo "" >> "$PROGRESS_FILE"
|
|
199
|
+
echo "[Linear] Synced $last_completed to Done ($completed/$total completed)" >> "$PROGRESS_FILE"
|
|
200
|
+
log_event "INFO" "Synced $last_completed to Done in Linear"
|
|
201
|
+
fi
|
|
202
|
+
fi
|
|
203
|
+
fi
|
|
204
|
+
|
|
205
|
+
return 0
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
# =============================================================================
|
|
209
|
+
# Git Checkpointing
|
|
210
|
+
# =============================================================================
|
|
211
|
+
|
|
212
|
+
git_checkpoint() {
|
|
213
|
+
local iteration="$1"
|
|
214
|
+
|
|
215
|
+
# Only checkpoint if there are changes
|
|
216
|
+
if ! git diff --quiet 2>/dev/null || ! git diff --cached --quiet 2>/dev/null; then
|
|
217
|
+
git add -A 2>/dev/null || true
|
|
218
|
+
git commit -m "ralph: checkpoint iteration $iteration" --no-verify 2>/dev/null || true
|
|
219
|
+
log_event "INFO" "Git checkpoint created for iteration $iteration"
|
|
220
|
+
fi
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
# =============================================================================
|
|
224
|
+
# CCS Checkpoint Handling (ANV-200)
|
|
225
|
+
# =============================================================================
|
|
226
|
+
|
|
227
|
+
check_ccs_checkpoint() {
|
|
228
|
+
# Check if CCS context checkpoint was triggered
|
|
229
|
+
local checkpoint_active
|
|
230
|
+
checkpoint_active=$(jq -r '.context_checkpoint.active // false' "$STATE_FILE" 2>/dev/null)
|
|
231
|
+
if [[ "$checkpoint_active" == "true" ]]; then
|
|
232
|
+
return 0
|
|
233
|
+
fi
|
|
234
|
+
return 1
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
handle_ccs_checkpoint() {
|
|
238
|
+
log_event "INFO" "CCS checkpoint detected - preparing resume context"
|
|
239
|
+
local level percent handoff_file current_item
|
|
240
|
+
level=$(jq -r '.context_checkpoint.level // "L2"' "$STATE_FILE")
|
|
241
|
+
percent=$(jq -r '.context_checkpoint.percent_at_checkpoint // 0' "$STATE_FILE")
|
|
242
|
+
handoff_file=$(jq -r '.context_checkpoint.handoff_file // ""' "$STATE_FILE")
|
|
243
|
+
current_item=$(jq -r '.context_checkpoint.current_todo_item // ""' "$STATE_FILE")
|
|
244
|
+
regenerate_prompt_with_checkpoint "$handoff_file" "$current_item" "$level" "$percent"
|
|
245
|
+
jq '.context_checkpoint.active = false' "$STATE_FILE" > tmp.$$ && mv tmp.$$ "$STATE_FILE"
|
|
246
|
+
log_event "OK" "Resume context prepared (L${level} at ${percent}%)"
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
regenerate_prompt_with_checkpoint() {
|
|
250
|
+
local handoff_file="$1"
|
|
251
|
+
local current_item="$2"
|
|
252
|
+
local level="$3"
|
|
253
|
+
local percent="$4"
|
|
254
|
+
local prompt_file="PROMPT.md"
|
|
255
|
+
|
|
256
|
+
# Use the Python prompt generator (ANV-201) for enhanced resume templates
|
|
257
|
+
local script_dir
|
|
258
|
+
script_dir="$(dirname "$0")/../lib"
|
|
259
|
+
|
|
260
|
+
if [[ -f "$script_dir/ralph_prompt_generator.py" ]]; then
|
|
261
|
+
# Use Python generator with full state context
|
|
262
|
+
python3 "$script_dir/ralph_prompt_generator.py" \
|
|
263
|
+
--mode ralph \
|
|
264
|
+
--state "$STATE_FILE" \
|
|
265
|
+
--output "$prompt_file" 2>/dev/null
|
|
266
|
+
|
|
267
|
+
if [[ $? -eq 0 ]]; then
|
|
268
|
+
log_event "INFO" "Generated resume prompt via Python generator: $prompt_file"
|
|
269
|
+
return 0
|
|
270
|
+
fi
|
|
271
|
+
# Fall back to inline template if Python fails
|
|
272
|
+
log_event "WARN" "Python generator failed, using fallback template"
|
|
273
|
+
fi
|
|
274
|
+
|
|
275
|
+
# Fallback: Inline template (original ANV-200 implementation)
|
|
276
|
+
local timestamp
|
|
277
|
+
timestamp=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
|
|
278
|
+
local todo_items
|
|
279
|
+
todo_items=$(jq -r '.todo_items // [] | .[]' "$STATE_FILE" 2>/dev/null | head -10)
|
|
280
|
+
local iteration
|
|
281
|
+
iteration=$(jq -r '.iteration // 0' "$STATE_FILE")
|
|
282
|
+
local task_name
|
|
283
|
+
task_name=$(jq -r '.task_name // "Unknown Task"' "$STATE_FILE")
|
|
284
|
+
|
|
285
|
+
cat > "$prompt_file" << RESUME_PROMPT
|
|
286
|
+
# Ralph Autonomous Session Resume
|
|
287
|
+
|
|
288
|
+
> **Context Checkpoint Recovery**
|
|
289
|
+
> Generated at $timestamp
|
|
290
|
+
> Previous session hit L${level} threshold (${percent}% context)
|
|
291
|
+
|
|
292
|
+
---
|
|
293
|
+
|
|
294
|
+
## Previous Session Summary
|
|
295
|
+
|
|
296
|
+
**Task**: $task_name
|
|
297
|
+
**Iteration**: $iteration
|
|
298
|
+
**Checkpoint Level**: L${level}
|
|
299
|
+
**Context at Checkpoint**: ${percent}%
|
|
300
|
+
|
|
301
|
+
---
|
|
302
|
+
|
|
303
|
+
## Handoff Document
|
|
304
|
+
|
|
305
|
+
The previous session created a detailed handoff document with full context:
|
|
306
|
+
|
|
307
|
+
\`\`\`
|
|
308
|
+
$handoff_file
|
|
309
|
+
\`\`\`
|
|
310
|
+
|
|
311
|
+
**IMPORTANT**: Read this file first to understand the full context of where work left off.
|
|
312
|
+
|
|
313
|
+
---
|
|
314
|
+
|
|
315
|
+
## Current Task Context
|
|
316
|
+
|
|
317
|
+
### Current Item (In Progress)
|
|
318
|
+
|
|
319
|
+
**Task**: $current_item
|
|
320
|
+
|
|
321
|
+
### Remaining Work
|
|
322
|
+
|
|
323
|
+
\`\`\`
|
|
324
|
+
$todo_items
|
|
325
|
+
\`\`\`
|
|
326
|
+
|
|
327
|
+
---
|
|
328
|
+
|
|
329
|
+
## Resume Instructions
|
|
330
|
+
|
|
331
|
+
**CRITICAL**: This is a continuation session. Follow these rules:
|
|
332
|
+
|
|
333
|
+
1. **DO NOT restart from the beginning** - Work has already been done
|
|
334
|
+
2. **Read the handoff document first** - Contains full context
|
|
335
|
+
3. **Continue from the current item** - Pick up exactly where left off
|
|
336
|
+
4. **Complete current task before moving on** - Don't skip items
|
|
337
|
+
5. **Signal completion with \`<promise>COMPLETE</promise>\`** when done
|
|
338
|
+
|
|
339
|
+
---
|
|
340
|
+
|
|
341
|
+
*This prompt was auto-generated by ralph_stop.sh checkpoint handling (ANV-200/ANV-201)*
|
|
342
|
+
RESUME_PROMPT
|
|
343
|
+
log_event "INFO" "Generated resume prompt (fallback): $prompt_file"
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
# =============================================================================
|
|
347
|
+
# Transcript Analysis
|
|
348
|
+
# =============================================================================
|
|
349
|
+
|
|
350
|
+
check_completion_promise() {
|
|
351
|
+
# Check if the Claude transcript contains the completion promise
|
|
352
|
+
# CLAUDE_TRANSCRIPT is set by Claude Code with the session output
|
|
353
|
+
|
|
354
|
+
local transcript="${CLAUDE_TRANSCRIPT:-}"
|
|
355
|
+
|
|
356
|
+
if [[ -z "$transcript" ]]; then
|
|
357
|
+
# No transcript available, check if we can read from stdin or a file
|
|
358
|
+
return 1
|
|
359
|
+
fi
|
|
360
|
+
|
|
361
|
+
if echo "$transcript" | grep -q "<promise>$COMPLETION_PROMISE</promise>"; then
|
|
362
|
+
return 0
|
|
363
|
+
fi
|
|
364
|
+
|
|
365
|
+
return 1
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
check_fatal_error() {
|
|
369
|
+
local transcript="${CLAUDE_TRANSCRIPT:-}"
|
|
370
|
+
|
|
371
|
+
if [[ -z "$transcript" ]]; then
|
|
372
|
+
return 1
|
|
373
|
+
fi
|
|
374
|
+
|
|
375
|
+
if echo "$transcript" | grep -q "<fatal>"; then
|
|
376
|
+
return 0
|
|
377
|
+
fi
|
|
378
|
+
|
|
379
|
+
return 1
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
check_stuck_signals() {
|
|
383
|
+
local transcript="${CLAUDE_TRANSCRIPT:-}"
|
|
384
|
+
|
|
385
|
+
if [[ -z "$transcript" ]]; then
|
|
386
|
+
return 1
|
|
387
|
+
fi
|
|
388
|
+
|
|
389
|
+
# Check for stuck/blocked language
|
|
390
|
+
if echo "$transcript" | grep -qiE "(I'm stuck|cannot proceed|blocked by|unresolvable)"; then
|
|
391
|
+
return 0
|
|
392
|
+
fi
|
|
393
|
+
|
|
394
|
+
return 1
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
# =============================================================================
|
|
398
|
+
# Main Logic
|
|
399
|
+
# =============================================================================
|
|
400
|
+
|
|
401
|
+
main() {
|
|
402
|
+
# Check if Ralph mode is active
|
|
403
|
+
if [[ ! -f "$STATE_FILE" ]]; then
|
|
404
|
+
# Not in Ralph mode, don't interfere
|
|
405
|
+
exit 0
|
|
406
|
+
fi
|
|
407
|
+
|
|
408
|
+
init_state
|
|
409
|
+
|
|
410
|
+
# Read and increment iteration
|
|
411
|
+
local iteration
|
|
412
|
+
iteration=$(read_state "iteration" "0")
|
|
413
|
+
iteration=$((iteration + 1))
|
|
414
|
+
update_state "iteration" "$iteration"
|
|
415
|
+
|
|
416
|
+
log_event "INFO" "Iteration $iteration starting"
|
|
417
|
+
|
|
418
|
+
# ==========================================================================
|
|
419
|
+
# CCS Check: Context checkpoint triggered (ANV-200)
|
|
420
|
+
# ==========================================================================
|
|
421
|
+
if check_ccs_checkpoint; then
|
|
422
|
+
handle_ccs_checkpoint
|
|
423
|
+
git_checkpoint "$iteration"
|
|
424
|
+
exit 1 # Restart loop with checkpoint context
|
|
425
|
+
fi
|
|
426
|
+
|
|
427
|
+
# ==========================================================================
|
|
428
|
+
# Safety Check 1: Max iterations
|
|
429
|
+
# ==========================================================================
|
|
430
|
+
if [[ $iteration -ge $MAX_ITERATIONS ]]; then
|
|
431
|
+
log_event "WARN" "Maximum iterations ($MAX_ITERATIONS) reached"
|
|
432
|
+
update_state "status" "max_iterations"
|
|
433
|
+
cleanup_state
|
|
434
|
+
exit 0 # Allow exit
|
|
435
|
+
fi
|
|
436
|
+
|
|
437
|
+
# ==========================================================================
|
|
438
|
+
# Check 2: Completion promise detected
|
|
439
|
+
# ==========================================================================
|
|
440
|
+
if check_completion_promise; then
|
|
441
|
+
log_event "OK" "Task complete after $iteration iterations! Promise detected."
|
|
442
|
+
update_state "status" "completed"
|
|
443
|
+
cleanup_state
|
|
444
|
+
exit 0 # Allow exit
|
|
445
|
+
fi
|
|
446
|
+
|
|
447
|
+
# ==========================================================================
|
|
448
|
+
# Check 3: Fatal error detected
|
|
449
|
+
# ==========================================================================
|
|
450
|
+
if check_fatal_error; then
|
|
451
|
+
log_event "ERROR" "Fatal error detected - stopping loop"
|
|
452
|
+
update_state "status" "fatal_error"
|
|
453
|
+
cleanup_state
|
|
454
|
+
exit 0 # Allow exit
|
|
455
|
+
fi
|
|
456
|
+
|
|
457
|
+
# ==========================================================================
|
|
458
|
+
# Check 4: Stuck signals detected
|
|
459
|
+
# ==========================================================================
|
|
460
|
+
if check_stuck_signals; then
|
|
461
|
+
log_event "WARN" "Stuck signals detected - may need manual intervention"
|
|
462
|
+
# Don't exit yet, but log warning
|
|
463
|
+
fi
|
|
464
|
+
|
|
465
|
+
# ==========================================================================
|
|
466
|
+
# Check 5: Circuit breaker - no changes
|
|
467
|
+
# ==========================================================================
|
|
468
|
+
if ! check_no_change_circuit; then
|
|
469
|
+
update_state "status" "circuit_breaker"
|
|
470
|
+
cleanup_state
|
|
471
|
+
exit 0 # Allow exit
|
|
472
|
+
fi
|
|
473
|
+
|
|
474
|
+
# ==========================================================================
|
|
475
|
+
# Report Linear progress at iteration boundary (ANV-213)
|
|
476
|
+
# ==========================================================================
|
|
477
|
+
report_linear_progress
|
|
478
|
+
|
|
479
|
+
# ==========================================================================
|
|
480
|
+
# Git checkpoint before restart
|
|
481
|
+
# ==========================================================================
|
|
482
|
+
git_checkpoint "$iteration"
|
|
483
|
+
|
|
484
|
+
# ==========================================================================
|
|
485
|
+
# Continue loop
|
|
486
|
+
# ==========================================================================
|
|
487
|
+
log_event "INFO" "Iteration $iteration incomplete - restarting..."
|
|
488
|
+
exit 1 # Block exit, trigger restart
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
# =============================================================================
|
|
492
|
+
# Entry Point
|
|
493
|
+
# =============================================================================
|
|
494
|
+
|
|
495
|
+
# Check for jq dependency
|
|
496
|
+
if ! command -v jq &> /dev/null; then
|
|
497
|
+
echo "❌ Ralph requires jq for JSON processing" >&2
|
|
498
|
+
echo " Install: apt install jq (Linux) or brew install jq (macOS)" >&2
|
|
499
|
+
exit 0 # Allow exit rather than blocking
|
|
500
|
+
fi
|
|
501
|
+
|
|
502
|
+
main "$@"
|