lockfile-sync
Bir commit'in bir manifest'in dependency'lerini değiştirip lockfile'ını değiştirmediğini modele söyleyen bir Claude Code Mod'u. Modelin çalıştırdığı her git commit sonrasında mod, commit'in lockfile'ını dışarıda bıraktığı manifest'leri commit'in sonucuna ekler. Commit hiçbir zaman durdurulmaz.
Ne yapar
Mod Bash tool'unu hook'lar.
git commitçalıştıran bir komut (git -C <dir> commitde dahil,--dry-runya da--helpdeğil) kontrol edilir.Komut çalışmadan önce repository kökünü session'ın dizininden, commit'ten önceki son
cd'den ve commit'ingit -Cdeğerinden bulur veHEAD'i kaydeder.HEAD'i ilerleten başarılı bir komuttan sonra commit'in eklenen ve değiştirilen dosyalarınıgit show --name-status HEADile listeler ve her manifest'i lockfile'ı ile eşleştirir:Manifest Lockfile package.jsonpackage-lock.json,yarn.lock,pnpm-lock.yaml,bun.lock,bun.lockbcomposer.jsoncomposer.lockCargo.tomlCargo.lockgo.modgo.sumpyproject.tomlpoetry.lock,uv.lock,pdm.lockPipfilePipfile.lockGemfileGemfile.lockpubspec.yamlpubspec.lockmix.exsmix.lockLockfile, manifest'in dizininden repository köküne doğru diskte bulunan ilkidir, yani bir workspace package'i kökteki lockfile ile eşleşir. Diskte lockfile'ı olmayan bir manifest'e dokunulmaz: proje bir tane tutmuyordur.
Commit o lockfile'ı dışarıda bıraktığında mod manifest'in diff'ini okur (
git show --unified=20 HEAD -- <manifest>) ve değişen satırların nerede olduğunu kontrol eder. Yalnız lockfile'ı değiştirebilecek bir değişiklik sayılır:Manifest Sayılır Sayılmaz package.json,composer.jsondependencies,devDependencies,peerDependencies,optionalDependencies,overrides,resolutions,require,require-devve benzerleriscripts,version, diğer key'lerCargo.toml,pyproject.toml,Pipfile[dependencies],[dev-dependencies],[target.*.dependencies],[project],[tool.poetry.dependencies],[packages]ve benzerleri[package],[tool.ruff], diğer table'largo.modrequire,replace,excludesatırları ve bloklarıgo 1.22,moduleGemfilegem,source,gemspec,groupsatırlarıyorumlar pubspec.yamldependencies,dev_dependencies,dependency_overridesdiğer key'ler mix.exsher değişiklik 20 satırlık context dışındaki bir key ya da table sayılır, yani bilinmeyen bir section yine de notu alır.
Model bu notu commit'in sonucundan sonra okur:
lockfile-sync: this commit changes package.json but not package-lock.json · go.mod but not go.sum. Run the package manager's install so the lockfile matches, and commit it.Aynı anda transcript'e bir satır yazılır, böylece modele ne söylendiğini görürsünüz. Bu satır yalnız çiftleri taşır, talimat cümlesi olmadan:
lockfile-sync: this commit changes package.json but not package-lock.json · go.mod but not go.sumNot ve satır ayrı iki kanaldır: model satırı hiç okumaz, siz notu hiç okumazsınız.
sidebar açıkken bu çiftler oraya gider, çift başına bir satır olarak, stream'inde bir entry halinde, ve transcript temiz kalır. Entry, yenileri pane'den itene kadar durur. Sidebar kapalıyken ya da o mod kurulu değilken transcript satırı yukarıdaki gibi yazılır.
Bir bulgu hiçbir zaman hatırlanmış bir cevap değildir. Her ölçüm, sonraki her commit'ten sonra ve guarded bir git komutundan önce git'e yeniden sorar, yani iki yoldan kapanır:
- lockfile yazıldı: sonraki bir commit onu değiştirdi ya da
git status --porcelainworking tree'de değiştiğini gösteriyor; - manifest artık bir lockfile değişikliği istemiyor:
git log -1 -- <lockfile>lockfile'ı en son yazan commit'i adlandırır ve manifest'in o commit'e karşı diff'i hiçbir dependency'ye dokunmaz. Geri alınmış bir değişiklik böyle okunur.
Entry temizlenir ve yeni bir satır hangisi olduğunu söyler:
lockfile-sync: a later change brought the lockfiles along: package-lock.json lockfile-sync: the dependencies match the lockfile again: package.jsonSidebar kapalıyken aynı metin tek bir transcript satırıdır. Model bunun hiçbirini okumaz: bulgu kendi işiyle kapandı, bir not yalnız az önce yaptığını tekrar ederdi.
- lockfile yazıldı: sonraki bir commit onu değiştirdi ya da
Modelin kapatmadığı bir bulgu her ana döngü turunun sonunda yeniden ölçülür ve kalan, bir sonraki prompt'la modele tek bir not olarak ulaşır:
lockfile-sync: 1 lockfile(s) are still behind their manifest: package-lock.json behind package.json. Run the package manager's install so the lockfile is written, or take the dependency change back.Tur başına bir not, prompt başına değil. Bu olmasa bulgu bir kere, commit anında söylenir ve sonra model onu unutmuşken pane'de dururdu. Siz yeni bir şey okumazsınız: pane zaten aynı bulguyu taşıyor.
denymodunda mod ayrıca, bir lockfile geride kaldığı sürecegit commit,git pushvegit mergekomutlarını durdurur. Bir komutu durdurmadan önce iki ölçümü de çalıştırır, yani package manager'ın az önce yazdığı bir lockfile ve geri alınmış bir dependency değişikliği gate'i kendileri açar. Birgit commityalnız kendi dosyaları için cevap verir: mod index'i okur (git diff --cached --name-only) ve index açık manifest'lerin hiçbirini tutmuyorsa commit'in çalışmasına izin verir, size kaçının hâlâ durduğunu söyleyen bir satırla. Birpushve birmergeokunacak index tutmaz, yani orada her çift durur. Kaçış yolu yok; gate'i yalnız kişi/lockfile-sync mode noteile kapatır.notemodu varsayılandır ve hiçbir şeyi durdurmaz.
Bir git hatası bir kere log'lanır ve commit'in sonucu olduğu gibi kalır.
Canlı kontrolde model bir package.json dependency'sini yükseltti, yalnız o dosyayı commit etti ve notu kelimesi kelimesine alıntıladı.
Komut
/lockfile-sync on ya da off, mod ve hâlâ geride olan lockfile'lar
/lockfile-sync on | off varsayılan on
/lockfile-sync mode note yalnız not; varsayılan
/lockfile-sync mode deny bir lockfile geride kalmışken commit, push ve merge de durur
Kurulum
claude plugin marketplace add KilimcininKorOglu/claude-code-mods
claude plugin install lockfile-sync@kilimcininkoroglu-mods
Function hook'lar early access. Flag olmadan hiçbir şey yüklenmez. Flag'i kalıcı yapmak için ~/.claude/settings.json dosyasına ekleyin:
{ "env": { "CLAUDE_CODE_ENABLE_FUNCTION_HOOKS": "1" } }
Kurulumdan sonra
- Claude Code'u yeniden başlatın.
Nereye uzanır
Claude Code 2.1.278 üzerinde claude plugin validate ile doğrulandı:
❯ ./register.ts hooks: session.start, command.run{command=lockfile-sync}, turn.complete, prompt.submit, tool.call{tool=Bash}
❯ ./register.ts calls: $.command.register, $.fs.exists (via lockOnDisk), $.process.run (via git), $.session.cwd (via beforeCommit), $.sidebar.clear (via dropEntry), $.sidebar.set (via toPerson), $.store.get, $.store.set (via runCommand, setMode), $.ui.log (via denyFor, report, toPerson)
Reach L2, process çalıştırır.
1. Okur: Bash komut metnini; repository'de lockfile'ların var olup olmadığını; git üzerinden commit'in dosya listesini ve manifest diff'lerini
2. Çalıştırır: git rev-parse, git show, git status, git log, git diff ve git diff --cached --name-only komutlarını salt okuma olarak argv ile: commit başına dört, lockfile'ı olmayan manifest başına bir, ve her ölçümde açık çift başına iki, turun sonunda da
3. Gönderir: commit'in sonucundan sonra modele bir not, bulgu dururken sonraki prompt'la bir tane daha ve transcript'e bir satır; makineden hiçbir şey çıkmaz
4. Saklar: $.store içinde on/off ayarını ve modu
5. Düşman girdi: dizin komut metninden gelir ve git'e yalnız working directory olarak ulaşır, hiçbir zaman bir shell üzerinden geçmez; manifest path'leri git'e `--` sonrası tek bir argv girdisi olarak ulaşır
Sınırlar
- Mod dosya adlarını ve diff section'larını karşılaştırır. Lockfile'ın içeriğinin manifest ile eşleştiğini kontrol etmez.
- Hiçbir commit'in yazmadığı bir lockfile, manifest'i karşılaştıracak bir şey tutmaz, yani bulgusunu yalnız ilk ölçüm kapatabilir.
- Bir dizinde bir manager'ın iki lockfile'ı (bir
package-lock.jsonyanındaki biryarn.lock) tablodaki ilkiyle eşleşir. git commit'i gizleyen bir script ya da alias üzerinden atılan commit görülmez.cd ~/xgenişletilmez.- Bir merge commit'inin birleşik diff'i okunmaz.
denymodunun kaçış yolu yoktur. Bir bulgu düzeltilemediğinde kişi gate'i/lockfile-sync mode noteile kapatır.- Gate, working tree'deki lockfile'a yapılan her değişikliği düzeltme olarak okur; o değişikliğin ne taşıdığını kontrol etmez.
- Bir
git commit -a, bir-amve--sonrası pathspec taşıyan bir commit index'e göre daraltılmaz, çünkü bunlar index'in henüz tutmadığı dosyaları commit eder. Onlar için her açık çift durur.
Geliştirme
make install # eslint, typescript-eslint, typescript
make lint # complexity limiti 10, üstünde build'i düşürür
make typecheck # /plugin-types ile üretilen .claude/types/ gerekir
make validate
make test # claude plugin test
lockfile-sync
A Claude Code Mod that tells the model when a commit changes the dependencies of a manifest but not its lockfile. After each git commit the model runs, the mod adds the manifests whose lockfile the commit left out to the commit's result. The commit is never stopped.
What it does
The mod hooks the Bash tool. A command that runs
git commit(alsogit -C <dir> commit, not--dry-runor--help) is checked.Before the command runs, it finds the repository root from the session's directory, the last
cdbefore the commit and the commit'sgit -C, and recordsHEAD.After a successful command that moved
HEAD, it lists the commit's added and modified files withgit show --name-status HEAD, and pairs each manifest with its lockfile:Manifest Lockfile package.jsonpackage-lock.json,yarn.lock,pnpm-lock.yaml,bun.lock,bun.lockbcomposer.jsoncomposer.lockCargo.tomlCargo.lockgo.modgo.sumpyproject.tomlpoetry.lock,uv.lock,pdm.lockPipfilePipfile.lockGemfileGemfile.lockpubspec.yamlpubspec.lockmix.exsmix.lockThe lockfile is the first one on disk from the manifest's directory up to the repository root, so a workspace package pairs with the root lockfile. A manifest without a lockfile on disk is left alone: the project does not keep one.
When the commit leaves that lockfile out, the mod reads the manifest's diff (
git show --unified=20 HEAD -- <manifest>) and checks where the changed lines sit. Only a change that can change the lockfile counts:Manifest Counts Does not count package.json,composer.jsondependencies,devDependencies,peerDependencies,optionalDependencies,overrides,resolutions,require,require-devand the likescripts,version, other keysCargo.toml,pyproject.toml,Pipfile[dependencies],[dev-dependencies],[target.*.dependencies],[project],[tool.poetry.dependencies],[packages]and the like[package],[tool.ruff], other tablesgo.modrequire,replace,excludelines and blocksgo 1.22,moduleGemfilegem,source,gemspec,grouplinescomments pubspec.yamldependencies,dev_dependencies,dependency_overridesother keys mix.exsevery change A key or table outside the 20 lines of context counts, so an unknown section still gets the note.
The model reads this note after the commit's result:
lockfile-sync: this commit changes package.json but not package-lock.json · go.mod but not go.sum. Run the package manager's install so the lockfile matches, and commit it.The same moment writes one line to the transcript, so you see what the model was told. The line holds the pairs alone, without the instruction:
lockfile-sync: this commit changes package.json but not package-lock.json · go.mod but not go.sumThe note and the line are separate channels: the model never reads the line, and you never read the note.
While the sidebar is open, those pairs go there instead, one line per pair, as an entry in its stream, and the transcript stays clean. The entry stays until newer ones push it off the pane. With the sidebar closed, or without that mod installed, the transcript line is written as above.
A finding is never a remembered answer. Each measure, after every later commit and before a guarded git command, asks git again, so it closes two ways:
- the lockfile was written: a later commit changed it, or
git status --porcelainshows it changed in the working tree; - the manifest asks for no lockfile change any more:
git log -1 -- <lockfile>names the commit that last wrote the lockfile, and the manifest's diff against that commit touches no dependency. A change that was reverted reads this way.
The entry is cleared and a new one says which of the two it was:
lockfile-sync: a later change brought the lockfiles along: package-lock.json lockfile-sync: the dependencies match the lockfile again: package.jsonWith the sidebar closed the same text is one transcript line. The model reads nothing of this: the finding closed by its own work, so a note would only repeat what it just did.
- the lockfile was written: a later commit changed it, or
A finding the model did not close is measured again at the end of each main-loop turn, and what is left reaches the model as one note with its next prompt:
lockfile-sync: 1 lockfile(s) are still behind their manifest: package-lock.json behind package.json. Run the package manager's install so the lockfile is written, or take the dependency change back.One note per turn, not one per prompt. Without this the finding would be said once, at the commit, and then stand in the pane while the model forgot it. You read nothing new: the pane already carries the same finding.
In
denymode the mod also stopsgit commit,git pushandgit mergewhile a lockfile is behind. Before it stops one it runs both measures, so a lockfile the package manager just wrote, and a dependency change that was taken back, each open the gate themselves. Agit commitanswers for its own files alone: the mod reads the index (git diff --cached --name-only) and lets the commit run when it holds none of the open manifests, with one line to you naming how many still stand. Apushand amergehold no index to read, so every pair stands there. There is no bypass; only the person turns the gate off with/lockfile-sync mode note.notemode is the default and stops nothing.
A git error is logged once, and the commit's result stays as it was.
In the live check the model raised a package.json dependency, committed only that file, and quoted the note word for word.
Command
/lockfile-sync on or off, the mode, and the lockfiles still behind
/lockfile-sync on | off on by default
/lockfile-sync mode note note only; the default
/lockfile-sync mode deny a commit, a push and a merge also stop while a lockfile is behind
Install
claude plugin marketplace add KilimcininKorOglu/claude-code-mods
claude plugin install lockfile-sync@kilimcininkoroglu-mods
Function hooks are early access. Nothing loads without the flag. To keep it on, add this to ~/.claude/settings.json:
{ "env": { "CLAUDE_CODE_ENABLE_FUNCTION_HOOKS": "1" } }
After installing
- Restart Claude Code.
What it can reach
Validated with claude plugin validate on Claude Code 2.1.278:
❯ ./register.ts hooks: session.start, command.run{command=lockfile-sync}, turn.complete, prompt.submit, tool.call{tool=Bash}
❯ ./register.ts calls: $.command.register, $.fs.exists (via lockOnDisk), $.process.run (via git), $.session.cwd (via beforeCommit), $.sidebar.clear (via dropEntry), $.sidebar.set (via toPerson), $.store.get, $.store.set (via runCommand, setMode), $.ui.log (via denyFor, report, toPerson)
Reach L2, runs processes.
1. Reads: the Bash command text; whether lockfiles exist in the repository; through git, the commit's file list and manifest diffs
2. Runs: git rev-parse, git show, git status, git log, git diff and git diff --cached --name-only, read-only, by argv: four per commit, one per manifest without its lockfile, and two per open pair at each measure, also at the turn's end
3. Sends: a note to the model after the commit's result, one more with the next prompt while a finding stands, and one line to the transcript; nothing leaves the machine
4. Persists: in $.store, the on/off setting and the mode
5. Hostile input: the directory comes from the command text and reaches git only as the working directory, never through a shell; manifest paths reach git as one argv entry after --
Limits
- The mod compares file names and diff sections. It does not check that the lockfile's content matches the manifest.
- A lockfile no commit ever wrote has nothing to compare the manifest against, so only the first measure can close its finding.
- Two lockfiles of one manager in one directory (a
yarn.lockbeside apackage-lock.json) pair with the first in the table. - A commit through a script or an alias that hides
git commitis not seen.cd ~/xis not expanded. - A merge commit's combined diff is not read.
- The
denymode has no bypass. When a finding cannot be fixed, the person turns the gate off with/lockfile-sync mode note. - The gate reads any change to the lockfile in the working tree as the fix; it does not check what that change holds.
- A
git commit -a, a-amand a commit with a pathspec after--are not narrowed to the index, because they commit files the index does not hold yet. Every open pair stands for those.
Development
make install # eslint, typescript-eslint, typescript
make lint # complexity limit 10, fails the build above it
make typecheck # needs .claude/types/ from /plugin-types
make validate
make test # claude plugin test