surshar / claude-code
Claude Code is an agentic coding tool that lives in your terminal, understands your codebase, and helps you code faster by executing routine tasks, explaining complex code, and handling git workflows - all through natural language commands.
Built by Anthropic ยท github.com/anthropics
| 1 | # Real-World Plugin Settings Examples |
| 2 | |
| 3 | Detailed analysis of how production plugins use the `.claude/plugin-name.local.md` pattern. |
| 4 | |
| 5 | ## multi-agent-swarm Plugin |
| 6 | |
| 7 | ### Settings File Structure |
| 8 | |
| 9 | **.claude/multi-agent-swarm.local.md:** |
| 10 | |
| 11 | ```markdown |
| 12 | --- |
| 13 | agent_name: auth-implementation |
| 14 | task_number: 3.5 |
| 15 | pr_number: 1234 |
| 16 | coordinator_session: team-leader |
| 17 | enabled: true |
| 18 | dependencies: ["Task 3.4"] |
| 19 | additional_instructions: "Use JWT tokens, not sessions" |
| 20 | --- |
| 21 | |
| 22 | # Task: Implement Authentication |
| 23 | |
| 24 | Build JWT-based authentication for the REST API. |
| 25 | |
| 26 | ## Requirements |
| 27 | - JWT token generation and validation |
| 28 | - Refresh token flow |
| 29 | - Secure password hashing |
| 30 | |
| 31 | ## Success Criteria |
| 32 | - Auth endpoints implemented |
| 33 | - Tests passing (100% coverage) |
| 34 | - PR created and CI green |
| 35 | - Documentation updated |
| 36 | |
| 37 | ## Coordination |
| 38 | Depends on Task 3.4 (user model). |
| 39 | Report status to 'team-leader' session. |
| 40 | ``` |
| 41 | |
| 42 | ### How It's Used |
| 43 | |
| 44 | **File:** `hooks/agent-stop-notification.sh` |
| 45 | |
| 46 | **Purpose:** Send notifications to coordinator when agent becomes idle |
| 47 | |
| 48 | **Implementation:** |
| 49 | |
| 50 | ```bash |
| 51 | #!/bin/bash |
| 52 | set -euo pipefail |
| 53 | |
| 54 | SWARM_STATE_FILE=".claude/multi-agent-swarm.local.md" |
| 55 | |
| 56 | # Quick exit if no swarm active |
| 57 | if [[ ! -f "$SWARM_STATE_FILE" ]]; then |
| 58 | exit 0 |
| 59 | fi |
| 60 | |
| 61 | # Parse frontmatter |
| 62 | FRONTMATTER=$(sed -n '/^---$/,/^---$/{ /^---$/d; p; }' "$SWARM_STATE_FILE") |
| 63 | |
| 64 | # Extract configuration |
| 65 | COORDINATOR_SESSION=$(echo "$FRONTMATTER" | grep '^coordinator_session:' | sed 's/coordinator_session: *//' | sed 's/^"\(.*\)"$/\1/') |
| 66 | AGENT_NAME=$(echo "$FRONTMATTER" | grep '^agent_name:' | sed 's/agent_name: *//' | sed 's/^"\(.*\)"$/\1/') |
| 67 | TASK_NUMBER=$(echo "$FRONTMATTER" | grep '^task_number:' | sed 's/task_number: *//' | sed 's/^"\(.*\)"$/\1/') |
| 68 | PR_NUMBER=$(echo "$FRONTMATTER" | grep '^pr_number:' | sed 's/pr_number: *//' | sed 's/^"\(.*\)"$/\1/') |
| 69 | ENABLED=$(echo "$FRONTMATTER" | grep '^enabled:' | sed 's/enabled: *//') |
| 70 | |
| 71 | # Check if enabled |
| 72 | if [[ "$ENABLED" != "true" ]]; then |
| 73 | exit 0 |
| 74 | fi |
| 75 | |
| 76 | # Send notification to coordinator |
| 77 | NOTIFICATION="๐ค Agent ${AGENT_NAME} (Task ${TASK_NUMBER}, PR #${PR_NUMBER}) is idle." |
| 78 | |
| 79 | if tmux has-session -t "$COORDINATOR_SESSION" 2>/dev/null; then |
| 80 | tmux send-keys -t "$COORDINATOR_SESSION" "$NOTIFICATION" Enter |
| 81 | sleep 0.5 |
| 82 | tmux send-keys -t "$COORDINATOR_SESSION" Enter |
| 83 | fi |
| 84 | |
| 85 | exit 0 |
| 86 | ``` |
| 87 | |
| 88 | **Key patterns:** |
| 89 | 1. **Quick exit** (line 7-9): Returns immediately if file doesn't exist |
| 90 | 2. **Field extraction** (lines 11-17): Parses each frontmatter field |
| 91 | 3. **Enabled check** (lines 19-21): Respects enabled flag |
| 92 | 4. **Action based on settings** (lines 23-29): Uses coordinator_session to send notification |
| 93 | |
| 94 | ### Creation |
| 95 | |
| 96 | **File:** `commands/launch-swarm.md` |
| 97 | |
| 98 | Settings files are created during swarm launch with: |
| 99 | |
| 100 | ```bash |
| 101 | cat > "$WORKTREE_PATH/.claude/multi-agent-swarm.local.md" <<EOF |
| 102 | --- |
| 103 | agent_name: $AGENT_NAME |
| 104 | task_number: $TASK_ID |
| 105 | pr_number: TBD |
| 106 | coordinator_session: $COORDINATOR_SESSION |
| 107 | enabled: true |
| 108 | dependencies: [$DEPENDENCIES] |
| 109 | additional_instructions: "$EXTRA_INSTRUCTIONS" |
| 110 | --- |
| 111 | |
| 112 | # Task: $TASK_DESCRIPTION |
| 113 | |
| 114 | $TASK_DETAILS |
| 115 | EOF |
| 116 | ``` |
| 117 | |
| 118 | ### Updates |
| 119 | |
| 120 | PR number updated after PR creation: |
| 121 | |
| 122 | ```bash |
| 123 | # Update pr_number field |
| 124 | sed "s/^pr_number: .*/pr_number: $PR_NUM/" \ |
| 125 | ".claude/multi-agent-swarm.local.md" > temp.md |
| 126 | mv temp.md ".claude/multi-agent-swarm.local.md" |
| 127 | ``` |
| 128 | |
| 129 | ## ralph-wiggum Plugin |
| 130 | |
| 131 | ### Settings File Structure |
| 132 | |
| 133 | **.claude/ralph-loop.local.md:** |
| 134 | |
| 135 | ```markdown |
| 136 | --- |
| 137 | iteration: 1 |
| 138 | max_iterations: 10 |
| 139 | completion_promise: "All tests passing and build successful" |
| 140 | started_at: "2025-01-15T14:30:00Z" |
| 141 | --- |
| 142 | |
| 143 | Fix all the linting errors in the project. |
| 144 | Make sure tests pass after each fix. |
| 145 | Document any changes needed in CLAUDE.md. |
| 146 | ``` |
| 147 | |
| 148 | ### How It's Used |
| 149 | |
| 150 | **File:** `hooks/stop-hook.sh` |
| 151 | |
| 152 | **Purpose:** Prevent session exit and loop Claude's output back as input |
| 153 | |
| 154 | **Implementation:** |
| 155 | |
| 156 | ```bash |
| 157 | #!/bin/bash |
| 158 | set -euo pipefail |
| 159 | |
| 160 | RALPH_STATE_FILE=".claude/ralph-loop.local.md" |
| 161 | |
| 162 | # Quick exit if no active loop |
| 163 | if [[ ! -f "$RALPH_STATE_FILE" ]]; then |
| 164 | exit 0 |
| 165 | fi |
| 166 | |
| 167 | # Parse frontmatter |
| 168 | FRONTMATTER=$(sed -n '/^---$/,/^---$/{ /^---$/d; p; }' "$RALPH_STATE_FILE") |
| 169 | |
| 170 | # Extract configuration |
| 171 | ITERATION=$(echo "$FRONTMATTER" | grep '^iteration:' | sed 's/iteration: *//') |
| 172 | MAX_ITERATIONS=$(echo "$FRONTMATTER" | grep '^max_iterations:' | sed 's/max_iterations: *//') |
| 173 | COMPLETION_PROMISE=$(echo "$FRONTMATTER" | grep '^completion_promise:' | sed 's/completion_promise: *//' | sed 's/^"\(.*\)"$/\1/') |
| 174 | |
| 175 | # Check max iterations |
| 176 | if [[ $MAX_ITERATIONS -gt 0 ]] && [[ $ITERATION -ge $MAX_ITERATIONS ]]; then |
| 177 | echo "๐ Ralph loop: Max iterations ($MAX_ITERATIONS) reached." |
| 178 | rm "$RALPH_STATE_FILE" |
| 179 | exit 0 |
| 180 | fi |
| 181 | |
| 182 | # Get transcript and check for completion promise |
| 183 | TRANSCRIPT_PATH=$(echo "$HOOK_INPUT" | jq -r '.transcript_path') |
| 184 | LAST_OUTPUT=$(grep '"role":"assistant"' "$TRANSCRIPT_PATH" | tail -1 | jq -r '.message.content | map(select(.type == "text")) | map(.text) | join("\n")') |
| 185 | |
| 186 | # Check for completion |
| 187 | if [[ "$COMPLETION_PROMISE" != "null" ]] && [[ -n "$COMPLETION_PROMISE" ]]; then |
| 188 | PROMISE_TEXT=$(echo "$LAST_OUTPUT" | perl -0777 -pe 's/.*?<promise>(.*?)<\/promise>.*/$1/s; s/^\s+|\s+$//g') |
| 189 | |
| 190 | if [[ "$PROMISE_TEXT" = "$COMPLETION_PROMISE" ]]; then |
| 191 | echo "โ Ralph loop: Detected completion" |
| 192 | rm "$RALPH_STATE_FILE" |
| 193 | exit 0 |
| 194 | fi |
| 195 | fi |
| 196 | |
| 197 | # Continue loop - increment iteration |
| 198 | NEXT_ITERATION=$((ITERATION + 1)) |
| 199 | |
| 200 | # Extract prompt from markdown body |
| 201 | PROMPT_TEXT=$(awk '/^---$/{i++; next} i>=2' "$RALPH_STATE_FILE") |
| 202 | |
| 203 | # Update iteration counter |
| 204 | TEMP_FILE="${RALPH_STATE_FILE}.tmp.$$" |
| 205 | sed "s/^iteration: .*/iteration: $NEXT_ITERATION/" "$RALPH_STATE_FILE" > "$TEMP_FILE" |
| 206 | mv "$TEMP_FILE" "$RALPH_STATE_FILE" |
| 207 | |
| 208 | # Block exit and feed prompt back |
| 209 | jq -n \ |
| 210 | --arg prompt "$PROMPT_TEXT" \ |
| 211 | --arg msg "๐ Ralph iteration $NEXT_ITERATION" \ |
| 212 | '{ |
| 213 | "decision": "block", |
| 214 | "reason": $prompt, |
| 215 | "systemMessage": $msg |
| 216 | }' |
| 217 | |
| 218 | exit 0 |
| 219 | ``` |
| 220 | |
| 221 | **Key patterns:** |
| 222 | 1. **Quick exit** (line 7-9): Skip if not active |
| 223 | 2. **Iteration tracking** (lines 11-20): Count and enforce max iterations |
| 224 | 3. **Promise detection** (lines 25-33): Check for completion signal in output |
| 225 | 4. **Prompt extraction** (line 38): Read markdown body as next prompt |
| 226 | 5. **State update** (lines 40-43): Increment iteration atomically |
| 227 | 6. **Loop continuation** (lines 45-53): Block exit and feed prompt back |
| 228 | |
| 229 | ### Creation |
| 230 | |
| 231 | **File:** `scripts/setup-ralph-loop.sh` |
| 232 | |
| 233 | ```bash |
| 234 | #!/bin/bash |
| 235 | PROMPT="$1" |
| 236 | MAX_ITERATIONS="${2:-0}" |
| 237 | COMPLETION_PROMISE="${3:-}" |
| 238 | |
| 239 | # Create state file |
| 240 | cat > ".claude/ralph-loop.local.md" <<EOF |
| 241 | --- |
| 242 | iteration: 1 |
| 243 | max_iterations: $MAX_ITERATIONS |
| 244 | completion_promise: "$COMPLETION_PROMISE" |
| 245 | started_at: "$(date -Iseconds)" |
| 246 | --- |
| 247 | |
| 248 | $PROMPT |
| 249 | EOF |
| 250 | |
| 251 | echo "Ralph loop initialized: .claude/ralph-loop.local.md" |
| 252 | ``` |
| 253 | |
| 254 | ## Pattern Comparison |
| 255 | |
| 256 | | Feature | multi-agent-swarm | ralph-wiggum | |
| 257 | |---------|-------------------|--------------| |
| 258 | | **File** | `.claude/multi-agent-swarm.local.md` | `.claude/ralph-loop.local.md` | |
| 259 | | **Purpose** | Agent coordination state | Loop iteration state | |
| 260 | | **Frontmatter** | Agent metadata | Loop configuration | |
| 261 | | **Body** | Task assignment | Prompt to loop | |
| 262 | | **Updates** | PR number, status | Iteration counter | |
| 263 | | **Deletion** | Manual or on completion | On loop exit | |
| 264 | | **Hook** | Stop (notifications) | Stop (loop control) | |
| 265 | |
| 266 | ## Best Practices from Real Plugins |
| 267 | |
| 268 | ### 1. Quick Exit Pattern |
| 269 | |
| 270 | Both plugins check file existence first: |
| 271 | |
| 272 | ```bash |
| 273 | if [[ ! -f "$STATE_FILE" ]]; then |
| 274 | exit 0 # Not active |
| 275 | fi |
| 276 | ``` |
| 277 | |
| 278 | **Why:** Avoids errors when plugin isn't configured and performs fast. |
| 279 | |
| 280 | ### 2. Enabled Flag |
| 281 | |
| 282 | Both use an `enabled` field for explicit control: |
| 283 | |
| 284 | ```yaml |
| 285 | enabled: true |
| 286 | ``` |
| 287 | |
| 288 | **Why:** Allows temporary deactivation without deleting file. |
| 289 | |
| 290 | ### 3. Atomic Updates |
| 291 | |
| 292 | Both use temp file + atomic move: |
| 293 | |
| 294 | ```bash |
| 295 | TEMP_FILE="${FILE}.tmp.$$" |
| 296 | sed "s/^field: .*/field: $NEW_VALUE/" "$FILE" > "$TEMP_FILE" |
| 297 | mv "$TEMP_FILE" "$FILE" |
| 298 | ``` |
| 299 | |
| 300 | **Why:** Prevents corruption if process is interrupted. |
| 301 | |
| 302 | ### 4. Quote Handling |
| 303 | |
| 304 | Both strip surrounding quotes from YAML values: |
| 305 | |
| 306 | ```bash |
| 307 | sed 's/^"\(.*\)"$/\1/' |
| 308 | ``` |
| 309 | |
| 310 | **Why:** YAML allows both `field: value` and `field: "value"`. |
| 311 | |
| 312 | ### 5. Error Handling |
| 313 | |
| 314 | Both handle missing/corrupt files gracefully: |
| 315 | |
| 316 | ```bash |
| 317 | if [[ ! -f "$FILE" ]]; then |
| 318 | exit 0 # No error, just not configured |
| 319 | fi |
| 320 | |
| 321 | if [[ -z "$CRITICAL_FIELD" ]]; then |
| 322 | echo "Settings file corrupt" >&2 |
| 323 | rm "$FILE" # Clean up |
| 324 | exit 0 |
| 325 | fi |
| 326 | ``` |
| 327 | |
| 328 | **Why:** Fails gracefully instead of crashing. |
| 329 | |
| 330 | ## Anti-Patterns to Avoid |
| 331 | |
| 332 | ### โ Hardcoded Paths |
| 333 | |
| 334 | ```bash |
| 335 | # BAD |
| 336 | FILE="/Users/alice/.claude/my-plugin.local.md" |
| 337 | |
| 338 | # GOOD |
| 339 | FILE=".claude/my-plugin.local.md" |
| 340 | ``` |
| 341 | |
| 342 | ### โ Unquoted Variables |
| 343 | |
| 344 | ```bash |
| 345 | # BAD |
| 346 | echo $VALUE |
| 347 | |
| 348 | # GOOD |
| 349 | echo "$VALUE" |
| 350 | ``` |
| 351 | |
| 352 | ### โ Non-Atomic Updates |
| 353 | |
| 354 | ```bash |
| 355 | # BAD: Can corrupt file if interrupted |
| 356 | sed -i "s/field: .*/field: $VALUE/" "$FILE" |
| 357 | |
| 358 | # GOOD: Atomic |
| 359 | TEMP_FILE="${FILE}.tmp.$$" |
| 360 | sed "s/field: .*/field: $VALUE/" "$FILE" > "$TEMP_FILE" |
| 361 | mv "$TEMP_FILE" "$FILE" |
| 362 | ``` |
| 363 | |
| 364 | ### โ No Default Values |
| 365 | |
| 366 | ```bash |
| 367 | # BAD: Fails if field missing |
| 368 | if [[ $MAX -gt 100 ]]; then |
| 369 | # MAX might be empty! |
| 370 | fi |
| 371 | |
| 372 | # GOOD: Provide default |
| 373 | MAX=${MAX:-10} |
| 374 | ``` |
| 375 | |
| 376 | ### โ Ignoring Edge Cases |
| 377 | |
| 378 | ```bash |
| 379 | # BAD: Assumes exactly 2 --- markers |
| 380 | sed -n '/^---$/,/^---$/{ /^---$/d; p; }' |
| 381 | |
| 382 | # GOOD: Handles --- in body |
| 383 | awk '/^---$/{i++; next} i>=2' # For body |
| 384 | ``` |
| 385 | |
| 386 | ## Conclusion |
| 387 | |
| 388 | The `.claude/plugin-name.local.md` pattern provides: |
| 389 | - Simple, human-readable configuration |
| 390 | - Version-control friendly (gitignored) |
| 391 | - Per-project settings |
| 392 | - Easy parsing with standard bash tools |
| 393 | - Supports both structured config (YAML) and freeform content (markdown) |
| 394 | |
| 395 | Use this pattern for any plugin that needs user-configurable behavior or state persistence. |