maestro-sync
This skill keeps the orchestrator instance aligned with the Maestro template at https://github.com/spleenteo/maestro. It applies upstream pattern changes (CLAUDE.md sections, base hub skills, base craft agents) without ever touching the instance's personalizations.
Architecture
Two paths, two roles (decided 2026-04-30):
~/Sites/me/maestro/— primary working tree. Where the owner promotes new patterns from this instance to the template (modify, commit, push). This skill never writes here. It only reads its git status to warn the owner if there are uncommitted changes that should be pushed before syncing.~/.maestro/— read-only mirror used by this skill as a stable comparison point. The mirror is updated only by this skill, withgit fetch origin && git reset --hard origin/main. Never committed to, never modified by hand.
The skill scans the instance (the orchestrator that invokes the skill) for files with origin: maestro in their frontmatter, compares each with the mirror version, and proposes a diff per file with confirmation. It also scans the mirror in the opposite direction: files marked origin: maestro that exist upstream but not in the instance are proposed as new files, with the same per-file confirmation.
Files in scope
The skill operates on files marked with both origin: maestro and maestro_version: vYYYY.MM.DD.N in their frontmatter. Typical inheritable files:
CLAUDE.md(top-level).claude/skills/<name>/SKILL.mdfor hub skills distributed by Maestro (e.g.setup,logbook,add-external-app,guide,maestro-syncitself).claude/agents/<name>.mdfor craft agents distributed by Maestro (e.g.librarian,scheduler,hr)
Files never in scope:
private/*(preferences, memories.db, logs, anything sensitive)apps/*(sub-apps and their internals)- Custom skills/agents added by the instance (no
origin: maestromarker) .claude/roster.yaml(instance-specific list of active agents — Maestro doesn't choose which agents an instance enrolls)
When this skill runs
Invoke it when the owner says something like:
- "Sync Maestro" / "Update from Maestro" / "Pull Maestro changes"
- "/maestro-sync"
- "Are there new patterns from the template?"
- "Bring this instance up to date with the latest Maestro"
Operational flow
Six phases. Stop and report on the first error — never continue past a failure silently.
Phase 1 — Locate the two paths
Resolve from preferences (or use sensible defaults):
- Mirror path:
~/.maestro/(default). If preferences declaremaestro_mirror_path:, use that. - Working tree path:
~/Sites/me/maestro/(default). If preferences declaremaestro_worktree_path:, use that.
If the mirror doesn't exist yet, bootstrap it: git clone git@github.com:spleenteo/maestro <mirror-path>. Tell the owner: "First run: I'm cloning the Maestro mirror at ."
If the working tree doesn't exist, that's fine — promotions can still be done by cloning it on demand. Skip Phase 2 with a soft note: "No primary working tree at ; skipping the uncommitted-changes check. Nothing to lose."
Phase 2 — Pre-sync check on the working tree
This is the hook that catches in-flight promotions before they get clobbered by a sync. Run on the primary working tree, never on the mirror.
cd <worktree-path>
git status --porcelain # any uncommitted changes?
git log @{u}..HEAD --oneline # any local commits not yet pushed?
- If
git status --porcelainreturns non-empty → there are uncommitted changes. - If
git log @{u}..HEADreturns non-empty → there are local commits not pushed.
In either case, stop and ask the owner:
⚠ I see local changes in your Maestro working tree (
<worktree-path>):
- <list of modified/untracked files, max 10>
- <list of unpushed commits, max 5>
If you're in the middle of promoting a pattern to the template, push these first — otherwise the sync will pull
origin/mainwithout them, and the instance won't see your work-in-progress.Options:
push— let me push them now (only if all changes are committed; if there are uncommitted edits, I won'tgit add -Afor you)continue— sync anyway, knowing the local work isn't reflectedabort— stop here, you handle it manually
Wait for the owner. If push, run git push origin main from the working tree. If continue, proceed to Phase 3. If abort, exit with a clean message.
Phase 3 — Refresh the read-only mirror
cd <mirror-path>
git fetch origin
git reset --hard origin/main
This is the only write operation on the mirror — it brings it to whatever origin/main is. The mirror is never trusted as a working tree; it's a reproducible snapshot of upstream.
After this, capture:
MIRROR_HEAD=git -C <mirror-path> rev-parse HEAD(commit SHA at the mirror)MIRROR_VERSION= the latest## v...heading in<mirror-path>/CHANGELOG.md
Phase 4 — Scan the instance
Walk the instance's repository (cwd from which the skill was invoked, expected to be the orchestrator instance root) and collect files with origin: maestro in their frontmatter. The scan should cover:
CLAUDE.md(root).claude/skills/*/SKILL.md.claude/agents/*.md
For each match, read the file's maestro_version value. Build a list:
[
{ path: "CLAUDE.md", version: "v2026.04.29.1" },
{ path: ".claude/skills/setup/SKILL.md", version: "v2026.04.30.1" },
{ path: ".claude/agents/librarian.md", version: "v2026.04.30.1" },
...
]
If maestro_version is missing in a file that has origin: maestro, treat it as the baseline v2026.04.29.1 (the snapshot version before versioning was introduced).
Phase 4b — Reverse scan: new files from upstream
Walk the mirror for *.md files (excluding .git/) with origin: maestro in their frontmatter. Any such file whose path does not exist in the instance is a new upstream file — the instance-side scan cannot see it, so without this step it would never be delivered.
The reverse scan is glob-based on purpose (the whole mirror, not just the Phase 4 path list): future distributed files may live in paths that don't exist yet in older instances (e.g. a marked howto/ guide).
Build a second list:
new_from_upstream: [
{ path: "howto/08-markdown-discipline.md", version: "v2026.08.01.1" },
...
]
These files join the Phase 6 flow after the diffed ones. A new file the owner declines is simply not copied — it will be re-proposed at the next sync (the owner can keep declining; nothing is recorded to suppress it).
Phase 5 — Show changelog delta
Read <mirror-path>/CHANGELOG.md. The CHANGELOG is ordered most-recent-first, with each entry headed ## vYYYY.MM.DD.N — YYYY-MM-DD.
Compute the lowest instance maestro_version across all scanned files (call it INSTANCE_FLOOR). Show the owner all CHANGELOG entries strictly between INSTANCE_FLOOR and MIRROR_VERSION (inclusive of MIRROR_VERSION, exclusive of INSTANCE_FLOOR).
Format:
🔍 Maestro changelog from your version to upstream:
## v2026.04.30.2 — 2026-04-30
**Theme**: Promote three patterns proven in the Alfred instance into the template.
- (Added) early-morning rule, YAML safety, wikilinks discipline
## v2026.04.30.1 — 2026-04-30
**Theme**: Available apps moves out of CLAUDE.md into preferences.md.
- (Changed) CLAUDE.md, preferences.example.md, add-external-app skill
- (Added) Distribution and modifications section
You are at: v2026.04.29.1 (oldest file in this instance).
Upstream is at: v2026.04.30.2.
This is context before the diff, not a confirmation prompt yet.
Phase 6 — Per-file diff and confirmation
For each file in the scan list, compute the diff between the instance's version and the mirror's version of the same file path.
diff -u <instance-path>/<file> <mirror-path>/<file>
Skip files that are byte-identical (already up to date — common when the instance is mostly aligned).
Frontmatter tools: exemption (per CLAUDE.md → "Distribution and modifications"): when comparing skill or agent files, ignore differences in the tools: frontmatter field. The instance is free to extend that field with its own MCPs/tools, and those changes must survive the sync. Concretely: parse the YAML frontmatter, set tools: of the mirror version to match the instance's tools: before computing the diff, then proceed as usual on body and other frontmatter keys. If only tools: differs, the file is considered identical and skipped.
For each file with a non-empty diff, show:
─── <file> ───
Instance version: <maestro_version of instance file>
Mirror version: <MIRROR_VERSION>
<unified diff, colorized if terminal supports it>
Apply this change?
[a] apply this file
[s] skip this file
[A] apply all remaining files (no further prompts)
[n] abort the whole sync
For files where the owner answers a or A:
- Copy the mirror file over the instance file.
- Update the
maestro_versionin the file's frontmatter toMIRROR_VERSION. The mirror file already has the correct value — copying preserves it. - Append an entry to
private/maestro-sync.log(create the file if it doesn't exist):
2026-04-30T18:42:13Z v2026.04.29.1 → v2026.04.30.2 CLAUDE.md (applied)
2026-04-30T18:42:13Z v2026.04.30.1 → v2026.04.30.2 .claude/skills/setup/SKILL.md (skipped by owner)
If the owner answers n (abort), stop immediately. Files already applied stay applied; files not yet shown are not touched. Log a final entry: 2026-04-30T18:42:13Z ABORTED by owner after <N> files.
New files from the reverse scan (Phase 4b) go through the same prompt, after all diffed files. Since there is no instance version to diff against, show the file's frontmatter (description, maestro_version) and the first ~30 lines of body instead of a diff, plus its total length:
─── NEW: <file> ───
Not present in this instance. Upstream version: <maestro_version>
Description: <frontmatter description>
<first ~30 lines of the file>
[... 145 more lines]
Add this file?
[a] add this file
[s] skip this file
[A] apply all remaining files (no further prompts)
[n] abort the whole sync
On a or A: copy the mirror file to the instance at the same relative path (creating parent directories if needed) and log it with the marker (new):
2026-07-15T18:42:13Z — → v2026.07.15.1 howto/08-markdown-discipline.md (new)
Phase 7 — Final summary
After all files are processed (or the owner picked A), summarize:
✅ Maestro sync complete
Updated: 3 files (CLAUDE.md, .claude/skills/setup/SKILL.md, .claude/agents/librarian.md)
Added: 1 file (howto/08-markdown-discipline.md — new from upstream)
Skipped: 1 file (.claude/skills/logbook/SKILL.md — owner declined)
Identical: 4 files (no change in upstream)
Instance now at: v2026.04.30.2
Log: private/maestro-sync.log
Omit the Added: line when the reverse scan found nothing.
If everything was identical:
✅ Maestro sync — already up to date (v2026.04.30.2)
Self-update
This skill is itself marked origin: maestro, so it will appear in its own scan. When upstream releases a new version of maestro-sync, the skill applies the new version to itself like any other file. The next invocation will use the updated logic.
There's no special handling for self-update beyond this — the standard flow works because each invocation is a single shot from start to finish, not a long-running daemon.
Bootstrap notes
Brand new instance (instance was just created from the template, never synced before):
- Files already carry the
maestro_versiondeclared in their frontmatter at template-clone time. - First run of
maestro-syncfinds the mirror at<MIRROR_VERSION>. If the instance was cloned recently fromorigin/main, files match the mirror exactly → sync reports "already up to date". - If the instance was cloned from an older commit, the diff workflow handles it normally.
Old instance (predates the introduction of origin: maestro frontmatter, ~before v2026.04.30.1):
- Files have no
maestro_version(and possibly noorigin: maestroeither). Two recovery options:- Owner adds the markers manually to the files they recognize as Maestro-distributed (CLAUDE.md, hub skills, base agents), using
maestro_version: v2026.04.29.1as the safe baseline. Then runsmaestro-sync. - Re-clone the instance scaffold separately and reconcile by hand. Heavier, but clean.
- Owner adds the markers manually to the files they recognize as Maestro-distributed (CLAUDE.md, hub skills, base agents), using
The skill itself does not auto-mark files — that would risk misclassifying instance customizations as upstream patterns.
What this skill does NOT do
- Push to upstream. Promotions happen in the primary working tree (
~/Sites/me/maestro/), not from this skill. - Edit files in
private/,apps/, or any file withoutorigin: maestromarker. - Resolve conflicts when the owner has hand-edited a file marked
origin: maestroand upstream also changed it. The diff is shown, the owner decides per file. Hand-editingorigin: maestrofiles is discouraged inCLAUDE.md→ "Distribution and modifications" precisely to avoid this. - Run silently. Every phase that touches state (mirror reset, file copy, log append) reports to the owner.
Memory log
Per the "Announce every write" rule, after the sync, insert one memory entry summarizing the run. Prefer bin/mem when the instance has it:
bin/mem save "Maestro sync: <FROM_VERSION> → <TO_VERSION> (<N> files updated, <M> added)" -t maestro,sync,upstream -d "<list of updated/added files, comma-separated>. Skipped: <list>. Log: private/maestro-sync.log."
Fallback for instances without bin/mem:
sqlite3 private/memories.db "INSERT INTO log (date, title, description, tags, type) VALUES (date('now'), 'Maestro sync: <FROM_VERSION> → <TO_VERSION> (<N> files updated)', '<list of updated files, comma-separated>. Skipped: <list>. Log: private/maestro-sync.log.', 'maestro,sync,upstream', 'memory');"
Announce:
📝 saved: "Maestro sync: <FROM_VERSION> → <TO_VERSION>" [maestro,sync,upstream] (memory)
Failure modes
- Mirror clone fails (no network, no SSH key): stop, report the error, suggest
gh auth statusor checking SSH agent. - Working tree has uncommitted changes and owner picks
pushbut staging is needed: refuse silently togit add -A(would risk staging files the owner didn't intend); ask the owner to stage manually and re-run. - CHANGELOG.md missing or unparseable in the mirror: warn but continue — files are still diffable, just no high-level context.
- A file marked
origin: maestroexists in the instance but not in the mirror (e.g. an old skill that has since been retired upstream): show this in the summary as "orphan: file is no longer in upstream — keep, archive, or delete by hand?". Do not auto-delete.