Lockfile, status and update
atril.lock.json sits in the store root (.atril/atril.lock.json, or under whatever --root or ATRIL_ROOT names). It records, per kind:name, where each installed artifact came from and a hash of its content:
{ "version": 1, "artifacts": { "skill:lint": { "origin": "catalog", "catalog": "catalogs/team.json", "source": "owner/repo/skills", "commit": "<40-character sha>", "hash": "sha256:<64 hex characters>" } }}originiscatalog(installed from a catalog, with itscatalog, and for git sources thesourceand pinnedcommit),import(moved in withatril import, withfrom) orlocal(installed from a path).hashcovers the artifact’s content. For a command or agent, enrichment (theatril:block) is not content; for a skill, every other file in its folder is.- It holds no timestamp and no local path, and it is written deterministically, so commit it: the team shares one record of what is installed and from where, and diffs stay small.
atril install,atril importandatril updatewrite it;atril removedrops the entry. Reading never writes: a missing lockfile is an empty one, and a malformed entry fails loudly with the file and entry named.
atril status and atril update read it.
Status
Section titled “Status”atril status # one line per artifact, then a summaryatril status --check # for CI: exit 1 on edited or missingatril status --json # the rows as a JSON arrayatril status compares the store with the lockfile. Each artifact is in one state, the first that holds:
| State | Meaning |
|---|---|
missing |
in the lockfile, gone from disk |
edited |
its content differs from the recorded hash |
uncurated |
not in the lockfile, or its origin is no longer a configured catalog (local and import are never curated) |
ok |
none of the above |
Lines are state, kind:name and the origin, separated by tabs, followed by a summary such as 3 ok, 1 edited, 0 missing, 1 uncurated; an empty store prints nothing installed. With --json each row is { kind, name, state, origin }. --check exits 1 only for edited or missing, so uncurated artifacts do not fail CI. --root <dir> picks the store. Status is read-only.
Update
Section titled “Update”atril update # ask, per outdated artifact, whether to update itatril update skill:lint # only the named artifactsatril update --yes # apply every outdated one, skip edited onesatril update --yes --discard-editsatril update --jsonatril update brings the artifacts installed from a catalog up to what that catalog offers now. (atril catalog update is the one that moves a git source’s pinned commit; atril update then moves your copies.) It prints each pending artifact with its diff, then:
- With a terminal, it asks
update <kind:name>? [y/N]for each one. For an artifact you edited locally, the question warns that the edit will be lost. --yesapplies every outdated artifact and skips the edited ones, unless--discard-editsis also given, which overwrites them.- Without a terminal and without
--yes, it is a dry run: it prints the diffs, applies nothing and exits 0. --jsonprints{ updates, applied }: every artifact’s state (current,outdated,editedorunavailable) and the refs that were updated.
An update replaces the copy atomically: if copying fails, the previous version stays. A named kind:name with no catalog origin in the lockfile fails before anything is applied. atril init reports a recommended artifact that changed in this version and tells you to run atril update.
Karajan Code™ · AGPL-3.0