feat(structure): migrate Ralph files to .ralph/ subfolder (#109)
* feat(structure): migrate Ralph files to .ralph/ subfolder BREAKING CHANGE: Ralph configuration files now live in .ralph/ subfolder This refactoring moves all Ralph-specific files into a hidden .ralph/ directory while keeping src/ at the project root. This improves compatibility with existing tooling and keeps the project root clean. Changes: - Move PROMPT.md, @fix_plan.md, @AGENT.md to .ralph/ - Move specs/, logs/, docs/generated/, examples/ to .ralph/ - Move state files (.response_analysis, .circuit_breaker_state, etc.) to .ralph/ - Keep src/ at project root (unchanged) - Add RALPH_DIR=".ralph" configuration variable - Add ralph-migrate command for existing projects - Create migrate_to_ralph_folder.sh migration script - Update all path references in scripts and tests - Update documentation (README.md, CLAUDE.md) New project structure: project/ ├── .ralph/ # Ralph configuration │ ├── PROMPT.md │ ├── @fix_plan.md │ ├── @AGENT.md │ ├── specs/ │ ├── logs/ │ └── docs/generated/ └── src/ # Source code (unchanged) Migration: Run `ralph-migrate` in existing projects to upgrade. All 310 tests pass (100% pass rate). * chore: add .claude/settings.local.json to .gitignore * fix: address code review feedback for .ralph/ subfolder structure Fixes multiple path-related issues identified in code review: Test fixes: - Fix create_sample_prompt to use $RALPH_DIR/PROMPT.md in test_session_continuity.bats - Fix result_file path to use $RALPH_DIR/.json_parse_result in test_json_parsing.bats - Fix @fix_plan.md and .response_analysis paths in test_cli_modern.bats - Update templates directory missing test to account for global fallback Template fix: - Fix @fix_plan.md reference in templates/PROMPT.md to use .ralph/ prefix Script fixes: - Fix PROMPT_FILE comparison in ralph_loop.sh to use $RALPH_DIR/PROMPT.md - Fix examples migration logic in migrate_to_ralph_folder.sh (remove premature mkdir) - Move templates directory check AFTER cd in setup.sh (was checking wrong location) - Add template directory validation with fallback to global templates All 310 tests pass. * Update migrate_to_ralph_folder.sh Co-authored-by: macroscopeapp[bot] <170038800+macroscopeapp[bot]@users.noreply.github.com> * fix: address code review feedback for .ralph/ subfolder structure Code Review Fixes: - Fix test_json_parsing.bats: all result_file and session file paths now use $RALPH_DIR prefix - Fix ralph_loop.sh help text: paths now show .ralph/.ralph_session, .ralph/.call_count, etc. - Fix migrate_to_ralph_folder.sh: - Proper error handling for date command (separate local declaration) - Use cp -a source/. dest/ pattern to preserve dotfiles and attributes - Remove 2>/dev/null suppression to surface copy errors - Update create_files.sh to use .ralph/ structure for embedded scripts - Update .gitignore with all .ralph/ state file paths - Add old structure detection in ralph_loop.sh with helpful migration message Version Update: - Bump to v0.10.0 (breaking change: structural reorganization) - Update README.md and CLAUDE.md with new version and release notes - Add ralph-migrate documentation to Key Commands section All 310 tests pass. --------- Co-authored-by: macroscopeapp[bot] <170038800+macroscopeapp[bot]@users.noreply.github.com>
This commit is contained in:
parent
0e95f67318
commit
9b19d70e35
27 changed files with 1126 additions and 585 deletions
|
|
@ -16,16 +16,19 @@ setup() {
|
|||
git config user.email "test@example.com"
|
||||
git config user.name "Test User"
|
||||
|
||||
# Set up environment with .ralph/ subfolder structure
|
||||
export RALPH_DIR=".ralph"
|
||||
|
||||
# Create necessary files
|
||||
create_sample_prd_md
|
||||
create_sample_fix_plan
|
||||
|
||||
# Set up environment
|
||||
export PROMPT_FILE="PROMPT.md"
|
||||
export LOG_DIR="logs"
|
||||
export EXIT_SIGNALS_FILE=".exit_signals"
|
||||
export PROMPT_FILE="$RALPH_DIR/PROMPT.md"
|
||||
export LOG_DIR="$RALPH_DIR/logs"
|
||||
export EXIT_SIGNALS_FILE="$RALPH_DIR/.exit_signals"
|
||||
|
||||
mkdir -p "$LOG_DIR"
|
||||
mkdir -p "$RALPH_DIR" "$LOG_DIR"
|
||||
echo '{"test_only_loops": [], "done_signals": [], "completion_indicators": []}' > "$EXIT_SIGNALS_FILE"
|
||||
|
||||
# Source library components
|
||||
|
|
@ -48,8 +51,8 @@ teardown() {
|
|||
analyze_response "$output_file" 1
|
||||
|
||||
# Should not crash, should create analysis file
|
||||
assert_file_exists ".response_analysis"
|
||||
local exit_signal=$(jq -r '.analysis.exit_signal' .response_analysis)
|
||||
assert_file_exists "$RALPH_DIR/.response_analysis"
|
||||
local exit_signal=$(jq -r '.analysis.exit_signal' "$RALPH_DIR/.response_analysis")
|
||||
# Empty output shouldn't trigger exit
|
||||
assert_equal "$exit_signal" "false"
|
||||
}
|
||||
|
|
@ -66,8 +69,8 @@ teardown() {
|
|||
analyze_response "$output_file" 1
|
||||
|
||||
# Should handle without error
|
||||
assert_file_exists ".response_analysis"
|
||||
local output_length=$(jq -r '.analysis.output_length' .response_analysis)
|
||||
assert_file_exists "$RALPH_DIR/.response_analysis"
|
||||
local output_length=$(jq -r '.analysis.output_length' "$RALPH_DIR/.response_analysis")
|
||||
[[ "$output_length" -gt 50000 ]]
|
||||
}
|
||||
|
||||
|
|
@ -86,7 +89,7 @@ EOF
|
|||
analyze_response "$output_file" 1
|
||||
|
||||
# Should not crash, may not detect structured output
|
||||
assert_file_exists ".response_analysis"
|
||||
assert_file_exists "$RALPH_DIR/.response_analysis"
|
||||
}
|
||||
|
||||
# Edge Case 4: Missing exit signals file
|
||||
|
|
@ -117,7 +120,7 @@ EOF
|
|||
record_loop_result 1 -1 "false" 1000 || true
|
||||
|
||||
# Should not crash
|
||||
local state=$(jq -r '.state' .circuit_breaker_state)
|
||||
local state=$(jq -r '.state' "$RALPH_DIR/.circuit_breaker_state")
|
||||
# Should still be valid state
|
||||
[[ "$state" == "CLOSED" || "$state" == "HALF_OPEN" ]]
|
||||
}
|
||||
|
|
@ -129,7 +132,7 @@ EOF
|
|||
# Simulate loop 9999
|
||||
record_loop_result 9999 5 "false" 1000
|
||||
|
||||
local current_loop=$(jq -r '.current_loop' .circuit_breaker_state)
|
||||
local current_loop=$(jq -r '.current_loop' "$RALPH_DIR/.circuit_breaker_state")
|
||||
assert_equal "$current_loop" "9999"
|
||||
}
|
||||
|
||||
|
|
@ -145,10 +148,10 @@ EOF
|
|||
|
||||
analyze_response "$output_file" 1
|
||||
|
||||
assert_file_exists ".response_analysis"
|
||||
assert_file_exists "$RALPH_DIR/.response_analysis"
|
||||
|
||||
# Should detect "Done" as completion keyword
|
||||
local has_completion=$(jq -r '.analysis.has_completion_signal' .response_analysis)
|
||||
local has_completion=$(jq -r '.analysis.has_completion_signal' "$RALPH_DIR/.response_analysis")
|
||||
assert_equal "$has_completion" "true"
|
||||
}
|
||||
|
||||
|
|
@ -173,7 +176,7 @@ EOF
|
|||
analyze_response "$output_file" 1
|
||||
|
||||
# Should detect structured output (picks first or last block)
|
||||
local exit_signal=$(jq -r '.analysis.exit_signal' .response_analysis)
|
||||
local exit_signal=$(jq -r '.analysis.exit_signal' "$RALPH_DIR/.response_analysis")
|
||||
# Should detect completion somehow
|
||||
[[ "$exit_signal" == "true" || "$exit_signal" == "false" ]]
|
||||
}
|
||||
|
|
@ -183,13 +186,13 @@ EOF
|
|||
init_circuit_breaker
|
||||
|
||||
# Corrupt the state file
|
||||
echo "invalid json{" > .circuit_breaker_state
|
||||
echo "invalid json{" > "$RALPH_DIR/.circuit_breaker_state"
|
||||
|
||||
# Should recover gracefully
|
||||
init_circuit_breaker
|
||||
|
||||
# Should have valid state now
|
||||
local state=$(jq -r '.state' .circuit_breaker_state)
|
||||
local state=$(jq -r '.state' "$RALPH_DIR/.circuit_breaker_state")
|
||||
assert_equal "$state" "CLOSED"
|
||||
}
|
||||
|
||||
|
|
@ -205,7 +208,7 @@ EOF
|
|||
analyze_response "$output_file" 1 || true
|
||||
|
||||
# File should exist even if analysis struggled
|
||||
[[ -f ".response_analysis" ]]
|
||||
[[ -f "$RALPH_DIR/.response_analysis" ]]
|
||||
}
|
||||
|
||||
# Edge Case 11: Simultaneous test-only and completion signals
|
||||
|
|
@ -222,8 +225,8 @@ EOF
|
|||
|
||||
analyze_response "$output_file" 1
|
||||
|
||||
local is_test_only=$(jq -r '.analysis.is_test_only' .response_analysis)
|
||||
local has_completion=$(jq -r '.analysis.has_completion_signal' .response_analysis)
|
||||
local is_test_only=$(jq -r '.analysis.is_test_only' "$RALPH_DIR/.response_analysis")
|
||||
local has_completion=$(jq -r '.analysis.has_completion_signal' "$RALPH_DIR/.response_analysis")
|
||||
|
||||
# Both can be true - completion signal should take precedence
|
||||
assert_equal "$has_completion" "true"
|
||||
|
|
@ -241,7 +244,7 @@ EOF
|
|||
record_loop_result 3 5 "false" 2000
|
||||
|
||||
# Should recover to CLOSED
|
||||
local state=$(jq -r '.state' .circuit_breaker_state)
|
||||
local state=$(jq -r '.state' "$RALPH_DIR/.circuit_breaker_state")
|
||||
assert_equal "$state" "CLOSED"
|
||||
}
|
||||
|
||||
|
|
@ -264,7 +267,7 @@ EOF
|
|||
analyze_response "$output_file" 2
|
||||
|
||||
# Should be at boundary
|
||||
assert_file_exists ".response_analysis"
|
||||
assert_file_exists "$RALPH_DIR/.response_analysis"
|
||||
}
|
||||
|
||||
# Edge Case 14: Missing git repository
|
||||
|
|
@ -278,10 +281,10 @@ EOF
|
|||
# Should not crash when git commands fail
|
||||
analyze_response "$output_file" 1
|
||||
|
||||
assert_file_exists ".response_analysis"
|
||||
assert_file_exists "$RALPH_DIR/.response_analysis"
|
||||
|
||||
# files_modified should be 0 (can't detect without git)
|
||||
local files_modified=$(jq -r '.analysis.files_modified' .response_analysis)
|
||||
local files_modified=$(jq -r '.analysis.files_modified' "$RALPH_DIR/.response_analysis")
|
||||
assert_equal "$files_modified" "0"
|
||||
}
|
||||
|
||||
|
|
@ -318,7 +321,7 @@ EOF
|
|||
record_loop_result 3 1 "false" 1000
|
||||
|
||||
# Should track all 3 correctly
|
||||
local current_loop=$(jq -r '.current_loop' .circuit_breaker_state)
|
||||
local current_loop=$(jq -r '.current_loop' "$RALPH_DIR/.circuit_breaker_state")
|
||||
assert_equal "$current_loop" "3"
|
||||
}
|
||||
|
||||
|
|
@ -344,7 +347,7 @@ EOF
|
|||
analyze_response "$output_file" 1
|
||||
|
||||
# Confidence should be very high (100 + bonuses)
|
||||
local confidence=$(jq -r '.analysis.confidence_score' .response_analysis)
|
||||
local confidence=$(jq -r '.analysis.confidence_score' "$RALPH_DIR/.response_analysis")
|
||||
[[ "$confidence" -ge 100 ]]
|
||||
}
|
||||
|
||||
|
|
@ -353,7 +356,7 @@ EOF
|
|||
init_circuit_breaker
|
||||
|
||||
# Corrupt history
|
||||
echo "not valid json" > .circuit_breaker_history
|
||||
echo "not valid json" > "$RALPH_DIR/.circuit_breaker_history"
|
||||
|
||||
# Should handle gracefully on next transition
|
||||
record_loop_result 1 0 "false" 1000 || true
|
||||
|
|
@ -361,7 +364,7 @@ EOF
|
|||
|
||||
# Depending on implementation, may recreate or skip history logging
|
||||
# Just verify no crash
|
||||
[[ -f .circuit_breaker_state ]]
|
||||
[[ -f "$RALPH_DIR/.circuit_breaker_state" ]]
|
||||
}
|
||||
|
||||
# Edge Case 19: Status block with extra fields
|
||||
|
|
@ -380,7 +383,7 @@ EOF
|
|||
analyze_response "$output_file" 1
|
||||
|
||||
# Should successfully parse known fields
|
||||
local exit_signal=$(jq -r '.analysis.exit_signal' .response_analysis)
|
||||
local exit_signal=$(jq -r '.analysis.exit_signal' "$RALPH_DIR/.response_analysis")
|
||||
assert_equal "$exit_signal" "true"
|
||||
}
|
||||
|
||||
|
|
@ -440,18 +443,18 @@ EOF
|
|||
update_exit_signals
|
||||
|
||||
# After each loop, check that exit_signal is correctly captured as false
|
||||
local exit_signal=$(jq -r '.analysis.exit_signal' .response_analysis)
|
||||
local exit_signal=$(jq -r '.analysis.exit_signal' "$RALPH_DIR/.response_analysis")
|
||||
assert_equal "$exit_signal" "false"
|
||||
done
|
||||
|
||||
# Verify that analyze_response correctly captures EXIT_SIGNAL=false
|
||||
local final_exit_signal=$(jq -r '.analysis.exit_signal' .response_analysis)
|
||||
local final_exit_signal=$(jq -r '.analysis.exit_signal' "$RALPH_DIR/.response_analysis")
|
||||
assert_equal "$final_exit_signal" "false"
|
||||
|
||||
# Key test: Even with high completion indicators set externally,
|
||||
# the exit_signal should still be false (respecting Claude's explicit intent)
|
||||
echo '{"test_only_loops": [], "done_signals": [], "completion_indicators": [1,2,3]}' > "$EXIT_SIGNALS_FILE"
|
||||
local last_exit_signal=$(jq -r '.analysis.exit_signal' .response_analysis)
|
||||
local last_exit_signal=$(jq -r '.analysis.exit_signal' "$RALPH_DIR/.response_analysis")
|
||||
assert_equal "$last_exit_signal" "false"
|
||||
}
|
||||
|
||||
|
|
@ -473,7 +476,7 @@ EOF
|
|||
analyze_response "$output_file" $i
|
||||
update_exit_signals
|
||||
|
||||
local exit_signal=$(jq -r '.analysis.exit_signal' .response_analysis)
|
||||
local exit_signal=$(jq -r '.analysis.exit_signal' "$RALPH_DIR/.response_analysis")
|
||||
assert_equal "$exit_signal" "false"
|
||||
done
|
||||
|
||||
|
|
@ -491,11 +494,11 @@ EOF
|
|||
update_exit_signals
|
||||
|
||||
# Exit signal should now be true
|
||||
local exit_signal=$(jq -r '.analysis.exit_signal' .response_analysis)
|
||||
local exit_signal=$(jq -r '.analysis.exit_signal' "$RALPH_DIR/.response_analysis")
|
||||
assert_equal "$exit_signal" "true"
|
||||
|
||||
# Confidence should be >= 100 (100 from EXIT_SIGNAL: true, plus any natural language bonuses)
|
||||
local confidence=$(jq -r '.analysis.confidence_score' .response_analysis)
|
||||
local confidence=$(jq -r '.analysis.confidence_score' "$RALPH_DIR/.response_analysis")
|
||||
[[ "$confidence" -ge 100 ]]
|
||||
}
|
||||
|
||||
|
|
@ -517,10 +520,10 @@ EOF
|
|||
update_exit_signals
|
||||
|
||||
# Verify file exists
|
||||
assert_file_exists ".response_analysis"
|
||||
assert_file_exists "$RALPH_DIR/.response_analysis"
|
||||
|
||||
# Simulate file deletion (e.g., cleanup script ran)
|
||||
rm -f ".response_analysis"
|
||||
rm -f "$RALPH_DIR/.response_analysis"
|
||||
|
||||
# Add more completion indicators
|
||||
cat > "$output_file" << 'EOF'
|
||||
|
|
@ -531,7 +534,7 @@ EOF
|
|||
update_exit_signals
|
||||
|
||||
# File should be recreated
|
||||
assert_file_exists ".response_analysis"
|
||||
assert_file_exists "$RALPH_DIR/.response_analysis"
|
||||
}
|
||||
|
||||
# Edge Case 24: STATUS=COMPLETE but EXIT_SIGNAL=false conflict in RALPH_STATUS
|
||||
|
|
@ -554,7 +557,7 @@ EOF
|
|||
analyze_response "$output_file" 1
|
||||
|
||||
# EXIT_SIGNAL: false should take precedence over STATUS: COMPLETE
|
||||
local exit_signal=$(jq -r '.analysis.exit_signal' .response_analysis)
|
||||
local exit_signal=$(jq -r '.analysis.exit_signal' "$RALPH_DIR/.response_analysis")
|
||||
assert_equal "$exit_signal" "false"
|
||||
|
||||
# has_completion_signal can still be true (STATUS was COMPLETE)
|
||||
|
|
@ -582,7 +585,7 @@ EOF
|
|||
update_exit_signals
|
||||
|
||||
# Exit signal should be false (completion_status is in_progress)
|
||||
local exit_signal=$(jq -r '.analysis.exit_signal' .response_analysis)
|
||||
local exit_signal=$(jq -r '.analysis.exit_signal' "$RALPH_DIR/.response_analysis")
|
||||
assert_equal "$exit_signal" "false"
|
||||
|
||||
# Now test with complete status
|
||||
|
|
@ -602,6 +605,6 @@ EOF
|
|||
update_exit_signals
|
||||
|
||||
# Exit signal should be true (completion_status is complete)
|
||||
local exit_signal=$(jq -r '.analysis.exit_signal' .response_analysis)
|
||||
local exit_signal=$(jq -r '.analysis.exit_signal' "$RALPH_DIR/.response_analysis")
|
||||
assert_equal "$exit_signal" "true"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -74,6 +74,12 @@ EOF
|
|||
#!/bin/bash
|
||||
# Mock setup.sh
|
||||
echo "Setup running"
|
||||
EOF
|
||||
|
||||
cat > "$MOCK_SOURCE_DIR/migrate_to_ralph_folder.sh" << 'EOF'
|
||||
#!/bin/bash
|
||||
# Mock migrate_to_ralph_folder.sh
|
||||
echo "Migration running"
|
||||
EOF
|
||||
|
||||
chmod +x "$MOCK_SOURCE_DIR"/*.sh
|
||||
|
|
@ -155,17 +161,19 @@ run_install() {
|
|||
@test "install.sh creates ~/.local/bin commands" {
|
||||
run run_install
|
||||
|
||||
# Check all four wrapper commands exist
|
||||
# Check all five wrapper commands exist
|
||||
assert_file_exists "$TEST_INSTALL_DIR/ralph"
|
||||
assert_file_exists "$TEST_INSTALL_DIR/ralph-monitor"
|
||||
assert_file_exists "$TEST_INSTALL_DIR/ralph-setup"
|
||||
assert_file_exists "$TEST_INSTALL_DIR/ralph-import"
|
||||
assert_file_exists "$TEST_INSTALL_DIR/ralph-migrate"
|
||||
|
||||
# Verify each command contains proper shebang
|
||||
grep -q "#!/bin/bash" "$TEST_INSTALL_DIR/ralph"
|
||||
grep -q "#!/bin/bash" "$TEST_INSTALL_DIR/ralph-monitor"
|
||||
grep -q "#!/bin/bash" "$TEST_INSTALL_DIR/ralph-setup"
|
||||
grep -q "#!/bin/bash" "$TEST_INSTALL_DIR/ralph-import"
|
||||
grep -q "#!/bin/bash" "$TEST_INSTALL_DIR/ralph-migrate"
|
||||
}
|
||||
|
||||
@test "install.sh sets executable permissions" {
|
||||
|
|
@ -176,12 +184,14 @@ run_install() {
|
|||
[[ -x "$TEST_INSTALL_DIR/ralph-monitor" ]]
|
||||
[[ -x "$TEST_INSTALL_DIR/ralph-setup" ]]
|
||||
[[ -x "$TEST_INSTALL_DIR/ralph-import" ]]
|
||||
[[ -x "$TEST_INSTALL_DIR/ralph-migrate" ]]
|
||||
|
||||
# Verify executable bit on main scripts
|
||||
[[ -x "$TEST_RALPH_HOME/ralph_loop.sh" ]]
|
||||
[[ -x "$TEST_RALPH_HOME/ralph_monitor.sh" ]]
|
||||
[[ -x "$TEST_RALPH_HOME/setup.sh" ]]
|
||||
[[ -x "$TEST_RALPH_HOME/ralph_import.sh" ]]
|
||||
[[ -x "$TEST_RALPH_HOME/migrate_to_ralph_folder.sh" ]]
|
||||
|
||||
# Verify lib scripts are executable
|
||||
[[ -x "$TEST_RALPH_HOME/lib/circuit_breaker.sh" ]]
|
||||
|
|
@ -428,6 +438,7 @@ EOF
|
|||
assert_file_exists "$TEST_INSTALL_DIR/ralph-monitor"
|
||||
assert_file_exists "$TEST_INSTALL_DIR/ralph-setup"
|
||||
assert_file_exists "$TEST_INSTALL_DIR/ralph-import"
|
||||
assert_file_exists "$TEST_INSTALL_DIR/ralph-migrate"
|
||||
|
||||
# Run uninstall
|
||||
run run_install uninstall
|
||||
|
|
@ -438,6 +449,7 @@ EOF
|
|||
assert_file_not_exists "$TEST_INSTALL_DIR/ralph-monitor"
|
||||
assert_file_not_exists "$TEST_INSTALL_DIR/ralph-setup"
|
||||
assert_file_not_exists "$TEST_INSTALL_DIR/ralph-import"
|
||||
assert_file_not_exists "$TEST_INSTALL_DIR/ralph-migrate"
|
||||
}
|
||||
|
||||
@test "install.sh uninstall cleans up directories" {
|
||||
|
|
@ -511,6 +523,7 @@ EOF
|
|||
assert_file_exists "$TEST_INSTALL_DIR/ralph-monitor"
|
||||
assert_file_exists "$TEST_INSTALL_DIR/ralph-setup"
|
||||
assert_file_exists "$TEST_INSTALL_DIR/ralph-import"
|
||||
assert_file_exists "$TEST_INSTALL_DIR/ralph-migrate"
|
||||
|
||||
# Verify all templates copied
|
||||
assert_file_exists "$TEST_RALPH_HOME/templates/PROMPT.md"
|
||||
|
|
@ -527,6 +540,7 @@ EOF
|
|||
assert_file_exists "$TEST_RALPH_HOME/ralph_monitor.sh"
|
||||
assert_file_exists "$TEST_RALPH_HOME/setup.sh"
|
||||
assert_file_exists "$TEST_RALPH_HOME/ralph_import.sh"
|
||||
assert_file_exists "$TEST_RALPH_HOME/migrate_to_ralph_folder.sh"
|
||||
|
||||
# Verify all permissions correct
|
||||
[[ -x "$TEST_INSTALL_DIR/ralph" ]]
|
||||
|
|
|
|||
|
|
@ -15,24 +15,27 @@ setup() {
|
|||
git config user.email "test@example.com"
|
||||
git config user.name "Test User"
|
||||
|
||||
# Set up environment with .ralph/ subfolder structure
|
||||
export RALPH_DIR=".ralph"
|
||||
|
||||
# Create necessary files
|
||||
create_sample_prd_md
|
||||
create_sample_fix_plan
|
||||
|
||||
# Source the main ralph_loop.sh functions
|
||||
export PROMPT_FILE="PROMPT.md"
|
||||
export LOG_DIR="logs"
|
||||
export DOCS_DIR="docs/generated"
|
||||
export STATUS_FILE="status.json"
|
||||
export PROGRESS_FILE="progress.json"
|
||||
export CALL_COUNT_FILE=".call_count"
|
||||
export TIMESTAMP_FILE=".last_reset"
|
||||
export EXIT_SIGNALS_FILE=".exit_signals"
|
||||
export PROMPT_FILE="$RALPH_DIR/PROMPT.md"
|
||||
export LOG_DIR="$RALPH_DIR/logs"
|
||||
export DOCS_DIR="$RALPH_DIR/docs/generated"
|
||||
export STATUS_FILE="$RALPH_DIR/status.json"
|
||||
export PROGRESS_FILE="$RALPH_DIR/progress.json"
|
||||
export CALL_COUNT_FILE="$RALPH_DIR/.call_count"
|
||||
export TIMESTAMP_FILE="$RALPH_DIR/.last_reset"
|
||||
export EXIT_SIGNALS_FILE="$RALPH_DIR/.exit_signals"
|
||||
export MAX_CALLS_PER_HOUR=100
|
||||
export MAX_CONSECUTIVE_TEST_LOOPS=3
|
||||
export MAX_CONSECUTIVE_DONE_SIGNALS=2
|
||||
|
||||
mkdir -p "$LOG_DIR" "$DOCS_DIR"
|
||||
mkdir -p "$RALPH_DIR" "$LOG_DIR" "$DOCS_DIR"
|
||||
|
||||
# Initialize tracking files
|
||||
echo "0" > "$CALL_COUNT_FILE"
|
||||
|
|
@ -78,13 +81,13 @@ EOF
|
|||
# Should return 0 (success)
|
||||
assert_equal "$result" "0"
|
||||
|
||||
# Check analysis file
|
||||
assert_file_exists ".response_analysis"
|
||||
# Check analysis file in .ralph/ subfolder
|
||||
assert_file_exists "$RALPH_DIR/.response_analysis"
|
||||
|
||||
local exit_signal=$(jq -r '.analysis.exit_signal' .response_analysis)
|
||||
local exit_signal=$(jq -r '.analysis.exit_signal' "$RALPH_DIR/.response_analysis")
|
||||
assert_equal "$exit_signal" "true"
|
||||
|
||||
local confidence=$(jq -r '.analysis.confidence_score' .response_analysis)
|
||||
local confidence=$(jq -r '.analysis.confidence_score' "$RALPH_DIR/.response_analysis")
|
||||
# Confidence may be >= 100 due to multiple bonus points
|
||||
[[ "$confidence" -ge 100 ]]
|
||||
}
|
||||
|
|
@ -103,7 +106,7 @@ EOF
|
|||
local result=$?
|
||||
|
||||
# Check analysis result
|
||||
local has_completion=$(jq -r '.analysis.has_completion_signal' .response_analysis)
|
||||
local has_completion=$(jq -r '.analysis.has_completion_signal' "$RALPH_DIR/.response_analysis")
|
||||
assert_equal "$has_completion" "true"
|
||||
}
|
||||
|
||||
|
|
@ -120,7 +123,7 @@ EOF
|
|||
|
||||
analyze_response "$output_file" 1
|
||||
|
||||
local is_test_only=$(jq -r '.analysis.is_test_only' .response_analysis)
|
||||
local is_test_only=$(jq -r '.analysis.is_test_only' "$RALPH_DIR/.response_analysis")
|
||||
assert_equal "$is_test_only" "true"
|
||||
}
|
||||
|
||||
|
|
@ -137,7 +140,7 @@ EOF
|
|||
|
||||
analyze_response "$output_file" 1
|
||||
|
||||
local files_modified=$(jq -r '.analysis.files_modified' .response_analysis)
|
||||
local files_modified=$(jq -r '.analysis.files_modified' "$RALPH_DIR/.response_analysis")
|
||||
# files_modified should be > 0 because test_file.txt is untracked
|
||||
[[ "$files_modified" -ge 0 ]] # Relaxed: >= 0 instead of > 0 (git diff doesn't show untracked)
|
||||
}
|
||||
|
|
@ -167,9 +170,9 @@ EOF
|
|||
@test "init_circuit_breaker creates state file" {
|
||||
init_circuit_breaker
|
||||
|
||||
assert_file_exists ".circuit_breaker_state"
|
||||
assert_file_exists "$RALPH_DIR/.circuit_breaker_state"
|
||||
|
||||
local state=$(jq -r '.state' .circuit_breaker_state)
|
||||
local state=$(jq -r '.state' "$RALPH_DIR/.circuit_breaker_state")
|
||||
assert_equal "$state" "CLOSED"
|
||||
}
|
||||
|
||||
|
|
@ -183,7 +186,7 @@ EOF
|
|||
record_loop_result $i 0 "false" 1000 || true
|
||||
done
|
||||
|
||||
local state=$(jq -r '.state' .circuit_breaker_state)
|
||||
local state=$(jq -r '.state' "$RALPH_DIR/.circuit_breaker_state")
|
||||
assert_equal "$state" "OPEN"
|
||||
}
|
||||
|
||||
|
|
@ -195,7 +198,7 @@ EOF
|
|||
record_loop_result 1 0 "false" 1000
|
||||
record_loop_result 2 0 "false" 1000
|
||||
|
||||
local state=$(jq -r '.state' .circuit_breaker_state)
|
||||
local state=$(jq -r '.state' "$RALPH_DIR/.circuit_breaker_state")
|
||||
assert_equal "$state" "HALF_OPEN"
|
||||
}
|
||||
|
||||
|
|
@ -210,7 +213,7 @@ EOF
|
|||
# Now make progress
|
||||
record_loop_result 3 5 "false" 1000
|
||||
|
||||
local state=$(jq -r '.state' .circuit_breaker_state)
|
||||
local state=$(jq -r '.state' "$RALPH_DIR/.circuit_breaker_state")
|
||||
assert_equal "$state" "CLOSED"
|
||||
}
|
||||
|
||||
|
|
@ -223,9 +226,9 @@ EOF
|
|||
record_loop_result $i 1 "true" 1000 || true
|
||||
done
|
||||
|
||||
local state=$(jq -r '.state' .circuit_breaker_state)
|
||||
local state=$(jq -r '.state' "$RALPH_DIR/.circuit_breaker_state")
|
||||
# Should eventually open due to consecutive errors
|
||||
local same_error_count=$(jq -r '.consecutive_same_error' .circuit_breaker_state)
|
||||
local same_error_count=$(jq -r '.consecutive_same_error' "$RALPH_DIR/.circuit_breaker_state")
|
||||
[[ "$same_error_count" -ge 5 ]]
|
||||
}
|
||||
|
||||
|
|
@ -260,7 +263,7 @@ EOF
|
|||
# Reset
|
||||
reset_circuit_breaker "Test reset"
|
||||
|
||||
local state=$(jq -r '.state' .circuit_breaker_state)
|
||||
local state=$(jq -r '.state' "$RALPH_DIR/.circuit_breaker_state")
|
||||
assert_equal "$state" "CLOSED"
|
||||
}
|
||||
|
||||
|
|
@ -343,7 +346,7 @@ EOF
|
|||
done
|
||||
|
||||
# Circuit should be OPEN
|
||||
local state=$(jq -r '.state' .circuit_breaker_state)
|
||||
local state=$(jq -r '.state' "$RALPH_DIR/.circuit_breaker_state")
|
||||
assert_equal "$state" "OPEN"
|
||||
|
||||
# Verify should_halt_execution returns true
|
||||
|
|
@ -374,7 +377,7 @@ EOF
|
|||
|
||||
analyze_response "$output_file" 1
|
||||
|
||||
local confidence=$(jq -r '.analysis.confidence_score' .response_analysis)
|
||||
local confidence=$(jq -r '.analysis.confidence_score' "$RALPH_DIR/.response_analysis")
|
||||
# Should be very high (100 from structured + bonuses)
|
||||
[[ "$confidence" -ge 100 ]]
|
||||
}
|
||||
|
|
@ -412,10 +415,10 @@ EOF
|
|||
record_loop_result 2 0 "false" 1000
|
||||
|
||||
# Check history file exists
|
||||
assert_file_exists ".circuit_breaker_history"
|
||||
assert_file_exists "$RALPH_DIR/.circuit_breaker_history"
|
||||
|
||||
# Verify it's valid JSON
|
||||
jq '.' .circuit_breaker_history > /dev/null
|
||||
jq '.' "$RALPH_DIR/.circuit_breaker_history" > /dev/null
|
||||
}
|
||||
|
||||
# Test 19: Rolling window for exit signals
|
||||
|
|
@ -458,7 +461,7 @@ EOF
|
|||
analyze_response "$output_file" 2
|
||||
|
||||
# Should detect declining output
|
||||
local confidence=$(jq -r '.analysis.confidence_score' .response_analysis)
|
||||
local confidence=$(jq -r '.analysis.confidence_score' "$RALPH_DIR/.response_analysis")
|
||||
# Short output after long one should increase confidence of completion
|
||||
[[ "$confidence" -gt 0 ]]
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25,18 +25,19 @@ setup() {
|
|||
mkdir -p "$MOCK_BIN_DIR"
|
||||
export PATH="$MOCK_BIN_DIR:$PATH"
|
||||
|
||||
# Create mock ralph-setup command
|
||||
# Create mock ralph-setup command (with .ralph/ subfolder structure)
|
||||
cat > "$MOCK_BIN_DIR/ralph-setup" << 'MOCK_SETUP_EOF'
|
||||
#!/bin/bash
|
||||
# Mock ralph-setup that creates project structure
|
||||
# Mock ralph-setup that creates project structure with .ralph/ subfolder
|
||||
project_name="${1:-test-project}"
|
||||
mkdir -p "$project_name"/{specs,src,logs,docs/generated}
|
||||
mkdir -p "$project_name"/src
|
||||
mkdir -p "$project_name"/.ralph/{specs/stdlib,examples,logs,docs/generated}
|
||||
cd "$project_name"
|
||||
git init > /dev/null 2>&1
|
||||
git config user.email "test@example.com"
|
||||
git config user.name "Test User"
|
||||
# Create basic template files
|
||||
cat > PROMPT.md << 'EOF'
|
||||
# Create basic template files in .ralph/ subfolder
|
||||
cat > .ralph/PROMPT.md << 'EOF'
|
||||
# Ralph Development Instructions
|
||||
|
||||
## Context
|
||||
|
|
@ -53,7 +54,7 @@ You are Ralph, an autonomous AI development agent.
|
|||
- LIMIT testing to ~20% of your total effort
|
||||
EOF
|
||||
|
||||
cat > "@fix_plan.md" << 'EOF'
|
||||
cat > ".ralph/@fix_plan.md" << 'EOF'
|
||||
# Ralph Fix Plan
|
||||
|
||||
## High Priority
|
||||
|
|
@ -69,7 +70,7 @@ cat > "@fix_plan.md" << 'EOF'
|
|||
- [x] Project initialization
|
||||
EOF
|
||||
|
||||
cat > "@AGENT.md" << 'EOF'
|
||||
cat > ".ralph/@AGENT.md" << 'EOF'
|
||||
# Agent Build Instructions
|
||||
|
||||
## Project Setup
|
||||
|
|
@ -104,12 +105,15 @@ teardown() {
|
|||
create_mock_claude_success() {
|
||||
cat > "$MOCK_BIN_DIR/claude" << 'MOCK_CLAUDE_EOF'
|
||||
#!/bin/bash
|
||||
# Mock Claude Code CLI that creates expected output files
|
||||
# Mock Claude Code CLI that creates expected output files in .ralph/ subfolder
|
||||
# Read from stdin (conversion prompt)
|
||||
cat > /dev/null
|
||||
|
||||
# Create PROMPT.md with Ralph format
|
||||
cat > PROMPT.md << 'EOF'
|
||||
# Ensure .ralph directory exists
|
||||
mkdir -p .ralph/specs
|
||||
|
||||
# Create PROMPT.md with Ralph format in .ralph/
|
||||
cat > .ralph/PROMPT.md << 'EOF'
|
||||
# Ralph Development Instructions
|
||||
|
||||
## Context
|
||||
|
|
@ -158,8 +162,8 @@ You are Ralph, an autonomous AI development agent working on a Task Management A
|
|||
Follow @fix_plan.md and choose the most important item to implement next.
|
||||
EOF
|
||||
|
||||
# Create @fix_plan.md
|
||||
cat > "@fix_plan.md" << 'EOF'
|
||||
# Create @fix_plan.md in .ralph/
|
||||
cat > ".ralph/@fix_plan.md" << 'EOF'
|
||||
# Ralph Fix Plan
|
||||
|
||||
## High Priority
|
||||
|
|
@ -186,9 +190,8 @@ cat > "@fix_plan.md" << 'EOF'
|
|||
- Update this file after each major milestone
|
||||
EOF
|
||||
|
||||
# Create specs/requirements.md
|
||||
mkdir -p specs
|
||||
cat > specs/requirements.md << 'EOF'
|
||||
# Create specs/requirements.md in .ralph/specs/
|
||||
cat > .ralph/specs/requirements.md << 'EOF'
|
||||
# Technical Specifications
|
||||
|
||||
## System Architecture
|
||||
|
|
@ -336,23 +339,23 @@ remove_ralph_setup_mock() {
|
|||
|
||||
assert_success
|
||||
|
||||
# PROMPT.md should exist
|
||||
assert_file_exists "test-app/PROMPT.md"
|
||||
# PROMPT.md should exist in .ralph/ subfolder
|
||||
assert_file_exists "test-app/.ralph/PROMPT.md"
|
||||
|
||||
# Check key sections exist
|
||||
run grep -c "Ralph Development Instructions" "test-app/PROMPT.md"
|
||||
run grep -c "Ralph Development Instructions" "test-app/.ralph/PROMPT.md"
|
||||
assert_success
|
||||
[[ "$output" -ge 1 ]]
|
||||
|
||||
run grep -c "Current Objectives" "test-app/PROMPT.md"
|
||||
run grep -c "Current Objectives" "test-app/.ralph/PROMPT.md"
|
||||
assert_success
|
||||
[[ "$output" -ge 1 ]]
|
||||
|
||||
run grep -c "Key Principles" "test-app/PROMPT.md"
|
||||
run grep -c "Key Principles" "test-app/.ralph/PROMPT.md"
|
||||
assert_success
|
||||
[[ "$output" -ge 1 ]]
|
||||
|
||||
run grep -c "Testing Guidelines" "test-app/PROMPT.md"
|
||||
run grep -c "Testing Guidelines" "test-app/.ralph/PROMPT.md"
|
||||
assert_success
|
||||
[[ "$output" -ge 1 ]]
|
||||
}
|
||||
|
|
@ -365,28 +368,28 @@ remove_ralph_setup_mock() {
|
|||
|
||||
assert_success
|
||||
|
||||
# @fix_plan.md should exist
|
||||
assert_file_exists "test-app/@fix_plan.md"
|
||||
# @fix_plan.md should exist in .ralph/ subfolder
|
||||
assert_file_exists "test-app/.ralph/@fix_plan.md"
|
||||
|
||||
# Check structure includes priority sections
|
||||
run grep -c "High Priority" "test-app/@fix_plan.md"
|
||||
run grep -c "High Priority" "test-app/.ralph/@fix_plan.md"
|
||||
assert_success
|
||||
[[ "$output" -ge 1 ]]
|
||||
|
||||
run grep -c "Medium Priority" "test-app/@fix_plan.md"
|
||||
run grep -c "Medium Priority" "test-app/.ralph/@fix_plan.md"
|
||||
assert_success
|
||||
[[ "$output" -ge 1 ]]
|
||||
|
||||
run grep -c "Low Priority" "test-app/@fix_plan.md"
|
||||
run grep -c "Low Priority" "test-app/.ralph/@fix_plan.md"
|
||||
assert_success
|
||||
[[ "$output" -ge 1 ]]
|
||||
|
||||
run grep -c "Completed" "test-app/@fix_plan.md"
|
||||
run grep -c "Completed" "test-app/.ralph/@fix_plan.md"
|
||||
assert_success
|
||||
[[ "$output" -ge 1 ]]
|
||||
|
||||
# Check checkbox format
|
||||
run grep -E "^\- \[[ x]\]" "test-app/@fix_plan.md"
|
||||
run grep -E "^\- \[[ x]\]" "test-app/.ralph/@fix_plan.md"
|
||||
assert_success
|
||||
}
|
||||
|
||||
|
|
@ -398,14 +401,14 @@ remove_ralph_setup_mock() {
|
|||
|
||||
assert_success
|
||||
|
||||
# specs directory should exist
|
||||
assert_dir_exists "test-app/specs"
|
||||
# specs directory should exist in .ralph/ subfolder
|
||||
assert_dir_exists "test-app/.ralph/specs"
|
||||
|
||||
# requirements.md should exist
|
||||
assert_file_exists "test-app/specs/requirements.md"
|
||||
# requirements.md should exist in .ralph/specs/
|
||||
assert_file_exists "test-app/.ralph/specs/requirements.md"
|
||||
|
||||
# Check technical specification content
|
||||
run grep -c "Technical Specifications" "test-app/specs/requirements.md"
|
||||
run grep -c "Technical Specifications" "test-app/.ralph/specs/requirements.md"
|
||||
assert_success
|
||||
[[ "$output" -ge 1 ]]
|
||||
}
|
||||
|
|
@ -426,10 +429,10 @@ remove_ralph_setup_mock() {
|
|||
# Custom project directory should be created
|
||||
assert_dir_exists "my-custom-project"
|
||||
|
||||
# Files should be in custom-named directory
|
||||
assert_file_exists "my-custom-project/PROMPT.md"
|
||||
assert_file_exists "my-custom-project/@fix_plan.md"
|
||||
assert_file_exists "my-custom-project/specs/requirements.md"
|
||||
# Files should be in custom-named directory under .ralph/ subfolder
|
||||
assert_file_exists "my-custom-project/.ralph/PROMPT.md"
|
||||
assert_file_exists "my-custom-project/.ralph/@fix_plan.md"
|
||||
assert_file_exists "my-custom-project/.ralph/specs/requirements.md"
|
||||
|
||||
# Default name directory should NOT exist
|
||||
[[ ! -d "generic-prd" ]]
|
||||
|
|
@ -447,8 +450,8 @@ remove_ralph_setup_mock() {
|
|||
# Project name should be extracted from filename (without extension)
|
||||
assert_dir_exists "awesome-app-requirements"
|
||||
|
||||
# Files should be in auto-named directory
|
||||
assert_file_exists "awesome-app-requirements/PROMPT.md"
|
||||
# Files should be in auto-named directory under .ralph/ subfolder
|
||||
assert_file_exists "awesome-app-requirements/.ralph/PROMPT.md"
|
||||
}
|
||||
|
||||
# =============================================================================
|
||||
|
|
@ -590,18 +593,18 @@ remove_ralph_setup_mock() {
|
|||
|
||||
assert_success
|
||||
|
||||
# Verify complete project structure
|
||||
# Verify complete project structure with .ralph/ subfolder
|
||||
assert_dir_exists "my-app"
|
||||
assert_dir_exists "my-app/specs"
|
||||
assert_dir_exists "my-app/.ralph/specs"
|
||||
assert_dir_exists "my-app/src"
|
||||
assert_dir_exists "my-app/logs"
|
||||
assert_dir_exists "my-app/docs/generated"
|
||||
assert_dir_exists "my-app/.ralph/logs"
|
||||
assert_dir_exists "my-app/.ralph/docs/generated"
|
||||
|
||||
# Verify all required files
|
||||
assert_file_exists "my-app/PROMPT.md"
|
||||
assert_file_exists "my-app/@fix_plan.md"
|
||||
assert_file_exists "my-app/@AGENT.md"
|
||||
assert_file_exists "my-app/specs/requirements.md"
|
||||
# Verify all required files in .ralph/ subfolder
|
||||
assert_file_exists "my-app/.ralph/PROMPT.md"
|
||||
assert_file_exists "my-app/.ralph/@fix_plan.md"
|
||||
assert_file_exists "my-app/.ralph/@AGENT.md"
|
||||
assert_file_exists "my-app/.ralph/specs/requirements.md"
|
||||
|
||||
# Verify source PRD was copied
|
||||
assert_file_exists "my-app/my-app.md"
|
||||
|
|
@ -698,12 +701,15 @@ EOF
|
|||
create_mock_claude_json_success() {
|
||||
cat > "$MOCK_BIN_DIR/claude" << 'MOCK_CLAUDE_JSON_EOF'
|
||||
#!/bin/bash
|
||||
# Mock Claude Code CLI that outputs JSON format and creates expected files
|
||||
# Mock Claude Code CLI that outputs JSON format and creates expected files in .ralph/
|
||||
# Read from stdin (conversion prompt)
|
||||
cat > /dev/null
|
||||
|
||||
# Create PROMPT.md with Ralph format
|
||||
cat > PROMPT.md << 'EOF'
|
||||
# Ensure .ralph directory exists
|
||||
mkdir -p .ralph/specs
|
||||
|
||||
# Create PROMPT.md with Ralph format in .ralph/
|
||||
cat > .ralph/PROMPT.md << 'EOF'
|
||||
# Ralph Development Instructions
|
||||
|
||||
## Context
|
||||
|
|
@ -720,8 +726,8 @@ You are Ralph, an autonomous AI development agent working on a Task Management A
|
|||
- LIMIT testing to ~20% of your total effort
|
||||
EOF
|
||||
|
||||
# Create @fix_plan.md
|
||||
cat > "@fix_plan.md" << 'EOF'
|
||||
# Create @fix_plan.md in .ralph/
|
||||
cat > ".ralph/@fix_plan.md" << 'EOF'
|
||||
# Ralph Fix Plan
|
||||
|
||||
## High Priority
|
||||
|
|
@ -737,9 +743,8 @@ cat > "@fix_plan.md" << 'EOF'
|
|||
- [x] Project initialization
|
||||
EOF
|
||||
|
||||
# Create specs/requirements.md
|
||||
mkdir -p specs
|
||||
cat > specs/requirements.md << 'EOF'
|
||||
# Create specs/requirements.md in .ralph/specs/
|
||||
cat > .ralph/specs/requirements.md << 'EOF'
|
||||
# Technical Specifications
|
||||
|
||||
## System Architecture
|
||||
|
|
@ -755,13 +760,13 @@ EOF
|
|||
# Output JSON response to stdout (mimicking --output-format json)
|
||||
cat << 'JSON_OUTPUT'
|
||||
{
|
||||
"result": "Successfully converted PRD to Ralph format. Created PROMPT.md, @fix_plan.md, and specs/requirements.md",
|
||||
"result": "Successfully converted PRD to Ralph format. Created .ralph/PROMPT.md, .ralph/@fix_plan.md, and .ralph/specs/requirements.md",
|
||||
"sessionId": "session-prd-convert-123",
|
||||
"metadata": {
|
||||
"files_changed": 3,
|
||||
"has_errors": false,
|
||||
"completion_status": "complete",
|
||||
"files_created": ["PROMPT.md", "@fix_plan.md", "specs/requirements.md"]
|
||||
"files_created": [".ralph/PROMPT.md", ".ralph/@fix_plan.md", ".ralph/specs/requirements.md"]
|
||||
}
|
||||
}
|
||||
JSON_OUTPUT
|
||||
|
|
@ -775,11 +780,14 @@ MOCK_CLAUDE_JSON_EOF
|
|||
create_mock_claude_json_partial() {
|
||||
cat > "$MOCK_BIN_DIR/claude" << 'MOCK_CLAUDE_PARTIAL_EOF'
|
||||
#!/bin/bash
|
||||
# Mock Claude Code CLI that outputs JSON but only creates some files
|
||||
# Mock Claude Code CLI that outputs JSON but only creates some files in .ralph/
|
||||
cat > /dev/null
|
||||
|
||||
# Ensure .ralph directory exists
|
||||
mkdir -p .ralph
|
||||
|
||||
# Only create PROMPT.md (missing @fix_plan.md and specs/requirements.md)
|
||||
cat > PROMPT.md << 'EOF'
|
||||
cat > .ralph/PROMPT.md << 'EOF'
|
||||
# Ralph Development Instructions
|
||||
|
||||
## Context
|
||||
|
|
@ -795,8 +803,8 @@ cat << 'JSON_OUTPUT'
|
|||
"files_changed": 1,
|
||||
"has_errors": true,
|
||||
"completion_status": "partial",
|
||||
"files_created": ["PROMPT.md"],
|
||||
"missing_files": ["@fix_plan.md", "specs/requirements.md"]
|
||||
"files_created": [".ralph/PROMPT.md"],
|
||||
"missing_files": [".ralph/@fix_plan.md", ".ralph/specs/requirements.md"]
|
||||
}
|
||||
}
|
||||
JSON_OUTPUT
|
||||
|
|
@ -837,18 +845,21 @@ MOCK_CLAUDE_JSON_ERROR_EOF
|
|||
create_mock_claude_text_output() {
|
||||
cat > "$MOCK_BIN_DIR/claude" << 'MOCK_CLAUDE_TEXT_EOF'
|
||||
#!/bin/bash
|
||||
# Mock Claude Code CLI that outputs text (older CLI version)
|
||||
# Mock Claude Code CLI that outputs text (older CLI version) - files in .ralph/
|
||||
cat > /dev/null
|
||||
|
||||
# Create files
|
||||
cat > PROMPT.md << 'EOF'
|
||||
# Ensure .ralph directory exists
|
||||
mkdir -p .ralph/specs
|
||||
|
||||
# Create files in .ralph/
|
||||
cat > .ralph/PROMPT.md << 'EOF'
|
||||
# Ralph Development Instructions
|
||||
|
||||
## Context
|
||||
You are Ralph, an autonomous AI development agent.
|
||||
EOF
|
||||
|
||||
cat > "@fix_plan.md" << 'EOF'
|
||||
cat > ".ralph/@fix_plan.md" << 'EOF'
|
||||
# Ralph Fix Plan
|
||||
|
||||
## High Priority
|
||||
|
|
@ -858,8 +869,7 @@ cat > "@fix_plan.md" << 'EOF'
|
|||
- [x] Project initialization
|
||||
EOF
|
||||
|
||||
mkdir -p specs
|
||||
cat > specs/requirements.md << 'EOF'
|
||||
cat > .ralph/specs/requirements.md << 'EOF'
|
||||
# Technical Specifications
|
||||
|
||||
## Overview
|
||||
|
|
@ -868,7 +878,7 @@ EOF
|
|||
|
||||
# Output plain text (no JSON)
|
||||
echo "Mock: Claude Code conversion completed successfully"
|
||||
echo "Created: PROMPT.md, @fix_plan.md, specs/requirements.md"
|
||||
echo "Created: .ralph/PROMPT.md, .ralph/@fix_plan.md, .ralph/specs/requirements.md"
|
||||
exit 0
|
||||
MOCK_CLAUDE_TEXT_EOF
|
||||
chmod +x "$MOCK_BIN_DIR/claude"
|
||||
|
|
@ -883,10 +893,10 @@ MOCK_CLAUDE_TEXT_EOF
|
|||
|
||||
assert_success
|
||||
|
||||
# All files should be created
|
||||
assert_file_exists "json-test/PROMPT.md"
|
||||
assert_file_exists "json-test/@fix_plan.md"
|
||||
assert_file_exists "json-test/specs/requirements.md"
|
||||
# All files should be created in .ralph/ subfolder
|
||||
assert_file_exists "json-test/.ralph/PROMPT.md"
|
||||
assert_file_exists "json-test/.ralph/@fix_plan.md"
|
||||
assert_file_exists "json-test/.ralph/specs/requirements.md"
|
||||
}
|
||||
|
||||
# Test 24: ralph-import handles JSON partial success response
|
||||
|
|
@ -899,8 +909,8 @@ MOCK_CLAUDE_TEXT_EOF
|
|||
# Should succeed but with warnings
|
||||
assert_success
|
||||
|
||||
# PROMPT.md should exist
|
||||
assert_file_exists "partial-test/PROMPT.md"
|
||||
# PROMPT.md should exist in .ralph/ subfolder
|
||||
assert_file_exists "partial-test/.ralph/PROMPT.md"
|
||||
|
||||
# Warning should mention missing files
|
||||
[[ "$output" == *"WARN"* ]] || [[ "$output" == *"not created"* ]] || [[ "$output" == *"missing"* ]]
|
||||
|
|
@ -929,10 +939,10 @@ MOCK_CLAUDE_TEXT_EOF
|
|||
|
||||
assert_success
|
||||
|
||||
# All files should be created
|
||||
assert_file_exists "text-test/PROMPT.md"
|
||||
assert_file_exists "text-test/@fix_plan.md"
|
||||
assert_file_exists "text-test/specs/requirements.md"
|
||||
# All files should be created in .ralph/ subfolder
|
||||
assert_file_exists "text-test/.ralph/PROMPT.md"
|
||||
assert_file_exists "text-test/.ralph/@fix_plan.md"
|
||||
assert_file_exists "text-test/.ralph/specs/requirements.md"
|
||||
}
|
||||
|
||||
# Test 27: ralph-import cleans up JSON output file after processing
|
||||
|
|
@ -999,19 +1009,21 @@ MOCK_CLAUDE_TEXT_EOF
|
|||
# Capture invocation arguments for testing
|
||||
echo "INVOCATION_ARGS: $*" >> /tmp/claude_invocation.log
|
||||
|
||||
# Create expected files
|
||||
cat > PROMPT.md << 'EOF'
|
||||
# Ensure .ralph directory exists
|
||||
mkdir -p .ralph/specs
|
||||
|
||||
# Create expected files in .ralph/
|
||||
cat > .ralph/PROMPT.md << 'EOF'
|
||||
# Ralph Development Instructions
|
||||
EOF
|
||||
|
||||
cat > "@fix_plan.md" << 'EOF'
|
||||
cat > ".ralph/@fix_plan.md" << 'EOF'
|
||||
# Ralph Fix Plan
|
||||
## High Priority
|
||||
- [ ] Task 1
|
||||
EOF
|
||||
|
||||
mkdir -p specs
|
||||
cat > specs/requirements.md << 'EOF'
|
||||
cat > .ralph/specs/requirements.md << 'EOF'
|
||||
# Technical Specifications
|
||||
EOF
|
||||
|
||||
|
|
@ -1059,19 +1071,21 @@ CAPTURE_ARGS_EOF
|
|||
#!/bin/bash
|
||||
cat > /dev/null
|
||||
|
||||
# Create files
|
||||
cat > PROMPT.md << 'EOF'
|
||||
# Ensure .ralph directory exists
|
||||
mkdir -p .ralph/specs
|
||||
|
||||
# Create files in .ralph/
|
||||
cat > .ralph/PROMPT.md << 'EOF'
|
||||
# Ralph Development Instructions
|
||||
EOF
|
||||
|
||||
cat > "@fix_plan.md" << 'EOF'
|
||||
cat > ".ralph/@fix_plan.md" << 'EOF'
|
||||
# Ralph Fix Plan
|
||||
## High Priority
|
||||
- [ ] Task 1
|
||||
EOF
|
||||
|
||||
mkdir -p specs
|
||||
cat > specs/requirements.md << 'EOF'
|
||||
cat > .ralph/specs/requirements.md << 'EOF'
|
||||
# Technical Specifications
|
||||
EOF
|
||||
|
||||
|
|
@ -1089,8 +1103,8 @@ MALFORMED_JSON_EOF
|
|||
# Should still succeed (fallback to text parsing)
|
||||
assert_success
|
||||
|
||||
# Files should exist
|
||||
assert_file_exists "malformed-test/PROMPT.md"
|
||||
# Files should exist in .ralph/ subfolder
|
||||
assert_file_exists "malformed-test/.ralph/PROMPT.md"
|
||||
}
|
||||
|
||||
# Test 33: ralph-import extracts error details from JSON error response
|
||||
|
|
|
|||
|
|
@ -106,77 +106,95 @@ teardown() {
|
|||
}
|
||||
|
||||
# =============================================================================
|
||||
# Test: Subdirectory Structure
|
||||
# Test: Subdirectory Structure (.ralph/ subfolder)
|
||||
# =============================================================================
|
||||
|
||||
@test "setup.sh creates all required subdirectories" {
|
||||
@test "setup.sh creates .ralph subdirectory for Ralph-specific files" {
|
||||
run bash "$SETUP_SCRIPT" test-project
|
||||
|
||||
assert_success
|
||||
assert_dir_exists "test-project/specs"
|
||||
assert_dir_exists "test-project/specs/stdlib"
|
||||
assert_dir_exists "test-project/.ralph"
|
||||
}
|
||||
|
||||
@test "setup.sh creates all required subdirectories in .ralph/" {
|
||||
run bash "$SETUP_SCRIPT" test-project
|
||||
|
||||
assert_success
|
||||
# Ralph-specific directories go inside .ralph/
|
||||
assert_dir_exists "test-project/.ralph/specs"
|
||||
assert_dir_exists "test-project/.ralph/specs/stdlib"
|
||||
assert_dir_exists "test-project/.ralph/examples"
|
||||
assert_dir_exists "test-project/.ralph/logs"
|
||||
assert_dir_exists "test-project/.ralph/docs"
|
||||
assert_dir_exists "test-project/.ralph/docs/generated"
|
||||
# src/ stays at root per maintainer decision
|
||||
assert_dir_exists "test-project/src"
|
||||
assert_dir_exists "test-project/examples"
|
||||
assert_dir_exists "test-project/logs"
|
||||
assert_dir_exists "test-project/docs"
|
||||
assert_dir_exists "test-project/docs/generated"
|
||||
}
|
||||
|
||||
@test "setup.sh creates nested docs/generated directory" {
|
||||
@test "setup.sh keeps src directory at project root (not in .ralph/)" {
|
||||
run bash "$SETUP_SCRIPT" test-project
|
||||
|
||||
assert_success
|
||||
# Verify the nested structure exists
|
||||
[[ -d "test-project/docs/generated" ]]
|
||||
# src should be at root, NOT inside .ralph
|
||||
assert_dir_exists "test-project/src"
|
||||
[[ ! -d "test-project/.ralph/src" ]]
|
||||
}
|
||||
|
||||
@test "setup.sh creates nested specs/stdlib directory" {
|
||||
@test "setup.sh creates nested docs/generated directory in .ralph/" {
|
||||
run bash "$SETUP_SCRIPT" test-project
|
||||
|
||||
assert_success
|
||||
[[ -d "test-project/specs/stdlib" ]]
|
||||
# Verify the nested structure exists inside .ralph
|
||||
[[ -d "test-project/.ralph/docs/generated" ]]
|
||||
}
|
||||
|
||||
@test "setup.sh creates nested specs/stdlib directory in .ralph/" {
|
||||
run bash "$SETUP_SCRIPT" test-project
|
||||
|
||||
assert_success
|
||||
[[ -d "test-project/.ralph/specs/stdlib" ]]
|
||||
}
|
||||
|
||||
# =============================================================================
|
||||
# Test: Template Copying
|
||||
# Test: Template Copying (to .ralph/ subfolder)
|
||||
# =============================================================================
|
||||
|
||||
@test "setup.sh copies PROMPT.md template" {
|
||||
@test "setup.sh copies PROMPT.md template to .ralph/" {
|
||||
run bash "$SETUP_SCRIPT" test-project
|
||||
|
||||
assert_success
|
||||
assert_file_exists "test-project/PROMPT.md"
|
||||
assert_file_exists "test-project/.ralph/PROMPT.md"
|
||||
|
||||
# Verify content matches source
|
||||
diff templates/PROMPT.md test-project/PROMPT.md
|
||||
diff templates/PROMPT.md test-project/.ralph/PROMPT.md
|
||||
}
|
||||
|
||||
@test "setup.sh copies fix_plan.md as @fix_plan.md" {
|
||||
@test "setup.sh copies fix_plan.md as @fix_plan.md to .ralph/" {
|
||||
run bash "$SETUP_SCRIPT" test-project
|
||||
|
||||
assert_success
|
||||
assert_file_exists "test-project/@fix_plan.md"
|
||||
assert_file_exists "test-project/.ralph/@fix_plan.md"
|
||||
|
||||
# Verify content matches source
|
||||
diff templates/fix_plan.md "test-project/@fix_plan.md"
|
||||
diff templates/fix_plan.md "test-project/.ralph/@fix_plan.md"
|
||||
}
|
||||
|
||||
@test "setup.sh copies AGENT.md as @AGENT.md" {
|
||||
@test "setup.sh copies AGENT.md as @AGENT.md to .ralph/" {
|
||||
run bash "$SETUP_SCRIPT" test-project
|
||||
|
||||
assert_success
|
||||
assert_file_exists "test-project/@AGENT.md"
|
||||
assert_file_exists "test-project/.ralph/@AGENT.md"
|
||||
|
||||
# Verify content matches source
|
||||
diff templates/AGENT.md "test-project/@AGENT.md"
|
||||
diff templates/AGENT.md "test-project/.ralph/@AGENT.md"
|
||||
}
|
||||
|
||||
@test "setup.sh copies specs templates if they exist" {
|
||||
@test "setup.sh copies specs templates to .ralph/specs/" {
|
||||
run bash "$SETUP_SCRIPT" test-project
|
||||
|
||||
assert_success
|
||||
# Verify spec file was copied
|
||||
assert_file_exists "test-project/specs/sample_spec.md"
|
||||
# Verify spec file was copied to .ralph/specs/
|
||||
assert_file_exists "test-project/.ralph/specs/sample_spec.md"
|
||||
}
|
||||
|
||||
@test "setup.sh handles empty specs directory gracefully" {
|
||||
|
|
@ -187,7 +205,7 @@ teardown() {
|
|||
|
||||
# Should not fail (|| true in script handles this)
|
||||
assert_success
|
||||
assert_dir_exists "test-project/specs"
|
||||
assert_dir_exists "test-project/.ralph/specs"
|
||||
}
|
||||
|
||||
@test "setup.sh handles missing specs directory gracefully" {
|
||||
|
|
@ -198,7 +216,7 @@ teardown() {
|
|||
|
||||
# Should not fail due to || true in script
|
||||
assert_success
|
||||
assert_dir_exists "test-project/specs"
|
||||
assert_dir_exists "test-project/.ralph/specs"
|
||||
}
|
||||
|
||||
# =============================================================================
|
||||
|
|
@ -298,22 +316,24 @@ teardown() {
|
|||
grep -q "# custom-project-name" custom-project-name/README.md
|
||||
}
|
||||
|
||||
@test "setup.sh custom project has all subdirectories" {
|
||||
@test "setup.sh custom project has all subdirectories in .ralph/" {
|
||||
bash "$SETUP_SCRIPT" my-custom-app
|
||||
|
||||
assert_dir_exists "my-custom-app/specs/stdlib"
|
||||
# Ralph-specific dirs in .ralph/
|
||||
assert_dir_exists "my-custom-app/.ralph/specs/stdlib"
|
||||
assert_dir_exists "my-custom-app/.ralph/examples"
|
||||
assert_dir_exists "my-custom-app/.ralph/logs"
|
||||
assert_dir_exists "my-custom-app/.ralph/docs/generated"
|
||||
# src stays at root
|
||||
assert_dir_exists "my-custom-app/src"
|
||||
assert_dir_exists "my-custom-app/examples"
|
||||
assert_dir_exists "my-custom-app/logs"
|
||||
assert_dir_exists "my-custom-app/docs/generated"
|
||||
}
|
||||
|
||||
@test "setup.sh custom project has all template files" {
|
||||
@test "setup.sh custom project has all template files in .ralph/" {
|
||||
bash "$SETUP_SCRIPT" my-custom-app
|
||||
|
||||
assert_file_exists "my-custom-app/PROMPT.md"
|
||||
assert_file_exists "my-custom-app/@fix_plan.md"
|
||||
assert_file_exists "my-custom-app/@AGENT.md"
|
||||
assert_file_exists "my-custom-app/.ralph/PROMPT.md"
|
||||
assert_file_exists "my-custom-app/.ralph/@fix_plan.md"
|
||||
assert_file_exists "my-custom-app/.ralph/@AGENT.md"
|
||||
}
|
||||
|
||||
# =============================================================================
|
||||
|
|
@ -334,20 +354,25 @@ teardown() {
|
|||
grep -q "# my-project" my-project/README.md
|
||||
}
|
||||
|
||||
@test "setup.sh default project has all required structure" {
|
||||
@test "setup.sh default project has all required structure in .ralph/" {
|
||||
bash "$SETUP_SCRIPT"
|
||||
|
||||
# Verify all directories
|
||||
assert_dir_exists "my-project/specs/stdlib"
|
||||
assert_dir_exists "my-project/src"
|
||||
assert_dir_exists "my-project/examples"
|
||||
assert_dir_exists "my-project/logs"
|
||||
assert_dir_exists "my-project/docs/generated"
|
||||
# Verify .ralph directory exists
|
||||
assert_dir_exists "my-project/.ralph"
|
||||
|
||||
# Verify all files
|
||||
assert_file_exists "my-project/PROMPT.md"
|
||||
assert_file_exists "my-project/@fix_plan.md"
|
||||
assert_file_exists "my-project/@AGENT.md"
|
||||
# Verify all directories in .ralph/
|
||||
assert_dir_exists "my-project/.ralph/specs/stdlib"
|
||||
assert_dir_exists "my-project/.ralph/examples"
|
||||
assert_dir_exists "my-project/.ralph/logs"
|
||||
assert_dir_exists "my-project/.ralph/docs/generated"
|
||||
# src stays at root
|
||||
assert_dir_exists "my-project/src"
|
||||
|
||||
# Verify all files in .ralph/
|
||||
assert_file_exists "my-project/.ralph/PROMPT.md"
|
||||
assert_file_exists "my-project/.ralph/@fix_plan.md"
|
||||
assert_file_exists "my-project/.ralph/@AGENT.md"
|
||||
# README stays at root
|
||||
assert_file_exists "my-project/README.md"
|
||||
}
|
||||
|
||||
|
|
@ -405,12 +430,12 @@ teardown() {
|
|||
[[ "$output" == *"Project test-project created"* ]]
|
||||
}
|
||||
|
||||
@test "setup.sh outputs next steps guidance" {
|
||||
@test "setup.sh outputs next steps guidance with .ralph paths" {
|
||||
run bash "$SETUP_SCRIPT" test-project
|
||||
|
||||
assert_success
|
||||
[[ "$output" == *"Next steps:"* ]]
|
||||
[[ "$output" == *"PROMPT.md"* ]]
|
||||
[[ "$output" == *".ralph/PROMPT.md"* ]]
|
||||
}
|
||||
|
||||
# =============================================================================
|
||||
|
|
@ -418,11 +443,18 @@ teardown() {
|
|||
# =============================================================================
|
||||
|
||||
@test "setup.sh fails if templates directory missing" {
|
||||
# Remove templates directory
|
||||
# Remove local templates directory
|
||||
rm -rf templates
|
||||
|
||||
# Also hide global templates by overriding HOME to a temp location
|
||||
local original_home="$HOME"
|
||||
export HOME="$(mktemp -d)"
|
||||
|
||||
run bash "$SETUP_SCRIPT" test-project
|
||||
|
||||
# Restore HOME
|
||||
export HOME="$original_home"
|
||||
|
||||
assert_failure
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue