merge-rules
Original:🇺🇸 English
Translated
Merge extract-rules output from multiple projects into a unified portable rule set. Promotes .local.md patterns shared across projects to Principles format.
4installs
Added on
NPX Install
npx skill4agent add hiroro-work/claude-plugins merge-rulesTags
Translated version includes tags in frontmatterSKILL.md Content
View Translation Comparison →Merge Rules
Merges from multiple projects into a unified portable rule set (.md + .examples.md). Promotes patterns that appear across a threshold of projects by converting them to Principles format. Merges files alongside rule files.
.claude/rules/.local.md.examples.mdUsage
text
/merge-rules # Merge using config file
/merge-rules --config <path> # Merge using specified config file
/merge-rules --dry-run # Show what would be merged without writingConfiguration
Config file search order:
- argument
--config <path> - (project-level)
.claude/merge-rules.local.md - (user-level)
~/.claude/merge-rules.local.md
File format: YAML frontmatter only (no markdown body), same convention as .
extract-rules.local.mdyaml
---
# Source projects (each must have extract-rules output)
projects:
- ~/projects/frontend-app
- ~/projects/backend-api
- ~/projects/shared-lib
# Output directory (default: .claude/rules/)
output_dir: .claude/rules/
# Rules directory within each project (default: .claude/rules/)
# Corresponds to extract-rules' output_dir setting
rules_dir: .claude/rules/
# Threshold for promoting .local.md patterns (default: 0.5 = majority)
# Examples: 3 projects → 2/3 needed, 4 projects → 3/4, 5 projects → 3/5
promote_threshold: 0.5
# Report language (default: ja)
language: ja
---Processing Flow
Step 1: Load Configuration
- Search for config file (see search order above)
- If not found: Error "No config file found. Create or specify with
.claude/merge-rules.local.md."--config
- If not found: Error "No config file found. Create
- Parse YAML frontmatter, apply defaults for omitted fields
- resolution order: Skill config → Claude Code settings (
language→~/.claude/settings.jsonfield) → defaultlanguageja
- Validate:
- must have at least 2 entries
projects - Each project path must exist and contain
rules_dir - Error with clear message if validation fails
Step 2: Collect Rule Files
For each project:
- Find all ,
.md, and.local.mdfiles under.examples.md(recursive){path}/{rules_dir}/ - Categorize:
- → portable principles (always merge). If the file also contains
languages/*.md(hybrid format from## Project-specific patterns), treat patterns as promotion candidates (same assplit_output: false).local.md - → same as above
frameworks/*.md - → same as above
integrations/*.md - → promotion candidate
languages/*.local.md - → promotion candidate
frameworks/*.local.md - → promotion candidate
integrations/*.local.md - → example file (merge with rules)
languages/*.examples.md - → example file (merge with rules)
frameworks/*.examples.md - → example file (merge with rules)
integrations/*.examples.md - → skip (inherently project-specific)
project.md - → skip (inherently project-specific)
project.examples.md
- Parse each file: extract YAML frontmatter () and body sections (
paths:,## Principles,## Project-specific patterns,## Principles Examples)## Project-specific Examples
Step 3: Normalize Similar File Names
Before merging, group files that refer to the same concept but have different names. This applies to , , and files — a and its corresponding and share the same normalization (e.g., , , and are normalized together with their variants).
.md.local.md.examples.md.md.local.md.examples.mdrails-controller.mdrails-controller.local.mdrails-controller.examples.mdrails-controllers.*- Detect similar file names within the same directory (e.g., vs
rails-controller.md,rails-controllers.mdvsrails-model.md)rails-models.md- Singular/plural variants (e.g., /
controller)controllers - Minor naming differences for the same concept (use AI judgment based on file content and frontmatter overlap)
paths:
- Singular/plural variants (e.g.,
- For each group of similar files, select a canonical name:
- Prefer the name used by the majority of projects
- If tied, prefer the name matching extract-rules' layered framework convention (e.g., )
<framework>-<layer>
- Treat grouped files as the same file for subsequent merge steps (Step 4 and Step 5)
- Report normalized groups in the summary (e.g., "+
rails-controller.md→rails-controllers.md")rails-controllers.md
Step 4: Merge Portable Rules (.md)
Design note: Once a pattern is promoted to a Principle (via Step 5), it becomes a permanent org-level rule. Subsequent merge-rules runs will preserve it through Step 4's principle deduplication, regardless of whether the original pattern still meets the promotion threshold. To demote or remove a promoted Principle, manually edit the org rules output.
.local.mdFor each unique (normalized) file name across projects (e.g., , ):
languages/typescript.mdintegrations/rails-inertia.md- Collect all versions from projects that have this file (including normalized variants)
- Merge sections:
## Principles- Deduplicate by principle name (text before parenthetical hints)
- Union hints from all projects for the same principle
- If same principle name but clearly different meaning → keep both, flag in report (see Conflict Handling)
- Preserve unique principles from any project
- Merge frontmatter: union of all path patterns, deduplicate
paths: - If file exists in only 1 project, include as-is
Step 5: Promote .local.md Patterns to Principles
For each normalized category (e.g., , , ):
languages/typescriptframeworks/rails-controllersintegrations/rails-inertia- Collect from all projects — from
## Project-specific patternsfiles and from hybrid.local.mdfiles that contain this section (see Step 2).md - Deduplicate against existing Principles: Exclude patterns whose description (text after ) semantically matches an existing principle name in the corresponding
-output (from Step 4). Use AI judgment for semantic equivalence (case-insensitive, synonyms). This prevents self-amplification when.mdcontains patterns previously promoted by older versions.local.md - Match remaining patterns by inline code signature (backtick portion before )
-- Use AI judgment to determine semantic equivalence (e.g., and
useAuth()refer to the same pattern)useAuth() → { user, login, logout }
- Use AI judgment to determine semantic equivalence (e.g.,
- Count occurrences per pattern across projects
- Calculate threshold: pattern must appear in more than projects (i.e., strict majority when threshold = 0.5)
len(projects) * promote_threshold - Convert to Principles format and append to in the corresponding normalized
## Principlesoutput:.md- Signature format: → Principles format:
`signature` - descriptionDescription (simplified signature) - The description becomes the principle name, the function/type name from the signature becomes the hint
- Examples:
- →
`useAuth() → { user, login, logout }` - auth hook interfaceAuth hook interface (useAuth) - →
`clean_bracket_params(:keyword)` - WAF付加のブラケット除去WAF付加のブラケット除去 (clean_bracket_params) - →
`RefOrNull<T extends { id: string }> = T | { id: null }` - nullable refsNullable refs (RefOrNull<T>)
- Apply Step 4's principle deduplication to the converted principles (skip if same principle name already exists)
- Signature format:
- Patterns below threshold → discard (listed in report for reference)
Step 5.5: Merge Examples (.examples.md)
For each normalized file group:
.examples.md- Collect all versions from projects that have this file (including normalized variants)
- Principles Examples: Merge by section heading (e.g., )
### FP only- Same principle heading across projects → adopt the most detailed example, or merge Good/Bad from different projects
- If Good/Bad contrast exists in one project but not another → adopt from the project that has it
- Deduplicate identical examples
- Promoted pattern examples: For patterns promoted in Step 5, include their examples under
## Principles Examples- Use the same semantic equivalence judgment as Step 5 (matching by inline code signature with AI judgment) to link example headings to promoted patterns — do not rely solely on exact heading match
### - title uses the converted Principle name (from Step 5), not the original signature
### - Include the full original signature as a Good example showing usage
- Discard examples for patterns below threshold (same as the pattern itself)
- Use the same semantic equivalence judgment as Step 5 (matching by inline code signature with AI judgment) to link
- Output file structure:
.examples.md
markdown
# <Category> Rules - Examples
## Principles Examples
### <Principle name>
**Good:**
```<lang>
<example>Bad:
<example>
- `###` titles must match the corresponding rule name in the merged output `.md` file. Do not rephrase
- No `paths:` frontmatter (prevents auto-loading)
- If no examples exist for any merged rule, skip generating the `.examples.md` file
### Step 6: Write Output
1. Check output directory:
- If `--dry-run`: skip writing, show planned file list with contents summary, then go to Step 7
- If exists and has files: warn and ask for confirmation before overwriting
- If not exists: create with `mkdir -p`
2. Write merged files preserving directory structure:
- `languages/<lang>.md`
- `languages/<lang>.examples.md` (if examples exist)
- `frameworks/<framework>.md`
- `frameworks/<framework>.examples.md` (if examples exist)
- `integrations/<framework>-<integration>.md`
- `integrations/<framework>-<integration>.examples.md` (if examples exist)
- Only `.md` and `.examples.md` files (no `.local.md` in output)
3. Output file format:
```markdown
---
paths:
- "**/*.ts"
- "**/*.tsx"
---
# TypeScript Rules
## Principles
- Immutability (spread, map/filter/reduce, const)
- Type safety (strict mode, explicit annotations, no any)
- Auth hook interface (useAuth)- Output contains only
.md(promoted patterns are converted and included here)## Principles - Omit section if no principles exist for this category
## Principles - If a corresponding was generated, append a reference section at the end:
.examples.mdmarkdown## Examples When in doubt: ./<name>.examples.md
Step 7: Report Summary
Display report using the project's directory name (last path component) as label. Report headers are always in English.
# Merge Rules Report
## Sources
- frontend-app (3 files)
- backend-api (2 files)
- shared-lib (4 files)
## File Name Normalization
- `rails-controller.md` + `rails-controllers.md` → `rails-controllers.md`
- `rails-model.md` + `rails-models.md` → `rails-models.md`
## Merge Results
| File | Sources | Principles | Promoted to Principles | Examples |
|------|---------|------------|------------------------|----------|
| languages/typescript.md | 3/3 | 5 | 2 | 7 |
| frameworks/react.md | 2/3 | 3 | 1 | 4 |
| integrations/rails-inertia.md | 2/3 | 2 | 0 | 2 |
**Principles** = total including promoted. **Examples** = total `###` entries in the output `.examples.md`.
## Promoted to Principles
- `useAuth()` → Auth hook interface (useAuth) - 3/3 projects
- `pathFor() + url()` → Path helpers (pathFor, url) - 2/3 projects
## Below Threshold (reference)
- `useCustomHook()` (typescript) - 1/3 (frontend-app only)
- `ApiClient.create()` (typescript) - 1/3 (backend-api only)
## Skipped
- project.md x3 (project-specific, skipped)Conflict Handling
- Same principle, different hints: Union all hints, deduplicate
- Same principle name, different meaning: Keep both, flag in report for human review
- Same category, different paths: Union all path patterns
- Contradicting principles: Keep both, report as conflict for human review