How Claude Plugins Handle User Preferences: File-Based Configuration Guide
Claude plugins store user preferences in per-project markdown files located at .claude/<plugin-name>.local.md, using YAML front-matter for structured data that is parsed by config_loader.py and consumed by hooks and commands at runtime.
Claude plugins manage user preferences through a declarative, file-based configuration system designed for per-project customization. In the anthropics/claude-plugins-official repository, preferences are persisted as markdown files with YAML front-matter, allowing developers to store settings alongside source code without committing them to version control. This approach enables plugins to read user-specific configurations at runtime to adjust behavior, thresholds, and toolchains dynamically.
The .claude/ Directory and Local Configuration Files
Claude plugins treat user preferences as per-project configuration files that live in a hidden .claude/ directory at the repository root. The preferred pattern follows the naming convention:
.claude/<plugin-name>.local.md
These files are ignored by Git—developers are encouraged to add .claude/*.local.md to .gitignore—so each user can maintain personal settings without polluting the shared repository. The format consists of YAML front-matter for structured key-value pairs, followed by an optional markdown body that can hold extra context or prompts.
File Structure and Format
A typical preferences file contains:
---
enabled: true
max_retries: 5
notification_level: "info"
---
# Optional explanatory notes or context
The front-matter section between the triple dashes stores the actual configuration values, while the markdown body below provides human-readable documentation or additional prompts for the user.
Loading and Parsing Preferences with config_loader.py
When a plugin or hook needs to honor a preference, the system loads and parses these markdown files through a centralized configuration loader. In plugins/hookify/core/config_loader.py, the load_rules() function scans the .claude/ directory to discover relevant configuration files.
The parsing workflow follows three steps:
- File Discovery – The loader identifies files matching
hookify.*.local.mdfor rule-based hooks or<plugin>.local.mdfor the plugin-settings skill. - Front-Matter Extraction – The
extract_frontmatter()function parses the YAML block into a Python dictionary, returning a tuple of(frontmatter_dict, body). - Value Application – The resulting dictionary is consulted by hook logic, agents, or commands to enable or disable functionality, adjust thresholds, or select toolchains.
The Plugin-Settings Skill for Interactive Configuration
The plugin-settings skill (defined in plugins/plugin-dev/skills/plugin-settings/SKILL.md) provides a guided workflow for creating and managing these configuration files. This skill automates the process of preference management by:
- Prompting the user for configuration values they want to persist
- Generating a starter
.claude/<plugin>.local.mdfile with the supplied values - Documenting the schema so future hooks can read the same keys reliably
Users invoke this skill through Claude Code to interactively generate properly formatted configuration files without manually editing YAML.
Implementing Preference Checks in Hooks and Commands
Plugins consume user preferences uniformly across the ecosystem, whether in Python-based hooks or shell commands.
Reading Preferences in Python Hooks
Hooks can load and evaluate preferences using the configuration loader and rule engine:
#!/usr/bin/env python3
import json
import sys
from core.config_loader import load_rules
from core.rule_engine import RuleEngine
def main():
input_data = json.load(sys.stdin) # data about the tool use
rules = load_rules(event='bash') # loads .claude/*.local.md
engine = RuleEngine()
result = engine.evaluate_rules(rules, input_data)
# The result may contain a decision based on preferences like `enabled`
print(json.dumps(result), file=sys.stdout)
if __name__ == "__main__":
main()
In this example, load_rules(event='bash') pulls the front-matter from .claude/hookify.my-plugin.local.md (if present) and constructs Rule objects that the RuleEngine evaluates against the incoming tool-use payload.
Reading Preferences in Bash Commands
For shell-based plugins, you can extract preferences using standard Unix tools:
#!/usr/bin/env bash
SETTINGS=".claude/my-plugin.local.md"
# Exit if no settings file
[[ -f "$SETTINGS" ]] || exit 0
# Extract front-matter
FRONT=$(sed -n '/^---$/,/^---$/{/^---$/d;p;}' "$SETTINGS")
# Get individual preferences
ENABLED=$(echo "$FRONT" | grep '^enabled:' | cut -d' ' -f2)
MAX_RETRIES=$(echo "$FRONT" | grep '^max_retries:' | cut -d' ' -f2)
if [[ "$ENABLED" != "true" ]]; then
echo "Plugin disabled – skipping"
exit 0
fi
echo "Running with max retries = $MAX_RETRIES"
# …rest of command logic…
This script extracts the YAML front-matter by parsing content between the first two --- delimiters, then reads specific preference values to control execution flow.
Summary
- Storage Location: User preferences reside in
.claude/<plugin-name>.local.mdfiles at the project root, ignored by Git to prevent repository pollution. - Format: YAML front-matter provides structured key-value storage, with an optional markdown body for documentation.
- Parsing Mechanism: The
extract_frontmatter()function inplugins/hookify/core/config_loader.pyconverts YAML blocks into Python dictionaries for runtime consumption. - Discovery: The
load_rules()function scans the.claude/directory to locate and load relevant configuration files based on event type or plugin name. - Workflow: The plugin-settings skill offers an interactive interface for generating and updating preference files without manual YAML editing.
Frequently Asked Questions
What file format do Claude plugins use for user preferences?
Claude plugins use markdown files with YAML front-matter. The front-matter section (delimited by triple dashes) contains structured key-value pairs, while the remainder of the file can contain markdown documentation. This format is human-readable, editable in any text editor, and easily parsed by both Python and shell scripts.
Are user preferences shared across different projects?
No, preferences are per-project by design. The .claude/<plugin-name>.local.md file lives within the specific repository's root directory, allowing users to maintain different configurations for different codebases. Since these files are gitignored, each clone of the repository can have its own independent settings.
How do hooks programmatically access user preferences?
Hooks access preferences through the load_rules() function in plugins/hookify/core/config_loader.py, which returns parsed rule objects containing the front-matter data. Alternatively, hooks can import and use extract_frontmatter() directly to parse specific .claude/ files, receiving a tuple of (frontmatter_dict, body) for custom processing logic.
Why are preference files stored as markdown rather than JSON or YAML files?
The markdown format with YAML front-matter provides a balance between machine-parseable structure and human readability. The .local.md extension clearly indicates these are local configuration files, while the markdown body allows developers to include explanatory notes, context, or prompts alongside the structured data. This approach keeps documentation and configuration co-located for easier maintenance.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →