| # Real-World Plugin Settings Examples |
|
|
| Detailed analysis of how production plugins use the `.claude/plugin-name.local.md` pattern. |
|
|
| ## multi-agent-swarm Plugin |
|
|
| ### Settings File Structure |
|
|
| **.claude/multi-agent-swarm.local.md:** |
|
|
| ```markdown |
| --- |
| agent_name: auth-implementation |
| task_number: 3.5 |
| pr_number: 1234 |
| coordinator_session: team-leader |
| enabled: true |
| dependencies: ["Task 3.4"] |
| additional_instructions: "Use JWT tokens, not sessions" |
| --- |
| |
| # Task: Implement Authentication |
| |
| Build JWT-based authentication for the REST API. |
| |
| ## Requirements |
| - JWT token generation and validation |
| - Refresh token flow |
| - Secure password hashing |
| |
| ## Success Criteria |
| - Auth endpoints implemented |
| - Tests passing (100% coverage) |
| - PR created and CI green |
| - Documentation updated |
| |
| ## Coordination |
| Depends on Task 3.4 (user model). |
| Report status to 'team-leader' session. |
| ``` |
| |
| ### How It's Used |
| |
| **File:** `hooks/agent-stop-notification.sh` |
| |
| **Purpose:** Send notifications to coordinator when agent becomes idle |
| |
| **Implementation:** |
| |
| ```bash |
| #!/bin/bash |
| set -euo pipefail |
| |
| SWARM_STATE_FILE=".claude/multi-agent-swarm.local.md" |
| |
| # Quick exit if no swarm active |
| if [[ ! -f "$SWARM_STATE_FILE" ]]; then |
| exit 0 |
| fi |
| |
| # Parse frontmatter |
| FRONTMATTER=$(sed -n '/^---$/,/^---$/{ /^---$/d; p; }' "$SWARM_STATE_FILE") |
| |
| # Extract configuration |
| COORDINATOR_SESSION=$(echo "$FRONTMATTER" | grep '^coordinator_session:' | sed 's/coordinator_session: *//' | sed 's/^"\(.*\)"$/\1/') |
| AGENT_NAME=$(echo "$FRONTMATTER" | grep '^agent_name:' | sed 's/agent_name: *//' | sed 's/^"\(.*\)"$/\1/') |
| TASK_NUMBER=$(echo "$FRONTMATTER" | grep '^task_number:' | sed 's/task_number: *//' | sed 's/^"\(.*\)"$/\1/') |
| PR_NUMBER=$(echo "$FRONTMATTER" | grep '^pr_number:' | sed 's/pr_number: *//' | sed 's/^"\(.*\)"$/\1/') |
| ENABLED=$(echo "$FRONTMATTER" | grep '^enabled:' | sed 's/enabled: *//') |
| |
| # Check if enabled |
| if [[ "$ENABLED" != "true" ]]; then |
| exit 0 |
| fi |
| |
| # Send notification to coordinator |
| NOTIFICATION="🤖 Agent ${AGENT_NAME} (Task ${TASK_NUMBER}, PR #${PR_NUMBER}) is idle." |
| |
| if tmux has-session -t "$COORDINATOR_SESSION" 2>/dev/null; then |
| tmux send-keys -t "$COORDINATOR_SESSION" "$NOTIFICATION" Enter |
| sleep 0.5 |
| tmux send-keys -t "$COORDINATOR_SESSION" Enter |
| fi |
| |
| exit 0 |
| ``` |
| |
| **Key patterns:** |
| 1. **Quick exit** (line 7-9): Returns immediately if file doesn't exist |
| 2. **Field extraction** (lines 11-17): Parses each frontmatter field |
| 3. **Enabled check** (lines 19-21): Respects enabled flag |
| 4. **Action based on settings** (lines 23-29): Uses coordinator_session to send notification |
| |
| ### Creation |
| |
| **File:** `commands/launch-swarm.md` |
| |
| Settings files are created during swarm launch with: |
| |
| ```bash |
| cat > "$WORKTREE_PATH/.claude/multi-agent-swarm.local.md" <<EOF |
| --- |
| agent_name: $AGENT_NAME |
| task_number: $TASK_ID |
| pr_number: TBD |
| coordinator_session: $COORDINATOR_SESSION |
| enabled: true |
| dependencies: [$DEPENDENCIES] |
| additional_instructions: "$EXTRA_INSTRUCTIONS" |
| --- |
| |
| # Task: $TASK_DESCRIPTION |
| |
| $TASK_DETAILS |
| EOF |
| ``` |
| |
| ### Updates |
| |
| PR number updated after PR creation: |
| |
| ```bash |
| # Update pr_number field |
| sed "s/^pr_number: .*/pr_number: $PR_NUM/" \ |
| ".claude/multi-agent-swarm.local.md" > temp.md |
| mv temp.md ".claude/multi-agent-swarm.local.md" |
| ``` |
| |
| ## ralph-wiggum Plugin |
| |
| ### Settings File Structure |
| |
| **.claude/ralph-loop.local.md:** |
| |
| ```markdown |
| --- |
| iteration: 1 |
| max_iterations: 10 |
| completion_promise: "All tests passing and build successful" |
| started_at: "2025-01-15T14:30:00Z" |
| --- |
|
|
| Fix all the linting errors in the project. |
| Make sure tests pass after each fix. |
| Document any changes needed in CLAUDE.md. |
| ``` |
| |
| ### How It's Used |
| |
| **File:** `hooks/stop-hook.sh` |
| |
| **Purpose:** Prevent session exit and loop Claude's output back as input |
| |
| **Implementation:** |
| |
| ```bash |
| #!/bin/bash |
| set -euo pipefail |
|
|
| RALPH_STATE_FILE=".claude/ralph-loop.local.md" |
|
|
| # Quick exit if no active loop |
| if [[ ! -f "$RALPH_STATE_FILE" ]]; then |
| exit 0 |
| fi |
|
|
| # Parse frontmatter |
| FRONTMATTER=$(sed -n '/^---$/,/^---$/{ /^---$/d; p; }' "$RALPH_STATE_FILE") |
|
|
| # Extract configuration |
| ITERATION=$(echo "$FRONTMATTER" | grep '^iteration:' | sed 's/iteration: *//') |
| MAX_ITERATIONS=$(echo "$FRONTMATTER" | grep '^max_iterations:' | sed 's/max_iterations: *//') |
| COMPLETION_PROMISE=$(echo "$FRONTMATTER" | grep '^completion_promise:' | sed 's/completion_promise: *//' | sed 's/^"\(.*\)"$/\1/') |
| |
| # Check max iterations |
| if [[ $MAX_ITERATIONS -gt 0 ]] && [[ $ITERATION -ge $MAX_ITERATIONS ]]; then |
| echo "🛑 Ralph loop: Max iterations ($MAX_ITERATIONS) reached." |
| rm "$RALPH_STATE_FILE" |
| exit 0 |
| fi |
|
|
| # Get transcript and check for completion promise |
| TRANSCRIPT_PATH=$(echo "$HOOK_INPUT" | jq -r '.transcript_path') |
| LAST_OUTPUT=$(grep '"role":"assistant"' "$TRANSCRIPT_PATH" | tail -1 | jq -r '.message.content | map(select(.type == "text")) | map(.text) | join("\n")') |
| |
| # Check for completion |
| if [[ "$COMPLETION_PROMISE" != "null" ]] && [[ -n "$COMPLETION_PROMISE" ]]; then |
| PROMISE_TEXT=$(echo "$LAST_OUTPUT" | perl -0777 -pe 's/.*?<promise>(.*?)<\/promise>.*/$1/s; s/^\s+|\s+$//g') |
| |
| if [[ "$PROMISE_TEXT" = "$COMPLETION_PROMISE" ]]; then |
| echo "✅ Ralph loop: Detected completion" |
| rm "$RALPH_STATE_FILE" |
| exit 0 |
| fi |
| fi |
| |
| # Continue loop - increment iteration |
| NEXT_ITERATION=$((ITERATION + 1)) |
|
|
| # Extract prompt from markdown body |
| PROMPT_TEXT=$(awk '/^---$/{i++; next} i>=2' "$RALPH_STATE_FILE") |
| |
| # Update iteration counter |
| TEMP_FILE="${RALPH_STATE_FILE}.tmp.$$" |
| sed "s/^iteration: .*/iteration: $NEXT_ITERATION/" "$RALPH_STATE_FILE" > "$TEMP_FILE" |
| mv "$TEMP_FILE" "$RALPH_STATE_FILE" |
| |
| # Block exit and feed prompt back |
| jq -n \ |
| --arg prompt "$PROMPT_TEXT" \ |
| --arg msg "🔄 Ralph iteration $NEXT_ITERATION" \ |
| '{ |
| "decision": "block", |
| "reason": $prompt, |
| "systemMessage": $msg |
| }' |
| |
| exit 0 |
| ``` |
| |
| **Key patterns:** |
| 1. **Quick exit** (line 7-9): Skip if not active |
| 2. **Iteration tracking** (lines 11-20): Count and enforce max iterations |
| 3. **Promise detection** (lines 25-33): Check for completion signal in output |
| 4. **Prompt extraction** (line 38): Read markdown body as next prompt |
| 5. **State update** (lines 40-43): Increment iteration atomically |
| 6. **Loop continuation** (lines 45-53): Block exit and feed prompt back |
| |
| ### Creation |
| |
| **File:** `scripts/setup-ralph-loop.sh` |
| |
| ```bash |
| #!/bin/bash |
| PROMPT="$1" |
| MAX_ITERATIONS="${2:-0}" |
| COMPLETION_PROMISE="${3:-}" |
| |
| # Create state file |
| cat > ".claude/ralph-loop.local.md" <<EOF |
| --- |
| iteration: 1 |
| max_iterations: $MAX_ITERATIONS |
| completion_promise: "$COMPLETION_PROMISE" |
| started_at: "$(date -Iseconds)" |
| --- |
| |
| $PROMPT |
| EOF |
| |
| echo "Ralph loop initialized: .claude/ralph-loop.local.md" |
| ``` |
| |
| ## Pattern Comparison |
| |
| | Feature | multi-agent-swarm | ralph-wiggum | |
| |---------|-------------------|--------------| |
| | **File** | `.claude/multi-agent-swarm.local.md` | `.claude/ralph-loop.local.md` | |
| | **Purpose** | Agent coordination state | Loop iteration state | |
| | **Frontmatter** | Agent metadata | Loop configuration | |
| | **Body** | Task assignment | Prompt to loop | |
| | **Updates** | PR number, status | Iteration counter | |
| | **Deletion** | Manual or on completion | On loop exit | |
| | **Hook** | Stop (notifications) | Stop (loop control) | |
| |
| ## Best Practices from Real Plugins |
| |
| ### 1. Quick Exit Pattern |
| |
| Both plugins check file existence first: |
| |
| ```bash |
| if [[ ! -f "$STATE_FILE" ]]; then |
| exit 0 # Not active |
| fi |
| ``` |
| |
| **Why:** Avoids errors when plugin isn't configured and performs fast. |
| |
| ### 2. Enabled Flag |
| |
| Both use an `enabled` field for explicit control: |
| |
| ```yaml |
| enabled: true |
| ``` |
| |
| **Why:** Allows temporary deactivation without deleting file. |
| |
| ### 3. Atomic Updates |
| |
| Both use temp file + atomic move: |
| |
| ```bash |
| TEMP_FILE="${FILE}.tmp.$$" |
| sed "s/^field: .*/field: $NEW_VALUE/" "$FILE" > "$TEMP_FILE" |
| mv "$TEMP_FILE" "$FILE" |
| ``` |
| |
| **Why:** Prevents corruption if process is interrupted. |
| |
| ### 4. Quote Handling |
| |
| Both strip surrounding quotes from YAML values: |
| |
| ```bash |
| sed 's/^"\(.*\)"$/\1/' |
| ``` |
| |
| **Why:** YAML allows both `field: value` and `field: "value"`. |
| |
| ### 5. Error Handling |
| |
| Both handle missing/corrupt files gracefully: |
| |
| ```bash |
| if [[ ! -f "$FILE" ]]; then |
| exit 0 # No error, just not configured |
| fi |
|
|
| if [[ -z "$CRITICAL_FIELD" ]]; then |
| echo "Settings file corrupt" >&2 |
| rm "$FILE" # Clean up |
| exit 0 |
| fi |
| ``` |
| |
| **Why:** Fails gracefully instead of crashing. |
| |
| ## Anti-Patterns to Avoid |
| |
| ### ❌ Hardcoded Paths |
| |
| ```bash |
| # BAD |
| FILE="/Users/alice/.claude/my-plugin.local.md" |
| |
| # GOOD |
| FILE=".claude/my-plugin.local.md" |
| ``` |
| |
| ### ❌ Unquoted Variables |
| |
| ```bash |
| # BAD |
| echo $VALUE |
| |
| # GOOD |
| echo "$VALUE" |
| ``` |
| |
| ### ❌ Non-Atomic Updates |
| |
| ```bash |
| # BAD: Can corrupt file if interrupted |
| sed -i "s/field: .*/field: $VALUE/" "$FILE" |
| |
| # GOOD: Atomic |
| TEMP_FILE="${FILE}.tmp.$$" |
| sed "s/field: .*/field: $VALUE/" "$FILE" > "$TEMP_FILE" |
| mv "$TEMP_FILE" "$FILE" |
| ``` |
| |
| ### ❌ No Default Values |
| |
| ```bash |
| # BAD: Fails if field missing |
| if [[ $MAX -gt 100 ]]; then |
| # MAX might be empty! |
| fi |
| |
| # GOOD: Provide default |
| MAX=${MAX:-10} |
| ``` |
| |
| ### ❌ Ignoring Edge Cases |
| |
| ```bash |
| # BAD: Assumes exactly 2 --- markers |
| sed -n '/^---$/,/^---$/{ /^---$/d; p; }' |
| |
| # GOOD: Handles --- in body |
| awk '/^---$/{i++; next} i>=2' # For body |
| ``` |
| |
| ## Conclusion |
| |
| The `.claude/plugin-name.local.md` pattern provides: |
| - Simple, human-readable configuration |
| - Version-control friendly (gitignored) |
| - Per-project settings |
| - Easy parsing with standard bash tools |
| - Supports both structured config (YAML) and freeform content (markdown) |
| |
| Use this pattern for any plugin that needs user-configurable behavior or state persistence. |
| |