Skip to content

feat(enable): add ralph-enable wizard for existing projects (v0.11.0)#124

Merged
frankbria merged 5 commits into
mainfrom
feature/ralph-enable-wizard
Jan 25, 2026
Merged

feat(enable): add ralph-enable wizard for existing projects (v0.11.0)#124
frankbria merged 5 commits into
mainfrom
feature/ralph-enable-wizard

Conversation

@frankbria
Copy link
Copy Markdown
Owner

@frankbria frankbria commented Jan 24, 2026

Summary

  • Add ralph-enable interactive wizard for enabling Ralph in existing projects
  • Add ralph-enable-ci non-interactive version for CI/automation
  • Add task import from beads, GitHub Issues, and PRD documents
  • Add .ralphrc project configuration file support

New Commands

Interactive Wizard

cd existing-project
ralph-enable                          # Full interactive wizard
ralph-enable --from beads             # With specific task source
ralph-enable --from github --label x  # With GitHub Issues
ralph-enable --from prd ./docs/prd.md # With PRD document
ralph-enable --force                  # Overwrite existing config

CI/Automation Version

ralph-enable-ci                       # Sensible defaults
ralph-enable-ci --from github         # With task source
ralph-enable-ci --project-type ts     # Override detection
ralph-enable-ci --json                # Machine-readable output

New Files

File Description
ralph_enable.sh Interactive 5-phase wizard
ralph_enable_ci.sh Non-interactive CI version
lib/enable_core.sh Shared logic (idempotency, detection, templates)
lib/wizard_utils.sh Interactive prompt utilities
lib/task_sources.sh Task import from beads/GitHub/PRD
templates/ralphrc.template Default .ralphrc configuration

Modified Files

File Change
install.sh Added ralph-enable and ralph-enable-ci to global commands
ralph_loop.sh Loads .ralphrc configuration at startup
CLAUDE.md Updated documentation for v0.11.0

Features

  • Auto-detection: Project type (TypeScript, Python, Rust, Go) and framework (Next.js, FastAPI, Django)
  • Task import: From beads, GitHub Issues, or PRD documents
  • Idempotent: Safe to run multiple times, respects existing files
  • Configuration: Generates .ralphrc for project-specific settings
  • Exit codes: 0 (success), 1 (error), 2 (already enabled)
  • JSON output: Machine-parseable output for CI integration

Test plan

  • 30 unit tests for lib/enable_core.sh
  • 23 unit tests for lib/task_sources.sh
  • 22 integration tests for ralph_enable.sh and ralph_enable_ci.sh
  • All 396 tests passing (100% pass rate)
  • Manual testing of interactive wizard
  • Manual testing of CI version with JSON output

Related Issues

Closes #85, #121, #64, #87, #99

Summary by CodeRabbit

  • New Features

    • v0.11.0: Interactive "enable Ralph" wizard and non‑interactive CI variant with JSON output for automation.
    • Automatic project detection, multi-source task import (beads, GitHub, PRD), and per‑project .ralphrc loading at startup.
    • New templates and installer exposure of enable commands with updated help/quick‑start.
  • Tests

    • Large expansion of unit and integration tests covering enable flows, imports, CI outputs, idempotency, and verification.

✏️ Tip: You can customize this high-level summary in your review settings.

Add interactive wizard and CI version for enabling Ralph in existing projects.

New commands:
- ralph-enable: Interactive 5-phase wizard for humans
- ralph-enable-ci: Non-interactive version with JSON output for CI/automation

New library components:
- lib/enable_core.sh: Shared logic for idempotency, project detection, templates
- lib/wizard_utils.sh: Interactive prompt utilities
- lib/task_sources.sh: Task import from beads, GitHub Issues, PRD documents

Features:
- Auto-detects project type (TypeScript, Python, Rust, Go)
- Auto-detects framework (Next.js, FastAPI, Django, Express)
- Imports tasks from beads, GitHub Issues, or PRD documents
- Generates .ralphrc project configuration file
- Idempotent: safe to run multiple times, respects existing files
- Exit codes: 0 (success), 1 (error), 2 (already enabled)

Updated:
- install.sh: Added new commands to global installation
- ralph_loop.sh: Loads .ralphrc configuration at startup

Tests: 75 new tests (30 enable_core + 23 task_sources + 22 integration)
Total: 396 tests passing (100% pass rate)

Closes #85, #121, #64, #87, #99
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Jan 24, 2026

Note

Other AI code review bot(s) detected

CodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review.

Walkthrough

Adds an idempotent "enable Ralph in existing project" feature: interactive (ralph-enable) and CI (ralph-enable-ci) commands, three new libraries (enable_core.sh, wizard_utils.sh, task_sources.sh), .ralphrc template and runtime loader, installer wiring for new commands, and extensive unit/integration tests and mocks.

Changes

Cohort / File(s) Summary
Documentation & Templates
CLAUDE.md, templates/ralphrc.template
Bumped version to v0.11.0, expanded enablement docs and recent improvements; added templates/ralphrc.template with project-level defaults and comments.
Installer & CLI
install.sh
Adds global CLI shims ralph-enable and ralph-enable-ci during install/uninstall; updates help/quick-start output and test mocks.
Enable Core Library
lib/enable_core.sh
New idempotent enablement library: explicit exit codes, colorized logging, state detection (none/partial/complete), safe FS ops, project & Git detection, task-source detection, template generation, and enable_ralph_in_directory() entrypoint.
Task Sources Library
lib/task_sources.sh
New multi-source task import/normalization: beads integration, GitHub issues via gh, PRD extraction/Claude-fallback, normalization, prioritization, and combined import orchestration.
Wizard Utilities
lib/wizard_utils.sh
New interactive helpers: confirm/prompt/select utilities, display helpers, progress/summary printers, and input validation for the interactive wizard.
Enable Scripts
ralph_enable.sh, ralph_enable_ci.sh
Interactive wizard and non-interactive CI variant: environment & project detection, source selection, task import, configuration generation, verification, JSON CI output, CLI args, and structured error/exit codes.
Runtime Loader
ralph_loop.sh
Adds load_ralphrc() to source per-project .ralphrc, map selected keys into runtime variables while preserving env overrides, and integrate loader into startup.
Tests & Mocks
tests/unit/*, tests/integration/test_installation.bats
Added/expanded Bats suites: enable_core, task_sources, ralph_enable (unit + integration), CI JSON outputs; installation tests include mocks for new scripts/libs and updated assertions; small test tweaks for .ralphrc precedence and session continuity.

Sequence Diagram(s)

sequenceDiagram
    actor User
    participant CLI as "ralph-enable / ralph-enable-ci"
    participant Core as "lib/enable_core.sh"
    participant Tasks as "lib/task_sources.sh"
    participant FS as "FileSystem"

    User->>CLI: invoke enable command (opts)
    CLI->>Core: detect_project_context()
    Core-->>CLI: project metadata
    CLI->>Core: detect_git_info()
    Core-->>CLI: git/github info
    CLI->>Core: detect_task_sources()
    Core-->>CLI: available sources
    alt Interactive
        CLI->>User: prompt for sources/config
        User-->>CLI: selections
    end
    CLI->>Tasks: import_tasks_from_sources(selected)
    Tasks->>Tasks: fetch, normalize, prioritize
    Tasks-->>CLI: aggregated tasks
    CLI->>Core: enable_ralph_in_directory(options + tasks)
    Core->>FS: create `.ralph` structure and write files (PROMPT.md, `@AGENT.md`, `@fix_plan.md`, .ralphrc)
    Core-->>CLI: success + created files
    CLI->>FS: verify required files exist
    CLI->>User: print completion or emit JSON result
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~65 minutes

Possibly related PRs

Poem

🐇 I hop in with a wizard's cozy tune,
I probe your repo by sun and moon,
Beads, GH, or PRD — I gather and sort,
.ralph grows branches in tidy report,
Now projects blossom; I celebrate with a carrot-toot! 🥕

🚥 Pre-merge checks | ✅ 4 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 78.87% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title directly describes the main feature: adding an interactive wizard (ralph-enable) for enabling Ralph in existing projects, with version reference (v0.11.0).
Linked Issues check ✅ Passed The PR fulfills all coding requirements from issue #85: enables Ralph in existing projects with task import from beads/GitHub/PRD, generates .ralphrc for project-specific config, idempotent operation with explicit exit codes, and non-interactive CI variant.
Out of Scope Changes check ✅ Passed All changes are within scope: new enable scripts and libraries, installation integration, .ralphrc loading in ralph_loop.sh, documentation updates in CLAUDE.md, and corresponding test coverage.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feature/ralph-enable-wizard

Comment @coderabbitai help to get the list of available commands and usage tips.

@macroscopeapp
Copy link
Copy Markdown
Contributor

macroscopeapp Bot commented Jan 24, 2026

Add ralph-enable wizard and CI command to enable Ralph in existing projects and update startup to load .ralphrc with environment-precedence (v0.11.0)

Introduce interactive ralph-enable and non-interactive ralph-enable-ci commands, add shared lib/enable_core.sh and task source utilities, update installer to deploy new binaries, and modify ralph_loop.sh to respect environment variables and load .ralphrc; documentation and tests are updated to cover the enable flow.

📍Where to Start

Start with the enable_ralph_in_directory function in enable_core.sh, then review the CLI wiring in ralph_enable.sh and ralph_enable_ci.sh.


Macroscope summarized 0bce761.

Comment thread ralph_enable.sh Outdated
Comment thread lib/task_sources.sh Outdated
Comment thread ralph_enable_ci.sh
Comment thread lib/task_sources.sh
Comment thread ralph_enable.sh Outdated
Comment thread ralph_enable.sh Outdated
Comment thread lib/wizard_utils.sh Outdated
Comment thread lib/wizard_utils.sh Outdated
Comment thread lib/wizard_utils.sh
@claude
Copy link
Copy Markdown

claude Bot commented Jan 24, 2026

Code Review - PR 124: Ralph Enable Wizard (v0.11.0)

This is an excellent feature addition that significantly improves the onboarding experience for existing projects. The implementation is well-structured, thoroughly tested, and follows Ralph's established patterns.


Strengths

1. Comprehensive Testing (75 new tests, 100% pass rate)

  • Excellent test coverage across all three new library files
  • Integration tests validate both interactive and CI modes
  • Tests cover edge cases, error handling, and idempotency

2. Solid Architecture

  • Modular design with clear separation of concerns
  • Reusable components that follow DRY principles
  • Well-documented functions with clear parameter/return specifications

3. Idempotent and Safe

  • check_existing_ralph() properly detects partial/complete states
  • safe_create_file() prevents accidental overwrites
  • Proper exit codes (0=success, 1=error, 2=already enabled)

4. Project Detection

  • Auto-detects TypeScript, Python, Rust, Go with framework awareness
  • Intelligent build command detection (npm/yarn/pnpm, uv/pip, cargo)
  • Graceful fallback to sensible defaults

Critical Issues

1. Shell Injection Risk in safe_create_file() (lib/enable_core.sh:160)

Using echo for file writing can cause problems with content containing backslashes, content starting with -e/-n/-E flags, and newline interpretation variations.

Fix: Use printf for reliable output:

if printf '%s\n' "$content" > "$target" 2>/dev/null; then

Impact: Template generation could corrupt content with special characters.


High Priority Issues

2. Unchecked Command Substitution in Project Detection

Multiple locations (lib/enable_core.sh:277, 320, 347, 357) use command substitution without validation. Malformed JSON/TOML could inject unexpected values.

3. Missing JSON Validation

lib/task_sources.sh:168 and similar functions should validate JSON structure before parsing.


Medium Priority Issues

4. Inconsistent Bash Shebang

Library files use #!/usr/bin/env bash while main scripts use #!/bin/bash. Should standardize for portability.

5. Environment Variable Handling Bug (ralph_loop.sh:102-136)

The save/restore pattern loses empty string vs unset distinction. Should use [[ -v VAR ]] test.

6. Template Variable Substitution Issue

templates/ralphrc.template:14 uses shell parameter expansion that evaluates at runtime instead of generation time.


Recommendations

Must fix before merge:

  • Issue 1 (echo to printf in safe_create_file)
  • Issue 6 (ralphrc template variable substitution)

Recommended for follow-up PR:

  • Issues 2-5 (input validation, error handling, consistency)

Overall Assessment

Code Quality: 4/5
Testing: 5/5
Documentation: 5/5
Architecture: 5/5
Security: 3/5

APPROVED with requested changes

Great work on this PR! It closes 5 issues and delivers exactly what the Ralph community needs for easier adoption.

@github-actions
Copy link
Copy Markdown

Code Review: feat(enable): add ralph-enable wizard for existing projects (v0.11.0)

Excellent work on adding the
�[1m╔════════════════════════════════════════════════════════════╗�[0m
�[1m║ Ralph Enable - Existing Project Wizard ║�[0m
�[1m╚════════════════════════════════════════════════════════════╝�[0m

�[1m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━�[0m
�[1m Environment Detection�[0m �[0;36m(Phase 1 of 5)�[0m
�[1m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━�[0m

Analyzing your project...

�[1;33m○�[0m Ralph status: Not configured
�[0;32m✓�[0m Project name: �[1mralph-claude-code�[0m
�[0;32m✓�[0m Project type: �[1mjavascript�[0m
�[0;32m✓�[0m Git repository: �[1mYes�[0m
�[0;32m✓�[0m GitHub remote: �[1mYes�[0m

Available task sources:
�[0;32m✓�[0m GitHub Issues: �[1m30 open issues�[0m
�[0;32m✓�[0m PRD files: �[1m1 found�[0m

�[1m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━�[0m
�[1m Task Source Selection�[0m �[0;36m(Phase 2 of 5)�[0m
�[1m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━�[0m

Where would you like to import tasks from?

Selected sources: none

�[1m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━�[0m
�[1m Configuration�[0m �[0;36m(Phase 3 of 5)�[0m
�[1m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━�[0m

�[1m┌─ Configuration ───────────────────────────────────────┐�[0m

│ �[0;36mProject: �[0m �[0;36mProject name�[0m [ralph-claude-code]: ralph-claude-code
│ �[0;36mType: �[0m javascript
│ �[0;36mMax calls/hour: �[0m �[0;36mMax API calls per hour�[0m [100]: 100
│ �[0;36mTask sources: �[0m none

�[1m└────────────────────────────────────────────────────┘�[0m

�[1m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━�[0m
�[1m File Generation�[0m �[0;36m(Phase 4 of 5)�[0m
�[1m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━�[0m

Creating Ralph configuration...

�[0;34m[INFO]�[0m Enabling Ralph for: �[0;36mProject name�[0m [ralph-claude-code]: ralph-claude-code
�[0;34m[INFO]�[0m Project type: javascript
�[0;32m[SUCCESS]�[0m Created directory: .ralph
�[0;32m[SUCCESS]�[0m Created directory: .ralph/specs
�[0;32m[SUCCESS]�[0m Created directory: .ralph/examples
�[0;32m[SUCCESS]�[0m Created directory: .ralph/logs
�[0;32m[SUCCESS]�[0m Created directory: .ralph/docs/generated
�[0;32m[SUCCESS]�[0m Created .ralph/PROMPT.md
�[0;32m[SUCCESS]�[0m Created .ralph/@AGENT.md
�[0;32m[SUCCESS]�[0m Created .ralph/@fix_plan.md
�[0;32m[SUCCESS]�[0m Created .ralphrc
�[0;32m[SUCCESS]�[0m Ralph enabled successfully!

�[1m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━�[0m
�[1m Verification�[0m �[0;36m(Phase 5 of 5)�[0m
�[1m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━�[0m

Checking created files...

�[0;32m✓�[0m .ralph/PROMPT.md
�[0;32m✓�[0m .ralph/@fix_plan.md
�[0;32m✓�[0m .ralph/@AGENT.md
�[0;32m✓�[0m .ralphrc
�[0;32m✓�[0m .ralph/specs/
�[0;32m✓�[0m .ralph/logs/

�[0;32m✓�[0m Ralph enabled successfully!

Next steps:

�[0;36m1.�[0m Review and customize .ralph/PROMPT.md
�[0;36m2.�[0m Edit tasks in .ralph/@fix_plan.md
�[0;36m3.�[0m Update build commands in .ralph/@AGENT.md
�[0;36m4.�[0m Start Ralph: ralph --monitor

�[0;36mShow current status?�[0m [Y/n]:
No status file found. Ralph may not be running. wizard! The implementation is comprehensive with good separation of concerns between the three new library modules. Here are my findings:


✅ Strengths

  • Modular architecture: Good separation into , , and
  • Comprehensive testing: 75 new tests added (30 enable_core, 23 task_sources, 22 integration)
  • User experience: The 5-phase wizard provides clear progression and helpful feedback
  • CI/automation support: Separate Ralph Enable CI - Non-Interactive Mode

Ralph is already enabled in this project. Use --force to override. script with JSON output is well-designed

  • Idempotency: Proper checks prevent accidental overwrites via

⚠️ Security Concerns

1. sed command injection vulnerability (Medium)

Location:

The sed commands on lines 456 and 460 use user input without escaping:

sed -i "s/GITHUB_TASK_LABEL=.*/GITHUB_TASK_LABEL=\"$CONFIG_GITHUB_LABEL\"/" .ralphrc

Issue: If contains special characters like /, &, or newlines, the sed command will break or behave unexpectedly.

Example:

  • --label "ralph/task" → breaks because / is sed delimiter
  • --label "test&replace"& is special in sed replacement

Fix: Use a different sed delimiter or escape special characters:

# Option 1: Use | as delimiter
sed -i "s|GITHUB_TASK_LABEL=.*|GITHUB_TASK_LABEL=\"$CONFIG_GITHUB_LABEL\"|" .ralphrc

# Option 2: Escape special chars
safe_label=$(echo "$CONFIG_GITHUB_LABEL" | sed 's/[&\/]/\\&/g')

2. Insufficient input validation (Low)

Location:

CLI arguments --prd, --label, --from are only validated to check they're not empty and don't start with --. No validation for:

  • Path traversal in --prd (e.g., /etc/passwd)
  • Special characters in --label
  • Invalid task source values

Recommendation: Add validation:

# Validate --label is a valid GitHub label format
if [[ ! "$GITHUB_LABEL" =~ ^[a-zA-Z0-9_-]+$ ]]; then
    echo "Error: --label must contain only letters, numbers, hyphens, and underscores"
    exit 1
fi

# Validate --prd is within allowed directory
if [[ "$PRD_FILE" =~ ^\.\./ ]]; then
    echo "Error: --prd cannot use parent directory traversal"
    exit 1
fi

3. Sourcing untrusted .ralphrc (Informational)

Location:

The script sources .ralphrc which is user-controlled:

source "$RALPHRC_FILE"

Note: This is documented as a feature for project-specific configuration, but users should be aware that .ralphrc can execute arbitrary code. The current implementation mitigates this by:

  • Saving environment variables before sourcing (lines 103-109)
  • Restoring env vars after sourcing (lines 130-136)

This is acceptable for a dev tool but worth documenting.


🐛 Potential Bugs

1. grep pattern with literal brackets

Location:

TASKS_IMPORTED=$(echo "$imported_tasks" | grep -c '^\- \[' || echo "0")

In some shells, the [\] pattern might need escaping. The pattern should be:

grep -c '^\- \\['

However, this may work fine in bash since the double quotes protect it. Consider testing on different shells.

2. Global variable state in ralph_enable_ci.sh

Location:

declare -a CREATED_FILES=()
TASKS_IMPORTED=0

These are declared without declare -g or local. While this works in the current script context, it could cause issues if the functions are sourced or called from subshells.

Recommendation: Use declare -g for global variables or ensure they're properly scoped:

declare -g -a CREATED_FILES=()
declare -g TASKS_IMPORTED=0

📊 Code Quality & Best Practices

Good Practices

  • Consistent error handling with exit codes (0=success, 1=error, 2=already_enabled)
  • Good use of set -e for error propagation
  • Comprehensive inline documentation and comments
  • Proper function parameter validation

Minor Issues

  1. Hardcoded paths in library loading: checks for libraries in hardcoded locations. Consider adding a RALPH_LIB_PATH environment variable override.

  2. Magic numbers: Circuit breaker thresholds and timeouts are hardcoded. Some are configurable in , but code defaults could use named constants.

  3. printf safety: While reviewing, I noticed most string output uses echo rather than printf. Consider using printf for user-facing output with special characters.


🚀 Performance Considerations

The implementation is efficient with no obvious performance concerns:

  • Single-pass file operations
  • Minimal subshell usage
  • Efficient grep/find patterns for PRD file detection

🧪 Test Coverage

  • 396 tests passing (100% pass rate) ✅
  • 75 new tests added specifically for enable functionality ✅
  • Tests cover idempotency, detection, templates, and CI output ✅

One suggestion: Add a test for the sed injection vulnerability (issue #1 above) to prevent regressions.


📋 Summary

Overall, this is a well-architected and thoroughly tested feature. The main concerns are:

  1. Must fix: sed command injection on lines 456/460 of
  2. Should fix: Add input validation for CLI arguments
  3. Nice to have: Document .ralphrc security implications
  4. Minor: Global variable scoping in ralph_enable_ci.sh

The security issues are medium-severity since they require user action (providing malicious input via CLI) and don't escalate privileges, but they could cause unexpected behavior or break the tool.

@github-actions
Copy link
Copy Markdown

Author identity unknown

*** Please tell me who you are.

Run

git config --global user.email "you@example.com"
git config --global user.name "Your Name"

to set your account's default identity.
Omit --global to set the identity only in this repository.

fatal: empty ident name (for runner@runnervmymu0l.rpzyaon4tsbuzbyx4xvysldu2h.bx.internal.cloudapp.net) not allowed

opencode session  |  github run

@github-actions
Copy link
Copy Markdown

Code Review: PR #124 - Ralph Enable Wizard

Overall this is a well-architected feature with excellent test coverage (75 new tests, 100% pass rate). The modular design follows Ralph's established patterns and provides significant value for onboarding existing projects.

However, there are several issues that should be addressed before merge:


🔴 Critical Issues (Must Fix)

1. sed Command Injection Vulnerability (ralph_enable.sh:456, 460)

The sed commands use user-provided CONFIG_MAX_CALLS and CONFIG_GITHUB_LABEL values without escaping:

sed -i "s/MAX_CALLS_PER_HOUR=.*/MAX_CALLS_PER_HOUR=/" .ralphrc
sed -i "s/GITHUB_TASK_LABEL=.*/GITHUB_TASK_LABEL=\"\"/" .ralphrc

Impact: Special characters break the command. Examples:

  • --label "ralph/task" → breaks (slash is sed delimiter)
  • --label "test&replace"& has special meaning in sed
  • --max-calls 100/200" → unexpected behavior

Fix: Use different delimiter or escape:

# Option 1: Use | as delimiter
sed -i "s|MAX_CALLS_PER_HOUR=.*|MAX_CALLS_PER_HOUR=|" .ralphrc
sed -i "s|GITHUB_TASK_LABEL=.*|GITHUB_TASK_LABEL=\"\"|" .ralphrc

# Option 2: Escape special chars (more robust)
safe_label=""
sed -i "s/GITHUB_TASK_LABEL=.*/GITHUB_TASK_LABEL=\"\"/" .ralphrc

2. Missing jq Dependency Check (ralph_enable_ci.sh:174-177)

The --json flag requires jq but the script doesn't verify it's installed:

--json)
    OUTPUT_JSON=true
    shift
    ;;

At line 230, jq is used without checking availability:

files_json=[
  "\"${CREATED_FILES[@]}\"\\n"
]

Fix: Add validation when --json is parsed:

--json)
    if ! command -v jq &>/dev/null; then
        output_error "jq is required for --json output. Install with: apt-get install jq (Linux) or brew install jq (macOS)"
        exit 1
    fi
    OUTPUT_JSON=true
    shift
    ;;

3. Shell Injection via echo in safe_create_file (lib/enable_core.sh:160)

Using echo for file writing is unreliable with special characters:

if echo "" > "" 2>/dev/null; then

Issues:

  • Backslashes may be interpreted as escape sequences
  • Content starting with -e, -n, -E is interpreted as flags
  • Newline behavior varies by shell

Fix: Use printf '%s\n' for reliable output:

if printf '%s\n' "" > "" 2>/dev/null; then

🟡 High Priority Issues

4. .ralphrc Template Variable Substitution Issue (templates/ralphrc.template:14)

PROJECT_NAME="${PROJECT_NAME:-my-project}"

This evaluates at runtime when sourced, not at file generation time. If PROJECT_NAME changes in the environment after generation, the value changes unexpectedly.

Expected behavior: Template variables should be substituted when the file is created.

Fix: In lib/enable_core.sh's generate_ralphrc(), use sed to substitute template variables with actual values during file generation.


5. Environment Variable Handling Bug (ralph_loop.sh:102-136)

The save/restore pattern loses the distinction between "empty string" and "unset":

local saved_MAX_CALLS_PER_HOUR="${MAX_CALLS_PER_HOUR:-}"
# ... source .ralphrc ...
[[ -n "" ]] && MAX_CALLS_PER_HOUR=""

If MAX_CALLS_PER_HOUR was unset before sourcing, it's saved as empty string. If .ralphrc sets it to empty, restoration happens incorrectly.

Fix: Use `[[ -v VAR ]]" test:

# Save
[[ -v MAX_CALLS_PER_HOUR ]] && saved_MAX_CALLS_PER_HOUR="true" || saved_MAX_CALLS_PER_HOUR="false"
local saved_max_calls_value="$MAX_CALLS_PER_HOUR"

# Restore
if [[ "" == "true" ]]; then
    MAX_CALLS_PER_HOUR=""
fi

6. Race Condition in File Creation

In lib/enable_core.sh, the idempotency check and file creation are not atomic:

# check_existing_ralph checks if file exists
# Later, safe_create_file creates it

Between the check and creation, another process could create the file.

Impact: Low risk for interactive use, but could affect CI scenarios.

Fix: Use set -o noclobber or check return codes carefully.


🟢 Medium Priority Issues

7. Global Variable State (ralph_enable_ci.sh:207-208)

declare -a CREATED_FILES=()
TASKS_IMPORTED=0

TASKS_IMPORTED is declared without declare. While it works in this context, it's inconsistent. Consider using declare -g explicitly for globals or ensure proper scoping.


8. Shebang Inconsistency

Library files use #!/usr/bin/env bash while main scripts use #!/bin/bash. Standardize for portability across different systems (e.g., macOS where bash may not be in /bin).


9. sed Portability (lib/task_sources.sh:378, 386)

text=""

BSD sed (macOS) doesn't support \s. Use POSIX-compatible patterns or sed -E:

text=""

✅ Strengths

  1. Excellent test coverage: 75 new tests, 100% pass rate
  2. Modular architecture: Clean separation into enable_core, wizard_utils, task_sources
  3. Idempotency: Proper checks prevent accidental overwrites
  4. User experience: 5-phase wizard with clear feedback
  5. CI/automation support: Separate non-interactive version with JSON output
  6. Project detection: Auto-detects TypeScript, Python, Rust, Go with framework awareness
  7. Good error handling: Consistent exit codes (0=success, 1=error, 2=already_enabled)

📋 Recommended Action Plan

Before merge (must fix):

  1. Fix sed injection vulnerability (Issue cannot find file ~/.ralph/lib/response_analyzer.sh #1)
  2. Add jq dependency check (Issue is_error: false, triggers "error" circuit breaker incorrectly #2)
  3. Replace echo with printf in safe_create_file (Issue Quality improvements: Bun migration, code quality tooling, documentation consolidation #3)
  4. Fix .ralphrc template variable substitution (Issue fix(install): add lib/ directory copy for response_analyzer.sh #4)

Follow-up PR (should fix):
5. Fix environment variable handling bug (Issue #5)
6. Fix sed portability issues (Issue #9)

Optional (nice to have):
7. Address race condition (Issue #6)
8. Standardize shebangs (Issue #8)
9. Add input validation for CLI arguments (e.g., path traversal in --prd)


🎯 Overall Assessment

Code Quality: 4/5
Testing: 5/5
Architecture: 5/5
Security: 3/5 (due to injection vulnerabilities)
Documentation: 5/5

Status: ⚠️ REQUESTED CHANGES REQUIRED

The feature is well-designed and thoroughly tested, but the sed injection and jq dependency issues must be fixed before merge. These are not theoretical—they will cause real problems for users.

Great work on closing issues #85, #121, #64, #87, #99!

@github-actions
Copy link
Copy Markdown

Review posted focusing on security vulnerabilities (sed injection, missing jq check, echo/printf issue), high-priority bugs (.ralphrc template substitution, environment var handling), and providing a clear action plan for before merge vs follow-up PR.

New%20session%20-%202026-01-24T20%3A49%3A44.439Z
opencode session  |  github run

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 7

🤖 Fix all issues with AI agents
In `@lib/enable_core.sh`:
- Around line 140-166: safe_create_file currently always skips if the target
exists which prevents enable_ralph_in_directory's --force behavior from working;
change safe_create_file to accept a third parameter (e.g., force) and only
return "SKIP" when the file exists and force is not true, otherwise overwrite
the file (perform the same parent-dir creation and write logic) and log
"OVERWRITE" or "SUCCESS" appropriately; update callers like
enable_ralph_in_directory to pass the force flag through when invoking
safe_create_file so --force actually replaces existing
PROMPT/@AGENT/@fix_plan/.ralphrc files.

In `@lib/task_sources.sh`:
- Around line 482-533: The header lines like "# Tasks from beads" are being
treated as tasks because normalize_tasks treats every line as a task; fix by
normalizing each source's raw output separately and appending only the
normalized results (or strip header lines before normalization). Specifically,
for each fetch function (fetch_beads_tasks -> beads_tasks, fetch_github_tasks ->
github_tasks, extract_prd_tasks -> prd_tasks) call normalize_tasks on that
string and then append the normalized output to all_tasks (increment
source_count as before) instead of inserting literal "# Tasks ..." headings;
alternatively remove any leading '#' comment lines from each source before
calling normalize_tasks. Ensure you update the code paths that currently build
all_tasks and still use source_count, and keep the final normalize_tasks
"$all_tasks" "combined" call only if you intend a second pass normalization.
- Around line 43-82: fetch_beads_tasks currently runs bd list --json and only
falls back to plain-text if the bd command fails, so if jq is missing or JSON
parsing yields no tasks they get silently dropped; also the local filter
parameter is never applied. Update fetch_beads_tasks to (1) include the filter
when invoking bd (e.g., pass "$filter" to bd list or build args from filter) or
apply the filter in the jq/select step, (2) after capturing json_output, attempt
to parse it with jq only if jq is present, but if jq is missing or parsing
produces an empty $tasks, run the plain-text fallback (bd list with the same
filter) and parse lines to produce the markdown task lines; mirror this same
pattern and fallback behavior in fetch_github_tasks so missing jq or parse
failures never drop results.

In `@ralph_enable_ci.sh`:
- Around line 381-385: The current post-generation verification in
ralph_enable_ci.sh only checks for ".ralph/PROMPT.md" and ".ralph/@fix_plan.md"
but check_existing_ralph (in enable_core.sh) also treats ".ralph/@AGENT.md" as
required; update the verification block in ralph_enable_ci.sh to include
".ralph/@AGENT.md" so the script fails when the agent file is missing, ensuring
consistency with check_existing_ralph.

In `@ralph_enable.sh`:
- Around line 184-186: The call to check_existing_ralph can return non-zero (1
or 2) and with set -e enabled will abort the script before the subsequent case
on RALPH_STATE; change the invocation so the script does not exit on those
expected return codes (for example, run check_existing_ralph in a way that
ignores its exit status or temporarily disable set -e around the call) so
RALPH_STATE can be evaluated in the case statement; update the call to
check_existing_ralph used before the case and ensure RALPH_STATE is still set
afterwards.
- Around line 453-462: The current in-place edits use "sed -i" which fails on
macOS; replace the two sed -i invocations (the MAX_CALLS_PER_HOUR and
GITHUB_TASK_LABEL updates) with a POSIX-safe edit that writes to a temporary
file and atomically moves it back (e.g., use mktemp -> run sed without -i into
the temp -> mv temp .ralphrc), remove the blind 2>/dev/null || true suppression
so failures are visible, and keep the same substitution patterns referencing
CONFIG_MAX_CALLS, CONFIG_GITHUB_LABEL, and GITHUB_TASK_LABEL to ensure the
intended updates to .ralphrc occur on both Linux and macOS.

In `@ralph_loop.sh`:
- Around line 102-136: The restore logic in load_ralphrc (the saved_* -> restore
block) always wins because defaults make saved_* non-empty, wiping out .ralphrc
changes; change this to restore only when an explicit override was provided by
the CLI/env by introducing per-setting override flags (e.g.,
MAX_CALLS_PER_HOUR_OVERRIDE, CLAUDE_ALLOWED_TOOLS_OVERRIDE,
CLAUDE_USE_CONTINUE_OVERRIDE, CLAUDE_SESSION_EXPIRY_HOURS_OVERRIDE,
VERBOSE_PROGRESS_OVERRIDE) and check those flags in the restore block instead of
testing saved_* (e.g., only restore MAX_CALLS_PER_HOUR if
MAX_CALLS_PER_HOUR_OVERRIDE is true); set the corresponding *_OVERRIDE=true at
the points that explicitly override values (for example in the --calls handler
and wherever env/CLI parsing assigns defaults) so .ralphrc values remain
effective unless explicitly overridden.
🧹 Nitpick comments (5)
lib/enable_core.sh (1)

18-24: Add a function-level comment for _color.

This helper currently lacks the required function-level comment. As per coding guidelines, add a short doc line describing its behavior.

tests/unit/test_enable_core.bats (1)

335-346: Force-overwrite test doesn’t validate the overwrite behavior.

The test only checks exit status; it doesn’t confirm that old content was replaced. Adding a content assertion would catch the current --force overwrite bug and prevent regressions. Based on learnings, strengthen this test to validate behavior.

✅ Example assertion
     run enable_ralph_in_directory
 
     assert_success
+    [[ "$(cat .ralph/PROMPT.md)" != "old content" ]]
tests/unit/test_task_sources.bats (1)

209-239: Minor inconsistency in test patterns.

Tests at lines 213 and 236 use direct function calls with || true, while test at line 225 uses run. Both approaches work, but consistency would improve maintainability. Consider using run consistently for all tests to leverage BATS' $output and $status variables uniformly.

ralph_enable_ci.sh (1)

224-247: Consider escaping special characters in JSON string values.

The output_success function directly interpolates $project_name and $project_type into the JSON heredoc. If these contain quotes or special characters, the JSON could be malformed. Since jq is available, consider using it for proper escaping.

♻️ Proposed fix for JSON safety
 output_success() {
     local project_name="$1"
     local project_type="$2"

     if [[ "$OUTPUT_JSON" == "true" ]]; then
         local files_json
         files_json=$(printf '%s\n' "${CREATED_FILES[@]}" | jq -R . | jq -s .)

-        cat << EOF
-{
-    "success": true,
-    "project_name": "$project_name",
-    "project_type": "$project_type",
-    "files_created": $files_json,
-    "tasks_imported": $TASKS_IMPORTED,
-    "message": "Ralph enabled successfully"
-}
-EOF
+        jq -n \
+            --arg name "$project_name" \
+            --arg type "$project_type" \
+            --argjson files "$files_json" \
+            --argjson tasks "$TASKS_IMPORTED" \
+            '{success: true, project_name: $name, project_type: $type, files_created: $files, tasks_imported: $tasks, message: "Ralph enabled successfully"}'
     else
install.sh (1)

349-349: Consider breaking the long uninstall command for readability.

The uninstall rm -f command now spans many arguments. While functional, breaking it across multiple lines would improve maintainability.

♻️ Suggested improvement
     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" "$INSTALL_DIR/ralph-migrate" "$INSTALL_DIR/ralph-enable" "$INSTALL_DIR/ralph-enable-ci"
+        rm -f "$INSTALL_DIR/ralph" \
+              "$INSTALL_DIR/ralph-monitor" \
+              "$INSTALL_DIR/ralph-setup" \
+              "$INSTALL_DIR/ralph-import" \
+              "$INSTALL_DIR/ralph-migrate" \
+              "$INSTALL_DIR/ralph-enable" \
+              "$INSTALL_DIR/ralph-enable-ci"
         rm -rf "$RALPH_HOME"

Comment thread lib/enable_core.sh
Comment on lines +140 to +166
safe_create_file() {
local target=$1
local content=$2

if [[ -f "$target" ]]; then
enable_log "SKIP" "$target already exists"
return 1
fi

# Create parent directory if needed
local parent_dir
parent_dir=$(dirname "$target")
if [[ ! -d "$parent_dir" ]]; then
if ! mkdir -p "$parent_dir" 2>/dev/null; then
enable_log "ERROR" "Failed to create directory: $parent_dir"
return 2
fi
fi

# Write content to file
if echo "$content" > "$target" 2>/dev/null; then
enable_log "SUCCESS" "Created $target"
return 0
else
enable_log "ERROR" "Failed to create: $target"
return 2
fi
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

--force doesn’t overwrite existing files.

enable_ralph_in_directory avoids the early “already enabled” exit when force=true, but safe_create_file still skips existing files. That means --force leaves old PROMPT/@AGENT/@fix_plan/.ralphrc content intact, which is not the expected behavior.

🐛 Proposed fix
-safe_create_file() {
-    local target=$1
-    local content=$2
+safe_create_file() {
+    local target=$1
+    local content=$2
+    local force="${3:-false}"
 
-    if [[ -f "$target" ]]; then
+    if [[ -f "$target" && "$force" != "true" ]]; then
         enable_log "SKIP" "$target already exists"
         return 1
     fi
+    if [[ -f "$target" && "$force" == "true" ]]; then
+        enable_log "WARN" "Overwriting $target"
+    fi
@@
-    safe_create_file ".ralph/PROMPT.md" "$prompt_content"
+    safe_create_file ".ralph/PROMPT.md" "$prompt_content" "$force"
@@
-    safe_create_file ".ralph/@AGENT.md" "$agent_content"
+    safe_create_file ".ralph/@AGENT.md" "$agent_content" "$force"
@@
-    safe_create_file ".ralph/@fix_plan.md" "$fix_plan_content"
+    safe_create_file ".ralph/@fix_plan.md" "$fix_plan_content" "$force"
@@
-    safe_create_file ".ralphrc" "$ralphrc_content"
+    safe_create_file ".ralphrc" "$ralphrc_content" "$force"

Also applies to: 707-775

🤖 Prompt for AI Agents
In `@lib/enable_core.sh` around lines 140 - 166, safe_create_file currently always
skips if the target exists which prevents enable_ralph_in_directory's --force
behavior from working; change safe_create_file to accept a third parameter
(e.g., force) and only return "SKIP" when the file exists and force is not true,
otherwise overwrite the file (perform the same parent-dir creation and write
logic) and log "OVERWRITE" or "SUCCESS" appropriately; update callers like
enable_ralph_in_directory to pass the force flag through when invoking
safe_create_file so --force actually replaces existing
PROMPT/@AGENT/@fix_plan/.ralphrc files.

Comment thread lib/task_sources.sh
Comment on lines +43 to +82
fetch_beads_tasks() {
local filter="${1:-status:open}"
local tasks=""

# Check if beads is available
if ! check_beads_available; then
return 1
fi

# Try to get tasks as JSON
local json_output
if json_output=$(bd list --json 2>/dev/null); then
# Parse JSON and format as markdown tasks
if command -v jq &>/dev/null; then
tasks=$(echo "$json_output" | jq -r '
.[] |
select(.status != "closed") |
"- [ ] [\(.id)] \(.title)"
' 2>/dev/null)
fi
else
# Fallback: try plain text output
tasks=$(bd list 2>/dev/null | while IFS= read -r line; do
# Extract ID and title from bd list output
local id title
id=$(echo "$line" | grep -oE '^[a-z]+-[0-9]+' || echo "")
title=$(echo "$line" | sed 's/^[a-z]+-[0-9]* *//' || echo "$line")
if [[ -n "$id" ]]; then
echo "- [ ] [$id] $title"
fi
done)
fi

if [[ -n "$tasks" ]]; then
echo "$tasks"
return 0
else
return 0 # Empty is not an error
fi
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

find . -type f -name "task_sources.sh" | head -20

Repository: frankbria/ralph-claude-code

Length of output: 92


🏁 Script executed:

if [ -f "./lib/task_sources.sh" ]; then
  wc -l ./lib/task_sources.sh
else
  echo "File not found at expected path"
fi

Repository: frankbria/ralph-claude-code

Length of output: 96


🏁 Script executed:

git ls-files | grep -i task_sources

Repository: frankbria/ralph-claude-code

Length of output: 124


🏁 Script executed:

sed -n '43,82p' ./lib/task_sources.sh

Repository: frankbria/ralph-claude-code

Length of output: 1238


🏁 Script executed:

sed -n '150,185p' ./lib/task_sources.sh

Repository: frankbria/ralph-claude-code

Length of output: 892


🏁 Script executed:

sed -n '1,50p' ./lib/task_sources.sh

Repository: frankbria/ralph-claude-code

Length of output: 1313


🏁 Script executed:

sed -n '43,82p' ./lib/task_sources.sh | cat -n

Repository: frankbria/ralph-claude-code

Length of output: 1518


🏁 Script executed:

sed -n '150,185p' ./lib/task_sources.sh | cat -n

Repository: frankbria/ralph-claude-code

Length of output: 1144


🏁 Script executed:

grep -n "normalize_tasks" ./lib/task_sources.sh

Repository: frankbria/ralph-claude-code

Length of output: 242


🏁 Script executed:

sed -n '346,375p' ./lib/task_sources.sh

Repository: frankbria/ralph-claude-code

Length of output: 861


🏁 Script executed:

sed -n '355,400p' ./lib/task_sources.sh

Repository: frankbria/ralph-claude-code

Length of output: 1243


🏁 Script executed:

sed -n '520,550p' ./lib/task_sources.sh

Repository: frankbria/ralph-claude-code

Length of output: 840


🏁 Script executed:

head -20 ./lib/task_sources.sh

Repository: frankbria/ralph-claude-code

Length of output: 675


🏁 Script executed:

grep -A5 "import_tasks_from_sources" ./lib/task_sources.sh | head -30

Repository: frankbria/ralph-claude-code

Length of output: 520


Task import can silently drop results if jq is unavailable; beads filter is ignored.

Both fetch_beads_tasks and fetch_github_tasks rely on jq for JSON parsing without a fallback if jq is missing. In fetch_beads_tasks, the fallback to plain text parsing only triggers if the bd list --json command itself fails, not if jq is unavailable. If the JSON call succeeds but jq is not installed, tasks silently becomes empty. This violates the coding guideline requiring JSON fallback to text parsing. Additionally, the filter parameter in fetch_beads_tasks is documented but never applied to the bd query, so caller-supplied filters are ignored.

Restructure both functions to check if tasks are empty after attempting JSON parsing (whether due to missing jq or parse failure) and fall back to text output. Apply the filter parameter to the beads query or post-filter step.

🛠️ Suggested fix for fetch_beads_tasks (apply same pattern to fetch_github_tasks)
-    if json_output=$(bd list --json 2>/dev/null); then
-        # Parse JSON and format as markdown tasks
-        if command -v jq &>/dev/null; then
-            tasks=$(echo "$json_output" | jq -r '
-                .[] |
-                select(.status != "closed") |
-                "- [ ] [\(.id)] \(.title)"
-            ' 2>/dev/null)
-        fi
-    else
-        # Fallback: try plain text output
-        tasks=$(bd list 2>/dev/null | while IFS= read -r line; do
+    if json_output=$(bd list --json 2>/dev/null); then
+        # Parse JSON and format as markdown tasks
+        if command -v jq &>/dev/null; then
+            tasks=$(echo "$json_output" | jq -r '
+                .[] |
+                select(.status != "closed") |
+                "- [ ] [\(.id)] \(.title)"
+            ' 2>/dev/null)
+        fi
+    fi
+
+    # Fallback: try plain text output if JSON parse is unavailable/empty
+    if [[ -z "$tasks" ]]; then
+        tasks=$(bd list 2>/dev/null | while IFS= read -r line; do
             # Extract ID and title from bd list output
             local id title
             id=$(echo "$line" | grep -oE '^[a-z]+-[0-9]+' || echo "")
             title=$(echo "$line" | sed 's/^[a-z]+-[0-9]* *//' || echo "$line")
             if [[ -n "$id" ]]; then
                 echo "- [ ] [$id] $title"
             fi
         done)
     fi
🧰 Tools
🪛 Shellcheck (0.11.0)

[warning] 44-44: filter appears unused. Verify use (or export if used externally).

(SC2034)

🤖 Prompt for AI Agents
In `@lib/task_sources.sh` around lines 43 - 82, fetch_beads_tasks currently runs
bd list --json and only falls back to plain-text if the bd command fails, so if
jq is missing or JSON parsing yields no tasks they get silently dropped; also
the local filter parameter is never applied. Update fetch_beads_tasks to (1)
include the filter when invoking bd (e.g., pass "$filter" to bd list or build
args from filter) or apply the filter in the jq/select step, (2) after capturing
json_output, attempt to parse it with jq only if jq is present, but if jq is
missing or parsing produces an empty $tasks, run the plain-text fallback (bd
list with the same filter) and parse lines to produce the markdown task lines;
mirror this same pattern and fallback behavior in fetch_github_tasks so missing
jq or parse failures never drop results.

Comment thread lib/task_sources.sh
Comment on lines +482 to +533
# Import from beads
if echo "$sources" | grep -qw "beads"; then
local beads_tasks
if beads_tasks=$(fetch_beads_tasks); then
if [[ -n "$beads_tasks" ]]; then
all_tasks="${all_tasks}
# Tasks from beads
${beads_tasks}
"
((source_count++))
fi
fi
fi

# Import from GitHub
if echo "$sources" | grep -qw "github"; then
local github_tasks
if github_tasks=$(fetch_github_tasks "$github_label"); then
if [[ -n "$github_tasks" ]]; then
all_tasks="${all_tasks}
# Tasks from GitHub
${github_tasks}
"
((source_count++))
fi
fi
fi

# Import from PRD
if echo "$sources" | grep -qw "prd"; then
if [[ -n "$prd_file" && -f "$prd_file" ]]; then
local prd_tasks
if prd_tasks=$(extract_prd_tasks "$prd_file"); then
if [[ -n "$prd_tasks" ]]; then
all_tasks="${all_tasks}
# Tasks from PRD
${prd_tasks}
"
((source_count++))
fi
fi
fi
fi

if [[ -z "$all_tasks" ]]; then
return 1
fi

# Normalize and output
normalize_tasks "$all_tasks" "combined"
return 0
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Source headings are converted into tasks during normalization.

normalize_tasks treats every line as a task, so # Tasks from beads/GitHub/PRD headings become checkbox items. This loses source separation and adds noisy tasks. Consider normalizing each source before concatenation (or skipping normalization for heading lines).

♻️ Suggested adjustment
-        if beads_tasks=$(fetch_beads_tasks); then
+        if beads_tasks=$(fetch_beads_tasks); then
+            beads_tasks=$(normalize_tasks "$beads_tasks" "beads")
             if [[ -n "$beads_tasks" ]]; then
                 all_tasks="${all_tasks}
 # Tasks from beads
 ${beads_tasks}
 "
                 ((source_count++))
             fi
         fi
...
-    # Normalize and output
-    normalize_tasks "$all_tasks" "combined"
+    # Already normalized per-source; keep headings intact
+    echo "$all_tasks"
🤖 Prompt for AI Agents
In `@lib/task_sources.sh` around lines 482 - 533, The header lines like "# Tasks
from beads" are being treated as tasks because normalize_tasks treats every line
as a task; fix by normalizing each source's raw output separately and appending
only the normalized results (or strip header lines before normalization).
Specifically, for each fetch function (fetch_beads_tasks -> beads_tasks,
fetch_github_tasks -> github_tasks, extract_prd_tasks -> prd_tasks) call
normalize_tasks on that string and then append the normalized output to
all_tasks (increment source_count as before) instead of inserting literal "#
Tasks ..." headings; alternatively remove any leading '#' comment lines from
each source before calling normalize_tasks. Ensure you update the code paths
that currently build all_tasks and still use source_count, and keep the final
normalize_tasks "$all_tasks" "combined" call only if you intend a second pass
normalization.

Comment thread ralph_enable_ci.sh
Comment on lines +381 to +385
# Verify required files exist
if [[ ! -f ".ralph/PROMPT.md" ]] || [[ ! -f ".ralph/@fix_plan.md" ]]; then
output_error "Required files were not created"
exit $ENABLE_ERROR
fi
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Consider including @AGENT.md in required files verification.

The verification only checks for .ralph/PROMPT.md and .ralph/@fix_plan.md, but check_existing_ralph in enable_core.sh considers .ralph/@AGENT.md as a required file. This inconsistency could lead to success being reported when the configuration is actually incomplete.

🐛 Proposed fix
     # Verify required files exist
-    if [[ ! -f ".ralph/PROMPT.md" ]] || [[ ! -f ".ralph/@fix_plan.md" ]]; then
+    if [[ ! -f ".ralph/PROMPT.md" ]] || [[ ! -f ".ralph/@fix_plan.md" ]] || [[ ! -f ".ralph/@AGENT.md" ]]; then
         output_error "Required files were not created"
         exit $ENABLE_ERROR
     fi
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
# Verify required files exist
if [[ ! -f ".ralph/PROMPT.md" ]] || [[ ! -f ".ralph/@fix_plan.md" ]]; then
output_error "Required files were not created"
exit $ENABLE_ERROR
fi
# Verify required files exist
if [[ ! -f ".ralph/PROMPT.md" ]] || [[ ! -f ".ralph/@fix_plan.md" ]] || [[ ! -f ".ralph/@AGENT.md" ]]; then
output_error "Required files were not created"
exit $ENABLE_ERROR
fi
🤖 Prompt for AI Agents
In `@ralph_enable_ci.sh` around lines 381 - 385, The current post-generation
verification in ralph_enable_ci.sh only checks for ".ralph/PROMPT.md" and
".ralph/@fix_plan.md" but check_existing_ralph (in enable_core.sh) also treats
".ralph/@AGENT.md" as required; update the verification block in
ralph_enable_ci.sh to include ".ralph/@AGENT.md" so the script fails when the
agent file is missing, ensuring consistency with check_existing_ralph.

Comment thread ralph_enable.sh Outdated
Comment thread ralph_enable.sh
Comment thread ralph_loop.sh Outdated
Fixes from PR #124 review:

1. sed -i portability (ralph_enable.sh:456)
   - Use portable sed + mv pattern instead of GNU-only sed -i

2. sed regex portability (lib/task_sources.sh)
   - Replace \s with POSIX [[:space:]] character class
   - Add sed -E flag for extended regex

3. jq availability check (ralph_enable_ci.sh:177)
   - Add check for jq when --json flag is used

4. Unused filter parameter (lib/task_sources.sh:44)
   - Pass filter to bd list --filter command

5. Word-splitting in select_multiple (ralph_enable.sh:322)
   - Return comma-separated indices instead of space-separated text
   - Update caller to parse indices correctly

6. Missing || true for check_existing_ralph (ralph_enable.sh:185)
   - Prevent set -e from exiting on non-zero return

7. select_multiple stdout corruption (lib/wizard_utils.sh)
   - Redirect interactive output to stderr
   - Only final result goes to stdout

8. Color variables not exported (lib/wizard_utils.sh:12)
   - Export WIZARD_* color variables for subshells

9. select_option infinite loop (lib/wizard_utils.sh:179)
   - Add guard for empty options array
@github-actions
Copy link
Copy Markdown

Critical Bug: .ralphrc Configuration Not Working

Issue

The configuration file feature (introduced in this PR) is completely broken. Configuration values from are immediately overwritten by defaults.

Root Cause

In , the function attempts to save and restore environment variable overrides:

The problem: Defaults are set at line 24 () before is called. This means is never empty, so line 130 always restores the default 100, wiping out whatever set.

This affects all configuration variables:

Impact

The entire configuration system is non-functional. Users cannot:

  • Customize max API calls per hour
  • Override tool permissions
  • Enable/disable session continuity
  • Set custom timeouts
  • Adjust verbosity
  • Configure circuit breaker thresholds

This defeats the purpose of adding support in the first place.

Fix

Track which variables were explicitly set by user/environment (vs defaults):

Then set when CLI args or environment variables explicitly set values (e.g., in the handler and wherever env variables are parsed).

Other Issues Already Addressed

✅ macOS compatibility (addressed in commit d84c83e)
✅ premature exit (addressed in commit d84c83e)

Summary

This is a must-fix issue that makes a core feature ( configuration) completely non-functional. The test suite (396 tests) doesn't catch this because it likely doesn't test the loading behavior end-to-end with actual configuration files.

Otherwise, the PR is well-designed with excellent architecture and comprehensive test coverage. The sed injection vulnerabilities and jq dependency checks are also critical fixes identified by other reviewers.

Comment thread ralph_enable_ci.sh
local project_name="$1"
local project_type="$2"

if [[ "$OUTPUT_JSON" == "true" ]]; then
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

$project_name and $project_type are interpolated directly into JSON without escaping. If either contains quotes, backslashes, or newlines, the JSON output will be malformed. Consider using jq to safely construct the JSON object (since jq is already required for --json mode).

🚀 Want me to fix this? Reply ex: "fix it for me".

Comment thread ralph_enable_ci.sh Outdated
Comment thread ralph_enable.sh
Comment on lines +119 to +121
if [[ -n "$2" && ! "$2" =~ ^-- ]]; then
TASK_SOURCE="$2"
shift 2
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The --from option accepts any value without validating it's one of beads, github, or prd. Consider adding validation to fail early with a clear error message for invalid sources.

-                if [[ -n "$2" && ! "$2" =~ ^-- ]]; then
-                    TASK_SOURCE="$2"
-                    shift 2
+                if [[ -n "$2" && ! "$2" =~ ^-- ]]; then
+                    if [[ ! "$2" =~ ^(beads|github|prd)$ ]]; then
+                        echo "Error: Invalid source '$2'. Must be one of: beads, github, prd"
+                        exit 1
+                    fi
+                    TASK_SOURCE="$2"
+                    shift 2

🚀 Want me to fix this? Reply ex: "fix it for me".

Comment thread ralph_enable.sh
Comment on lines +196 to +197
exit $ENABLE_ALREADY_ENABLED
fi
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

After the user confirms continuation at line 196, FORCE_OVERWRITE should be set to true. Otherwise, enable_ralph_in_directory will still detect the complete state and return ENABLE_ALREADY_ENABLED, causing the "Failed to enable Ralph" error despite user confirmation.

Suggested change
exit $ENABLE_ALREADY_ENABLED
fi
echo "Exiting. Use --force to overwrite."
exit $ENABLE_ALREADY_ENABLED
fi
FORCE_OVERWRITE=true

🚀 Want me to fix this? Reply ex: "fix it for me".

@claude
Copy link
Copy Markdown

claude Bot commented Jan 24, 2026

Code Review - Ralph Enable Feature (v0.11.0)

I've conducted a thorough review of this PR. Overall, this is an excellent addition to Ralph with comprehensive test coverage and well-structured code.

Strengths

  1. Excellent Test Coverage: 75 new tests bringing total to 396 with 100% pass rate
  2. Modular Architecture: Clean separation into lib/enable_core.sh, lib/task_sources.sh, and lib/wizard_utils.sh
  3. Idempotency: Proper checks via check_existing_ralph() and is_ralph_enabled()
  4. Security: Good use of array-based command building to prevent shell injection
  5. Documentation: Comprehensive function-level comments and updated CLAUDE.md
  6. CI/CD Integration: Non-interactive ralph-enable-ci with JSON output and proper exit codes

Issues to Address

1. CRITICAL: --force Flag Not Working Properly

The safe_create_file() function in lib/enable_core.sh:164 always skips existing files, which breaks the --force override behavior. Add a force parameter to safe_create_file() and update callers to pass it through.

2. Task Import Header Lines Treated as Tasks

In lib/task_sources.sh:517-518, comment headers like 'Tasks from beads' may get normalized as actual tasks. Filter these out in normalize_tasks().

3. Missing jq Fallback in fetch_beads_tasks

In lib/task_sources.sh:56-62, if jq is missing, tasks remains empty. Add fallback to plain-text parsing when jq is unavailable. Same issue in fetch_github_tasks().

4. Incomplete File Verification in ralph_enable_ci.sh

In ralph_enable_ci.sh:381-385, only PROMPT.md and @fix_plan.md are checked. Add verification for @AGENT.md to match check_existing_ralph().

Suggestions (Non-blocking)

  1. Add validation for project names to prevent path traversal
  2. Add error handling for malformed .ralphrc files
  3. Document performance/cost implications of SESSION_CONTINUITY=true
  4. Log which template directory is being used for debugging

Security Assessment: GOOD

  • No eval/exec of user input
  • Array-based command building prevents shell injection
  • Proper quoting in string interpolation
  • File path validation via safe_create_file()

Recommendation

APPROVE with minor fixes required for the 4 issues above. The core implementation is solid, test coverage is excellent, and this adds significant value to Ralph.

Once the critical --force flag issue and verification gaps are fixed, this is ready to merge.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Fix all issues with AI agents
In `@lib/task_sources.sh`:
- Around line 209-214: The fallback branch ignores the label filter by calling a
hardcoded "gh issue list --state open"; change it to reuse the same gh_args
array so the label/state filters are preserved: run gh with "${gh_args[@]}"
(redirecting stderr to /dev/null) and pipe that output to wc -l (and tr -d ' '
if desired) to compute count; update the fallback that sets the local variable
count to call gh "${gh_args[@]}" instead of the hardcoded command so the label
filter applied earlier is honored (refer to the gh_args array and the count
variable).
- Around line 150-185: The function fetch_github_tasks currently only parses
GitHub JSON via jq, so when jq is missing tasks stays empty; add a fallback
JSON-to-text parser for json_output inside fetch_github_tasks so it behaves like
fetch_beads_tasks. Specifically, after capturing json_output (and checking
command -v jq), if jq is unavailable parse json_output with shell text tools
(grep/sed/awk or perl) to extract .number and .title from the JSON and build
lines like "- [ ] [#<number>] <title>" assigned to tasks; keep existing behavior
of adding label via gh_args and returning non-zero on gh failure (symbols:
fetch_github_tasks, check_github_available, gh_args, json_output, tasks). Ensure
fallback handles escaping/quotes and still prints tasks when non-empty and
returns 0.
♻️ Duplicate comments (3)
lib/task_sources.sh (2)

43-82: jq unavailability still causes silent task loss.

When bd list --json succeeds but jq is unavailable, tasks remains empty and no fallback occurs. The else branch only triggers when bd list --json fails, not when jq parsing is skipped/fails. This was flagged in a prior review.

🐛 Proposed fix to ensure fallback when jq is unavailable
 fetch_beads_tasks() {
     local filter="${1:-status:open}"
     local tasks=""

     # Check if beads is available
     if ! check_beads_available; then
         return 1
     fi

     # Try to get tasks as JSON (pass filter if provided)
     local json_output
     if json_output=$(bd list --json --filter "$filter" 2>/dev/null); then
         # Parse JSON and format as markdown tasks
         if command -v jq &>/dev/null; then
             tasks=$(echo "$json_output" | jq -r '
                 .[] |
                 select(.status != "closed") |
                 "- [ ] [\(.id)] \(.title)"
             ' 2>/dev/null)
         fi
-    else
-        # Fallback: try plain text output
+    fi
+
+    # Fallback: try plain text output if JSON parsing unavailable/empty
+    if [[ -z "$tasks" ]]; then
         tasks=$(bd list 2>/dev/null | while IFS= read -r line; do
             # Extract ID and title from bd list output
             local id title
             id=$(echo "$line" | grep -oE '^[a-z]+-[0-9]+' || echo "")
             title=$(echo "$line" | sed 's/^[a-z]+-[0-9]* *//' || echo "$line")
             if [[ -n "$id" ]]; then
                 echo "- [ ] [$id] $title"
             fi
         done)
     fi

     if [[ -n "$tasks" ]]; then
         echo "$tasks"
         return 0
     else
         return 0  # Empty is not an error
     fi
 }

482-533: Source headings converted into tasks during normalization.

The # Tasks from beads/GitHub/PRD headings added at lines 488, 502, and 517 are passed to normalize_tasks, which converts every non-empty line into a checkbox. This creates spurious task entries like - [ ] # Tasks from beads. This was flagged in a prior review.

🐛 Proposed fix: normalize each source separately
     # Import from beads
     if echo "$sources" | grep -qw "beads"; then
         local beads_tasks
         if beads_tasks=$(fetch_beads_tasks); then
             if [[ -n "$beads_tasks" ]]; then
+                local normalized_beads
+                normalized_beads=$(normalize_tasks "$beads_tasks" "beads")
                 all_tasks="${all_tasks}
 # Tasks from beads
-${beads_tasks}
+${normalized_beads}
 "
                 ((source_count++))
             fi
         fi
     fi
     # ... similar for github and prd ...
-    # Normalize and output
-    normalize_tasks "$all_tasks" "combined"
+    # Output already-normalized tasks with preserved headings
+    echo "$all_tasks" | grep -v '^$'
     return 0
 }
ralph_enable_ci.sh (1)

385-389: Verification missing check for @AGENT.md.

The verification at lines 386-388 only checks for .ralph/PROMPT.md and .ralph/@fix_plan.md, but check_existing_ralph in enable_core.sh considers .ralph/@AGENT.md as a required file. This inconsistency could report success when the configuration is incomplete. This was flagged in a prior review but appears unaddressed.

🐛 Proposed fix
     # Verify required files exist
-    if [[ ! -f ".ralph/PROMPT.md" ]] || [[ ! -f ".ralph/@fix_plan.md" ]]; then
+    if [[ ! -f ".ralph/PROMPT.md" ]] || [[ ! -f ".ralph/@fix_plan.md" ]] || [[ ! -f ".ralph/@AGENT.md" ]]; then
         output_error "Required files were not created"
         exit $ENABLE_ERROR
     fi
🧹 Nitpick comments (3)
lib/task_sources.sh (3)

284-294: Heading extraction loop is dead code.

The section_name variable is assigned but never used. The loop iterates through headings without producing any output or side effects. Consider removing this block or implementing the intended functionality.

♻️ Proposed removal of dead code
-    # Look for headings that might be task sections
-    local headings
-    headings=$(grep -E '^#{1,3}[[:space:]]+(TODO|Tasks|Requirements|Features|Backlog|Sprint)' "$prd_file" 2>/dev/null)
-    if [[ -n "$headings" ]]; then
-        # Extract content after these headings as potential tasks
-        while IFS= read -r heading; do
-            local section_name
-            section_name=$(echo "$heading" | sed -E 's/^#*[[:space:]]*//')
-            # This is informational - actual task extraction would need more context
-        done <<< "$headings"
-    fi

318-340: Unused output_dir parameter and counter-intuitive return value.

The output_dir parameter is declared but never used. Additionally, returning 1 when ralph-import exists (line 335) is confusing—consider documenting this more clearly in the function header or refactoring the logic.

♻️ Suggested improvements
 # convert_prd_with_claude - Full PRD conversion using Claude (calls ralph-import logic)
 #
 # Parameters:
 #   $1 (prd_file) - Path to the PRD file
-#   $2 (output_dir) - Directory to output converted files (optional, defaults to .ralph/)
+#   $2 (output_dir) - Reserved for future use (currently unused)
 #
-# Outputs:
-#   Sets CONVERTED_PROMPT_FILE, CONVERTED_FIX_PLAN_FILE, CONVERTED_SPECS_FILE
-#
 # Returns:
-#   0 - Success
-#   1 - Error
+#   0 - Basic extraction performed (fallback)
+#   1 - Full conversion available but not performed (caller should use ralph-import directly)
 #
 convert_prd_with_claude() {
     local prd_file=$1
-    local output_dir="${2:-.ralph}"
+    # shellcheck disable=SC2034
+    local output_dir="${2:-.ralph}"  # Reserved for future use

355-358: Unused source parameter in normalize_tasks.

The source parameter is declared but never used within the function. If source attribution is planned, consider implementing it; otherwise, remove the parameter to avoid confusion.

Comment thread lib/task_sources.sh
Comment on lines +150 to +185
fetch_github_tasks() {
local label="${1:-}"
local limit="${2:-50}"
local tasks=""

# Check if GitHub is available
if ! check_github_available; then
return 1
fi

# Build gh command
local gh_args=("issue" "list" "--state" "open" "--limit" "$limit" "--json" "number,title,labels")
if [[ -n "$label" ]]; then
gh_args+=("--label" "$label")
fi

# Fetch issues
local json_output
if ! json_output=$(gh "${gh_args[@]}" 2>/dev/null); then
return 1
fi

# Parse JSON and format as markdown tasks
if command -v jq &>/dev/null; then
tasks=$(echo "$json_output" | jq -r '
.[] |
"- [ ] [#\(.number)] \(.title)"
' 2>/dev/null)
fi

if [[ -n "$tasks" ]]; then
echo "$tasks"
fi

return 0
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

No fallback when jq is unavailable for GitHub tasks.

Similar to fetch_beads_tasks, if jq is not installed, tasks remains empty and no error/warning is raised. Per coding guidelines, implement JSON fallback to text parsing.

🐛 Proposed fix
     # Parse JSON and format as markdown tasks
     if command -v jq &>/dev/null; then
         tasks=$(echo "$json_output" | jq -r '
             .[] |
             "- [ ] [#\(.number)] \(.title)"
         ' 2>/dev/null)
+    else
+        # Fallback: parse JSON output without jq using basic pattern matching
+        # Note: This is a best-effort fallback; jq is recommended
+        tasks=$(echo "$json_output" | grep -oE '"number":[0-9]+|"title":"[^"]*"' | \
+            paste - - | sed -E 's/"number":([0-9]+).*"title":"([^"]*)"/- [ ] [#\1] \2/')
     fi
🤖 Prompt for AI Agents
In `@lib/task_sources.sh` around lines 150 - 185, The function fetch_github_tasks
currently only parses GitHub JSON via jq, so when jq is missing tasks stays
empty; add a fallback JSON-to-text parser for json_output inside
fetch_github_tasks so it behaves like fetch_beads_tasks. Specifically, after
capturing json_output (and checking command -v jq), if jq is unavailable parse
json_output with shell text tools (grep/sed/awk or perl) to extract .number and
.title from the JSON and build lines like "- [ ] [#<number>] <title>" assigned
to tasks; keep existing behavior of adding label via gh_args and returning
non-zero on gh failure (symbols: fetch_github_tasks, check_github_available,
gh_args, json_output, tasks). Ensure fallback handles escaping/quotes and still
prints tasks when non-empty and returns 0.

Comment thread lib/task_sources.sh
Comment on lines +209 to +214
local count
if command -v jq &>/dev/null; then
count=$(gh "${gh_args[@]}" 2>/dev/null | jq 'length' 2>/dev/null || echo "0")
else
count=$(gh issue list --state open 2>/dev/null | wc -l | tr -d ' ')
fi
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Fallback count ignores label filter.

When jq is unavailable, the fallback uses gh issue list --state open without the --label filter, returning a count that may differ from the filtered count.

🐛 Proposed fix
     local count
     if command -v jq &>/dev/null; then
         count=$(gh "${gh_args[@]}" 2>/dev/null | jq 'length' 2>/dev/null || echo "0")
     else
-        count=$(gh issue list --state open 2>/dev/null | wc -l | tr -d ' ')
+        # Use same args but without --json for text output
+        local text_args=("issue" "list" "--state" "open")
+        [[ -n "$label" ]] && text_args+=("--label" "$label")
+        count=$(gh "${text_args[@]}" 2>/dev/null | wc -l | tr -d ' ')
     fi
🤖 Prompt for AI Agents
In `@lib/task_sources.sh` around lines 209 - 214, The fallback branch ignores the
label filter by calling a hardcoded "gh issue list --state open"; change it to
reuse the same gh_args array so the label/state filters are preserved: run gh
with "${gh_args[@]}" (redirecting stderr to /dev/null) and pipe that output to
wc -l (and tr -d ' ' if desired) to compute count; update the fallback that sets
the local variable count to call gh "${gh_args[@]}" instead of the hardcoded
command so the label filter applied earlier is honored (refer to the gh_args
array and the count variable).

- Add RESPONSE_ANALYSIS_FILE export to test_session_continuity.bats setup
- Add mock ralph_enable.sh and ralph_enable_ci.sh to test_installation.bats
- Add mock lib files: enable_core.sh, wizard_utils.sh, task_sources.sh, timeout_utils.sh

All 396 tests now pass.
Comment thread lib/wizard_utils.sh
local default_index=$2
shift 2
local options=("$@")
local num_options=${#options[@]}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggestion: Add input guards in wizard_utils.sh functions to validate parameters before computing, avoiding errors from invalid defaults or zero totals.

 local num_options=${#options[@]}
+
+    # Validate default_index
+    if [[ $default_index -lt 1 || $default_index -gt $num_options ]]; then
+        echo "" >&2
+        return 1
+    fi

🚀 Want me to fix this? Reply ex: "fix it for me".

Comment thread lib/enable_core.sh Outdated
@github-actions
Copy link
Copy Markdown

Code Review Summary

This PR adds excellent functionality for onboarding existing projects to Ralph with comprehensive test coverage (75 new tests, 100% pass rate). The modular architecture and user experience are well-designed.

However, two critical bugs remain that must be fixed before merge:


🔴 Critical Issues

1. --force flag completely broken (lib/enable_core.sh)

doesn't accept a force parameter, so bypasses the early check but then silently skips all file creation.

Location:

Fix: Add force parameter and honor it:


2. .ralphrc configuration completely non-functional (ralph_loop.sh)

The entire feature is broken because the save/restore logic always restores default values, ignoring contents.

Location:

Fix: Track which variables were explicitly overridden by CLI/env:

Do the same for all 6 variables: CLAUDE_TIMEOUT_MINUTES, CLAUDE_OUTPUT_FORMAT, CLAUDE_ALLOWED_TOOLS, CLAUDE_USE_CONTINUE, CLAUDE_SESSION_EXPIRY_HOURS, VERBOSE_PROGRESS.


✅ Issues Already Addressed

  • jq dependency check for --json (ralph_enable_ci.sh:175-178) ✅
  • macOS sed compatibility (commit d84c83e) ✅
  • set -e premature exit (commit d84c83e) ✅

📋 Overall Assessment

Code Quality: 4/5
Testing: 5/5
Architecture: 5/5
Functionality: 3/5 (2 critical bugs block core features)

Status: ⚠️ REQUESTED CHANGES REQUIRED

These two bugs completely break the new configuration system and the flag. Once fixed, this PR will be ready to merge and provides significant value for Ralph adoption.

Great work on closing issues #85, #121, #64, #87, #99!

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
tests/integration/test_installation.bats (2)

468-490: Uninstall test should verify removal of new enable commands.

This test verifies removal of the original five commands but doesn't verify that ralph-enable and ralph-enable-ci are also removed during uninstall.

Suggested fix
     # Verify command files are removed
     assert_file_not_exists "$TEST_INSTALL_DIR/ralph"
     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"
+    assert_file_not_exists "$TEST_INSTALL_DIR/ralph-enable"
+    assert_file_not_exists "$TEST_INSTALL_DIR/ralph-enable-ci"

539-589: End-to-end test should verify complete enable functionality installation.

This comprehensive test verifies the complete installation workflow but doesn't include assertions for the new enable scripts and library files that are part of v0.11.0.

Suggested additions (after line 580)
     assert_file_exists "$TEST_RALPH_HOME/ralph_import.sh"
     assert_file_exists "$TEST_RALPH_HOME/migrate_to_ralph_folder.sh"
+    assert_file_exists "$TEST_RALPH_HOME/ralph_enable.sh"
+    assert_file_exists "$TEST_RALPH_HOME/ralph_enable_ci.sh"
+
+    # Verify enable-related lib files
+    assert_file_exists "$TEST_RALPH_HOME/lib/enable_core.sh"
+    assert_file_exists "$TEST_RALPH_HOME/lib/wizard_utils.sh"
+    assert_file_exists "$TEST_RALPH_HOME/lib/task_sources.sh"
+
+    # Verify enable commands installed
+    assert_file_exists "$TEST_INSTALL_DIR/ralph-enable"
+    assert_file_exists "$TEST_INSTALL_DIR/ralph-enable-ci"
🤖 Fix all issues with AI agents
In `@tests/integration/test_installation.bats`:
- Around line 85-119: Add assertions in the specified tests to verify the new
enable commands and libs: in the test "install.sh creates ~/.local/bin commands"
assert that the installed command stubs ralph-enable and ralph-enable-ci exist
in the bin install location (same style as existing checks for other commands);
in "install.sh sets executable permissions" add permission checks for
ralph-enable and ralph-enable-ci in both $TEST_INSTALL_DIR and $TEST_RALPH_HOME
(matching how other scripts are checked); in "install.sh uninstall removes all
files" assert that ralph-enable and ralph-enable-ci are removed after uninstall;
and in "complete installation workflow end-to-end" assert that ralph-enable,
ralph-enable-ci and the new lib files (enable_core.sh, wizard_utils.sh,
task_sources.sh, timeout_utils.sh) are present in the installed lib directory.
Ensure you follow the existing test assertion helpers and naming conventions
used elsewhere in the file.
🧹 Nitpick comments (1)
tests/integration/test_installation.bats (1)

13-124: Consider adding a comment explaining the test strategy for enable mocks.

Per coding guidelines, complex test scenarios should include comments explaining the test strategy. A brief comment above the enable-related mocks (around line 85) would help clarify that these mocks are added to support testing the v0.11.0 enable functionality without invoking the actual wizard behavior.

Suggested comment
 EOF

+    # =========================================================================
+    # Mocks for v0.11.0 enable functionality (ralph-enable wizard/CI commands)
+    # These mocks allow installation tests to verify enable scripts are deployed
+    # without triggering actual interactive or GitHub/PRD integration behavior.
+    # =========================================================================
     cat > "$MOCK_SOURCE_DIR/ralph_enable.sh" << 'EOF'

Based on coding guidelines requiring comments for complex test strategies.

Comment on lines +85 to +119
cat > "$MOCK_SOURCE_DIR/ralph_enable.sh" << 'EOF'
#!/bin/bash
# Mock ralph_enable.sh
echo "Ralph enable running"
EOF

cat > "$MOCK_SOURCE_DIR/ralph_enable_ci.sh" << 'EOF'
#!/bin/bash
# Mock ralph_enable_ci.sh
echo "Ralph enable CI running"
EOF

# Create mock lib files for new enable functionality
cat > "$MOCK_SOURCE_DIR/lib/enable_core.sh" << 'EOF'
#!/bin/bash
# Mock enable_core.sh
check_existing_ralph() { :; }
EOF

cat > "$MOCK_SOURCE_DIR/lib/wizard_utils.sh" << 'EOF'
#!/bin/bash
# Mock wizard_utils.sh
confirm() { :; }
EOF

cat > "$MOCK_SOURCE_DIR/lib/task_sources.sh" << 'EOF'
#!/bin/bash
# Mock task_sources.sh
fetch_beads_tasks() { :; }
EOF

cat > "$MOCK_SOURCE_DIR/lib/timeout_utils.sh" << 'EOF'
#!/bin/bash
# Mock timeout_utils.sh
portable_timeout() { timeout "$@"; }
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Mocks for enable functionality look good, but test assertions are missing.

The mock scripts for ralph_enable.sh, ralph_enable_ci.sh, and the new library files (enable_core.sh, wizard_utils.sh, task_sources.sh, timeout_utils.sh) are correctly structured and follow the existing pattern.

However, while the mocks are in place to support testing the enable functionality, the test assertions in this file haven't been updated to verify that the new commands (ralph-enable, ralph-enable-ci) are actually installed. Specifically:

  1. Test "install.sh creates ~/.local/bin commands" (line 198) should assert ralph-enable and ralph-enable-ci exist.
  2. Test "install.sh sets executable permissions" (line 216) should verify permissions for enable scripts in both $TEST_INSTALL_DIR and $TEST_RALPH_HOME.
  3. Test "install.sh uninstall removes all files" (line 468) should verify enable commands are removed.
  4. Test "complete installation workflow end-to-end" (line 539) should verify enable scripts and lib files are installed.
Suggested additions for test "install.sh creates ~/.local/bin commands" (around line 206)
     assert_file_exists "$TEST_INSTALL_DIR/ralph-import"
     assert_file_exists "$TEST_INSTALL_DIR/ralph-migrate"
+    assert_file_exists "$TEST_INSTALL_DIR/ralph-enable"
+    assert_file_exists "$TEST_INSTALL_DIR/ralph-enable-ci"

     # 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"
+    grep -q "#!/bin/bash" "$TEST_INSTALL_DIR/ralph-enable"
+    grep -q "#!/bin/bash" "$TEST_INSTALL_DIR/ralph-enable-ci"
Suggested additions for test "install.sh sets executable permissions" (around line 231)
     [[ -x "$TEST_RALPH_HOME/ralph_import.sh" ]]
     [[ -x "$TEST_RALPH_HOME/migrate_to_ralph_folder.sh" ]]
+    [[ -x "$TEST_RALPH_HOME/ralph_enable.sh" ]]
+    [[ -x "$TEST_RALPH_HOME/ralph_enable_ci.sh" ]]

     # Verify lib scripts are executable
     [[ -x "$TEST_RALPH_HOME/lib/circuit_breaker.sh" ]]
     [[ -x "$TEST_RALPH_HOME/lib/response_analyzer.sh" ]]
     [[ -x "$TEST_RALPH_HOME/lib/date_utils.sh" ]]
+    [[ -x "$TEST_RALPH_HOME/lib/enable_core.sh" ]]
+    [[ -x "$TEST_RALPH_HOME/lib/wizard_utils.sh" ]]
+    [[ -x "$TEST_RALPH_HOME/lib/task_sources.sh" ]]
🤖 Prompt for AI Agents
In `@tests/integration/test_installation.bats` around lines 85 - 119, Add
assertions in the specified tests to verify the new enable commands and libs: in
the test "install.sh creates ~/.local/bin commands" assert that the installed
command stubs ralph-enable and ralph-enable-ci exist in the bin install location
(same style as existing checks for other commands); in "install.sh sets
executable permissions" add permission checks for ralph-enable and
ralph-enable-ci in both $TEST_INSTALL_DIR and $TEST_RALPH_HOME (matching how
other scripts are checked); in "install.sh uninstall removes all files" assert
that ralph-enable and ralph-enable-ci are removed after uninstall; and in
"complete installation workflow end-to-end" assert that ralph-enable,
ralph-enable-ci and the new lib files (enable_core.sh, wizard_utils.sh,
task_sources.sh, timeout_utils.sh) are present in the installed lib directory.
Ensure you follow the existing test assertion helpers and naming conventions
used elsewhere in the file.

@github-actions
Copy link
Copy Markdown

Posted review comment to PR #124: #124 (comment)

New%20session%20-%202026-01-24T21%3A27%3A46.555Z
opencode session  |  github run

@claude
Copy link
Copy Markdown

claude Bot commented Jan 24, 2026

Code Review - PR 124: Ralph Enable Wizard (v0.11.0)

This is a well-architected feature that significantly improves Ralph's usability for existing projects. The implementation demonstrates strong engineering practices with 75 new tests, modular design, and comprehensive documentation. However, there are critical issues that must be addressed before merge.


Critical Issues (Must Fix Before Merge)

1. .ralphrc Configuration System is Broken (ralph_loop.sh:97-140)

Issue: The configuration file loading logic has a fundamental flaw that makes .ralphrc settings ineffective.

Root Cause: At line 24, defaults are set (MAX_CALLS_PER_HOUR=100), then at line 97-110, these variables are saved to restore later. Since defaults are set at line 24, saved_MAX_CALLS_PER_HOUR is never empty - it's always 100. Line 130 then always overwrites whatever .ralphrc set, restoring the default.

Impact: ALL configuration from .ralphrc is immediately discarded: MAX_CALLS_PER_HOUR, CLAUDE_TIMEOUT_MINUTES, CLAUDE_OUTPUT_FORMAT, CLAUDE_ALLOWED_TOOLS, CLAUDE_USE_CONTINUE, VERBOSE_PROGRESS. This defeats the entire purpose of adding .ralphrc support in this PR.

Fix: Track which variables were explicitly set by environment/CLI vs defaulted, or move the save logic to before defaults are set in the main script.


2. sed Command Injection Risk (ralph_enable.sh:454, 458)

Issue: Using unescaped user input in sed substitution commands. Special characters break sed: --label "ralph/task" breaks (/ is sed delimiter), --label "test&replace" (& has special meaning in sed replacement).

Fix: Use a different delimiter like | or properly escape special characters.


3. Missing jq Validation for --json Flag (ralph_enable_ci.sh:174-180)

Issue: The check happens AFTER setting OUTPUT_JSON=true. Better approach is to move jq check to main execution flow after argument parsing to allow proper error handling.


High Priority Issues

4. Shell Injection Risk in safe_create_file() (lib/enable_core.sh:160)

Issue: Using echo for file writing has edge cases. Content starting with -e, -n, -E flags may be interpreted. Use printf for predictable output.


5. Inconsistent Error Codes in ralph_enable_ci.sh

Issue: Documentation says 0=Success, 1=Error, 2=Already enabled, but code uses exit 1 for various error types. Recommend adding specific error codes for different failure types (3=Invalid arguments, 4=Missing dependencies).


Strengths (Great Work!)

  1. Excellent Test Coverage: 75 new tests with 100% pass rate demonstrates commitment to quality
  2. Modular Architecture: Clear separation between enable_core, wizard_utils, and task_sources
  3. Comprehensive Documentation: CLAUDE.md updates are thorough and well-organized
  4. Idempotency: check_existing_ralph() properly handles partial/complete states
  5. Project Detection: Auto-detection of TypeScript, Python, Rust, Go with framework awareness is impressive
  6. CLI Design: Both interactive and CI versions with consistent interfaces
  7. Error Handling: Most error paths are well-handled with appropriate messages

Recommendation

Do Not Merge until issue No. 1 (.ralphrc configuration broken) is fixed. This is a fundamental flaw that breaks the entire feature.

Issues No. 2 and No. 3 should also be addressed before merge as they represent security and reliability concerns.

Issues No. 4-5 can be addressed in a follow-up PR if needed, but fixing them now would be better.


Summary

This PR adds significant value to Ralph and demonstrates excellent engineering practices. However, the .ralphrc loading bug is critical and must be fixed. Once that's addressed, along with the sed injection and jq validation issues, this will be a solid addition.

Great work on the comprehensive testing and documentation! The modular architecture is particularly well done.

Files reviewed: ralph_enable.sh, ralph_enable_ci.sh, lib/enable_core.sh, lib/wizard_utils.sh, lib/task_sources.sh, ralph_loop.sh, test files, CLAUDE.md

Related Issues: Addresses issue 85

1. .ralphrc Configuration Loading Fix:
   - Captured env var state BEFORE setting defaults with _env_* variables
   - load_ralphrc now only restores values explicitly set by environment
   - .ralphrc settings are now properly applied (not overwritten by defaults)

2. sed Command Injection Fix:
   - Replaced sed with awk for .ralphrc updates in ralph_enable.sh
   - awk -v pattern safely handles user input without shell injection risk

3. Shell Injection Fix in safe_create_file():
   - Replaced echo with printf '%s\n' for safer content handling
   - Prevents issues with backslashes, -n, and special characters

4. Specific Error Codes:
   - Added ENABLE_INVALID_ARGS=3 for argument errors
   - Added ENABLE_FILE_NOT_FOUND=4 for missing files
   - Added ENABLE_DEPENDENCY_MISSING=5 for missing deps (e.g., jq)
   - Added ENABLE_PERMISSION_DENIED=6 for permission errors
   - Updated ralph_enable.sh and ralph_enable_ci.sh to use specific codes

5. Added tests for .ralphrc loading pattern verification

Test count: 398 (up from 396)
Comment thread ralph_enable_ci.sh
shift
;;
-v|--version)
if [[ "$OUTPUT_JSON" == "true" ]]; then
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The --version handler checks OUTPUT_JSON before --json may have been parsed (e.g., --version --json). Consider parsing all arguments first, then handling --version in main() after parse_arguments() completes.

🚀 Want me to fix this? Reply ex: "fix it for me".

Comment thread ralph_enable_ci.sh
beads)
if beads_tasks=$(fetch_beads_tasks 2>/dev/null); then
imported_tasks="$beads_tasks"
TASKS_IMPORTED=$(echo "$imported_tasks" | grep -c '^\- \[' || echo "0")
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

grep -c outputs "0" before returning exit code 1 when no matches, so || echo "0" appends a second "0", making TASKS_IMPORTED a multi-line string. Consider capturing to a temp variable and using || true instead, or use grep -c ... 2>/dev/null || : then default empty to "0".

Suggested change
TASKS_IMPORTED=$(echo "$imported_tasks" | grep -c '^\- \[' || echo "0")
TASKS_IMPORTED=$(echo "$imported_tasks" | grep -c '^\- \[' 2>/dev/null) || TASKS_IMPORTED=0

🚀 Want me to fix this? Reply ex: "fix it for me".

Comment thread lib/enable_core.sh
Comment thread lib/wizard_utils.sh
local response

local yn_hint="[y/N]"
if [[ "${default,,}" == "y" ]]; then
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The ${default,,} lowercase syntax requires bash 4.0+. macOS ships with bash 3.2 by default and will fail. Consider using a POSIX-compatible alternative like $(echo "$default" | tr '[:upper:]' '[:lower:]') or [[ "$default" == [Yy]* ]].

🚀 Want me to fix this? Reply ex: "fix it for me".

Comment thread ralph_enable.sh
# Using awk instead of sed to avoid command injection from user input
if [[ -f ".ralphrc" ]]; then
# Update max calls (awk safely handles the value without shell interpretation)
awk -v val="$CONFIG_MAX_CALLS" '/^MAX_CALLS_PER_HOUR=/{$0="MAX_CALLS_PER_HOUR="val}1' .ralphrc > .ralphrc.tmp && mv .ralphrc.tmp .ralphrc
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The awk pipeline may leave .ralphrc.tmp orphaned if mv fails. Consider using a trap to clean up the temp file on failure, or checking each command's exit status explicitly.

🚀 Want me to fix this? Reply ex: "fix it for me".

Comment thread lib/task_sources.sh
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In `@lib/enable_core.sh`:
- Around line 712-718: In enable_ralph_in_directory() the local variable
skip_tasks (from ENABLE_SKIP_TASKS) is declared but never used; either remove
the declaration (delete the local skip_tasks line) or implement the skip
behavior by checking skip_tasks within the function (e.g., test the variable and
bypass task-related logic where task_content or task execution occurs); update
any conditionals that run task creation/processing to respect skip_tasks so
ENABLE_SKIP_TASKS actually prevents those actions.
♻️ Duplicate comments (5)
lib/enable_core.sh (1)

144-172: --force flag is not propagated to safe_create_file, leaving existing files intact.

The safe_create_file function always skips existing files regardless of the force flag passed to enable_ralph_in_directory. When --force is specified, users expect existing configuration files to be overwritten.

🐛 Proposed fix
 safe_create_file() {
     local target=$1
     local content=$2
+    local force="${3:-false}"
 
-    if [[ -f "$target" ]]; then
+    if [[ -f "$target" && "$force" != "true" ]]; then
         enable_log "SKIP" "$target already exists"
         return 1
     fi
+    
+    if [[ -f "$target" && "$force" == "true" ]]; then
+        enable_log "WARN" "Overwriting $target (--force)"
+    fi
 
     # Create parent directory if needed

Then update callers in enable_ralph_in_directory (lines 756-779):

-    safe_create_file ".ralph/PROMPT.md" "$prompt_content"
+    safe_create_file ".ralph/PROMPT.md" "$prompt_content" "$force"
     ...
-    safe_create_file ".ralph/@AGENT.md" "$agent_content"
+    safe_create_file ".ralph/@AGENT.md" "$agent_content" "$force"
     ...
-    safe_create_file ".ralph/@fix_plan.md" "$fix_plan_content"
+    safe_create_file ".ralph/@fix_plan.md" "$fix_plan_content" "$force"
     ...
-    safe_create_file ".ralphrc" "$ralphrc_content"
+    safe_create_file ".ralphrc" "$ralphrc_content" "$force"
ralph_enable_ci.sh (2)

391-395: Include @AGENT.md in required files verification for consistency.

check_existing_ralph in enable_core.sh considers .ralph/@AGENT.md as a required file, but this verification only checks PROMPT.md and @fix_plan.md. This inconsistency could report success when configuration is incomplete.

🐛 Proposed fix
     # Verify required files exist
-    if [[ ! -f ".ralph/PROMPT.md" ]] || [[ ! -f ".ralph/@fix_plan.md" ]]; then
+    if [[ ! -f ".ralph/PROMPT.md" ]] || [[ ! -f ".ralph/@fix_plan.md" ]] || [[ ! -f ".ralph/@AGENT.md" ]]; then
         output_error "Required files were not created"
         exit $ENABLE_ERROR
     fi

234-257: JSON output may be malformed if project_name or project_type contain special characters.

Variables are interpolated directly into the JSON template. If either contains quotes, backslashes, or newlines, the JSON output will be invalid. Since jq is already required for --json mode, use it for safe JSON construction.

🐛 Proposed fix
 output_success() {
     local project_name="$1"
     local project_type="$2"
 
     if [[ "$OUTPUT_JSON" == "true" ]]; then
         local files_json
         files_json=$(printf '%s\n' "${CREATED_FILES[@]}" | jq -R . | jq -s .)
 
-        cat << EOF
-{
-    "success": true,
-    "project_name": "$project_name",
-    "project_type": "$project_type",
-    "files_created": $files_json,
-    "tasks_imported": $TASKS_IMPORTED,
-    "message": "Ralph enabled successfully"
-}
-EOF
+        jq -n \
+            --arg name "$project_name" \
+            --arg type "$project_type" \
+            --argjson files "$files_json" \
+            --argjson tasks "$TASKS_IMPORTED" \
+            '{
+                success: true,
+                project_name: $name,
+                project_type: $type,
+                files_created: $files,
+                tasks_imported: $tasks,
+                message: "Ralph enabled successfully"
+            }'
     else
ralph_enable.sh (2)

193-201: Missing FORCE_OVERWRITE=true after user confirms continuation.

When the user confirms they want to continue despite Ralph already being enabled (line 194), FORCE_OVERWRITE should be set to true. Otherwise, enable_ralph_in_directory will still detect the complete state and return ENABLE_ALREADY_ENABLED.

🐛 Proposed fix
                 if [[ "$NON_INTERACTIVE" != "true" ]]; then
                     if ! confirm "Do you want to continue anyway?" "n"; then
                         echo "Exiting. Use --force to overwrite."
                         exit $ENABLE_ALREADY_ENABLED
                     fi
+                    FORCE_OVERWRITE=true
                 else

119-126: The --from option accepts any value without validation.

Invalid sources like --from invalid are accepted silently and will fail later in task import. Add early validation for clearer error messages.

🐛 Proposed fix
             --from)
                 if [[ -n "$2" && ! "$2" =~ ^-- ]]; then
+                    if [[ ! "$2" =~ ^(beads|github|prd)$ ]]; then
+                        echo "Error: Invalid source '$2'. Must be one of: beads, github, prd" >&2
+                        exit $ENABLE_INVALID_ARGS
+                    fi
                     TASK_SOURCE="$2"
                     shift 2
🧹 Nitpick comments (2)
lib/enable_core.sh (2)

101-101: RALPH_MISSING_FILES is set but never used outside this file.

Static analysis flagged this variable as unused. While it's set in check_existing_ralph, it's only referenced in the interactive wizard's Phase 1 output (Line 207 in ralph_enable.sh). Consider exporting it or documenting its cross-file usage.

♻️ Proposed fix
+# Export for use by callers (e.g., ralph_enable.sh phase_environment_detection)
+export RALPH_MISSING_FILES
+
 check_existing_ralph() {
     RALPH_STATE="none"
-    RALPH_MISSING_FILES=()
+    RALPH_MISSING_FILES=()  # Exported for interactive wizard display

319-347: Python project detection may overwrite JavaScript/TypeScript detection.

If a project has both package.json and pyproject.toml (e.g., a monorepo or a JS project with Python tooling), Python detection will overwrite the earlier JavaScript/TypeScript detection results. Consider using elif or early return to respect detection precedence.

♻️ Proposed fix
-    # Detect from pyproject.toml or setup.py (Python)
-    if [[ -f "pyproject.toml" ]] || [[ -f "setup.py" ]]; then
+    # Detect from pyproject.toml or setup.py (Python) - only if not already detected
+    if [[ "$DETECTED_PROJECT_TYPE" == "unknown" ]] && { [[ -f "pyproject.toml" ]] || [[ -f "setup.py" ]]; }; then
         DETECTED_PROJECT_TYPE="python"

Apply similar changes to Rust (line 350) and Go (line 359) detection blocks.

Comment thread lib/enable_core.sh
Comment on lines +712 to +718
enable_ralph_in_directory() {
local force="${ENABLE_FORCE:-false}"
local skip_tasks="${ENABLE_SKIP_TASKS:-false}"
local project_name="${ENABLE_PROJECT_NAME:-}"
local project_type="${ENABLE_PROJECT_TYPE:-}"
local task_content="${ENABLE_TASK_CONTENT:-}"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

skip_tasks variable is declared but never used.

The ENABLE_SKIP_TASKS environment variable is read into skip_tasks but never referenced in the function logic. Either remove it or implement the skip-tasks behavior.

🐛 Proposed fix (remove unused variable)
 enable_ralph_in_directory() {
     local force="${ENABLE_FORCE:-false}"
-    local skip_tasks="${ENABLE_SKIP_TASKS:-false}"
     local project_name="${ENABLE_PROJECT_NAME:-}"
     local project_type="${ENABLE_PROJECT_TYPE:-}"
     local task_content="${ENABLE_TASK_CONTENT:-}"
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
enable_ralph_in_directory() {
local force="${ENABLE_FORCE:-false}"
local skip_tasks="${ENABLE_SKIP_TASKS:-false}"
local project_name="${ENABLE_PROJECT_NAME:-}"
local project_type="${ENABLE_PROJECT_TYPE:-}"
local task_content="${ENABLE_TASK_CONTENT:-}"
enable_ralph_in_directory() {
local force="${ENABLE_FORCE:-false}"
local project_name="${ENABLE_PROJECT_NAME:-}"
local project_type="${ENABLE_PROJECT_TYPE:-}"
local task_content="${ENABLE_TASK_CONTENT:-}"
🧰 Tools
🪛 Shellcheck (0.11.0)

[warning] 714-714: skip_tasks appears unused. Verify use (or export if used externally).

(SC2034)

🤖 Prompt for AI Agents
In `@lib/enable_core.sh` around lines 712 - 718, In enable_ralph_in_directory()
the local variable skip_tasks (from ENABLE_SKIP_TASKS) is declared but never
used; either remove the declaration (delete the local skip_tasks line) or
implement the skip behavior by checking skip_tasks within the function (e.g.,
test the variable and bypass task-related logic where task_content or task
execution occurs); update any conditionals that run task creation/processing to
respect skip_tasks so ENABLE_SKIP_TASKS actually prevents those actions.

@claude
Copy link
Copy Markdown

claude Bot commented Jan 25, 2026

Follow-Up Code Review - PR 124

I've reviewed the latest commits. Good progress, but one critical issue remains.

✅ Issues Fixed

1. .ralphrc Configuration Loading - FIXED ✓
Lines 29-37 in ralph_loop.sh now capture environment state BEFORE defaults, and load_ralphrc() properly restores only explicitly-set env vars.

2. Shell Injection via echo - FIXED ✓
Line 165 in lib/enable_core.sh now uses printf instead of echo for safer output.

🔴 Critical Issue: --force Flag Non-Functional

Problem: The --force flag is accepted but files are never actually overwritten.

Root cause: safe_create_file() at lib/enable_core.sh:144-151 always skips existing files, regardless of ENABLE_FORCE value.

Evidence: When force=true, enable_ralph_in_directory bypasses early return (line 722-726) but then calls safe_create_file which returns 1 for all existing files without overwriting them.

Impact: Users running ralph-enable --force --project-name new-name will see success but files still contain old content.

Fix needed: Make safe_create_file check ENABLE_FORCE and delete/overwrite existing files when true, or delete files before calling safe_create_file in enable_ralph_in_directory.

Test gap: tests/unit/test_enable_core.bats:335 only checks assert_success, doesn't verify file contents actually changed.

Summary

  • .ralphrc loading - FIXED
  • echo injection - FIXED
  • --force flag - NEEDS FIX

Excellent work overall with 75 new tests and modular design. Once --force is fixed, ready to merge.

The --force flag was accepted but safe_create_file() always skipped
existing files regardless of ENABLE_FORCE value.

Changes:
- safe_create_file() now checks ENABLE_FORCE environment variable
- When ENABLE_FORCE="true", overwrites existing files instead of skipping
- Added proper logging for overwrite operations

Added tests:
- Verify enable_ralph_in_directory actually changes file contents with --force
- Test safe_create_file overwrites when ENABLE_FORCE is true
- Test safe_create_file skips when ENABLE_FORCE is false

Test count: 400 (up from 398)
@github-actions
Copy link
Copy Markdown

Code Review Summary

This PR adds excellent functionality for enabling Ralph in existing projects with comprehensive test coverage (75 new tests, 100% pass rate). The modular architecture with separate libraries for enable_core, task_sources, and wizard_utils demonstrates good design principles.

However, one critical issue remains that must be fixed before merge.


🔴 Critical Issue: --force Flag Completely Non-Functional

Location: lib/enable_core.sh:712-779

Problem: The --force flag bypasses the early "already enabled" check but fails to actually overwrite existing files.

Root Cause: safe_create_file() (lines 144-172) always skips existing files. It does not accept or check a force parameter, so even when enable_ralph_in_directory is called with force=true, all files are skipped:

safe_create_file() {
    local target=$1
    local content=$2
    # No force parameter!
    
    if [[ -f "$target" ]]; then  # Always skips if file exists
        enable_log "SKIP" "$target already exists"
        return 1
    fi

Impact: Users running ralph-enable --force --project-name new-name see success messages but files still contain old content. The entire force override feature is broken.

Fix Required:

safe_create_file() {
    local target=$1
    local content=$2
    local force="${3:-false}"  # Add force parameter
    
    if [[ -f "$target" && "$force" != "true" ]]; then
        enable_log "SKIP" "$target already exists"
        return 1
    fi
    
    if [[ -f "$target" && "$force" == "true" ]]; then
        enable_log "WARN" "Overwriting $target (--force)"
        rm -f "$target"  # Delete before writing
   
    
    # ... rest of function
}

# In enable_ralph_in_directory (lines 756-779):
safe_create_file ".ralph/PROMPT.md" "$prompt_content" "$force"
safe_create_file ".ralph/@AGENT.md" "$agent_content" "$force"
safe_create_file ".ralph/@fix_plan.md" "$fix_plan_content" "$force"
safe_create_file ".ralphrc" "$ralphrc_content" "$force"

Test Gap: tests/unit/test_enable_core.bats:335-346 only checks assert_success but doesn't verify file contents actually changed.


✅ Strengths

  1. Excellent Test Coverage: 75 new tests with 100% pass rate demonstrates commitment to quality
  2. Modular Architecture: Clear separation between enable_core, wizard_utils, and task_sources follows DRY principles
  3. Comprehensive Documentation: CLAUDE.md updates are thorough and well-organized
  4. Idempotency: check_existing_ralph() properly handles partial/complete states
  5. Project Detection: Auto-detection of TypeScript, Python, Rust, Go with framework awareness is impressive
  6. Security: Good use of array-based command building prevents shell injection
  7. CI/CD Integration: Non-interactive version with JSON output and proper exit codes

✅ Issues Already Addressed

  • ✅ .ralphrc configuration loading (commit e427e67)
  • ✅ Shell injection via echo (commit e427e67)
  • ✅ macOS sed compatibility (commit d84c83e)
  • ✅ set -e premature exit (commit d84c83e)
  • ✅ jq dependency check for --json (commit e427e67)

🟡 Minor Issues (Can Be Follow-up PR)

  1. Task source headings converted to tasks (lib/task_sources.sh:533) - # Tasks from beads comment lines become checkbox items during normalization
  2. Missing @AGENT.md verification (ralph_enable_ci.sh:381-385) - Verification only checks PROMPT.md and @fix_plan.md
  3. jq fallback when unavailable (lib/task_sources.sh:82, 185) - When jq is missing, tasks are silently dropped instead of falling back to text parsing

📋 Recommendation

Status: ⚠️ REQUESTED CHANGES REQUIRED

The --force flag issue is a critical bug that completely breaks a core feature of this PR. This must be fixed before merge.

The overall implementation is excellent with strong architecture, testing, and documentation. Once the force flag issue is resolved, this PR will be ready to merge and provides significant value for Ralph adoption.

Great work on closing issues #85, #121, #64, #87, #99!

@github-actions
Copy link
Copy Markdown

Code review posted successfully. The review identified the critical --force flag bug as the only remaining blocker, while acknowledging the excellent overall implementation, comprehensive test coverage (100% pass rate on 396 tests), and the issues already addressed in commits d84c83e and e427e67.

New%20session%20-%202026-01-25T21%3A30%3A32.084Z
opencode session  |  github run

Comment thread ralph_enable_ci.sh
Comment thread lib/enable_core.sh
# Write content to file using printf to avoid shell injection
# printf '%s\n' is safer than echo for arbitrary content (handles backslashes, -n, etc.)
if printf '%s\n' "$content" > "$target" 2>/dev/null; then
if [[ -f "$target" ]] && [[ "$force" == "true" ]]; then
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The condition [[ -f "$target" ]] at line 176 will always be true since you just wrote the file. Consider checking if the file existed before writing (use the result from the earlier check at line 152) to correctly distinguish "Overwrote" vs "Created" messages.

🚀 Want me to fix this? Reply ex: "fix it for me".

Comment thread lib/task_sources.sh
# Tasks from beads
${beads_tasks}
"
((source_count++))
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

((source_count++)) returns exit code 1 when source_count is 0 (pre-increment value). Under set -e, this will exit the script. Consider using ((source_count++)) || true or source_count=$((source_count + 1)).

🚀 Want me to fix this? Reply ex: "fix it for me".

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In `@lib/enable_core.sh`:
- Around line 767-793: The snippet ignores return codes from safe_create_file so
write failures (safe_create_file returning 2) can be missed; after each call to
safe_create_file for ".ralph/PROMPT.md", ".ralph/@AGENT.md",
".ralph/@fix_plan.md", and ".ralphrc" check its exit status ($?) and if it
indicates a hard failure (e.g., return 2) log an error via process/logger (or
echo to stderr) including the target filename and exit code and then exit with
non-zero to prevent printing “Ralph enabled successfully”; apply the same check
after generate_* invocations where safe_create_file is called and ensure
detect_task_sources and task_sources logic runs only if file creation succeeded.
♻️ Duplicate comments (1)
lib/enable_core.sh (1)

726-731: ENABLE_SKIP_TASKS has no effect.

skip_tasks is read but never used, so the flag is a no-op. Either remove it or implement the skip behavior (e.g., ignore task_content when skip_tasks=true).
As per coding guidelines, this should be wired or removed.

🔧 Suggested wiring
 enable_ralph_in_directory() {
     local force="${ENABLE_FORCE:-false}"
     local skip_tasks="${ENABLE_SKIP_TASKS:-false}"
     local project_name="${ENABLE_PROJECT_NAME:-}"
     local project_type="${ENABLE_PROJECT_TYPE:-}"
     local task_content="${ENABLE_TASK_CONTENT:-}"
+
+    if [[ "$skip_tasks" == "true" ]]; then
+        task_content=""
+    fi
🧹 Nitpick comments (1)
lib/enable_core.sh (1)

22-28: Add a brief function-level comment for _color.

The coding guidelines require function-level comments for all bash functions; _color is missing one.
As per coding guidelines, please add a short doc comment.

Comment thread lib/enable_core.sh
Comment on lines +767 to +793
# Generate and create files
local prompt_content
prompt_content=$(generate_prompt_md "$project_name" "$DETECTED_PROJECT_TYPE" "$DETECTED_FRAMEWORK")
safe_create_file ".ralph/PROMPT.md" "$prompt_content"

local agent_content
agent_content=$(generate_agent_md "$DETECTED_BUILD_CMD" "$DETECTED_TEST_CMD" "$DETECTED_RUN_CMD")
safe_create_file ".ralph/@AGENT.md" "$agent_content"

local fix_plan_content
fix_plan_content=$(generate_fix_plan_md "$task_content")
safe_create_file ".ralph/@fix_plan.md" "$fix_plan_content"

# Detect task sources for .ralphrc
detect_task_sources
local task_sources="local"
if [[ "$DETECTED_BEADS_AVAILABLE" == "true" ]]; then
task_sources="beads,$task_sources"
fi
if [[ "$DETECTED_GITHUB_AVAILABLE" == "true" ]]; then
task_sources="github,$task_sources"
fi

# Generate .ralphrc
local ralphrc_content
ralphrc_content=$(generate_ralphrc "$project_name" "$DETECTED_PROJECT_TYPE" "$task_sources")
safe_create_file ".ralphrc" "$ralphrc_content"
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Handle safe_create_file failures explicitly.

safe_create_file can return 2 on write failure, but the return codes are ignored. This can emit “Ralph enabled successfully” even when files weren’t created.

🛠️ Proposed fix (check for hard failures only)
     local prompt_content
     prompt_content=$(generate_prompt_md "$project_name" "$DETECTED_PROJECT_TYPE" "$DETECTED_FRAMEWORK")
-    safe_create_file ".ralph/PROMPT.md" "$prompt_content"
+    safe_create_file ".ralph/PROMPT.md" "$prompt_content"
+    rc=$?
+    if [[ $rc -eq 2 ]]; then
+        enable_log "ERROR" "Failed to write .ralph/PROMPT.md"
+        return $ENABLE_ERROR
+    fi

     local agent_content
     agent_content=$(generate_agent_md "$DETECTED_BUILD_CMD" "$DETECTED_TEST_CMD" "$DETECTED_RUN_CMD")
-    safe_create_file ".ralph/@AGENT.md" "$agent_content"
+    safe_create_file ".ralph/@AGENT.md" "$agent_content"
+    rc=$?
+    if [[ $rc -eq 2 ]]; then
+        enable_log "ERROR" "Failed to write .ralph/@AGENT.md"
+        return $ENABLE_ERROR
+    fi

     local fix_plan_content
     fix_plan_content=$(generate_fix_plan_md "$task_content")
-    safe_create_file ".ralph/@fix_plan.md" "$fix_plan_content"
+    safe_create_file ".ralph/@fix_plan.md" "$fix_plan_content"
+    rc=$?
+    if [[ $rc -eq 2 ]]; then
+        enable_log "ERROR" "Failed to write .ralph/@fix_plan.md"
+        return $ENABLE_ERROR
+    fi
@@
     local ralphrc_content
     ralphrc_content=$(generate_ralphrc "$project_name" "$DETECTED_PROJECT_TYPE" "$task_sources")
-    safe_create_file ".ralphrc" "$ralphrc_content"
+    safe_create_file ".ralphrc" "$ralphrc_content"
+    rc=$?
+    if [[ $rc -eq 2 ]]; then
+        enable_log "ERROR" "Failed to write .ralphrc"
+        return $ENABLE_ERROR
+    fi
🤖 Prompt for AI Agents
In `@lib/enable_core.sh` around lines 767 - 793, The snippet ignores return codes
from safe_create_file so write failures (safe_create_file returning 2) can be
missed; after each call to safe_create_file for ".ralph/PROMPT.md",
".ralph/@AGENT.md", ".ralph/@fix_plan.md", and ".ralphrc" check its exit status
($?) and if it indicates a hard failure (e.g., return 2) log an error via
process/logger (or echo to stderr) including the target filename and exit code
and then exit with non-zero to prevent printing “Ralph enabled successfully”;
apply the same check after generate_* invocations where safe_create_file is
called and ensure detect_task_sources and task_sources logic runs only if file
creation succeeded.

@claude
Copy link
Copy Markdown

claude Bot commented Jan 25, 2026

Code Review - PR 124: Ralph Enable Feature (v0.11.0)

Overview

This is an excellent addition to Ralph that addresses a real user need - enabling Ralph in existing projects. The implementation is comprehensive, well-tested, and follows Ralph's existing architectural patterns.

Summary: APPROVED with minor recommendations


Strengths

1. Excellent Architecture & Design

  • Modular library structure following established patterns
  • Separation of concerns between interactive and non-interactive versions
  • Idempotency built-in with check_existing_ralph() and proper state management
  • Well-defined exit codes (0=success, 1=error, 2=already enabled, 3-6 for specific failures)

2. Security & Safety

  • No shell injection vulnerabilities - proper use of arrays for command building
  • No dangerous rm -rf operations in enable_core.sh
  • No eval or unquoted exec usage
  • Safe file operations with safe_create_file() and safe_create_dir()

3. Comprehensive Testing

  • 75 new tests (30 enable_core + 23 task_sources + 22 integration)
  • 396 total tests maintaining 100% pass rate
  • Test coverage for edge cases: idempotency, partial state, project detection
  • Proper test isolation with temp directories

4. User Experience

  • Two interfaces for different use cases (interactive wizard vs CI automation)
  • Auto-detection of project type, framework, git repo, task sources
  • Multiple task sources (beads, GitHub Issues, PRD documents)
  • JSON output mode for machine parsing in CI environments

5. Documentation

  • CLAUDE.md updated with new commands, library components, test counts
  • Inline documentation with function headers describing parameters and returns
  • Template file (ralphrc.template) with comprehensive comments

Code Quality Issues (Minor)

1. Potential Code Execution in .ralphrc Loading (ralph_loop.sh:109-145)
The load_ralphrc() function uses source to load user-provided configuration, which could theoretically execute arbitrary code if .ralphrc is malicious.

Risk: LOW (user controls their own .ralphrc)
Recommendation: Add a warning comment that .ralphrc should not be sourced from untrusted sources.

2. Error Handling in Task Import Functions
In lib/task_sources.sh, some functions silently return 0 on empty results (line 80). While intentional, it could mask real errors.

Recommendation: Consider distinguishing between no tasks found vs command failed for better debuggability.

3. Missing Validation for Project Type Override (ralph_enable_ci.sh)
The --project-type flag accepts arbitrary values without validation against known types.

Risk: User could specify invalid type, leading to incorrect template generation
Recommendation: Add validation in argument parsing to ensure valid project types


Performance Considerations

1. PRD File Search Efficiency (lib/enable_core.sh:446-454)
The detect_task_sources() function runs multiple find commands in nested loops (4 directories × 5 patterns = 20 find operations).

Performance: Acceptable for small projects, could be slow for large monorepos
Recommendation: Consider combining patterns with -o flag to reduce find invocations

2. jq Availability Checks
Multiple functions check command -v jq repeatedly. Consider caching the result in a global variable during initialization.


Best Practices Adherence

The code follows Ralphs documented standards from CLAUDE.md:

  • 100% test pass rate maintained
  • Conventional commit format in commit messages
  • Clear inline documentation for complex logic
  • Modular library architecture following existing patterns
  • CLAUDE.md updated with new features and commands
  • Template files maintained (ralphrc.template added)
  • No TODO/FIXME/HACK comments left in code

Integration Quality

.ralphrc Configuration System
The implementation of .ralphrc loading in ralph_loop.sh is well-designed:

  1. Correct precedence order: Environment > .ralphrc > Defaults
  2. Pre-capture of env vars before setting defaults
  3. Selective restoration of only user-set environment variables
  4. Variable name mapping (ALLOWED_TOOLS to CLAUDE_ALLOWED_TOOLS)

This ensures .ralphrc can override defaults, but environment variables always win.


Testing Quality

Reviewed test files:

  • test_enable_core.bats (30 tests): Thorough coverage of idempotency, file operations, project detection
  • test_task_sources.bats (23 tests): Validates beads, GitHub, PRD integration
  • test_ralph_enable.bats (22 tests): E2E testing of wizard and CI versions

Test quality highlights:

  • Proper isolation with temp directories
  • Mock infrastructure for external commands (bd, gh)
  • Edge case coverage (partial state, missing dependencies, invalid inputs)
  • JSON output validation in CI tests

Recommendations

Priority 1 (Security - Minor)

  1. Add warning comment about .ralphrc sourcing security (1 line change)

Priority 2 (Robustness - Optional)
2. Add project type validation in ralph_enable_ci.sh
3. Improve error distinction in task import functions

Priority 3 (Performance - Optional)
4. Optimize find command patterns in detect_task_sources()
5. Cache jq availability check in global variable

Priority 4 (Documentation - Nice to have)
6. Add examples/ directory with sample .ralphrc files for different project types


Conclusion

This is high-quality work that demonstrates:

  • Strong understanding of Ralphs architecture
  • Excellent test coverage and TDD practices
  • Attention to security and safety
  • Good UX design with both interactive and automated interfaces

The identified issues are all minor and non-blocking. The code is production-ready as-is.

Recommendation: APPROVE and MERGE

The feature successfully addresses issues No.85, No.121, No.64, No.87, No.99 and provides significant value to Ralph users who want to adopt it in existing projects.


Related Files Reviewed

  • ralph_enable.sh (572 lines)
  • ralph_enable_ci.sh (395 lines)
  • lib/enable_core.sh (796 lines)
  • lib/task_sources.sh (550 lines)
  • lib/wizard_utils.sh (531 lines)
  • ralph_loop.sh (load_ralphrc integration)
  • templates/ralphrc.template (95 lines)
  • Test files: test_enable_core.bats, test_task_sources.bats, test_ralph_enable.bats

Total new code: approximately 4,012 lines
Test coverage: 75 new tests (100% pass rate)
Files modified: 15 (12 new, 3 modified)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Best way to drop into a existing project?

1 participant