gemini-compact
Compaction'ı Claude'dan Gemini'ye taşıyan bir Claude Code Mod'u. İki modu vardır:
- summary (varsayılan): Gemini, en yeni 6 mesajdan öncesini özetler ve o mesajlar özetin ardından kelimesi kelimesine kalır. Claude hiçbir özet yazmaz; engine'in yerleşik özeti yalnız Gemini başarısız olduğunda çalışır.
- prune: Gemini, eski her tool çağrısı için, çağrının ve çıktısının kalıp kalmayacağını, çıktısı kısaltılarak mı kalacağını ya da gideceğini söyler. Her kullanıcı ve assistant mesajı kelimesi kelimesine kalır. Hiçbir şey özetlenmez.
Prune fikri, Tamara Tran'ın TypeSafe Jev'e soran fast-jev-compaction mod'unu izler (tamaratran/fast-jev-compaction). Kod yenidir ve Gemini'ye sorar.
Hangi mod
Yerleşik compaction tek bir Claude isteğidir: tüm context'i okur ve bir özet yazar, ve o istek sizin Claude kullanımınıza sayılır. İki mod da o isteğin yerine bir Gemini isteği koyar.
Compaction'dan sonra her Claude isteği geriye kalanı okur. summary modunda bu, özet ve en yeni mesajlardır; yerleşik özetin bıraktığına yakındır. prune modunda ise konuşmanın her mesajı eksi atılan tool çıktısıdır; bu daha büyüktür, yani sonraki her istek daha fazlasını okur. Boyutlar uzun bir session'da ölçülmedi.
En çok Claude kullanımını korumak için summary modunu kullanın. Her mesajın tam ifadesi boyuttan önemliyse prune modunu kullanın.
Summary modu
/compactanında, engine'in kendi compaction'ında ve context eşiğin üstündeyken biten bir turn'den sonra,session.compacthook'u konuşmayı alır.- En yeni 6 mesaj kalır. Kesim noktası geriye, bir assistant mesajına kayar; böylece bir tool sonucu hiçbir zaman çağrısı olmadan kalmaz ve korunan kısım özetten sonra bir assistant mesajıyla açılır.
- Tek bir
generateContentisteği kesimden öncesini Gemini'ye gönderir: her mesajı ve her çağrıyı input'u ile tam output'u ile birlikte. 2.000.000 karakterin üstünde önce en uzun çıktılar baş ve son kısmına kısaltılır. - Gemini dokuz bölümlü düz metin bir özet yazar: istek ve niyet, teknik kavramlar, dosyalar ve kod, hatalar ve düzeltmeler, problem çözme, her kullanıcı mesajı kelimesi kelimesine, bekleyen işler, şu anki iş ve sonraki adım.
/compactsonrasına yazdığınız metin ona iletilir. - Konuşma, tek bir kullanıcı mesajına (bir not, sonra özet) ve onu izleyen korunan mesajlara dönüşür; bunlar engine'in kendi mesajları olarak geri döner.
- Key yoksa, Gemini başarısız olursa, özet 200 karakterin altındaysa ya da output limitinde (32.768 token) kesilmişse, veya sonuç konuşmadan küçük değilse yerleşik özet çalışır ve bir satır sebebini söyler.
İki modda da isteği gemini-core kurar: gemini-compact için tuttuğu key, model ve thinking seviyesi ile; cevabı da o okur. HTTP 503 ("high demand") sonrasında mod 1 sn, 2 sn ve 3 sn sonra tekrar sorar, en fazla dört kere, ve 60 saniye geçtikten sonra yeni deneme başlatmaz.
2.1.277 üzerindeki canlı testte /compact, gemini-3.5-flash-lite ile 2,6 saniye sürdü, Claude hiç compaction isteği göndermedi ve sonrasında model yalnız özetlenen kısımda geçen bir kelimeyi ve bir dosyayı adlandırdı.
Prune modu
- Aynı üç tetikleyici
session.compacthook'una ulaşır. - İlk mesajdaki ve en yeni 6 mesajdaki tool çağrıları bütün olarak korunur. Diğer her çağrı bir id alır (
c1,c2, ...). - Tek bir
generateContentisteği konuşmayı Gemini'ye gönderir: her mesajı ve her çağrıyı input'u ile tam output'u ile birlikte. 400.000 karakterin üstünde önce en uzun çıktılar baş ve son kısmına kısaltılır. Bir response schema, id başına tam olarak bir cevaba izin verir:keep,truncateya dadrop. - Mod cevabı kontrol eder (her id tam bir kere, başka id yok) ve konuşmayı yeniden kurar:
keep: çağrı ve çıktısı kalır.truncate: çağrı kalır, çıktı ilk 300 karakterini ve kesildiğini söyleyen bir satırı korur.drop: çağrı ve çıktısı gider. En yakın assistant mesajındaki bir not kaldırılan çağrıları adlandırır, örneğin[gemini-compact removed 1 earlier tool call(s) and their output after a compaction; they ran: Bash(ls -la /usr/bin)]. Not olmadan model, işi gitmiş bir cevabı okudu ve o işi hiç yapmadığını söyledi (2.1.277 üzerinde ölçüldü).- Cevabın dokunmadığı bir mesaj engine'in kendi mesajı olarak geri döner.
- Sonuç %25'ten az küçüldüyse ya da bir şey başarısız olduysa (key yok, bir HTTP hatası, schema'yı bozan bir cevap), engine'in yerleşik özeti çalışır ve bir satır sebebini söyler.
2.1.277 üzerindeki canlı testte /compact, gemini-3.5-flash-lite ile 1,1 saniye sürdü. Gemini bir ls listesini attı ve kullanıcının düzenlemek üzere olduğu bir dosyanın cat çıktısını korudu; konuşma %93 küçüldü.
Ne gösterir
Transcript'te bir satır, modele gitmez, ve bir toast:
gemini-compact: summary: 58 → 7 messages · 91% smaller · 312k in, 5k out
gemini-compact: kept 9/11 messages · 93% smaller · 1 dropped, 0 truncated · 5k in, 59 out
gemini-compact: built-in summary: under 25% smaller (kept 14/16 messages · 3% smaller · ...)
gemini-compact: built-in summary: Gemini HTTP 429: Resource has been exhausted
Free tier'da toast · sent to Gemini free tier ekler.
Komut
/gemini-compact on ya da off, mod, gemini-core'un tuttuğu model ve thinking seviyesi, eşik, tier, key var mı, son sonuç
/gemini-compact on | off off her compaction'ı yerleşik özete bırakır; gemini-core'da key yokken on reddedilir
/gemini-compact mode summary | mode prune
/gemini-compact at <1-99> context bu yüzdenin üstündeyken biten bir turn'den sonra compact et
/gemini-compact at off otomatik compaction yok; /compact ve engine'in kendi compaction'ı yine Gemini'ye sorar
/gemini-compact reset plugin option'larına dön, ve off
Mod kurulumdan sonra kapalıdır: her compaction yerleşiktir, hiçbiri mod'un eşiğinde başlamaz ve /gemini-compact on yazılana kadar Gemini'ye hiçbir şey gönderilmez. Komut ayarları session'lar arasında saklanır ve hemen geçerli olur. Kendi başlattığı bir compaction'dan sonra mod, context eşiğin altındayken biten bir turn olana kadar başka bir tane başlatmaz; böylece eşiğin üstünde kalan bir context her turn'den sonra compact edilmez.
Key, tier, model (varsayılan gemini-3.5-flash-lite) ve thinking seviyesi gemini-core'a aittir:
/gemini-core model compact gemini-3.5-flash
/gemini-core thinking compact low
/gemini-core paid
Free tier ya da paid tier
Konuşma; prompt'larınızı, modelin çalıştırdığı komutları ve okuduğu dosyaların içeriğini tutar. Free tier'da Google bunları kullanabilir ve insan denetçiler okuyabilir; gemini-core README'si Gemini API Additional Terms'ten alıntılar. Google'a göstermeyeceğiniz bir projede billing açık bir key kullanın ve /gemini-core paid ayarlayın. Hiçbir mod bir key'in hangi tier'da olduğunu bilemez; tier ayarı yalnız uyarıyı seçer.
Model başına free tier limitleri Google AI Studio'da gösterilir, dokümantasyonda değil. Ölçülmedi. Uzun bir konuşmanın özeti tek bir büyük istektir, yani dakika başına token limiti onu HTTP 429 ile reddedebilir; o zaman yerleşik özet çalışır.
Kurulum
claude plugin marketplace add KilimcininKorOglu/claude-code-mods
claude plugin install gemini-compact@kilimcininkoroglu-mods
gemini-core'a bağlıdır; claude plugin install onu da ekler. 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" } }
Tek bir session için local checkout'tan, yanında gemini-core ile yükleyin:
CLAUDE_CODE_ENABLE_FUNCTION_HOOKS=1 claude --plugin-dir plugins/gemini-core --plugin-dir plugins/gemini-compact
Kurulumdan sonra
- Gemini key'ini ve tier'ı gemini-core'da ayarlayın, onun After installing bölümünde yazdığı gibi, sonra Claude Code'u yeniden başlatın.
/gemini-compact onçalıştırın. Key olmadanstill off: gemini-core has no Gemini keycevabını verir ve kapalı kalır./gemini-compactçalıştırın. İlk satıron · summary · <model> · thinking ... · automatic at 60% · <tier> tier · key setdemelidir.- Bir kere
/compactçalıştırın. Transcript satırıgemini-compact: summary:ile başlamalıdır.built-in summary:ile başlayan bir satır Gemini'nin neden kullanılmadığını adlandırır.
0.2.x'ten güncellemeden sonra: claude plugin update gemini-core'u eklemez (2.1.278 üzerinde ölçüldü), bu yüzden bir kere claude plugin install gemini-core@kilimcininkoroglu-mods çalıştırın. 0.3.0 sürümü key, tier ve model'i gemini-core'a taşıdı; daha önce saklanan apiKey, tier ve model option'ları ile /gemini-compact free|paid|model ayarları artık okunmuyor, onları gemini-core'da yeniden ayarlayın. mode ve at ayarları kalır. 0.4.0 sürümü mod'u varsayılan olarak kapattı: daha eski bir sürümden güncellediyseniz, daha önce /gemini-compact on çalıştırmadıysanız kapalıdır; bir kere /gemini-compact on çalıştırın.
Option'lar
| Option | Varsayılan | Ne ayarlar |
|---|---|---|
mode |
summary |
summary ya da prune; /gemini-compact mode onu ezer |
compactAtPercent |
60 |
Otomatik eşik; 0 kapatır; /gemini-compact at onu ezer |
keepRecent |
6 |
Kelimesi kelimesine korunan en yeni mesaj sayısı (summary) ya da çağrıları karar için hiç gönderilmeyen mesaj sayısı (prune) |
minReduction |
0.25 |
Prune modu: bu oranın altında yerleşik özet çalışır |
headChars |
300 |
Prune modu: kısaltılan bir çıktıdan korunan karakter sayısı |
maxInputChars |
400000 |
Prune modu: Gemini'ye en fazla gönderilen karakter |
summaryMaxInputChars |
2000000 |
Summary modu: Gemini'ye en fazla gönderilen karakter |
Nereye uzanır
Claude Code 2.1.278 üzerinde claude plugin validate ile doğrulandı:
❯ ./register.ts hooks: session.start, command.run{command=gemini-compact}, session.compact, turn.complete
❯ ./register.ts calls: $.clock.now (via askGemini), $.clock.sleep (via askGemini), $.command.register, $.gemini.enroll, $.gemini.read (via askGemini), $.gemini.request (via askGemini), $.gemini.settings (via compactWithGemini, runCommand, storePatch), $.http.fetch (via askGemini), $.session.compact (via maybeCompact), $.session.usage (via maybeCompact), $.store.delete (via runCommand), $.store.get (via loadConfig), $.store.set (via storePatch), $.ui.log, $.ui.toast (via report)
Reach L3, network'e çıkar.
1. Okur: her compaction'da konuşmayı (mesajlar, tool input'ları ve output'ları); her main-loop turn'ünden sonra context doluluğunu; kendi $.store dosyasını; gemini-core'dan key'i taşıyan isteği
2. Çalıştırır: hiçbir process; eşiğin üstünde biten bir turn'den sonra bir $.session.compact, context tekrar eşiğin altına inene kadar en fazla bir kere
3. Gönderir: konuşmayı (summary: en yeni mesajlar dışında hepsi; prune: tamamı), compaction başına bir istek (503 sonrası en fazla dört, 429 ya da key hatası sonrası ek key başına bir tane daha), gemini-core'un kurduğu URL'ye (generativelanguage.googleapis.com), key x-goog-api-key header'ında, hiçbir zaman URL'de değil
4. Saklar: $.store içinde üç komut ayarını (enabled, mode, atPercent); son sonuç bellekte yaşar
5. Düşman girdi: Gemini cevabı güvenilmezdir: bir prune cevabı yalnız schema şeklinde ve her aday id bir kere geçtiğinde uygulanır; bir özet, modelin okuduğu tek bir kullanıcı mesajının metni olur, yani düşman bir özet modeli, okuduğu bir dosyadaki metin gibi yönlendirebilir; bozuk her şey yerleşik özete düşer
Sınırlar
- Bir özet ve bir atma, bir modelin kararıdır. Özet, en yeni mesajların tekrarlamadığı ayrıntıyı kaybeder. Prune notu modele hangi çağrıların çalıştığını söyler, böylece bir tool'u tekrar çalıştırabilir.
- Compaction'dan sonra context tekrar cache'e yazılır. Prune modunda yerleşik özetten büyük kalır, yani sonraki mesaj daha büyük bir cache write öder.
- Uzun bir konuşmanın özeti Gemini'nin daha uzun sürmesine yol açar; compaction onu bekler. Yalnız kısa konuşmalar ölçüldü.
- Bir subagent'ın kendi compaction'ı engine'e bırakılır.
- Engine'in önceden hesapladığı bir compaction (
precompute) da Gemini'ye sorar. Engine'in o sonucu sonraki compaction'da yeniden kullanıp kullanmadığı ölçülmedi. claude plugin testtest engine'i bir$.session.compact()çağrısınatriggergeçirmez.plugintetikleyicisi ve onu cevaplayan hook canlı bir session'da ölçüldü.
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
gemini-compact
A Claude Code Mod that moves compaction from Claude to Gemini. It has two modes:
- summary (the default): Gemini summarizes the conversation before the newest 6 messages, and those messages stay verbatim after the summary. Claude writes no summary; the engine's built-in summary runs only when Gemini fails.
- prune: Gemini answers, for every older tool call, whether the call and its output stay, stay with a cut output, or go. Every user and assistant message stays verbatim. Nothing is summarized.
The prune idea follows fast-jev-compaction by Tamara Tran (tamaratran/fast-jev-compaction), which asks TypeSafe Jev. The code is new and asks Gemini.
Which mode
The built-in compaction is one Claude request: it reads the whole context and writes a summary, and that request counts against your Claude usage. Both modes replace that request with a Gemini request.
After the compaction, every Claude request reads what is left. In summary mode that is the summary and the newest messages, close to what the built-in summary leaves. In prune mode it is every message of the conversation less the dropped tool output, which is larger, so each following request reads more. The sizes were not measured on a long session.
Use summary mode to save the most Claude usage. Use prune mode when the exact wording of every message matters more than the size.
Summary mode
- At
/compact, at the engine's own compaction, and after a turn that ends with the context over the threshold, thesession.compacthook takes the conversation. - The newest 6 messages stay. The cut moves back to an assistant message, so a tool result is never kept without its call and the kept part opens with an assistant message after the summary.
- One
generateContentrequest sends everything before the cut to Gemini: every message, and each call with its input and its full output. Above 2,000,000 characters the longest outputs are cut to their head and tail first. - Gemini writes a plain-text summary in nine sections: the request and intent, technical concepts, files and code, errors and fixes, problem solving, every user message verbatim, pending tasks, the current work, and the next step. The text after
/compactis passed to it. - The conversation becomes one user message (a note, then the summary) followed by the kept messages, which go back as the engine's own messages.
- The built-in summary runs, and one line says why, when there is no key, Gemini fails, the summary is under 200 characters or cut at the output limit (32,768 tokens), or the result is not smaller than the conversation.
In both modes gemini-core builds the request with the key, the model and the thinking level it holds for gemini-compact, and reads the answer. After an HTTP 503 ("high demand") the mod asks again after 1 s, 2 s and 3 s, at most four times, and starts no attempt once 60 s have passed.
In a live check on 2.1.277, /compact took 2.6 seconds with gemini-3.5-flash-lite, Claude sent no compaction request, and after it the model named a word and a file that appeared only in the summarized part.
Prune mode
- The same three triggers reach the
session.compacthook. - Tool calls in the first message and in the newest 6 messages are kept whole. Every other call gets an id (
c1,c2, ...). - One
generateContentrequest sends the conversation to Gemini: every message, and each call with its input and its full output. Above 400,000 characters the longest outputs are cut to their head and tail first. A response schema allows exactly one answer per id:keep,truncateordrop. - The mod checks the answer (every id exactly once, no other id) and rebuilds the conversation:
keep: the call and its output stay.truncate: the call stays, the output keeps its first 300 characters and one line that says it was cut.drop: the call and its output go. A note on the nearest assistant message names the removed calls, for example[gemini-compact removed 1 earlier tool call(s) and their output after a compaction; they ran: Bash(ls -la /usr/bin)]. Without the note, the model read a reply whose work was gone and said it had never done that work (measured on 2.1.277).- A message the answer does not touch goes back as the engine's own message.
- When the result is less than 25% smaller, or anything fails (no key, an HTTP error, an answer that breaks the schema), the engine's built-in summary runs and one line says why.
In a live check on 2.1.277, /compact took 1.1 seconds with gemini-3.5-flash-lite. Gemini dropped an ls listing and kept the cat output of a file the user was about to edit, and the conversation became 93% smaller.
What it shows
One line in the transcript, not sent to the model, and a toast:
gemini-compact: summary: 58 → 7 messages · 91% smaller · 312k in, 5k out
gemini-compact: kept 9/11 messages · 93% smaller · 1 dropped, 0 truncated · 5k in, 59 out
gemini-compact: built-in summary: under 25% smaller (kept 14/16 messages · 3% smaller · ...)
gemini-compact: built-in summary: Gemini HTTP 429: Resource has been exhausted
On the free tier the toast adds · sent to Gemini free tier.
Command
/gemini-compact on or off, mode, the model and thinking level gemini-core holds, threshold, tier, whether a key is set, the last result
/gemini-compact on | off off leaves every compaction to the built-in summary; on is refused while gemini-core has no key
/gemini-compact mode summary | mode prune
/gemini-compact at <1-99> compact after a turn that ends with the context over this percentage
/gemini-compact at off no automatic compaction; /compact and the engine's own compaction still ask Gemini
/gemini-compact reset back to the plugin options, and off
The mod is off after an install: every compaction is the built-in one, none starts on the mod's threshold, and nothing is sent to Gemini until /gemini-compact on. The command settings are kept across sessions and take effect at once. After a compaction it started, the mod starts no other one until a turn ends with the context under the threshold, so a context that stays over it does not compact after every turn.
The key, the tier, the model (default gemini-3.5-flash-lite) and the thinking level are gemini-core's:
/gemini-core model compact gemini-3.5-flash
/gemini-core thinking compact low
/gemini-core paid
Free tier or paid tier
The conversation holds your prompts, the commands the model ran and the contents of the files it read. On the free tier Google may use them and human reviewers may read them; the gemini-core README quotes the Gemini API Additional Terms. On a project you would not show to Google, use a key with billing enabled and set /gemini-core paid. No mod can tell which tier a key is on; the tier setting only chooses the warning.
The free tier limits per model are shown in Google AI Studio, not in the documentation. They were not measured. A summary of a long conversation is one large request, so a per-minute token limit can refuse it with HTTP 429; the built-in summary then runs.
Install
claude plugin marketplace add KilimcininKorOglu/claude-code-mods
claude plugin install gemini-compact@kilimcininkoroglu-mods
It depends on gemini-core, which claude plugin install adds. 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" } }
Load it from a local checkout for one session, with gemini-core beside it:
CLAUDE_CODE_ENABLE_FUNCTION_HOOKS=1 claude --plugin-dir plugins/gemini-core --plugin-dir plugins/gemini-compact
After installing
- Set the Gemini key and the tier in gemini-core, as its After installing section says, then restart Claude Code.
- Run
/gemini-compact on. Without a key it answersstill off: gemini-core has no Gemini keyand stays off. - Run
/gemini-compact. The first line readson · summary · <model> · thinking ... · automatic at 60% · <tier> tier · key set. - Run
/compactonce. The transcript line should start withgemini-compact: summary:. A line that starts withbuilt-in summary:names why Gemini was not used.
After an update from 0.2.x: claude plugin update does not add gemini-core (measured on 2.1.278), so run claude plugin install gemini-core@kilimcininkoroglu-mods once. Version 0.3.0 moved the key, tier and model to gemini-core; the apiKey, tier and model options and the settings /gemini-compact free|paid|model stored before are no longer read, so set them again in gemini-core. The mode and at settings stay. Version 0.4.0 made the mod off by default: after an update from an earlier version it is off unless you ran /gemini-compact on before, so run /gemini-compact on once.
Options
| Option | Default | What it sets |
|---|---|---|
mode |
summary |
summary or prune; /gemini-compact mode overrides it |
compactAtPercent |
60 |
The automatic threshold; 0 turns it off; /gemini-compact at overrides it |
keepRecent |
6 |
Newest messages kept verbatim (summary) or whose calls are never sent for a decision (prune) |
minReduction |
0.25 |
Prune mode: below this fraction the built-in summary runs |
headChars |
300 |
Prune mode: characters kept of a truncated output |
maxInputChars |
400000 |
Prune mode: characters sent to Gemini at most |
summaryMaxInputChars |
2000000 |
Summary mode: characters sent to Gemini at most |
What it can reach
Validated with claude plugin validate on Claude Code 2.1.278:
❯ ./register.ts hooks: session.start, command.run{command=gemini-compact}, session.compact, turn.complete
❯ ./register.ts calls: $.clock.now (via askGemini), $.clock.sleep (via askGemini), $.command.register, $.gemini.enroll, $.gemini.read (via askGemini), $.gemini.request (via askGemini), $.gemini.settings (via compactWithGemini, runCommand, storePatch), $.http.fetch (via askGemini), $.session.compact (via maybeCompact), $.session.usage (via maybeCompact), $.store.delete (via runCommand), $.store.get (via loadConfig), $.store.set (via storePatch), $.ui.log, $.ui.toast (via report)
Reach L3, reaches the network.
1. Reads: the conversation at each compaction (messages, tool inputs and outputs); the context fill after each main-loop turn; its own $.store; from gemini-core, the request with the key
2. Runs: no process; one $.session.compact after a turn that ends over the threshold, at most once until the context was under it again
3. Sends: the conversation (summary: all but the newest messages; prune: all of it), one request per compaction (up to four after a 503, and once more per extra key after a 429 or a key error), to the URL gemini-core builds (generativelanguage.googleapis.com) with the key in the x-goog-api-key header, never in the URL
4. Persists: in $.store, the three command settings (enabled, mode, atPercent); the last result lives in memory
5. Hostile input: the Gemini answer is untrusted: a prune answer is applied only in the schema shape with every candidate id once; a summary becomes the text of one user message the model reads, so a hostile summary can steer the model, as text in a file it reads can; anything malformed falls back to the built-in summary
Limits
- A summary and a drop are a model's judgment. A summary loses detail the newest messages do not repeat. The prune note tells the model which calls ran, so it can run a tool again.
- After the compaction the context is written to the cache again. In prune mode it stays larger than a built-in summary, so the next message pays a larger cache write.
- A summary of a long conversation takes Gemini longer; the compaction waits for it. Only short conversations were timed.
- A subagent's own compaction is left to the engine.
- A compaction the engine precomputes (
precompute) also asks Gemini. Whether the engine reuses that result for the compaction that follows was not measured. - The test engine of
claude plugin testpasses notriggerto a$.session.compact()call. Theplugintrigger and the hook that answers it were measured in a live session.
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