diff --git a/plugins/ralph-loop/CLAUDE.md b/plugins/ralph-loop/CLAUDE.md
deleted file mode 100644
index 71ab3aca..00000000
--- a/plugins/ralph-loop/CLAUDE.md
+++ /dev/null
@@ -1,64 +0,0 @@
-
-
-
-# ralph-loop
-
-## Purpose
-Continuous self-referential AI loops for interactive iterative development, implementing the Ralph Wiggum technique.
-
-## Key Files
-
-| File | Description |
-|------|-------------|
-| `README.md` | Plugin documentation |
-| `.claude-plugin/plugin.json` | Plugin manifest |
-| `hooks/hooks.json` | Hook configuration |
-| `scripts/lib/` | Utility libraries |
-
-## Commands
-
-| Command | Purpose |
-|---------|---------|
-| `ralph-loop.md` | Start Ralph Loop with prompt and iteration count |
-| `cancel-ralph.md` | Cancel active Ralph Loop |
-| `help.md` | Display Ralph Loop help |
-
-## Subdirectories
-
-| Directory | Purpose |
-|-----------|---------|
-| `commands/` | Ralph Loop commands |
-| `hooks/` | SessionStart/SessionStop hooks |
-| `scripts/` | Setup and control scripts |
-| `scripts/lib/` | Shared libraries |
-| `tests/` | Plugin tests |
-
-## For AI Agents
-
-### Working In This Directory
-- Stop hook intercepts session termination
-- Feeds last assistant output as next prompt
-- Uses state files in `~/.claude/ralph-loop/`
-- Supports completion promises (`TAG`)
-
-### Testing Requirements
-- Test loop iteration mechanics
-- Verify completion detection
-- Test cancellation functionality
-- Ensure state cleanup on completion
-
-### Common Patterns
-- Hook scripts use `set -euo pipefail`
-- State files use `.local.md` extension
-- Session ID in environment
-- Maximum iterations configurable
-
-## Dependencies
-
-### Internal
-- `tests/ralph-loop/` - Plugin tests (if exists)
-
-### External
-- None (self-contained)
-
-
diff --git a/plugins/ralph-loop/hooks/CLAUDE.md b/plugins/ralph-loop/hooks/CLAUDE.md
deleted file mode 100644
index 46472d86..00000000
--- a/plugins/ralph-loop/hooks/CLAUDE.md
+++ /dev/null
@@ -1,51 +0,0 @@
-
-
-
-# hooks
-
-## Purpose
-Session hooks for Ralph Loop - SessionStart setup and SessionStop looping mechanism.
-
-## Key Files
-
-| File | Description |
-|------|-------------|
-| `hooks.json` | Hook configuration |
-| `session-start-hook.sh` | Initialize Ralph Loop state on session start |
-| `session-env.sh` | Set environment variables for session tracking |
-| `stop-hook.sh` | Intercept session stop and continue loop |
-
-## Subdirectories
-
-None
-
-## For AI Agents
-
-### Working In This Directory
-- SessionStart creates state directory and files
-- SessionStop is intercepted to continue iteration
-- State files track iteration count and completion
-- Completes when `TAG` detected or max iterations reached
-
-### Testing Requirements
-- Test session start initialization
-- Verify stop hook interception
-- Test completion detection
-- Ensure state cleanup on completion
-
-### Common Patterns
-- `set -euo pipefail` for error detection
-- State in `~/.claude/ralph-loop/`
-- Session ID from `${CLAUDE_SESSION_ID}`
-- Last assistant output fed as next prompt
-
-## Dependencies
-
-### External
-- None (self-contained)
-
-### Internal
-- `../commands/` - Commands that start/control loop
-- `../scripts/lib/` - Shared utilities
-
-
diff --git a/plugins/ralph-loop/hooks/hooks.json b/plugins/ralph-loop/hooks/hooks.json
index 800aa7ca..69a3bdcb 100644
--- a/plugins/ralph-loop/hooks/hooks.json
+++ b/plugins/ralph-loop/hooks/hooks.json
@@ -4,7 +4,7 @@
"hooks": {
"SessionStart": [
{
- "matcher": "ralph-loop-active",
+ "matcher": "*",
"hooks": [
{
"type": "command",
@@ -15,7 +15,7 @@
],
"Stop": [
{
- "matcher": "ralph-loop-active",
+ "matcher": "*",
"hooks": [
{
"type": "command",
diff --git a/plugins/ralph-loop/hooks/session-start-hook.sh b/plugins/ralph-loop/hooks/session-start-hook.sh
index ba3c6c59..db018f71 100755
--- a/plugins/ralph-loop/hooks/session-start-hook.sh
+++ b/plugins/ralph-loop/hooks/session-start-hook.sh
@@ -7,8 +7,8 @@ set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
source "$SCRIPT_DIR/../scripts/lib/state.sh"
-# Extract session_id from stdin (remove useless use of cat)
-SESSION_ID=$(jq -r '.session_id')
+# Extract session_id from stdin
+SESSION_ID=$(jq -r '.session_id' > "$ENV_FILE"
+elif [[ ! -f "$ENV_FILE" ]]; then
+ # File doesn't exist (just created above), create it with content
+ # Safe to use unquoted since we validated SESSION_ID contains only safe characters
+ echo "export RALPH_SESSION_ID=$SESSION_ID" > "$ENV_FILE"
fi
exit 0
diff --git a/plugins/ralph-loop/tests/ralph-loop-specific.bats b/plugins/ralph-loop/tests/ralph-loop-specific.bats
index c86ce025..8f43ccc3 100644
--- a/plugins/ralph-loop/tests/ralph-loop-specific.bats
+++ b/plugins/ralph-loop/tests/ralph-loop-specific.bats
@@ -531,3 +531,128 @@ EOF
# This prevents "unbound variable" errors when set -u is enabled
grep -q 'PROMPT_PARTS\[\*\]:-' "$script"
}
+
+# ============================================================================
+# Bug Fix Tests - These tests should FAIL before the fix
+# ============================================================================
+
+@test "ralph-loop: hooks.json uses wildcard matcher for SessionStart hook" {
+ local hooks_json="${PLUGIN_DIR}/hooks/hooks.json"
+ [ -f "$hooks_json" ]
+ ensure_jq
+
+ # The matcher should be "*" to run on all sessions
+ # The hook script itself checks if state file exists
+ local matcher
+ matcher=$($JQ_BIN -r '.hooks.SessionStart[0].matcher' "$hooks_json")
+
+ # This test will FAIL until the bug is fixed
+ [ "$matcher" = "*" ]
+}
+
+@test "ralph-loop: hooks.json uses wildcard matcher for Stop hook" {
+ local hooks_json="${PLUGIN_DIR}/hooks/hooks.json"
+ [ -f "$hooks_json" ]
+ ensure_jq
+
+ # The matcher should be "*" to run on all sessions
+ local matcher
+ matcher=$($JQ_BIN -r '.hooks.Stop[0].matcher' "$hooks_json")
+
+ # This test will FAIL until the bug is fixed
+ [ "$matcher" = "*" ]
+}
+
+@test "ralph-loop: session-start-hook.sh reads SESSION_ID from stdin" {
+ local hook="${PLUGIN_DIR}/hooks/session-start-hook.sh"
+ [ -f "$hook" ]
+
+ # The hook must read from stdin (>.*ENV_FILE' "$hook"
+ grep -q 'echo.*RALPH_SESSION_ID.*>.*ENV_FILE' "$hook"
+}
+
+# ============================================================================
+# E2E Integration Tests
+# These tests verify the core integration scenarios work correctly
+# ============================================================================
+
+@test "ralph-loop: session-start-hook.sh reads session_id from stdin" {
+ local hook="${PLUGIN_DIR}/hooks/session-start-hook.sh"
+ [ -f "$hook" ]
+
+ # Verify the hook reads from stdin using ' "$hook"
+ grep -q 'PROMISE_TEXT=.*perl' "$hook"
+}
+
+@test "ralph-loop: stop-hook.sh validates numeric iteration fields" {
+ local hook="${PLUGIN_DIR}/hooks/stop-hook.sh"
+ [ -f "$hook" ]
+
+ # Verify numeric validation before arithmetic
+ grep -q 'ITERATION.*0-9' "$hook"
+ grep -q 'MAX_ITERATIONS.*0-9' "$hook"
+}
+
+@test "ralph-loop: stop-hook.sh handles max iterations reached" {
+ local hook="${PLUGIN_DIR}/hooks/stop-hook.sh"
+ [ -f "$hook" ]
+
+ # Verify max iteration check
+ grep -q 'MAX_ITERATIONS.*-gt.*0.*ITERATION.*-ge.*MAX_ITERATIONS' "$hook"
+}
+
+@test "ralph-loop: cancel-ralph.sh removes state file when cancelled" {
+ local script="${PLUGIN_DIR}/scripts/cancel-ralph.sh"
+ [ -f "$script" ]
+ [ -x "$script" ]
+
+ # Verify state file removal logic
+ grep -q 'rm.*STATE_FILE' "$script"
+}