How sync state works
What the CLI remembers between runs — the .airskills-sync file and why it matters
You usually never think about this — the CLI just tracks what it needs and quietly does the right thing. This page is for when something looks odd and you want to know why.
What the CLI remembers
At the root of your skills directory there's a .airskills-sync file.
It stores, for each skill on this machine:
- The
skill_id(the skill's permanent UUID — never changes) - The last content the CLI saw (as a hash), so it can tell if you edited the skill since the last push
- The
head_commit_id(the SHA of the newest server commit it saw for this skill), used for conflict detection - The
resolved_hash(sourced skills only) — the upstream content hash the user last reviewed against. See customisation.
You don't edit this file by hand. The CLI keeps it correct on every push, pull, sync, and resolve.
Why it matters
The sync markers are what let the CLI answer three questions quickly, without asking the server anything:
- Has this skill changed locally since I last synced it? (yes if current files don't match the stored hash)
- Has the server moved on since I last saw it? (yes if the server's
head_commit_iddoesn't match the stored one) - Did both of us edit it? (both answers yes = conflict)
That third case is the one to care about. If both sides changed, the CLI does not silently pick a winner. It saves the remote version into a temporary folder and points your agent at both, so the agent can read through them and explain what changed in plain language. You decide what to keep — see incorporation.
The state of every skill
Each skill on your machine is in one of these observable states. The
classifier underneath produces this taxonomy; airskills list and
airskills doctor both surface it.
| State | What it means | Next step |
|---|---|---|
| synced | Local matches remote, marker tracks. | Nothing — you're done. |
| modified | Local has diverged from the marker. For owned skills this means you have unpublished changes. For sourced skills it means you've customised it — see customisation. | Owned: airskills push. Sourced: see below. |
modified, pending review (modified*) | Sourced skill, local has been customised, AND the original has moved since you last resolved. Something new to look at. | Review and airskills resolve (with or without edits), or airskills add <owner>/<skill> --force to drop your customised copy. |
| untracked | Local skill directory exists, but airskills has no record of installing it. Common when a skill arrived via git rather than airskills add. | airskills sync will either silently link it (if bytes match the server's copy) or surface a conflict for you to resolve. |
| linked | Transient state during a sync — local matched the server's bytes exactly, so airskills claimed it without downloading. Goes back to synced on the next sync. | Nothing. |
| — (not local) | Skill exists on the server, not installed here. | airskills add or airskills sync. |
The two states that didn't exist before this design — untracked and the pending/resolved split inside modified — are what make customisation observable. A sourced skill is no longer just "modified"; it's either "you've reviewed and chose your customised copy" or "there's something new in the original you haven't seen". The CLI can be quiet about the first and loud about the second.
The three commands that read or write sync state
airskills pushuses the stored hash to pick which skills actually changed, and sends the storedhead_commit_idasX-Expected-Commitso the server can detect a concurrent push.airskills pullfetches the latesthead_commit_idfor each tracked skill and downloads any that moved. Because the CLI fetches byskill_id, a rename on the owner's side is picked up transparently — the new name comes back as metadata, the skill keeps flowing.airskills statuscompares on-disk content against the markers without any network call. Useful when you just want to know "did I change anything since last time?" on the plane.
If the sync file gets lost
If you delete .airskills-sync or move to a fresh machine, nothing
breaks — the CLI treats every skill as new and re-syncs on the next
push/pull, picking up the existing server-side skill by matching
skill_ids back to local directories. Conflict detection is weaker
in this window (no local hash to compare), but content is never lost;
the remote always lands in a temporary folder for you to read
through and incorporate.
Deep dive: the marker schema
For curious developers — skip if you just want to use it.
.airskills-sync is a JSON file with a Skills map keyed on local
directory name. Each entry looks like:
{
"SkillID": "7a3f...",
"Version": "1.2.5",
"ContentHash": "sha256:...",
"ResolvedHash": "sha256:...",
"Tool": "claude-code",
"Source": { "Owner": "chrismdp", "Slug": "code-review" },
"Deleted": false,
"MovedTo": ""
}
SkillIDis the stable identity. Pull resolves by this, not by name, which is what makes owner-side rename consumer-transparent.Sourceis only populated for skills installed viaairskills add(sourced from another user's namespace). It keeps the originalOwner/Slugfor display and anonymous-pull resolution.ResolvedHashis the last upstream content hash you reviewed against viaairskills resolve. Only meaningful for sourced skills (where there's an "upstream" distinct from yourself). The classifier compares this to the current upstream hash to decide betweenmodifiedandmodified-pending.Deleted/MovedTotrack tombstone state afterrmor a server-side transfer.
The CLI writes this atomically (temp file + rename) to avoid leaving a half-written marker if interrupted mid-sync.
Was this page helpful?