Dotforge sync-template
Update an existing project's Claude Code configuration against the current dotforge template, without losing local customizations.
git clone https://github.com/luiseiman/dotforge
T=$(mktemp -d) && git clone --depth=1 https://github.com/luiseiman/dotforge "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/sync-template" ~/.claude/skills/luiseiman-dotforge-sync-template && rm -rf "$T"
skills/sync-template/SKILL.mdSync Template
Update the current project's Claude Code configuration against the latest version of dotforge.
Principle: merge, not overwrite
NEVER overwrite existing files without confirmation. Compare and propose changes. NEVER touch
settings.local.json — it is the user's personal configuration.
NEVER modify sections marked with <!-- forge:custom --> in CLAUDE.md.
Step 0: Verify global
Before syncing the project, verify that
~/.claude/CLAUDE.md exists and contains behavior rules (communication, planning, autonomy). If global rules are active, the project's _common.md only needs technical rules (git, naming, testing, security). Do not duplicate what is already in global.
Step 1: Detect current state
- Read the current
.claude/settings.json - Read the current
CLAUDE.md - Read existing
.claude/rules/ - Read existing
.claude/hooks/ - Detect stacks using
$DOTFORGE_DIR/stacks/detect.md - Read
to know which rules are already covered globally~/.claude/CLAUDE.md
Step 2: Compare against template
For each component, compare with the dotforge version:
settings.json — Smart merge
- allow: union of sets. Add missing permissions from base template + stacks. NEVER remove local permissions the project already has.
- deny: union of sets. Add missing security denies. NEVER remove local denies.
- hooks: add missing hooks from template. Preserve custom project hooks.
- Other fields: preserve everything that is not allow/deny/hooks (e.g., MCP configs).
Rules
- Is
missing? → propose adding it_common.md - Are rules for the detected stack missing? → propose adding them
- Are existing rules outdated? → show diff, propose update
- Custom project rules (not in template) → DO NOT TOUCH
→ NEVER TOUCH. Domain rules are project-owned knowledge, not template-managed. Skip entirely during sync..claude/rules/domain/
Hooks
- Is
missing? → propose adding + chmod +xblock-destructive.sh - Is the stack's lint hook missing? → propose adding + chmod +x
- Custom project hooks → DO NOT TOUCH
- Verify that existing hooks are executable (chmod +x)
CLAUDE.md
- Compare standard template sections with the project's sections
- Sections with
→ SKIP entirely<!-- forge:custom --> - Missing template sections → propose adding
- Custom project sections → DO NOT TOUCH
Step 3: Generate dry-run
Show the user what would change BEFORE applying anything:
═══ SYNC DRY-RUN: {{project}} ═══ dotforge: {{version}} (current project: {{previous_version or "unknown"}}) NEW FILES (will be created): + .claude/rules/_common.md + .claude/hooks/block-destructive.sh UPDATED FILES (merge): ~ .claude/settings.json + allow: "Bash(docker *)", "Bash(docker compose *)" + deny: "**/.env.local" (local permissions preserved: "Bash(custom-script *)") ~ .claude/rules/backend.md diff: +3 lines (new common errors) NO CHANGES: = .claude/rules/frontend.md (already up to date) = .claude/hooks/lint-ts.sh (already up to date) IGNORED (custom): ⊘ .claude/rules/strategies.md (not in template) ⊘ CLAUDE.md section "<!-- forge:custom -->" Apply changes? (yes/no/select)
Step 4: Apply with confirmation
Only apply the changes the user approves.
→ apply allyes
→ cancelno
→ show each change and ask yes/no individuallyselect
For settings.json: build the final merged JSON. Before writing, validate that the JSON is valid:
echo '<json_content>' | python3 -c 'import json,sys; json.load(sys.stdin)'
If validation fails, show the exact error and DO NOT write the file. Fix the JSON before continuing.
For hooks: copy +
chmod +x.
Step 4b: Update manifest
After applying changes, update (or create)
.claude/.forge-manifest.json:
- If manifest exists, read it
- For each file created or modified during sync, recalculate hash:
shasum -a 256 <file> | cut -d' ' -f1 - Update
,dotforge_version
, andsynced_at
(from detected stacks)stacks - Write the updated manifest
Format:
{ "dotforge_version": "<version from $DOTFORGE_DIR/VERSION>", "synced_at": "<current date YYYY-MM-DD>", "stacks": ["<detected-stack-1>", "<detected-stack-2>"], "files": { ".claude/settings.json": {"hash": "sha256:<hash>", "source": "template+stacks"}, ".claude/rules/_common.md": {"hash": "sha256:<hash>", "source": "template"} } }
Include ALL files in
.claude/ that are managed by dotforge (not only those that changed in this sync).
Step 5: Update registry
Update in
$DOTFORGE_DIR/registry/projects.yml:
→ current datelast_sync:
→ current dotforge versiondotforge_version:
Step 6: Verify
Run the
/audit-project logic to confirm the score improved or held steady.
Show score before and after.