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
|
|
@ -23,7 +23,8 @@
|
|||
"Bash(timeout 30 bats:*)",
|
||||
"mcp__sequential-thinking__sequentialthinking",
|
||||
"Bash(git fetch:*)",
|
||||
"Bash(npm run test:unit:*)"
|
||||
"Bash(npm run test:unit:*)",
|
||||
"Bash(node_modules/.bin/bats:*)"
|
||||
],
|
||||
"deny": [],
|
||||
"ask": []
|
||||
|
|
|
|||
41
.gitignore
vendored
41
.gitignore
vendored
|
|
@ -1,21 +1,26 @@
|
|||
# Ralph generated files
|
||||
.call_count
|
||||
.last_reset
|
||||
.exit_signals
|
||||
status.json
|
||||
.ralph_session
|
||||
.ralph_session_history
|
||||
.claude_session_id
|
||||
# Ralph generated files (inside .ralph/ subfolder)
|
||||
.ralph/.call_count
|
||||
.ralph/.last_reset
|
||||
.ralph/.exit_signals
|
||||
.ralph/status.json
|
||||
.ralph/.ralph_session
|
||||
.ralph/.ralph_session_history
|
||||
.ralph/.claude_session_id
|
||||
.ralph/.response_analysis
|
||||
.ralph/.circuit_breaker_state
|
||||
.ralph/.circuit_breaker_history
|
||||
.ralph/.json_parse_result
|
||||
.ralph/.last_output_length
|
||||
|
||||
# Logs
|
||||
logs/*
|
||||
!logs/.gitkeep
|
||||
# Ralph logs and generated docs
|
||||
.ralph/logs/*
|
||||
!.ralph/logs/.gitkeep
|
||||
.ralph/docs/generated/*
|
||||
!.ralph/docs/generated/.gitkeep
|
||||
|
||||
# General logs
|
||||
*.log
|
||||
|
||||
# Generated documentation
|
||||
docs/generated/*
|
||||
!docs/generated/.gitkeep
|
||||
|
||||
# OS files
|
||||
.DS_Store
|
||||
Thumbs.db
|
||||
|
|
@ -39,3 +44,9 @@ target/
|
|||
.idea/
|
||||
*.swp
|
||||
*.swo
|
||||
|
||||
# Claude Code local settings
|
||||
.claude/settings.local.json
|
||||
|
||||
# Ralph backup directories (created by migration)
|
||||
.ralph_backup_*
|
||||
|
|
|
|||
92
CLAUDE.md
92
CLAUDE.md
|
|
@ -6,7 +6,7 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co
|
|||
|
||||
This is the Ralph for Claude Code repository - an autonomous AI development loop system that enables continuous development cycles with intelligent exit detection and rate limiting.
|
||||
|
||||
**Version**: v0.9.8 | **Tests**: 276 passing (100% pass rate) | **CI/CD**: GitHub Actions
|
||||
**Version**: v0.10.0 | **Tests**: 310 passing (100% pass rate) | **CI/CD**: GitHub Actions
|
||||
|
||||
## Core Architecture
|
||||
|
||||
|
|
@ -39,9 +39,9 @@ The system uses a modular architecture with reusable components in the `lib/` di
|
|||
- Supports both flat JSON format and Claude CLI format (`result`, `sessionId`, `metadata`)
|
||||
- Extracts structured fields: status, exit_signal, work_type, files_modified
|
||||
- **Session management**: `store_session_id()`, `get_last_session_id()`, `should_resume_session()`
|
||||
- Automatic session persistence to `.claude_session_id` file with 24-hour expiration
|
||||
- Automatic session persistence to `.ralph/.claude_session_id` file with 24-hour expiration
|
||||
- Session lifecycle: `get_session_id()`, `reset_session()`, `log_session_transition()`, `init_session_tracking()`
|
||||
- Session history tracked in `.ralph_session_history` (last 50 transitions)
|
||||
- Session history tracked in `.ralph/.ralph_session_history` (last 50 transitions)
|
||||
- Session auto-reset on: circuit breaker open, manual interrupt, project completion
|
||||
- Detects test-only loops and stuck error patterns
|
||||
- Two-stage error filtering to eliminate false positives
|
||||
|
|
@ -77,6 +77,13 @@ ralph-setup my-project-name
|
|||
cd my-project-name
|
||||
```
|
||||
|
||||
### Migrating Existing Projects
|
||||
```bash
|
||||
# Migrate from flat structure to .ralph/ subfolder (v0.10.0+)
|
||||
cd existing-project
|
||||
ralph-migrate
|
||||
```
|
||||
|
||||
### Running the Ralph Loop
|
||||
```bash
|
||||
# Start with integrated tmux monitoring (recommended)
|
||||
|
|
@ -129,13 +136,13 @@ bats tests/unit/test_cli_modern.bats
|
|||
|
||||
## Ralph Loop Configuration
|
||||
|
||||
The loop is controlled by several key files and environment variables:
|
||||
The loop is controlled by several key files and environment variables within the `.ralph/` subfolder:
|
||||
|
||||
- **PROMPT.md** - Main prompt file that drives each loop iteration
|
||||
- **@fix_plan.md** - Prioritized task list that Ralph follows
|
||||
- **@AGENT.md** - Build and run instructions maintained by Ralph
|
||||
- **status.json** - Real-time status tracking (JSON format)
|
||||
- **logs/** - Execution logs for each loop iteration
|
||||
- **.ralph/PROMPT.md** - Main prompt file that drives each loop iteration
|
||||
- **.ralph/@fix_plan.md** - Prioritized task list that Ralph follows
|
||||
- **.ralph/@AGENT.md** - Build and run instructions maintained by Ralph
|
||||
- **.ralph/status.json** - Real-time status tracking (JSON format)
|
||||
- **.ralph/logs/** - Execution logs for each loop iteration
|
||||
|
||||
### Rate Limiting
|
||||
- Default: 100 API calls per hour (configurable via `--calls` flag)
|
||||
|
|
@ -167,7 +174,7 @@ Each loop iteration injects context via `build_loop_context()`:
|
|||
- Previous loop work summary
|
||||
|
||||
**Session Continuity:**
|
||||
- Sessions are preserved in `.claude_session_id`
|
||||
- Sessions are preserved in `.ralph/.claude_session_id`
|
||||
- Use `--continue` flag to maintain context across loops
|
||||
- Disable with `--no-continue` for isolated iterations
|
||||
|
||||
|
|
@ -178,12 +185,12 @@ The loop uses a dual-condition check to prevent premature exits during productiv
|
|||
1. `recent_completion_indicators >= 2` (heuristic-based detection from natural language patterns)
|
||||
2. Claude's explicit `EXIT_SIGNAL: true` in the RALPH_STATUS block
|
||||
|
||||
The `EXIT_SIGNAL` value is read from `.response_analysis` (at `.analysis.exit_signal`) which is populated by `response_analyzer.sh` from Claude's RALPH_STATUS output block.
|
||||
The `EXIT_SIGNAL` value is read from `.ralph/.response_analysis` (at `.analysis.exit_signal`) which is populated by `response_analyzer.sh` from Claude's RALPH_STATUS output block.
|
||||
|
||||
**Other exit conditions (checked before completion indicators):**
|
||||
- Multiple consecutive "done" signals from Claude Code (`done_signals >= 2`)
|
||||
- Too many test-only loops indicating feature completeness (`test_loops >= 3`)
|
||||
- All items in @fix_plan.md marked as completed
|
||||
- All items in .ralph/@fix_plan.md marked as completed
|
||||
|
||||
**Example behavior when EXIT_SIGNAL is false:**
|
||||
```
|
||||
|
|
@ -223,19 +230,22 @@ Bash code coverage measurement with kcov has fundamental limitations when tracin
|
|||
|
||||
## Project Structure for Ralph-Managed Projects
|
||||
|
||||
Each project created with `./setup.sh` follows this structure:
|
||||
Each project created with `./setup.sh` follows this structure with a `.ralph/` subfolder:
|
||||
```
|
||||
project-name/
|
||||
├── PROMPT.md # Main development instructions
|
||||
├── @fix_plan.md # Prioritized TODO list
|
||||
├── @AGENT.md # Build/run instructions
|
||||
├── specs/ # Project specifications
|
||||
├── src/ # Source code
|
||||
├── examples/ # Usage examples
|
||||
├── logs/ # Loop execution logs
|
||||
└── docs/generated/ # Auto-generated documentation
|
||||
├── .ralph/ # Ralph configuration and state (hidden folder)
|
||||
│ ├── PROMPT.md # Main development instructions
|
||||
│ ├── @fix_plan.md # Prioritized TODO list
|
||||
│ ├── @AGENT.md # Build/run instructions
|
||||
│ ├── specs/ # Project specifications
|
||||
│ ├── examples/ # Usage examples
|
||||
│ ├── logs/ # Loop execution logs
|
||||
│ └── docs/generated/ # Auto-generated documentation
|
||||
└── src/ # Source code (at project root)
|
||||
```
|
||||
|
||||
> **Migration**: Existing projects can be migrated with `ralph-migrate`.
|
||||
|
||||
## Template System
|
||||
|
||||
Templates in `templates/` provide starting points for new projects:
|
||||
|
|
@ -245,25 +255,26 @@ Templates in `templates/` provide starting points for new projects:
|
|||
|
||||
## File Naming Conventions
|
||||
|
||||
- Files prefixed with `@` (e.g., `@fix_plan.md`) are Ralph-specific control files
|
||||
- Hidden files (e.g., `.call_count`, `.exit_signals`) track loop state
|
||||
- `logs/` contains timestamped execution logs
|
||||
- `docs/generated/` for Ralph-created documentation
|
||||
- `docs/code-review/` for code review reports
|
||||
- Files prefixed with `@` (e.g., `.ralph/@fix_plan.md`) are Ralph-specific control files
|
||||
- Hidden files within `.ralph/` (e.g., `.ralph/.call_count`, `.ralph/.exit_signals`) track loop state
|
||||
- `.ralph/logs/` contains timestamped execution logs
|
||||
- `.ralph/docs/generated/` for Ralph-created documentation
|
||||
- `docs/code-review/` for code review reports (at project root)
|
||||
|
||||
## Global Installation
|
||||
|
||||
Ralph installs to:
|
||||
- **Commands**: `~/.local/bin/` (ralph, ralph-monitor, ralph-setup, ralph-import)
|
||||
- **Commands**: `~/.local/bin/` (ralph, ralph-monitor, ralph-setup, ralph-import, ralph-migrate)
|
||||
- **Templates**: `~/.ralph/templates/`
|
||||
- **Scripts**: `~/.ralph/` (ralph_loop.sh, ralph_monitor.sh, setup.sh, ralph_import.sh)
|
||||
- **Libraries**: `~/.ralph/lib/` (circuit_breaker.sh, response_analyzer.sh, date_utils.sh)
|
||||
- **Scripts**: `~/.ralph/` (ralph_loop.sh, ralph_monitor.sh, setup.sh, ralph_import.sh, migrate_to_ralph_folder.sh)
|
||||
- **Libraries**: `~/.ralph/lib/` (circuit_breaker.sh, response_analyzer.sh, date_utils.sh, timeout_utils.sh)
|
||||
|
||||
After installation, the following global commands are available:
|
||||
- `ralph` - Start the autonomous development loop
|
||||
- `ralph-monitor` - Launch the monitoring dashboard
|
||||
- `ralph-setup` - Create a new Ralph-managed project
|
||||
- `ralph-import` - Import PRD/specification documents to Ralph format
|
||||
- `ralph-migrate` - Migrate existing projects from flat structure to `.ralph/` subfolder
|
||||
|
||||
## Integration Points
|
||||
|
||||
|
|
@ -283,7 +294,7 @@ Ralph uses multiple mechanisms to detect when to exit:
|
|||
- `MAX_CONSECUTIVE_TEST_LOOPS=3` - Exit if too many test-only iterations
|
||||
- `MAX_CONSECUTIVE_DONE_SIGNALS=2` - Exit on repeated completion signals
|
||||
- `TEST_PERCENTAGE_THRESHOLD=30%` - Flag if testing dominates recent loops
|
||||
- Completion detection via @fix_plan.md checklist items
|
||||
- Completion detection via .ralph/@fix_plan.md checklist items
|
||||
|
||||
### Completion Indicators with EXIT_SIGNAL Gate
|
||||
|
||||
|
|
@ -300,8 +311,8 @@ The `completion_indicators` exit condition requires dual verification:
|
|||
**Implementation** (`ralph_loop.sh:312-327`):
|
||||
```bash
|
||||
local claude_exit_signal="false"
|
||||
if [[ -f ".response_analysis" ]]; then
|
||||
claude_exit_signal=$(jq -r '.analysis.exit_signal // false' ".response_analysis" 2>/dev/null || echo "false")
|
||||
if [[ -f "$RALPH_DIR/.response_analysis" ]]; then
|
||||
claude_exit_signal=$(jq -r '.analysis.exit_signal // false' "$RALPH_DIR/.response_analysis" 2>/dev/null || echo "false")
|
||||
fi
|
||||
|
||||
if [[ $recent_completion_indicators -ge 2 ]] && [[ "$claude_exit_signal" == "true" ]]; then
|
||||
|
|
@ -370,6 +381,15 @@ bats tests/unit/test_cli_parsing.bats
|
|||
|
||||
## Recent Improvements
|
||||
|
||||
### .ralph/ Subfolder Structure (v0.10.0) - BREAKING CHANGE
|
||||
- **Breaking**: Moved all Ralph-specific files to `.ralph/` subfolder
|
||||
- Project root stays clean: only `src/`, `README.md`, and user files remain
|
||||
- Added `ralph-migrate` command for upgrading existing projects
|
||||
- Migration script with fail-safe copy pattern (`cp -a source/. dest/`)
|
||||
- Auto-detection of old structure with upgrade guidance
|
||||
- Updated all configuration variables to use `$RALPH_DIR` prefix
|
||||
- Test count: 310 (up from 308)
|
||||
|
||||
### Modern CLI for PRD Import (v0.9.8)
|
||||
- Modernized `ralph_import.sh` to use Claude Code CLI JSON output format
|
||||
- Added `--output-format json` flag for structured responses
|
||||
|
|
@ -566,9 +586,9 @@ Before moving to the next feature, ALL changes must be:
|
|||
- Create pull requests for all significant changes
|
||||
|
||||
4. **Ralph Integration**:
|
||||
- Update @fix_plan.md with new tasks before starting work
|
||||
- Mark items complete in @fix_plan.md upon completion
|
||||
- Update PROMPT.md if Ralph's behavior needs modification
|
||||
- Update .ralph/@fix_plan.md with new tasks before starting work
|
||||
- Mark items complete in .ralph/@fix_plan.md upon completion
|
||||
- Update .ralph/PROMPT.md if Ralph's behavior needs modification
|
||||
- Test Ralph loop with new features before completion
|
||||
|
||||
### Documentation Requirements
|
||||
|
|
@ -613,7 +633,7 @@ Before marking ANY feature as complete, verify:
|
|||
- [ ] All changes committed with conventional commit messages
|
||||
- [ ] All commits pushed to remote repository
|
||||
- [ ] CI/CD pipeline passes
|
||||
- [ ] @fix_plan.md task marked as complete
|
||||
- [ ] .ralph/@fix_plan.md task marked as complete
|
||||
- [ ] Implementation documentation updated
|
||||
- [ ] Inline code comments updated or added
|
||||
- [ ] CLAUDE.md updated (if new patterns introduced)
|
||||
|
|
|
|||
79
README.md
79
README.md
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
[](https://github.com/frankbria/ralph-claude-code/actions/workflows/test.yml)
|
||||
[](LICENSE)
|
||||

|
||||

|
||||

|
||||
[](https://github.com/frankbria/ralph-claude-code/issues)
|
||||
[](https://github.com/hesreallyhim/awesome-claude-code)
|
||||
|
|
@ -16,7 +16,7 @@ Ralph is an implementation of the Geoffrey Huntley's technique for Claude Code t
|
|||
|
||||
## Project Status
|
||||
|
||||
**Version**: v0.9.9 - Active Development
|
||||
**Version**: v0.10.0 - Active Development
|
||||
**Core Features**: Working and tested
|
||||
**Test Coverage**: 308 tests, 100% pass rate
|
||||
|
||||
|
|
@ -40,6 +40,15 @@ Ralph is an implementation of the Geoffrey Huntley's technique for Claude Code t
|
|||
|
||||
### Recent Improvements
|
||||
|
||||
**v0.10.0 - .ralph/ Subfolder Structure (BREAKING CHANGE)**
|
||||
- **Breaking**: Moved all Ralph-specific files to `.ralph/` subfolder
|
||||
- Project root stays clean: only `src/`, `README.md`, and user files remain
|
||||
- Added `ralph-migrate` command for upgrading existing projects
|
||||
- Migration script creates backup before moving files
|
||||
- Fail-safe migration: preserves dotfiles with `cp -a source/. dest/` pattern
|
||||
- Auto-detection of old structure with helpful upgrade guidance
|
||||
- Updated all tests for new structure (310 tests)
|
||||
|
||||
**v0.9.9 - EXIT_SIGNAL Gate & Uninstall Script**
|
||||
- Fixed premature exit bug: completion indicators now require Claude's explicit `EXIT_SIGNAL: true`
|
||||
- Added dual-condition check preventing exits when Claude reports work in progress
|
||||
|
|
@ -152,7 +161,7 @@ cd ralph-claude-code
|
|||
./install.sh
|
||||
```
|
||||
|
||||
This adds `ralph`, `ralph-monitor`, and `ralph-setup` commands to your PATH.
|
||||
This adds `ralph`, `ralph-monitor`, `ralph-setup`, `ralph-import`, and `ralph-migrate` commands to your PATH.
|
||||
|
||||
> **Note**: You only need to do this once per system. After installation, you can delete the cloned repository if desired.
|
||||
|
||||
|
|
@ -167,9 +176,9 @@ ralph-import my-requirements.md my-project
|
|||
cd my-project
|
||||
|
||||
# Review and adjust the generated files:
|
||||
# - PROMPT.md (Ralph instructions)
|
||||
# - @fix_plan.md (task priorities)
|
||||
# - specs/requirements.md (technical specs)
|
||||
# - .ralph/PROMPT.md (Ralph instructions)
|
||||
# - .ralph/@fix_plan.md (task priorities)
|
||||
# - .ralph/specs/requirements.md (technical specs)
|
||||
|
||||
# Start autonomous development
|
||||
ralph --monitor
|
||||
|
|
@ -182,9 +191,9 @@ ralph-setup my-awesome-project
|
|||
cd my-awesome-project
|
||||
|
||||
# Configure your project requirements manually
|
||||
# Edit PROMPT.md with your project goals
|
||||
# Edit specs/ with detailed specifications
|
||||
# Edit @fix_plan.md with initial priorities
|
||||
# Edit .ralph/PROMPT.md with your project goals
|
||||
# Edit .ralph/specs/ with detailed specifications
|
||||
# Edit .ralph/@fix_plan.md with initial priorities
|
||||
|
||||
# Start autonomous development
|
||||
ralph --monitor
|
||||
|
|
@ -247,7 +256,7 @@ Loop 8: Claude outputs "All tasks complete, project ready"
|
|||
```
|
||||
|
||||
**Other exit conditions:**
|
||||
- All tasks in `@fix_plan.md` marked complete
|
||||
- All tasks in `.ralph/@fix_plan.md` marked complete
|
||||
- Multiple consecutive "done" signals from Claude Code
|
||||
- Too many test-focused loops (indicating feature completeness)
|
||||
- Claude API 5-hour usage limit reached (with user prompt to wait or exit)
|
||||
|
|
@ -284,10 +293,10 @@ ralph-import design-doc.pdf
|
|||
|
||||
Ralph-import creates a complete project with:
|
||||
|
||||
- **PROMPT.md** - Converted into Ralph development instructions
|
||||
- **@fix_plan.md** - Requirements broken down into prioritized tasks
|
||||
- **specs/requirements.md** - Technical specifications extracted from your document
|
||||
- **Standard Ralph structure** - All necessary directories and template files
|
||||
- **.ralph/PROMPT.md** - Converted into Ralph development instructions
|
||||
- **.ralph/@fix_plan.md** - Requirements broken down into prioritized tasks
|
||||
- **.ralph/specs/requirements.md** - Technical specifications extracted from your document
|
||||
- **Standard Ralph structure** - All necessary directories and template files in `.ralph/`
|
||||
|
||||
The conversion is intelligent and preserves your original requirements while making them actionable for autonomous development.
|
||||
|
||||
|
|
@ -384,8 +393,8 @@ ralph --no-continue # Isolated iterations
|
|||
ralph --reset-session # Clears current session
|
||||
|
||||
# Check session status
|
||||
cat .ralph_session # View current session file
|
||||
cat .ralph_session_history # View session transition history
|
||||
cat .ralph/.ralph_session # View current session file
|
||||
cat .ralph/.ralph_session_history # View session transition history
|
||||
```
|
||||
|
||||
**Session Auto-Reset Triggers:**
|
||||
|
|
@ -395,7 +404,7 @@ cat .ralph_session_history # View session transition history
|
|||
- Manual circuit breaker reset (`--reset-circuit`)
|
||||
- Session expiration (default: 24 hours)
|
||||
|
||||
Sessions are persisted to `.ralph_session` with a configurable expiration (default: 24 hours). The last 50 session transitions are logged to `.ralph_session_history` for debugging.
|
||||
Sessions are persisted to `.ralph/.ralph_session` with a configurable expiration (default: 24 hours). The last 50 session transitions are logged to `.ralph/.ralph_session_history` for debugging.
|
||||
|
||||
### Exit Thresholds
|
||||
|
||||
|
|
@ -426,42 +435,45 @@ CB_OUTPUT_DECLINE_THRESHOLD=70 # Open circuit if output declines by >70%
|
|||
|
||||
## Project Structure
|
||||
|
||||
Ralph creates a standardized structure for each project:
|
||||
Ralph creates a standardized structure for each project with a `.ralph/` subfolder for configuration:
|
||||
|
||||
```
|
||||
my-project/
|
||||
├── PROMPT.md # Main development instructions for Ralph
|
||||
├── @fix_plan.md # Prioritized task list (@ prefix = Ralph control file)
|
||||
├── @AGENT.md # Build and run instructions
|
||||
├── specs/ # Project specifications and requirements
|
||||
│ └── stdlib/ # Standard library specifications
|
||||
├── src/ # Source code implementation
|
||||
├── examples/ # Usage examples and test cases
|
||||
├── logs/ # Ralph execution logs
|
||||
└── docs/generated/ # Auto-generated documentation
|
||||
├── .ralph/ # Ralph configuration and state (hidden folder)
|
||||
│ ├── PROMPT.md # Main development instructions for Ralph
|
||||
│ ├── @fix_plan.md # Prioritized task list (@ prefix = Ralph control file)
|
||||
│ ├── @AGENT.md # Build and run instructions
|
||||
│ ├── specs/ # Project specifications and requirements
|
||||
│ │ └── stdlib/ # Standard library specifications
|
||||
│ ├── examples/ # Usage examples and test cases
|
||||
│ ├── logs/ # Ralph execution logs
|
||||
│ └── docs/generated/ # Auto-generated documentation
|
||||
└── src/ # Source code implementation (at project root)
|
||||
```
|
||||
|
||||
> **Migration**: If you have existing Ralph projects using the old flat structure, run `ralph-migrate` to automatically move files to the `.ralph/` subfolder.
|
||||
|
||||
## Best Practices
|
||||
|
||||
### Writing Effective Prompts
|
||||
|
||||
1. **Be Specific** - Clear requirements lead to better results
|
||||
2. **Prioritize** - Use `@fix_plan.md` to guide Ralph's focus
|
||||
2. **Prioritize** - Use `.ralph/@fix_plan.md` to guide Ralph's focus
|
||||
3. **Set Boundaries** - Define what's in/out of scope
|
||||
4. **Include Examples** - Show expected inputs/outputs
|
||||
|
||||
### Project Specifications
|
||||
|
||||
- Place detailed requirements in `specs/`
|
||||
- Use `@fix_plan.md` for prioritized task tracking
|
||||
- Keep `@AGENT.md` updated with build instructions
|
||||
- Place detailed requirements in `.ralph/specs/`
|
||||
- Use `.ralph/@fix_plan.md` for prioritized task tracking
|
||||
- Keep `.ralph/@AGENT.md` updated with build instructions
|
||||
- Document key decisions and architecture
|
||||
|
||||
### Monitoring Progress
|
||||
|
||||
- Use `ralph-monitor` for live status updates
|
||||
- Check logs in `logs/` for detailed execution history
|
||||
- Monitor `status.json` for programmatic access
|
||||
- Check logs in `.ralph/logs/` for detailed execution history
|
||||
- Monitor `.ralph/status.json` for programmatic access
|
||||
- Watch for exit condition signals
|
||||
|
||||
## System Requirements
|
||||
|
|
@ -646,6 +658,7 @@ This project is licensed under the MIT License - see the [LICENSE](LICENSE) file
|
|||
./uninstall.sh # Remove Ralph from system (dedicated script)
|
||||
./install.sh uninstall # Alternative: Remove Ralph from system
|
||||
./install.sh --help # Show installation help
|
||||
ralph-migrate # Migrate existing project to .ralph/ structure
|
||||
```
|
||||
|
||||
### Ralph Loop Options
|
||||
|
|
|
|||
107
create_files.sh
107
create_files.sh
|
|
@ -6,7 +6,9 @@ set -e
|
|||
echo "🚀 Creating Ralph for Claude Code repository structure..."
|
||||
|
||||
# Create directories
|
||||
mkdir -p {logs,docs/generated,specs/stdlib,src,examples,templates/specs}
|
||||
# Note: Project structure uses .ralph/ subfolder for Ralph-specific files
|
||||
# src/ stays at root for compatibility with existing tooling
|
||||
mkdir -p {src,templates/specs}
|
||||
|
||||
# Create main scripts
|
||||
cat > ralph_loop.sh << 'EOF'
|
||||
|
|
@ -17,19 +19,20 @@ cat > ralph_loop.sh << 'EOF'
|
|||
|
||||
set -e # Exit on any error
|
||||
|
||||
# Configuration
|
||||
PROMPT_FILE="PROMPT.md"
|
||||
LOG_DIR="logs"
|
||||
DOCS_DIR="docs/generated"
|
||||
STATUS_FILE="status.json"
|
||||
# Configuration - Ralph files live in .ralph/ subfolder
|
||||
RALPH_DIR="${RALPH_DIR:-.ralph}"
|
||||
PROMPT_FILE="$RALPH_DIR/PROMPT.md"
|
||||
LOG_DIR="$RALPH_DIR/logs"
|
||||
DOCS_DIR="$RALPH_DIR/docs/generated"
|
||||
STATUS_FILE="$RALPH_DIR/status.json"
|
||||
CLAUDE_CODE_CMD="npx @anthropic/claude-code"
|
||||
MAX_CALLS_PER_HOUR=100 # Adjust based on your plan
|
||||
SLEEP_DURATION=3600 # 1 hour in seconds
|
||||
CALL_COUNT_FILE=".call_count"
|
||||
TIMESTAMP_FILE=".last_reset"
|
||||
CALL_COUNT_FILE="$RALPH_DIR/.call_count"
|
||||
TIMESTAMP_FILE="$RALPH_DIR/.last_reset"
|
||||
|
||||
# Exit detection configuration
|
||||
EXIT_SIGNALS_FILE=".exit_signals"
|
||||
EXIT_SIGNALS_FILE="$RALPH_DIR/.exit_signals"
|
||||
MAX_CONSECUTIVE_TEST_LOOPS=3
|
||||
MAX_CONSECUTIVE_DONE_SIGNALS=2
|
||||
TEST_PERCENTAGE_THRESHOLD=30 # If more than 30% of recent loops are test-only, flag it
|
||||
|
|
@ -201,9 +204,9 @@ should_exit_gracefully() {
|
|||
fi
|
||||
|
||||
# 4. Check fix_plan.md for completion
|
||||
if [[ -f "@fix_plan.md" ]]; then
|
||||
local total_items=$(grep -c "^- \[" "@fix_plan.md" 2>/dev/null || echo "0")
|
||||
local completed_items=$(grep -c "^- \[x\]" "@fix_plan.md" 2>/dev/null || echo "0")
|
||||
if [[ -f "$RALPH_DIR/@fix_plan.md" ]]; then
|
||||
local total_items=$(grep -c "^- \[" "$RALPH_DIR/@fix_plan.md" 2>/dev/null || echo "0")
|
||||
local completed_items=$(grep -c "^- \[x\]" "$RALPH_DIR/@fix_plan.md" 2>/dev/null || echo "0")
|
||||
|
||||
if [[ $total_items -gt 0 ]] && [[ $completed_items -eq $total_items ]]; then
|
||||
log_status "WARN" "Exit condition: All fix_plan.md items completed ($completed_items/$total_items)"
|
||||
|
|
@ -377,8 +380,9 @@ cat > ralph_monitor.sh << 'EOF'
|
|||
# Ralph Status Monitor - Live terminal dashboard for the Ralph loop
|
||||
set -e
|
||||
|
||||
STATUS_FILE="status.json"
|
||||
LOG_FILE="logs/ralph.log"
|
||||
RALPH_DIR="${RALPH_DIR:-.ralph}"
|
||||
STATUS_FILE="$RALPH_DIR/status.json"
|
||||
LOG_FILE="$RALPH_DIR/logs/ralph.log"
|
||||
REFRESH_INTERVAL=2
|
||||
|
||||
# Colors
|
||||
|
|
@ -482,6 +486,7 @@ cat > setup.sh << 'EOF'
|
|||
#!/bin/bash
|
||||
|
||||
# Ralph Project Setup Script
|
||||
# Creates project structure with Ralph-specific files in .ralph/ subfolder
|
||||
set -e
|
||||
|
||||
PROJECT_NAME=${1:-"my-project"}
|
||||
|
|
@ -492,14 +497,17 @@ echo "🚀 Setting up Ralph project: $PROJECT_NAME"
|
|||
mkdir -p "$PROJECT_NAME"
|
||||
cd "$PROJECT_NAME"
|
||||
|
||||
# Create structure
|
||||
mkdir -p {specs/stdlib,src,examples,logs,docs/generated}
|
||||
# Create structure:
|
||||
# - src/ stays at root for compatibility with existing tooling
|
||||
# - All Ralph-specific files go in .ralph/ subfolder
|
||||
mkdir -p src
|
||||
mkdir -p .ralph/{specs/stdlib,examples,logs,docs/generated}
|
||||
|
||||
# Copy templates
|
||||
cp ../templates/PROMPT.md .
|
||||
cp ../templates/fix_plan.md @fix_plan.md
|
||||
cp ../templates/AGENT.md @AGENT.md
|
||||
cp -r ../templates/specs/* specs/ 2>/dev/null || true
|
||||
# Copy templates to .ralph/
|
||||
cp ../templates/PROMPT.md .ralph/
|
||||
cp ../templates/fix_plan.md .ralph/@fix_plan.md
|
||||
cp ../templates/AGENT.md .ralph/@AGENT.md
|
||||
cp -r ../templates/specs/* .ralph/specs/ 2>/dev/null || true
|
||||
|
||||
# Initialize git
|
||||
git init
|
||||
|
|
@ -509,8 +517,8 @@ git commit -m "Initial Ralph project setup"
|
|||
|
||||
echo "✅ Project $PROJECT_NAME created!"
|
||||
echo "Next steps:"
|
||||
echo " 1. Edit PROMPT.md with your project requirements"
|
||||
echo " 2. Update specs/ with your project specifications"
|
||||
echo " 1. Edit .ralph/PROMPT.md with your project requirements"
|
||||
echo " 2. Update .ralph/specs/ with your project specifications"
|
||||
echo " 3. Run: ../ralph_loop.sh"
|
||||
echo " 4. Monitor: ../ralph_monitor.sh"
|
||||
EOF
|
||||
|
|
@ -525,19 +533,19 @@ cat > templates/PROMPT.md << 'EOF'
|
|||
You are Ralph, an autonomous AI development agent working on a [YOUR PROJECT NAME] project.
|
||||
|
||||
## Current Objectives
|
||||
1. Study specs/* to learn about the project specifications
|
||||
2. Review @fix_plan.md for current priorities
|
||||
1. Study .ralph/specs/* to learn about the project specifications
|
||||
2. Review .ralph/@fix_plan.md for current priorities
|
||||
3. Implement the highest priority item using best practices
|
||||
4. Use parallel subagents for complex tasks (max 100 concurrent)
|
||||
5. Run tests after each implementation
|
||||
6. Update documentation and fix_plan.md
|
||||
6. Update documentation and .ralph/@fix_plan.md
|
||||
|
||||
## Key Principles
|
||||
- ONE task per loop - focus on the most important thing
|
||||
- Search the codebase before assuming something isn't implemented
|
||||
- Use subagents for expensive operations (file searching, analysis)
|
||||
- Write comprehensive tests with clear documentation
|
||||
- Update @fix_plan.md with your learnings
|
||||
- Update .ralph/@fix_plan.md with your learnings
|
||||
- Commit working changes with descriptive messages
|
||||
|
||||
## 🧪 Testing Guidelines (CRITICAL)
|
||||
|
|
@ -558,21 +566,21 @@ You are Ralph, an autonomous AI development agent working on a [YOUR PROJECT NAM
|
|||
|
||||
## Completion Awareness
|
||||
If you believe the project is complete or nearly complete:
|
||||
- Update @fix_plan.md to reflect completion status
|
||||
- Update .ralph/@fix_plan.md to reflect completion status
|
||||
- Summarize what has been accomplished
|
||||
- Note any remaining minor tasks
|
||||
- Do NOT continue with busy work like extensive testing
|
||||
- Do NOT implement features not in the specifications
|
||||
|
||||
## File Structure
|
||||
- specs/: Project specifications and requirements
|
||||
- src/: Source code implementation
|
||||
- examples/: Example usage and test cases
|
||||
- @fix_plan.md: Prioritized TODO list
|
||||
- @AGENT.md: Project build and run instructions
|
||||
- .ralph/specs/: Project specifications and requirements
|
||||
- src/: Source code implementation
|
||||
- .ralph/examples/: Example usage and test cases
|
||||
- .ralph/@fix_plan.md: Prioritized TODO list
|
||||
- .ralph/@AGENT.md: Project build and run instructions
|
||||
|
||||
## Current Task
|
||||
Follow @fix_plan.md and choose the most important item to implement next.
|
||||
Follow .ralph/@fix_plan.md and choose the most important item to implement next.
|
||||
Use your judgment to prioritize what will have the biggest impact on project progress.
|
||||
|
||||
Remember: Quality over speed. Build it right the first time. Know when you're done.
|
||||
|
|
@ -659,19 +667,27 @@ EOF
|
|||
|
||||
# Create gitignore
|
||||
cat > .gitignore << 'EOF'
|
||||
# Ralph generated files
|
||||
.call_count
|
||||
.last_reset
|
||||
.exit_signals
|
||||
status.json
|
||||
# Ralph generated files (inside .ralph/ subfolder)
|
||||
.ralph/.call_count
|
||||
.ralph/.last_reset
|
||||
.ralph/.exit_signals
|
||||
.ralph/status.json
|
||||
.ralph/.ralph_session
|
||||
.ralph/.ralph_session_history
|
||||
.ralph/.claude_session_id
|
||||
.ralph/.response_analysis
|
||||
.ralph/.circuit_breaker_state
|
||||
.ralph/.circuit_breaker_history
|
||||
|
||||
# Logs
|
||||
logs/
|
||||
# Ralph logs and generated docs
|
||||
.ralph/logs/*
|
||||
!.ralph/logs/.gitkeep
|
||||
.ralph/docs/generated/*
|
||||
!.ralph/docs/generated/.gitkeep
|
||||
|
||||
# General logs
|
||||
*.log
|
||||
|
||||
# Generated documentation
|
||||
docs/generated/
|
||||
|
||||
# OS files
|
||||
.DS_Store
|
||||
Thumbs.db
|
||||
|
|
@ -695,6 +711,9 @@ target/
|
|||
.idea/
|
||||
*.swp
|
||||
*.swo
|
||||
|
||||
# Ralph backup directories (created by migration)
|
||||
.ralph_backup_*
|
||||
EOF
|
||||
|
||||
# Make scripts executable
|
||||
|
|
|
|||
50
install.sh
50
install.sh
|
|
@ -154,21 +154,37 @@ EOF
|
|||
RALPH_HOME="$HOME/.ralph"
|
||||
|
||||
exec "$RALPH_HOME/ralph_import.sh" "$@"
|
||||
EOF
|
||||
|
||||
# Create ralph-migrate command
|
||||
cat > "$INSTALL_DIR/ralph-migrate" << 'EOF'
|
||||
#!/bin/bash
|
||||
# Ralph Migration - Global Command
|
||||
# Migrates existing projects from flat structure to .ralph/ subfolder
|
||||
|
||||
RALPH_HOME="$HOME/.ralph"
|
||||
|
||||
exec "$RALPH_HOME/migrate_to_ralph_folder.sh" "$@"
|
||||
EOF
|
||||
|
||||
# Copy actual script files to Ralph home with modifications for global operation
|
||||
cp "$SCRIPT_DIR/ralph_monitor.sh" "$RALPH_HOME/"
|
||||
|
||||
|
||||
# Copy PRD import script to Ralph home
|
||||
cp "$SCRIPT_DIR/ralph_import.sh" "$RALPH_HOME/"
|
||||
|
||||
|
||||
# Copy migration script to Ralph home
|
||||
cp "$SCRIPT_DIR/migrate_to_ralph_folder.sh" "$RALPH_HOME/"
|
||||
|
||||
# Make all commands executable
|
||||
chmod +x "$INSTALL_DIR/ralph"
|
||||
chmod +x "$INSTALL_DIR/ralph-monitor"
|
||||
chmod +x "$INSTALL_DIR/ralph-monitor"
|
||||
chmod +x "$INSTALL_DIR/ralph-setup"
|
||||
chmod +x "$INSTALL_DIR/ralph-import"
|
||||
chmod +x "$INSTALL_DIR/ralph-migrate"
|
||||
chmod +x "$RALPH_HOME/ralph_monitor.sh"
|
||||
chmod +x "$RALPH_HOME/ralph_import.sh"
|
||||
chmod +x "$RALPH_HOME/migrate_to_ralph_folder.sh"
|
||||
chmod +x "$RALPH_HOME/lib/"*.sh
|
||||
|
||||
log "SUCCESS" "Ralph scripts installed to $INSTALL_DIR"
|
||||
|
|
@ -193,7 +209,7 @@ install_ralph_loop() {
|
|||
# Install global setup.sh
|
||||
install_setup() {
|
||||
log "INFO" "Installing global setup script..."
|
||||
|
||||
|
||||
# Create modified setup.sh for global operation
|
||||
cat > "$RALPH_HOME/setup.sh" << 'EOF'
|
||||
#!/bin/bash
|
||||
|
|
@ -210,14 +226,15 @@ echo "🚀 Setting up Ralph project: $PROJECT_NAME"
|
|||
mkdir -p "$PROJECT_NAME"
|
||||
cd "$PROJECT_NAME"
|
||||
|
||||
# Create structure
|
||||
mkdir -p {specs/stdlib,src,examples,logs,docs/generated}
|
||||
# Create structure with .ralph/ subfolder
|
||||
mkdir -p src
|
||||
mkdir -p .ralph/{specs/stdlib,examples,logs,docs/generated}
|
||||
|
||||
# Copy templates from Ralph home
|
||||
cp "$RALPH_HOME/templates/PROMPT.md" .
|
||||
cp "$RALPH_HOME/templates/fix_plan.md" @fix_plan.md
|
||||
cp "$RALPH_HOME/templates/AGENT.md" @AGENT.md
|
||||
cp -r "$RALPH_HOME/templates/specs/"* specs/ 2>/dev/null || true
|
||||
# Copy templates from Ralph home to .ralph/ subfolder
|
||||
cp "$RALPH_HOME/templates/PROMPT.md" .ralph/
|
||||
cp "$RALPH_HOME/templates/fix_plan.md" .ralph/@fix_plan.md
|
||||
cp "$RALPH_HOME/templates/AGENT.md" .ralph/@AGENT.md
|
||||
cp -r "$RALPH_HOME/templates/specs/"* .ralph/specs/ 2>/dev/null || true
|
||||
|
||||
# Initialize git
|
||||
git init
|
||||
|
|
@ -227,14 +244,14 @@ git commit -m "Initial Ralph project setup"
|
|||
|
||||
echo "✅ Project $PROJECT_NAME created!"
|
||||
echo "Next steps:"
|
||||
echo " 1. Edit PROMPT.md with your project requirements"
|
||||
echo " 2. Update specs/ with your project specifications"
|
||||
echo " 1. Edit .ralph/PROMPT.md with your project requirements"
|
||||
echo " 2. Update .ralph/specs/ with your project specifications"
|
||||
echo " 3. Run: ralph --monitor"
|
||||
echo " 4. Monitor: ralph-monitor (if running manually)"
|
||||
EOF
|
||||
|
||||
chmod +x "$RALPH_HOME/setup.sh"
|
||||
|
||||
|
||||
log "SUCCESS" "Global setup script installed"
|
||||
}
|
||||
|
||||
|
|
@ -275,12 +292,13 @@ main() {
|
|||
echo " ralph --help # Show Ralph options"
|
||||
echo " ralph-setup my-project # Create new Ralph project"
|
||||
echo " ralph-import prd.md # Convert PRD to Ralph project"
|
||||
echo " ralph-migrate # Migrate existing project to .ralph/ structure"
|
||||
echo " ralph-monitor # Manual monitoring dashboard"
|
||||
echo ""
|
||||
echo "Quick start:"
|
||||
echo " 1. ralph-setup my-awesome-project"
|
||||
echo " 2. cd my-awesome-project"
|
||||
echo " 3. # Edit PROMPT.md with your requirements"
|
||||
echo " 3. # Edit .ralph/PROMPT.md with your requirements"
|
||||
echo " 4. ralph --monitor"
|
||||
echo ""
|
||||
|
||||
|
|
@ -296,7 +314,7 @@ case "${1:-install}" in
|
|||
;;
|
||||
uninstall)
|
||||
log "INFO" "Uninstalling Ralph for Claude Code..."
|
||||
rm -f "$INSTALL_DIR/ralph" "$INSTALL_DIR/ralph-monitor" "$INSTALL_DIR/ralph-setup" "$INSTALL_DIR/ralph-import"
|
||||
rm -f "$INSTALL_DIR/ralph" "$INSTALL_DIR/ralph-monitor" "$INSTALL_DIR/ralph-setup" "$INSTALL_DIR/ralph-import" "$INSTALL_DIR/ralph-migrate"
|
||||
rm -rf "$RALPH_HOME"
|
||||
log "SUCCESS" "Ralph for Claude Code uninstalled"
|
||||
;;
|
||||
|
|
|
|||
|
|
@ -12,8 +12,10 @@ CB_STATE_HALF_OPEN="HALF_OPEN" # Monitoring mode, checking for recovery
|
|||
CB_STATE_OPEN="OPEN" # Failure detected, execution halted
|
||||
|
||||
# Circuit Breaker Configuration
|
||||
CB_STATE_FILE=".circuit_breaker_state"
|
||||
CB_HISTORY_FILE=".circuit_breaker_history"
|
||||
# Use RALPH_DIR if set by main script, otherwise default to .ralph
|
||||
RALPH_DIR="${RALPH_DIR:-.ralph}"
|
||||
CB_STATE_FILE="$RALPH_DIR/.circuit_breaker_state"
|
||||
CB_HISTORY_FILE="$RALPH_DIR/.circuit_breaker_history"
|
||||
CB_NO_PROGRESS_THRESHOLD=3 # Open circuit after N loops with no progress
|
||||
CB_SAME_ERROR_THRESHOLD=5 # Open circuit after N loops with same error
|
||||
CB_OUTPUT_DECLINE_THRESHOLD=70 # Open circuit if output declines by >70%
|
||||
|
|
@ -301,15 +303,15 @@ should_halt_execution() {
|
|||
echo -e "${YELLOW}Ralph has detected that no progress is being made.${NC}"
|
||||
echo ""
|
||||
echo -e "${YELLOW}Possible reasons:${NC}"
|
||||
echo " • Project may be complete (check @fix_plan.md)"
|
||||
echo " • Project may be complete (check .ralph/@fix_plan.md)"
|
||||
echo " • Claude may be stuck on an error"
|
||||
echo " • PROMPT.md may need clarification"
|
||||
echo " • .ralph/PROMPT.md may need clarification"
|
||||
echo " • Manual intervention may be required"
|
||||
echo ""
|
||||
echo -e "${YELLOW}To continue:${NC}"
|
||||
echo " 1. Review recent logs: tail -20 logs/ralph.log"
|
||||
echo " 2. Check Claude output: ls -lt logs/claude_output_*.log | head -1"
|
||||
echo " 3. Update @fix_plan.md if needed"
|
||||
echo " 1. Review recent logs: tail -20 .ralph/logs/ralph.log"
|
||||
echo " 2. Check Claude output: ls -lt .ralph/logs/claude_output_*.log | head -1"
|
||||
echo " 3. Update .ralph/@fix_plan.md if needed"
|
||||
echo " 4. Reset circuit breaker: ralph --reset-circuit"
|
||||
echo ""
|
||||
return 0 # Signal to halt
|
||||
|
|
|
|||
|
|
@ -15,6 +15,9 @@ YELLOW='\033[1;33m'
|
|||
BLUE='\033[0;34m'
|
||||
NC='\033[0m'
|
||||
|
||||
# Use RALPH_DIR if set by main script, otherwise default to .ralph
|
||||
RALPH_DIR="${RALPH_DIR:-.ralph}"
|
||||
|
||||
# Analysis configuration
|
||||
COMPLETION_KEYWORDS=("done" "complete" "finished" "all tasks complete" "project complete" "ready for review")
|
||||
TEST_ONLY_PATTERNS=("npm test" "bats" "pytest" "jest" "cargo test" "go test" "running tests")
|
||||
|
|
@ -51,13 +54,13 @@ detect_output_format() {
|
|||
}
|
||||
|
||||
# Parse JSON response and extract structured fields
|
||||
# Creates .json_parse_result with normalized analysis data
|
||||
# Creates .ralph/.json_parse_result with normalized analysis data
|
||||
# Supports TWO JSON formats:
|
||||
# 1. Flat format: { status, exit_signal, work_type, files_modified, ... }
|
||||
# 2. Claude CLI format: { result, sessionId, metadata: { files_changed, has_errors, completion_status, ... } }
|
||||
parse_json_response() {
|
||||
local output_file=$1
|
||||
local result_file="${2:-.json_parse_result}"
|
||||
local result_file="${2:-$RALPH_DIR/.json_parse_result}"
|
||||
|
||||
if [[ ! -f "$output_file" ]]; then
|
||||
echo "ERROR: Output file not found: $output_file" >&2
|
||||
|
|
@ -198,7 +201,7 @@ parse_json_response() {
|
|||
analyze_response() {
|
||||
local output_file=$1
|
||||
local loop_number=$2
|
||||
local analysis_result_file=${3:-".response_analysis"}
|
||||
local analysis_result_file=${3:-"$RALPH_DIR/.response_analysis"}
|
||||
|
||||
# Initialize analysis result
|
||||
local has_completion_signal=false
|
||||
|
|
@ -224,16 +227,16 @@ analyze_response() {
|
|||
|
||||
if [[ "$output_format" == "json" ]]; then
|
||||
# Try JSON parsing
|
||||
if parse_json_response "$output_file" ".json_parse_result" 2>/dev/null; then
|
||||
if parse_json_response "$output_file" "$RALPH_DIR/.json_parse_result" 2>/dev/null; then
|
||||
# Extract values from JSON parse result
|
||||
has_completion_signal=$(jq -r '.has_completion_signal' .json_parse_result 2>/dev/null || echo "false")
|
||||
exit_signal=$(jq -r '.exit_signal' .json_parse_result 2>/dev/null || echo "false")
|
||||
is_test_only=$(jq -r '.is_test_only' .json_parse_result 2>/dev/null || echo "false")
|
||||
is_stuck=$(jq -r '.is_stuck' .json_parse_result 2>/dev/null || echo "false")
|
||||
work_summary=$(jq -r '.summary' .json_parse_result 2>/dev/null || echo "")
|
||||
files_modified=$(jq -r '.files_modified' .json_parse_result 2>/dev/null || echo "0")
|
||||
local json_confidence=$(jq -r '.confidence' .json_parse_result 2>/dev/null || echo "0")
|
||||
local session_id=$(jq -r '.session_id' .json_parse_result 2>/dev/null || echo "")
|
||||
has_completion_signal=$(jq -r '.has_completion_signal' $RALPH_DIR/.json_parse_result 2>/dev/null || echo "false")
|
||||
exit_signal=$(jq -r '.exit_signal' $RALPH_DIR/.json_parse_result 2>/dev/null || echo "false")
|
||||
is_test_only=$(jq -r '.is_test_only' $RALPH_DIR/.json_parse_result 2>/dev/null || echo "false")
|
||||
is_stuck=$(jq -r '.is_stuck' $RALPH_DIR/.json_parse_result 2>/dev/null || echo "false")
|
||||
work_summary=$(jq -r '.summary' $RALPH_DIR/.json_parse_result 2>/dev/null || echo "")
|
||||
files_modified=$(jq -r '.files_modified' $RALPH_DIR/.json_parse_result 2>/dev/null || echo "0")
|
||||
local json_confidence=$(jq -r '.confidence' $RALPH_DIR/.json_parse_result 2>/dev/null || echo "0")
|
||||
local session_id=$(jq -r '.session_id' $RALPH_DIR/.json_parse_result 2>/dev/null || echo "")
|
||||
|
||||
# Persist session ID if present (for session continuity across loop iterations)
|
||||
if [[ -n "$session_id" && "$session_id" != "null" ]]; then
|
||||
|
|
@ -289,7 +292,7 @@ analyze_response() {
|
|||
output_length: $output_length
|
||||
}
|
||||
}' > "$analysis_result_file"
|
||||
rm -f ".json_parse_result"
|
||||
rm -f "$RALPH_DIR/.json_parse_result"
|
||||
return 0
|
||||
fi
|
||||
# If JSON parsing failed, fall through to text parsing
|
||||
|
|
@ -394,8 +397,8 @@ analyze_response() {
|
|||
fi
|
||||
|
||||
# 7. Analyze output length trends (detect declining engagement)
|
||||
if [[ -f ".last_output_length" ]]; then
|
||||
local last_length=$(cat ".last_output_length")
|
||||
if [[ -f "$RALPH_DIR/.last_output_length" ]]; then
|
||||
local last_length=$(cat "$RALPH_DIR/.last_output_length")
|
||||
local length_ratio=$((output_length * 100 / last_length))
|
||||
|
||||
if [[ $length_ratio -lt 50 ]]; then
|
||||
|
|
@ -403,7 +406,7 @@ analyze_response() {
|
|||
((confidence_score+=10))
|
||||
fi
|
||||
fi
|
||||
echo "$output_length" > ".last_output_length"
|
||||
echo "$output_length" > "$RALPH_DIR/.last_output_length"
|
||||
|
||||
# 8. Extract work summary from output
|
||||
if [[ -z "$work_summary" ]]; then
|
||||
|
|
@ -463,8 +466,8 @@ analyze_response() {
|
|||
|
||||
# Update exit signals file based on analysis
|
||||
update_exit_signals() {
|
||||
local analysis_file=${1:-".response_analysis"}
|
||||
local exit_signals_file=${2:-".exit_signals"}
|
||||
local analysis_file=${1:-"$RALPH_DIR/.response_analysis"}
|
||||
local exit_signals_file=${2:-"$RALPH_DIR/.exit_signals"}
|
||||
|
||||
if [[ ! -f "$analysis_file" ]]; then
|
||||
echo "ERROR: Analysis file not found: $analysis_file"
|
||||
|
|
@ -514,7 +517,7 @@ update_exit_signals() {
|
|||
|
||||
# Log analysis results in human-readable format
|
||||
log_analysis_summary() {
|
||||
local analysis_file=${1:-".response_analysis"}
|
||||
local analysis_file=${1:-"$RALPH_DIR/.response_analysis"}
|
||||
|
||||
if [[ ! -f "$analysis_file" ]]; then
|
||||
return 1
|
||||
|
|
@ -541,7 +544,7 @@ log_analysis_summary() {
|
|||
# Detect if Claude is stuck (repeating same errors)
|
||||
detect_stuck_loop() {
|
||||
local current_output=$1
|
||||
local history_dir=${2:-"logs"}
|
||||
local history_dir=${2:-"$RALPH_DIR/logs"}
|
||||
|
||||
# Get last 3 output files
|
||||
local recent_outputs=$(ls -t "$history_dir"/claude_output_*.log 2>/dev/null | head -3)
|
||||
|
|
@ -592,7 +595,7 @@ detect_stuck_loop() {
|
|||
# =============================================================================
|
||||
|
||||
# Session file location - standardized across ralph_loop.sh and response_analyzer.sh
|
||||
SESSION_FILE=".claude_session_id"
|
||||
SESSION_FILE="$RALPH_DIR/.claude_session_id"
|
||||
# Session expiration time in seconds (24 hours)
|
||||
SESSION_EXPIRATION_SECONDS=86400
|
||||
|
||||
|
|
|
|||
317
migrate_to_ralph_folder.sh
Executable file
317
migrate_to_ralph_folder.sh
Executable file
|
|
@ -0,0 +1,317 @@
|
|||
#!/bin/bash
|
||||
|
||||
# Migration script for Ralph projects from flat structure to .ralph/ subfolder
|
||||
# Version: 1.0.0
|
||||
#
|
||||
# This script migrates existing Ralph projects from the old flat structure:
|
||||
# PROMPT.md, @fix_plan.md, @AGENT.md, specs/, logs/, docs/generated/
|
||||
# To the new .ralph/ subfolder structure:
|
||||
# .ralph/PROMPT.md, .ralph/@fix_plan.md, .ralph/@AGENT.md, .ralph/specs/, etc.
|
||||
#
|
||||
# Usage: ./migrate_to_ralph_folder.sh [project-directory]
|
||||
#
|
||||
# If no project directory is specified, the current directory is used.
|
||||
|
||||
set -e
|
||||
|
||||
# Colors
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
BLUE='\033[0;34m'
|
||||
NC='\033[0m'
|
||||
|
||||
log() {
|
||||
local level=$1
|
||||
local message=$2
|
||||
local color=""
|
||||
|
||||
case $level in
|
||||
"INFO") color=$BLUE ;;
|
||||
"WARN") color=$YELLOW ;;
|
||||
"ERROR") color=$RED ;;
|
||||
"SUCCESS") color=$GREEN ;;
|
||||
esac
|
||||
|
||||
echo -e "${color}[$(date '+%H:%M:%S')] [$level] $message${NC}"
|
||||
}
|
||||
|
||||
# Check if project is already migrated
|
||||
is_already_migrated() {
|
||||
local project_dir=$1
|
||||
|
||||
# Check if .ralph/ directory exists with key files
|
||||
if [[ -d "$project_dir/.ralph" ]] && \
|
||||
[[ -f "$project_dir/.ralph/PROMPT.md" ]] && \
|
||||
[[ -f "$project_dir/.ralph/@fix_plan.md" ]]; then
|
||||
return 0 # Already migrated
|
||||
fi
|
||||
return 1 # Not migrated
|
||||
}
|
||||
|
||||
# Check if project needs migration (has old-style structure)
|
||||
needs_migration() {
|
||||
local project_dir=$1
|
||||
|
||||
# Check for old-style structure (files in root)
|
||||
if [[ -f "$project_dir/PROMPT.md" ]] || \
|
||||
[[ -f "$project_dir/@fix_plan.md" ]] || \
|
||||
[[ -f "$project_dir/@AGENT.md" ]] || \
|
||||
[[ -d "$project_dir/specs" && ! -d "$project_dir/.ralph/specs" ]] || \
|
||||
[[ -d "$project_dir/logs" && ! -d "$project_dir/.ralph/logs" ]]; then
|
||||
return 0 # Needs migration
|
||||
fi
|
||||
return 1 # Doesn't need migration
|
||||
}
|
||||
|
||||
# Backup function
|
||||
create_backup() {
|
||||
local project_dir=$1
|
||||
local backup_dir
|
||||
local backup_ts
|
||||
|
||||
# Get timestamp with proper error handling
|
||||
backup_ts="$(date +%Y%m%d_%H%M%S)" || {
|
||||
log "ERROR" "Failed to get timestamp for backup"
|
||||
return 1
|
||||
}
|
||||
backup_dir="$project_dir/.ralph_backup_${backup_ts}"
|
||||
|
||||
log "INFO" "Creating backup at $backup_dir" >&2
|
||||
mkdir -p "$backup_dir"
|
||||
|
||||
# Backup files that will be moved
|
||||
[[ -f "$project_dir/PROMPT.md" ]] && cp "$project_dir/PROMPT.md" "$backup_dir/"
|
||||
[[ -f "$project_dir/@fix_plan.md" ]] && cp "$project_dir/@fix_plan.md" "$backup_dir/"
|
||||
[[ -f "$project_dir/@AGENT.md" ]] && cp "$project_dir/@AGENT.md" "$backup_dir/"
|
||||
[[ -d "$project_dir/specs" ]] && cp -r "$project_dir/specs" "$backup_dir/"
|
||||
[[ -d "$project_dir/logs" ]] && cp -r "$project_dir/logs" "$backup_dir/"
|
||||
[[ -d "$project_dir/docs/generated" ]] && cp -r "$project_dir/docs/generated" "$backup_dir/docs_generated"
|
||||
[[ -d "$project_dir/examples" ]] && cp -r "$project_dir/examples" "$backup_dir/"
|
||||
|
||||
# Backup hidden state files
|
||||
[[ -f "$project_dir/.call_count" ]] && cp "$project_dir/.call_count" "$backup_dir/"
|
||||
[[ -f "$project_dir/.last_reset" ]] && cp "$project_dir/.last_reset" "$backup_dir/"
|
||||
[[ -f "$project_dir/.exit_signals" ]] && cp "$project_dir/.exit_signals" "$backup_dir/"
|
||||
[[ -f "$project_dir/.response_analysis" ]] && cp "$project_dir/.response_analysis" "$backup_dir/"
|
||||
[[ -f "$project_dir/.circuit_breaker_state" ]] && cp "$project_dir/.circuit_breaker_state" "$backup_dir/"
|
||||
[[ -f "$project_dir/.circuit_breaker_history" ]] && cp "$project_dir/.circuit_breaker_history" "$backup_dir/"
|
||||
[[ -f "$project_dir/.claude_session_id" ]] && cp "$project_dir/.claude_session_id" "$backup_dir/"
|
||||
[[ -f "$project_dir/.ralph_session" ]] && cp "$project_dir/.ralph_session" "$backup_dir/"
|
||||
[[ -f "$project_dir/status.json" ]] && cp "$project_dir/status.json" "$backup_dir/"
|
||||
|
||||
echo "$backup_dir"
|
||||
}
|
||||
|
||||
# Migrate project to new structure
|
||||
migrate_project() {
|
||||
local project_dir=$1
|
||||
local backup_dir=$2
|
||||
|
||||
log "INFO" "Starting migration..."
|
||||
|
||||
# Create .ralph directory structure (examples created only if source exists)
|
||||
mkdir -p "$project_dir/.ralph/specs/stdlib"
|
||||
mkdir -p "$project_dir/.ralph/logs"
|
||||
mkdir -p "$project_dir/.ralph/docs/generated"
|
||||
|
||||
# Move main configuration files
|
||||
if [[ -f "$project_dir/PROMPT.md" ]]; then
|
||||
log "INFO" "Moving PROMPT.md to .ralph/"
|
||||
mv "$project_dir/PROMPT.md" "$project_dir/.ralph/PROMPT.md"
|
||||
fi
|
||||
|
||||
if [[ -f "$project_dir/@fix_plan.md" ]]; then
|
||||
log "INFO" "Moving @fix_plan.md to .ralph/"
|
||||
mv "$project_dir/@fix_plan.md" "$project_dir/.ralph/@fix_plan.md"
|
||||
fi
|
||||
|
||||
if [[ -f "$project_dir/@AGENT.md" ]]; then
|
||||
log "INFO" "Moving @AGENT.md to .ralph/"
|
||||
mv "$project_dir/@AGENT.md" "$project_dir/.ralph/@AGENT.md"
|
||||
fi
|
||||
|
||||
# Move specs directory contents (fail-safe: preserve dotfiles, verify copy before delete)
|
||||
if [[ -d "$project_dir/specs" ]]; then
|
||||
log "INFO" "Moving specs/ to .ralph/specs/"
|
||||
if [[ "$(ls -A "$project_dir/specs" 2>/dev/null)" ]]; then
|
||||
# Use cp -a with /. pattern to preserve dotfiles and attributes
|
||||
if cp -a "$project_dir/specs/." "$project_dir/.ralph/specs/"; then
|
||||
rm -rf "$project_dir/specs"
|
||||
else
|
||||
log "WARN" "Failed to copy specs/, keeping original (backup available)"
|
||||
fi
|
||||
else
|
||||
rm -rf "$project_dir/specs"
|
||||
fi
|
||||
fi
|
||||
|
||||
# Move logs directory contents (fail-safe: preserve dotfiles, verify copy before delete)
|
||||
if [[ -d "$project_dir/logs" ]]; then
|
||||
log "INFO" "Moving logs/ to .ralph/logs/"
|
||||
if [[ "$(ls -A "$project_dir/logs" 2>/dev/null)" ]]; then
|
||||
# Use cp -a with /. pattern to preserve dotfiles and attributes
|
||||
if cp -a "$project_dir/logs/." "$project_dir/.ralph/logs/"; then
|
||||
rm -rf "$project_dir/logs"
|
||||
else
|
||||
log "WARN" "Failed to copy logs/, keeping original (backup available)"
|
||||
fi
|
||||
else
|
||||
rm -rf "$project_dir/logs"
|
||||
fi
|
||||
fi
|
||||
|
||||
# Move docs/generated contents (fail-safe: preserve dotfiles, verify copy before delete)
|
||||
if [[ -d "$project_dir/docs/generated" ]]; then
|
||||
log "INFO" "Moving docs/generated/ to .ralph/docs/generated/"
|
||||
if [[ "$(ls -A "$project_dir/docs/generated" 2>/dev/null)" ]]; then
|
||||
# Use cp -a with /. pattern to preserve dotfiles and attributes
|
||||
if cp -a "$project_dir/docs/generated/." "$project_dir/.ralph/docs/generated/"; then
|
||||
rm -rf "$project_dir/docs/generated"
|
||||
# Remove docs directory if empty
|
||||
rmdir "$project_dir/docs" 2>/dev/null || true
|
||||
else
|
||||
log "WARN" "Failed to copy docs/generated/, keeping original (backup available)"
|
||||
fi
|
||||
else
|
||||
rm -rf "$project_dir/docs/generated"
|
||||
rmdir "$project_dir/docs" 2>/dev/null || true
|
||||
fi
|
||||
fi
|
||||
|
||||
# Move hidden state files
|
||||
local state_files=(
|
||||
".call_count"
|
||||
".last_reset"
|
||||
".exit_signals"
|
||||
".response_analysis"
|
||||
".circuit_breaker_state"
|
||||
".circuit_breaker_history"
|
||||
".claude_session_id"
|
||||
".ralph_session"
|
||||
".ralph_session_history"
|
||||
".json_parse_result"
|
||||
".last_output_length"
|
||||
"status.json"
|
||||
)
|
||||
|
||||
for file in "${state_files[@]}"; do
|
||||
if [[ -f "$project_dir/$file" ]]; then
|
||||
log "INFO" "Moving $file to .ralph/"
|
||||
mv "$project_dir/$file" "$project_dir/.ralph/$file"
|
||||
fi
|
||||
done
|
||||
|
||||
# Move examples if source exists (fail-safe: preserve dotfiles, verify copy before delete)
|
||||
if [[ -d "$project_dir/examples" ]]; then
|
||||
# Only move if target doesn't exist or is empty
|
||||
if [[ ! -d "$project_dir/.ralph/examples" ]] || [[ -z "$(ls -A "$project_dir/.ralph/examples" 2>/dev/null)" ]]; then
|
||||
log "INFO" "Moving examples/ to .ralph/examples/"
|
||||
mkdir -p "$project_dir/.ralph/examples"
|
||||
if [[ "$(ls -A "$project_dir/examples" 2>/dev/null)" ]]; then
|
||||
# Use cp -a with /. pattern to preserve dotfiles and attributes
|
||||
if cp -a "$project_dir/examples/." "$project_dir/.ralph/examples/"; then
|
||||
rm -rf "$project_dir/examples"
|
||||
else
|
||||
log "WARN" "Failed to copy examples/, keeping original (backup available)"
|
||||
fi
|
||||
else
|
||||
rm -rf "$project_dir/examples"
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
log "SUCCESS" "Migration completed successfully!"
|
||||
}
|
||||
|
||||
# Main function
|
||||
main() {
|
||||
local project_dir="${1:-.}"
|
||||
|
||||
# Convert to absolute path
|
||||
project_dir=$(cd "$project_dir" && pwd)
|
||||
|
||||
log "INFO" "Checking project directory: $project_dir"
|
||||
|
||||
# Check if already migrated
|
||||
if is_already_migrated "$project_dir"; then
|
||||
log "SUCCESS" "Project is already using the new .ralph/ structure"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Check if needs migration
|
||||
if ! needs_migration "$project_dir"; then
|
||||
log "WARN" "No Ralph project files found. Nothing to migrate."
|
||||
log "INFO" "Expected files: PROMPT.md, @fix_plan.md, @AGENT.md, specs/, logs/"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Create backup
|
||||
backup_dir=$(create_backup "$project_dir")
|
||||
log "SUCCESS" "Backup created at: $backup_dir"
|
||||
|
||||
# Perform migration
|
||||
migrate_project "$project_dir" "$backup_dir"
|
||||
|
||||
echo ""
|
||||
log "INFO" "Migration summary:"
|
||||
echo " - Project files moved to .ralph/ subfolder"
|
||||
echo " - Backup saved at: $backup_dir"
|
||||
echo " - src/ directory preserved at project root"
|
||||
echo ""
|
||||
log "INFO" "Next steps:"
|
||||
echo " 1. Verify the migration by checking .ralph/ contents"
|
||||
echo " 2. Run 'ralph --status' to verify Ralph can read the new structure"
|
||||
echo " 3. If everything works, you can delete the backup directory"
|
||||
echo ""
|
||||
}
|
||||
|
||||
# Show help
|
||||
if [[ "$1" == "-h" || "$1" == "--help" ]]; then
|
||||
cat << 'HELPEOF'
|
||||
Ralph Migration Script - Migrate to .ralph/ subfolder structure
|
||||
|
||||
Usage: migrate_to_ralph_folder.sh [project-directory]
|
||||
|
||||
Arguments:
|
||||
project-directory Path to the Ralph project to migrate (default: current directory)
|
||||
|
||||
Description:
|
||||
This script migrates existing Ralph projects from the old flat structure to the
|
||||
new .ralph/ subfolder structure. This change keeps source code clean by moving
|
||||
Ralph-specific files into a dedicated subfolder.
|
||||
|
||||
Old structure:
|
||||
project/
|
||||
├── PROMPT.md
|
||||
├── @fix_plan.md
|
||||
├── @AGENT.md
|
||||
├── specs/
|
||||
├── logs/
|
||||
└── src/
|
||||
|
||||
New structure:
|
||||
project/
|
||||
├── .ralph/
|
||||
│ ├── PROMPT.md
|
||||
│ ├── @fix_plan.md
|
||||
│ ├── @AGENT.md
|
||||
│ ├── specs/
|
||||
│ ├── logs/
|
||||
│ └── docs/generated/
|
||||
└── src/
|
||||
|
||||
Features:
|
||||
- Automatically detects if migration is needed
|
||||
- Creates backup before migration
|
||||
- Moves all Ralph-specific files and state
|
||||
- Preserves src/ at project root
|
||||
|
||||
Examples:
|
||||
migrate_to_ralph_folder.sh # Migrate current directory
|
||||
migrate_to_ralph_folder.sh ./my-project # Migrate specific project
|
||||
HELPEOF
|
||||
exit 0
|
||||
fi
|
||||
|
||||
main "$@"
|
||||
|
|
@ -260,9 +260,9 @@ Supported formats:
|
|||
The command will:
|
||||
1. Create a new Ralph project
|
||||
2. Use Claude Code to intelligently convert your PRD into:
|
||||
- PROMPT.md (Ralph instructions)
|
||||
- @fix_plan.md (prioritized tasks)
|
||||
- specs/ (technical specifications)
|
||||
- .ralph/PROMPT.md (Ralph instructions)
|
||||
- .ralph/@fix_plan.md (prioritized tasks)
|
||||
- .ralph/specs/ (technical specifications)
|
||||
|
||||
HELPEOF
|
||||
}
|
||||
|
|
@ -312,9 +312,9 @@ Analyze the provided specification file and extract:
|
|||
|
||||
## Required Outputs
|
||||
|
||||
Create these files in the current directory:
|
||||
Create these files in the .ralph/ subdirectory:
|
||||
|
||||
### 1. PROMPT.md
|
||||
### 1. .ralph/PROMPT.md
|
||||
Transform the PRD into Ralph development instructions:
|
||||
```markdown
|
||||
# Ralph Development Instructions
|
||||
|
|
@ -353,7 +353,7 @@ You are Ralph, an autonomous AI development agent working on a [PROJECT NAME] pr
|
|||
Follow @fix_plan.md and choose the most important item to implement next.
|
||||
```
|
||||
|
||||
### 2. @fix_plan.md
|
||||
### 2. .ralph/@fix_plan.md
|
||||
Convert requirements into a prioritized task list:
|
||||
```markdown
|
||||
# Ralph Fix Plan
|
||||
|
|
@ -374,7 +374,7 @@ Convert requirements into a prioritized task list:
|
|||
[Any important context from the original PRD]
|
||||
```
|
||||
|
||||
### 3. specs/requirements.md
|
||||
### 3. .ralph/specs/requirements.md
|
||||
Create detailed technical specifications:
|
||||
```markdown
|
||||
# Technical Specifications
|
||||
|
|
@ -505,7 +505,7 @@ PROMPTEOF
|
|||
# Use PARSED_FILES_CREATED from JSON if available, otherwise check filesystem
|
||||
local missing_files=()
|
||||
local created_files=()
|
||||
local expected_files=("PROMPT.md" "@fix_plan.md" "specs/requirements.md")
|
||||
local expected_files=(".ralph/PROMPT.md" ".ralph/@fix_plan.md" ".ralph/specs/requirements.md")
|
||||
|
||||
# If JSON provided files_created, use that to inform verification
|
||||
if [[ "$json_parsed" == "true" && -n "$PARSED_FILES_CREATED" && "$PARSED_FILES_CREATED" != "[]" ]]; then
|
||||
|
|
@ -605,9 +605,9 @@ main() {
|
|||
echo ""
|
||||
echo "Next steps:"
|
||||
echo " 1. Review and edit the generated files:"
|
||||
echo " - PROMPT.md (Ralph instructions)"
|
||||
echo " - @fix_plan.md (task priorities)"
|
||||
echo " - specs/requirements.md (technical specs)"
|
||||
echo " - .ralph/PROMPT.md (Ralph instructions)"
|
||||
echo " - .ralph/@fix_plan.md (task priorities)"
|
||||
echo " - .ralph/specs/requirements.md (technical specs)"
|
||||
echo " 2. Start autonomous development:"
|
||||
echo " ralph --monitor"
|
||||
echo ""
|
||||
|
|
|
|||
|
|
@ -13,31 +13,33 @@ source "$SCRIPT_DIR/lib/response_analyzer.sh"
|
|||
source "$SCRIPT_DIR/lib/circuit_breaker.sh"
|
||||
|
||||
# Configuration
|
||||
PROMPT_FILE="PROMPT.md"
|
||||
LOG_DIR="logs"
|
||||
DOCS_DIR="docs/generated"
|
||||
STATUS_FILE="status.json"
|
||||
PROGRESS_FILE="progress.json"
|
||||
# Ralph-specific files live in .ralph/ subfolder
|
||||
RALPH_DIR=".ralph"
|
||||
PROMPT_FILE="$RALPH_DIR/PROMPT.md"
|
||||
LOG_DIR="$RALPH_DIR/logs"
|
||||
DOCS_DIR="$RALPH_DIR/docs/generated"
|
||||
STATUS_FILE="$RALPH_DIR/status.json"
|
||||
PROGRESS_FILE="$RALPH_DIR/progress.json"
|
||||
CLAUDE_CODE_CMD="claude"
|
||||
MAX_CALLS_PER_HOUR=100 # Adjust based on your plan
|
||||
VERBOSE_PROGRESS=false # Default: no verbose progress updates
|
||||
CLAUDE_TIMEOUT_MINUTES=15 # Default: 15 minutes timeout for Claude Code execution
|
||||
SLEEP_DURATION=3600 # 1 hour in seconds
|
||||
CALL_COUNT_FILE=".call_count"
|
||||
TIMESTAMP_FILE=".last_reset"
|
||||
CALL_COUNT_FILE="$RALPH_DIR/.call_count"
|
||||
TIMESTAMP_FILE="$RALPH_DIR/.last_reset"
|
||||
USE_TMUX=false
|
||||
|
||||
# Modern Claude CLI configuration (Phase 1.1)
|
||||
CLAUDE_OUTPUT_FORMAT="json" # Options: json, text
|
||||
CLAUDE_ALLOWED_TOOLS="Write,Bash(git *),Read" # Comma-separated list of allowed tools
|
||||
CLAUDE_USE_CONTINUE=true # Enable session continuity
|
||||
CLAUDE_SESSION_FILE=".claude_session_id" # Session ID persistence file
|
||||
CLAUDE_SESSION_FILE="$RALPH_DIR/.claude_session_id" # Session ID persistence file
|
||||
CLAUDE_MIN_VERSION="2.0.76" # Minimum required Claude CLI version
|
||||
|
||||
# Session management configuration (Phase 1.2)
|
||||
# Note: SESSION_EXPIRATION_SECONDS is defined in lib/response_analyzer.sh (86400 = 24 hours)
|
||||
RALPH_SESSION_FILE=".ralph_session" # Ralph-specific session tracking (lifecycle)
|
||||
RALPH_SESSION_HISTORY_FILE=".ralph_session_history" # Session transition history
|
||||
RALPH_SESSION_FILE="$RALPH_DIR/.ralph_session" # Ralph-specific session tracking (lifecycle)
|
||||
RALPH_SESSION_HISTORY_FILE="$RALPH_DIR/.ralph_session_history" # Session transition history
|
||||
# Session expiration: 24 hours default balances project continuity with fresh context
|
||||
# Too short = frequent context loss; Too long = stale context causes unpredictable behavior
|
||||
CLAUDE_SESSION_EXPIRY_HOURS=${CLAUDE_SESSION_EXPIRY_HOURS:-24}
|
||||
|
|
@ -65,7 +67,8 @@ VALID_TOOL_PATTERNS=(
|
|||
)
|
||||
|
||||
# Exit detection configuration
|
||||
EXIT_SIGNALS_FILE=".exit_signals"
|
||||
EXIT_SIGNALS_FILE="$RALPH_DIR/.exit_signals"
|
||||
RESPONSE_ANALYSIS_FILE="$RALPH_DIR/.response_analysis"
|
||||
MAX_CONSECUTIVE_TEST_LOOPS=3
|
||||
MAX_CONSECUTIVE_DONE_SIGNALS=2
|
||||
TEST_PERCENTAGE_THRESHOLD=30 # If more than 30% of recent loops are test-only, flag it
|
||||
|
|
@ -124,7 +127,7 @@ setup_tmux_session() {
|
|||
if [[ "$MAX_CALLS_PER_HOUR" != "100" ]]; then
|
||||
ralph_cmd="$ralph_cmd --calls $MAX_CALLS_PER_HOUR"
|
||||
fi
|
||||
if [[ "$PROMPT_FILE" != "PROMPT.md" ]]; then
|
||||
if [[ "$PROMPT_FILE" != "$RALPH_DIR/PROMPT.md" ]]; then
|
||||
ralph_cmd="$ralph_cmd --prompt '$PROMPT_FILE'"
|
||||
fi
|
||||
|
||||
|
|
@ -315,8 +318,8 @@ should_exit_gracefully() {
|
|||
# but Claude explicitly indicates work is still in progress via RALPH_STATUS block.
|
||||
# The exit_signal in .response_analysis represents Claude's explicit intent.
|
||||
local claude_exit_signal="false"
|
||||
if [[ -f ".response_analysis" ]]; then
|
||||
claude_exit_signal=$(jq -r '.analysis.exit_signal // false' ".response_analysis" 2>/dev/null || echo "false")
|
||||
if [[ -f "$RESPONSE_ANALYSIS_FILE" ]]; then
|
||||
claude_exit_signal=$(jq -r '.analysis.exit_signal // false' "$RESPONSE_ANALYSIS_FILE" 2>/dev/null || echo "false")
|
||||
fi
|
||||
|
||||
if [[ $recent_completion_indicators -ge 2 ]] && [[ "$claude_exit_signal" == "true" ]]; then
|
||||
|
|
@ -328,23 +331,23 @@ should_exit_gracefully() {
|
|||
fi
|
||||
|
||||
# 4. Check fix_plan.md for completion
|
||||
if [[ -f "@fix_plan.md" ]]; then
|
||||
local total_items=$(grep -c "^- \[" "@fix_plan.md" 2>/dev/null)
|
||||
local completed_items=$(grep -c "^- \[x\]" "@fix_plan.md" 2>/dev/null)
|
||||
|
||||
if [[ -f "$RALPH_DIR/@fix_plan.md" ]]; then
|
||||
local total_items=$(grep -c "^- \[" "$RALPH_DIR/@fix_plan.md" 2>/dev/null)
|
||||
local completed_items=$(grep -c "^- \[x\]" "$RALPH_DIR/@fix_plan.md" 2>/dev/null)
|
||||
|
||||
# Handle case where grep returns no matches (exit code 1)
|
||||
[[ -z "$total_items" ]] && total_items=0
|
||||
[[ -z "$completed_items" ]] && completed_items=0
|
||||
|
||||
log_status "INFO" "DEBUG: @fix_plan.md check - total_items:$total_items, completed_items:$completed_items" >&2
|
||||
|
||||
|
||||
log_status "INFO" "DEBUG: .ralph/@fix_plan.md check - total_items:$total_items, completed_items:$completed_items" >&2
|
||||
|
||||
if [[ $total_items -gt 0 ]] && [[ $completed_items -eq $total_items ]]; then
|
||||
log_status "WARN" "Exit condition: All fix_plan.md items completed ($completed_items/$total_items)" >&2
|
||||
echo "plan_complete"
|
||||
return 0
|
||||
fi
|
||||
else
|
||||
log_status "INFO" "DEBUG: @fix_plan.md file not found" >&2
|
||||
log_status "INFO" "DEBUG: .ralph/@fix_plan.md file not found" >&2
|
||||
fi
|
||||
|
||||
log_status "INFO" "DEBUG: No exit conditions met, continuing loop" >&2
|
||||
|
|
@ -442,22 +445,22 @@ build_loop_context() {
|
|||
context="Loop #${loop_count}. "
|
||||
|
||||
# Extract incomplete tasks from @fix_plan.md
|
||||
if [[ -f "@fix_plan.md" ]]; then
|
||||
local incomplete_tasks=$(grep -c "^- \[ \]" "@fix_plan.md" 2>/dev/null || echo "0")
|
||||
if [[ -f "$RALPH_DIR/@fix_plan.md" ]]; then
|
||||
local incomplete_tasks=$(grep -c "^- \[ \]" "$RALPH_DIR/@fix_plan.md" 2>/dev/null || echo "0")
|
||||
context+="Remaining tasks: ${incomplete_tasks}. "
|
||||
fi
|
||||
|
||||
# Add circuit breaker state
|
||||
if [[ -f ".circuit_breaker_state" ]]; then
|
||||
local cb_state=$(jq -r '.state // "UNKNOWN"' .circuit_breaker_state 2>/dev/null)
|
||||
if [[ -f "$RALPH_DIR/.circuit_breaker_state" ]]; then
|
||||
local cb_state=$(jq -r '.state // "UNKNOWN"' "$RALPH_DIR/.circuit_breaker_state" 2>/dev/null)
|
||||
if [[ "$cb_state" != "CLOSED" && "$cb_state" != "null" && -n "$cb_state" ]]; then
|
||||
context+="Circuit breaker: ${cb_state}. "
|
||||
fi
|
||||
fi
|
||||
|
||||
# Add previous loop summary (truncated)
|
||||
if [[ -f ".response_analysis" ]]; then
|
||||
local prev_summary=$(jq -r '.analysis.work_summary // ""' .response_analysis 2>/dev/null | head -c 200)
|
||||
if [[ -f "$RESPONSE_ANALYSIS_FILE" ]]; then
|
||||
local prev_summary=$(jq -r '.analysis.work_summary // ""' "$RESPONSE_ANALYSIS_FILE" 2>/dev/null | head -c 200)
|
||||
if [[ -n "$prev_summary" && "$prev_summary" != "null" ]]; then
|
||||
context+="Previous: ${prev_summary}"
|
||||
fi
|
||||
|
|
@ -1028,32 +1031,46 @@ loop_count=0
|
|||
|
||||
# Main loop
|
||||
main() {
|
||||
|
||||
|
||||
log_status "SUCCESS" "🚀 Ralph loop starting with Claude Code"
|
||||
log_status "INFO" "Max calls per hour: $MAX_CALLS_PER_HOUR"
|
||||
log_status "INFO" "Logs: $LOG_DIR/ | Docs: $DOCS_DIR/ | Status: $STATUS_FILE"
|
||||
|
||||
|
||||
# Check if project uses old flat structure and needs migration
|
||||
if [[ -f "PROMPT.md" ]] && [[ ! -d ".ralph" ]]; then
|
||||
log_status "ERROR" "This project uses the old flat structure."
|
||||
echo ""
|
||||
echo "Ralph v0.10.0+ uses a .ralph/ subfolder to keep your project root clean."
|
||||
echo ""
|
||||
echo "To upgrade your project, run:"
|
||||
echo " ralph-migrate"
|
||||
echo ""
|
||||
echo "This will move Ralph-specific files to .ralph/ while preserving src/ at root."
|
||||
echo "A backup will be created before migration."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Check if this is a Ralph project directory
|
||||
if [[ ! -f "$PROMPT_FILE" ]]; then
|
||||
log_status "ERROR" "Prompt file '$PROMPT_FILE' not found!"
|
||||
echo ""
|
||||
|
||||
# Check if this looks like a partial Ralph project
|
||||
if [[ -f "@fix_plan.md" ]] || [[ -d "specs" ]] || [[ -f "@AGENT.md" ]]; then
|
||||
echo "This appears to be a Ralph project but is missing PROMPT.md."
|
||||
if [[ -f "$RALPH_DIR/@fix_plan.md" ]] || [[ -d "$RALPH_DIR/specs" ]] || [[ -f "$RALPH_DIR/@AGENT.md" ]]; then
|
||||
echo "This appears to be a Ralph project but is missing .ralph/PROMPT.md."
|
||||
echo "You may need to create or restore the PROMPT.md file."
|
||||
else
|
||||
echo "This directory is not a Ralph project."
|
||||
fi
|
||||
|
||||
|
||||
echo ""
|
||||
echo "To fix this:"
|
||||
echo " 1. Create a new project: ralph-setup my-project"
|
||||
echo " 2. Import existing requirements: ralph-import requirements.md"
|
||||
echo " 3. Navigate to an existing Ralph project directory"
|
||||
echo " 4. Or create PROMPT.md manually in this directory"
|
||||
echo " 4. Or create .ralph/PROMPT.md manually in this directory"
|
||||
echo ""
|
||||
echo "Ralph projects should contain: PROMPT.md, @fix_plan.md, specs/, src/, etc."
|
||||
echo "Ralph projects should contain: .ralph/PROMPT.md, .ralph/@fix_plan.md, .ralph/specs/, src/, etc."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
|
|
@ -1203,10 +1220,10 @@ Files created:
|
|||
- $LOG_DIR/: All execution logs
|
||||
- $DOCS_DIR/: Generated documentation
|
||||
- $STATUS_FILE: Current status (JSON)
|
||||
- .ralph_session: Session lifecycle tracking
|
||||
- .ralph_session_history: Session transition history (last 50)
|
||||
- .call_count: API call counter for rate limiting
|
||||
- .last_reset: Timestamp of last rate limit reset
|
||||
- .ralph/.ralph_session: Session lifecycle tracking
|
||||
- .ralph/.ralph_session_history: Session transition history (last 50)
|
||||
- .ralph/.call_count: API call counter for rate limiting
|
||||
- .ralph/.last_reset: Timestamp of last rate limit reset
|
||||
|
||||
Example workflow:
|
||||
ralph-setup my-project # Create project
|
||||
|
|
|
|||
42
setup.sh
42
setup.sh
|
|
@ -1,6 +1,7 @@
|
|||
#!/bin/bash
|
||||
|
||||
# Ralph Project Setup Script
|
||||
# Creates project structure with Ralph-specific files in .ralph/ subfolder
|
||||
set -e
|
||||
|
||||
PROJECT_NAME=${1:-"my-project"}
|
||||
|
|
@ -11,14 +12,37 @@ echo "🚀 Setting up Ralph project: $PROJECT_NAME"
|
|||
mkdir -p "$PROJECT_NAME"
|
||||
cd "$PROJECT_NAME"
|
||||
|
||||
# Create structure
|
||||
mkdir -p {specs/stdlib,src,examples,logs,docs/generated}
|
||||
# Determine templates directory location (checked AFTER cd into project)
|
||||
# Check local ../templates first, then global ~/.ralph/templates
|
||||
TEMPLATES_DIR=""
|
||||
if [[ -d "../templates" ]]; then
|
||||
TEMPLATES_DIR="../templates"
|
||||
elif [[ -d "$HOME/.ralph/templates" ]]; then
|
||||
TEMPLATES_DIR="$HOME/.ralph/templates"
|
||||
else
|
||||
echo "❌ Error: Templates directory not found."
|
||||
echo " Expected at: ../templates or ~/.ralph/templates"
|
||||
echo " Please run ./install.sh first to install Ralph globally."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Copy templates
|
||||
cp ../templates/PROMPT.md .
|
||||
cp ../templates/fix_plan.md @fix_plan.md
|
||||
cp ../templates/AGENT.md @AGENT.md
|
||||
cp -r ../templates/specs/* specs/ 2>/dev/null || true
|
||||
# Verify required template files exist
|
||||
if [[ ! -f "$TEMPLATES_DIR/PROMPT.md" ]]; then
|
||||
echo "❌ Error: Required template file PROMPT.md not found in $TEMPLATES_DIR"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Create structure:
|
||||
# - src/ stays at root for compatibility with existing tooling
|
||||
# - All Ralph-specific files go in .ralph/ subfolder
|
||||
mkdir -p src
|
||||
mkdir -p .ralph/{specs/stdlib,examples,logs,docs/generated}
|
||||
|
||||
# Copy templates to .ralph/
|
||||
cp "$TEMPLATES_DIR/PROMPT.md" .ralph/
|
||||
cp "$TEMPLATES_DIR/fix_plan.md" .ralph/@fix_plan.md
|
||||
cp "$TEMPLATES_DIR/AGENT.md" .ralph/@AGENT.md
|
||||
cp -r "$TEMPLATES_DIR/specs"/* .ralph/specs/ 2>/dev/null || true
|
||||
|
||||
# Initialize git
|
||||
git init
|
||||
|
|
@ -28,7 +52,7 @@ git commit -m "Initial Ralph project setup"
|
|||
|
||||
echo "✅ Project $PROJECT_NAME created!"
|
||||
echo "Next steps:"
|
||||
echo " 1. Edit PROMPT.md with your project requirements"
|
||||
echo " 2. Update specs/ with your project specifications"
|
||||
echo " 1. Edit .ralph/PROMPT.md with your project requirements"
|
||||
echo " 2. Update .ralph/specs/ with your project specifications"
|
||||
echo " 3. Run: ../ralph_loop.sh"
|
||||
echo " 4. Monitor: ../ralph_monitor.sh"
|
||||
|
|
|
|||
|
|
@ -94,9 +94,9 @@ Before moving to the next feature, ALL changes must be:
|
|||
- Create pull requests for all significant changes
|
||||
|
||||
4. **Ralph Integration**:
|
||||
- Update @fix_plan.md with new tasks before starting work
|
||||
- Mark items complete in @fix_plan.md upon completion
|
||||
- Update PROMPT.md if development patterns change
|
||||
- Update .ralph/@fix_plan.md with new tasks before starting work
|
||||
- Mark items complete in .ralph/@fix_plan.md upon completion
|
||||
- Update .ralph/PROMPT.md if development patterns change
|
||||
- Test features work within Ralph's autonomous loop
|
||||
|
||||
### Documentation Requirements
|
||||
|
|
@ -137,10 +137,10 @@ Before marking ANY feature as complete, verify:
|
|||
- [ ] Type checking passes (if applicable)
|
||||
- [ ] All changes committed with conventional commit messages
|
||||
- [ ] All commits pushed to remote repository
|
||||
- [ ] @fix_plan.md task marked as complete
|
||||
- [ ] .ralph/@fix_plan.md task marked as complete
|
||||
- [ ] Implementation documentation updated
|
||||
- [ ] Inline code comments updated or added
|
||||
- [ ] AGENT.md updated (if new patterns introduced)
|
||||
- [ ] .ralph/@AGENT.md updated (if new patterns introduced)
|
||||
- [ ] Breaking changes documented
|
||||
- [ ] Features tested within Ralph loop (if applicable)
|
||||
- [ ] CI/CD pipeline passes
|
||||
|
|
@ -149,7 +149,7 @@ Before marking ANY feature as complete, verify:
|
|||
|
||||
These standards ensure:
|
||||
- **Quality**: High test coverage and pass rates prevent regressions
|
||||
- **Traceability**: Git commits and @fix_plan.md provide clear history of changes
|
||||
- **Traceability**: Git commits and .ralph/@fix_plan.md provide clear history of changes
|
||||
- **Maintainability**: Current documentation reduces onboarding time and prevents knowledge loss
|
||||
- **Collaboration**: Pushed changes enable team visibility and code review
|
||||
- **Reliability**: Consistent quality gates maintain production stability
|
||||
|
|
|
|||
|
|
@ -4,8 +4,8 @@
|
|||
You are Ralph, an autonomous AI development agent working on a [YOUR PROJECT NAME] project.
|
||||
|
||||
## Current Objectives
|
||||
1. Study specs/* to learn about the project specifications
|
||||
2. Review @fix_plan.md for current priorities
|
||||
1. Study .ralph/specs/* to learn about the project specifications
|
||||
2. Review .ralph/@fix_plan.md for current priorities
|
||||
3. Implement the highest priority item using best practices
|
||||
4. Use parallel subagents for complex tasks (max 100 concurrent)
|
||||
5. Run tests after each implementation
|
||||
|
|
@ -16,7 +16,7 @@ You are Ralph, an autonomous AI development agent working on a [YOUR PROJECT NAM
|
|||
- Search the codebase before assuming something isn't implemented
|
||||
- Use subagents for expensive operations (file searching, analysis)
|
||||
- Write comprehensive tests with clear documentation
|
||||
- Update @fix_plan.md with your learnings
|
||||
- Update .ralph/@fix_plan.md with your learnings
|
||||
- Commit working changes with descriptive messages
|
||||
|
||||
## 🧪 Testing Guidelines (CRITICAL)
|
||||
|
|
@ -31,7 +31,7 @@ You are Ralph, an autonomous AI development agent working on a [YOUR PROJECT NAM
|
|||
- Before making changes: search codebase using subagents
|
||||
- After implementation: run ESSENTIAL tests for the modified code only
|
||||
- If tests fail: fix them as part of your current work
|
||||
- Keep @AGENT.md updated with build/run instructions
|
||||
- Keep .ralph/@AGENT.md updated with build/run instructions
|
||||
- Document the WHY behind tests and implementations
|
||||
- No placeholder implementations - build it properly
|
||||
|
||||
|
|
@ -115,10 +115,10 @@ Each scenario shows the exact conditions and expected behavior.
|
|||
|
||||
### Scenario 1: Successful Project Completion
|
||||
**Given**:
|
||||
- All items in @fix_plan.md are marked [x]
|
||||
- All items in .ralph/@fix_plan.md are marked [x]
|
||||
- Last test run shows all tests passing
|
||||
- No errors in recent logs/
|
||||
- All requirements from specs/ are implemented
|
||||
- All requirements from .ralph/specs/ are implemented
|
||||
|
||||
**When**: You evaluate project status at end of loop
|
||||
|
||||
|
|
@ -193,7 +193,7 @@ RECOMMENDATION: Stuck on [error description] - human intervention needed
|
|||
### Scenario 4: No Work Remaining
|
||||
**Given**:
|
||||
- All tasks in @fix_plan.md are complete
|
||||
- You analyze specs/ and find nothing new to implement
|
||||
- You analyze .ralph/specs/ and find nothing new to implement
|
||||
- Code quality is acceptable
|
||||
- Tests are passing
|
||||
|
||||
|
|
@ -208,7 +208,7 @@ FILES_MODIFIED: 0
|
|||
TESTS_STATUS: PASSING
|
||||
WORK_TYPE: DOCUMENTATION
|
||||
EXIT_SIGNAL: true
|
||||
RECOMMENDATION: No remaining work, all specs implemented
|
||||
RECOMMENDATION: No remaining work, all .ralph/specs implemented
|
||||
---END_RALPH_STATUS---
|
||||
```
|
||||
|
||||
|
|
@ -218,7 +218,7 @@ RECOMMENDATION: No remaining work, all specs implemented
|
|||
|
||||
### Scenario 5: Making Progress
|
||||
**Given**:
|
||||
- Tasks remain in @fix_plan.md
|
||||
- Tasks remain in .ralph/@fix_plan.md
|
||||
- Implementation is underway
|
||||
- Files are being modified
|
||||
- Tests are passing or being fixed
|
||||
|
|
@ -234,7 +234,7 @@ FILES_MODIFIED: 7
|
|||
TESTS_STATUS: PASSING
|
||||
WORK_TYPE: IMPLEMENTATION
|
||||
EXIT_SIGNAL: false
|
||||
RECOMMENDATION: Continue with next task from @fix_plan.md
|
||||
RECOMMENDATION: Continue with next task from .ralph/@fix_plan.md
|
||||
---END_RALPH_STATUS---
|
||||
```
|
||||
|
||||
|
|
@ -268,14 +268,18 @@ RECOMMENDATION: Blocked on [specific dependency] - need [what's needed]
|
|||
---
|
||||
|
||||
## File Structure
|
||||
- specs/: Project specifications and requirements
|
||||
- src/: Source code implementation
|
||||
- .ralph/: Ralph-specific configuration and documentation
|
||||
- specs/: Project specifications and requirements
|
||||
- @fix_plan.md: Prioritized TODO list
|
||||
- @AGENT.md: Project build and run instructions
|
||||
- PROMPT.md: This file - Ralph development instructions
|
||||
- logs/: Loop execution logs
|
||||
- docs/generated/: Auto-generated documentation
|
||||
- src/: Source code implementation
|
||||
- examples/: Example usage and test cases
|
||||
- @fix_plan.md: Prioritized TODO list
|
||||
- @AGENT.md: Project build and run instructions
|
||||
|
||||
## Current Task
|
||||
Follow @fix_plan.md and choose the most important item to implement next.
|
||||
Follow .ralph/@fix_plan.md and choose the most important item to implement next.
|
||||
Use your judgment to prioritize what will have the biggest impact on project progress.
|
||||
|
||||
Remember: Quality over speed. Build it right the first time. Know when you're done.
|
||||
|
|
|
|||
|
|
@ -353,20 +353,25 @@ EOF
|
|||
}
|
||||
|
||||
# Create complete test project structure
|
||||
# Creates .ralph/ subfolder structure for Ralph-specific files
|
||||
create_test_project() {
|
||||
local project_dir=${1:-"test_project"}
|
||||
|
||||
mkdir -p "$project_dir"/{specs/stdlib,src,examples,logs,docs/generated}
|
||||
# Create project with .ralph/ subfolder structure
|
||||
mkdir -p "$project_dir"/src
|
||||
mkdir -p "$project_dir"/.ralph/{specs/stdlib,examples,logs,docs/generated}
|
||||
|
||||
cd "$project_dir" || return 1
|
||||
|
||||
create_sample_prompt "PROMPT.md"
|
||||
create_sample_fix_plan "@fix_plan.md" 10 3
|
||||
create_sample_agent_md "@AGENT.md"
|
||||
# Create Ralph files in .ralph/ subdirectory
|
||||
create_sample_prompt ".ralph/PROMPT.md"
|
||||
create_sample_fix_plan ".ralph/@fix_plan.md" 10 3
|
||||
create_sample_agent_md ".ralph/@AGENT.md"
|
||||
|
||||
echo "0" > .call_count
|
||||
echo "$(date +%Y%m%d%H)" > .last_reset
|
||||
echo '{"test_only_loops": [], "done_signals": [], "completion_indicators": []}' > .exit_signals
|
||||
# Create state files in .ralph/
|
||||
echo "0" > .ralph/.call_count
|
||||
echo "$(date +%Y%m%d%H)" > .ralph/.last_reset
|
||||
echo '{"test_only_loops": [], "done_signals": [], "completion_indicators": []}' > .ralph/.exit_signals
|
||||
|
||||
cd - > /dev/null || return 1
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,12 @@
|
|||
#!/usr/bin/env bash
|
||||
# Test Helper Utilities for Ralph Test Suite
|
||||
|
||||
# Helper: Fail with message (for use in assertions)
|
||||
fail() {
|
||||
echo "$1"
|
||||
return 1
|
||||
}
|
||||
|
||||
# Simple assertion functions (replacing bats-assert)
|
||||
assert_success() {
|
||||
if [ "$status" -ne 0 ]; then
|
||||
|
|
@ -43,18 +49,20 @@ setup() {
|
|||
export TEST_TEMP_DIR="$(mktemp -d "${BATS_TEST_TMPDIR}/test.XXXXXX")"
|
||||
cd "$TEST_TEMP_DIR"
|
||||
|
||||
# Set up test environment variables
|
||||
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"
|
||||
# Set up test environment variables with .ralph/ subfolder structure
|
||||
export RALPH_DIR=".ralph"
|
||||
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 RESPONSE_ANALYSIS_FILE="$RALPH_DIR/.response_analysis"
|
||||
|
||||
# Create necessary directories
|
||||
mkdir -p "$LOG_DIR" "$DOCS_DIR"
|
||||
mkdir -p "$LOG_DIR" "$DOCS_DIR" "$RALPH_DIR"
|
||||
|
||||
# Initialize files
|
||||
echo "0" > "$CALL_COUNT_FILE"
|
||||
|
|
@ -77,6 +85,7 @@ strip_colors() {
|
|||
|
||||
# Helper: Create a mock PROMPT.md file
|
||||
create_mock_prompt() {
|
||||
mkdir -p "$RALPH_DIR"
|
||||
cat > "$PROMPT_FILE" << 'EOF'
|
||||
# Test Prompt
|
||||
This is a test prompt for Ralph.
|
||||
|
|
@ -90,19 +99,21 @@ EOF
|
|||
create_mock_fix_plan() {
|
||||
local total=${1:-5}
|
||||
local completed=${2:-0}
|
||||
local fix_plan_file="$RALPH_DIR/@fix_plan.md"
|
||||
|
||||
cat > "@fix_plan.md" << EOF
|
||||
mkdir -p "$RALPH_DIR"
|
||||
cat > "$fix_plan_file" << EOF
|
||||
# Fix Plan
|
||||
|
||||
## High Priority
|
||||
EOF
|
||||
|
||||
for ((i=1; i<=completed; i++)); do
|
||||
echo "- [x] Completed task $i" >> "@fix_plan.md"
|
||||
echo "- [x] Completed task $i" >> "$fix_plan_file"
|
||||
done
|
||||
|
||||
for ((i=completed+1; i<=total; i++)); do
|
||||
echo "- [ ] Pending task $i" >> "@fix_plan.md"
|
||||
echo "- [ ] Pending task $i" >> "$fix_plan_file"
|
||||
done
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -15,15 +15,16 @@ setup() {
|
|||
git config user.email "test@example.com"
|
||||
git config user.name "Test User"
|
||||
|
||||
# Set up environment
|
||||
export PROMPT_FILE="PROMPT.md"
|
||||
export LOG_DIR="logs"
|
||||
export DOCS_DIR="docs/generated"
|
||||
export STATUS_FILE="status.json"
|
||||
export EXIT_SIGNALS_FILE=".exit_signals"
|
||||
export CALL_COUNT_FILE=".call_count"
|
||||
export TIMESTAMP_FILE=".last_reset"
|
||||
export CLAUDE_SESSION_FILE=".claude_session_id"
|
||||
# Set up environment with .ralph/ subfolder structure
|
||||
export RALPH_DIR=".ralph"
|
||||
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 EXIT_SIGNALS_FILE="$RALPH_DIR/.exit_signals"
|
||||
export CALL_COUNT_FILE="$RALPH_DIR/.call_count"
|
||||
export TIMESTAMP_FILE="$RALPH_DIR/.last_reset"
|
||||
export CLAUDE_SESSION_FILE="$RALPH_DIR/.claude_session_id"
|
||||
export CLAUDE_MIN_VERSION="2.0.76"
|
||||
export CLAUDE_CODE_CMD="claude"
|
||||
|
||||
|
|
@ -34,7 +35,7 @@ setup() {
|
|||
|
||||
# Create sample project files
|
||||
create_sample_prompt
|
||||
create_sample_fix_plan "@fix_plan.md" 10 3
|
||||
create_sample_fix_plan "$RALPH_DIR/@fix_plan.md" 10 3
|
||||
|
||||
# Source library components
|
||||
source "${BATS_TEST_DIRNAME}/../../lib/date_utils.sh"
|
||||
|
|
@ -92,20 +93,20 @@ setup() {
|
|||
|
||||
context="Loop #${loop_count}. "
|
||||
|
||||
if [[ -f "@fix_plan.md" ]]; then
|
||||
local incomplete_tasks=$(grep -c "^- \[ \]" "@fix_plan.md" 2>/dev/null || echo "0")
|
||||
if [[ -f "$RALPH_DIR/@fix_plan.md" ]]; then
|
||||
local incomplete_tasks=$(grep -c "^- \[ \]" "$RALPH_DIR/@fix_plan.md" 2>/dev/null || echo "0")
|
||||
context+="Remaining tasks: ${incomplete_tasks}. "
|
||||
fi
|
||||
|
||||
if [[ -f ".circuit_breaker_state" ]]; then
|
||||
local cb_state=$(jq -r '.state // "UNKNOWN"' .circuit_breaker_state 2>/dev/null)
|
||||
if [[ -f "$RALPH_DIR/.circuit_breaker_state" ]]; then
|
||||
local cb_state=$(jq -r '.state // "UNKNOWN"' "$RALPH_DIR/.circuit_breaker_state" 2>/dev/null)
|
||||
if [[ "$cb_state" != "CLOSED" && "$cb_state" != "null" && -n "$cb_state" ]]; then
|
||||
context+="Circuit breaker: ${cb_state}. "
|
||||
fi
|
||||
fi
|
||||
|
||||
if [[ -f ".response_analysis" ]]; then
|
||||
local prev_summary=$(jq -r '.analysis.work_summary // ""' .response_analysis 2>/dev/null | head -c 200)
|
||||
if [[ -f "$RALPH_DIR/.response_analysis" ]]; then
|
||||
local prev_summary=$(jq -r '.analysis.work_summary // ""' "$RALPH_DIR/.response_analysis" 2>/dev/null | head -c 200)
|
||||
if [[ -n "$prev_summary" && "$prev_summary" != "null" ]]; then
|
||||
context+="Previous: ${prev_summary}"
|
||||
fi
|
||||
|
|
@ -217,8 +218,8 @@ teardown() {
|
|||
}
|
||||
|
||||
@test "build_loop_context counts remaining tasks from @fix_plan.md" {
|
||||
# Create fix plan with 7 incomplete tasks
|
||||
cat > "@fix_plan.md" << 'EOF'
|
||||
# Create fix plan with 7 incomplete tasks in .ralph/ directory
|
||||
cat > "$RALPH_DIR/@fix_plan.md" << 'EOF'
|
||||
# Fix Plan
|
||||
- [x] Task 1 done
|
||||
- [x] Task 2 done
|
||||
|
|
@ -252,7 +253,7 @@ EOF
|
|||
|
||||
@test "build_loop_context includes previous loop summary" {
|
||||
# Create previous response analysis
|
||||
cat > ".response_analysis" << 'EOF'
|
||||
cat > "$RALPH_DIR/.response_analysis" << 'EOF'
|
||||
{
|
||||
"loop_number": 1,
|
||||
"analysis": {
|
||||
|
|
@ -270,7 +271,7 @@ EOF
|
|||
@test "build_loop_context limits output length to 500 chars" {
|
||||
# Create very long work summary
|
||||
local long_summary=$(printf 'x%.0s' {1..1000})
|
||||
cat > ".response_analysis" << EOF
|
||||
cat > "$RALPH_DIR/.response_analysis" << EOF
|
||||
{
|
||||
"loop_number": 1,
|
||||
"analysis": {
|
||||
|
|
@ -286,7 +287,7 @@ EOF
|
|||
}
|
||||
|
||||
@test "build_loop_context handles missing @fix_plan.md gracefully" {
|
||||
rm -f "@fix_plan.md"
|
||||
rm -f "$RALPH_DIR/@fix_plan.md"
|
||||
|
||||
run build_loop_context 1
|
||||
|
||||
|
|
@ -295,7 +296,7 @@ EOF
|
|||
}
|
||||
|
||||
@test "build_loop_context handles missing .response_analysis gracefully" {
|
||||
rm -f ".response_analysis"
|
||||
rm -f "$RALPH_DIR/.response_analysis"
|
||||
|
||||
run build_loop_context 1
|
||||
|
||||
|
|
|
|||
|
|
@ -19,13 +19,14 @@ setup() {
|
|||
git config user.email "test@example.com"
|
||||
git config user.name "Test User"
|
||||
|
||||
# Set up required environment
|
||||
export PROMPT_FILE="PROMPT.md"
|
||||
export LOG_DIR="logs"
|
||||
export STATUS_FILE="status.json"
|
||||
export EXIT_SIGNALS_FILE=".exit_signals"
|
||||
export CALL_COUNT_FILE=".call_count"
|
||||
export TIMESTAMP_FILE=".last_reset"
|
||||
# Set up required environment with .ralph/ subfolder structure
|
||||
export RALPH_DIR=".ralph"
|
||||
export PROMPT_FILE="$RALPH_DIR/PROMPT.md"
|
||||
export LOG_DIR="$RALPH_DIR/logs"
|
||||
export STATUS_FILE="$RALPH_DIR/status.json"
|
||||
export EXIT_SIGNALS_FILE="$RALPH_DIR/.exit_signals"
|
||||
export CALL_COUNT_FILE="$RALPH_DIR/.call_count"
|
||||
export TIMESTAMP_FILE="$RALPH_DIR/.last_reset"
|
||||
|
||||
mkdir -p "$LOG_DIR"
|
||||
|
||||
|
|
@ -38,6 +39,7 @@ setup() {
|
|||
# Create lib directory with circuit breaker stub
|
||||
mkdir -p lib
|
||||
cat > lib/circuit_breaker.sh << 'EOF'
|
||||
RALPH_DIR="${RALPH_DIR:-.ralph}"
|
||||
reset_circuit_breaker() { echo "Circuit breaker reset: $1"; }
|
||||
show_circuit_status() { echo "Circuit breaker status: CLOSED"; }
|
||||
init_circuit_breaker() { :; }
|
||||
|
|
@ -45,6 +47,7 @@ record_loop_result() { :; }
|
|||
EOF
|
||||
|
||||
cat > lib/response_analyzer.sh << 'EOF'
|
||||
RALPH_DIR="${RALPH_DIR:-.ralph}"
|
||||
analyze_response() { :; }
|
||||
detect_output_format() { echo "text"; }
|
||||
EOF
|
||||
|
|
|
|||
|
|
@ -7,15 +7,17 @@ setup() {
|
|||
# Source helper functions
|
||||
source "$(dirname "$BATS_TEST_FILENAME")/../helpers/test_helper.bash"
|
||||
|
||||
# Set up environment
|
||||
export EXIT_SIGNALS_FILE=".exit_signals"
|
||||
export RESPONSE_ANALYSIS_FILE=".response_analysis"
|
||||
# Set up environment with .ralph/ subfolder structure
|
||||
export RALPH_DIR=".ralph"
|
||||
export EXIT_SIGNALS_FILE="$RALPH_DIR/.exit_signals"
|
||||
export RESPONSE_ANALYSIS_FILE="$RALPH_DIR/.response_analysis"
|
||||
export MAX_CONSECUTIVE_TEST_LOOPS=3
|
||||
export MAX_CONSECUTIVE_DONE_SIGNALS=2
|
||||
|
||||
# Create temp test directory
|
||||
export TEST_TEMP_DIR="$(mktemp -d /tmp/ralph-test.XXXXXX)"
|
||||
cd "$TEST_TEMP_DIR"
|
||||
mkdir -p "$RALPH_DIR"
|
||||
|
||||
# Initialize exit signals file
|
||||
echo '{"test_only_loops": [], "done_signals": [], "completion_indicators": []}' > "$EXIT_SIGNALS_FILE"
|
||||
|
|
@ -73,9 +75,9 @@ should_exit_gracefully() {
|
|||
fi
|
||||
|
||||
# 4. Check fix_plan.md for completion
|
||||
if [[ -f "@fix_plan.md" ]]; then
|
||||
local total_items=$(grep -c "^- \[" "@fix_plan.md" 2>/dev/null)
|
||||
local completed_items=$(grep -c "^- \[x\]" "@fix_plan.md" 2>/dev/null)
|
||||
if [[ -f "$RALPH_DIR/@fix_plan.md" ]]; then
|
||||
local total_items=$(grep -c "^- \[" "$RALPH_DIR/@fix_plan.md" 2>/dev/null)
|
||||
local completed_items=$(grep -c "^- \[x\]" "$RALPH_DIR/@fix_plan.md" 2>/dev/null)
|
||||
|
||||
# Handle case where grep returns no matches (exit code 1)
|
||||
[[ -z "$total_items" ]] && total_items=0
|
||||
|
|
@ -176,7 +178,7 @@ EOF
|
|||
|
||||
# Test 10: Exit when @fix_plan.md all items complete
|
||||
@test "should_exit_gracefully exits when all fix_plan items complete" {
|
||||
cat > "@fix_plan.md" << 'EOF'
|
||||
cat > "$RALPH_DIR/@fix_plan.md" << 'EOF'
|
||||
# Fix Plan
|
||||
- [x] Task 1
|
||||
- [x] Task 2
|
||||
|
|
@ -189,7 +191,7 @@ EOF
|
|||
|
||||
# Test 11: No exit when @fix_plan.md partially complete
|
||||
@test "should_exit_gracefully continues when fix_plan partially complete" {
|
||||
cat > "@fix_plan.md" << 'EOF'
|
||||
cat > "$RALPH_DIR/@fix_plan.md" << 'EOF'
|
||||
# Fix Plan
|
||||
- [x] Task 1
|
||||
- [ ] Task 2
|
||||
|
|
@ -236,7 +238,7 @@ EOF
|
|||
|
||||
# Test 16: @fix_plan.md with no checkboxes
|
||||
@test "should_exit_gracefully handles fix_plan with no checkboxes" {
|
||||
cat > "@fix_plan.md" << 'EOF'
|
||||
cat > "$RALPH_DIR/@fix_plan.md" << 'EOF'
|
||||
# Fix Plan
|
||||
This is just text, no tasks yet.
|
||||
EOF
|
||||
|
|
@ -247,7 +249,7 @@ EOF
|
|||
|
||||
# Test 17: @fix_plan.md with mixed checkbox formats
|
||||
@test "should_exit_gracefully handles mixed checkbox formats" {
|
||||
cat > "@fix_plan.md" << 'EOF'
|
||||
cat > "$RALPH_DIR/@fix_plan.md" << 'EOF'
|
||||
# Fix Plan
|
||||
- [x] Task 1 completed
|
||||
- [ ] Task 2 pending
|
||||
|
|
|
|||
|
|
@ -15,12 +15,13 @@ setup() {
|
|||
git config user.email "test@example.com"
|
||||
git config user.name "Test User"
|
||||
|
||||
# Set up environment
|
||||
export PROMPT_FILE="PROMPT.md"
|
||||
export LOG_DIR="logs"
|
||||
export DOCS_DIR="docs/generated"
|
||||
export STATUS_FILE="status.json"
|
||||
export EXIT_SIGNALS_FILE=".exit_signals"
|
||||
# Set up environment with .ralph/ subfolder structure
|
||||
export RALPH_DIR=".ralph"
|
||||
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 EXIT_SIGNALS_FILE="$RALPH_DIR/.exit_signals"
|
||||
|
||||
mkdir -p "$LOG_DIR" "$DOCS_DIR"
|
||||
echo '{"test_only_loops": [], "done_signals": [], "completion_indicators": []}' > "$EXIT_SIGNALS_FILE"
|
||||
|
|
@ -124,7 +125,7 @@ EOF
|
|||
EOF
|
||||
|
||||
run parse_json_response "$output_file"
|
||||
local result_file=".json_parse_result"
|
||||
local result_file="$RALPH_DIR/.json_parse_result"
|
||||
|
||||
# Should create result file with parsed values
|
||||
[[ -f "$result_file" ]] || skip "parse_json_response not yet implemented"
|
||||
|
|
@ -145,7 +146,7 @@ EOF
|
|||
EOF
|
||||
|
||||
run parse_json_response "$output_file"
|
||||
local result_file=".json_parse_result"
|
||||
local result_file="$RALPH_DIR/.json_parse_result"
|
||||
|
||||
[[ -f "$result_file" ]] || skip "parse_json_response not yet implemented"
|
||||
|
||||
|
|
@ -166,7 +167,7 @@ EOF
|
|||
EOF
|
||||
|
||||
run parse_json_response "$output_file"
|
||||
local result_file=".json_parse_result"
|
||||
local result_file="$RALPH_DIR/.json_parse_result"
|
||||
|
||||
[[ -f "$result_file" ]] || skip "parse_json_response not yet implemented"
|
||||
|
||||
|
|
@ -187,7 +188,7 @@ EOF
|
|||
EOF
|
||||
|
||||
run parse_json_response "$output_file"
|
||||
local result_file=".json_parse_result"
|
||||
local result_file="$RALPH_DIR/.json_parse_result"
|
||||
|
||||
[[ -f "$result_file" ]] || skip "parse_json_response not yet implemented"
|
||||
|
||||
|
|
@ -207,7 +208,7 @@ EOF
|
|||
EOF
|
||||
|
||||
run parse_json_response "$output_file"
|
||||
local result_file=".json_parse_result"
|
||||
local result_file="$RALPH_DIR/.json_parse_result"
|
||||
|
||||
[[ -f "$result_file" ]] || skip "parse_json_response not yet implemented"
|
||||
|
||||
|
|
@ -228,7 +229,7 @@ EOF
|
|||
EOF
|
||||
|
||||
run parse_json_response "$output_file"
|
||||
local result_file=".json_parse_result"
|
||||
local result_file="$RALPH_DIR/.json_parse_result"
|
||||
|
||||
[[ -f "$result_file" ]] || skip "parse_json_response not yet implemented"
|
||||
|
||||
|
|
@ -248,7 +249,7 @@ EOF
|
|||
EOF
|
||||
|
||||
run parse_json_response "$output_file"
|
||||
local result_file=".json_parse_result"
|
||||
local result_file="$RALPH_DIR/.json_parse_result"
|
||||
|
||||
[[ -f "$result_file" ]] || skip "parse_json_response not yet implemented"
|
||||
|
||||
|
|
@ -271,7 +272,7 @@ EOF
|
|||
EOF
|
||||
|
||||
run parse_json_response "$output_file"
|
||||
local result_file=".json_parse_result"
|
||||
local result_file="$RALPH_DIR/.json_parse_result"
|
||||
|
||||
[[ -f "$result_file" ]] || skip "parse_json_response not yet implemented"
|
||||
|
||||
|
|
@ -312,7 +313,7 @@ EOF
|
|||
EOF
|
||||
|
||||
run parse_json_response "$output_file"
|
||||
local result_file=".json_parse_result"
|
||||
local result_file="$RALPH_DIR/.json_parse_result"
|
||||
|
||||
[[ -f "$result_file" ]] || skip "parse_json_response not yet implemented"
|
||||
|
||||
|
|
@ -342,9 +343,9 @@ EOF
|
|||
local result=$?
|
||||
|
||||
assert_equal "$result" "0"
|
||||
assert_file_exists ".response_analysis"
|
||||
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"
|
||||
}
|
||||
|
||||
|
|
@ -361,10 +362,10 @@ EOF
|
|||
local result=$?
|
||||
|
||||
assert_equal "$result" "0"
|
||||
assert_file_exists ".response_analysis"
|
||||
assert_file_exists "$RALPH_DIR/.response_analysis"
|
||||
|
||||
# Should still detect completion via text parsing
|
||||
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"
|
||||
}
|
||||
|
||||
|
|
@ -382,7 +383,7 @@ EOF
|
|||
analyze_response "$output_file" 1
|
||||
|
||||
# JSON with explicit exit_signal should have high confidence
|
||||
local confidence=$(jq -r '.analysis.confidence_score' .response_analysis)
|
||||
local confidence=$(jq -r '.analysis.confidence_score' "$RALPH_DIR/.response_analysis")
|
||||
[[ "$confidence" -ge 50 ]]
|
||||
}
|
||||
|
||||
|
|
@ -405,10 +406,10 @@ EOF
|
|||
|
||||
analyze_response "$output_file" 1
|
||||
|
||||
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" -ge 100 ]]
|
||||
}
|
||||
|
||||
|
|
@ -423,7 +424,7 @@ EOF
|
|||
|
||||
analyze_response "$output_file" 1
|
||||
|
||||
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"
|
||||
}
|
||||
|
||||
|
|
@ -438,7 +439,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"
|
||||
}
|
||||
|
||||
|
|
@ -480,7 +481,7 @@ EOF
|
|||
EOF
|
||||
|
||||
run parse_json_response "$output_file"
|
||||
local result_file=".json_parse_result"
|
||||
local result_file="$RALPH_DIR/.json_parse_result"
|
||||
|
||||
[[ -f "$result_file" ]] || skip "parse_json_response not yet implemented"
|
||||
|
||||
|
|
@ -500,7 +501,7 @@ EOF
|
|||
EOF
|
||||
|
||||
run parse_json_response "$output_file"
|
||||
local result_file=".json_parse_result"
|
||||
local result_file="$RALPH_DIR/.json_parse_result"
|
||||
|
||||
[[ -f "$result_file" ]] || skip "parse_json_response not yet implemented"
|
||||
|
||||
|
|
@ -523,7 +524,7 @@ EOF
|
|||
EOF
|
||||
|
||||
run parse_json_response "$output_file"
|
||||
local result_file=".json_parse_result"
|
||||
local result_file="$RALPH_DIR/.json_parse_result"
|
||||
|
||||
[[ -f "$result_file" ]] || skip "parse_json_response not yet implemented"
|
||||
|
||||
|
|
@ -546,7 +547,7 @@ EOF
|
|||
EOF
|
||||
|
||||
run parse_json_response "$output_file"
|
||||
local result_file=".json_parse_result"
|
||||
local result_file="$RALPH_DIR/.json_parse_result"
|
||||
|
||||
[[ -f "$result_file" ]] || skip "parse_json_response not yet implemented"
|
||||
|
||||
|
|
@ -573,7 +574,7 @@ EOF
|
|||
EOF
|
||||
|
||||
run parse_json_response "$output_file"
|
||||
local result_file=".json_parse_result"
|
||||
local result_file="$RALPH_DIR/.json_parse_result"
|
||||
|
||||
[[ -f "$result_file" ]] || skip "parse_json_response not yet implemented"
|
||||
|
||||
|
|
@ -597,7 +598,7 @@ EOF
|
|||
EOF
|
||||
|
||||
run parse_json_response "$output_file"
|
||||
local result_file=".json_parse_result"
|
||||
local result_file="$RALPH_DIR/.json_parse_result"
|
||||
|
||||
[[ -f "$result_file" ]] || skip "parse_json_response not yet implemented"
|
||||
|
||||
|
|
@ -623,7 +624,7 @@ EOF
|
|||
EOF
|
||||
|
||||
run parse_json_response "$output_file"
|
||||
local result_file=".json_parse_result"
|
||||
local result_file="$RALPH_DIR/.json_parse_result"
|
||||
|
||||
[[ -f "$result_file" ]] || skip "parse_json_response not yet implemented"
|
||||
|
||||
|
|
@ -648,12 +649,12 @@ EOF
|
|||
|
||||
analyze_response "$output_file" 1
|
||||
|
||||
assert_file_exists ".response_analysis"
|
||||
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 output_format=$(jq -r '.output_format' .response_analysis)
|
||||
local output_format=$(jq -r '.output_format' "$RALPH_DIR/.response_analysis")
|
||||
assert_equal "$output_format" "json"
|
||||
}
|
||||
|
||||
|
|
@ -670,9 +671,9 @@ EOF
|
|||
analyze_response "$output_file" 1
|
||||
|
||||
# Session ID should be persisted for continuity
|
||||
[[ -f ".claude_session_id" ]] || skip "Session persistence not yet implemented"
|
||||
[[ -f "$RALPH_DIR/.claude_session_id" ]] || skip "Session persistence not yet implemented"
|
||||
|
||||
local stored_session=$(cat .claude_session_id)
|
||||
local stored_session=$(cat "$RALPH_DIR/.claude_session_id")
|
||||
[[ "$stored_session" == *"session-persist-test-123"* ]]
|
||||
}
|
||||
|
||||
|
|
@ -683,15 +684,15 @@ EOF
|
|||
@test "store_session_id writes session to file with timestamp" {
|
||||
run store_session_id "session-test-abc"
|
||||
|
||||
[[ -f ".claude_session_id" ]] || skip "store_session_id not yet implemented"
|
||||
[[ -f "$RALPH_DIR/.claude_session_id" ]] || skip "store_session_id not yet implemented"
|
||||
|
||||
local content=$(cat .claude_session_id)
|
||||
local content=$(cat "$RALPH_DIR/.claude_session_id")
|
||||
[[ "$content" == *"session-test-abc"* ]]
|
||||
}
|
||||
|
||||
@test "get_last_session_id retrieves stored session" {
|
||||
# First store a session
|
||||
echo '{"session_id": "session-retrieve-test", "timestamp": "2026-01-09T10:00:00Z"}' > .claude_session_id
|
||||
echo '{"session_id": "session-retrieve-test", "timestamp": "2026-01-09T10:00:00Z"}' > "$RALPH_DIR/.claude_session_id"
|
||||
|
||||
run get_last_session_id
|
||||
|
||||
|
|
@ -699,7 +700,7 @@ EOF
|
|||
}
|
||||
|
||||
@test "get_last_session_id returns empty when no session file" {
|
||||
rm -f .claude_session_id
|
||||
rm -f "$RALPH_DIR/.claude_session_id"
|
||||
|
||||
run get_last_session_id
|
||||
|
||||
|
|
@ -711,7 +712,7 @@ EOF
|
|||
@test "should_resume_session returns true for recent session" {
|
||||
# Store a recent session (simulated as current timestamp)
|
||||
local now=$(date +%s)
|
||||
echo "{\"session_id\": \"session-recent\", \"timestamp\": \"$(date -Iseconds)\"}" > .claude_session_id
|
||||
echo "{\"session_id\": \"session-recent\", \"timestamp\": \"$(date -Iseconds)\"}" > "$RALPH_DIR/.claude_session_id"
|
||||
|
||||
run should_resume_session
|
||||
|
||||
|
|
@ -721,7 +722,7 @@ EOF
|
|||
|
||||
@test "should_resume_session returns false for old session" {
|
||||
# Store an old session (24+ hours ago)
|
||||
echo '{"session_id": "session-old", "timestamp": "2020-01-01T00:00:00Z"}' > .claude_session_id
|
||||
echo '{"session_id": "session-old", "timestamp": "2020-01-01T00:00:00Z"}' > "$RALPH_DIR/.claude_session_id"
|
||||
|
||||
run should_resume_session
|
||||
|
||||
|
|
@ -730,7 +731,7 @@ EOF
|
|||
}
|
||||
|
||||
@test "should_resume_session returns false when no session file" {
|
||||
rm -f .claude_session_id
|
||||
rm -f "$RALPH_DIR/.claude_session_id"
|
||||
|
||||
run should_resume_session
|
||||
|
||||
|
|
|
|||
|
|
@ -8,14 +8,16 @@ setup() {
|
|||
# Source helper functions
|
||||
source "$(dirname "$BATS_TEST_FILENAME")/../helpers/test_helper.bash"
|
||||
|
||||
# Set up environment
|
||||
# Set up environment with .ralph/ subfolder structure
|
||||
export RALPH_DIR=".ralph"
|
||||
export MAX_CALLS_PER_HOUR=100
|
||||
export CALL_COUNT_FILE=".call_count"
|
||||
export TIMESTAMP_FILE=".last_reset"
|
||||
export CALL_COUNT_FILE="$RALPH_DIR/.call_count"
|
||||
export TIMESTAMP_FILE="$RALPH_DIR/.last_reset"
|
||||
|
||||
# Create temp test directory
|
||||
export TEST_TEMP_DIR="$(mktemp -d /tmp/ralph-test.XXXXXX)"
|
||||
cd "$TEST_TEMP_DIR"
|
||||
mkdir -p "$RALPH_DIR"
|
||||
|
||||
# Initialize files
|
||||
echo "0" > "$CALL_COUNT_FILE"
|
||||
|
|
|
|||
|
|
@ -15,17 +15,18 @@ setup() {
|
|||
git config user.email "test@example.com"
|
||||
git config user.name "Test User"
|
||||
|
||||
# Set up environment
|
||||
export PROMPT_FILE="PROMPT.md"
|
||||
export LOG_DIR="logs"
|
||||
export DOCS_DIR="docs/generated"
|
||||
export STATUS_FILE="status.json"
|
||||
export EXIT_SIGNALS_FILE=".exit_signals"
|
||||
export CALL_COUNT_FILE=".call_count"
|
||||
export TIMESTAMP_FILE=".last_reset"
|
||||
export CLAUDE_SESSION_FILE=".claude_session_id"
|
||||
export RALPH_SESSION_FILE=".ralph_session"
|
||||
export RALPH_SESSION_HISTORY_FILE=".ralph_session_history"
|
||||
# Set up environment with .ralph/ subfolder structure
|
||||
export RALPH_DIR=".ralph"
|
||||
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 EXIT_SIGNALS_FILE="$RALPH_DIR/.exit_signals"
|
||||
export CALL_COUNT_FILE="$RALPH_DIR/.call_count"
|
||||
export TIMESTAMP_FILE="$RALPH_DIR/.last_reset"
|
||||
export CLAUDE_SESSION_FILE="$RALPH_DIR/.claude_session_id"
|
||||
export RALPH_SESSION_FILE="$RALPH_DIR/.ralph_session"
|
||||
export RALPH_SESSION_HISTORY_FILE="$RALPH_DIR/.ralph_session_history"
|
||||
export CLAUDE_MIN_VERSION="2.0.76"
|
||||
export CLAUDE_CODE_CMD="claude"
|
||||
export CLAUDE_USE_CONTINUE="true"
|
||||
|
|
@ -35,9 +36,9 @@ setup() {
|
|||
echo "$(date +%Y%m%d%H)" > "$TIMESTAMP_FILE"
|
||||
echo '{"test_only_loops": [], "done_signals": [], "completion_indicators": []}' > "$EXIT_SIGNALS_FILE"
|
||||
|
||||
# Create sample project files
|
||||
create_sample_prompt
|
||||
create_sample_fix_plan "@fix_plan.md" 10 3
|
||||
# Create sample project files in .ralph/ directory
|
||||
create_sample_prompt "$RALPH_DIR/PROMPT.md"
|
||||
create_sample_fix_plan "$RALPH_DIR/@fix_plan.md" 10 3
|
||||
|
||||
# Source library components
|
||||
source "${BATS_TEST_DIRNAME}/../../lib/date_utils.sh"
|
||||
|
|
@ -238,7 +239,7 @@ function_exists_in_ralph() {
|
|||
EOF
|
||||
|
||||
run parse_json_response "$output_file"
|
||||
local result_file=".json_parse_result"
|
||||
local result_file="$RALPH_DIR/.json_parse_result"
|
||||
|
||||
[[ -f "$result_file" ]]
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue