DEV Community

Nasrul Hazim Bin Mohamad
Nasrul Hazim Bin Mohamad

Posted on

I Built a Claude Code Skill to Sync CLAUDE.md Across 12+ Laravel Projects

You know that feeling when you update something important in one place, then you realise — oh no, I need to propagate this to like 10 other projects?

Yeah. That was me last week.


A Bit of Background

I maintain a Laravel kickstart project called Kickoff. Every Laravel project I start — personal, client, internal — starts from this base. It contains my opinionated project structure, conventions, Docker setup, and one particularly important file:

stubs/CLAUDE.md

If you're using Claude Code, this file matters a lot. It's the instruction manual Claude reads before it touches your codebase. Stack, conventions, patterns, boundaries — it's all there. Without it, Claude guesses. With it, Claude codes like it already knows your project.

Problem: I have multiple projects built on top of Kickoff. Every time I update stubs/CLAUDE.md in Kickoff, those changes need to flow downstream. Into every project. One by one.

I did it manually twice. Third time, I refused.


The Boring Fix vs The Right Fix

The boring fix: write a bash script that loops through directories and overwrites CLAUDE.md. Quick. Fragile. Dumb.

The problem with dumb scripts? They don't understand context. My projects aren't identical. Each one has project-specific sections in CLAUDE.md — custom domain notes, environment variables, Docker services, known issues, client-specific conventions. A dumb overwrite nukes all of that.

What I actually need is something that can:

  1. Scan a parent directory for projects
  2. Figure out which ones are based on Kickoff
  3. Diff their CLAUDE.md against the latest from Kickoff
  4. Merge the changes without destroying project-specific content

That's not a bash script. That's judgment. And Claude Code is actually good at that — if you give it the right structure upfront.

So I built a Claude Code skill.


What's a Claude Code Skill?

A skill in Claude Code is a SKILL.md file that provides Claude with structured context, instructions, and a defined workflow for a specific repeatable task.

Think of it like this — instead of you typing a long prompt every single time, you encode the decision tree once into a skill file. Claude reads it, understands what it's supposed to do, and executes consistently every time.

I've been building these skills in my own repo: nasrulhazim/agent-skills. I already have skills for testing, Livewire components, API lifecycle, CI/CD pipelines, and more. This new one — project-sync — joins the collection.

A skill is just a directory:

skills/project-sync/
├── SKILL.md                              # The instructions
└── references/
    ├── merge-algorithm.md                # How to merge sections
    ├── section-classification.md         # What's shared vs project-specific
    ├── detection-markers.md              # How to identify Kickoff projects
    └── registry-schema.md               # Registry file format
Enter fullscreen mode Exit fullscreen mode

No runtime. No API. No build step. Just markdown that Claude reads and follows.


What the Skill Actually Does

Here's the workflow, broken into commands:

Scan: Find All Kickoff Projects

/project-sync scan ~/Projects --since=2025
Enter fullscreen mode Exit fullscreen mode

Claude scans the directory tree, finds Laravel projects (checks for artisan + composer.json), then verifies Kickoff-specific markers:

  • app/Models/Base.php exists
  • composer.json requires cleaniquecoders/traitify
  • support/helpers.php in autoload
  • CLAUDE.md mentions "Kickoff"

At least one marker must match. This avoids false positives from vanilla Laravel projects.

Output:

Scanned: ~/Projects
Found: 12 Kickoff Laravel projects (filtered: --since=2025)

~/Projects/2025/ — 8 projects
  ✓ project-alpha          CLAUDE.md: 18.2 KB
  ✓ project-beta           CLAUDE.md: 22.1 KB
  ✗ project-gamma          No CLAUDE.md

~/Projects/2026/ — 4 projects
  ✓ project-delta          CLAUDE.md: 19.8 KB
  ...
Enter fullscreen mode Exit fullscreen mode

It can also scan a GitHub account without cloning anything:

/project-sync scan gh:cleaniquecoders --since=2025
Enter fullscreen mode Exit fullscreen mode

Everything gets saved to a registry at ~/.claude/projects/.project-sync.json — persistent across Claude Code sessions.

Status: What Needs Updating?

/project-sync status
Enter fullscreen mode Exit fullscreen mode
Source: kickoff/stubs/CLAUDE.md (fetched from GitHub)

Project                 Status      Size     Last Synced
─────────────────────────────────────────────────────────
project-alpha           ✓ current   18.2 KB  2026-03-10
project-beta            ✗ outdated  22.1 KB  2026-02-15
project-gamma           ⚠ missing   —        never
project-delta           ✗ outdated  19.8 KB  2026-03-01

Summary: 1 current, 2 outdated, 1 missing CLAUDE.md
Enter fullscreen mode Exit fullscreen mode

Instantly see which projects have drifted.

Diff: Preview Before You Touch Anything

/project-sync diff project-beta
Enter fullscreen mode Exit fullscreen mode

Dry-run merge. No files changed. Just shows what would happen:

## Sections to UPDATE (from source):
  - Architecture & Key Concepts > Models - CRITICAL  [changed]
  - Architecture & Key Concepts > Enums  [changed]
  - Livewire Patterns > Toast Notifications  [changed]

## Sections to MERGE (combine items):
  - DO list: +2 new items from source, 3 project-only preserved
  - DON'T list: +1 new item from source, 1 project-only preserved

## Sections PRESERVED (project-specific):
  - Project Overview
  - Common Commands
  - Packages
  - Docker Services
  - Environment Variables
Enter fullscreen mode Exit fullscreen mode

No surprises. You see exactly what gets replaced, what gets merged, and what stays untouched.

Update: Do the Thing

/project-sync update all
Enter fullscreen mode Exit fullscreen mode
Sync complete:
  ✓ Updated: 8 projects
  — Skipped: 2 projects (already current)
  ✗ Failed: 1 project (git working tree dirty)

Report saved: ~/.claude/projects/reports/20260314.0600/
Enter fullscreen mode Exit fullscreen mode

Each updated project gets a clean commit: docs: sync CLAUDE.md with kickoff conventions.


The Secret Sauce: Section-Level Merging

This is the part that makes it actually work. Not line-level diffing — section-level merging.

A CLAUDE.md built from Kickoff has a predictable structure. Every H2/H3 section falls into one of four categories:

Type Action Examples
SHARED Replace from source Model conventions, testing rules, Livewire patterns
PROJECT-SPECIFIC Never touch Project overview, packages, env vars, Docker config
MERGE Combine both lists DO/DON'T — keep source items + project-only items
CONDITIONAL Check each subsection Important Conventions (mixed shared + project-specific)

How a DO List Merge Works

Source (from Kickoff):

- ✅ DO extend App\Models\Base for all models
- ✅ DO use dual-key pattern
- ✅ DO use Pest syntax for tests
- ✅ DO use Form Requests for validation     ← NEW
Enter fullscreen mode Exit fullscreen mode

Target (project-beta has custom items):

- ✅ DO extend App\Models\Base for all models
- ✅ DO use Pest syntax for tests
- ✅ DO use PostgreSQL for all new tables     ← PROJECT-ONLY
- ✅ DO prefix admin routes with /admin       ← PROJECT-ONLY
Enter fullscreen mode Exit fullscreen mode

Merged result:

- ✅ DO extend App\Models\Base for all models
- ✅ DO use dual-key pattern                   ← RESTORED FROM SOURCE
- ✅ DO use Pest syntax for tests
- ✅ DO use Form Requests for validation       ← NEW FROM SOURCE
- ✅ DO use PostgreSQL for all new tables       ← PRESERVED
- ✅ DO prefix admin routes with /admin         ← PRESERVED
Enter fullscreen mode Exit fullscreen mode

Source items are updated and new ones added. Project-only items are preserved. Nobody loses anything.

The Section Classification Map

Here's the actual map encoded in the skill:

Section Classification
## Project Overview PROJECT-SPECIFIC
## Common Commands PROJECT-SPECIFIC
## Architecture & Key Concepts SHARED
## File Organization SHARED
## Testing with Pest SHARED
## Livewire Patterns SHARED
## Important Conventions CONDITIONAL
## Code Quality Checklist SHARED
## Packages PROJECT-SPECIFIC
## Docker Services PROJECT-SPECIFIC
## Environment Variables PROJECT-SPECIFIC
## Gotchas CONDITIONAL
Any unlisted H2 section PROJECT-SPECIFIC

That last row is important — if a project adds a custom section like ## Deployment Notes or ## Client Requirements, the skill automatically preserves it because it's not in the source.


Edge Cases

Because real-world projects are messy:

Scenario What Happens
Project has no CLAUDE.md Creates one from source + composer.json metadata
Dirty git working tree Skips the project, reports as failed
Source URL unreachable Falls back to cache, or asks for --source override
Custom H2 sections in project Always preserved — if it's not in source, it's yours
Merged file exceeds 40 KB Auto-refines (trims whitespace, condenses examples), then asks if still over
Sections in different order Preserves project's order — merge is content-based, not position-based

Why This Matters Beyond CLAUDE.md

The real point here isn't about CLAUDE.md specifically. It's about a pattern shift.

Every time I find myself doing something tedious and repetitive — especially something that involves judgment, not just mechanics — I now ask one question:

Can I encode this into a skill?

Same instinct that makes me write packages instead of copy-pasting code. Same instinct that makes me extract traits and contracts instead of duplicating logic. Except now, instead of PHP abstractions, I'm building AI workflow abstractions.

The mental model:

Tedious thing detected
  → Solve it once manually, understand the decision tree
    → Encode the decision tree into a skill
      → Never think about it again
Enter fullscreen mode Exit fullscreen mode

Practical Steps If You Want to Do This

  1. Start with the boring problem. What do you keep doing manually that involves some judgment?

  2. Map out the decision tree before writing anything. What are the conditions? What are the outputs? What needs human review vs what's automatable?

  3. Write the SKILL.md. Phases, inputs, workflow, edge cases. Be explicit. Claude follows structure well.

  4. Keep it in version control. I keep mine in nasrulhazim/agent-skills. Commit it, tag it, evolve it.

  5. Test it on a safe project first. Dry run. Verify the output before letting it touch 10 projects.


Try It Yourself

Install all my skills:

curl -fsSL https://raw.githubusercontent.com/nasrulhazim/agent-skills/main/install.sh | bash
Enter fullscreen mode Exit fullscreen mode

Or just this one:

git clone https://github.com/nasrulhazim/agent-skills.git
cp -r agent-skills/skills/project-sync ~/.claude/skills/
Enter fullscreen mode Exit fullscreen mode

Then in Claude Code:

/project-sync scan ~/Projects
/project-sync status
/project-sync diff project-name
/project-sync update all
Enter fullscreen mode Exit fullscreen mode

Final Thought

AI tools aren't magic. But when you invest time designing how you use them — giving them structure, context, repeatable workflows — they become serious force multipliers.

The leverage isn't in using AI. The leverage is in designing how you use AI.

Working smart isn't about working faster. It's about doing the tedious thing once, then letting the system carry it forward.

That's the whole game.


My agent skills collection (26 skills, MIT licensed) → github.com/nasrulhazim/agent-skills

Kickoff (Laravel kickstart) → kickoff.my

Top comments (0)