L2 v0.8.0 source

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

  1. Mod Bash tool'unu hook'lar. git commit çalıştıran bir komut (git -C <dir> commit de dahil, --dry-run ya da --help değil) kontrol edilir.

  2. Komut çalışmadan önce repository kökünü session'ın dizininden, commit'ten önceki son cd'den ve commit'in git -C değerinden bulur ve HEAD'i kaydeder.

  3. HEAD'i ilerleten başarılı bir komuttan sonra commit'in eklenen ve değiştirilen dosyalarını git show --name-status HEAD ile listeler ve her manifest'i lockfile'ı ile eşleştirir:

    Manifest Lockfile
    package.json package-lock.json, yarn.lock, pnpm-lock.yaml, bun.lock, bun.lockb
    composer.json composer.lock
    Cargo.toml Cargo.lock
    go.mod go.sum
    pyproject.toml poetry.lock, uv.lock, pdm.lock
    Pipfile Pipfile.lock
    Gemfile Gemfile.lock
    pubspec.yaml pubspec.lock
    mix.exs mix.lock

    Lockfile, 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.

  4. 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.json dependencies, devDependencies, peerDependencies, optionalDependencies, overrides, resolutions, require, require-dev ve benzerleri scripts, version, diğer key'ler
    Cargo.toml, pyproject.toml, Pipfile [dependencies], [dev-dependencies], [target.*.dependencies], [project], [tool.poetry.dependencies], [packages] ve benzerleri [package], [tool.ruff], diğer table'lar
    go.mod require, replace, exclude satırları ve blokları go 1.22, module
    Gemfile gem, source, gemspec, group satırları yorumlar
    pubspec.yaml dependencies, dev_dependencies, dependency_overrides diğer key'ler
    mix.exs her 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.

  5. 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.
    
  6. 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.sum
    

    Not ve satır ayrı iki kanaldır: model satırı hiç okumaz, siz notu hiç okumazsınız.

  7. 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.

  8. 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 --porcelain working 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.json
    

    Sidebar 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.

  9. 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.

  10. deny modunda mod ayrıca, bir lockfile geride kaldığı sürece git commit, git push ve git merge komutları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. Bir git commit yalnı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. Bir push ve bir merge okunacak index tutmaz, yani orada her çift durur. Kaçış yolu yok; gate'i yalnız kişi /lockfile-sync mode note ile kapatır. note modu 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

  1. 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.json yanındaki bir yarn.lock) tablodaki ilkiyle eşleşir.
  • git commit'i gizleyen bir script ya da alias üzerinden atılan commit görülmez. cd ~/x genişletilmez.
  • Bir merge commit'inin birleşik diff'i okunmaz.
  • deny modunun kaçış yolu yoktur. Bir bulgu düzeltilemediğinde kişi gate'i /lockfile-sync mode note ile 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 -am ve -- 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

  1. The mod hooks the Bash tool. A command that runs git commit (also git -C <dir> commit, not --dry-run or --help) is checked.

  2. Before the command runs, it finds the repository root from the session's directory, the last cd before the commit and the commit's git -C, and records HEAD.

  3. After a successful command that moved HEAD, it lists the commit's added and modified files with git show --name-status HEAD, and pairs each manifest with its lockfile:

    Manifest Lockfile
    package.json package-lock.json, yarn.lock, pnpm-lock.yaml, bun.lock, bun.lockb
    composer.json composer.lock
    Cargo.toml Cargo.lock
    go.mod go.sum
    pyproject.toml poetry.lock, uv.lock, pdm.lock
    Pipfile Pipfile.lock
    Gemfile Gemfile.lock
    pubspec.yaml pubspec.lock
    mix.exs mix.lock

    The 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.

  4. 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.json dependencies, devDependencies, peerDependencies, optionalDependencies, overrides, resolutions, require, require-dev and the like scripts, version, other keys
    Cargo.toml, pyproject.toml, Pipfile [dependencies], [dev-dependencies], [target.*.dependencies], [project], [tool.poetry.dependencies], [packages] and the like [package], [tool.ruff], other tables
    go.mod require, replace, exclude lines and blocks go 1.22, module
    Gemfile gem, source, gemspec, group lines comments
    pubspec.yaml dependencies, dev_dependencies, dependency_overrides other keys
    mix.exs every change

    A key or table outside the 20 lines of context counts, so an unknown section still gets the note.

  5. 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.
    
  6. 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.sum
    

    The note and the line are separate channels: the model never reads the line, and you never read the note.

  7. 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.

  8. 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 --porcelain shows 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.json
    

    With 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.

  9. 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.

  10. In deny mode the mod also stops git commit, git push and git merge while 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. A git commit answers 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. A push and a merge hold 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. note mode 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

  1. 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.lock beside a package-lock.json) pair with the first in the table.
  • A commit through a script or an alias that hides git commit is not seen. cd ~/x is not expanded.
  • A merge commit's combined diff is not read.
  • The deny mode 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 -am and 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