All skills
anthropics avatar

/plugin-settings

@2438937

This skill should be used when the user asks about "plugin settings", "store plugin configuration", "user-configurable plugin", ".local.md files", "plugin state files", "read YAML frontmatter", "per-project plugin settings", or wants to make plugin behavior configurable. Documents the .claude/plugin-name.local.md pattern for storing plugin-specific configuration with YAML frontmatter and markdown content.

Use this Skill: https://skilld.dev/gh/anthropics/claude-plugins-official/plugin-settings

This session only. Nothing lands on disk.

referencesreal-world-examples.md

≈2.4k tokens on demand. Your agent reads this file only when SKILL.md points to it.

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:

---
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:

#!/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:

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:

# 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-loop Plugin

Settings File Structure

.claude/ralph-loop.local.md:

---
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:

#!/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

#!/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-loop
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:

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:

enabled: true

Why: Allows temporary deactivation without deleting file.

3. Atomic Updates

Both use temp file + atomic move:

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:

sed 's/^"\(.*\)"$/\1/'

Why: YAML allows both field: value and field: "value".

5. Error Handling

Both handle missing/corrupt files gracefully:

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

# BAD
FILE="/Users/alice/.claude/my-plugin.local.md"

# GOOD
FILE=".claude/my-plugin.local.md"

❌ Unquoted Variables

# BAD
echo $VALUE

# GOOD
echo "$VALUE"

❌ Non-Atomic Updates

# 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

# BAD: Fails if field missing
if [[ $MAX -gt 100 ]]; then
  # MAX might be empty!
fi

# GOOD: Provide default
MAX=${MAX:-10}

❌ Ignoring Edge Cases

# 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.

Source: SKILL.md on GitHub

1 warning16d5 checks · Risk SAFE
  • Gen Agent Trust Hub16d

    This skill acts as a design pattern reference and architectural guide for Claude Code plugin developers. It provides standard templates and educational shell scripting instructions to manage user-configurable state files (.local.md) using YAML frontmatter and markdown. The contents consist solely of static documentation, example configuration templates, and benign script examples with standard shell parsing structures.

  • Socket16d

    No alerts

  • Snyk16d

    Risk: LOW · No issues

  • Runlayer7mo

    8/8 files flagged

  • ZeroLeaks5mo

    Score: 93/100 · 2 sections analyzed

Signed by skilld at 2438937. This ties the file your Agent reads to that commit on GitHub. It does not review the instructions.

Last checked against GitHub yesterday.

Activeupdated 8 months ago
version
0.1.0
  • plugin-settings
  • yaml
  • frontmatter
  • configuration
  • hooks
  • state-management
  • markdown
  • bash
  • claude-code

README badge

README badge for anthropics/claude-plugins-official/plugin-settings

Stores per-project plugin configuration in `.claude/plugin-name.local.md` files using YAML frontmatter and markdown body. Enables hooks, commands, and agents to read settings and adapt behavior without code changes or restart (though hook changes require restart). Useful for toggling plugin features, storing state like agent task assignments, or configuring validation levels per project.

Generated from the current SKILL.md.

Where do I store plugin settings?
Store settings in `.claude/plugin-name.local.md` in your project root, using YAML frontmatter for structured configuration and markdown body for additional context or prompts.
Do I need to restart Claude Code after changing settings?
Yes. Changes to `.claude/plugin-name.local.md` require restarting Claude Code for hooks to recognize the new settings.
How do I prevent settings files from being committed to git?
Add `.claude/*.local.md` to your `.gitignore` file. Settings files are user-specific and should not be version-controlled.
Can I use settings files to enable or disable hooks without editing hooks.json?
Yes. Use the `enabled` field in the YAML frontmatter and check it in your hook script to control activation without requiring a restart of the hooks system.
What format should I use for different data types in settings?
Use standard YAML: boolean values like `enabled: true`, numbers like `max_retries: 3`, strings like `mode: standard`, and arrays like `allowed_extensions: [".js", ".ts"]`.

Generated from the current SKILL.md. These answers refresh after source changes.