storage-guard
Bir edit'in tarayıcı verisini cookie yerine localStorage ya da sessionStorage ile sakladığını modele söyleyen bir Claude Code Mod'u. Not Edit'in sonucuyla gelir ve her satırı adlandırır, yani model veriyi aynı turda bir cookie'ye taşır. Varsayılan olarak hiçbir şey durmaz; deny modunda bir dosya storage'ı hâlâ kullanırken commit, push ve merge durur.
Ne yapar
Mod Edit ve Write tool'larını hook'lar. Bir JavaScript, TypeScript ya da component dosyasında (
.js,.jsx,.ts,.tsx,.mjs,.cjs,.mts,.cts,.vue,.svelte,.astro,.html) başarılı bir çağrıdan sonra edit'in eklediği satırları okur:new_stringiçinde olupold_stringiçinde olmayanları ya da bir Write'ın her satırını. Test dosyaları da okunur.Bir satır,
localStorageya dasessionStorage'ı kod olarak kullanıyorsa sayılır:Sayılır Sayılmaz localStorage.setItem('token', token)// localStorage.setItem(...)ve<!-- ... -->yorum satırlarıwindow.sessionStorage.getItem(key)save(token) // not localStorage, koddan sonraki bir yorumwindow['localStorage']"we never use localStorage", bir string içindeki kelimeconst { sessionStorage } = window`localStorage is off`, bir template literal'in metnindeki kelime`saved: ${localStorage.getItem(key)}`,${...}kısmımyLocalStorage,indexedDB,document.cookieModel bu notu Edit'in sonucundan sonra okur:
storage-guard: this edit stores data in the browser with localStorage or sessionStorage: src/auth.ts:12. Store it in a cookie instead (document.cookie, or the server's Set-Cookie).Satır numarası edit sonrası dosyadan gelir; bir Write kendi içeriğinden numaralandırılır. En fazla 8 yer adlandırılır, kalanı sayılır. Dosya okunamadığında path satırsız durur ve hata bir kere log'lanır. Dosya session'ın başladığı dizinin içindeyse path o dizine göre yazılır. O dizin session'ın başlangıcında bir kere okunur, çünkü bir Bash
cdsession'ın kendi dizinini taşır.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 yerleri taşır, talimat cümlesi olmadan:
storage-guard: browser storage instead of a cookie: src/auth.ts:12Not ve satır ayrı iki kanaldır: model satırı hiç okumaz, siz notu hiç okumazsınız.
sidebar açıkken bu yerler oraya gider, her biri bir satır olarak, stream'inde kırmızı 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 dosya hakkında bir iddiadır, hiçbir zaman hatırlanmış bir cevap değildir. Sonraki her Edit ya da Write'tan sonra mod her açık dosyayı yeniden okur ve her ölçüm bulguyu dosyanın şu an tuttuğu hâliyle değiştirir: bildirilen satırlardan storage'ı hâlâ kullananlar, güncel satır numaralarıyla. Gitmiş ya da yoruma çevrilmiş bir satır artık sayılmaz. Kısmi bir düzeltme bulguyu kalan yerlerle açık tutar. Satırlarının hepsi gitmiş bir dosya kapanır, artık var olmayan bir dosya da; var olan ama okunamayan bir dosya bulgusunu açık tutar, çünkü okunamayan bir dosya hiçbir şeyi kanıtlamaz. Kapanış entry'si yeşildir:
storage-guard: the browser storage is gone from src/auth.ts: src/auth.ts:12Sidebar kapalıyken aynı metin tek bir transcript satırıdır. Model bunun hiçbirini okumaz: veriyi kendisi taşıdı.
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:
storage-guard: 1 place(s) still store data in localStorage or sessionStorage: src/auth.ts:12. Move the data to a cookie, or take the lines out.Tur başına bir not, prompt başına değil. Bu olmasa bulgu bir kere, edit 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 dosya storage'ı hâlâ kullanırkengit commit,git pushvegit mergekomutlarını durdurur. Bir komutu durdurmadan önce her açık dosyayı yeniden okur, yani modelin düzelttiği bir dosya gate'i kendisi açar. Birgit commityalnız kendi dosyaları için cevap verir: mod index'i okur (git diff --cached --name-only) ve index açık dosyaların 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 bulgu durur. Kaçış yolu yok; gate'i yalnız kişi/storage-guard mode noteile kapatır.notemodu varsayılandır ve hiçbir şeyi durdurmaz.
Canlı kontrolde model tek bir Edit ile bir dosyaya localStorage.setItem('token', token) ekledi ve app.ts:2 adlandıran notu kelimesi kelimesine alıntıladı. Sonraki prompt'ta turn sonu notunu alıntıladı, satırı document.cookie ile yeniden yazdı, ve o Edit'in ardından the browser storage is gone from app.ts: app.ts:2 kapanış satırı geldi.
Komut
/storage-guard on ya da off, mod ve storage'ı hâlâ kullanan dosyalar
/storage-guard on | off varsayılan on
/storage-guard mode note yalnız not; varsayılan
/storage-guard mode deny bir dosya storage'ı kullanırken commit, push ve merge de durur
Kurulum
claude plugin marketplace add KilimcininKorOglu/claude-code-mods
claude plugin install storage-guard@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.280 üzerinde claude plugin validate ile doğrulandı:
❯ ./register.ts hooks: session.start, command.run{command=storage-guard}, turn.complete, prompt.submit, tool.call{tool=Bash}, tool.call{tool=Edit}, tool.call{tool=Write}
❯ ./register.ts calls: $.command.register, $.fs.exists (via isGone), $.fs.read (via fileText), $.process.run (via stagedPaths), $.session.cwd, $.sidebar.clear (via dropEntry), $.sidebar.set (via toPerson), $.store.get, $.store.set (via runCommand, setMode), $.ui.log (via fileText, gate, toPerson)
Reach L2, index'i okumak için git çalıştırır.
1. Okur: her Edit ve Write çağrısının metnini; Bash komut metnini; storage ekleyen bir Edit sonrası edit edilen dosyayı, satır numaraları için, ve bulgu dururken her açık dosyayı yeniden
2. Çalıştırır: deny modunda bir commit'te git rev-parse --show-toplevel ve git diff --cached --name-only, commit'in hangi dosyaları tuttuğunu okumak için
3. Gönderir: storage ekleyen bir edit'ten 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: edit edilen metin yalnız regular expression ile eşleştirilir ve file:line olarak yazılır, hiçbir zaman çalıştırılmaz
Sınırlar
- Kontrol satır satır regular expression ile yapılır, bir parser ile değil. Birkaç satıra yayılan bir string ya da yorum satır satır okunur, yani içindeki bir kullanım sayılabilir.
- Tırnak içindeki bir HTML ya da component attribute'u (
onclick="localStorage.clear()") string olarak okunur ve sayılmaz. - Başka bir adla ulaşılan bir storage (
const s = window[name], bir wrapper kütüphane,indexedDB) görülmez. - Bash üzerinden yapılan bir edit kontrol edilmez.
- Bir bulgu, bildirilen satırlar dosyadan gittiğinde ya da yoruma çevrildiğinde kapanır. Başka bir dosyaya taşınan bir satır bulguyu açık tutar.
denymodunun kaçış yolu yoktur. Bir bulgu düzeltilemediğinde kişi gate'i/storage-guard mode noteile kapatır.- Gate komut metnini okur.
git commit'i gizleyen bir script ya da alias üzerinden atılan commit durdurulmaz. - 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 bulgu durur. - Index, komut çalışmadan önce okunur. Okuma ile çalışma arasında dosyaları değişen bir commit, okuma anındaki index'e göre ölçülür.
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
storage-guard
A Claude Code Mod that tells the model when an edit stores browser data with localStorage or sessionStorage instead of a cookie. The note comes with the Edit's result and names each line, so the model moves the data to a cookie in the same turn. By default nothing is stopped; in deny mode a commit, a push and a merge stop while a file still uses the storage.
What it does
The mod hooks the Edit and Write tools. After a successful call on a JavaScript, TypeScript or component file (
.js,.jsx,.ts,.tsx,.mjs,.cjs,.mts,.cts,.vue,.svelte,.astro,.html), it reads the lines the edit added: those ofnew_stringthatold_stringdoes not have, or every line of a Write. Test files are read too.A line counts when it uses
localStorageorsessionStorageas code:Counts Does not count localStorage.setItem('token', token)// localStorage.setItem(...)and<!-- ... -->comment lineswindow.sessionStorage.getItem(key)save(token) // not localStorage, a comment after the codewindow['localStorage']"we never use localStorage", the word inside a stringconst { sessionStorage } = window`localStorage is off`, the word in a template literal's text`saved: ${localStorage.getItem(key)}`, the${...}partmyLocalStorage,indexedDB,document.cookieThe model reads this note after the Edit's result:
storage-guard: this edit stores data in the browser with localStorage or sessionStorage: src/auth.ts:12. Store it in a cookie instead (document.cookie, or the server's Set-Cookie).The line number comes from the file after the edit; a Write is numbered from its own content. At most 8 places are named, the rest counted. When the file cannot be read, the path stands without a line and the error is logged once. The path is written against the directory the session started in when the file is inside it. That directory is read once at the session's start, because a Bash
cdmoves the session's own directory.The same moment writes one line to the transcript, so you see what the model was told. The line holds the places alone, without the instruction:
storage-guard: browser storage instead of a cookie: src/auth.ts:12The 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 places go there instead, one line each, as a red 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 a claim about the file, never a remembered answer. After each later Edit or Write the mod reads each open file again, and every measure replaces the finding with what the file holds now: the reported lines that still use the storage, at their current line numbers. A line that is gone or commented out no longer counts. A partial fix keeps the finding open with the places left. A file whose lines are all gone closes, and so does a file that is no longer there; a file that is there and cannot be read keeps its finding, because an unread file proves nothing. The closing entry is green:
storage-guard: the browser storage is gone from src/auth.ts: src/auth.ts:12With the sidebar closed the same text is one transcript line. The model reads nothing of this: it moved the data itself.
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:
storage-guard: 1 place(s) still store data in localStorage or sessionStorage: src/auth.ts:12. Move the data to a cookie, or take the lines out.One note per turn, not one per prompt. Without this the finding would be said once, at the edit, 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 file still uses the storage. Before it stops one it reads each open file again, so a file the model fixed opens the gate itself. 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 files, with one line to you naming how many still stand. Apushand amergehold no index to read, so every finding stands there. There is no bypass; only the person turns the gate off with/storage-guard mode note.notemode is the default and stops nothing.
In the live check the model added localStorage.setItem('token', token) to a file with one Edit and quoted the note naming app.ts:2 word for word. At the next prompt it quoted the turn-end note, rewrote the line with document.cookie, and the closing line the browser storage is gone from app.ts: app.ts:2 followed that Edit.
Command
/storage-guard on or off, the mode, and the files still using the storage
/storage-guard on | off on by default
/storage-guard mode note note only; the default
/storage-guard mode deny a commit, a push and a merge also stop while a file uses the storage
Install
claude plugin marketplace add KilimcininKorOglu/claude-code-mods
claude plugin install storage-guard@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.280:
❯ ./register.ts hooks: session.start, command.run{command=storage-guard}, turn.complete, prompt.submit, tool.call{tool=Bash}, tool.call{tool=Edit}, tool.call{tool=Write}
❯ ./register.ts calls: $.command.register, $.fs.exists (via isGone), $.fs.read (via fileText), $.process.run (via stagedPaths), $.session.cwd, $.sidebar.clear (via dropEntry), $.sidebar.set (via toPerson), $.store.get, $.store.set (via runCommand, setMode), $.ui.log (via fileText, gate, toPerson)
Reach L2, it runs git to read the index.
1. Reads: the text of each Edit and Write call; the Bash command text; the edited file after an Edit that adds the storage, for the line numbers, and each open file again while a finding stands
2. Runs: git rev-parse --show-toplevel and git diff --cached --name-only, at a commit in deny mode, to read which files the commit holds
3. Sends: a note to the model after an edit that adds the storage, 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 edited text is only matched by regular expressions and printed as file:line, never run
Limits
- The check is line by line with regular expressions, not a parser. A string or a comment that spans several lines is read line by line, so a use inside it can count.
- An HTML or component attribute in quotes (
onclick="localStorage.clear()") is read as a string and does not count. - A storage reached through another name (
const s = window[name], a wrapper library,indexedDB) is not seen. - An edit through Bash is not checked.
- A finding closes when the reported lines are gone from the file or commented out. A line moved to another file keeps it open.
- The
denymode has no bypass. When a finding cannot be fixed, the person turns the gate off with/storage-guard mode note. - The gate reads the command text. A commit through a script or an alias that hides
git commitis not stopped. - 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 finding stands for those. - The index is read before the command runs. A commit whose files change between the read and the run is measured against what the index held at the read.
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