diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 0000000..988e705 --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,42 @@ +name: Test Ralph Extension + +on: + push: + branches: [ main ] + pull_request: + branches: [ main ] + +jobs: + test: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + + - name: Install dependencies + run: | + sudo apt-get update + sudo apt-get install -y jq + + - name: Make scripts executable + run: | + chmod +x scripts/*.sh + chmod +x hooks/*.sh + chmod +x tests/*.sh + + - name: Run setup tests + run: bash tests/setup_test.sh + + - name: Run hook tests + run: bash tests/hook_test.sh + + - name: Run status tests + run: bash tests/status_test.sh + + - name: Validate scripts + run: | + bash -n scripts/setup.sh + bash -n scripts/cancel.sh + bash -n scripts/status.sh + bash -n scripts/validate.sh + bash -n hooks/stop-hook.sh diff --git a/README.md b/README.md index 0a3dc53..4820fcc 100644 --- a/README.md +++ b/README.md @@ -28,6 +28,14 @@ Install the extension directly from GitHub: gemini extensions install https://github.com/gemini-cli-extensions/ralph --auto-update ``` +### Verify Installation + +After installing, validate your setup: + +```bash +bash ~/.gemini/extensions/ralph/scripts/validate.sh +``` + ## Configuration To use Ralph, you must enable hooks and preview features in your `~/.gemini/settings.json`: @@ -60,6 +68,7 @@ Start a loop by using the `/ralph:loop` command followed by your task. ### Manual Controls +- `/ralph:status`: Check the current status of an active loop. - `/ralph:cancel`: Stops an active loop and cleans up all state files. - `/ralph:help`: Displays detailed usage information and configuration tips. diff --git a/TROUBLESHOOTING.md b/TROUBLESHOOTING.md new file mode 100644 index 0000000..4fcf1c4 --- /dev/null +++ b/TROUBLESHOOTING.md @@ -0,0 +1,85 @@ +# Troubleshooting Ralph + +## Common Issues + +### Loop Won't Start + +**Symptom**: Running `/ralph:loop` doesn't start the loop. + +**Solutions**: +1. Verify hooks are enabled in `~/.gemini/settings.json`: + ```json + { + "hooksConfig": { + "enabled": true + } + } + ``` + +2. Check that the extension directory is included: + ```json + { + "context": { + "includeDirectories": ["~/.gemini/extensions/ralph"] + } + } + ``` + +3. Ensure `jq` is installed: `brew install jq` + +### Loop Won't Stop + +**Symptom**: Loop continues even after completion promise is output. + +**Solutions**: +1. Verify the promise format is exact: `YOUR_TEXT` +2. Check the state file: `cat .gemini/ralph/state.json` +3. Manually cancel: `/ralph:cancel` + +### Ghost Loop + +**Symptom**: Old loop interferes with new tasks. + +**Solution**: The hook should auto-detect this, but you can manually clean up: +```bash +rm -rf .gemini/ralph +``` + +### Permission Errors + +**Symptom**: Scripts fail with permission denied. + +**Solution**: Make scripts executable: +```bash +chmod +x ~/.gemini/extensions/ralph/scripts/*.sh +chmod +x ~/.gemini/extensions/ralph/hooks/*.sh +``` + +## Debugging + +### Check Loop Status +```bash +/ralph:status +``` + +### View State File +```bash +cat .gemini/ralph/state.json | jq +``` + +### Enable Debug Logging +Add to your prompt: +``` +Before each action, output the current iteration number and your plan. +``` + +## Getting Help + +If you encounter issues not covered here: +1. Check the [README](README.md) for configuration details +2. Review test files in `tests/` for expected behavior +3. Open an issue on GitHub with: + - Your `~/.gemini/settings.json` (redact sensitive info) + - The command you ran + - Contents of `.gemini/ralph/state.json` (if exists) + - Error messages diff --git a/commands/ralph/help.toml b/commands/ralph/help.toml index b311eb9..7b346ce 100644 --- a/commands/ralph/help.toml +++ b/commands/ralph/help.toml @@ -4,10 +4,26 @@ Display the Ralph help documentation. Do NOT use any other subagent for this. Es Summarize the available commands for the user: - `/ralph:loop [OPTIONS]`: Start a Ralph loop. +- `/ralph:status`: Check the current status of an active loop. - `/ralph:cancel`: Cancel an active Ralph loop and clean up all temporary files. - `/ralph:help`: Show this message. **Options:** - `--max-iterations `: Stop after N iterations (default: 5). - `--completion-promise `: Only stop when the agent outputs `TEXT`. + +**Examples:** +```bash +# Basic loop with iteration limit +/ralph:loop "Build a REST API" --max-iterations 10 + +# Loop with completion promise +/ralph:loop "Fix all linting errors" --completion-promise "ALL_CLEAN" + +# Combined options +/ralph:loop "Implement feature X" --max-iterations 15 --completion-promise "FEATURE_COMPLETE" +``` + +**Troubleshooting:** +For common issues and solutions, see TROUBLESHOOTING.md in the extension directory. """ diff --git a/commands/ralph/status.toml b/commands/ralph/status.toml new file mode 100644 index 0000000..b92feb1 --- /dev/null +++ b/commands/ralph/status.toml @@ -0,0 +1,9 @@ +description = "Check the status of the current Ralph loop." +prompt = """ +You are checking the Ralph loop status. + +Run the status script to display the current loop state: +```bash +bash "${extensionPath}/scripts/status.sh" +``` +""" diff --git a/scripts/bump-version.sh b/scripts/bump-version.sh new file mode 100755 index 0000000..03e09c4 --- /dev/null +++ b/scripts/bump-version.sh @@ -0,0 +1,34 @@ +#!/bin/bash +# Copyright 2026 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Bump version in gemini-extension.json + +if [[ $# -ne 1 ]]; then + echo "Usage: $0 " + echo "Example: $0 1.1.0" + exit 1 +fi + +NEW_VERSION="$1" + +if [[ ! "$NEW_VERSION" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then + echo "Error: Version must be in format X.Y.Z" + exit 1 +fi + +jq --arg version "$NEW_VERSION" '.version = $version' gemini-extension.json > gemini-extension.json.tmp +mv gemini-extension.json.tmp gemini-extension.json + +echo "✅ Version bumped to $NEW_VERSION" diff --git a/scripts/setup.sh b/scripts/setup.sh index 15a6276..be61f60 100755 --- a/scripts/setup.sh +++ b/scripts/setup.sh @@ -41,11 +41,23 @@ fi # Parse arguments while [[ $# -gt 0 ]]; do case "$1" in + --max-iterations=*) + VALUE="${1#*=}" + [[ "$VALUE" =~ ^[0-9]+$ ]] || die "Invalid iteration limit: '$VALUE'" + MAX_ITERATIONS="$VALUE" + shift + ;; --max-iterations) [[ "${2:-}" =~ ^[0-9]+$ ]] || die "Invalid iteration limit: '${2:-}'" MAX_ITERATIONS="$2" shift 2 ;; + --completion-promise=*) + VALUE="${1#*=}" + [[ -n "$VALUE" ]] || die "Missing promise text." + COMPLETION_PROMISE="$VALUE" + shift + ;; --completion-promise) [[ -n "${2:-}" ]] || die "Missing promise text." COMPLETION_PROMISE="$2" diff --git a/scripts/status.sh b/scripts/status.sh new file mode 100755 index 0000000..213fa22 --- /dev/null +++ b/scripts/status.sh @@ -0,0 +1,43 @@ +#!/bin/bash +# Copyright 2026 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +STATE_FILE=".gemini/ralph/state.json" + +if [[ ! -f "$STATE_FILE" ]]; then + echo "Ralph: I'm not doing anything right now!" >&2 + exit 0 +fi + +echo "🔄 Ralph Loop Status" >&2 +echo "===================" >&2 +echo "" >&2 + +ACTIVE=$(jq -r '.active' "$STATE_FILE") +CURRENT=$(jq -r '.current_iteration' "$STATE_FILE") +MAX=$(jq -r '.max_iterations' "$STATE_FILE") +PROMISE=$(jq -r '.completion_promise' "$STATE_FILE") +PROMPT=$(jq -r '.original_prompt' "$STATE_FILE") +STARTED=$(jq -r '.started_at' "$STATE_FILE") + +echo "Status: $([ "$ACTIVE" = "true" ] && echo "🟢 Active" || echo "🔴 Inactive")" >&2 +echo "Iteration: $CURRENT / $MAX" >&2 +echo "Started: $STARTED" >&2 +echo "Task: $PROMPT" >&2 + +if [[ -n "$PROMISE" ]]; then + echo "Completion Promise: $PROMISE" >&2 +fi + +echo "" >&2 diff --git a/scripts/validate.sh b/scripts/validate.sh new file mode 100755 index 0000000..370e062 --- /dev/null +++ b/scripts/validate.sh @@ -0,0 +1,43 @@ +#!/bin/bash +# Copyright 2026 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Validate Ralph prerequisites + +echo "🔍 Validating Ralph prerequisites..." + +# Check for jq +if ! command -v jq &> /dev/null; then + echo "❌ Error: jq is not installed. Install it with: brew install jq" + exit 1 +fi + +# Check for bash version (need 4.0+) +if [[ "${BASH_VERSINFO[0]}" -lt 4 ]]; then + echo "⚠️ Warning: Bash version ${BASH_VERSION} detected. Bash 4.0+ recommended." +fi + +# Check hooks configuration +SETTINGS_FILE="$HOME/.gemini/settings.json" +if [[ -f "$SETTINGS_FILE" ]]; then + HOOKS_ENABLED=$(jq -r '.hooksConfig.enabled // false' "$SETTINGS_FILE") + if [[ "$HOOKS_ENABLED" != "true" ]]; then + echo "⚠️ Warning: Hooks are not enabled in ~/.gemini/settings.json" + echo " Add: \"hooksConfig\": { \"enabled\": true }" + fi +else + echo "⚠️ Warning: ~/.gemini/settings.json not found" +fi + +echo "✅ Validation complete!" diff --git a/tests/status_test.sh b/tests/status_test.sh new file mode 100755 index 0000000..4d68f2d --- /dev/null +++ b/tests/status_test.sh @@ -0,0 +1,47 @@ +#!/bin/bash +# Copyright 2026 Google LLC +# Licensed under the Apache License, Version 2.0 + +STATE_FILE=".gemini/ralph/state.json" +STATE_DIR=".gemini/ralph" +STATUS_SCRIPT="./scripts/status.sh" + +setup() { + mkdir -p "$STATE_DIR" +} + +cleanup() { + rm -f "$STATE_FILE" + if [[ -d "$STATE_DIR" ]]; then + rmdir "$STATE_DIR" 2>/dev/null || true + fi +} + +trap cleanup EXIT + +echo "Running Test 1: No active loop..." +setup +OUTPUT=$("$STATUS_SCRIPT" 2>&1) +if [[ "$OUTPUT" != *"not doing anything"* ]]; then + echo "FAIL: Expected 'not doing anything' message" + exit 1 +fi + +echo "Running Test 2: Active loop status..." +setup +jq -n '{ + active: true, + current_iteration: 3, + max_iterations: 10, + completion_promise: "DONE", + original_prompt: "Test task", + started_at: "2026-01-27T12:00:00Z" +}' > "$STATE_FILE" + +OUTPUT=$("$STATUS_SCRIPT" 2>&1) +if [[ "$OUTPUT" != *"Active"* ]] || [[ "$OUTPUT" != *"3 / 10"* ]]; then + echo "FAIL: Expected active status with iteration count" + exit 1 +fi + +echo "PASS: All tests passed!"