One-file solution for syncing markdown documents with Craft.
Sync all files (bidirectional):
node craft-mirror.jsPush only (local → Craft, ignore Craft changes):
node craft-mirror.js --pushPull only (Craft → local, ignore local changes):
node craft-mirror.js --pullReset config and start fresh:
node craft-mirror.js --reset
node craft-mirror.js --pushSync specific files only:
node craft-mirror.js FILE1.md FILE2.mdSync specific folders only:
node craft-mirror.js archive
node craft-mirror.js archive templatesPush specific files only:
node craft-mirror.js --push FILE1.md FILE2.mdPush specific folders only:
node craft-mirror.js --push archiveThe script handles everything automatically - discovers new files, syncs changes, creates nested pages for subdirectories.
If you delete all pages in Craft and want to restore from local:
-
Reset the config (this removes old page IDs):
node craft-mirror.js --reset
-
Push everything fresh:
node craft-mirror.js --push
This prevents duplicate pages from being created.
- Make sure
bundle.md(from Craft's Multi-Document API) is in this directory - Run
node craft-mirror.js - The script auto-creates config and syncs all markdown files
- ✅ Bidirectional Sync - Compares timestamps, syncs newest version
- ✅ One-Way Sync Modes - Push-only or pull-only modes for controlled syncing
- ✅ Auto-Discovery - Finds new files and creates Craft pages automatically
- ✅ Subdirectory Nesting - Creates hierarchical Craft page structures with 📂 emoji prefixes
- ✅ File Move Detection - Automatically handles files moved between directories
- ✅ Title Tracking - Renames local files when Craft page titles change
- ✅ Zero Config - Extracts API settings from
bundle.md, maintains config automatically - ✅ Duplicate Prevention - Removes duplicate H1 titles to avoid repetition on Craft pages
Bidirectional (default)
- Push: Local files newer than Craft → uploaded to Craft
- Pull: Craft pages newer than local → downloaded locally
- Skip: Content matches → no action needed
Push Mode (--push)
- All local changes are pushed to Craft, regardless of timestamps
- Craft changes are ignored
- Use case: You deleted all pages in Craft and want to restore from local
Pull Mode (--pull)
- All Craft changes are pulled to local, regardless of timestamps
- Local changes are ignored
- Use case: You want to sync Craft changes made by others without pushing your local edits
- New local
.mdfiles → automatically creates Craft pages - New Craft pages → automatically downloads as local files
- Subdirectories → creates nested Craft page structures
docs/
├── MyDoc1.md → Sub-page inside your Craft page
├── MyDoc2.md → Sub-page inside your Craft page
└── archive/ → Creates "📂archive" parent page
├── OLD_PLAN.md → Nested sub-page under "📂archive"
└── HISTORY.md → Nested sub-page under "📂archive"
Directory pages are automatically prefixed with a 📂 emoji to distinguish them from regular document pages in Craft.
The script creates and maintains craft-mirror-config.json automatically:
{
"craftApiBase": "https://connect.craft.do/links/XXX/api/v1",
"parentDocumentId": "YOUR-DOC-ID",
"mappings": [...],
"directories": [...]
}You don't need to edit this file - it updates automatically as you add/remove files.
Note: The script ignores certain files and never syncs them to Craft:
- Files containing
README(like README.md) - Files containing
bundle(like bundle.md) - Files containing
CRAFT(like craft-mirror-config.json)
These files are meant to remain local-only for the sync system itself.
When the user says "sync docs with Craft" (all files):
node craft-mirror.jsWhen the user says "push docs to Craft" or wants to restore after deleting Craft pages:
node craft-mirror.js --pushWhen the user says "pull docs from Craft" or wants to get Craft changes without pushing local:
node craft-mirror.js --pullWhen the user says "sync FILE.md with Craft" (specific files):
node craft-mirror.js FILE.mdPush specific files only:
node craft-mirror.js --push FILE1.md FILE2.mdPull specific files only:
node craft-mirror.js --pull FILE1.md FILE2.md