Merge pull request #67 from frankbria/feature/import-modern-cli
feat(import): modernize ralph_import.sh with JSON output parsing
This commit is contained in:
commit
1c72010a93
4 changed files with 879 additions and 51 deletions
27
CLAUDE.md
27
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.
|
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.7 | **Tests**: 265 passing (100% pass rate) | **CI/CD**: GitHub Actions
|
**Version**: v0.9.8 | **Tests**: 276 passing (100% pass rate) | **CI/CD**: GitHub Actions
|
||||||
|
|
||||||
## Core Architecture
|
## Core Architecture
|
||||||
|
|
||||||
|
|
@ -19,6 +19,9 @@ The system consists of four main bash scripts and a modular library system:
|
||||||
3. **setup.sh** - Project initialization script for new Ralph projects
|
3. **setup.sh** - Project initialization script for new Ralph projects
|
||||||
4. **create_files.sh** - Bootstrap script that creates the entire Ralph system
|
4. **create_files.sh** - Bootstrap script that creates the entire Ralph system
|
||||||
5. **ralph_import.sh** - PRD/specification import tool that converts documents to Ralph format
|
5. **ralph_import.sh** - PRD/specification import tool that converts documents to Ralph format
|
||||||
|
- Uses modern Claude Code CLI with `--output-format json` for structured responses
|
||||||
|
- Implements `detect_response_format()` and `parse_conversion_response()` for JSON parsing
|
||||||
|
- Backward compatible with older CLI versions (automatic text fallback)
|
||||||
|
|
||||||
### Library Components (lib/)
|
### Library Components (lib/)
|
||||||
|
|
||||||
|
|
@ -295,7 +298,7 @@ Ralph uses advanced error detection with two-stage filtering to eliminate false
|
||||||
| `test_edge_cases.bats` | 20 | Edge case handling |
|
| `test_edge_cases.bats` | 20 | Edge case handling |
|
||||||
| `test_installation.bats` | 14 | Global installation/uninstall workflows |
|
| `test_installation.bats` | 14 | Global installation/uninstall workflows |
|
||||||
| `test_project_setup.bats` | 36 | Project setup (setup.sh) validation |
|
| `test_project_setup.bats` | 36 | Project setup (setup.sh) validation |
|
||||||
| `test_prd_import.bats` | 22 | PRD import (ralph_import.sh) workflows |
|
| `test_prd_import.bats` | 33 | PRD import (ralph_import.sh) workflows + modern CLI tests |
|
||||||
|
|
||||||
### Running Tests
|
### Running Tests
|
||||||
```bash
|
```bash
|
||||||
|
|
@ -311,6 +314,26 @@ bats tests/unit/test_cli_parsing.bats
|
||||||
|
|
||||||
## Recent Improvements
|
## Recent Improvements
|
||||||
|
|
||||||
|
### 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
|
||||||
|
- Implemented `detect_response_format()` for JSON vs text detection
|
||||||
|
- Implemented `parse_conversion_response()` for extracting JSON fields
|
||||||
|
- Enhanced error handling with structured JSON error messages
|
||||||
|
- Extracts `error_message` and `error_code` from JSON metadata
|
||||||
|
- Provides specific, actionable feedback on conversion failures
|
||||||
|
- Improved file verification with JSON-derived status information
|
||||||
|
- Reports files created vs missing based on JSON metadata
|
||||||
|
- Logs session ID for potential conversion continuation
|
||||||
|
- Backward compatibility with older CLI versions
|
||||||
|
- Automatic fallback to text-based parsing when JSON unavailable
|
||||||
|
- Version detection with `check_claude_version()` function
|
||||||
|
- Enhanced logging with modern CLI awareness
|
||||||
|
- Reports which CLI mode is being used
|
||||||
|
- Detailed file creation status reporting
|
||||||
|
- Added 11 new tests for modern CLI features (tests 23-33)
|
||||||
|
- Test count: 276 (up from 265)
|
||||||
|
|
||||||
### Session Lifecycle Management (v0.9.7)
|
### Session Lifecycle Management (v0.9.7)
|
||||||
- Added complete session lifecycle management with automatic reset triggers:
|
- Added complete session lifecycle management with automatic reset triggers:
|
||||||
- `get_session_id()` - Retrieves current session from `.ralph_session`
|
- `get_session_id()` - Retrieves current session from `.ralph_session`
|
||||||
|
|
|
||||||
40
README.md
40
README.md
|
|
@ -1,8 +1,8 @@
|
||||||
# Ralph for Claude Code
|
# Ralph for Claude Code
|
||||||
|
|
||||||

|

|
||||||

|

|
||||||

|

|
||||||

|

|
||||||
[](https://github.com/hesreallyhim/awesome-claude-code)
|
[](https://github.com/hesreallyhim/awesome-claude-code)
|
||||||
[](https://x.com/FrankBria18044)
|
[](https://x.com/FrankBria18044)
|
||||||
|
|
@ -15,9 +15,9 @@ Ralph is an implementation of the Geoffrey Huntley's technique for Claude Code t
|
||||||
|
|
||||||
## Project Status
|
## Project Status
|
||||||
|
|
||||||
**Version**: v0.9.3 - Active Development
|
**Version**: v0.9.8 - Active Development
|
||||||
**Core Features**: Working and tested
|
**Core Features**: Working and tested
|
||||||
**Test Coverage**: 165 tests, 100% pass rate
|
**Test Coverage**: 276 tests, 100% pass rate
|
||||||
|
|
||||||
### What's Working Now
|
### What's Working Now
|
||||||
- Autonomous development loops with intelligent exit detection
|
- Autonomous development loops with intelligent exit detection
|
||||||
|
|
@ -32,7 +32,7 @@ Ralph is an implementation of the Geoffrey Huntley's technique for Claude Code t
|
||||||
- tmux integration for live monitoring
|
- tmux integration for live monitoring
|
||||||
- PRD import functionality
|
- PRD import functionality
|
||||||
- **CI/CD pipeline with GitHub Actions**
|
- **CI/CD pipeline with GitHub Actions**
|
||||||
- 165 passing tests across 8 test files
|
- 276 passing tests across 11 test files
|
||||||
|
|
||||||
### Recent Improvements
|
### Recent Improvements
|
||||||
|
|
||||||
|
|
@ -219,6 +219,17 @@ Ralph-import creates a complete project with:
|
||||||
|
|
||||||
The conversion is intelligent and preserves your original requirements while making them actionable for autonomous development.
|
The conversion is intelligent and preserves your original requirements while making them actionable for autonomous development.
|
||||||
|
|
||||||
|
### Modern CLI Features (v0.9.8)
|
||||||
|
|
||||||
|
Ralph-import uses modern Claude Code CLI features for improved reliability:
|
||||||
|
|
||||||
|
- **JSON Output Format**: Structured responses enable precise parsing of conversion results
|
||||||
|
- **Automatic Fallback**: Gracefully handles older CLI versions with text-based parsing
|
||||||
|
- **Enhanced Error Reporting**: Extracts specific error messages and codes from JSON responses
|
||||||
|
- **Session Tracking**: Captures session IDs for potential continuation of interrupted conversions
|
||||||
|
|
||||||
|
> **Note**: These features require Claude Code CLI version 2.0.76 or later. Older versions will work with standard text output.
|
||||||
|
|
||||||
## Configuration
|
## Configuration
|
||||||
|
|
||||||
### Rate Limiting & Circuit Breaker
|
### Rate Limiting & Circuit Breaker
|
||||||
|
|
@ -361,7 +372,7 @@ If you want to run the test suite:
|
||||||
# Install BATS testing framework
|
# Install BATS testing framework
|
||||||
npm install -g bats bats-support bats-assert
|
npm install -g bats bats-support bats-assert
|
||||||
|
|
||||||
# Run all tests (165 tests)
|
# Run all tests (276 tests)
|
||||||
npm test
|
npm test
|
||||||
|
|
||||||
# Run specific test suites
|
# Run specific test suites
|
||||||
|
|
@ -378,8 +389,8 @@ bats tests/integration/test_loop_execution.bats
|
||||||
```
|
```
|
||||||
|
|
||||||
Current test status:
|
Current test status:
|
||||||
- **165 tests** across 8 test files
|
- **276 tests** across 11 test files
|
||||||
- **100% pass rate** (165/165 passing)
|
- **100% pass rate** (276/276 passing)
|
||||||
- Comprehensive unit and integration tests
|
- Comprehensive unit and integration tests
|
||||||
- Specialized tests for JSON parsing, CLI flags, circuit breaker, and installation workflows
|
- Specialized tests for JSON parsing, CLI flags, circuit breaker, and installation workflows
|
||||||
|
|
||||||
|
|
@ -598,24 +609,25 @@ tmux attach -t <name> # Reattach to detached session
|
||||||
|
|
||||||
Ralph is under active development with a clear path to v1.0.0. See [IMPLEMENTATION_PLAN.md](IMPLEMENTATION_PLAN.md) for the complete roadmap.
|
Ralph is under active development with a clear path to v1.0.0. See [IMPLEMENTATION_PLAN.md](IMPLEMENTATION_PLAN.md) for the complete roadmap.
|
||||||
|
|
||||||
### Current Status: v0.9.3
|
### Current Status: v0.9.8
|
||||||
|
|
||||||
**What's Delivered:**
|
**What's Delivered:**
|
||||||
- Core loop functionality with intelligent exit detection
|
- Core loop functionality with intelligent exit detection
|
||||||
- Rate limiting (100 calls/hour) and circuit breaker pattern
|
- Rate limiting (100 calls/hour) and circuit breaker pattern
|
||||||
- Response analyzer with semantic understanding
|
- Response analyzer with semantic understanding
|
||||||
- 165 comprehensive tests (100% pass rate)
|
- 276 comprehensive tests (100% pass rate)
|
||||||
- tmux integration and live monitoring
|
- tmux integration and live monitoring
|
||||||
- PRD import functionality
|
- PRD import functionality with modern CLI JSON parsing
|
||||||
- Installation system and project templates
|
- Installation system and project templates
|
||||||
- Modern CLI commands with JSON output support
|
- Modern CLI commands with JSON output support
|
||||||
- CI/CD pipeline with GitHub Actions
|
- CI/CD pipeline with GitHub Actions
|
||||||
- Comprehensive installation test suite
|
- Comprehensive installation test suite
|
||||||
|
- Session lifecycle management with auto-reset triggers
|
||||||
|
|
||||||
**Test Coverage Breakdown:**
|
**Test Coverage Breakdown:**
|
||||||
- Unit Tests: 111 (CLI parsing, JSON, exit detection, rate limiting)
|
- Unit Tests: 154 (CLI parsing, JSON, exit detection, rate limiting, session continuity)
|
||||||
- Integration Tests: 54 (loop execution, edge cases, installation)
|
- Integration Tests: 122 (loop execution, edge cases, installation, project setup, PRD import)
|
||||||
- Test Files: 8
|
- Test Files: 11
|
||||||
|
|
||||||
### Path to v1.0.0 (~4 weeks)
|
### Path to v1.0.0 (~4 weeks)
|
||||||
|
|
||||||
|
|
|
||||||
395
ralph_import.sh
395
ralph_import.sh
|
|
@ -1,11 +1,35 @@
|
||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
|
|
||||||
# Ralph Import - Convert PRDs to Ralph format using Claude Code
|
# Ralph Import - Convert PRDs to Ralph format using Claude Code
|
||||||
|
# Version: 0.9.8 - Modern CLI support with JSON output parsing
|
||||||
set -e
|
set -e
|
||||||
|
|
||||||
# Configuration
|
# Configuration
|
||||||
CLAUDE_CODE_CMD="claude"
|
CLAUDE_CODE_CMD="claude"
|
||||||
|
|
||||||
|
# Modern CLI Configuration (Phase 1.1)
|
||||||
|
# These flags enable structured JSON output and controlled file operations
|
||||||
|
CLAUDE_OUTPUT_FORMAT="json"
|
||||||
|
# Use bash array for proper quoting of each tool argument
|
||||||
|
declare -a CLAUDE_ALLOWED_TOOLS=('Read' 'Write' 'Bash(mkdir:*)' 'Bash(cp:*)')
|
||||||
|
CLAUDE_MIN_VERSION="2.0.76" # Minimum version for modern CLI features
|
||||||
|
|
||||||
|
# Temporary file names
|
||||||
|
CONVERSION_OUTPUT_FILE=".ralph_conversion_output.json"
|
||||||
|
CONVERSION_PROMPT_FILE=".ralph_conversion_prompt.md"
|
||||||
|
|
||||||
|
# Global parsed conversion result variables
|
||||||
|
# Set by parse_conversion_response() when parsing JSON output from Claude CLI
|
||||||
|
declare PARSED_RESULT="" # Result/summary text from Claude response
|
||||||
|
declare PARSED_SESSION_ID="" # Session ID for potential continuation
|
||||||
|
declare PARSED_FILES_CHANGED="" # Count of files changed
|
||||||
|
declare PARSED_HAS_ERRORS="" # Boolean flag indicating errors occurred
|
||||||
|
declare PARSED_COMPLETION_STATUS="" # Completion status (complete/partial/failed)
|
||||||
|
declare PARSED_ERROR_MESSAGE="" # Error message if conversion failed
|
||||||
|
declare PARSED_ERROR_CODE="" # Error code if conversion failed
|
||||||
|
declare PARSED_FILES_CREATED="" # JSON array of files created
|
||||||
|
declare PARSED_MISSING_FILES="" # JSON array of files that should exist but don't
|
||||||
|
|
||||||
# Colors
|
# Colors
|
||||||
RED='\033[0;31m'
|
RED='\033[0;31m'
|
||||||
GREEN='\033[0;32m'
|
GREEN='\033[0;32m'
|
||||||
|
|
@ -28,6 +52,187 @@ log() {
|
||||||
echo -e "${color}[$(date '+%H:%M:%S')] [$level] $message${NC}"
|
echo -e "${color}[$(date '+%H:%M:%S')] [$level] $message${NC}"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# =============================================================================
|
||||||
|
# JSON OUTPUT FORMAT DETECTION AND PARSING
|
||||||
|
# =============================================================================
|
||||||
|
|
||||||
|
# detect_response_format - Detect whether file contains JSON or plain text output
|
||||||
|
#
|
||||||
|
# Parameters:
|
||||||
|
# $1 (output_file) - Path to the file to inspect
|
||||||
|
#
|
||||||
|
# Returns:
|
||||||
|
# Echoes "json" if file is non-empty, starts with { or [, and validates as JSON
|
||||||
|
# Echoes "text" otherwise (empty file, non-JSON content, or invalid JSON)
|
||||||
|
#
|
||||||
|
# Dependencies:
|
||||||
|
# - jq (used for JSON validation; if unavailable, falls back to "text")
|
||||||
|
#
|
||||||
|
detect_response_format() {
|
||||||
|
local output_file=$1
|
||||||
|
|
||||||
|
if [[ ! -f "$output_file" ]] || [[ ! -s "$output_file" ]]; then
|
||||||
|
echo "text"
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Check if file starts with { or [ (JSON indicators)
|
||||||
|
# Use grep to find first non-whitespace character (handles leading whitespace)
|
||||||
|
local first_char=$(grep -m1 -o '[^[:space:]]' "$output_file" 2>/dev/null)
|
||||||
|
|
||||||
|
if [[ "$first_char" != "{" && "$first_char" != "[" ]]; then
|
||||||
|
echo "text"
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Validate as JSON using jq
|
||||||
|
if command -v jq &>/dev/null && jq empty "$output_file" 2>/dev/null; then
|
||||||
|
echo "json"
|
||||||
|
else
|
||||||
|
echo "text"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# parse_conversion_response - Parse JSON response and extract conversion status
|
||||||
|
#
|
||||||
|
# Parameters:
|
||||||
|
# $1 (output_file) - Path to JSON file containing Claude CLI response
|
||||||
|
#
|
||||||
|
# Returns:
|
||||||
|
# 0 on success (valid JSON parsed)
|
||||||
|
# 1 on error (file not found, jq unavailable, or invalid JSON)
|
||||||
|
#
|
||||||
|
# Sets Global Variables:
|
||||||
|
# PARSED_RESULT - Result/summary text from response
|
||||||
|
# PARSED_SESSION_ID - Session ID for continuation
|
||||||
|
# PARSED_FILES_CHANGED - Count of files changed
|
||||||
|
# PARSED_HAS_ERRORS - "true"/"false" indicating errors
|
||||||
|
# PARSED_COMPLETION_STATUS - Status: "complete", "partial", "failed", "unknown"
|
||||||
|
# PARSED_ERROR_MESSAGE - Error message if conversion failed
|
||||||
|
# PARSED_ERROR_CODE - Error code if conversion failed
|
||||||
|
# PARSED_FILES_CREATED - JSON array string of created files
|
||||||
|
# PARSED_MISSING_FILES - JSON array string of missing files
|
||||||
|
#
|
||||||
|
# Dependencies:
|
||||||
|
# - jq (required for JSON parsing)
|
||||||
|
#
|
||||||
|
parse_conversion_response() {
|
||||||
|
local output_file=$1
|
||||||
|
|
||||||
|
if [[ ! -f "$output_file" ]]; then
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Check if jq is available
|
||||||
|
if ! command -v jq &>/dev/null; then
|
||||||
|
log "WARN" "jq not found, skipping JSON parsing"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Validate JSON first
|
||||||
|
if ! jq empty "$output_file" 2>/dev/null; then
|
||||||
|
log "WARN" "Invalid JSON in output, falling back to text parsing"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Extract fields from JSON response
|
||||||
|
# Supports both flat format and Claude CLI format with metadata
|
||||||
|
|
||||||
|
# Result/summary field
|
||||||
|
PARSED_RESULT=$(jq -r '.result // .summary // ""' "$output_file" 2>/dev/null)
|
||||||
|
|
||||||
|
# Session ID (for potential continuation)
|
||||||
|
PARSED_SESSION_ID=$(jq -r '.sessionId // .session_id // ""' "$output_file" 2>/dev/null)
|
||||||
|
|
||||||
|
# Files changed count
|
||||||
|
PARSED_FILES_CHANGED=$(jq -r '.metadata.files_changed // .files_changed // 0' "$output_file" 2>/dev/null)
|
||||||
|
|
||||||
|
# Has errors flag
|
||||||
|
PARSED_HAS_ERRORS=$(jq -r '.metadata.has_errors // .has_errors // false' "$output_file" 2>/dev/null)
|
||||||
|
|
||||||
|
# Completion status
|
||||||
|
PARSED_COMPLETION_STATUS=$(jq -r '.metadata.completion_status // .completion_status // "unknown"' "$output_file" 2>/dev/null)
|
||||||
|
|
||||||
|
# Error message (if any)
|
||||||
|
PARSED_ERROR_MESSAGE=$(jq -r '.metadata.error_message // .error_message // ""' "$output_file" 2>/dev/null)
|
||||||
|
|
||||||
|
# Error code (if any)
|
||||||
|
PARSED_ERROR_CODE=$(jq -r '.metadata.error_code // .error_code // ""' "$output_file" 2>/dev/null)
|
||||||
|
|
||||||
|
# Files created (as array)
|
||||||
|
PARSED_FILES_CREATED=$(jq -r '.metadata.files_created // [] | @json' "$output_file" 2>/dev/null)
|
||||||
|
|
||||||
|
# Missing files (as array)
|
||||||
|
PARSED_MISSING_FILES=$(jq -r '.metadata.missing_files // [] | @json' "$output_file" 2>/dev/null)
|
||||||
|
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
# check_claude_version - Verify Claude Code CLI version meets minimum requirements
|
||||||
|
#
|
||||||
|
# Checks if the installed Claude Code CLI version is at or above CLAUDE_MIN_VERSION.
|
||||||
|
# Uses numeric semantic version comparison (major.minor.patch).
|
||||||
|
#
|
||||||
|
# Parameters:
|
||||||
|
# None (uses global CLAUDE_CODE_CMD and CLAUDE_MIN_VERSION)
|
||||||
|
#
|
||||||
|
# Returns:
|
||||||
|
# 0 if version is >= CLAUDE_MIN_VERSION
|
||||||
|
# 1 if version cannot be determined or is below CLAUDE_MIN_VERSION
|
||||||
|
#
|
||||||
|
# Side Effects:
|
||||||
|
# Logs warning via log() if version check fails
|
||||||
|
#
|
||||||
|
check_claude_version() {
|
||||||
|
local version
|
||||||
|
version=$($CLAUDE_CODE_CMD --version 2>/dev/null | grep -oE '[0-9]+\.[0-9]+\.[0-9]+' | head -1)
|
||||||
|
|
||||||
|
if [[ -z "$version" ]]; then
|
||||||
|
log "WARN" "Could not determine Claude Code CLI version"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Numeric semantic version comparison
|
||||||
|
# Split versions into major.minor.patch components
|
||||||
|
local ver_major ver_minor ver_patch
|
||||||
|
local min_major min_minor min_patch
|
||||||
|
|
||||||
|
IFS='.' read -r ver_major ver_minor ver_patch <<< "$version"
|
||||||
|
IFS='.' read -r min_major min_minor min_patch <<< "$CLAUDE_MIN_VERSION"
|
||||||
|
|
||||||
|
# Default empty components to 0 (handles versions like "2.1" without patch)
|
||||||
|
ver_major=${ver_major:-0}
|
||||||
|
ver_minor=${ver_minor:-0}
|
||||||
|
ver_patch=${ver_patch:-0}
|
||||||
|
min_major=${min_major:-0}
|
||||||
|
min_minor=${min_minor:-0}
|
||||||
|
min_patch=${min_patch:-0}
|
||||||
|
|
||||||
|
# Compare major version
|
||||||
|
if [[ $ver_major -lt $min_major ]]; then
|
||||||
|
log "WARN" "Claude Code CLI version $version is below recommended $CLAUDE_MIN_VERSION"
|
||||||
|
return 1
|
||||||
|
elif [[ $ver_major -gt $min_major ]]; then
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Major equal, compare minor version
|
||||||
|
if [[ $ver_minor -lt $min_minor ]]; then
|
||||||
|
log "WARN" "Claude Code CLI version $version is below recommended $CLAUDE_MIN_VERSION"
|
||||||
|
return 1
|
||||||
|
elif [[ $ver_minor -gt $min_minor ]]; then
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Minor equal, compare patch version
|
||||||
|
if [[ $ver_patch -lt $min_patch ]]; then
|
||||||
|
log "WARN" "Claude Code CLI version $version is below recommended $CLAUDE_MIN_VERSION"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
show_help() {
|
show_help() {
|
||||||
cat << HELPEOF
|
cat << HELPEOF
|
||||||
Ralph Import - Convert PRDs to Ralph Format
|
Ralph Import - Convert PRDs to Ralph Format
|
||||||
|
|
@ -78,11 +283,21 @@ check_dependencies() {
|
||||||
convert_prd() {
|
convert_prd() {
|
||||||
local source_file=$1
|
local source_file=$1
|
||||||
local project_name=$2
|
local project_name=$2
|
||||||
|
local use_modern_cli=true
|
||||||
|
local cli_exit_code=0
|
||||||
|
|
||||||
log "INFO" "Converting PRD to Ralph format using Claude Code..."
|
log "INFO" "Converting PRD to Ralph format using Claude Code..."
|
||||||
|
|
||||||
|
# Check for modern CLI support
|
||||||
|
if ! check_claude_version 2>/dev/null; then
|
||||||
|
log "INFO" "Using standard CLI mode (modern features may not be available)"
|
||||||
|
use_modern_cli=false
|
||||||
|
else
|
||||||
|
log "INFO" "Using modern CLI with JSON output format"
|
||||||
|
fi
|
||||||
|
|
||||||
# Create conversion prompt
|
# Create conversion prompt
|
||||||
cat > .ralph_conversion_prompt.md << 'PROMPTEOF'
|
cat > "$CONVERSION_PROMPT_FILE" << 'PROMPTEOF'
|
||||||
# PRD to Ralph Conversion Task
|
# PRD to Ralph Conversion Task
|
||||||
|
|
||||||
You are tasked with converting a Product Requirements Document (PRD) or specification into Ralph for Claude Code format.
|
You are tasked with converting a Product Requirements Document (PRD) or specification into Ralph for Claude Code format.
|
||||||
|
|
@ -185,28 +400,164 @@ Create detailed technical specifications:
|
||||||
|
|
||||||
PROMPTEOF
|
PROMPTEOF
|
||||||
|
|
||||||
# Run Claude Code with the source file and prompt
|
# Append the PRD source content to the conversion prompt
|
||||||
if $CLAUDE_CODE_CMD < .ralph_conversion_prompt.md; then
|
local source_basename
|
||||||
|
source_basename=$(basename "$source_file")
|
||||||
|
|
||||||
|
if [[ -f "$source_file" ]]; then
|
||||||
|
echo "" >> "$CONVERSION_PROMPT_FILE"
|
||||||
|
echo "---" >> "$CONVERSION_PROMPT_FILE"
|
||||||
|
echo "" >> "$CONVERSION_PROMPT_FILE"
|
||||||
|
echo "## Source PRD File: $source_basename" >> "$CONVERSION_PROMPT_FILE"
|
||||||
|
echo "" >> "$CONVERSION_PROMPT_FILE"
|
||||||
|
cat "$source_file" >> "$CONVERSION_PROMPT_FILE"
|
||||||
|
else
|
||||||
|
log "ERROR" "Source file not found: $source_file"
|
||||||
|
rm -f "$CONVERSION_PROMPT_FILE"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Build and execute Claude Code command
|
||||||
|
# Modern CLI: Use --output-format json and --allowedTools for structured output
|
||||||
|
# Fallback: Standard CLI invocation for older versions
|
||||||
|
# Note: stderr is written to separate file to avoid corrupting JSON output
|
||||||
|
local stderr_file="${CONVERSION_OUTPUT_FILE}.err"
|
||||||
|
|
||||||
|
if [[ "$use_modern_cli" == "true" ]]; then
|
||||||
|
# Modern CLI invocation with JSON output and controlled tool permissions
|
||||||
|
# --allowedTools permits file operations without user prompts
|
||||||
|
# Array expansion preserves quoting for each tool argument
|
||||||
|
if $CLAUDE_CODE_CMD --output-format "$CLAUDE_OUTPUT_FORMAT" --allowedTools "${CLAUDE_ALLOWED_TOOLS[@]}" < "$CONVERSION_PROMPT_FILE" > "$CONVERSION_OUTPUT_FILE" 2> "$stderr_file"; then
|
||||||
|
cli_exit_code=0
|
||||||
|
else
|
||||||
|
cli_exit_code=$?
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
# Standard CLI invocation (backward compatible)
|
||||||
|
if $CLAUDE_CODE_CMD < "$CONVERSION_PROMPT_FILE" > "$CONVERSION_OUTPUT_FILE" 2> "$stderr_file"; then
|
||||||
|
cli_exit_code=0
|
||||||
|
else
|
||||||
|
cli_exit_code=$?
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Log stderr if there was any (for debugging)
|
||||||
|
if [[ -s "$stderr_file" ]]; then
|
||||||
|
log "WARN" "CLI stderr output detected (see $stderr_file)"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Process the response
|
||||||
|
local output_format="text"
|
||||||
|
local json_parsed=false
|
||||||
|
|
||||||
|
if [[ -f "$CONVERSION_OUTPUT_FILE" ]]; then
|
||||||
|
output_format=$(detect_response_format "$CONVERSION_OUTPUT_FILE")
|
||||||
|
|
||||||
|
if [[ "$output_format" == "json" ]]; then
|
||||||
|
if parse_conversion_response "$CONVERSION_OUTPUT_FILE"; then
|
||||||
|
json_parsed=true
|
||||||
|
log "INFO" "Parsed JSON response from Claude CLI"
|
||||||
|
|
||||||
|
# Check for errors in JSON response
|
||||||
|
if [[ "$PARSED_HAS_ERRORS" == "true" && "$PARSED_COMPLETION_STATUS" == "failed" ]]; then
|
||||||
|
log "ERROR" "PRD conversion failed"
|
||||||
|
if [[ -n "$PARSED_ERROR_MESSAGE" ]]; then
|
||||||
|
log "ERROR" "Error: $PARSED_ERROR_MESSAGE"
|
||||||
|
fi
|
||||||
|
if [[ -n "$PARSED_ERROR_CODE" ]]; then
|
||||||
|
log "ERROR" "Error code: $PARSED_ERROR_CODE"
|
||||||
|
fi
|
||||||
|
rm -f "$CONVERSION_PROMPT_FILE" "$CONVERSION_OUTPUT_FILE" "$stderr_file"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Log session ID if available (for potential continuation)
|
||||||
|
if [[ -n "$PARSED_SESSION_ID" && "$PARSED_SESSION_ID" != "null" ]]; then
|
||||||
|
log "INFO" "Session ID: $PARSED_SESSION_ID"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Log files changed from metadata
|
||||||
|
if [[ -n "$PARSED_FILES_CHANGED" && "$PARSED_FILES_CHANGED" != "0" ]]; then
|
||||||
|
log "INFO" "Files changed: $PARSED_FILES_CHANGED"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Check CLI exit code
|
||||||
|
if [[ $cli_exit_code -ne 0 ]]; then
|
||||||
|
log "ERROR" "PRD conversion failed (exit code: $cli_exit_code)"
|
||||||
|
rm -f "$CONVERSION_PROMPT_FILE" "$CONVERSION_OUTPUT_FILE" "$stderr_file"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Use PARSED_RESULT for success message if available
|
||||||
|
if [[ "$json_parsed" == "true" && -n "$PARSED_RESULT" && "$PARSED_RESULT" != "null" ]]; then
|
||||||
|
log "SUCCESS" "PRD conversion completed: $PARSED_RESULT"
|
||||||
|
else
|
||||||
log "SUCCESS" "PRD conversion completed"
|
log "SUCCESS" "PRD conversion completed"
|
||||||
|
fi
|
||||||
|
|
||||||
# Clean up temp file
|
# Clean up temp files
|
||||||
rm -f .ralph_conversion_prompt.md
|
rm -f "$CONVERSION_PROMPT_FILE" "$CONVERSION_OUTPUT_FILE" "$stderr_file"
|
||||||
|
|
||||||
# Verify files were created
|
# Verify files were created
|
||||||
local missing_files=()
|
# Use PARSED_FILES_CREATED from JSON if available, otherwise check filesystem
|
||||||
if [[ ! -f "PROMPT.md" ]]; then missing_files+=("PROMPT.md"); fi
|
local missing_files=()
|
||||||
if [[ ! -f "@fix_plan.md" ]]; then missing_files+=("@fix_plan.md"); fi
|
local created_files=()
|
||||||
if [[ ! -f "specs/requirements.md" ]]; then missing_files+=("specs/requirements.md"); fi
|
local expected_files=("PROMPT.md" "@fix_plan.md" "specs/requirements.md")
|
||||||
|
|
||||||
if [[ ${#missing_files[@]} -ne 0 ]]; then
|
# If JSON provided files_created, use that to inform verification
|
||||||
log "WARN" "Some files were not created: ${missing_files[*]}"
|
if [[ "$json_parsed" == "true" && -n "$PARSED_FILES_CREATED" && "$PARSED_FILES_CREATED" != "[]" ]]; then
|
||||||
log "INFO" "You may need to create these files manually or run the conversion again"
|
# Validate that PARSED_FILES_CREATED is a valid JSON array before iteration
|
||||||
|
local is_array
|
||||||
|
is_array=$(echo "$PARSED_FILES_CREATED" | jq -e 'type == "array"' 2>/dev/null)
|
||||||
|
if [[ "$is_array" == "true" ]]; then
|
||||||
|
# Parse JSON array and verify each file exists
|
||||||
|
local json_files
|
||||||
|
json_files=$(echo "$PARSED_FILES_CREATED" | jq -r '.[]' 2>/dev/null)
|
||||||
|
if [[ -n "$json_files" ]]; then
|
||||||
|
while IFS= read -r file; do
|
||||||
|
if [[ -n "$file" && -f "$file" ]]; then
|
||||||
|
created_files+=("$file")
|
||||||
|
elif [[ -n "$file" ]]; then
|
||||||
|
missing_files+=("$file")
|
||||||
|
fi
|
||||||
|
done <<< "$json_files"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Always verify expected files exist (filesystem is source of truth)
|
||||||
|
for file in "${expected_files[@]}"; do
|
||||||
|
if [[ -f "$file" ]]; then
|
||||||
|
# Add to created_files if not already there
|
||||||
|
if [[ ! " ${created_files[*]} " =~ " ${file} " ]]; then
|
||||||
|
created_files+=("$file")
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
# Add to missing_files if not already there
|
||||||
|
if [[ ! " ${missing_files[*]} " =~ " ${file} " ]]; then
|
||||||
|
missing_files+=("$file")
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
# Report created files
|
||||||
|
if [[ ${#created_files[@]} -gt 0 ]]; then
|
||||||
|
log "INFO" "Created files: ${created_files[*]}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Report and handle missing files
|
||||||
|
if [[ ${#missing_files[@]} -ne 0 ]]; then
|
||||||
|
log "WARN" "Some files were not created: ${missing_files[*]}"
|
||||||
|
|
||||||
|
# If JSON parsing provided missing files info, use that for better feedback
|
||||||
|
if [[ "$json_parsed" == "true" && -n "$PARSED_MISSING_FILES" && "$PARSED_MISSING_FILES" != "[]" ]]; then
|
||||||
|
log "INFO" "Missing files reported by Claude: $PARSED_MISSING_FILES"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
else
|
log "INFO" "You may need to create these files manually or run the conversion again"
|
||||||
log "ERROR" "PRD conversion failed"
|
|
||||||
rm -f .ralph_conversion_prompt.md
|
|
||||||
exit 1
|
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -242,11 +593,13 @@ main() {
|
||||||
ralph-setup "$project_name"
|
ralph-setup "$project_name"
|
||||||
cd "$project_name"
|
cd "$project_name"
|
||||||
|
|
||||||
# Copy source file to project
|
# Copy source file to project (uses basename since we cd'd into project)
|
||||||
cp "../$source_file" .
|
local source_basename
|
||||||
|
source_basename=$(basename "$source_file")
|
||||||
|
cp "../$source_file" "$source_basename"
|
||||||
|
|
||||||
# Run conversion
|
# Run conversion using local copy (basename, not original path)
|
||||||
convert_prd "$source_file" "$project_name"
|
convert_prd "$source_basename" "$project_name"
|
||||||
|
|
||||||
log "SUCCESS" "🎉 PRD imported successfully!"
|
log "SUCCESS" "🎉 PRD imported successfully!"
|
||||||
echo ""
|
echo ""
|
||||||
|
|
|
||||||
|
|
@ -688,3 +688,443 @@ EOF
|
||||||
run grep "Unique requirement A" "unique-prd/unique-prd.md"
|
run grep "Unique requirement A" "unique-prd/unique-prd.md"
|
||||||
assert_success
|
assert_success
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# =============================================================================
|
||||||
|
# MODERN CLI FEATURES TESTS (Phase 1.1)
|
||||||
|
# Tests for --output-format json, --allowedTools, and JSON response parsing
|
||||||
|
# =============================================================================
|
||||||
|
|
||||||
|
# Helper: Create mock claude command that outputs JSON format
|
||||||
|
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
|
||||||
|
# Read from stdin (conversion prompt)
|
||||||
|
cat > /dev/null
|
||||||
|
|
||||||
|
# Create PROMPT.md with Ralph format
|
||||||
|
cat > PROMPT.md << 'EOF'
|
||||||
|
# Ralph Development Instructions
|
||||||
|
|
||||||
|
## Context
|
||||||
|
You are Ralph, an autonomous AI development agent working on a Task Management App project.
|
||||||
|
|
||||||
|
## Current Objectives
|
||||||
|
1. Study specs/* to learn about the project specifications
|
||||||
|
2. Review @fix_plan.md for current priorities
|
||||||
|
|
||||||
|
## Key Principles
|
||||||
|
- ONE task per loop
|
||||||
|
|
||||||
|
## Testing Guidelines (CRITICAL)
|
||||||
|
- LIMIT testing to ~20% of your total effort
|
||||||
|
EOF
|
||||||
|
|
||||||
|
# Create @fix_plan.md
|
||||||
|
cat > "@fix_plan.md" << 'EOF'
|
||||||
|
# Ralph Fix Plan
|
||||||
|
|
||||||
|
## High Priority
|
||||||
|
- [ ] Set up user authentication with JWT
|
||||||
|
|
||||||
|
## Medium Priority
|
||||||
|
- [ ] Add team/workspace management
|
||||||
|
|
||||||
|
## Low Priority
|
||||||
|
- [ ] Real-time updates with WebSocket
|
||||||
|
|
||||||
|
## Completed
|
||||||
|
- [x] Project initialization
|
||||||
|
EOF
|
||||||
|
|
||||||
|
# Create specs/requirements.md
|
||||||
|
mkdir -p specs
|
||||||
|
cat > specs/requirements.md << 'EOF'
|
||||||
|
# Technical Specifications
|
||||||
|
|
||||||
|
## System Architecture
|
||||||
|
- Frontend: React.js SPA with TypeScript
|
||||||
|
- Backend: Node.js REST API with Express
|
||||||
|
|
||||||
|
## Data Models
|
||||||
|
### User
|
||||||
|
- id: UUID
|
||||||
|
- email: string (unique)
|
||||||
|
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",
|
||||||
|
"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"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
JSON_OUTPUT
|
||||||
|
|
||||||
|
exit 0
|
||||||
|
MOCK_CLAUDE_JSON_EOF
|
||||||
|
chmod +x "$MOCK_BIN_DIR/claude"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Helper: Create mock claude command with JSON output but partial file creation
|
||||||
|
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
|
||||||
|
cat > /dev/null
|
||||||
|
|
||||||
|
# Only create PROMPT.md (missing @fix_plan.md and specs/requirements.md)
|
||||||
|
cat > PROMPT.md << 'EOF'
|
||||||
|
# Ralph Development Instructions
|
||||||
|
|
||||||
|
## Context
|
||||||
|
You are Ralph, an autonomous AI development agent.
|
||||||
|
EOF
|
||||||
|
|
||||||
|
# Output JSON response indicating partial success
|
||||||
|
cat << 'JSON_OUTPUT'
|
||||||
|
{
|
||||||
|
"result": "Partial conversion completed. Some files could not be created.",
|
||||||
|
"sessionId": "session-prd-partial-456",
|
||||||
|
"metadata": {
|
||||||
|
"files_changed": 1,
|
||||||
|
"has_errors": true,
|
||||||
|
"completion_status": "partial",
|
||||||
|
"files_created": ["PROMPT.md"],
|
||||||
|
"missing_files": ["@fix_plan.md", "specs/requirements.md"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
JSON_OUTPUT
|
||||||
|
|
||||||
|
exit 0
|
||||||
|
MOCK_CLAUDE_PARTIAL_EOF
|
||||||
|
chmod +x "$MOCK_BIN_DIR/claude"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Helper: Create mock claude command with JSON error output
|
||||||
|
create_mock_claude_json_error() {
|
||||||
|
cat > "$MOCK_BIN_DIR/claude" << 'MOCK_CLAUDE_JSON_ERROR_EOF'
|
||||||
|
#!/bin/bash
|
||||||
|
# Mock Claude Code CLI that outputs JSON error response
|
||||||
|
cat > /dev/null
|
||||||
|
|
||||||
|
# Output JSON error response
|
||||||
|
cat << 'JSON_OUTPUT'
|
||||||
|
{
|
||||||
|
"result": "",
|
||||||
|
"sessionId": "session-error-789",
|
||||||
|
"metadata": {
|
||||||
|
"files_changed": 0,
|
||||||
|
"has_errors": true,
|
||||||
|
"completion_status": "failed",
|
||||||
|
"error_message": "Failed to parse PRD structure",
|
||||||
|
"error_code": "PARSE_ERROR"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
JSON_OUTPUT
|
||||||
|
|
||||||
|
exit 1
|
||||||
|
MOCK_CLAUDE_JSON_ERROR_EOF
|
||||||
|
chmod +x "$MOCK_BIN_DIR/claude"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Helper: Create mock claude that returns text (backward compatibility)
|
||||||
|
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)
|
||||||
|
cat > /dev/null
|
||||||
|
|
||||||
|
# Create files
|
||||||
|
cat > PROMPT.md << 'EOF'
|
||||||
|
# Ralph Development Instructions
|
||||||
|
|
||||||
|
## Context
|
||||||
|
You are Ralph, an autonomous AI development agent.
|
||||||
|
EOF
|
||||||
|
|
||||||
|
cat > "@fix_plan.md" << 'EOF'
|
||||||
|
# Ralph Fix Plan
|
||||||
|
|
||||||
|
## High Priority
|
||||||
|
- [ ] Set up project structure
|
||||||
|
|
||||||
|
## Completed
|
||||||
|
- [x] Project initialization
|
||||||
|
EOF
|
||||||
|
|
||||||
|
mkdir -p specs
|
||||||
|
cat > specs/requirements.md << 'EOF'
|
||||||
|
# Technical Specifications
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
Basic technical requirements.
|
||||||
|
EOF
|
||||||
|
|
||||||
|
# Output plain text (no JSON)
|
||||||
|
echo "Mock: Claude Code conversion completed successfully"
|
||||||
|
echo "Created: PROMPT.md, @fix_plan.md, specs/requirements.md"
|
||||||
|
exit 0
|
||||||
|
MOCK_CLAUDE_TEXT_EOF
|
||||||
|
chmod +x "$MOCK_BIN_DIR/claude"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Test 23: ralph-import parses JSON output format successfully
|
||||||
|
@test "ralph-import parses JSON output from Claude CLI" {
|
||||||
|
create_sample_prd_md "json-test.md"
|
||||||
|
create_mock_claude_json_success
|
||||||
|
|
||||||
|
run bash "$PROJECT_ROOT/ralph_import.sh" "json-test.md"
|
||||||
|
|
||||||
|
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"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Test 24: ralph-import handles JSON partial success response
|
||||||
|
@test "ralph-import handles JSON partial success and warns about missing files" {
|
||||||
|
create_sample_prd_md "partial-test.md"
|
||||||
|
create_mock_claude_json_partial
|
||||||
|
|
||||||
|
run bash "$PROJECT_ROOT/ralph_import.sh" "partial-test.md"
|
||||||
|
|
||||||
|
# Should succeed but with warnings
|
||||||
|
assert_success
|
||||||
|
|
||||||
|
# PROMPT.md should exist
|
||||||
|
assert_file_exists "partial-test/PROMPT.md"
|
||||||
|
|
||||||
|
# Warning should mention missing files
|
||||||
|
[[ "$output" == *"WARN"* ]] || [[ "$output" == *"not created"* ]] || [[ "$output" == *"missing"* ]]
|
||||||
|
}
|
||||||
|
|
||||||
|
# Test 25: ralph-import handles JSON error response gracefully
|
||||||
|
@test "ralph-import handles JSON error response with structured error message" {
|
||||||
|
create_sample_prd_md "error-test.md"
|
||||||
|
create_mock_claude_json_error
|
||||||
|
|
||||||
|
run bash "$PROJECT_ROOT/ralph_import.sh" "error-test.md"
|
||||||
|
|
||||||
|
# Should fail
|
||||||
|
assert_failure
|
||||||
|
|
||||||
|
# Error output should be present
|
||||||
|
[[ "$output" == *"failed"* ]] || [[ "$output" == *"ERROR"* ]] || [[ "$output" == *"error"* ]]
|
||||||
|
}
|
||||||
|
|
||||||
|
# Test 26: ralph-import maintains backward compatibility with text output
|
||||||
|
@test "ralph-import works with text output (backward compatibility)" {
|
||||||
|
create_sample_prd_md "text-test.md"
|
||||||
|
create_mock_claude_text_output
|
||||||
|
|
||||||
|
run bash "$PROJECT_ROOT/ralph_import.sh" "text-test.md"
|
||||||
|
|
||||||
|
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"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Test 27: ralph-import cleans up JSON output file after processing
|
||||||
|
@test "ralph-import cleans up temporary JSON output file" {
|
||||||
|
create_sample_prd_md "cleanup-test.md"
|
||||||
|
create_mock_claude_json_success
|
||||||
|
|
||||||
|
run bash "$PROJECT_ROOT/ralph_import.sh" "cleanup-test.md"
|
||||||
|
|
||||||
|
assert_success
|
||||||
|
|
||||||
|
# Temporary output file should NOT exist
|
||||||
|
[[ ! -f "cleanup-test/.ralph_conversion_output.json" ]]
|
||||||
|
|
||||||
|
# Temporary prompt file should NOT exist
|
||||||
|
[[ ! -f "cleanup-test/.ralph_conversion_prompt.md" ]]
|
||||||
|
}
|
||||||
|
|
||||||
|
# Test 28: ralph-import detects JSON vs text output format correctly
|
||||||
|
@test "ralph-import detects output format and uses appropriate parsing" {
|
||||||
|
create_sample_prd_md "format-test.md"
|
||||||
|
create_mock_claude_json_success
|
||||||
|
|
||||||
|
run bash "$PROJECT_ROOT/ralph_import.sh" "format-test.md"
|
||||||
|
|
||||||
|
assert_success
|
||||||
|
|
||||||
|
# Success message should indicate completion
|
||||||
|
[[ "$output" == *"SUCCESS"* ]] || [[ "$output" == *"successfully"* ]]
|
||||||
|
}
|
||||||
|
|
||||||
|
# Test 29: ralph-import extracts session ID from JSON response
|
||||||
|
@test "ralph-import extracts and stores session ID from JSON response" {
|
||||||
|
create_sample_prd_md "session-test.md"
|
||||||
|
create_mock_claude_json_success
|
||||||
|
|
||||||
|
run bash "$PROJECT_ROOT/ralph_import.sh" "session-test.md"
|
||||||
|
|
||||||
|
assert_success
|
||||||
|
|
||||||
|
# Check for session file (optional - only if session persistence is implemented)
|
||||||
|
# The session ID should be available for potential continuation
|
||||||
|
# This test verifies JSON parsing extracts the sessionId field
|
||||||
|
}
|
||||||
|
|
||||||
|
# Test 30: ralph-import reports file creation status from JSON metadata
|
||||||
|
@test "ralph-import reports files created based on JSON metadata" {
|
||||||
|
create_sample_prd_md "files-test.md"
|
||||||
|
create_mock_claude_json_success
|
||||||
|
|
||||||
|
run bash "$PROJECT_ROOT/ralph_import.sh" "files-test.md"
|
||||||
|
|
||||||
|
assert_success
|
||||||
|
|
||||||
|
# Should show success with next steps
|
||||||
|
[[ "$output" == *"Next steps"* ]] || [[ "$output" == *"PROMPT.md"* ]]
|
||||||
|
}
|
||||||
|
|
||||||
|
# Test 31: ralph-import uses modern CLI flags
|
||||||
|
@test "ralph-import invokes Claude CLI with modern flags" {
|
||||||
|
# Create a wrapper that captures the command invocation
|
||||||
|
cat > "$MOCK_BIN_DIR/claude" << 'CAPTURE_ARGS_EOF'
|
||||||
|
#!/bin/bash
|
||||||
|
# Capture invocation arguments for testing
|
||||||
|
echo "INVOCATION_ARGS: $*" >> /tmp/claude_invocation.log
|
||||||
|
|
||||||
|
# Create expected files
|
||||||
|
cat > PROMPT.md << 'EOF'
|
||||||
|
# Ralph Development Instructions
|
||||||
|
EOF
|
||||||
|
|
||||||
|
cat > "@fix_plan.md" << 'EOF'
|
||||||
|
# Ralph Fix Plan
|
||||||
|
## High Priority
|
||||||
|
- [ ] Task 1
|
||||||
|
EOF
|
||||||
|
|
||||||
|
mkdir -p specs
|
||||||
|
cat > specs/requirements.md << 'EOF'
|
||||||
|
# Technical Specifications
|
||||||
|
EOF
|
||||||
|
|
||||||
|
# Return JSON output
|
||||||
|
cat << 'JSON_OUTPUT'
|
||||||
|
{
|
||||||
|
"result": "Conversion complete",
|
||||||
|
"sessionId": "test-session",
|
||||||
|
"metadata": {
|
||||||
|
"files_changed": 3,
|
||||||
|
"has_errors": false,
|
||||||
|
"completion_status": "complete"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
JSON_OUTPUT
|
||||||
|
|
||||||
|
exit 0
|
||||||
|
CAPTURE_ARGS_EOF
|
||||||
|
chmod +x "$MOCK_BIN_DIR/claude"
|
||||||
|
|
||||||
|
# Clear previous log
|
||||||
|
rm -f /tmp/claude_invocation.log
|
||||||
|
|
||||||
|
create_sample_prd_md "cli-flags-test.md"
|
||||||
|
|
||||||
|
run bash "$PROJECT_ROOT/ralph_import.sh" "cli-flags-test.md"
|
||||||
|
|
||||||
|
assert_success
|
||||||
|
|
||||||
|
# Check if modern flags were used (if invocation log exists)
|
||||||
|
if [[ -f "/tmp/claude_invocation.log" ]]; then
|
||||||
|
# Verify --output-format or similar flag was passed
|
||||||
|
run cat /tmp/claude_invocation.log
|
||||||
|
# The specific flags depend on implementation
|
||||||
|
# This test ensures CLI modernization is in effect
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Clean up
|
||||||
|
rm -f /tmp/claude_invocation.log
|
||||||
|
}
|
||||||
|
|
||||||
|
# Test 32: ralph-import handles malformed JSON gracefully
|
||||||
|
@test "ralph-import handles malformed JSON and falls back to text parsing" {
|
||||||
|
cat > "$MOCK_BIN_DIR/claude" << 'MALFORMED_JSON_EOF'
|
||||||
|
#!/bin/bash
|
||||||
|
cat > /dev/null
|
||||||
|
|
||||||
|
# Create files
|
||||||
|
cat > PROMPT.md << 'EOF'
|
||||||
|
# Ralph Development Instructions
|
||||||
|
EOF
|
||||||
|
|
||||||
|
cat > "@fix_plan.md" << 'EOF'
|
||||||
|
# Ralph Fix Plan
|
||||||
|
## High Priority
|
||||||
|
- [ ] Task 1
|
||||||
|
EOF
|
||||||
|
|
||||||
|
mkdir -p specs
|
||||||
|
cat > specs/requirements.md << 'EOF'
|
||||||
|
# Technical Specifications
|
||||||
|
EOF
|
||||||
|
|
||||||
|
# Output malformed JSON
|
||||||
|
echo '{"result": "Success but json is broken'
|
||||||
|
echo "Files created successfully"
|
||||||
|
exit 0
|
||||||
|
MALFORMED_JSON_EOF
|
||||||
|
chmod +x "$MOCK_BIN_DIR/claude"
|
||||||
|
|
||||||
|
create_sample_prd_md "malformed-test.md"
|
||||||
|
|
||||||
|
run bash "$PROJECT_ROOT/ralph_import.sh" "malformed-test.md"
|
||||||
|
|
||||||
|
# Should still succeed (fallback to text parsing)
|
||||||
|
assert_success
|
||||||
|
|
||||||
|
# Files should exist
|
||||||
|
assert_file_exists "malformed-test/PROMPT.md"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Test 33: ralph-import extracts error details from JSON error response
|
||||||
|
@test "ralph-import extracts specific error message from JSON error" {
|
||||||
|
cat > "$MOCK_BIN_DIR/claude" << 'DETAILED_ERROR_EOF'
|
||||||
|
#!/bin/bash
|
||||||
|
cat > /dev/null
|
||||||
|
|
||||||
|
# Output detailed JSON error
|
||||||
|
cat << 'JSON_OUTPUT'
|
||||||
|
{
|
||||||
|
"result": "",
|
||||||
|
"sessionId": "error-session",
|
||||||
|
"metadata": {
|
||||||
|
"files_changed": 0,
|
||||||
|
"has_errors": true,
|
||||||
|
"completion_status": "failed",
|
||||||
|
"error_message": "Unable to parse PRD: Missing required sections",
|
||||||
|
"error_code": "PRD_PARSE_ERROR"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
JSON_OUTPUT
|
||||||
|
|
||||||
|
exit 1
|
||||||
|
DETAILED_ERROR_EOF
|
||||||
|
chmod +x "$MOCK_BIN_DIR/claude"
|
||||||
|
|
||||||
|
create_sample_prd_md "detailed-error-test.md"
|
||||||
|
|
||||||
|
run bash "$PROJECT_ROOT/ralph_import.sh" "detailed-error-test.md"
|
||||||
|
|
||||||
|
# Should fail
|
||||||
|
assert_failure
|
||||||
|
|
||||||
|
# Error message should be shown
|
||||||
|
[[ "$output" == *"ERROR"* ]] || [[ "$output" == *"failed"* ]]
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue