Skill v1.0.2
currentAutomated scan100/1004 files
name: merge-rules description: Merge extract-rules output from multiple projects into a unified portable rule set. Promotes .local.md patterns shared across projects to Principles format. allowed-tools: Read, Glob, Grep, Write, Bash(ls ), Bash(mkdir ), Bash(wc *) metadata: version: "1.0.2"
Merge Rules
Usage
/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 writing
Configuration
Config file search order:
--config <path>argument.claude/merge-rules.local.md(project-level)~/.claude/merge-rules.local.md(user-level)
File format: YAML frontmatter only (no markdown body).
---# Source projects (each must have extract-rules output)projects:- ~/projects/frontend-app- ~/projects/backend-api- ~/projects/shared-lib# Output directory (default: .claude/rules)# Examples go to the sibling <output_dir>-extras (.claude/rules-extras)output_dir: .claude/rules# Rules directory within each project (default: .claude/rules)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/5promote_threshold: 0.5# Report language (default: ja)language: ja---
Examples directories (no configuration key). Examples live in a sibling of the rules directory: that path with any trailing / removed and -extras appended — {path}/{rules_dir}-extras for each source project, <output_dir>-extras for the output. Strip the trailing / before appending, or a configured .claude/rules/ yields .claude/rules/-extras. Collection takes each rule file's examples from the derived directory first and from the rule directory itself only when that misses, so a pre-split layout — examples still beside the rule files — is still collected; writing always targets the derived directory. merge-rules does not read extract-rules' examples_output_dir.
Processing Flow
Step 1: Load Configuration
- Search for config file (see search order above)
- If not found: Error "No config file found. Create
.claude/merge-rules.local.mdor specify with--config."
- Parse YAML frontmatter, apply defaults for omitted fields
- `language` resolution order: Skill config → Claude Code settings (
~/.claude/settings.json→languagefield) → defaultja
- Validate:
projectsmust have at least 2 entries- Each project path must exist and contain
rules_dir - Error with clear message if validation fails
Step 2: Collect Rule Files
For each project:
- Recursively list
{path}/{rules_dir}/and{path}/{rules_dir}-extras(derived per § Configuration's "Examples directories (no configuration key)" paragraph) — two listings per project, never a third. A missing-extrasdirectory is the normal pre-split case: treat it as an empty listing rather than an error. Rule files are the.mdand.local.mdentries of the first listing; examples are the union of the*.examples.mdentries of both, with-extraswinning when the same relative sub-path appears in each. A project part-way through migration therefore contributes both its moved and its still-co-located examples - Categorize:
languages/*.md→ portable principles (always merge). If the file also contains `## Project-specific patterns` (hybrid format fromsplit_output: false), treat patterns as promotion candidates (same as.local.md)frameworks/*.md→ same as aboveintegrations/*.md→ same as abovelanguages/*.local.md→ promotion candidateframeworks/*.local.md→ promotion candidateintegrations/*.local.md→ promotion candidatelanguages/*.examples.md→ example file (merge with rules)frameworks/*.examples.md→ example file (merge with rules)integrations/*.examples.md→ example file (merge with rules)project.md→ skip (inherently project-specific)project.examples.md→ skip (inherently project-specific)
- Parse each file: extract YAML frontmatter (
paths:) and body sections (## 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 .md, .local.md, and .examples.md files — a .md and its corresponding .local.md and .examples.md share the same normalization (e.g., rails-controller.md, rails-controller.local.md, and rails-controller.examples.md are normalized together with their rails-controllers.* variants).
- Detect similar file names at the same relative sub-path (e.g.,
rails-controller.mdvsrails-controllers.md,rails-model.mdvsrails-models.md). The rule tree and the examples tree mirror each other, soframeworks/in either tree is the same position
- Singular/plural variants (e.g.,
controller/controllers) - Minor naming differences for the same concept (use AI judgment based on file content and
paths:frontmatter overlap)
- 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)
Once a pattern is promoted to a Principle (via Step 5), subsequent merge-rules runs preserve it through Step 4's principle deduplication, regardless of whether the original .local.md pattern still meets the promotion threshold. To demote or remove a promoted Principle, manually edit the org rules output.
For each unique (normalized) file name across projects (e.g., languages/typescript.md, integrations/rails-inertia.md):
- Collect all versions from projects that have this file (including normalized variants)
- Merge
## Principlessections:
- 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
- Preserve unique principles from any project
- Merge
paths:frontmatter: union of all path patterns, deduplicate - 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/typescript, frameworks/rails-controllers, integrations/rails-inertia):
- Collect
## Project-specific patternsfrom all projects — from.local.mdfiles and from hybrid.mdfiles that contain this section (see Step 2) - Deduplicate against existing Principles: Exclude patterns whose description (text after
-) semantically matches an existing principle name in the corresponding.mdoutput (from Step 4). Use AI judgment for semantic equivalence (case-insensitive, synonyms). - Match remaining patterns by inline code signature (backtick portion before
-)
- Use AI judgment to determine semantic equivalence (e.g.,
useAuth()anduseAuth() → { user, login, logout }refer to the same pattern)
- Count occurrences per pattern across projects
- Calculate threshold: pattern must appear in more than
len(projects) * promote_thresholdprojects (i.e., strict majority when threshold = 0.5) - Convert to Principles format and append to
## Principlesin the corresponding normalized.mdoutput:
- Signature format: `
signature- description→ Principles format:Description (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 interface→Auth hook interface (useAuth)` - `
clean_bracket_params(:keyword)- WAF付加のブラケット除去→WAF付加のブラケット除去 (clean_bracket_params)` - `
RefOrNull<T extends { id: string }> = T | { id: null }- nullable refs→Nullable refs (RefOrNull<T>)` - Apply Step 4's principle deduplication to the converted principles (skip if same principle name already exists)
- Patterns below threshold → discard (listed in report for reference)
Step 5.5: Merge Examples (.examples.md)
For each normalized .examples.md file group:
- 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)
- Output
.examples.mdfile structure:
# <Category> Rules - Examples## Principles Examples### <Principle name>**Good:**
<example>
**Bad:**
<example>
###titles must match the corresponding rule name in the merged output.mdfile. Do not rephrase- No
paths:frontmatter - If no examples exist for any merged rule, skip generating the
.examples.mdfile
Step 6: Write Output
- Check the output directories
output_dirand<output_dir>-extras(derived per § Configuration's "Examples directories (no configuration key)" paragraph):
- If
--dry-run: skip writing, show planned file list with contents summary, then go to Step 7 - If either exists and has files: warn and ask for confirmation before overwriting
- For either that does not exist: create with
mkdir -p
- Write merged files preserving directory structure:
<output_dir>/languages/<lang>.md<output_dir>-extras/languages/<lang>.examples.md(if examples exist)<output_dir>/frameworks/<framework>.md<output_dir>-extras/frameworks/<framework>.examples.md(if examples exist)<output_dir>/integrations/<framework>-<integration>.md<output_dir>-extras/integrations/<framework>-<integration>.examples.md(if examples exist)- Only
.mdand.examples.mdfiles (no.local.mdin output)
- Output file format:
---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
.mdcontains only## Principles(promoted patterns are converted and included here) - Omit
## Principlessection if no principles exist for this category - If a corresponding
.examples.mdwas generated, append a reference section at the end:
``markdown ## Examples When in doubt: <relative-path-to-examples-file> ` The path is that examples file — <output_dir>-extras/<same relative sub-path>/<name>.examples.md — expressed relative to the rule file's own directory. With the default output_dir, languages/typescript.md gets ../../rules-extras/languages/typescript.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
- Contradicting principles: Keep both, report as conflict for human review